1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-12-22 10:19:57 +01:00

scd: APP centric approach for device management.

* scd/app.c (lock_app): Rename from lock_reader and use internal field
of APP.
(unlock_app): Likewise.
(app_dump_state): Use APP.
(application_notify_card_reset): Remove.
(check_conflict): Change API for APP, instead of SLOT.
(check_application_conflict): Likewise.
(release_application_internal): New.
(app_reset): New.
(app_new_register): New.
(select_application): Change API for APP, instead of SLOT.
(deallocate_app, release_application): Modify for manage link.
(report_change): New.
(scd_update_reader_status_file): Moved from command.c and
use APP list, instead of VREADER.
(initialize_module_command): Moved from command.c.

* scd/command.c (TEST_CARD_REMOVAL): Remove.
(IS_LOCKED): Simplify.
(vreader_table): Remove.
(vreader_slot, update_card_removed): Remove.
(do_reset): Call app_reset.
(get_current_reader): Remove.
(open_card): Add SCAN arg.
(cmd_serialno): No retry, since retry is done in lower layer in apdu.c.
No do_reset, since it is done in lower layer.
Add clearing card_removed flag.
(cmd_disconnect): Call apdu_disconnect.
(send_client_notifications): Modify for APP.
(update_reader_status_file): Remove.

--

APP is the abstraction of the card application.  For management of
cards, it is better to focus on the APP instead of the physical reader.
This change makes support of multiple card/token easier.

Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
This commit is contained in:
NIIBE Yutaka 2016-12-28 12:29:17 +09:00
parent f9882d8336
commit 4cc9fc5eb9
4 changed files with 410 additions and 773 deletions

View File

@ -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*/

547
scd/app.c
View File

@ -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;
gpg_error_t err;
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)
err = npth_mutex_lock (&app->lock);
if (err)
{
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;
log_error ("failed to acquire APP lock for %p: %s\n",
app, strerror (err));
return gpg_error_from_errno (err);
}
res = npth_mutex_lock (&lock_table[slot].lock);
if (res)
{
log_error ("failed to acquire APP lock for slot %d: %s\n",
slot, strerror (res));
return gpg_error_from_errno (res);
}
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;
gpg_error_t err;
if (slot < 0 || slot >= DIM (lock_table)
|| !lock_table[slot].initialized)
log_bug ("unlock_reader called for invalid slot %d\n", slot);
apdu_set_progress_cb (app->slot, NULL, NULL);
apdu_set_progress_cb (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;
if (slot < 0 || slot >= DIM (lock_table))
return gpg_error (GPG_ERR_INV_VALUE);
err = lock_reader (slot, ctrl);
if (err)
return err;
err = check_conflict (slot, name);
unlock_reader (slot);
return err;
return check_conflict (app, name);
}
/* 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. */
static void
release_application_internal (app_t app)
{
if (!app->ref_count)
log_bug ("trying to release an already released context\n");
--app->ref_count;
}
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;
err = lock_app (app, ctrl);
if (err)
return err;
if (send_reset)
{
int sw = apdu_reset (app->slot);
if (sw)
err = gpg_error (GPG_ERR_CARD_RESET);
/* Release the same application which is used by other sessions. */
send_client_notifications (app);
}
else
{
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;
(void)ctrl;
*r_app = NULL;
want_undefined = (name && !strcmp (name, "undefined"));
err = lock_reader (slot, ctrl);
if (err)
return err;
/* First check whether we already have an application to share. */
err = check_conflict (slot, name);
if (err)
{
unlock_reader (slot);
return err;
}
app = lock_table[slot].app;
/* If we can reuse an application, bump the reference count and
return it. */
if (app)
{
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. */
}
/* 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;
}
}

View File

@ -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;
app_t app = ctrl->app_ctx;
if (!(vrdr == -1 || (vrdr >= 0 && vrdr < DIM(vreader_table))))
BUG ();
/* 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);
err = select_application (ctrl, apptype, &ctrl->app_ctx, scan);
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);
}
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;
slot = vreader_slot (ctrl->server_local->vreader_idx);
reader = apdu_get_reader_name (slot);
if (!app)
return gpg_error (GPG_ERR_CARD_NOT_PRESENT);
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 (!ctrl->server_local->card_removed && app)
flag = 'u';
if (!(vrdr >= 0 && vrdr < DIM(vreader_table)))
BUG ();
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*/
sl->ctrl_backlink->app_ctx = NULL;
sl->card_removed = 1;
release_application (app);
}
}
}
/* 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;
}
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));
}

View File

@ -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*/