mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01: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:
parent
f084afaeda
commit
9f9a18c011
@ -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>
|
||||
|
||||
* app-openpgp.c (do_check_pin): Add hack to allow verification of
|
||||
|
@ -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
|
||||
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;
|
||||
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;
|
||||
|
||||
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)
|
||||
/* 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 (;;)
|
||||
{
|
||||
/* FIXME? */
|
||||
max_length = 1024;
|
||||
line_ret = read_line (fp, &line, &line_size, &max_length);
|
||||
if (line_ret < 0)
|
||||
char *p;
|
||||
char *fields[6];
|
||||
int nfields;
|
||||
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;
|
||||
break;
|
||||
err = gpg_error_from_errno (errno);
|
||||
goto leave; /* Error. */
|
||||
}
|
||||
if (! line_ret)
|
||||
/* EOF. */
|
||||
/* FIXME? */
|
||||
break;
|
||||
if (!max_length)
|
||||
{
|
||||
err = gpg_error (GPG_ERR_TRUNCATED);
|
||||
goto leave; /* Line truncated - we better stop processing. */
|
||||
}
|
||||
|
||||
token = retrieve_next_token (line);
|
||||
if (! found_key)
|
||||
{
|
||||
/* Key not found yet, search for key entry. */
|
||||
if ((! strcmp (token, "pub")) || (! strcmp (token, "sub")))
|
||||
{
|
||||
/* Reached next key entry, parse it. */
|
||||
/* 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. */
|
||||
|
||||
/* This is the trust level (right, FIXME?). */
|
||||
token = retrieve_next_token (NULL);
|
||||
if (! token)
|
||||
{
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
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. */
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
{
|
||||
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 = gcry_mpi_aprint (GCRYMPI_FMT_STD, &e_new, &e_new_n, mpi);
|
||||
gcry_mpi_release (mpi);
|
||||
if (err)
|
||||
goto leave;
|
||||
}
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (pkd_n < 2)
|
||||
|
||||
if (m_new && e_new)
|
||||
{
|
||||
/* Not enough pkds retrieved. */
|
||||
ret = 1;
|
||||
goto out;
|
||||
*m = m_new;
|
||||
*mlen = m_new_n;
|
||||
m_new = NULL;
|
||||
*e = e_new;
|
||||
*elen = e_new_n;
|
||||
e_new = NULL;
|
||||
}
|
||||
else
|
||||
err = gpg_error (GPG_ERR_GENERAL);
|
||||
|
||||
*m = m_new;
|
||||
*mlen = m_new_n;
|
||||
*e = e_new;
|
||||
*elen = e_new_n;
|
||||
|
||||
out:
|
||||
|
||||
if (ret)
|
||||
{
|
||||
gcry_free (m_new);
|
||||
gcry_free (e_new);
|
||||
}
|
||||
gcry_mpi_release (mpi);
|
||||
gcry_free (line);
|
||||
|
||||
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 @@ get_public_key (app_t app, int keyno)
|
||||
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 @@ get_public_key (app_t app, int keyno)
|
||||
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;
|
||||
|
361
scd/command.c
361
scd/command.c
@ -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 @@ static ctrl_t primary_connection;
|
||||
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 @@ has_option (const char *line, const char *name)
|
||||
|
||||
|
||||
/* 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 @@ do_reset (ctrl_t ctrl, int do_close)
|
||||
}
|
||||
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);
|
||||
ctrl->reader_slot = -1;
|
||||
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. */
|
||||
{
|
||||
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 @@ 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
|
||||
function returns an Assuan error, so don't map the error a second
|
||||
time */
|
||||
@ -154,10 +262,13 @@ open_card (ctrl_t ctrl, const char *apptype)
|
||||
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 @@ open_card (ctrl_t ctrl, const char *apptype)
|
||||
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 @@ cmd_serialno (assuan_context_t ctx, char *line)
|
||||
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 @@ cmd_serialno (assuan_context_t ctx, char *line)
|
||||
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 @@ cmd_learn (assuan_context_t ctx, char *line)
|
||||
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 @@ cmd_readkey (assuan_context_t ctx, char *line)
|
||||
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 @@ pin_cb (void *opaque, const char *info, char **retstr)
|
||||
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 @@ cmd_pksign (assuan_context_t ctx, char *line)
|
||||
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 @@ cmd_pkauth (assuan_context_t ctx, char *line)
|
||||
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 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line)
|
||||
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 @@ cmd_getattr (assuan_context_t ctx, char *line)
|
||||
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 @@ cmd_setattr (assuan_context_t ctx, char *orig_line)
|
||||
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 @@ cmd_genkey (assuan_context_t ctx, char *line)
|
||||
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 @@ cmd_random (assuan_context_t ctx, char *line)
|
||||
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 @@ cmd_passwd (assuan_context_t ctx, char *line)
|
||||
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 @@ 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 */
|
||||
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 @@ register_commands (ASSUAN_CONTEXT ctx)
|
||||
{ "RANDOM", cmd_random },
|
||||
{ "PASSWD", cmd_passwd },
|
||||
{ "CHECKPIN", cmd_checkpin },
|
||||
{ "LOCK", cmd_lock },
|
||||
{ "UNLOCK", cmd_unlock },
|
||||
{ NULL }
|
||||
};
|
||||
int i, rc;
|
||||
@ -1172,7 +1359,7 @@ void
|
||||
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_command_handler (int listen_fd)
|
||||
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 @@ scd_command_handler (int listen_fd)
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 @@ scd_command_handler (int listen_fd)
|
||||
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 @@ 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
|
||||
requested by the caller also send a signal to the caller. */
|
||||
void
|
||||
@ -1328,6 +1532,7 @@ scd_update_reader_status_file (void)
|
||||
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 @@ scd_update_reader_status_file (void)
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user