summaryrefslogtreecommitdiffstats
path: root/scd
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2002-02-28 12:07:59 +0100
committerWerner Koch <wk@gnupg.org>2002-02-28 12:07:59 +0100
commit56341c289cabffb7f468f7a3ee706626a9106a96 (patch)
tree41fdd684c4cfdd7a164f22ae3fc56ba061d19a6c /scd
parent* assuan-client.c (assuan_transact): Add 2 more arguments to (diff)
downloadgnupg2-56341c289cabffb7f468f7a3ee706626a9106a96.tar.xz
gnupg2-56341c289cabffb7f468f7a3ee706626a9106a96.zip
Changes needed to support smartcards. Well, only _support_. There is
no real code yet.
Diffstat (limited to 'scd')
-rw-r--r--scd/Makefile.am6
-rw-r--r--scd/card.c208
-rw-r--r--scd/command.c156
-rw-r--r--scd/scdaemon.c2
-rw-r--r--scd/scdaemon.h5
5 files changed, 347 insertions, 30 deletions
diff --git a/scd/Makefile.am b/scd/Makefile.am
index dca4f3bcb..5379d519a 100644
--- a/scd/Makefile.am
+++ b/scd/Makefile.am
@@ -20,7 +20,8 @@
bin_PROGRAMS = scdaemon
-AM_CPPFLAGS = -I$(top_srcdir)/common $(LIBOPENSC_CFLAGS) $(LIBGCRYPT_CFLAGS)
+AM_CPPFLAGS = -I$(top_srcdir)/common $(LIBOPENSC_CFLAGS) $(LIBGCRYPT_CFLAGS) \
+ $(LIBKSBA_CFLAGS)
LDFLAGS = @LDFLAGS@
scdaemon_SOURCES = \
@@ -29,7 +30,8 @@ scdaemon_SOURCES = \
scdaemon_LDADD = ../jnlib/libjnlib.a ../assuan/libassuan.a \
- ../common/libcommon.a $(LIBOPENSC_LIBS) $(LIBGCRYPT_LIBS)
+ ../common/libcommon.a \
+ $(LIBOPENSC_LIBS) $(LIBGCRYPT_LIBS) $(LIBKSBA_LIBS)
diff --git a/scd/card.c b/scd/card.c
index 3702ae348..dbfbe5333 100644
--- a/scd/card.c
+++ b/scd/card.c
@@ -26,6 +26,7 @@
#include <time.h>
#include <opensc-pkcs15.h>
+#include <ksba.h>
#include "scdaemon.h"
@@ -114,13 +115,13 @@ card_open (CARD *rcard)
}
card->ctx->error_file = log_get_stream ();
card->ctx->debug_file = log_get_stream ();
- if (sc_detect_card (card->ctx, card->reader) != 1)
+ if (sc_detect_card_presence (card->ctx->reader[card->reader], 0) != 1)
{
rc = GNUPG_Card_Not_Present;
goto leave;
}
- rc = sc_connect_card (card->ctx, card->reader, &card->scard);
+ rc = sc_connect_card (card->ctx->reader[card->reader], 0, &card->scard);
if (rc)
{
log_error ("failed to connect card in reader %d: %s\n",
@@ -175,7 +176,7 @@ card_close (CARD card)
if (card->scard)
{
sc_unlock (card->scard);
- sc_disconnect_card (card->scard);
+ sc_disconnect_card (card->scard, 0);
card->scard = NULL;
}
if (card->ctx)
@@ -219,3 +220,204 @@ card_get_serial_and_stamp (CARD card, char **serial, time_t *stamp)
return GNUPG_Out_Of_Core;
return 0;
}
+
+
+
+/* Get the keygrip from CERT, return 0 on success */
+static int
+get_keygrip (KsbaCert cert, unsigned char *array)
+{
+ GCRY_SEXP s_pkey;
+ int rc;
+ KsbaSexp p;
+ size_t n;
+
+ p = ksba_cert_get_public_key (cert);
+ if (!p)
+ return -1; /* oops */
+ n = gcry_sexp_canon_len (p, 0, NULL, NULL);
+ if (!n)
+ return -1; /* libksba did not return a proper S-expression */
+ rc = gcry_sexp_sscan ( &s_pkey, NULL, p, n);
+ xfree (p);
+ if (rc)
+ return -1; /* can't parse that S-expression */
+ array = gcry_pk_get_keygrip (s_pkey, array);
+ gcry_sexp_release (s_pkey);
+ if (!array)
+ return -1; /* failed to calculate the keygrip */
+ return 0;
+}
+
+
+
+/* Enumerate all keypairs on the card and return the Keygrip as well
+ as the internal identification of the key. KEYGRIP must be a
+ caller provided buffer with a size of 20 bytes which will receive
+ the KEYGRIP of the keypair. If KEYID is not NULL, it returns the
+ ID field of the key in allocated memory, NKEYID will then receive
+ the length of it. The function returns -1 when all keys have been
+ enumerated. Note that the error GNUPG_Missing_Certificate may be
+ returned if there is just the private key but no public key (ie.e a
+ certificate) available. Applications might want to continue
+ enumerating after this error.*/
+int
+card_enum_keypairs (CARD card, int idx,
+ unsigned char *keygrip,
+ unsigned char **keyid, size_t *nkeyid)
+{
+ int rc;
+ KsbaError krc;
+ struct sc_pkcs15_prkey_info *pinfo;
+ struct sc_pkcs15_cert_info *certinfo;
+ struct sc_pkcs15_cert *certder;
+ KsbaCert cert;
+
+ if (keyid)
+ *keyid = NULL;
+ if (nkeyid)
+ *nkeyid = 0;
+
+ if (!card || !keygrip || !card->p15card)
+ return GNUPG_Invalid_Value;
+ if (idx < 0)
+ return GNUPG_Invalid_Index;
+
+ rc = sc_pkcs15_enum_private_keys (card->p15card);
+ if (rc < 0)
+ {
+ log_error ("sc_pkcs15_enum_private_keys failed: %s\n", sc_strerror (rc));
+ return GNUPG_Card_Error;
+ }
+ if ( idx >= card->p15card->prkey_count)
+ return -1;
+ pinfo = card->p15card->prkey_info + idx;
+
+ /* now we need to read the certificate so that we can calculate the
+ keygrip */
+ rc = sc_pkcs15_find_cert_by_id (card->p15card, &pinfo->id, &certinfo);
+ if (rc)
+ {
+ log_info ("certificate for private key %d not found: %s\n",
+ idx, sc_strerror (rc));
+ /* but we should return the ID anyway */
+ if (keyid)
+ {
+ *keyid = xtrymalloc (pinfo->id.len);
+ if (!*keyid)
+ return GNUPG_Out_Of_Core;
+ memcpy (*keyid, pinfo->id.value, pinfo->id.len);
+ }
+ if (nkeyid)
+ *nkeyid = pinfo->id.len;
+ return GNUPG_Missing_Certificate;
+ }
+ rc = sc_pkcs15_read_certificate (card->p15card, certinfo, &certder);
+ if (rc)
+ {
+ log_info ("failed to read certificate for private key %d: %s\n",
+ idx, sc_strerror (rc));
+ return GNUPG_Card_Error;
+ }
+
+ cert = ksba_cert_new ();
+ if (!cert)
+ {
+ sc_pkcs15_free_certificate (certder);
+ return GNUPG_Out_Of_Core;
+ }
+ krc = ksba_cert_init_from_mem (cert, certder->data, certder->data_len);
+ sc_pkcs15_free_certificate (certder);
+ if (krc)
+ {
+ log_error ("failed to parse the certificate for private key %d: %s\n",
+ idx, ksba_strerror (krc));
+ ksba_cert_release (cert);
+ return GNUPG_Card_Error;
+ }
+ if (get_keygrip (cert, keygrip))
+ {
+ log_error ("failed to calculate the keygrip of private key %d\n", idx);
+ ksba_cert_release (cert);
+ return GNUPG_Card_Error;
+ }
+ ksba_cert_release (cert);
+
+ /* return the iD */
+ if (keyid)
+ {
+ *keyid = xtrymalloc (pinfo->id.len);
+ if (!*keyid)
+ return GNUPG_Out_Of_Core;
+ memcpy (*keyid, pinfo->id.value, pinfo->id.len);
+ }
+ if (nkeyid)
+ *nkeyid = pinfo->id.len;
+
+ return 0;
+}
+
+
+
+/* Read the certificate identified by CERTIDSTR which is the
+ hexadecimal encoded ID of the certificate, prefixed with the string
+ "3F005015.". The certificate is return in DER encoded form in CERT
+ and NCERT. */
+int
+card_read_cert (CARD card, const char *certidstr,
+ unsigned char **cert, size_t *ncert)
+{
+ struct sc_pkcs15_id certid;
+ struct sc_pkcs15_cert_info *certinfo;
+ struct sc_pkcs15_cert *certder;
+ const char *s;
+ int rc, n;
+
+ if (!card || !certidstr || !card->p15card || !cert || !ncert)
+ return GNUPG_Invalid_Value;
+
+ /* For now we only support the standard DF */
+ if (strncmp (certidstr, "3F005015.", 9) )
+ return GNUPG_Invalid_Id;
+ for (s=certidstr+9, n=0; hexdigitp (s); s++, n++)
+ ;
+ if (*s || (n&1))
+ return GNUPG_Invalid_Id; /* invalid or odd number of digits */
+ n /= 2;
+ if (!n || n > SC_PKCS15_MAX_ID_SIZE)
+ return GNUPG_Invalid_Id; /* empty or too large */
+ for (s=certidstr+9, n=0; *s; s += 2, n++)
+ certid.value[n] = xtoi_2 (s);
+ certid.len = n;
+
+ rc = sc_pkcs15_find_cert_by_id (card->p15card, &certid, &certinfo);
+ if (rc)
+ {
+ log_info ("certificate '%s' not found: %s\n",
+ certidstr, sc_strerror (rc));
+ return -1;
+ }
+ rc = sc_pkcs15_read_certificate (card->p15card, certinfo, &certder);
+ if (rc)
+ {
+ log_info ("failed to read certificate '%s': %s\n",
+ certidstr, sc_strerror (rc));
+ return GNUPG_Card_Error;
+ }
+
+ *cert = xtrymalloc (certder->data_len);
+ if (!*cert)
+ {
+ sc_pkcs15_free_certificate (certder);
+ return GNUPG_Out_Of_Core;
+ }
+ memcpy (*cert, certder->data, certder->data_len);
+ *ncert = certder->data_len;
+ sc_pkcs15_free_certificate (certder);
+ return 0;
+}
+
+
+
+
+
diff --git a/scd/command.c b/scd/command.c
index 10b900628..b4eaa8a47 100644
--- a/scd/command.c
+++ b/scd/command.c
@@ -75,14 +75,20 @@ option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value)
/* LEARN [--force]
Learn all useful information of the currently inserted card. When
- used without the force options, the command might to an INQUIRE
+ used without the force options, the command might do an INQUIRE
like this:
INQUIRE KNOWNCARDP <hexstring_with_serialNumber> <timestamp>
The client should just send an "END" if the processing should go on
or a "CANCEL" to force the function to terminate with a Cancel
- error message.
+ error message. The response of this command is a list of status
+ lines formatted as this:
+
+ S KEYPAIRINFO <hexstring_with_keygrip> <hexstring_with_id>
+
+ If there is no certificate yet stored on the card a single "X" is
+ returned as the keygrip.
*/
static int
@@ -90,6 +96,7 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
{
CTRL ctrl = assuan_get_pointer (ctx);
int rc = 0;
+ int idx;
/* if this is the first command issued for a new card, open the card and
and create a context */
@@ -104,40 +111,138 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
the card using a serial number and inquiring the client with
that. The client may choose to cancel the operation if he already
knows about this card */
- if (!has_option (line, "--force"))
+ {
+ char *serial_and_stamp;
+ char *serial;
+ time_t stamp;
+
+ rc = card_get_serial_and_stamp (ctrl->card_ctx, &serial, &stamp);
+ if (rc)
+ return map_to_assuan_status (rc);
+ rc = asprintf (&serial_and_stamp, "%s %lu", serial, (unsigned long)stamp);
+ xfree (serial);
+ if (rc < 0)
+ return ASSUAN_Out_Of_Core;
+ rc = 0;
+ assuan_write_status (ctx, "SERIALNO", serial_and_stamp);
+
+ if (!has_option (line, "--force"))
+ {
+ char *command;
+
+ rc = asprintf (&command, "KNOWNCARDP %s", serial_and_stamp);
+ if (rc < 0)
+ {
+ free (serial_and_stamp);
+ return ASSUAN_Out_Of_Core;
+ }
+ rc = 0;
+ rc = assuan_inquire (ctx, command, NULL, NULL, 0);
+ free (command); /* (must use standard free here) */
+ if (rc)
+ {
+ if (rc != ASSUAN_Canceled)
+ log_error ("inquire KNOWNCARDP failed: %s\n",
+ assuan_strerror (rc));
+ free (serial_and_stamp);
+ return rc;
+ }
+ /* not canceled, so we have to proceeed */
+ }
+ free (serial_and_stamp);
+ }
+
+ for (idx=0; !rc; idx++)
{
- char *serial;
- time_t stamp;
- char *command;
-
- rc = card_get_serial_and_stamp (ctrl->card_ctx, &serial, &stamp);
- if (rc)
- return map_to_assuan_status (rc);
-
- rc = asprintf (&command, "KNOWNCARDP %s %lu",
- serial, (unsigned long)stamp);
- xfree (serial);
- if (rc < 0)
- return ASSUAN_Out_Of_Core;
- rc = 0;
- rc = assuan_inquire (ctx, command, NULL, NULL, 0);
- free (command); /* (must use standard free here) */
- if (rc)
+ unsigned char keygrip[20];
+ unsigned char *keyid;
+ size_t nkeyid;
+ int no_cert = 0;
+
+ rc = card_enum_keypairs (ctrl->card_ctx, idx,
+ keygrip, &keyid, &nkeyid);
+ if (rc == GNUPG_Missing_Certificate && keyid)
+ {
+ /* this does happen with an incomplete personalized
+ card; i.e. during the time we have stored the key on the
+ card but not stored the certificate; probably becuase it
+ has not yet been received back from the CA. Note that we
+ must release KEYID in this case. */
+ rc = 0;
+ no_cert = 1;
+ }
+ if (!rc)
{
- if (rc != ASSUAN_Canceled)
- log_error ("inquire KNOWNCARDP failed: %s\n",
- assuan_strerror (rc));
- return rc;
+ char *buf, *p;
+
+ buf = p = xtrymalloc (40+1+9+2*nkeyid+1);
+ if (!buf)
+ rc = GNUPG_Out_Of_Core;
+ else
+ {
+ int i;
+
+ if (no_cert)
+ *p++ = 'X';
+ else
+ {
+ for (i=0; i < 20; i++, p += 2)
+ sprintf (p, "%02X", keygrip[i]);
+ }
+ *p++ = ' ';
+ /* fixme: we need to get the pkcs-15 DF from the card function */
+ p = stpcpy (p, "3F005015.");
+ for (i=0; i < nkeyid; i++, p += 2)
+ sprintf (p, "%02X", keyid[i]);
+ *p = 0;
+ assuan_write_status (ctx, "KEYPAIRINFO", buf);
+ xfree (buf);
+ }
}
- /* not canceled, so we have to proceeed */
+ xfree (keyid);
}
+ if (rc == -1)
+ rc = 0;
+
return map_to_assuan_status (rc);
}
+
+/* READCERT <hexified_certid>
+ */
+static int
+cmd_readcert (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc;
+ unsigned char *cert;
+ size_t ncert;
+ if (!ctrl->card_ctx)
+ {
+ rc = card_open (&ctrl->card_ctx);
+ if (rc)
+ return map_to_assuan_status (rc);
+ }
+
+ rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert);
+ if (rc)
+ {
+ log_error ("card_read_cert failed: %s\n", gnupg_strerror (rc));
+ }
+ if (!rc)
+ {
+ rc = assuan_send_data (ctx, cert, ncert);
+ xfree (cert);
+ if (rc)
+ return rc;
+ }
+
+ return map_to_assuan_status (rc);
+}
/* Tell the assuan library about our commands */
@@ -150,6 +255,7 @@ register_commands (ASSUAN_CONTEXT ctx)
int (*handler)(ASSUAN_CONTEXT, char *line);
} table[] = {
{ "LEARN", 0, cmd_learn },
+ { "READCERT", 0, cmd_readcert },
{ "", ASSUAN_CMD_INPUT, NULL },
{ "", ASSUAN_CMD_OUTPUT, NULL },
{ NULL }
diff --git a/scd/scdaemon.c b/scd/scdaemon.c
index d217b0020..92bccec9e 100644
--- a/scd/scdaemon.c
+++ b/scd/scdaemon.c
@@ -34,6 +34,7 @@
#include <unistd.h>
#include <signal.h>
+#include <ksba.h>
#include <gcrypt.h>
#define JNLIB_NEED_LOG_LOGV
@@ -242,6 +243,7 @@ main (int argc, char **argv )
"1.1.5", gcry_check_version (NULL) );
}
+ ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
assuan_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
gcry_set_log_handler (my_gcry_logger, NULL);
gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
diff --git a/scd/scdaemon.h b/scd/scdaemon.h
index 7d43dc129..e77012416 100644
--- a/scd/scdaemon.h
+++ b/scd/scdaemon.h
@@ -77,6 +77,11 @@ void scd_command_handler (int);
int card_open (CARD *rcard);
void card_close (CARD card);
int card_get_serial_and_stamp (CARD card, char **serial, time_t *stamp);
+int card_enum_keypairs (CARD card, int idx,
+ unsigned char *keygrip,
+ unsigned char **keyid, size_t *nkeyid);
+int card_read_cert (CARD card, const char *certidstr,
+ unsigned char **cert, size_t *ncert);