* call-scd.c (unescape_status_string): New. Actual a copy of

../g10/call-agent.c
(card_getattr_cb, agent_card_getattr): New.

* command-ssh.c (card_key_available): New.
(ssh_handler_request_identities): First see whether a card key is
available.

* app.c (app_getattr): Return APPTYPE or SERIALNO type even if the
application does dot support the getattr call.

* app.c (select_application): Return an error code and the
application context in an new arg.
* command.c (open_card): Adjusted for that.  Don't use the
fallback if no card is present.  Return an error if the card has
been removed without a reset.
(do_reset, cmd_serialno): Clear that error flag.
(TEST_CARD_REMOVAL): New. Use it with all command handlers.
(scd_update_reader_status_file): Set the error flag on all changes.
This commit is contained in:
Werner Koch 2005-02-24 21:40:48 +00:00
parent 3af261572b
commit 1f1f28555a
7 changed files with 320 additions and 81 deletions

View File

@ -1,5 +1,13 @@
2005-02-24 Werner Koch <wk@g10code.com> 2005-02-24 Werner Koch <wk@g10code.com>
* call-scd.c (unescape_status_string): New. Actual a copy of
../g10/call-agent.c
(card_getattr_cb, agent_card_getattr): New.
* command-ssh.c (card_key_available): New.
(ssh_handler_request_identities): First see whether a card key is
available.
* gpg-agent.c (handle_connections): Need to check for events if * gpg-agent.c (handle_connections): Need to check for events if
select returns with -1. select returns with -1.

View File

@ -259,6 +259,7 @@ int agent_card_pkdecrypt (ctrl_t ctrl,
int agent_card_readcert (ctrl_t ctrl, int agent_card_readcert (ctrl_t ctrl,
const char *id, char **r_buf, size_t *r_buflen); const char *id, char **r_buf, size_t *r_buflen);
int agent_card_readkey (ctrl_t ctrl, const char *id, unsigned char **r_buf); int agent_card_readkey (ctrl_t ctrl, const char *id, unsigned char **r_buf);
gpg_error_t agent_card_getattr (ctrl_t ctrl, const char *name, char **result);
int agent_card_scd (ctrl_t ctrl, const char *cmdline, int agent_card_scd (ctrl_t ctrl, const char *cmdline,
int (*getpin_cb)(void *, const char *, char*, size_t), int (*getpin_cb)(void *, const char *, char*, size_t),
void *getpin_cb_arg, void *assuan_context); void *getpin_cb_arg, void *assuan_context);

View File

@ -1,5 +1,5 @@
/* call-scd.c - fork of the scdaemon to do SC operations /* call-scd.c - fork of the scdaemon to do SC operations
* Copyright (C) 2001, 2002 Free Software Foundation, Inc. * Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -66,7 +66,7 @@ static pth_mutex_t scd_lock;
static int active_connection_fd = -1; static int active_connection_fd = -1;
static int active_connection = 0; static int active_connection = 0;
/* callback parameter for learn card */ /* Callback parameter for learn card */
struct learn_parm_s { struct learn_parm_s {
void (*kpinfo_cb)(void*, const char *); void (*kpinfo_cb)(void*, const char *);
void *kpinfo_cb_arg; void *kpinfo_cb_arg;
@ -266,6 +266,41 @@ agent_reset_scd (ctrl_t ctrl)
} }
/* Return a new malloced string by unescaping the string S. Escaping
is percent escaping and '+'/space mapping. A binary Nul will
silently be replaced by a 0xFF. Function returns NULL to indicate
an out of memory status. */
static char *
unescape_status_string (const unsigned char *s)
{
char *buffer, *d;
buffer = d = xtrymalloc (strlen (s)+1);
if (!buffer)
return NULL;
while (*s)
{
if (*s == '%' && s[1] && s[2])
{
s++;
*d = xtoi_2 (s);
if (!*d)
*d = '\xff';
d++;
s += 2;
}
else if (*s == '+')
{
*d++ = ' ';
s++;
}
else
*d++ = *s++;
}
*d = 0;
return buffer;
}
@ -375,14 +410,6 @@ agent_card_serialno (ctrl_t ctrl, char **r_serialno)
if (rc) if (rc)
return rc; return rc;
/* Hmm, do we really need this reset - scddaemon should do this or
we can do this if we for some reason figure out that the
operation might have failed due to a missing RESET. Hmmm, I feel
this is really SCdaemon's duty */
/* rc = assuan_transact (scd_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); */
/* if (rc) */
/* return unlock_scd (map_assuan_err (rc)); */
rc = assuan_transact (scd_ctx, "SERIALNO", rc = assuan_transact (scd_ctx, "SERIALNO",
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
get_serialno_cb, &serialno); get_serialno_cb, &serialno);
@ -395,6 +422,8 @@ agent_card_serialno (ctrl_t ctrl, char **r_serialno)
return unlock_scd (0); return unlock_scd (0);
} }
static AssuanError static AssuanError
membuf_data_cb (void *opaque, const void *buffer, size_t length) membuf_data_cb (void *opaque, const void *buffer, size_t length)
@ -644,6 +673,90 @@ agent_card_readkey (ctrl_t ctrl, const char *id, unsigned char **r_buf)
} }
/* Type used with the card_getattr_cb. */
struct card_getattr_parm_s {
const char *keyword; /* Keyword to look for. */
size_t keywordlen; /* strlen of KEYWORD. */
char *data; /* Malloced and unescaped data. */
int error; /* ERRNO value or 0 on success. */
};
/* Callback function for agent_card_getattr. */
static assuan_error_t
card_getattr_cb (void *opaque, const char *line)
{
struct card_getattr_parm_s *parm = opaque;
const char *keyword = line;
int keywordlen;
if (parm->data)
return 0; /* We want only the first occurrence. */
for (keywordlen=0; *line && !spacep (line); line++, keywordlen++)
;
while (spacep (line))
line++;
if (keywordlen == parm->keywordlen
&& !memcmp (keyword, parm->keyword, keywordlen))
{
parm->data = unescape_status_string (line);
if (!parm->data)
parm->error = errno;
}
return 0;
}
/* Call the agent to retrieve a single line data object. On success
the object is malloced and stored at RESULT; it is guaranteed that
NULL is never stored in this case. On error an error code is
returned and NULL stored at RESULT. */
gpg_error_t
agent_card_getattr (ctrl_t ctrl, const char *name, char **result)
{
int err;
struct card_getattr_parm_s parm;
char line[ASSUAN_LINELENGTH];
*result = NULL;
if (!*name)
return gpg_error (GPG_ERR_INV_VALUE);
memset (&parm, 0, sizeof parm);
parm.keyword = name;
parm.keywordlen = strlen (name);
/* We assume that NAME does not need escaping. */
if (8 + strlen (name) > DIM(line)-1)
return gpg_error (GPG_ERR_TOO_LARGE);
stpcpy (stpcpy (line, "GETATTR "), name);
err = start_scd (ctrl);
if (err)
return err;
err = map_assuan_err (assuan_transact (scd_ctx, line,
NULL, NULL, NULL, NULL,
card_getattr_cb, &parm));
if (!err && parm.error)
err = gpg_error_from_errno (parm.error);
if (!err && !parm.data)
err = gpg_error (GPG_ERR_NO_DATA);
if (!err)
*result = parm.data;
else
xfree (parm.data);
return unlock_scd (err);
}
static AssuanError static AssuanError

View File

@ -1201,7 +1201,7 @@ sexp_key_extract (gcry_sexp_t sexp,
return err; return err;
} }
/* Extract the car from SEXP, and create a newly created C-string it, /* Extract the car from SEXP, and create a newly created C-string
which is to be stored in IDENTIFIER. */ which is to be stored in IDENTIFIER. */
static gpg_error_t static gpg_error_t
sexp_extract_identifier (gcry_sexp_t sexp, const char **identifier) sexp_extract_identifier (gcry_sexp_t sexp, const char **identifier)
@ -1404,6 +1404,7 @@ ssh_convert_key_to_blob (unsigned char **blob, size_t *blob_size,
} }
/* Write the public key KEY_PUBLIC to STREAM in SSH key format. */
static gpg_error_t static gpg_error_t
ssh_send_key_public (estream_t stream, gcry_sexp_t key_public) ssh_send_key_public (estream_t stream, gcry_sexp_t key_public)
{ {
@ -1516,7 +1517,78 @@ key_secret_to_public (gcry_sexp_t *key_public,
return err; return err;
} }
/* Chec whether a smartcard is available and whether it has a usable
key. Store a copy of that key at R_PK and return 0. If no key is
available store NULL at R_PK and return an error code. */
static gpg_error_t
card_key_available (ctrl_t ctrl, gcry_sexp_t *r_pk)
{
gpg_error_t err;
char *appname;
unsigned char *sbuf;
size_t sbuflen;
gcry_sexp_t pk;
*r_pk = NULL;
/* First see whether a card is available and whether the application
is supported. */
err = agent_card_getattr (ctrl, "APPTYPE", &appname);
if ( gpg_err_code (err) == GPG_ERR_CARD_REMOVED )
{
/* Ask for the serial number to reset the card. */
err = agent_card_serialno (ctrl, &appname);
if (err)
{
if (opt.verbose)
log_info (_("can't get serial number of card: %s\n"),
gpg_strerror (err));
return err;
}
log_info (_("detected card with S/N: %s\n"), appname);
xfree (appname);
err = agent_card_getattr (ctrl, "APPTYPE", &appname);
}
if (err)
{
log_error (_("error getting application type of card: %s\n"),
gpg_strerror (err));
return err;
}
if (strcmp (appname, "OPENPGP"))
{
log_info (_("card application `%s' is not supported\n"), appname);
xfree (appname);
return gpg_error (GPG_ERR_NOT_SUPPORTED);
}
xfree (appname);
appname = NULL;
/* Read the public key. */
err = agent_card_readkey (ctrl, "OPENPGP.3", &sbuf);
if (err)
{
if (opt.verbose)
log_info (_("no suitable card key found: %s\n"), gpg_strerror (err));
return err;
}
sbuflen = gcry_sexp_canon_len (sbuf, 0, NULL, NULL);
err = gcry_sexp_sscan (&pk, NULL, sbuf, sbuflen);
xfree (sbuf);
if (err)
{
log_error ("failed to build S-Exp from received card key: %s\n",
gpg_strerror (err));
return err;
}
*r_pk = pk;
return 0;
}
/* /*
Request handler. Request handler.
@ -1589,91 +1661,95 @@ ssh_handler_request_identities (ctrl_t ctrl,
goto out; goto out;
} }
/* Iterate over key files. */
/* FIXME: make sure that buffer gets deallocated properly. */
/* First check whether a key is currently available in the card
reader - this should be allowed even without being listed in
sshcontrol.txt. */
if (!card_key_available (ctrl, &key_public))
{
err = ssh_send_key_public (key_blobs, key_public);
gcry_sexp_release (key_public);
key_public = NULL;
if (err)
goto out;
key_counter++;
}
/* Then look at all the registered an allowed keys. */
/* Fixme: We should better iterate over the control file and check /* Fixme: We should better iterate over the control file and check
whether the key file is there. This is better in resepct to whether the key file is there. This is better in resepct to
performance if tehre are a lot of key sin our key storage. */ performance if tehre are a lot of key sin our key storage. */
/* FIXME: make sure that buffer gets deallocated properly. */
err = open_control_file (&ctrl_fp, 0); err = open_control_file (&ctrl_fp, 0);
if (err) if (err)
goto out; goto out;
#warning Really need to fix this fixme. while ( (dir_entry = readdir (dir)) )
/*
FIXME: First check whether a key is currently available in the card reader - this should be allowed even without being listed in sshcontrol.txt.
*/
while (1)
{ {
dir_entry = readdir (dir); if ((strlen (dir_entry->d_name) == 44)
if (dir_entry) && (! strncmp (dir_entry->d_name + 40, ".key", 4)))
{ {
if ((strlen (dir_entry->d_name) == 44) char hexgrip[41];
&& (! strncmp (dir_entry->d_name + 40, ".key", 4))) int disabled;
{
char hexgrip[41];
int disabled;
/* We do only want to return keys listed in our control /* We do only want to return keys listed in our control
file. */ file. */
strncpy (hexgrip, dir_entry->d_name, 40); strncpy (hexgrip, dir_entry->d_name, 40);
hexgrip[40] = 0; hexgrip[40] = 0;
if ( strlen (hexgrip) != 40 ) if ( strlen (hexgrip) != 40 )
continue; continue;
if (search_control_file (ctrl_fp, hexgrip, &disabled) if (search_control_file (ctrl_fp, hexgrip, &disabled)
|| disabled) || disabled)
continue; continue;
strncpy (key_path + key_directory_n + 1, dir_entry->d_name, 40); strncpy (key_path + key_directory_n + 1, dir_entry->d_name, 40);
/* Read file content. */ /* Read file content. */
err = file_to_buffer (key_path, &buffer, &buffer_n); err = file_to_buffer (key_path, &buffer, &buffer_n);
if (err) if (err)
break; goto out;
err = gcry_sexp_sscan (&key_secret, NULL, buffer, buffer_n); err = gcry_sexp_sscan (&key_secret, NULL, buffer, buffer_n);
if (err) if (err)
break; goto out;
xfree (buffer); xfree (buffer);
buffer = NULL; buffer = NULL;
err = sexp_extract_identifier (key_secret, &key_type); err = sexp_extract_identifier (key_secret, &key_type);
if (err) if (err)
break; goto out;
err = ssh_key_type_lookup (NULL, key_type, &spec); err = ssh_key_type_lookup (NULL, key_type, &spec);
if (err) if (err)
break; goto out;
xfree ((void *) key_type); xfree ((void *) key_type);
key_type = NULL; key_type = NULL;
err = key_secret_to_public (&key_public, spec, key_secret); err = key_secret_to_public (&key_public, spec, key_secret);
if (err) if (err)
break; goto out;
gcry_sexp_release (key_secret); gcry_sexp_release (key_secret);
key_secret = NULL; key_secret = NULL;
err = ssh_send_key_public (key_blobs, key_public); err = ssh_send_key_public (key_blobs, key_public);
if (err) if (err)
break; goto out;
gcry_sexp_release (key_public); gcry_sexp_release (key_public);
key_public = NULL; key_public = NULL;
key_counter++; key_counter++;
} }
}
else
break;
} }
if (err)
goto out;
ret = es_fseek (key_blobs, 0, SEEK_SET); ret = es_fseek (key_blobs, 0, SEEK_SET);
if (ret) if (ret)

View File

@ -1,5 +1,8 @@
2005-02-24 Werner Koch <wk@g10code.com> 2005-02-24 Werner Koch <wk@g10code.com>
* app.c (app_getattr): Return APPTYPE or SERIALNO type even if the
application does dot support the getattr call.
* app-openpgp.c (get_one_do): Never try to get a non cacheable * app-openpgp.c (get_one_do): Never try to get a non cacheable
object from the cache. object from the cache.
(get_one_do): Add new arg to return an error code. Changed all (get_one_do): Add new arg to return an error code. Changed all
@ -13,6 +16,7 @@
been removed without a reset. been removed without a reset.
(do_reset, cmd_serialno): Clear that error flag. (do_reset, cmd_serialno): Clear that error flag.
(TEST_CARD_REMOVAL): New. Use it with all command handlers. (TEST_CARD_REMOVAL): New. Use it with all command handlers.
(scd_update_reader_status_file): Set the error flag on all changes.
* scdaemon.c (ticker_thread): Termintate if a shutdown is pending. * scdaemon.c (ticker_thread): Termintate if a shutdown is pending.

View File

@ -305,6 +305,35 @@ app_getattr (APP app, CTRL ctrl, const char *name)
return gpg_error (GPG_ERR_INV_VALUE); return gpg_error (GPG_ERR_INV_VALUE);
if (!app->initialized) if (!app->initialized)
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
if (app->apptype && name && !strcmp (name, "APPTYPE"))
{
send_status_info (ctrl, "APPTYPE",
app->apptype, strlen (app->apptype), NULL, 0);
return 0;
}
if (name && !strcmp (name, "SERIALNO"))
{
char *serial_and_stamp;
char *serial;
time_t stamp;
int rc;
rc = app_get_serial_and_stamp (app, &serial, &stamp);
if (rc)
return rc;
rc = asprintf (&serial_and_stamp, "%s %lu",
serial, (unsigned long)stamp);
rc = (rc < 0)? gpg_error_from_errno (errno) : 0;
xfree (serial);
if (rc)
return rc;
send_status_info (ctrl, "SERIALNO",
serial_and_stamp, strlen (serial_and_stamp), NULL, 0);
free (serial_and_stamp);
return 0;
}
if (!app->fnc.getattr) if (!app->fnc.getattr)
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
return app->fnc.getattr (app, ctrl, name); return app->fnc.getattr (app, ctrl, name);

View File

@ -239,7 +239,7 @@ percent_plus_unescape (unsigned char *string)
operations are done on the same card unless he calls this function. operations are done on the same card unless he calls this function.
*/ */
static int static int
cmd_serialno (ASSUAN_CONTEXT ctx, char *line) cmd_serialno (assuan_context_t ctx, char *line)
{ {
ctrl_t ctrl = assuan_get_pointer (ctx); ctrl_t ctrl = assuan_get_pointer (ctx);
int rc = 0; int rc = 0;
@ -248,7 +248,8 @@ cmd_serialno (ASSUAN_CONTEXT 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. */
ctrl->server_local->card_removed = 0; if (ctrl->server_local->card_removed)
do_reset (ctrl, 0);
if ((rc = open_card (ctrl, *line? line:NULL))) if ((rc = open_card (ctrl, *line? line:NULL)))
return rc; return rc;
@ -1092,7 +1093,6 @@ cmd_checkpin (ASSUAN_CONTEXT ctx, char *line)
/* Tell the assuan library about our commands */ /* Tell the assuan library about our commands */
static int static int
@ -1299,10 +1299,6 @@ scd_update_reader_status_file (void)
char templ[50]; char templ[50];
FILE *fp; FILE *fp;
last[slot].any = 1;
last[slot].status = status;
last[slot].changed = changed;
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);
sprintf (templ, "reader_%d.status", slot); sprintf (templ, "reader_%d.status", slot);
@ -1318,7 +1314,19 @@ scd_update_reader_status_file (void)
} }
xfree (fname); xfree (fname);
/* Send a signal to the primary client, if any. */ /* 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;
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 if (primary_connection && primary_connection->server_local
&& primary_connection->server_local->assuan_ctx) && primary_connection->server_local->assuan_ctx)
{ {