GNUPG-1-9-BRANCH gnupg/scd (ChangeLog app-openpgp.c command.c)
cvs user wk
cvs at cvs.gnupg.org
Thu Apr 14 19:09:24 CEST 2005
Date: Thursday, April 14, 2005 @ 19:25:43
Author: wk
Path: /cvs/gnupg/gnupg/scd
Tag: GNUPG-1-9-BRANCH
Modified: ChangeLog app-openpgp.c command.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.
---------------+
ChangeLog | 28 +++
app-openpgp.c | 394 +++++++++++++++++++-------------------------------------
command.c | 361 ++++++++++++++++++++++++++++++++++++++++-----------
3 files changed, 447 insertions(+), 336 deletions(-)
Index: gnupg/scd/ChangeLog
diff -u gnupg/scd/ChangeLog:1.25.2.72 gnupg/scd/ChangeLog:1.25.2.73
--- gnupg/scd/ChangeLog:1.25.2.72 Mon Apr 11 18:20:10 2005
+++ gnupg/scd/ChangeLog Thu Apr 14 19:25:43 2005
@@ -1,3 +1,31 @@
+2005-04-14 Werner Koch <wk at 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 at 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 at g10code.com>
* app-openpgp.c (do_check_pin): Add hack to allow verification of
Index: gnupg/scd/app-openpgp.c
diff -u gnupg/scd/app-openpgp.c:1.9.2.29 gnupg/scd/app-openpgp.c:1.9.2.30
--- gnupg/scd/app-openpgp.c:1.9.2.29 Mon Apr 11 18:20:10 2005
+++ gnupg/scd/app-openpgp.c Thu Apr 14 19:25:43 2005
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*
- * $Id: app-openpgp.c,v 1.9.2.29 2005/04/11 16:20:10 wk Exp $
+ * $Id: app-openpgp.c,v 1.9.2.30 2005/04/14 17:25:43 wk Exp $
*/
#include <config.h>
@@ -784,266 +784,149 @@
}
/* Retrieve the fingerprint from the card inserted in SLOT and write
- the according hex representation (40 hex digits plus NUL character)
- to FPR. */
+ the according hex representation to FPR. Caller must have provide
+ 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
-retrieve_fpr_from_card (int slot, char *fpr)
+retrieve_fpr_from_card (app_t app, int keyno, char *fpr)
{
- const unsigned char *value;
- unsigned char *data;
- size_t data_n;
- gpg_error_t err;
- size_t value_n;
- unsigned int i;
+ gpg_error_t err = 0;
+ void *relptr;
+ unsigned char *value;
+ size_t valuelen;
+ int i;
- data = NULL;
+ assert (keyno >=0 && keyno <= 2);
- err = iso7816_get_data (slot, 0x6E, &data, &data_n);
- if (err)
- /* 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"? */
+ relptr = get_one_do (app, 0x00C5, &value, &valuelen, NULL);
+ if (relptr && valuelen >= 60)
{
- /* FIXME? */
- err = gpg_error (GPG_ERR_CARD); /* */
- goto out;
+ for (i = 0; i < 20; i++)
+ sprintf (fpr + (i * 2), "%02X", value[(keyno*20)+i]);
}
-
- /* Copy out third key FPR. */
- for (i = 0; i < 20; i++)
- sprintf (fpr + (i * 2), "%02X", (value + (2 * 20))[i]);
-
- out:
-
- xfree (data);
-
+ else
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+ xfree (relptr);
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, ":");
- if (! p)
- log_error ("error while extracting token\n");
- return p;
-}
-
-/* Retrieve the secret key material for the key, whose fingerprint is
- FPR, from gpg output, which can be read through the stream FP. The
- 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,
+/* Retrieve the public key material for the RSA key, whose fingerprint
+ is FPR, from gpg output, which can be read through the stream FP.
+ 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
+ code on failure. Caller must release the allocated buffers at M
+ and E if the function returns success. */
+#if GNUPG_MAJOR_VERSION > 1
+static gpg_error_t
+retrieve_key_material (FILE *fp, const char *hexkeyid,
const unsigned char **m, size_t *mlen,
const unsigned char **e, size_t *elen)
{
- size_t line_size;
- ssize_t line_ret;
- char *line;
- int ret;
- int found_key;
- char *token;
- int pkd_n;
- unsigned char *m_new;
- 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;
- line = NULL;
- found_key = 0;
- pkd_n = 0;
- m_new = NULL;
- e_new = NULL;
- 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? */
- max_length = 1024;
- line_ret = read_line (fp, &line, &line_size, &max_length);
- if (line_ret < 0)
- {
- ret = 1;
- break;
- }
- if (! line_ret)
- /* EOF. */
- /* FIXME? */
- break;
+ gcry_error_t err = 0;
+ char *line = NULL; /* read_line() buffer. */
+ size_t line_size = 0; /* Helper for for read_line. */
+ int found_key = 0; /* Helper to find a matching key. */
+ unsigned char *m_new = NULL;
+ unsigned char *e_new = NULL;
+ size_t m_new_n = 0;
+ size_t e_new_n = 0;
+
+ /* Loop over all records until we have found the subkey
+ corresponsing to the fingerprint. Inm general the first record
+ should be the pub record, but we don't rely on that. Given that
+ we only need to look at one key, it is sufficient to compare the
+ keyid so that we don't need to look at "fpr" records. */
+ for (;;)
+ {
+ char *p;
+ char *fields[6];
+ int nfields;
+ size_t max_length;
+ gcry_mpi_t mpi;
+ int i;
- token = retrieve_next_token (line);
- if (! found_key)
+ max_length = 4096;
+ i = read_line (fp, &line, &line_size, &max_length);
+ if (!i)
+ break; /* EOF. */
+ if (i < 0)
{
- /* Key not found yet, search for key entry. */
- if ((! strcmp (token, "pub")) || (! strcmp (token, "sub")))
- {
- /* Reached next key entry, parse it. */
-
- /* This is the trust level (right, FIXME?). */
- token = retrieve_next_token (NULL);
- if (! token)
- {
- ret = 1;
- break;
- }
-
- /* This is the size. */
- token = retrieve_next_token (NULL);
- if (! token)
- {
- ret = 1;
- break;
- }
-
- /* This is the algorithm (right, FIXME?). */
- token = retrieve_next_token (NULL);
- if (! token)
- {
- ret = 1;
- break;
- }
- 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
- {
- if (! strcmp (token, "sub"))
- /* Next key entry, break. */
- break;
-
- 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;
- }
- }
+ err = gpg_error_from_errno (errno);
+ goto leave; /* Error. */
}
- }
- if (ret)
- goto out;
-
- if (pkd_n < 2)
- {
- /* Not enough pkds retrieved. */
- ret = 1;
- goto out;
- }
+ if (!max_length)
+ {
+ err = gpg_error (GPG_ERR_TRUNCATED);
+ goto leave; /* Line truncated - we better stop processing. */
+ }
- *m = m_new;
- *mlen = m_new_n;
- *e = e_new;
- *elen = e_new_n;
+ /* Parse the line into fields. */
+ for (nfields=0, p=line; p && nfields < DIM (fields); nfields++)
+ {
+ fields[nfields] = p;
+ p = strchr (p, ':');
+ if (p)
+ *(p++) = 0;
+ }
+ if (!nfields)
+ continue; /* No fields at all - skip line. */
- out:
+ if (!found_key)
+ {
+ if ( (!strcmp (fields[0], "sub") || !strcmp (fields[0], "pub") )
+ && nfields > 4 && !strcmp (fields[4], hexkeyid))
+ found_key = 1;
+ continue;
+ }
+
+ if ( !strcmp (fields[0], "sub") || !strcmp (fields[0], "pub") )
+ break; /* Next key - stop. */
- if (ret)
+ if ( strcmp (fields[0], "pkd") )
+ continue; /* Not a key data record. */
+ i = 0; /* Avoid erroneous compiler warning. */
+ if ( nfields < 4 || (i = atoi (fields[1])) < 0 || i > 1
+ || (!i && m_new) || (i && e_new))
+ {
+ err = gpg_error (GPG_ERR_GENERAL);
+ goto leave; /* Error: Invalid key data record or not an RSA key. */
+ }
+
+ err = gcry_mpi_scan (&mpi, GCRYMPI_FMT_HEX, fields[3], 0, NULL);
+ if (err)
+ mpi = NULL;
+ else if (!i)
+ 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);
+ gcry_mpi_release (mpi);
+ if (err)
+ goto leave;
+ }
+
+ if (m_new && e_new)
{
- gcry_free (m_new);
- gcry_free (e_new);
+ *m = m_new;
+ *mlen = m_new_n;
+ m_new = NULL;
+ *e = e_new;
+ *elen = e_new_n;
+ e_new = NULL;
}
- gcry_mpi_release (mpi);
- gcry_free (line);
+ else
+ err = gpg_error (GPG_ERR_GENERAL);
- return ret;
+ leave:
+ xfree (m_new);
+ xfree (e_new);
+ xfree (line);
+ return err;
}
+#endif /*GNUPG_MAJOR_VERSION > 1*/
+
/* 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
@@ -1158,52 +1041,49 @@
The helper we use here is gpg itself, which should know about
the key in any case. */
- char fpr_long[41];
- char *fpr = fpr_long + 24;
- char *command;
+ char fpr[41];
+ char *hexkeyid;
+ char *command = NULL;
FILE *fp;
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)
{
log_error ("error while retrieving fpr from card: %s\n",
gpg_strerror (err));
goto leave;
}
+ hexkeyid = fpr + 24;
ret = asprintf (&command,
"gpg --list-keys --with-colons --with-key-data '%s'",
- fpr_long);
+ fpr);
if (ret < 0)
{
err = gpg_error_from_errno (errno);
- log_error ("error while creating pipe command "
- "for retrieving key: %s\n", gpg_strerror (err));
goto leave;
}
fp = popen (command, "r");
- if (! fp)
+ free (command);
+ if (!fp)
{
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;
}
- ret = retrieve_key_material (fp, fpr, &m, &mlen, &e, &elen);
+ err = retrieve_key_material (fp, hexkeyid, &m, &mlen, &e, &elen);
fclose (fp);
- if (ret)
+ if (err)
{
- /* FIXME? */
- err = gpg_error (GPG_ERR_INTERNAL);
- log_error ("error while retrieving key material through pipe\n");
+ log_error ("error while retrieving key material through pipe: %s\n",
+ gpg_strerror (err));
goto leave;
}
-
- buffer = NULL;
}
/* Allocate a buffer to construct the S-expression. */
@@ -1216,11 +1096,11 @@
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);
memcpy (keybuf_p, m, 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);
memcpy (keybuf_p, e, elen);
keybuf_p += elen;
Index: gnupg/scd/command.c
diff -u gnupg/scd/command.c:1.19.2.19 gnupg/scd/command.c:1.19.2.20
--- gnupg/scd/command.c:1.19.2.19 Mon Apr 11 18:20:10 2005
+++ gnupg/scd/command.c Thu Apr 14 19:25:43 2005
@@ -26,6 +26,9 @@
#include <ctype.h>
#include <unistd.h>
#include <signal.h>
+#ifdef USE_GNU_PTH
+# include <pth.h>
+#endif
#include <assuan.h>
@@ -38,11 +41,6 @@
#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))
@@ -52,20 +50,65 @@
int _r = (r); \
if (gpg_err_code (_r) == GPG_ERR_CARD_NOT_PRESENT \
|| gpg_err_code (_r) == GPG_ERR_CARD_REMOVED) \
- (c)->server_local->card_removed = 1; \
+ update_card_removed ((c)->reader_slot, 1); \
} 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 {
+ /* 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;
+
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
- operation. */
+
+ /* True if the card has been removed and a reset is required to
+ 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 */
static int
has_option (const char *line, const char *name)
@@ -79,10 +122,13 @@
/* 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
do_reset (ctrl_t ctrl, int do_close)
{
+ int slot = ctrl->reader_slot;
+
if (ctrl->card_ctx)
{
card_close (ctrl->card_ctx);
@@ -97,20 +143,61 @@
}
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. */
+ {
+ if ( IS_LOCKED (ctrl) ) /* If it is locked, release it. */
+ 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. */
{
- apdu_close_reader (ctrl->reader_slot);
- ctrl->reader_slot = -1;
+ 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
reset_notify (assuan_context_t ctx)
{
- CTRL ctrl = assuan_get_pointer (ctx);
+ ctrl_t ctrl = assuan_get_pointer (ctx);
do_reset (ctrl, 0);
}
@@ -134,6 +221,27 @@
}
+/* 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
function returns an Assuan error, so don't map the error a second
time */
@@ -154,10 +262,13 @@
if (ctrl->card_ctx)
return 0; /* Already initialized using a card context. */
+ if ( IS_LOCKED (ctrl) )
+ return gpg_error (GPG_ERR_EBUSY);
+
if (ctrl->reader_slot != -1)
slot = ctrl->reader_slot;
else
- slot = apdu_open_reader (opt.reader_port);
+ slot = get_reader_slot ();
ctrl->reader_slot = slot;
if (slot == -1)
err = gpg_error (GPG_ERR_CARD);
@@ -177,9 +288,7 @@
err = card_open (&ctrl->card_ctx);
}
- if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT)
- ctrl->server_local->card_removed = 1;
-
+ TEST_CARD_REMOVAL (ctrl, err);
return map_to_assuan_status (err);
}
@@ -248,12 +357,12 @@
time_t stamp;
/* 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)
- 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)))
return rc;
@@ -342,7 +451,7 @@
static int
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 idx;
@@ -491,7 +600,7 @@
static int
cmd_readcert (assuan_context_t ctx, char *line)
{
- CTRL ctrl = assuan_get_pointer (ctx);
+ ctrl_t ctrl = assuan_get_pointer (ctx);
int rc;
unsigned char *cert;
size_t ncert;
@@ -630,12 +739,13 @@
static int
cmd_setdata (assuan_context_t ctx, char *line)
{
- CTRL ctrl = assuan_get_pointer (ctx);
+ ctrl_t ctrl = assuan_get_pointer (ctx);
int n;
char *p;
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. */
for (p=line,n=0; hexdigitp (p); p++, n++)
@@ -700,13 +810,14 @@
static int
cmd_pksign (assuan_context_t ctx, char *line)
{
- CTRL ctrl = assuan_get_pointer (ctx);
+ ctrl_t ctrl = assuan_get_pointer (ctx);
int rc;
unsigned char *outdata;
size_t outdatalen;
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)))
return rc;
@@ -753,13 +864,14 @@
static int
cmd_pkauth (assuan_context_t ctx, char *line)
{
- CTRL ctrl = assuan_get_pointer (ctx);
+ ctrl_t ctrl = assuan_get_pointer (ctx);
int rc;
unsigned char *outdata;
size_t outdatalen;
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)))
return rc;
@@ -802,13 +914,14 @@
static int
cmd_pkdecrypt (assuan_context_t ctx, char *line)
{
- CTRL ctrl = assuan_get_pointer (ctx);
+ ctrl_t ctrl = assuan_get_pointer (ctx);
int rc;
unsigned char *outdata;
size_t outdatalen;
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)))
return rc;
@@ -861,7 +974,7 @@
static int
cmd_getattr (assuan_context_t ctx, char *line)
{
- CTRL ctrl = assuan_get_pointer (ctx);
+ ctrl_t ctrl = assuan_get_pointer (ctx);
int rc;
char *keyword;
@@ -900,14 +1013,15 @@
static int
cmd_setattr (assuan_context_t ctx, char *orig_line)
{
- CTRL ctrl = assuan_get_pointer (ctx);
+ ctrl_t ctrl = assuan_get_pointer (ctx);
int rc;
char *keyword;
int keywordlen;
size_t nbytes;
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)))
return rc;
@@ -956,12 +1070,13 @@
static int
cmd_genkey (assuan_context_t ctx, char *line)
{
- CTRL ctrl = assuan_get_pointer (ctx);
+ ctrl_t ctrl = assuan_get_pointer (ctx);
int rc;
char *keyno;
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. */
while ( *line == '-' && line[1] == '-' )
@@ -1004,7 +1119,7 @@
static int
cmd_random (assuan_context_t ctx, char *line)
{
- CTRL ctrl = assuan_get_pointer (ctx);
+ ctrl_t ctrl = assuan_get_pointer (ctx);
int rc;
size_t nbytes;
unsigned char *buffer;
@@ -1044,12 +1159,13 @@
static int
cmd_passwd (assuan_context_t ctx, char *line)
{
- CTRL ctrl = assuan_get_pointer (ctx);
+ ctrl_t ctrl = assuan_get_pointer (ctx);
int rc;
char *chvnostr;
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. */
while (*line == '-' && line[1] == '-')
@@ -1091,11 +1207,12 @@
static int
cmd_checkpin (assuan_context_t ctx, char *line)
{
- CTRL ctrl = assuan_get_pointer (ctx);
+ ctrl_t ctrl = assuan_get_pointer (ctx);
int rc;
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)))
return rc;
@@ -1122,15 +1239,83 @@
}
+/* 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 */
static int
-register_commands (ASSUAN_CONTEXT ctx)
+register_commands (assuan_context_t ctx)
{
static struct {
const char *name;
- int (*handler)(ASSUAN_CONTEXT, char *line);
+ int (*handler)(assuan_context_t, char *line);
} table[] = {
{ "SERIALNO", cmd_serialno },
{ "LEARN", cmd_learn },
@@ -1148,6 +1333,8 @@
{ "RANDOM", cmd_random },
{ "PASSWD", cmd_passwd },
{ "CHECKPIN", cmd_checkpin },
+ { "LOCK", cmd_lock },
+ { "UNLOCK", cmd_unlock },
{ NULL }
};
int i, rc;
@@ -1172,7 +1359,7 @@
scd_command_handler (int listen_fd)
{
int rc;
- ASSUAN_CONTEXT ctx;
+ assuan_context_t ctx;
struct server_control_s ctrl;
memset (&ctrl, 0, sizeof ctrl);
@@ -1204,20 +1391,24 @@
scd_exit (2);
}
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->next_session = session_list;
+ session_list = ctrl.server_local;
+ ctrl.server_local->ctrl_backlink = &ctrl;
ctrl.server_local->assuan_ctx = ctx;
if (DBG_ASSUAN)
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
update the status file. */
if (ctrl.reader_slot == -1)
- ctrl.reader_slot = apdu_open_reader (opt.reader_port);
+ {
+ ctrl.reader_slot = get_reader_slot ();
+ }
/* Command processing loop. */
for (;;)
@@ -1241,13 +1432,26 @@
}
}
- /* The next client will be the primary conenction if this one
- terminates. */
- if (primary_connection == &ctrl)
- primary_connection = NULL;
+ /* Cleanup. */
+ do_reset (&ctrl, 1);
- 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);
}
@@ -1256,14 +1460,14 @@
buffers. The variable elements are pairs of (char *, size_t),
terminated with a (NULL, 0). */
void
-send_status_info (CTRL ctrl, const char *keyword, ...)
+send_status_info (ctrl_t ctrl, const char *keyword, ...)
{
va_list arg_ptr;
const unsigned char *value;
size_t valuelen;
char buf[950], *p;
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);
@@ -1299,7 +1503,7 @@
}
-/* 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
requested by the caller also send a signal to the caller. */
void
@@ -1328,6 +1532,7 @@
char *fname;
char templ[50];
FILE *fp;
+ struct server_local_s *sl;
log_info ("updating status of slot %d to 0x%04X\n", slot, status);
@@ -1344,33 +1549,31 @@
}
xfree (fname);
- /* Set the card removed flag. We will set this on any
- card change because a reset or SERIALNO request must be
- done in any case. */
- if (primary_connection && primary_connection->server_local
- && last[slot].any )
- primary_connection->server_local->card_removed = 1;
+ /* Set the card removed flag for all current sessions. We
+ will set this on any card change because a reset or
+ SERIALNO request must be done in any case. */
+ if (last[slot].any)
+ update_card_removed (slot, 1);
last[slot].any = 1;
last[slot].status = status;
last[slot].changed = changed;
- /* Send a signal to the primary client, if any. */
- if (primary_connection && primary_connection->server_local
- && primary_connection->server_local->assuan_ctx)
- {
- pid_t pid = assuan_get_pid (primary_connection
- ->server_local->assuan_ctx);
- int signo = primary_connection->server_local->event_signal;
-
- log_info ("client pid is %d, sending signal %d\n", pid, signo);
+ /* Send a signal to all clients who applied for it. */
+ for (sl=session_list; sl; sl = sl->next_session)
+ if (sl->event_signal && sl->assuan_ctx)
+ {
+ pid_t pid = assuan_get_pid (sl->assuan_ctx);
+ int signo = sl->event_signal;
+ log_info ("client pid is %d, sending signal %d\n",
+ pid, signo);
#ifndef HAVE_W32_SYSTEM
- if (pid != (pid_t)(-1) && pid && signo > 0)
- kill (pid, signo);
+ if (pid != (pid_t)(-1) && pid && signo > 0)
+ kill (pid, signo);
#endif
- }
+ }
}
}
}
More information about the Gnupg-commits
mailing list