1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-05-31 22:18:03 +02:00

(retrieve_key_material): Rewritten. Return a

proper error code.
(retrieve_next_token): Removed.
(retrieve_fpr_from_card): Rewritten to make use of DO caching and
to take the KEYNO as arg.
(get_public_key): Renamed variable for clarity.
This commit is contained in:
Werner Koch 2005-04-14 17:25:43 +00:00
parent f084afaeda
commit 9f9a18c011
3 changed files with 443 additions and 332 deletions

View File

@ -1,3 +1,31 @@
2005-04-14 Werner Koch <wk@g10code.com>
* app-openpgp.c (retrieve_key_material): Rewritten. Return a
proper error code.
(retrieve_next_token): Removed.
(retrieve_fpr_from_card): Rewritten to make use of DO caching and
to take the KEYNO as arg.
(get_public_key): Renamed variable for clarity.
2005-04-12 Werner Koch <wk@g10code.com>
Basic support for several sessions.
* command.c (scd_command_handler): Replace the primary_connection
stuff by a real connection list. Release the local context on
exit.
(scd_update_reader_status_file): Update accordingly. Send signal
to all connections who registered an event signal.
(cmd_lock, cmd_unlock, register_commands): New commands LOCK and
UNLOCK.
(cmd_setdata, cmd_pksign, cmd_pkauth, cmd_pkdecrypt, cmd_setattr)
(cmd_genkey, cmd_passwd, cmd_checkpin): Return an error if reader
is locked.
(do_reset): Handle locking.
(open_card): Ditto. Share the reader slot with other sessions.
(get_reader_slot): New.
(update_card_removed): New. Use it in the TEST_CARD_REMOVAL macro.
2005-04-07 Werner Koch <wk@g10code.com> 2005-04-07 Werner Koch <wk@g10code.com>
* app-openpgp.c (do_check_pin): Add hack to allow verification of * app-openpgp.c (do_check_pin): Add hack to allow verification of

View File

@ -784,266 +784,149 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
} }
/* Retrieve the fingerprint from the card inserted in SLOT and write /* Retrieve the fingerprint from the card inserted in SLOT and write
the according hex representation (40 hex digits plus NUL character) the according hex representation to FPR. Caller must have provide
to FPR. */ a buffer at FPR of least 41 bytes. Returns 0 on success or an
error code. */
#if GNUPG_MAJOR_VERSION > 1
static gpg_error_t static gpg_error_t
retrieve_fpr_from_card (int slot, char *fpr) retrieve_fpr_from_card (app_t app, int keyno, char *fpr)
{ {
const unsigned char *value; gpg_error_t err = 0;
unsigned char *data; void *relptr;
size_t data_n; unsigned char *value;
gpg_error_t err; size_t valuelen;
size_t value_n; int i;
unsigned int i;
data = NULL; assert (keyno >=0 && keyno <= 2);
err = iso7816_get_data (slot, 0x6E, &data, &data_n); relptr = get_one_do (app, 0x00C5, &value, &valuelen, NULL);
if (err) if (relptr && valuelen >= 60)
/* FIXME */
goto out;
value = find_tlv (data, data_n, 0x00C5, &value_n);
if (! (value
&& (! (value_n > (data_n - (value - data))))
&& (value_n >= 60))) /* FIXME: Shouldn't this be "== 60"? */
{ {
/* FIXME? */ for (i = 0; i < 20; i++)
err = gpg_error (GPG_ERR_CARD); /* */ sprintf (fpr + (i * 2), "%02X", value[(keyno*20)+i]);
goto out;
} }
else
/* Copy out third key FPR. */ err = gpg_error (GPG_ERR_NOT_FOUND);
for (i = 0; i < 20; i++) xfree (relptr);
sprintf (fpr + (i * 2), "%02X", (value + (2 * 20))[i]);
out:
xfree (data);
return err; return err;
} }
#endif /*GNUPG_MAJOR_VERSION > 1*/
/* Retrieve the next token from S, using ":" as delimiter. */
static char *
retrieve_next_token (char *s)
{
char *p;
p = strtok (s, ":"); /* Retrieve the public key material for the RSA key, whose fingerprint
if (! p) is FPR, from gpg output, which can be read through the stream FP.
log_error ("error while extracting token\n"); The RSA modulus will be stored at the address of M and MLEN, the
public exponent at E and ELEN. Returns zero on success, an error
return p; code on failure. Caller must release the allocated buffers at M
} and E if the function returns success. */
#if GNUPG_MAJOR_VERSION > 1
/* Retrieve the secret key material for the key, whose fingerprint is static gpg_error_t
FPR, from gpg output, which can be read through the stream FP. The retrieve_key_material (FILE *fp, const char *hexkeyid,
RSA modulus will be stored in m/mlen, the secret exponent in
e/elen. Return zero on success, one on failure. */
static int
retrieve_key_material (FILE *fp, const char *fpr,
const unsigned char **m, size_t *mlen, const unsigned char **m, size_t *mlen,
const unsigned char **e, size_t *elen) const unsigned char **e, size_t *elen)
{ {
size_t line_size; gcry_error_t err = 0;
ssize_t line_ret; char *line = NULL; /* read_line() buffer. */
char *line; size_t line_size = 0; /* Helper for for read_line. */
int ret; int found_key = 0; /* Helper to find a matching key. */
int found_key; unsigned char *m_new = NULL;
char *token; unsigned char *e_new = NULL;
int pkd_n; size_t m_new_n = 0;
unsigned char *m_new; size_t e_new_n = 0;
unsigned char *e_new;
size_t m_new_n;
size_t e_new_n;
int is_rsa;
gcry_mpi_t mpi;
gcry_error_t err;
size_t max_length;
line_size = 0; /* Loop over all records until we have found the subkey
line = NULL; corresponsing to the fingerprint. Inm general the first record
found_key = 0; should be the pub record, but we don't rely on that. Given that
pkd_n = 0; we only need to look at one key, it is sufficient to compare the
m_new = NULL; keyid so that we don't need to look at "fpr" records. */
e_new = NULL; for (;;)
mpi = NULL;
ret = 0;
#warning This part should get rewritten for clarity
/* We should use an algorithm similar to the one used by gpgme.
This will reduce the size of the code at least by 50%. [wk] */
while (1)
{ {
/* FIXME? */ char *p;
max_length = 1024; char *fields[6];
line_ret = read_line (fp, &line, &line_size, &max_length); int nfields;
if (line_ret < 0) size_t max_length;
gcry_mpi_t mpi;
int i;
max_length = 4096;
i = read_line (fp, &line, &line_size, &max_length);
if (!i)
break; /* EOF. */
if (i < 0)
{ {
ret = 1; err = gpg_error_from_errno (errno);
break; goto leave; /* Error. */
} }
if (! line_ret) if (!max_length)
/* EOF. */ {
/* FIXME? */ err = gpg_error (GPG_ERR_TRUNCATED);
break; goto leave; /* Line truncated - we better stop processing. */
}
token = retrieve_next_token (line); /* Parse the line into fields. */
if (! found_key) for (nfields=0, p=line; p && nfields < DIM (fields); nfields++)
{ {
/* Key not found yet, search for key entry. */ fields[nfields] = p;
if ((! strcmp (token, "pub")) || (! strcmp (token, "sub"))) p = strchr (p, ':');
{ if (p)
/* Reached next key entry, parse it. */ *(p++) = 0;
}
if (!nfields)
continue; /* No fields at all - skip line. */
/* This is the trust level (right, FIXME?). */ if (!found_key)
token = retrieve_next_token (NULL); {
if (! token) if ( (!strcmp (fields[0], "sub") || !strcmp (fields[0], "pub") )
{ && nfields > 4 && !strcmp (fields[4], hexkeyid))
ret = 1; found_key = 1;
break; continue;
} }
if ( !strcmp (fields[0], "sub") || !strcmp (fields[0], "pub") )
break; /* Next key - stop. */
/* This is the size. */ if ( strcmp (fields[0], "pkd") )
token = retrieve_next_token (NULL); continue; /* Not a key data record. */
if (! token) i = 0; /* Avoid erroneous compiler warning. */
{ if ( nfields < 4 || (i = atoi (fields[1])) < 0 || i > 1
ret = 1; || (!i && m_new) || (i && e_new))
break; {
} err = gpg_error (GPG_ERR_GENERAL);
goto leave; /* Error: Invalid key data record or not an RSA key. */
/* This is the algorithm (right, FIXME?). */ }
token = retrieve_next_token (NULL);
if (! token) err = gcry_mpi_scan (&mpi, GCRYMPI_FMT_HEX, fields[3], 0, NULL);
{ if (err)
ret = 1; mpi = NULL;
break; else if (!i)
} err = gcry_mpi_aprint (GCRYMPI_FMT_STD, &m_new, &m_new_n, mpi);
is_rsa = ! strcmp (token, "1");
/* This is the fingerprint. */
token = retrieve_next_token (NULL);
if (! token)
{
ret = 1;
break;
}
if (! strcmp (token, fpr))
{
/* Found our key. */
if (! is_rsa)
{
/* FIXME. */
ret = 1;
break;
}
found_key = 1;
}
}
}
else else
{ err = gcry_mpi_aprint (GCRYMPI_FMT_STD, &e_new, &e_new_n, mpi);
if (! strcmp (token, "sub")) gcry_mpi_release (mpi);
/* Next key entry, break. */ if (err)
break; goto leave;
if (! strcmp (token, "pkd"))
{
if ((pkd_n == 0) || (pkd_n == 1))
{
/* This is the pkd index. */
token = retrieve_next_token (NULL);
if (! token)
{
/* FIXME. */
ret = 1;
break;
}
/* This is the pkd size. */
token = retrieve_next_token (NULL);
if (! token)
{
/* FIXME. */
ret = 1;
break;
}
/* This is the pkd mpi. */
token = retrieve_next_token (NULL);
if (! token)
{
/* FIXME. */
ret = 1;
break;
}
err = gcry_mpi_scan (&mpi, GCRYMPI_FMT_HEX, token, 0, NULL);
if (err)
{
log_error ("error while converting pkd %i from hex: %s\n",
pkd_n, gcry_strerror (err));
ret = 1;
break;
}
if (pkd_n == 0)
err = gcry_mpi_aprint (GCRYMPI_FMT_STD,
&m_new, &m_new_n, mpi);
else
err = gcry_mpi_aprint (GCRYMPI_FMT_STD,
&e_new, &e_new_n, mpi);
if (err)
{
log_error ("error while converting pkd %i to std: %s\n",
pkd_n, gcry_strerror (err));
ret = 1;
break;
}
gcry_mpi_release (mpi);
mpi = NULL;
pkd_n++;
}
else
{
/* Too many pkd entries. */
/* FIXME */
ret = 1;
break;
}
}
}
} }
if (ret)
goto out; if (m_new && e_new)
if (pkd_n < 2)
{ {
/* Not enough pkds retrieved. */ *m = m_new;
ret = 1; *mlen = m_new_n;
goto out; m_new = NULL;
*e = e_new;
*elen = e_new_n;
e_new = NULL;
} }
else
err = gpg_error (GPG_ERR_GENERAL);
*m = m_new; leave:
*mlen = m_new_n; xfree (m_new);
*e = e_new; xfree (e_new);
*elen = e_new_n; xfree (line);
return err;
out:
if (ret)
{
gcry_free (m_new);
gcry_free (e_new);
}
gcry_mpi_release (mpi);
gcry_free (line);
return ret;
} }
#endif /*GNUPG_MAJOR_VERSION > 1*/
/* Get the public key for KEYNO and store it as an S-expresion with /* Get the public key for KEYNO and store it as an S-expresion with
the APP handle. On error that field gets cleared. If we already the APP handle. On error that field gets cleared. If we already
@ -1158,52 +1041,49 @@ get_public_key (app_t app, int keyno)
The helper we use here is gpg itself, which should know about The helper we use here is gpg itself, which should know about
the key in any case. */ the key in any case. */
char fpr_long[41]; char fpr[41];
char *fpr = fpr_long + 24; char *hexkeyid;
char *command; char *command = NULL;
FILE *fp; FILE *fp;
int ret; int ret;
command = NULL; buffer = NULL; /* We don't need buffer. */
err = retrieve_fpr_from_card (app->slot, fpr_long); err = retrieve_fpr_from_card (app, keyno, fpr);
if (err) if (err)
{ {
log_error ("error while retrieving fpr from card: %s\n", log_error ("error while retrieving fpr from card: %s\n",
gpg_strerror (err)); gpg_strerror (err));
goto leave; goto leave;
} }
hexkeyid = fpr + 24;
ret = asprintf (&command, ret = asprintf (&command,
"gpg --list-keys --with-colons --with-key-data '%s'", "gpg --list-keys --with-colons --with-key-data '%s'",
fpr_long); fpr);
if (ret < 0) if (ret < 0)
{ {
err = gpg_error_from_errno (errno); err = gpg_error_from_errno (errno);
log_error ("error while creating pipe command "
"for retrieving key: %s\n", gpg_strerror (err));
goto leave; goto leave;
} }
fp = popen (command, "r"); fp = popen (command, "r");
if (! fp) free (command);
if (!fp)
{ {
err = gpg_error_from_errno (errno); err = gpg_error_from_errno (errno);
log_error ("error while creating pipe: %s\n", gpg_strerror (err)); log_error ("running gpg failed: %s\n", gpg_strerror (err));
goto leave; goto leave;
} }
ret = retrieve_key_material (fp, fpr, &m, &mlen, &e, &elen); err = retrieve_key_material (fp, hexkeyid, &m, &mlen, &e, &elen);
fclose (fp); fclose (fp);
if (ret) if (err)
{ {
/* FIXME? */ log_error ("error while retrieving key material through pipe: %s\n",
err = gpg_error (GPG_ERR_INTERNAL); gpg_strerror (err));
log_error ("error while retrieving key material through pipe\n");
goto leave; goto leave;
} }
buffer = NULL;
} }
/* Allocate a buffer to construct the S-expression. */ /* Allocate a buffer to construct the S-expression. */
@ -1216,11 +1096,11 @@ get_public_key (app_t app, int keyno)
goto leave; goto leave;
} }
sprintf (keybuf, "(10:public-key(3:rsa(1:n%u", (unsigned int) mlen); sprintf (keybuf, "(10:public-key(3:rsa(1:n%u:", (unsigned int) mlen);
keybuf_p = keybuf + strlen (keybuf); keybuf_p = keybuf + strlen (keybuf);
memcpy (keybuf_p, m, mlen); memcpy (keybuf_p, m, mlen);
keybuf_p += mlen; keybuf_p += mlen;
sprintf (keybuf_p, ")(1:e%u", (unsigned int)elen); sprintf (keybuf_p, ")(1:e%u:", (unsigned int)elen);
keybuf_p += strlen (keybuf_p); keybuf_p += strlen (keybuf_p);
memcpy (keybuf_p, e, elen); memcpy (keybuf_p, e, elen);
keybuf_p += elen; keybuf_p += elen;

View File

@ -26,6 +26,9 @@
#include <ctype.h> #include <ctype.h>
#include <unistd.h> #include <unistd.h>
#include <signal.h> #include <signal.h>
#ifdef USE_GNU_PTH
# include <pth.h>
#endif
#include <assuan.h> #include <assuan.h>
@ -38,11 +41,6 @@
#define MAXLEN_PIN 100 #define MAXLEN_PIN 100
/* We keep track of the primary client using scdaemon. This one will
for example receive signal on card change. */
static ctrl_t primary_connection;
#define set_error(e,t) assuan_set_error (ctx, ASSUAN_ ## e, (t)) #define set_error(e,t) assuan_set_error (ctx, ASSUAN_ ## e, (t))
@ -52,20 +50,65 @@ static ctrl_t primary_connection;
int _r = (r); \ int _r = (r); \
if (gpg_err_code (_r) == GPG_ERR_CARD_NOT_PRESENT \ 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_REMOVED) \
(c)->server_local->card_removed = 1; \ update_card_removed ((c)->reader_slot, 1); \
} while (0) } while (0)
#define IS_LOCKED(c) \
(locked_session && locked_session != (c)->server_local \
&& (c)->reader_slot != -1 && locked_session->ctrl_backlink \
&& (c)->reader_slot == locked_session->ctrl_backlink->reader_slot)
/* Data used to associate an Assuan context with local server data */
/* Data used to associate an Assuan context with local server data.
This object describes the local properties of one session. */
struct server_local_s { struct server_local_s {
/* We keep a list of all active sessions with the anchor at
SESSION_LIST (see below). This field is used for linking. */
struct server_local_s *next_session;
/* This object is usually assigned to a CTRL object (which is
globally visible). While enumeratin all sessions we sometimes
need to access data of the CTRL object; thus we keep a
backpointer here. */
ctrl_t ctrl_backlink;
/* The Assuan context used by this session/server. */
assuan_context_t assuan_ctx; assuan_context_t assuan_ctx;
int event_signal; /* Or 0 if not used. */ int event_signal; /* Or 0 if not used. */
int card_removed; /* True if the card has been removed and a
reset is required to continue /* True if the card has been removed and a reset is required to
operation. */ continue operation. */
int card_removed;
}; };
/* 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;
/* If a session has been locked we store a link to its server object
in this variable. */
static struct server_local_s *locked_session;
/* Update the CARD_REMOVED element of all sessions using the reader
given by SLOT to VALUE */
static void
update_card_removed (int slot, int value)
{
struct server_local_s *sl;
for (sl=session_list; sl; sl = sl->next_session)
if (sl->ctrl_backlink
&& sl->ctrl_backlink->reader_slot == slot)
sl->card_removed = value;
}
/* Check whether the option NAME appears in LINE */ /* Check whether the option NAME appears in LINE */
static int static int
has_option (const char *line, const char *name) has_option (const char *line, const char *name)
@ -79,10 +122,13 @@ has_option (const char *line, const char *name)
/* Reset the card and free the application context. With DO_CLOSE set /* Reset the card and free the application context. With DO_CLOSE set
to true, close the reader and don't do just a reset. */ to true and this is the last session with a reference to teh
reader, close the reader and don't do just a reset. */
static void static void
do_reset (ctrl_t ctrl, int do_close) do_reset (ctrl_t ctrl, int do_close)
{ {
int slot = ctrl->reader_slot;
if (ctrl->card_ctx) if (ctrl->card_ctx)
{ {
card_close (ctrl->card_ctx); card_close (ctrl->card_ctx);
@ -97,20 +143,61 @@ do_reset (ctrl_t ctrl, int do_close)
} }
if (ctrl->reader_slot != -1) if (ctrl->reader_slot != -1)
{ {
if (do_close || apdu_reset (ctrl->reader_slot)) struct server_local_s *sl;
/* If we are the only session with the reader open we may close
it. If not, do a reset unless the a lock is held on the
reader. */
for (sl=session_list; sl; sl = sl->next_session)
if (sl != ctrl->server_local
&& sl->ctrl_backlink->reader_slot == ctrl->reader_slot)
break;
if (sl) /* There is another session with the reader open. */
{ {
apdu_close_reader (ctrl->reader_slot); if ( IS_LOCKED (ctrl) ) /* If it is locked, release it. */
ctrl->reader_slot = -1; ctrl->reader_slot = -1;
else
{
if (do_close) /* Always mark reader unused. */
ctrl->reader_slot = -1;
else if (apdu_reset (ctrl->reader_slot)) /* Reset only if
not locked */
{
/* The reset failed. Mark the reader as closed. */
ctrl->reader_slot = -1;
}
if (locked_session && ctrl->server_local == locked_session)
{
locked_session = NULL;
log_debug ("implicitly unlocking due to RESET\n");
}
}
}
else /* No other session has the reader open. */
{
if (do_close || apdu_reset (ctrl->reader_slot))
{
apdu_close_reader (ctrl->reader_slot);
ctrl->reader_slot = -1;
}
if ( IS_LOCKED (ctrl) )
{
log_debug ("WARNING: cleaning up stale session lock\n");
locked_session = NULL;
}
} }
} }
ctrl->server_local->card_removed = 0;
/* Reset card removed flag for the current reader. */
update_card_removed (slot, 0);
} }
static void static void
reset_notify (assuan_context_t ctx) reset_notify (assuan_context_t ctx)
{ {
CTRL ctrl = assuan_get_pointer (ctx); ctrl_t ctrl = assuan_get_pointer (ctx);
do_reset (ctrl, 0); do_reset (ctrl, 0);
} }
@ -134,6 +221,27 @@ option_handler (assuan_context_t ctx, const char *key, const char *value)
} }
/* Return the slot of the current reader or open the reader if no
other sessions are using a reader. 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_reader_slot (void)
{
struct server_local_s *sl;
int slot= -1;
for (sl=session_list; sl; sl = sl->next_session)
if (sl->ctrl_backlink
&& (slot = sl->ctrl_backlink->reader_slot) != -1)
break;
if (slot == -1)
slot = apdu_open_reader (opt.reader_port);
return slot;
}
/* If the card has not yet been opened, do it. Note that this /* If the card has not yet been opened, do it. Note that this
function returns an Assuan error, so don't map the error a second function returns an Assuan error, so don't map the error a second
time */ time */
@ -154,10 +262,13 @@ open_card (ctrl_t ctrl, const char *apptype)
if (ctrl->card_ctx) if (ctrl->card_ctx)
return 0; /* Already initialized using a card context. */ return 0; /* Already initialized using a card context. */
if ( IS_LOCKED (ctrl) )
return gpg_error (GPG_ERR_EBUSY);
if (ctrl->reader_slot != -1) if (ctrl->reader_slot != -1)
slot = ctrl->reader_slot; slot = ctrl->reader_slot;
else else
slot = apdu_open_reader (opt.reader_port); slot = get_reader_slot ();
ctrl->reader_slot = slot; ctrl->reader_slot = slot;
if (slot == -1) if (slot == -1)
err = gpg_error (GPG_ERR_CARD); err = gpg_error (GPG_ERR_CARD);
@ -177,9 +288,7 @@ open_card (ctrl_t ctrl, const char *apptype)
err = card_open (&ctrl->card_ctx); err = card_open (&ctrl->card_ctx);
} }
if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT) TEST_CARD_REMOVAL (ctrl, err);
ctrl->server_local->card_removed = 1;
return map_to_assuan_status (err); return map_to_assuan_status (err);
} }
@ -248,12 +357,12 @@ cmd_serialno (assuan_context_t ctx, char *line)
time_t stamp; time_t stamp;
/* Clear the remove flag so that the open_card is able to reread it. */ /* Clear the remove flag so that the open_card is able to reread it. */
/* FIXME: We can't do that if we are in a locked state. Retrun an
appropriate erro r in that case. IF the card has not been
removed we may very well continue. */
if (ctrl->server_local->card_removed) if (ctrl->server_local->card_removed)
do_reset (ctrl, 0); {
if ( IS_LOCKED (ctrl) )
return gpg_error (GPG_ERR_EBUSY);
do_reset (ctrl, 0);
}
if ((rc = open_card (ctrl, *line? line:NULL))) if ((rc = open_card (ctrl, *line? line:NULL)))
return rc; return rc;
@ -342,7 +451,7 @@ cmd_serialno (assuan_context_t ctx, char *line)
static int static int
cmd_learn (assuan_context_t ctx, char *line) cmd_learn (assuan_context_t ctx, char *line)
{ {
CTRL ctrl = assuan_get_pointer (ctx); ctrl_t ctrl = assuan_get_pointer (ctx);
int rc = 0; int rc = 0;
int idx; int idx;
@ -491,7 +600,7 @@ cmd_learn (assuan_context_t ctx, char *line)
static int static int
cmd_readcert (assuan_context_t ctx, char *line) cmd_readcert (assuan_context_t ctx, char *line)
{ {
CTRL ctrl = assuan_get_pointer (ctx); ctrl_t ctrl = assuan_get_pointer (ctx);
int rc; int rc;
unsigned char *cert; unsigned char *cert;
size_t ncert; size_t ncert;
@ -630,12 +739,13 @@ cmd_readkey (assuan_context_t ctx, char *line)
static int static int
cmd_setdata (assuan_context_t ctx, char *line) cmd_setdata (assuan_context_t ctx, char *line)
{ {
CTRL ctrl = assuan_get_pointer (ctx); ctrl_t ctrl = assuan_get_pointer (ctx);
int n; int n;
char *p; char *p;
unsigned char *buf; unsigned char *buf;
/* FIXME: If we are locked return an error. */ if (locked_session && locked_session != ctrl->server_local)
return gpg_error (GPG_ERR_EBUSY);
/* Parse the hexstring. */ /* Parse the hexstring. */
for (p=line,n=0; hexdigitp (p); p++, n++) for (p=line,n=0; hexdigitp (p); p++, n++)
@ -700,13 +810,14 @@ pin_cb (void *opaque, const char *info, char **retstr)
static int static int
cmd_pksign (assuan_context_t ctx, char *line) cmd_pksign (assuan_context_t ctx, char *line)
{ {
CTRL ctrl = assuan_get_pointer (ctx); ctrl_t ctrl = assuan_get_pointer (ctx);
int rc; int rc;
unsigned char *outdata; unsigned char *outdata;
size_t outdatalen; size_t outdatalen;
char *keyidstr; char *keyidstr;
/* FIXME: If we are locked return an error. */ if ( IS_LOCKED (ctrl) )
return gpg_error (GPG_ERR_EBUSY);
if ((rc = open_card (ctrl, NULL))) if ((rc = open_card (ctrl, NULL)))
return rc; return rc;
@ -753,13 +864,14 @@ cmd_pksign (assuan_context_t ctx, char *line)
static int static int
cmd_pkauth (assuan_context_t ctx, char *line) cmd_pkauth (assuan_context_t ctx, char *line)
{ {
CTRL ctrl = assuan_get_pointer (ctx); ctrl_t ctrl = assuan_get_pointer (ctx);
int rc; int rc;
unsigned char *outdata; unsigned char *outdata;
size_t outdatalen; size_t outdatalen;
char *keyidstr; char *keyidstr;
/* FIXME: If we are locked return an error. */ if ( IS_LOCKED (ctrl) )
return gpg_error (GPG_ERR_EBUSY);
if ((rc = open_card (ctrl, NULL))) if ((rc = open_card (ctrl, NULL)))
return rc; return rc;
@ -802,13 +914,14 @@ cmd_pkauth (assuan_context_t ctx, char *line)
static int static int
cmd_pkdecrypt (assuan_context_t ctx, char *line) cmd_pkdecrypt (assuan_context_t ctx, char *line)
{ {
CTRL ctrl = assuan_get_pointer (ctx); ctrl_t ctrl = assuan_get_pointer (ctx);
int rc; int rc;
unsigned char *outdata; unsigned char *outdata;
size_t outdatalen; size_t outdatalen;
char *keyidstr; char *keyidstr;
/* FIXME: If we are locked return an error. */ if ( IS_LOCKED (ctrl) )
return gpg_error (GPG_ERR_EBUSY);
if ((rc = open_card (ctrl, NULL))) if ((rc = open_card (ctrl, NULL)))
return rc; return rc;
@ -861,7 +974,7 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line)
static int static int
cmd_getattr (assuan_context_t ctx, char *line) cmd_getattr (assuan_context_t ctx, char *line)
{ {
CTRL ctrl = assuan_get_pointer (ctx); ctrl_t ctrl = assuan_get_pointer (ctx);
int rc; int rc;
char *keyword; char *keyword;
@ -900,14 +1013,15 @@ cmd_getattr (assuan_context_t ctx, char *line)
static int static int
cmd_setattr (assuan_context_t ctx, char *orig_line) cmd_setattr (assuan_context_t ctx, char *orig_line)
{ {
CTRL ctrl = assuan_get_pointer (ctx); ctrl_t ctrl = assuan_get_pointer (ctx);
int rc; int rc;
char *keyword; char *keyword;
int keywordlen; int keywordlen;
size_t nbytes; size_t nbytes;
char *line, *linebuf; char *line, *linebuf;
/* FIXME: If we are locked return an error. */ if ( IS_LOCKED (ctrl) )
return gpg_error (GPG_ERR_EBUSY);
if ((rc = open_card (ctrl, NULL))) if ((rc = open_card (ctrl, NULL)))
return rc; return rc;
@ -956,12 +1070,13 @@ cmd_setattr (assuan_context_t ctx, char *orig_line)
static int static int
cmd_genkey (assuan_context_t ctx, char *line) cmd_genkey (assuan_context_t ctx, char *line)
{ {
CTRL ctrl = assuan_get_pointer (ctx); ctrl_t ctrl = assuan_get_pointer (ctx);
int rc; int rc;
char *keyno; char *keyno;
int force = has_option (line, "--force"); int force = has_option (line, "--force");
/* FIXME: If we are locked return an error. */ if ( IS_LOCKED (ctrl) )
return gpg_error (GPG_ERR_EBUSY);
/* Skip over options. */ /* Skip over options. */
while ( *line == '-' && line[1] == '-' ) while ( *line == '-' && line[1] == '-' )
@ -1004,7 +1119,7 @@ cmd_genkey (assuan_context_t ctx, char *line)
static int static int
cmd_random (assuan_context_t ctx, char *line) cmd_random (assuan_context_t ctx, char *line)
{ {
CTRL ctrl = assuan_get_pointer (ctx); ctrl_t ctrl = assuan_get_pointer (ctx);
int rc; int rc;
size_t nbytes; size_t nbytes;
unsigned char *buffer; unsigned char *buffer;
@ -1044,12 +1159,13 @@ cmd_random (assuan_context_t ctx, char *line)
static int static int
cmd_passwd (assuan_context_t ctx, char *line) cmd_passwd (assuan_context_t ctx, char *line)
{ {
CTRL ctrl = assuan_get_pointer (ctx); ctrl_t ctrl = assuan_get_pointer (ctx);
int rc; int rc;
char *chvnostr; char *chvnostr;
int reset_mode = has_option (line, "--reset"); int reset_mode = has_option (line, "--reset");
/* FIXME: If we are locked return an error. */ if ( IS_LOCKED (ctrl) )
return gpg_error (GPG_ERR_EBUSY);
/* Skip over options. */ /* Skip over options. */
while (*line == '-' && line[1] == '-') while (*line == '-' && line[1] == '-')
@ -1091,11 +1207,12 @@ cmd_passwd (assuan_context_t ctx, char *line)
static int static int
cmd_checkpin (assuan_context_t ctx, char *line) cmd_checkpin (assuan_context_t ctx, char *line)
{ {
CTRL ctrl = assuan_get_pointer (ctx); ctrl_t ctrl = assuan_get_pointer (ctx);
int rc; int rc;
char *keyidstr; char *keyidstr;
/* FIXME: If we are locked return an error. */ if ( IS_LOCKED (ctrl) )
return gpg_error (GPG_ERR_EBUSY);
if ((rc = open_card (ctrl, NULL))) if ((rc = open_card (ctrl, NULL)))
return rc; return rc;
@ -1122,15 +1239,83 @@ cmd_checkpin (assuan_context_t ctx, char *line)
} }
/* LOCK [--wait]
Grant exclusive card access to this session. Note that there is
no lock counter used and a second lock from the same session will
get ignore. A single unlock (or RESET) unlocks the session.
Return GPG_ERR_EBUSY if another session has locked the reader.
If the option --wait is given the command will wait until a
lock has been released.
*/
static int
cmd_lock (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
int rc = 0;
retry:
if (locked_session)
{
if (locked_session != ctrl->server_local)
rc = gpg_error (GPG_ERR_EBUSY);
}
else
locked_session = ctrl->server_local;
#ifdef USE_GNU_PTH
if (rc && has_option (line, "--wait"))
{
pth_sleep (1); /* Better implement an event mechanism. However,
for card operations this should be
sufficient. */
goto retry;
}
#endif /*USE_GNU_PTH*/
if (rc)
log_error ("cmd_lock failed: %s\n", gpg_strerror (rc));
return map_to_assuan_status (rc);
}
/* UNLOCK
Release exclusive card access.
*/
static int
cmd_unlock (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
int rc = 0;
if (locked_session)
{
if (locked_session != ctrl->server_local)
rc = gpg_error (GPG_ERR_EBUSY);
else
locked_session = NULL;
}
else
rc = gpg_error (GPG_ERR_NOT_LOCKED);
if (rc)
log_error ("cmd_unlock failed: %s\n", gpg_strerror (rc));
return map_to_assuan_status (rc);
}
/* Tell the assuan library about our commands */ /* Tell the assuan library about our commands */
static int static int
register_commands (ASSUAN_CONTEXT ctx) register_commands (assuan_context_t ctx)
{ {
static struct { static struct {
const char *name; const char *name;
int (*handler)(ASSUAN_CONTEXT, char *line); int (*handler)(assuan_context_t, char *line);
} table[] = { } table[] = {
{ "SERIALNO", cmd_serialno }, { "SERIALNO", cmd_serialno },
{ "LEARN", cmd_learn }, { "LEARN", cmd_learn },
@ -1148,6 +1333,8 @@ register_commands (ASSUAN_CONTEXT ctx)
{ "RANDOM", cmd_random }, { "RANDOM", cmd_random },
{ "PASSWD", cmd_passwd }, { "PASSWD", cmd_passwd },
{ "CHECKPIN", cmd_checkpin }, { "CHECKPIN", cmd_checkpin },
{ "LOCK", cmd_lock },
{ "UNLOCK", cmd_unlock },
{ NULL } { NULL }
}; };
int i, rc; int i, rc;
@ -1172,7 +1359,7 @@ void
scd_command_handler (int listen_fd) scd_command_handler (int listen_fd)
{ {
int rc; int rc;
ASSUAN_CONTEXT ctx; assuan_context_t ctx;
struct server_control_s ctrl; struct server_control_s ctrl;
memset (&ctrl, 0, sizeof ctrl); memset (&ctrl, 0, sizeof ctrl);
@ -1204,20 +1391,24 @@ scd_command_handler (int listen_fd)
scd_exit (2); scd_exit (2);
} }
assuan_set_pointer (ctx, &ctrl); assuan_set_pointer (ctx, &ctrl);
/* Allocate and initialize the server object. Put it into the list
of active sessions. */
ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local); ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local);
ctrl.server_local->next_session = session_list;
session_list = ctrl.server_local;
ctrl.server_local->ctrl_backlink = &ctrl;
ctrl.server_local->assuan_ctx = ctx; ctrl.server_local->assuan_ctx = ctx;
if (DBG_ASSUAN) if (DBG_ASSUAN)
assuan_set_log_stream (ctx, log_get_stream ()); assuan_set_log_stream (ctx, log_get_stream ());
/* Store the primary connection's assuan context. */
if (!primary_connection)
primary_connection = &ctrl;
/* We open the reader right at startup so that the ticker is able to /* We open the reader right at startup so that the ticker is able to
update the status file. */ update the status file. */
if (ctrl.reader_slot == -1) if (ctrl.reader_slot == -1)
ctrl.reader_slot = apdu_open_reader (opt.reader_port); {
ctrl.reader_slot = get_reader_slot ();
}
/* Command processing loop. */ /* Command processing loop. */
for (;;) for (;;)
@ -1241,13 +1432,26 @@ scd_command_handler (int listen_fd)
} }
} }
/* The next client will be the primary conenction if this one /* Cleanup. */
terminates. */ do_reset (&ctrl, 1);
if (primary_connection == &ctrl)
primary_connection = NULL;
do_reset (&ctrl, 1); /* Cleanup. */ /* Release the server object. */
if (session_list == ctrl.server_local)
session_list = ctrl.server_local->next_session;
else
{
struct server_local_s *sl;
for (sl=session_list; sl->next_session; sl = sl->next_session)
if (sl->next_session == ctrl.server_local)
break;
if (!sl->next_session)
BUG ();
sl->next_session = ctrl.server_local->next_session;
}
xfree (ctrl.server_local);
/* Release the Assuan context. */
assuan_deinit_server (ctx); assuan_deinit_server (ctx);
} }
@ -1256,14 +1460,14 @@ scd_command_handler (int listen_fd)
buffers. The variable elements are pairs of (char *, size_t), buffers. The variable elements are pairs of (char *, size_t),
terminated with a (NULL, 0). */ terminated with a (NULL, 0). */
void void
send_status_info (CTRL ctrl, const char *keyword, ...) send_status_info (ctrl_t ctrl, const char *keyword, ...)
{ {
va_list arg_ptr; va_list arg_ptr;
const unsigned char *value; const unsigned char *value;
size_t valuelen; size_t valuelen;
char buf[950], *p; char buf[950], *p;
size_t n; size_t n;
ASSUAN_CONTEXT ctx = ctrl->server_local->assuan_ctx; assuan_context_t ctx = ctrl->server_local->assuan_ctx;
va_start (arg_ptr, keyword); va_start (arg_ptr, keyword);
@ -1299,7 +1503,7 @@ send_status_info (CTRL ctrl, const char *keyword, ...)
} }
/* This fucntion is called by the ticker thread to check for changes /* This function is called by the ticker thread to check for changes
of the reader stati. It updates the reader status files and if of the reader stati. It updates the reader status files and if
requested by the caller also send a signal to the caller. */ requested by the caller also send a signal to the caller. */
void void
@ -1328,6 +1532,7 @@ scd_update_reader_status_file (void)
char *fname; char *fname;
char templ[50]; char templ[50];
FILE *fp; FILE *fp;
struct server_local_s *sl;
log_info ("updating status of slot %d to 0x%04X\n", slot, status); log_info ("updating status of slot %d to 0x%04X\n", slot, status);
@ -1344,33 +1549,31 @@ scd_update_reader_status_file (void)
} }
xfree (fname); xfree (fname);
/* Set the card removed flag. We will set this on any /* Set the card removed flag for all current sessions. We
card change because a reset or SERIALNO request must be will set this on any card change because a reset or
done in any case. */ SERIALNO request must be done in any case. */
if (primary_connection && primary_connection->server_local if (last[slot].any)
&& last[slot].any ) update_card_removed (slot, 1);
primary_connection->server_local->card_removed = 1;
last[slot].any = 1; last[slot].any = 1;
last[slot].status = status; last[slot].status = status;
last[slot].changed = changed; last[slot].changed = changed;
/* Send a signal to the primary client, if any. */ /* Send a signal to all clients who applied for it. */
if (primary_connection && primary_connection->server_local for (sl=session_list; sl; sl = sl->next_session)
&& primary_connection->server_local->assuan_ctx) if (sl->event_signal && sl->assuan_ctx)
{ {
pid_t pid = assuan_get_pid (primary_connection pid_t pid = assuan_get_pid (sl->assuan_ctx);
->server_local->assuan_ctx); int signo = sl->event_signal;
int signo = primary_connection->server_local->event_signal;
log_info ("client pid is %d, sending signal %d\n", pid, signo);
log_info ("client pid is %d, sending signal %d\n",
pid, signo);
#ifndef HAVE_W32_SYSTEM #ifndef HAVE_W32_SYSTEM
if (pid != (pid_t)(-1) && pid && signo > 0) if (pid != (pid_t)(-1) && pid && signo > 0)
kill (pid, signo); kill (pid, signo);
#endif #endif
} }
} }
} }
} }