diff options
-rw-r--r-- | scd/app-common.h | 47 | ||||
-rw-r--r-- | scd/app.c | 535 | ||||
-rw-r--r-- | scd/command.c | 586 | ||||
-rw-r--r-- | scd/scdaemon.h | 1 |
4 files changed, 403 insertions, 766 deletions
diff --git a/scd/app-common.h b/scd/app-common.h index 21f659eb6..781bf465c 100644 --- a/scd/app-common.h +++ b/scd/app-common.h @@ -22,13 +22,8 @@ #ifndef GNUPG_SCD_APP_COMMON_H #define GNUPG_SCD_APP_COMMON_H -#if GNUPG_MAJOR_VERSION == 1 -# ifdef ENABLE_AGENT_SUPPORT -# include "assuan.h" -# endif -#else -# include <ksba.h> -#endif +#include <npth.h> +#include <ksba.h> #define APP_CHANGE_FLAG_RESET 1 @@ -41,6 +36,10 @@ struct app_local_s; /* Defined by all app-*.c. */ struct app_ctx_s { + struct app_ctx_s *next; + + npth_mutex_t lock; + /* Number of connections currently using this application context. If this is not 0 the application has been initialized and the function pointers may be used. Note that for unsupported @@ -50,18 +49,12 @@ struct app_ctx_s { /* Used reader slot. */ int slot; - /* If this is used by GnuPG 1.4 we need to know the assuan context - in case we need to divert the operation to an already running - agent. This if ASSUAN_CTX is not NULL we take this as indication - that all operations are diverted to gpg-agent. */ -#if GNUPG_MAJOR_VERSION == 1 - assuan_context_t assuan_ctx; -#endif /*GNUPG_MAJOR_VERSION == 1*/ - unsigned char *serialno; /* Serialnumber in raw form, allocated. */ size_t serialnolen; /* Length in octets of serialnumber. */ const char *apptype; unsigned int card_version; + unsigned int card_status; + unsigned int require_get_status:1; unsigned int did_chv1:1; unsigned int force_chv1:1; /* True if the card does not cache CHV1. */ unsigned int did_chv2:1; @@ -119,20 +112,8 @@ struct app_ctx_s { gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); } fnc; - }; -#if GNUPG_MAJOR_VERSION == 1 -gpg_error_t app_select_openpgp (app_t app); -gpg_error_t app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp); -gpg_error_t app_openpgp_storekey (app_t app, int keyno, - unsigned char *template, size_t template_len, - time_t created_at, - const unsigned char *m, size_t mlen, - const unsigned char *e, size_t elen, - gpg_error_t (*pincb)(void*, const char *, char **), - void *pincb_arg); -#else /*-- app-help.c --*/ unsigned int app_help_count_bits (const unsigned char *a, size_t len); gpg_error_t app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip); @@ -142,10 +123,10 @@ size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff); /*-- app.c --*/ void app_dump_state (void); void application_notify_card_reset (int slot); -gpg_error_t check_application_conflict (ctrl_t ctrl, int slot, - const char *name); -gpg_error_t select_application (ctrl_t ctrl, int slot, const char *name, - app_t *r_app); +gpg_error_t check_application_conflict (const char *name, app_t app); +gpg_error_t app_reset (app_t app, ctrl_t ctrl, int send_reset); +gpg_error_t select_application (ctrl_t ctrl, const char *name, app_t *r_app, + int scan); char *get_supported_applications (void); void release_application (app_t app); gpg_error_t app_munge_serialno (app_t app); @@ -222,8 +203,4 @@ gpg_error_t app_select_geldkarte (app_t app); gpg_error_t app_select_sc_hsm (app_t app); -#endif - - - #endif /*GNUPG_SCD_APP_COMMON_H*/ @@ -1,5 +1,5 @@ /* app.c - Application selection. - * Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. + * Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -25,27 +25,14 @@ #include <npth.h> #include "scdaemon.h" +#include "exechelp.h" #include "app-common.h" #include "iso7816.h" #include "apdu.h" #include "tlv.h" -/* This table is used to keep track of locks on a per reader base. - The index into the table is the slot number of the reader. The - mutex will be initialized on demand (one of the advantages of a - userland threading system). */ -static struct -{ - int initialized; - npth_mutex_t lock; - app_t app; /* Application context in use or NULL. */ -} lock_table[10]; - - - -static void deallocate_app (app_t app); - - +static npth_mutex_t app_list_lock; +static app_t app_top; static void print_progress_line (void *opaque, const char *what, int pc, int cur, int tot) @@ -69,54 +56,35 @@ print_progress_line (void *opaque, const char *what, int pc, int cur, int tot) success; only then the unlock_reader function must be called after returning from the handler. */ static gpg_error_t -lock_reader (int slot, ctrl_t ctrl) +lock_app (app_t app, ctrl_t ctrl) { - int res; - - if (slot < 0 || slot >= DIM (lock_table)) - return gpg_error (slot<0? GPG_ERR_INV_VALUE : GPG_ERR_RESOURCE_LIMIT); - - if (!lock_table[slot].initialized) - { - res = npth_mutex_init (&lock_table[slot].lock, NULL); - if (res) - { - log_error ("error initializing mutex: %s\n", strerror (res)); - return gpg_error_from_errno (res); - } - lock_table[slot].initialized = 1; - lock_table[slot].app = NULL; - } + gpg_error_t err; - res = npth_mutex_lock (&lock_table[slot].lock); - if (res) + err = npth_mutex_lock (&app->lock); + if (err) { - log_error ("failed to acquire APP lock for slot %d: %s\n", - slot, strerror (res)); - return gpg_error_from_errno (res); + log_error ("failed to acquire APP lock for %p: %s\n", + app, strerror (err)); + return gpg_error_from_errno (err); } - apdu_set_progress_cb (slot, print_progress_line, ctrl); + apdu_set_progress_cb (app->slot, print_progress_line, ctrl); return 0; } /* Release a lock on the reader. See lock_reader(). */ static void -unlock_reader (int slot) +unlock_app (app_t app) { - int res; - - if (slot < 0 || slot >= DIM (lock_table) - || !lock_table[slot].initialized) - log_bug ("unlock_reader called for invalid slot %d\n", slot); + gpg_error_t err; - apdu_set_progress_cb (slot, NULL, NULL); + apdu_set_progress_cb (app->slot, NULL, NULL); - res = npth_mutex_unlock (&lock_table[slot].lock); - if (res) - log_error ("failed to release APP lock for slot %d: %s\n", - slot, strerror (res)); + err = npth_mutex_unlock (&app->lock); + if (err) + log_error ("failed to release APP lock for %p: %s\n", + app, strerror (err)); } @@ -125,20 +93,12 @@ unlock_reader (int slot) void app_dump_state (void) { - int slot; + app_t a; - for (slot=0; slot < DIM (lock_table); slot++) - if (lock_table[slot].initialized) - { - log_info ("app_dump_state: slot=%d", slot); - if (lock_table[slot].app) - { - log_printf (" app=%p", lock_table[slot].app); - if (lock_table[slot].app->apptype) - log_printf (" type='%s'", lock_table[slot].app->apptype); - } - log_printf ("\n"); - } + npth_mutex_lock (&app_list_lock); + for (a = app_top; a; a = a->next) + log_info ("app_dump_state: app=%p type='%s'\n", a, a->apptype); + npth_mutex_unlock (&app_list_lock); } /* Check wether the application NAME is allowed. This does not mean @@ -155,54 +115,16 @@ is_app_allowed (const char *name) } -/* This may be called to tell this module about a removed or resetted card. */ -void -application_notify_card_reset (int slot) -{ - if (slot < 0 || slot >= DIM (lock_table)) - return; - - /* FIXME: We are ignoring any error value here. */ - lock_reader (slot, NULL); - - /* Release the APP, as it's not reusable any more. */ - if (lock_table[slot].app) - { - if (lock_table[slot].app->ref_count) - log_bug ("trying to release active context\n"); - - deallocate_app (lock_table[slot].app); - lock_table[slot].app = NULL; - log_debug ("application has been released\n"); - } - - unlock_reader (slot); -} - - -/* - * This function is called with lock held. - */ static gpg_error_t -check_conflict (int slot, const char *name) +check_conflict (app_t app, const char *name) { - app_t app = lock_table[slot].app; - if (!app || !name || (app->apptype && !ascii_strcasecmp (app->apptype, name))) return 0; - if (!app->ref_count) - { - lock_table[slot].app = NULL; - deallocate_app (app); - return 0; - } - else - { - log_info ("application '%s' in use by reader %d - can't switch\n", - app->apptype? app->apptype : "<null>", slot); - return gpg_error (GPG_ERR_CONFLICT); - } + log_info ("application '%s' in use - can't switch\n", + app->apptype? app->apptype : "<null>"); + + return gpg_error (GPG_ERR_CONFLICT); } /* This function is used by the serialno command to check for an @@ -210,85 +132,85 @@ check_conflict (int slot, const char *name) used to request a specific application and the connection has already done a select_application. */ gpg_error_t -check_application_conflict (ctrl_t ctrl, int slot, const char *name) +check_application_conflict (const char *name, app_t app) { - gpg_error_t err; + return check_conflict (app, name); +} - if (slot < 0 || slot >= DIM (lock_table)) - return gpg_error (GPG_ERR_INV_VALUE); - err = lock_reader (slot, ctrl); - if (err) - return err; +static void +release_application_internal (app_t app) +{ + if (!app->ref_count) + log_bug ("trying to release an already released context\n"); - err = check_conflict (slot, name); - unlock_reader (slot); - return err; + --app->ref_count; } - -/* If called with NAME as NULL, select the best fitting application - and return a context; otherwise select the application with NAME - and return a context. SLOT identifies the reader device. Returns - an error code and stores NULL at R_APP if no application was found - or no card is present. */ gpg_error_t -select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app) +app_reset (app_t app, ctrl_t ctrl, int send_reset) { gpg_error_t err; - app_t app = NULL; - unsigned char *result = NULL; - size_t resultlen; - int want_undefined; - - (void)ctrl; - *r_app = NULL; - - want_undefined = (name && !strcmp (name, "undefined")); - - err = lock_reader (slot, ctrl); + err = lock_app (app, ctrl); if (err) return err; - /* First check whether we already have an application to share. */ - err = check_conflict (slot, name); - if (err) + if (send_reset) { - unlock_reader (slot); - return err; - } - - app = lock_table[slot].app; + int sw = apdu_reset (app->slot); + if (sw) + err = gpg_error (GPG_ERR_CARD_RESET); - /* If we can reuse an application, bump the reference count and - return it. */ - if (app) + /* Release the same application which is used by other sessions. */ + send_client_notifications (app); + } + else { - if (app->slot != slot) - log_bug ("slot mismatch %d/%d\n", app->slot, slot); - app->slot = slot; - - app->ref_count++; - *r_app = app; - unlock_reader (slot); - return 0; /* Okay: We share that one. */ + ctrl->app_ctx = NULL; + release_application_internal (app); } + unlock_app (app); + return err; +} + +static gpg_error_t +app_new_register (int slot, ctrl_t ctrl, const char *name) +{ + gpg_error_t err = 0; + app_t app = NULL; + unsigned char *result = NULL; + size_t resultlen; + int want_undefined; + /* Need to allocate a new one. */ app = xtrycalloc (1, sizeof *app); if (!app) { err = gpg_error_from_syserror (); log_info ("error allocating context: %s\n", gpg_strerror (err)); - unlock_reader (slot); return err; } + app->slot = slot; + err = npth_mutex_init (&app->lock, NULL); + if (err) + { + err = gpg_error_from_syserror (); + log_error ("error initializing mutex: %s\n", strerror (err)); + xfree (app); + return err; + } + err = lock_app (app, ctrl); + if (err) + { + xfree (app); + return err; + } - /* Fixme: We should now first check whether a card is at all - present. */ + want_undefined = (name && !strcmp (name, "undefined")); /* Try to read the GDO file first to get a default serial number. We skip this if the undefined application has been requested. */ @@ -377,19 +299,87 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app) else log_info ("no supported card application found: %s\n", gpg_strerror (err)); + unlock_app (app); xfree (app); - unlock_reader (slot); return err; } - app->ref_count = 1; + app->require_get_status = 1; /* For token, this can be 0. */ - lock_table[slot].app = app; - *r_app = app; - unlock_reader (slot); + npth_mutex_lock (&app_list_lock); + app->next = app_top; + app_top = app; + npth_mutex_unlock (&app_list_lock); + unlock_app (app); return 0; } +/* If called with NAME as NULL, select the best fitting application + and return a context; otherwise select the application with NAME + and return a context. Returns an error code and stores NULL at + R_APP if no application was found or no card is present. */ +gpg_error_t +select_application (ctrl_t ctrl, const char *name, app_t *r_app, int scan) +{ + gpg_error_t err; + app_t app; + int slot; + + *r_app = NULL; + + if (scan + /* FIXME: Here, we can change code to support multiple readers. + For now, we only open a single reader. + */ + && !app_top) + { + slot = apdu_open_reader (opt.reader_port); + if (slot >= 0) + { + int sw = apdu_connect (slot); + + if (sw == SW_HOST_CARD_INACTIVE) + { + /* Try again. */ + sw = apdu_reset (slot); + } + + if (!sw || sw == SW_HOST_ALREADY_CONNECTED) + err = 0; + else + err = gpg_error (GPG_ERR_ENODEV); + } + else + err = gpg_error (GPG_ERR_ENODEV); + + if (!err) + err = app_new_register (slot, ctrl, name); + else + apdu_close_reader (slot); + } + else + err = 0; + + if (!err) + app = app_top; + else + app = NULL; + + if (app) + { + lock_app (app, ctrl); + err = check_conflict (app, name); + if (!err) + { + app->ref_count++; + *r_app = app; + } + unlock_app (app); + } + + return err; +} + char * get_supported_applications (void) @@ -425,10 +415,27 @@ get_supported_applications (void) } -/* Deallocate the application. */ +/* Deallocate the application. */ static void deallocate_app (app_t app) { + app_t a, a_prev = NULL; + + for (a = app_top; a; a = a->next) + if (a == app) + { + if (a_prev == NULL) + app_top = a->next; + else + a_prev->next = a->next; + break; + } + else + a_prev = a; + + if (app->ref_count) + log_error ("trying to release context used yet (%d)\n", app->ref_count); + if (app->fnc.deinit) { app->fnc.deinit (app); @@ -447,33 +454,17 @@ deallocate_app (app_t app) void release_application (app_t app) { - int slot; - if (!app) return; - if (!app->ref_count) - log_bug ("trying to release an already released context\n"); - if (--app->ref_count) - return; - - /* Move the reference to the application in the lock table. */ - slot = app->slot; - /* FIXME: We are ignoring any error value. */ - lock_reader (slot, NULL); - if (lock_table[slot].app != app) - { - unlock_reader (slot); - log_bug ("app mismatch %p/%p\n", app, lock_table[slot].app); - deallocate_app (app); - return; - } - /* We don't deallocate app here. Instead, we keep it. This is useful so that a card does not get reset even if only one session is using the card - this way the PIN cache and other cached data are preserved. */ - unlock_reader (slot); + + lock_app (app, NULL); + release_application_internal (app); + unlock_app (app); } @@ -570,11 +561,11 @@ app_write_learn_status (app_t app, ctrl_t ctrl, unsigned int flags) if (app->apptype && !(flags & 1)) send_status_info (ctrl, "APPTYPE", app->apptype, strlen (app->apptype), NULL, 0); - err = lock_reader (app->slot, ctrl); + err = lock_app (app, ctrl); if (err) return err; err = app->fnc.learn_status (app, ctrl, flags); - unlock_reader (app->slot); + unlock_app (app); return err; } @@ -595,11 +586,11 @@ app_readcert (app_t app, ctrl_t ctrl, const char *certid, return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.readcert) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_reader (app->slot, ctrl); + err = lock_app (app, ctrl); if (err) return err; err = app->fnc.readcert (app, certid, cert, certlen); - unlock_reader (app->slot); + unlock_app (app); return err; } @@ -628,11 +619,11 @@ app_readkey (app_t app, ctrl_t ctrl, int advanced, const char *keyid, return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.readkey) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_reader (app->slot, ctrl); + err = lock_app (app, ctrl); if (err) return err; err= app->fnc.readkey (app, advanced, keyid, pk, pklen); - unlock_reader (app->slot); + unlock_app (app); return err; } @@ -670,11 +661,11 @@ app_getattr (app_t app, ctrl_t ctrl, const char *name) if (!app->fnc.getattr) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_reader (app->slot, ctrl); + err = lock_app (app, ctrl); if (err) return err; err = app->fnc.getattr (app, ctrl, name); - unlock_reader (app->slot); + unlock_app (app); return err; } @@ -693,11 +684,11 @@ app_setattr (app_t app, ctrl_t ctrl, const char *name, return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.setattr) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_reader (app->slot, ctrl); + err = lock_app (app, ctrl); if (err) return err; err = app->fnc.setattr (app, name, pincb, pincb_arg, value, valuelen); - unlock_reader (app->slot); + unlock_app (app); return err; } @@ -719,14 +710,14 @@ app_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.sign) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_reader (app->slot, ctrl); + err = lock_app (app, ctrl); if (err) return err; err = app->fnc.sign (app, keyidstr, hashalgo, pincb, pincb_arg, indata, indatalen, outdata, outdatalen); - unlock_reader (app->slot); + unlock_app (app); if (opt.verbose) log_info ("operation sign result: %s\n", gpg_strerror (err)); return err; @@ -751,14 +742,14 @@ app_auth (app_t app, ctrl_t ctrl, const char *keyidstr, return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.auth) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_reader (app->slot, ctrl); + err = lock_app (app, ctrl); if (err) return err; err = app->fnc.auth (app, keyidstr, pincb, pincb_arg, indata, indatalen, outdata, outdatalen); - unlock_reader (app->slot); + unlock_app (app); if (opt.verbose) log_info ("operation auth result: %s\n", gpg_strerror (err)); return err; @@ -786,7 +777,7 @@ app_decipher (app_t app, ctrl_t ctrl, const char *keyidstr, return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.decipher) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_reader (app->slot, ctrl); + err = lock_app (app, ctrl); if (err) return err; err = app->fnc.decipher (app, keyidstr, @@ -794,7 +785,7 @@ app_decipher (app_t app, ctrl_t ctrl, const char *keyidstr, indata, indatalen, outdata, outdatalen, r_info); - unlock_reader (app->slot); + unlock_app (app); if (opt.verbose) log_info ("operation decipher result: %s\n", gpg_strerror (err)); return err; @@ -817,12 +808,12 @@ app_writecert (app_t app, ctrl_t ctrl, return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.writecert) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_reader (app->slot, ctrl); + err = lock_app (app, ctrl); if (err) return err; err = app->fnc.writecert (app, ctrl, certidstr, pincb, pincb_arg, data, datalen); - unlock_reader (app->slot); + unlock_app (app); if (opt.verbose) log_info ("operation writecert result: %s\n", gpg_strerror (err)); return err; @@ -845,12 +836,12 @@ app_writekey (app_t app, ctrl_t ctrl, return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.writekey) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_reader (app->slot, ctrl); + err = lock_app (app, ctrl); if (err) return err; err = app->fnc.writekey (app, ctrl, keyidstr, flags, pincb, pincb_arg, keydata, keydatalen); - unlock_reader (app->slot); + unlock_app (app); if (opt.verbose) log_info ("operation writekey result: %s\n", gpg_strerror (err)); return err; @@ -872,12 +863,12 @@ app_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags, return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.genkey) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_reader (app->slot, ctrl); + err = lock_app (app, ctrl); if (err) return err; err = app->fnc.genkey (app, ctrl, keynostr, flags, createtime, pincb, pincb_arg); - unlock_reader (app->slot); + unlock_app (app); if (opt.verbose) log_info ("operation genkey result: %s\n", gpg_strerror (err)); return err; @@ -896,11 +887,11 @@ app_get_challenge (app_t app, ctrl_t ctrl, size_t nbytes, unsigned char *buffer) return gpg_error (GPG_ERR_INV_VALUE); if (!app->ref_count) return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - err = lock_reader (app->slot, ctrl); + err = lock_app (app, ctrl); if (err) return err; err = iso7816_get_challenge (app->slot, nbytes, buffer); - unlock_reader (app->slot); + unlock_app (app); return err; } @@ -920,12 +911,12 @@ app_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, int reset_mode, return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.change_pin) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_reader (app->slot, ctrl); + err = lock_app (app, ctrl); if (err) return err; err = app->fnc.change_pin (app, ctrl, chvnostr, reset_mode, pincb, pincb_arg); - unlock_reader (app->slot); + unlock_app (app); if (opt.verbose) log_info ("operation change_pin result: %s\n", gpg_strerror (err)); return err; @@ -948,12 +939,134 @@ app_check_pin (app_t app, ctrl_t ctrl, const char *keyidstr, return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.check_pin) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_reader (app->slot, ctrl); + err = lock_app (app, ctrl); if (err) return err; err = app->fnc.check_pin (app, keyidstr, pincb, pincb_arg); - unlock_reader (app->slot); + unlock_app (app); if (opt.verbose) log_info ("operation check_pin result: %s\n", gpg_strerror (err)); return err; } + +static void +report_change (int slot, int old_status, int cur_status) +{ + char *homestr, *envstr; + char *fname; + char templ[50]; + FILE *fp; + + snprintf (templ, sizeof templ, "reader_%d.status", slot); + fname = make_filename (gnupg_homedir (), templ, NULL ); + fp = fopen (fname, "w"); + if (fp) + { + fprintf (fp, "%s\n", + (cur_status & 1)? "USABLE": + (cur_status & 4)? "ACTIVE": + (cur_status & 2)? "PRESENT": "NOCARD"); + fclose (fp); + } + xfree (fname); + + homestr = make_filename (gnupg_homedir (), NULL); + if (gpgrt_asprintf (&envstr, "GNUPGHOME=%s", homestr) < 0) + log_error ("out of core while building environment\n"); + else + { + gpg_error_t err; + const char *args[9], *envs[2]; + char numbuf1[30], numbuf2[30], numbuf3[30]; + + envs[0] = envstr; + envs[1] = NULL; + + sprintf (numbuf1, "%d", slot); + sprintf (numbuf2, "0x%04X", old_status); + sprintf (numbuf3, "0x%04X", cur_status); + args[0] = "--reader-port"; + args[1] = numbuf1; + args[2] = "--old-code"; + args[3] = numbuf2; + args[4] = "--new-code"; + args[5] = numbuf3; + args[6] = "--status"; + args[7] = ((cur_status & 1)? "USABLE": + (cur_status & 4)? "ACTIVE": + (cur_status & 2)? "PRESENT": "NOCARD"); + args[8] = NULL; + + fname = make_filename (gnupg_homedir (), "scd-event", NULL); + err = gnupg_spawn_process_detached (fname, args, envs); + if (err && gpg_err_code (err) != GPG_ERR_ENOENT) + log_error ("failed to run event handler '%s': %s\n", + fname, gpg_strerror (err)); + xfree (fname); + xfree (envstr); + } + xfree (homestr); +} + +void +scd_update_reader_status_file (void) +{ + app_t a, app_next; + + npth_mutex_lock (&app_list_lock); + for (a = app_top; a; a = app_next) + { + app_next = a->next; + if (a->require_get_status) + { + int sw; + unsigned int status; + sw = apdu_get_status (a->slot, 0, &status); + + if (sw == SW_HOST_NO_READER) + { + /* Most likely the _reader_ has been unplugged. */ + status = 0; + } + else if (sw) + { + /* Get status failed. Ignore that. */ + continue; + } + + if (a->card_status != status) + { + report_change (a->slot, a->card_status, status); + send_client_notifications (a); + + if (status == 0) + { + log_debug ("Removal of a card: %d\n", a->slot); + apdu_close_reader (a->slot); + deallocate_app (a); + } + else + a->card_status = status; + } + } + } + npth_mutex_unlock (&app_list_lock); +} + +/* This function must be called once to initialize this module. This + has to be done before a second thread is spawned. We can't do the + static initialization because Pth emulation code might not be able + to do a static init; in particular, it is not possible for W32. */ +void +initialize_module_command (void) +{ + static int initialized; + int err; + + if (!initialized) + { + err = npth_mutex_init (&app_list_lock, NULL); + if (!err) + initialized = 1; + } +} diff --git a/scd/command.c b/scd/command.c index 02bf76fa3..0c823d2c9 100644 --- a/scd/command.c +++ b/scd/command.c @@ -37,7 +37,6 @@ #include "iso7816.h" #include "apdu.h" /* Required for apdu_*_reader (). */ #include "atr.h" -#include "exechelp.h" #ifdef HAVE_LIBUSB #include "ccid-driver.h" #endif @@ -59,38 +58,7 @@ #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t)) - -/* Macro to flag a removed card. ENODEV is also tested to catch the - case of a removed reader. */ -#define TEST_CARD_REMOVAL(c,r) \ - do { \ - int _r = (r); \ - if (gpg_err_code (_r) == GPG_ERR_CARD_NOT_PRESENT \ - || gpg_err_code (_r) == GPG_ERR_CARD_REMOVED \ - || gpg_err_code (_r) == GPG_ERR_CARD_RESET \ - || gpg_err_code (_r) == GPG_ERR_ENODEV ) \ - update_card_removed ((c)->server_local->vreader_idx, 1); \ - } while (0) - -#define IS_LOCKED(c) \ - (locked_session \ - && locked_session != (c)->server_local \ - && (c)->server_local->vreader_idx != -1 \ - && locked_session->ctrl_backlink \ - && ((c)->server_local->vreader_idx \ - == locked_session->ctrl_backlink->server_local->vreader_idx)) - - -/* This structure is used to keep track of user readers. To - eventually accommodate this structure for RFID cards, where more - than one card is used per reader, we name it virtual reader. */ -struct vreader_s -{ - int valid; /* True if the other objects are valid. */ - int slot; /* APDU slot number of the reader or -1 if not open. */ - - unsigned int status; /* Last status of the reader. */ -}; +#define IS_LOCKED(c) (locked_session && locked_session != (c)->server_local) /* Data used to associate an Assuan context with local server data. @@ -116,16 +84,10 @@ struct server_local_s int event_signal; /* Or 0 if not used. */ #endif - /* Index into the vreader table (command.c) or -1 if not open. */ - int vreader_idx; - /* True if the card has been removed and a reset is required to continue operation. */ int card_removed; - /* A disconnect command has been sent. */ - int disconnect_allowed; - /* If set to true we will be terminate ourself at the end of the this session. */ int stopme; @@ -133,10 +95,6 @@ struct server_local_s }; -/* The table with information on all used virtual readers. */ -static struct vreader_s vreader_table[10]; - - /* To keep track of all running sessions, we link all active server contexts and the anchor in this variable. */ static struct server_local_s *session_list; @@ -145,88 +103,7 @@ static struct server_local_s *session_list; in this variable. */ static struct server_local_s *locked_session; -/* While doing a reset we need to make sure that the ticker does not - call scd_update_reader_status_file while we are using it. */ -static npth_mutex_t status_file_update_lock; - - -/*-- Local prototypes --*/ -static void update_reader_status_file (int set_card_removed_flag); - - - -/* This function must be called once to initialize this module. This - has to be done before a second thread is spawned. We can't do the - static initialization because Pth emulation code might not be able - to do a static init; in particular, it is not possible for W32. */ -void -initialize_module_command (void) -{ - static int initialized; - int err; - - if (!initialized) - { - err = npth_mutex_init (&status_file_update_lock, NULL); - if (!err) - initialized = 1; - } -} - - -/* Helper to return the slot number for a given virtual reader index - VRDR. In case on an error -1 is returned. */ -static int -vreader_slot (int vrdr) -{ - if (vrdr == -1 || !(vrdr >= 0 && vrdr < DIM(vreader_table))) - return -1; - if (!vreader_table [vrdr].valid) - return -1; - return vreader_table[vrdr].slot; -} - - -/* Update the CARD_REMOVED element of all sessions using the virtual - reader given by VRDR to VALUE. */ -static void -update_card_removed (int vrdr, int value) -{ - struct server_local_s *sl; - - if (vrdr == -1) - return; - - for (sl=session_list; sl; sl = sl->next_session) - { - ctrl_t ctrl = sl->ctrl_backlink; - - if (ctrl && ctrl->server_local->vreader_idx == vrdr) - { - sl->card_removed = value; - if (value) - { - struct app_ctx_s *app = ctrl->app_ctx; - ctrl->app_ctx = NULL; - release_application (app); - } - } - } - - /* Let the card application layer know about the removal. */ - if (value) - { - int slot = vreader_slot (vrdr); - - log_debug ("Removal of a card: %d\n", vrdr); - apdu_close_reader (slot); - application_notify_card_reset (slot); - vreader_table[vrdr].slot = -1; - } -} - - /* Convert the STRING into a newly allocated buffer while translating the hex numbers. Stops at the first invalid character. Blanks and colons are allowed to separate the hex digits. Returns NULL on @@ -265,61 +142,10 @@ hex_to_buffer (const char *string, size_t *r_length) static void do_reset (ctrl_t ctrl, int send_reset) { - int vrdr = ctrl->server_local->vreader_idx; - int slot; - int err; - struct app_ctx_s *app = ctrl->app_ctx; - - if (!(vrdr == -1 || (vrdr >= 0 && vrdr < DIM(vreader_table)))) - BUG (); + app_t app = ctrl->app_ctx; - /* If there is an active application, release it. */ if (app) - { - ctrl->app_ctx = NULL; - release_application (app); - } - - /* Release the same application which is used by other sessions. */ - if (send_reset) - { - struct server_local_s *sl; - - for (sl=session_list; sl; sl = sl->next_session) - { - ctrl_t c = sl->ctrl_backlink; - - if (c && c != ctrl && c->server_local->vreader_idx == vrdr) - { - struct app_ctx_s *app0 = c->app_ctx; - if (app0) - { - c->app_ctx = NULL; - release_application (app0); - } - } - } - } - - /* If we want a real reset for the card, send the reset APDU and - tell the application layer about it. */ - slot = vreader_slot (vrdr); - if (slot != -1 && send_reset && !IS_LOCKED (ctrl) ) - { - application_notify_card_reset (slot); - switch (apdu_reset (slot)) - { - case 0: - break; - case SW_HOST_NO_CARD: - case SW_HOST_CARD_INACTIVE: - break; - default: - apdu_close_reader (slot); - vreader_table[vrdr].slot = -1; - break; - } - } + app_reset (app, ctrl, IS_LOCKED (ctrl)? 0: send_reset); /* If we hold a lock, unlock now. */ if (locked_session && ctrl->server_local == locked_session) @@ -327,30 +153,7 @@ do_reset (ctrl_t ctrl, int send_reset) locked_session = NULL; log_info ("implicitly unlocking due to RESET\n"); } - - /* Reset the card removed flag for the current reader. We need to - take the lock here so that the ticker thread won't concurrently - try to update the file. Calling update_reader_status_file is - required to get hold of the new status of the card in the vreader - table. */ - err = npth_mutex_lock (&status_file_update_lock); - if (err) - { - log_error ("failed to acquire status_file_update lock\n"); - ctrl->server_local->vreader_idx = -1; - return; - } - update_reader_status_file (0); /* Update slot status table. */ - update_card_removed (vrdr, 0); /* Clear card_removed flag. */ - err = npth_mutex_unlock (&status_file_update_lock); - if (err) - log_error ("failed to release status_file_update lock: %s\n", - strerror (err)); - - /* Do this last, so that the update_card_removed above does its job. */ - ctrl->server_local->vreader_idx = -1; } - static gpg_error_t reset_notify (assuan_context_t ctx, char *line) @@ -388,50 +191,11 @@ option_handler (assuan_context_t ctx, const char *key, const char *value) } -/* Return the index of the current reader or open the reader if no - other sessions are using that reader. If it is not possible to - open the reader -1 is returned. Note, that we currently support - only one reader but most of the code (except for this function) - should be able to cope with several readers. */ -static int -get_current_reader (void) -{ - struct vreader_s *vr; - - /* We only support one reader for now. */ - vr = &vreader_table[0]; - - /* Initialize the vreader item if not yet done. */ - if (!vr->valid) - { - vr->slot = -1; - vr->valid = 1; - } - - /* Try to open the reader. */ - if (vr->slot == -1) - { - vr->slot = apdu_open_reader (opt.reader_port); - - /* If we still don't have a slot, we have no readers. - Invalidate for now until a reader is attached. */ - if (vr->slot == -1) - { - vr->valid = 0; - } - } - - /* Return the vreader index or -1. */ - return vr->valid ? 0 : -1; -} - - /* If the card has not yet been opened, do it. */ static gpg_error_t -open_card (ctrl_t ctrl, const char *apptype) +open_card (ctrl_t ctrl, const char *apptype, int scan) { gpg_error_t err; - int vrdr; /* If we ever got a card not present error code, return that. Only the SERIALNO command and a reset are able to clear from that @@ -445,43 +209,11 @@ open_card (ctrl_t ctrl, const char *apptype) /* If we are already initialized for one specific application we need to check that the client didn't requested a specific application different from the one in use before we continue. */ - if (ctrl->app_ctx) - { - return check_application_conflict - (ctrl, vreader_slot (ctrl->server_local->vreader_idx), apptype); - } + if (!scan && ctrl->app_ctx) + return check_application_conflict (apptype, ctrl->app_ctx); - /* Setup the vreader and select the application. */ - if (ctrl->server_local->vreader_idx != -1) - vrdr = ctrl->server_local->vreader_idx; - else - vrdr = get_current_reader (); - ctrl->server_local->vreader_idx = vrdr; - if (vrdr == -1) - err = gpg_error (GPG_ERR_CARD); - else - { - /* Fixme: We should move the apdu_connect call to - select_application. */ - int sw; - int slot = vreader_slot (vrdr); - - ctrl->server_local->disconnect_allowed = 0; - sw = apdu_connect (slot); - if (sw && sw != SW_HOST_ALREADY_CONNECTED) - { - if (sw == SW_HOST_NO_CARD) - err = gpg_error (GPG_ERR_CARD_NOT_PRESENT); - else if (sw == SW_HOST_CARD_INACTIVE) - err = gpg_error (GPG_ERR_CARD_RESET); - else - err = gpg_error (GPG_ERR_ENODEV); - } - else - err = select_application (ctrl, slot, apptype, &ctrl->app_ctx); - } + err = select_application (ctrl, apptype, &ctrl->app_ctx, scan); - TEST_CARD_REMOVAL (ctrl, err); return err; } @@ -509,28 +241,35 @@ static gpg_error_t cmd_serialno (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); + struct server_local_s *sl; int rc = 0; char *serial; time_t stamp; - int retries = 0; /* Clear the remove flag so that the open_card is able to reread it. */ - retry: if (ctrl->server_local->card_removed) { if ( IS_LOCKED (ctrl) ) return gpg_error (GPG_ERR_LOCKED); - do_reset (ctrl, 1); + + ctrl->server_local->card_removed = 0; } - if ((rc = open_card (ctrl, *line? line:NULL))) + if ((rc = open_card (ctrl, *line? line:NULL, 1))) { - /* In case of an inactive card, retry once. */ - if (gpg_err_code (rc) == GPG_ERR_CARD_RESET && retries++ < 1) - goto retry; + ctrl->server_local->card_removed = 1; return rc; } + /* Success, clear the card_removed flag for all sessions. */ + for (sl=session_list; sl; sl = sl->next_session) + { + ctrl_t c = sl->ctrl_backlink; + + if (c != ctrl) + c->server_local->card_removed = 0; + } + rc = app_get_serial_and_stamp (ctrl->app_ctx, &serial, &stamp); if (rc) return rc; @@ -618,7 +357,7 @@ cmd_learn (assuan_context_t ctx, char *line) int rc = 0; int only_keypairinfo = has_option (line, "--keypairinfo"); - if ((rc = open_card (ctrl, NULL))) + if ((rc = open_card (ctrl, NULL, 0))) return rc; /* Unless the force option is used we try a shortcut by identifying @@ -627,13 +366,15 @@ cmd_learn (assuan_context_t ctx, char *line) knows about this card */ if (!only_keypairinfo) { - int slot; const char *reader; char *serial; time_t stamp; + app_t app = ctrl->app_ctx; + + if (!app) + return gpg_error (GPG_ERR_CARD_NOT_PRESENT); - slot = vreader_slot (ctrl->server_local->vreader_idx); - reader = apdu_get_reader_name (slot); + reader = apdu_get_reader_name (app->slot); if (!reader) return out_of_core (); send_status_direct (ctrl, "READER", reader); @@ -682,7 +423,6 @@ cmd_learn (assuan_context_t ctx, char *line) if (!rc) rc = app_write_learn_status (ctrl->app_ctx, ctrl, only_keypairinfo); - TEST_CARD_REMOVAL (ctrl, rc); return rc; } @@ -700,7 +440,7 @@ cmd_readcert (assuan_context_t ctx, char *line) unsigned char *cert; size_t ncert; - if ((rc = open_card (ctrl, NULL))) + if ((rc = open_card (ctrl, NULL, 0))) return rc; line = xstrdup (line); /* Need a copy of the line. */ @@ -717,7 +457,6 @@ cmd_readcert (assuan_context_t ctx, char *line) return rc; } - TEST_CARD_REMOVAL (ctrl, rc); return rc; } @@ -743,7 +482,7 @@ cmd_readkey (assuan_context_t ctx, char *line) unsigned char *pk; size_t pklen; - if ((rc = open_card (ctrl, NULL))) + if ((rc = open_card (ctrl, NULL, 0))) return rc; if (has_option (line, "--advanced")) @@ -804,7 +543,6 @@ cmd_readkey (assuan_context_t ctx, char *line) leave: ksba_cert_release (kc); xfree (cert); - TEST_CARD_REMOVAL (ctrl, rc); return rc; } @@ -966,10 +704,7 @@ cmd_pksign (assuan_context_t ctx, char *line) line = skip_options (line); - if ( IS_LOCKED (ctrl) ) - return gpg_error (GPG_ERR_LOCKED); - - if ((rc = open_card (ctrl, NULL))) + if ((rc = open_card (ctrl, NULL, 0))) return rc; /* We have to use a copy of the key ID because the function may use @@ -998,7 +733,6 @@ cmd_pksign (assuan_context_t ctx, char *line) return rc; /* that is already an assuan error code */ } - TEST_CARD_REMOVAL (ctrl, rc); return rc; } @@ -1014,10 +748,7 @@ cmd_pkauth (assuan_context_t ctx, char *line) size_t outdatalen; char *keyidstr; - if ( IS_LOCKED (ctrl) ) - return gpg_error (GPG_ERR_LOCKED); - - if ((rc = open_card (ctrl, NULL))) + if ((rc = open_card (ctrl, NULL, 0))) return rc; if (!ctrl->app_ctx) @@ -1046,7 +777,6 @@ cmd_pkauth (assuan_context_t ctx, char *line) return rc; /* that is already an assuan error code */ } - TEST_CARD_REMOVAL (ctrl, rc); return rc; } @@ -1063,10 +793,7 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line) char *keyidstr; unsigned int infoflags; - if ( IS_LOCKED (ctrl) ) - return gpg_error (GPG_ERR_LOCKED); - - if ((rc = open_card (ctrl, NULL))) + if ((rc = open_card (ctrl, NULL, 0))) return rc; keyidstr = xtrystrdup (line); @@ -1096,7 +823,6 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line) return rc; /* that is already an assuan error code */ } - TEST_CARD_REMOVAL (ctrl, rc); return rc; } @@ -1120,7 +846,7 @@ cmd_getattr (assuan_context_t ctx, char *line) int rc; const char *keyword; - if ((rc = open_card (ctrl, NULL))) + if ((rc = open_card (ctrl, NULL, 0))) return rc; keyword = line; @@ -1135,7 +861,6 @@ cmd_getattr (assuan_context_t ctx, char *line) is locked. */ rc = app_getattr (ctrl->app_ctx, ctrl, keyword); - TEST_CARD_REMOVAL (ctrl, rc); return rc; } @@ -1163,10 +888,7 @@ cmd_setattr (assuan_context_t ctx, char *orig_line) size_t nbytes; char *line, *linebuf; - if ( IS_LOCKED (ctrl) ) - return gpg_error (GPG_ERR_LOCKED); - - if ((rc = open_card (ctrl, NULL))) + if ((rc = open_card (ctrl, NULL, 0))) return rc; /* We need to use a copy of LINE, because PIN_CB uses the same @@ -1188,7 +910,6 @@ cmd_setattr (assuan_context_t ctx, char *orig_line) (const unsigned char*)line, nbytes); xfree (linebuf); - TEST_CARD_REMOVAL (ctrl, rc); return rc; } @@ -1213,9 +934,6 @@ cmd_writecert (assuan_context_t ctx, char *line) unsigned char *certdata; size_t certdatalen; - if ( IS_LOCKED (ctrl) ) - return gpg_error (GPG_ERR_LOCKED); - line = skip_options (line); if (!*line) @@ -1225,7 +943,7 @@ cmd_writecert (assuan_context_t ctx, char *line) line++; *line = 0; - if ((rc = open_card (ctrl, NULL))) + if ((rc = open_card (ctrl, NULL, 0))) return rc; if (!ctrl->app_ctx) @@ -1250,7 +968,6 @@ cmd_writecert (assuan_context_t ctx, char *line) xfree (certid); xfree (certdata); - TEST_CARD_REMOVAL (ctrl, rc); return rc; } @@ -1279,9 +996,6 @@ cmd_writekey (assuan_context_t ctx, char *line) unsigned char *keydata; size_t keydatalen; - if ( IS_LOCKED (ctrl) ) - return gpg_error (GPG_ERR_LOCKED); - line = skip_options (line); if (!*line) @@ -1291,7 +1005,7 @@ cmd_writekey (assuan_context_t ctx, char *line) line++; *line = 0; - if ((rc = open_card (ctrl, NULL))) + if ((rc = open_card (ctrl, NULL, 0))) return rc; if (!ctrl->app_ctx) @@ -1317,7 +1031,6 @@ cmd_writekey (assuan_context_t ctx, char *line) xfree (keyid); xfree (keydata); - TEST_CARD_REMOVAL (ctrl, rc); return rc; } @@ -1357,9 +1070,6 @@ cmd_genkey (assuan_context_t ctx, char *line) const char *s; time_t timestamp; - if ( IS_LOCKED (ctrl) ) - return gpg_error (GPG_ERR_LOCKED); - force = has_option (line, "--force"); if ((s=has_option_name (line, "--timestamp"))) @@ -1382,7 +1092,7 @@ cmd_genkey (assuan_context_t ctx, char *line) line++; *line = 0; - if ((rc = open_card (ctrl, NULL))) + if ((rc = open_card (ctrl, NULL, 0))) return rc; if (!ctrl->app_ctx) @@ -1395,7 +1105,6 @@ cmd_genkey (assuan_context_t ctx, char *line) timestamp, pin_cb, ctx); xfree (keyno); - TEST_CARD_REMOVAL (ctrl, rc); return rc; } @@ -1421,7 +1130,7 @@ cmd_random (assuan_context_t ctx, char *line) "number of requested bytes missing"); nbytes = strtoul (line, NULL, 0); - if ((rc = open_card (ctrl, NULL))) + if ((rc = open_card (ctrl, NULL, 0))) return rc; if (!ctrl->app_ctx) @@ -1440,7 +1149,6 @@ cmd_random (assuan_context_t ctx, char *line) } xfree (buffer); - TEST_CARD_REMOVAL (ctrl, rc); return rc; } @@ -1466,9 +1174,6 @@ cmd_passwd (assuan_context_t ctx, char *line) if (has_option (line, "--nullpin")) flags |= APP_CHANGE_FLAG_NULLPIN; - if ( IS_LOCKED (ctrl) ) - return gpg_error (GPG_ERR_LOCKED); - line = skip_options (line); if (!*line) @@ -1478,7 +1183,7 @@ cmd_passwd (assuan_context_t ctx, char *line) line++; *line = 0; - if ((rc = open_card (ctrl, NULL))) + if ((rc = open_card (ctrl, NULL, 0))) return rc; if (!ctrl->app_ctx) @@ -1492,7 +1197,6 @@ cmd_passwd (assuan_context_t ctx, char *line) log_error ("command passwd failed: %s\n", gpg_strerror (rc)); xfree (chvnostr); - TEST_CARD_REMOVAL (ctrl, rc); return rc; } @@ -1536,10 +1240,7 @@ cmd_checkpin (assuan_context_t ctx, char *line) int rc; char *idstr; - if ( IS_LOCKED (ctrl) ) - return gpg_error (GPG_ERR_LOCKED); - - if ((rc = open_card (ctrl, NULL))) + if ((rc = open_card (ctrl, NULL, 0))) return rc; if (!ctrl->app_ctx) @@ -1557,7 +1258,6 @@ cmd_checkpin (assuan_context_t ctx, char *line) if (rc) log_error ("app_check_pin failed: %s\n", gpg_strerror (rc)); - TEST_CARD_REMOVAL (ctrl, rc); return rc; } @@ -1592,8 +1292,8 @@ cmd_lock (assuan_context_t ctx, char *line) { rc = 0; npth_sleep (1); /* Better implement an event mechanism. However, - for card operations this should be - sufficient. */ + for card operations this should be + sufficient. */ /* FIXME: Need to check that the connection is still alive. This can be done by issuing status messages. */ goto retry; @@ -1690,20 +1390,12 @@ cmd_getinfo (assuan_context_t ctx, char *line) else if (!strcmp (line, "status")) { ctrl_t ctrl = assuan_get_pointer (ctx); - int vrdr = ctrl->server_local->vreader_idx; + app_t app = ctrl->app_ctx; char flag = 'r'; - if (!ctrl->server_local->card_removed && vrdr != -1) - { - struct vreader_s *vr; - - if (!(vrdr >= 0 && vrdr < DIM(vreader_table))) - BUG (); + if (!ctrl->server_local->card_removed && app) + flag = 'u'; - vr = &vreader_table[vrdr]; - if (vr->valid && (vr->status & 1)) - flag = 'u'; - } rc = assuan_send_data (ctx, &flag, 1); } else if (!strcmp (line, "reader_list")) @@ -1751,7 +1443,7 @@ static gpg_error_t cmd_restart (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); - struct app_ctx_s *app = ctrl->app_ctx; + app_t app = ctrl->app_ctx; (void)line; @@ -1772,8 +1464,7 @@ cmd_restart (assuan_context_t ctx, char *line) static const char hlp_disconnect[] = "DISCONNECT\n" "\n" - "Disconnect the card if it is not any longer used by other\n" - "connections and the backend supports a disconnect operation."; + "Disconnect the card if the backend supports a disconnect operation."; static gpg_error_t cmd_disconnect (assuan_context_t ctx, char *line) { @@ -1781,7 +1472,10 @@ cmd_disconnect (assuan_context_t ctx, char *line) (void)line; - ctrl->server_local->disconnect_allowed = 1; + if (!ctrl->app_ctx) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + + apdu_disconnect (ctrl->app_ctx->slot); return 0; } @@ -1810,6 +1504,7 @@ static gpg_error_t cmd_apdu (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); + app_t app; int rc; unsigned char *apdu; size_t apdulen; @@ -1817,7 +1512,6 @@ cmd_apdu (assuan_context_t ctx, char *line) int handle_more; const char *s; size_t exlen; - int slot; if (has_option (line, "--dump-atr")) with_atr = 2; @@ -1837,13 +1531,12 @@ cmd_apdu (assuan_context_t ctx, char *line) line = skip_options (line); - if ( IS_LOCKED (ctrl) ) - return gpg_error (GPG_ERR_LOCKED); - - if ((rc = open_card (ctrl, NULL))) + if ((rc = open_card (ctrl, NULL, 0))) return rc; - slot = vreader_slot (ctrl->server_local->vreader_idx); + app = ctrl->app_ctx; + if (!app) + return gpg_error (GPG_ERR_CARD_NOT_PRESENT); if (with_atr) { @@ -1851,7 +1544,7 @@ cmd_apdu (assuan_context_t ctx, char *line) size_t atrlen; char hexbuf[400]; - atr = apdu_get_atr (slot, &atrlen); + atr = apdu_get_atr (app->slot, &atrlen); if (!atr || atrlen > sizeof hexbuf - 2 ) { rc = gpg_error (GPG_ERR_INV_CARD); @@ -1896,7 +1589,7 @@ cmd_apdu (assuan_context_t ctx, char *line) unsigned char *result = NULL; size_t resultlen; - rc = apdu_send_direct (slot, exlen, + rc = apdu_send_direct (app->slot, exlen, apdu, apdulen, handle_more, &result, &resultlen); if (rc) @@ -1910,7 +1603,6 @@ cmd_apdu (assuan_context_t ctx, char *line) xfree (apdu); leave: - TEST_CARD_REMOVAL (ctrl, rc); return rc; } @@ -2015,7 +1707,7 @@ scd_command_handler (ctrl_t ctrl, int fd) else { rc = assuan_init_socket_server (ctx, INT2FD(fd), - ASSUAN_SOCKET_SERVER_ACCEPTED); + ASSUAN_SOCKET_SERVER_ACCEPTED); } if (rc) { @@ -2040,10 +1732,6 @@ scd_command_handler (ctrl_t ctrl, int fd) ctrl->server_local->ctrl_backlink = ctrl; ctrl->server_local->assuan_ctx = ctx; - /* We open the reader right at startup so that the ticker is able to - update the status file. */ - ctrl->server_local->vreader_idx = get_current_reader (); - /* Command processing loop. */ for (;;) { @@ -2160,8 +1848,8 @@ send_status_direct (ctrl_t ctrl, const char *keyword, const char *args) /* Helper to send the clients a status change notification. */ -static void -send_client_notifications (void) +void +send_client_notifications (app_t app) { struct { pid_t pid; @@ -2177,7 +1865,8 @@ send_client_notifications (void) for (sl=session_list; sl; sl = sl->next_session) { - if (sl->event_signal && sl->assuan_ctx) + if (sl->event_signal && sl->assuan_ctx + && sl->ctrl_backlink->app_ctx == app) { pid_t pid = assuan_get_pid (sl->assuan_ctx); #ifdef HAVE_W32_SYSTEM @@ -2230,153 +1919,10 @@ send_client_notifications (void) } } #endif /*!HAVE_W32_SYSTEM*/ - } - } -} - - - -/* This is the core of scd_update_reader_status_file but the caller - needs to take care of the locking. */ -static void -update_reader_status_file (int set_card_removed_flag) -{ - int idx; - unsigned int status; - - /* Note, that we only try to get the status, because it does not - make sense to wait here for a operation to complete. If we are - busy working with a card, delays in the status file update should - be acceptable. */ - for (idx=0; idx < DIM(vreader_table); idx++) - { - struct vreader_s *vr = vreader_table + idx; - struct server_local_s *sl; - int sw_apdu; - if (!vr->valid || vr->slot == -1) - continue; /* Not valid or reader not yet open. */ - - sw_apdu = apdu_get_status (vr->slot, 0, &status); - if (sw_apdu == SW_HOST_NO_READER) - { - /* Most likely the _reader_ has been unplugged. */ - status = 0; - } - else if (sw_apdu) - { - /* Get status failed. Ignore that. */ - continue; + sl->ctrl_backlink->app_ctx = NULL; + sl->card_removed = 1; + release_application (app); } - - if (vr->status != status) - { - char *fname; - char templ[50]; - FILE *fp; - - log_info ("updating reader %d (%d) status: 0x%04X->0x%04X\n", - idx, vr->slot, vr->status, status); - - /* FIXME: Should this be IDX instead of vr->slot? This - depends on how client sessions will associate the reader - status with their session. */ - snprintf (templ, sizeof templ, "reader_%d.status", vr->slot); - fname = make_filename (gnupg_homedir (), templ, NULL ); - fp = fopen (fname, "w"); - if (fp) - { - fprintf (fp, "%s\n", - (status & 1)? "USABLE": - (status & 4)? "ACTIVE": - (status & 2)? "PRESENT": "NOCARD"); - fclose (fp); - } - xfree (fname); - - /* If a status script is executable, run it. */ - { - const char *args[9], *envs[2]; - char numbuf1[30], numbuf2[30], numbuf3[30]; - char *homestr, *envstr; - gpg_error_t err; - - homestr = make_filename (gnupg_homedir (), NULL); - if (gpgrt_asprintf (&envstr, "GNUPGHOME=%s", homestr) < 0) - log_error ("out of core while building environment\n"); - else - { - envs[0] = envstr; - envs[1] = NULL; - - sprintf (numbuf1, "%d", vr->slot); - sprintf (numbuf2, "0x%04X", vr->status); - sprintf (numbuf3, "0x%04X", status); - args[0] = "--reader-port"; - args[1] = numbuf1; - args[2] = "--old-code"; - args[3] = numbuf2; - args[4] = "--new-code"; - args[5] = numbuf3; - args[6] = "--status"; - args[7] = ((status & 1)? "USABLE": - (status & 4)? "ACTIVE": - (status & 2)? "PRESENT": "NOCARD"); - args[8] = NULL; - - fname = make_filename (gnupg_homedir (), "scd-event", NULL); - err = gnupg_spawn_process_detached (fname, args, envs); - if (err && gpg_err_code (err) != GPG_ERR_ENOENT) - log_error ("failed to run event handler '%s': %s\n", - fname, gpg_strerror (err)); - xfree (fname); - xfree (envstr); - } - xfree (homestr); - } - - /* Set the card removed flag for all current sessions. */ - if (status == 0 && set_card_removed_flag) - update_card_removed (idx, 1); - - vr->status = status; - - /* Send a signal to all clients who applied for it. */ - send_client_notifications (); - } - - /* Check whether a disconnect is pending. */ - if (opt.card_timeout) - { - for (sl=session_list; sl; sl = sl->next_session) - if (!sl->disconnect_allowed) - break; - if (session_list && !sl) - { - /* FIXME: Use a real timeout. */ - /* At least one connection and all allow a disconnect. */ - log_info ("disconnecting card in reader %d (%d)\n", - idx, vr->slot); - apdu_disconnect (vr->slot); - } - } - } } - -/* This function is called by the ticker thread to check for changes - of the reader stati. It updates the reader status files and if - requested by the caller also send a signal to the caller. */ -void -scd_update_reader_status_file (void) -{ - int err; - err = npth_mutex_lock (&status_file_update_lock); - if (err) - return; /* locked - give up. */ - update_reader_status_file (1); - err = npth_mutex_unlock (&status_file_update_lock); - if (err) - log_error ("failed to release status_file_update lock: %s\n", - strerror (err)); -} diff --git a/scd/scdaemon.h b/scd/scdaemon.h index 38aa4905b..e18ebfe4c 100644 --- a/scd/scdaemon.h +++ b/scd/scdaemon.h @@ -124,6 +124,7 @@ void send_status_info (ctrl_t ctrl, const char *keyword, ...) GPGRT_ATTR_SENTINEL(1); void send_status_direct (ctrl_t ctrl, const char *keyword, const char *args); void scd_update_reader_status_file (void); +void send_client_notifications (app_t app); #endif /*SCDAEMON_H*/ |