aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--scd/app-common.h47
-rw-r--r--scd/app.c535
-rw-r--r--scd/command.c586
-rw-r--r--scd/scdaemon.h1
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*/
diff --git a/scd/app.c b/scd/app.c
index 6868cc3f2..a78d6525a 100644
--- a/scd/app.c
+++ b/scd/app.c
@@ -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*/