diff options
Diffstat (limited to 'scd')
-rw-r--r-- | scd/ChangeLog | 31 | ||||
-rw-r--r-- | scd/Makefile.am | 1 | ||||
-rw-r--r-- | scd/app-common.h | 90 | ||||
-rw-r--r-- | scd/app-openpgp.c | 390 | ||||
-rw-r--r-- | scd/app.c | 208 | ||||
-rw-r--r-- | scd/card.c | 3 | ||||
-rw-r--r-- | scd/command.c | 290 | ||||
-rw-r--r-- | scd/sc-investigate.c | 14 | ||||
-rw-r--r-- | scd/scdaemon.c | 4 | ||||
-rw-r--r-- | scd/scdaemon.h | 5 |
10 files changed, 970 insertions, 66 deletions
diff --git a/scd/ChangeLog b/scd/ChangeLog index 6a8668b45..a6407b940 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,34 @@ +2003-06-26 Werner Koch <[email protected]> + + * app-openpgp.c (find_tlv): Fixed length header parsing. + + * app.c (app_genkey): New. + * command.c (cmd_genkey): New. + +2003-06-25 Werner Koch <[email protected]> + + * command.c (percent_plus_unescape): New. + (cmd_setattr): New. + +2003-06-24 Werner Koch <[email protected]> + + * command.c (send_status_info): New. + + * app-openpgp.c (app_select_openpgp): Replace SLOT arg by APP arg + and setup the function pointers in APP on success. Changed callers. + * app.c: New. + * app-common.h: New. + * scdaemon.h (APP): New type to handle applications. + (server_control_s): Add an APP context field. + + * command.c (cmd_serialno): Handle applications. + (cmd_pksign): Ditto. + (cmd_pkdecrypt): Ditto. + (reset_notify): Ditto. + (cmd_learn): For now return error for application contexts. + (cmd_readcert): Ditto. + (cmd_readkey): Ditto. + 2003-06-04 Werner Koch <[email protected]> * card.c (map_sc_err): Renamed gpg_make_err to gpg_err_make. diff --git a/scd/Makefile.am b/scd/Makefile.am index 55d195f11..833a4cdf9 100644 --- a/scd/Makefile.am +++ b/scd/Makefile.am @@ -33,6 +33,7 @@ scdaemon_SOURCES = \ card-p15.c card-dinsig.c \ apdu.c apdu.h \ iso7816.c iso7816.h \ + app.c app-common.h \ app-openpgp.c scdaemon_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \ diff --git a/scd/app-common.h b/scd/app-common.h new file mode 100644 index 000000000..16ee7d708 --- /dev/null +++ b/scd/app-common.h @@ -0,0 +1,90 @@ +/* app-common.h - Common declarations for all card applications + * Copyright (C) 2003 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#ifndef GNUPG_SCD_APP_COMMON_H +#define GNUPG_SCD_APP_COMMON_H + +struct app_ctx_s { + int initialized; /* The application has been initialied and the + function pointers may be used. Note that for + unsupported operations the particular + function pointer is set to NULL */ + int slot; /* Used reader. */ + unsigned char *serialno; /* Serialnumber in raw form, allocated. */ + size_t serialnolen; /* Length in octets of serialnumber. */ + int did_chv1; + int did_chv2; + int did_chv3; + struct { + int (*learn_status) (APP app, CTRL ctrl); + int (*setattr) (APP app, const char *name, + int (*pincb)(void*, const char *, char **), + void *pincb_arg, + const unsigned char *value, size_t valuelen); + int (*sign) (APP app, + const char *keyidstr, int hashalgo, + int (pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + void **outdata, size_t *outdatalen ); + int (*decipher) (APP app, const char *keyidstr, + int (pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + void **outdata, size_t *outdatalen); + int (*genkey) (APP app, CTRL ctrl, + const char *keynostr, unsigned int flags, + int (*pincb)(void*, const char *, char **), + void *pincb_arg); + } fnc; + + +}; + +/*-- app.c --*/ +APP select_application (void); +int app_get_serial_and_stamp (APP app, char **serial, time_t *stamp); +int app_write_learn_status (APP app, CTRL ctrl); +int app_setattr (APP app, const char *name, + int (*pincb)(void*, const char *, char **), + void *pincb_arg, + const unsigned char *value, size_t valuelen); +int app_sign (APP app, const char *keyidstr, int hashalgo, + int (pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + void **outdata, size_t *outdatalen ); +int app_decipher (APP app, const char *keyidstr, + int (pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + void **outdata, size_t *outdatalen ); +int app_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags, + int (*pincb)(void*, const char *, char **), + void *pincb_arg); + +/*-- app-openpgp.c --*/ +int app_select_openpgp (APP app, unsigned char **sn, size_t *snlen); + + +#endif /*GNUPG_SCD_APP_COMMON_H*/ + + + diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 82f004347..47bebed2c 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -26,8 +26,11 @@ #include <dlfcn.h> #include "scdaemon.h" +#include "app-common.h" #include "iso7816.h" + + static struct { int tag; int constructed; @@ -80,6 +83,12 @@ find_tlv (const unsigned char *buffer, size_t length, buffer = s; if (n < 2) return NULL; /* buffer definitely too short for tag and length. */ + if (!*s || *s == 0xff) + { /* Skip optional filler between TLV objects. */ + s++; + n--; + continue; + } composite = !!(*s & 0x20); if ((*s & 0x1f) == 0x1f) { /* more tag bytes to follow */ @@ -95,13 +104,26 @@ find_tlv (const unsigned char *buffer, size_t length, this_tag = s[0]; len = s[1]; s += 2; n -= 2; - if (len == 255) - { + if (len < 0x80) + ; + else if (len == 0x81) + { /* One byte length follows. */ + if (!n) + return NULL; /* we expected 1 more bytes with the length. */ + len = s[0]; + s++; n--; + } + else if (len == 0x82) + { /* Two byte length follows. */ if (n < 2) return NULL; /* we expected 2 more bytes with the length. */ len = (s[0] << 8) | s[1]; s += 2; n -= 2; } + else + return NULL; /* APDU limit is 65535, thus it does not make + sense to assume longer length fields. */ + if (composite && nestlevel < 100) { /* Dive into this composite DO after checking for too deep nesting. */ @@ -128,7 +150,64 @@ find_tlv (const unsigned char *buffer, size_t length, } +/* Get the DO identified by TAG from the card in SLOT and return a + buffer with its content in RESULT and NBYTES. The return value is + NULL if not found or a pointer which must be used to release the + buffer holding value. */ +static void * +get_one_do (int slot, int tag, unsigned char **result, size_t *nbytes) +{ + int rc, i; + unsigned char *buffer; + size_t buflen; + unsigned char *value; + size_t valuelen; + + *result = NULL; + *nbytes = 0; + for (i=0; data_objects[i].tag && data_objects[i].tag != tag; i++) + ; + + value = NULL; + rc = -1; + if (data_objects[i].tag && data_objects[i].get_from) + { + rc = iso7816_get_data (slot, data_objects[i].get_from, + &buffer, &buflen); + if (!rc) + { + value = find_tlv (buffer, buflen, tag, &valuelen, 0); + if (!value) + ; /* not found */ + else if (valuelen > buflen - (value - buffer)) + { + log_error ("warning: constructed DO too short\n"); + value = NULL; + xfree (buffer); buffer = NULL; + } + } + } + + if (!value) /* Not in a constructed DO, try simple. */ + { + rc = iso7816_get_data (slot, tag, &buffer, &buflen); + if (!rc) + { + value = buffer; + valuelen = buflen; + } + } + + if (!rc) + { + *nbytes = valuelen; + *result = value; + return buffer; + } + return NULL; +} +#if 0 /* not used */ static void dump_one_do (int slot, int tag) { @@ -191,6 +270,7 @@ dump_one_do (int slot, int tag) xfree (buffer); } } +#endif /*not used*/ static void @@ -257,15 +337,15 @@ dump_all_do (int slot) } } - +/* Note, that FPR must be at least 20 bytes. */ static int store_fpr (int slot, int keynumber, u32 timestamp, const unsigned char *m, size_t mlen, - const unsigned char *e, size_t elen) + const unsigned char *e, size_t elen, + unsigned char *fpr) { unsigned int n; unsigned char *buffer, *p; - unsigned char fpr[20]; int rc; n = 6 + 2 + mlen + 2 + elen; @@ -299,45 +379,189 @@ store_fpr (int slot, int keynumber, u32 timestamp, return rc; } + +static void +send_fpr_if_not_null (CTRL ctrl, const char *keyword, + int number, const unsigned char *fpr) +{ + int i; + char buf[41]; + char numbuf[25]; + + for (i=0; i < 20 && !fpr[i]; i++) + ; + if (i==20) + return; /* All zero. */ + for (i=0; i< 20; i++) + sprintf (buf+2*i, "%02X", fpr[i]); + if (number == -1) + *numbuf = 0; /* Don't print the key number */ + else + sprintf (numbuf, "%d", number); + send_status_info (ctrl, keyword, + numbuf, (size_t)strlen(numbuf), + buf, (size_t)strlen (buf), NULL, 0); +} + +static void +send_key_data (CTRL ctrl, const char *name, + const unsigned char *a, size_t alen) +{ + char *p, *buf = xmalloc (alen*2+1); + + for (p=buf; alen; a++, alen--, p += 2) + sprintf (p, "%02X", *a); + + send_status_info (ctrl, "KEY-DATA", + name, (size_t)strlen(name), + buf, (size_t)strlen (buf), + NULL, 0); + xfree (buf); +} -/* Generate a new key on the card and store the fingerprint in the - corresponding DO. A KEYNUMBER of 0 creates the digital signature - key, 1 the encryption key and 2 the authentication key. If the key - already exists an error is returned unless FORCE has been set to - true. Note, that the function does not return the public key; this - has to be done using openpgp_readkey(). */ -int -openpgp_genkey (int slot, int keynumber, int force) + +static int +do_learn_status (APP app, CTRL ctrl) +{ + void *relptr; + unsigned char *value; + size_t valuelen; + int i; + + relptr = get_one_do (app->slot, 0x005B, &value, &valuelen); + if (relptr) + { + send_status_info (ctrl, "DISP-NAME", value, valuelen, NULL, 0); + xfree (relptr); + } + relptr = get_one_do (app->slot, 0x5FF0, &value, &valuelen); + if (relptr) + { + send_status_info (ctrl, "PUBKEY-URL", value, valuelen, NULL, 0); + xfree (relptr); + } + + relptr = get_one_do (app->slot, 0x00C5, &value, &valuelen); + if (relptr && valuelen >= 60) + { + for (i=0; i < 3; i++) + send_fpr_if_not_null (ctrl, "KEY-FPR", i+1, value+i*20); + } + xfree (relptr); + relptr = get_one_do (app->slot, 0x00C6, &value, &valuelen); + if (relptr && valuelen >= 60) + { + for (i=0; i < 3; i++) + send_fpr_if_not_null (ctrl, "CA-FPR", i+1, value+i*20); + } + xfree (relptr); + return 0; +} + + +/* Handle the SETATTR operation. All arguments are already basically + checked. */ +static int +do_setattr (APP app, const char *name, + int (*pincb)(void*, const char *, char **), + void *pincb_arg, + const unsigned char *value, size_t valuelen) +{ + gpg_error_t rc; + + log_debug ("app_openpgp#setattr `%s' value of length %u\n", + name, (unsigned int)valuelen); /* fixme: name should be + sanitized. */ + + if (!app->did_chv3) + { + char *pinvalue; + +/* rc = pincb (pincb_arg, "Please enter the card's admin PIN (CHV3)", */ +/* &pinvalue); */ + pinvalue = xstrdup ("12345678"); + rc = 0; + if (rc) + { + log_info ("PIN callback returned error: %s\n", gpg_strerror (rc)); + return rc; + } + + rc = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue)); + xfree (pinvalue); + if (rc) + { + log_error ("verify CHV3 failed\n"); + rc = gpg_error (GPG_ERR_GENERAL); + return rc; + } + app->did_chv3 = 1; + } + + log_debug ("setting `%s' to `%.*s'\n", name, (int)valuelen, value); + if (!strcmp (name, "DISP-NAME")) + { + rc = iso7816_put_data (app->slot, 0x005B, value, valuelen); + if (rc) + { + /* FIXME: If this fails we should *once* try again after + doing a verify command, so that in case of a problem with + tracking the verify operation we have a fallback. */ + /* FIXME: change this when iso7816 returns correct error + codes. */ + log_error ("failed to set `Name'\n"); + rc = gpg_error (GPG_ERR_GENERAL); + } + } + else + rc = gpg_error (GPG_ERR_INV_NAME); + + return rc; +} + + +/* Handle the GENKEY command. */ +static int +do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags, + int (*pincb)(void*, const char *, char **), + void *pincb_arg) { int rc; int i; + char numbuf[30]; + unsigned char fprbuf[20]; const unsigned char *fpr; const unsigned char *keydata, *m, *e; unsigned char *buffer; size_t buflen, keydatalen, n, mlen, elen; time_t created_at; - - if (keynumber < 0 || keynumber > 2) - return -1; /* invalid value */ + int keyno = atoi (keynostr); + int force = (flags & 1); - rc = iso7816_get_data (slot, 0x006E, &buffer, &buflen); + if (keyno < 1 || keyno > 3) + return gpg_error (GPG_ERR_INV_ID); + keyno--; + + rc = iso7816_get_data (app->slot, 0x006E, &buffer, &buflen); if (rc) { log_error ("error reading application data\n"); - return -1; + return gpg_error (GPG_ERR_GENERAL); } fpr = find_tlv (buffer, buflen, 0x00C5, &n, 0); if (!fpr || n != 60) { + rc = gpg_error (GPG_ERR_GENERAL); log_error ("error reading fingerprint DO\n"); goto leave; } - fpr += 20*keynumber; + fpr += 20*keyno; for (i=0; i < 20 && !fpr[i]; i++) ; if (i!=20 && !force) { + rc = gpg_error (GPG_ERR_EEXIST); log_error ("key already exists\n"); goto leave; } @@ -346,8 +570,7 @@ openpgp_genkey (int slot, int keynumber, int force) else log_info ("generating new key\n"); - - rc = iso7816_verify (slot, 0x83, "12345678", 8); + rc = iso7816_verify (app->slot, 0x83, "12345678", 8); if (rc) { log_error ("verify CHV3 failed: rc=%04X\n", rc); @@ -355,13 +578,14 @@ openpgp_genkey (int slot, int keynumber, int force) } xfree (buffer); buffer = NULL; - rc = iso7816_generate_keypair (slot, - keynumber == 0? "\xB6" : - keynumber == 1? "\xB8" : "\xA4", + rc = iso7816_generate_keypair (app->slot, + keyno == 0? "\xB6" : + keyno == 1? "\xB8" : "\xA4", 2, &buffer, &buflen); if (rc) { + rc = gpg_error (GPG_ERR_CARD); log_error ("generating key failed\n"); goto leave; } @@ -372,7 +596,6 @@ openpgp_genkey (int slot, int keynumber, int force) goto leave; } - m = find_tlv (keydata, keydatalen, 0x0081, &mlen, 0); if (!m) { @@ -380,6 +603,8 @@ openpgp_genkey (int slot, int keynumber, int force) goto leave; } log_printhex ("RSA n:", m, mlen); + send_key_data (ctrl, "n", m, mlen); + e = find_tlv (keydata, keydatalen, 0x0082, &elen, 0); if (!e) { @@ -387,8 +612,18 @@ openpgp_genkey (int slot, int keynumber, int force) goto leave; } log_printhex ("RSA e:", e, elen); + send_key_data (ctrl, "e", e, elen); + created_at = gnupg_get_time (); - rc = store_fpr (slot, keynumber, (u32)created_at, m, mlen, e, elen); + sprintf (numbuf, "%lu", (unsigned long)created_at); + send_status_info (ctrl, "KEY-CREATED-AT", + numbuf, (size_t)strlen(numbuf), NULL, 0); + + rc = store_fpr (app->slot, keyno, (u32)created_at, + m, mlen, e, elen, fprbuf); + if (rc) + goto leave; + send_fpr_if_not_null (ctrl, "KEY-FPR", -1, fprbuf); leave: @@ -397,12 +632,75 @@ openpgp_genkey (int slot, int keynumber, int force) } +/* Comopute a digital signature on INDATA which is expected to be the + raw message digest. */ +static int +do_sign (APP app, const char *keyidstr, int hashalgo, + int (*pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + void **outdata, size_t *outdatalen ) +{ + static unsigned char sha1_prefix[15] = /* Object ID is 1.3.14.3.2.26 */ + { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, + 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 }; + static unsigned char rmd160_prefix[15] = /* Object ID is 1.3.36.3.2.1 */ + { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03, + 0x02, 0x01, 0x05, 0x00, 0x04, 0x14 }; + int rc; + unsigned char data[35]; + + /* We ignore KEYIDSTR, because the OpenPGP application has only one + signing key and no way to specify a different one. */ + + if (indatalen != 20) + return gpg_error (GPG_ERR_INV_VALUE); + if (hashalgo == GCRY_MD_SHA1) + memcpy (data, sha1_prefix, 15); + else if (hashalgo == GCRY_MD_RMD160) + memcpy (data, rmd160_prefix, 15); + else + return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); + memcpy (data+15, indata, indatalen); + + + if (!app->did_chv1) + { + char *pinvalue; + +/* rc = pincb (pincb_arg, "signature PIN", &pinvalue); */ + pinvalue = xstrdup ("123456"); + rc = 0; + if (rc) + { + log_info ("PIN callback returned error: %s\n", gpg_strerror (rc)); + return rc; + } + + rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue)); + xfree (pinvalue); + if (rc) + { + log_error ("verify CHV1 failed\n"); + rc = gpg_error (GPG_ERR_GENERAL); + return rc; + } + app->did_chv1 = 1; + } + + rc = iso7816_compute_ds (app->slot, data, 35, outdata, outdatalen); + + return rc; +} + + /* Select the OpenPGP application on the card in SLOT. This function - must be used to before any other OpenPGP application functions. */ + must be used before any other OpenPGP application functions. */ int -app_select_openpgp (int slot) +app_select_openpgp (APP app, unsigned char **sn, size_t *snlen) { static char const aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 }; + int slot = app->slot; int rc; unsigned char *buffer; size_t buflen; @@ -416,27 +714,31 @@ app_select_openpgp (int slot) if (rc) goto leave; if (opt.verbose) - log_info ("got AID: "); - log_printhex ("", buffer, buflen); - xfree (buffer); + { + log_info ("got AID: "); + log_printhex ("", buffer, buflen); + } + + if (sn) + { + *sn = buffer; + *snlen = buflen; + } + else + xfree (buffer); dump_all_do (slot); -/* rc = iso7816_verify (slot, 0x83, "12345678", 8); */ -/* if (rc) */ -/* log_error ("verify CHV3 failed: rc=%04X\n", rc); */ - - -/* rc = iso7816_put_data (slot, 0x005B, "Joe Hacker", 10); */ -/* if (rc) */ -/* log_error ("failed to set `Name': rc=%04X\n", rc); */ -/* else */ -/* dump_one_do (slot, 0x005B); */ - - /* fixme: associate the internal state with the slot */ - } + app->fnc.learn_status = do_learn_status; + app->fnc.setattr = do_setattr; + app->fnc.genkey = do_genkey; + app->fnc.sign = do_sign; + } leave: return rc; } + + + diff --git a/scd/app.c b/scd/app.c new file mode 100644 index 000000000..3688b1c8d --- /dev/null +++ b/scd/app.c @@ -0,0 +1,208 @@ +/* app.c - Application selection. + * Copyright (C) 2003 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include <config.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <dlfcn.h> + +#include "scdaemon.h" +#include "app-common.h" +#include "apdu.h" + +/* The select the best fitting application and return a context. + Returns NULL if no application was found or no card is present. */ +APP +select_application (void) +{ + int reader_port = 32768; /* First USB reader. */ + int slot; + int rc; + APP app; + + slot = apdu_open_reader (reader_port); + if (slot == -1) + { + log_error ("card reader not available\n"); + return NULL; + } + + app = xtrycalloc (1, sizeof *app); + if (!app) + { + rc = out_of_core (); + log_info ("error allocating context: %s\n", gpg_strerror (rc)); + /*apdu_close_reader (slot);*/ + return NULL; + } + + app->slot = slot; + rc = app_select_openpgp (app, &app->serialno, &app->serialnolen); + if (rc) + { +/* apdu_close_reader (slot); */ + log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc)); + xfree (app); + return NULL; + } + + app->initialized = 1; + return app; +} + + + +/* Retrieve the serial number and the time of the last update of the + card. The serial number is returned as a malloced string (hex + encoded) in SERIAL and the time of update is returned in STAMP. If + no update time is available the returned value is 0. Caller must + free SERIAL unless the function returns an error. */ +int +app_get_serial_and_stamp (APP app, char **serial, time_t *stamp) +{ + unsigned char *buf, *p; + int i; + + if (!app || !serial || !stamp) + return gpg_error (GPG_ERR_INV_VALUE); + + *serial = NULL; + *stamp = 0; /* not available */ + + buf = xtrymalloc (app->serialnolen * 2 + 1); + if (!buf) + return gpg_error_from_errno (errno); + for (p=buf, i=0; i < app->serialnolen; p +=2, i++) + sprintf (p, "%02X", app->serialno[i]); + *p = 0; + *serial = buf; + return 0; +} + + +/* Write out the application specifig status lines for the LEARN + command. */ +int +app_write_learn_status (APP app, CTRL ctrl) +{ + if (!app) + return gpg_error (GPG_ERR_INV_VALUE); + if (!app->initialized) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if (!app->fnc.learn_status) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + return app->fnc.learn_status (app, ctrl); +} + + +/* Perform a SETATTR operation. */ +int +app_setattr (APP app, const char *name, + int (*pincb)(void*, const char *, char **), + void *pincb_arg, + const unsigned char *value, size_t valuelen) +{ + if (!app || !name || !*name || !value) + return gpg_error (GPG_ERR_INV_VALUE); + if (!app->initialized) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if (!app->fnc.setattr) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + return app->fnc.setattr (app, name, pincb, pincb_arg, value, valuelen); +} + +/* Create the signature and return the allocated result in OUTDATA. + If a PIN is required the PINCB will be used to ask for the PIN; it + should return the PIN in an allocated buffer and put it into PIN. */ +int +app_sign (APP app, const char *keyidstr, int hashalgo, + int (pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + void **outdata, size_t *outdatalen ) +{ + int rc; + + if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb) + return gpg_error (GPG_ERR_INV_VALUE); + if (!app->initialized) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if (!app->fnc.sign) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + rc = app->fnc.sign (app, keyidstr, hashalgo, + pincb, pincb_arg, + indata, indatalen, + outdata, outdatalen); + if (opt.verbose) + log_info ("operation sign result: %s\n", gpg_strerror (rc)); + return rc; +} + + +/* Decrypt the data in INDATA and return the allocated result in OUTDATA. + If a PIN is required the PINCB will be used to ask for the PIN; it + should return the PIN in an allocated buffer and put it into PIN. */ +int +app_decipher (APP app, const char *keyidstr, + int (pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + void **outdata, size_t *outdatalen ) +{ + int rc; + + if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb) + return gpg_error (GPG_ERR_INV_VALUE); + if (!app->initialized) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if (!app->fnc.decipher) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + rc = app->fnc.decipher (app, keyidstr, + pincb, pincb_arg, + indata, indatalen, + outdata, outdatalen); + if (opt.verbose) + log_info ("operation decipher result: %s\n", gpg_strerror (rc)); + return rc; +} + + +/* Perform a SETATTR operation. */ +int +app_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags, + int (*pincb)(void*, const char *, char **), + void *pincb_arg) +{ + int rc; + + if (!app || !keynostr || !*keynostr || !pincb) + return gpg_error (GPG_ERR_INV_VALUE); + if (!app->initialized) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if (!app->fnc.genkey) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + rc = app->fnc.genkey (app, ctrl, keynostr, flags, pincb, pincb_arg); + if (opt.verbose) + log_info ("operation genkey result: %s\n", gpg_strerror (rc)); + return rc; +} + diff --git a/scd/card.c b/scd/card.c index 574867a58..e46bb9d21 100644 --- a/scd/card.c +++ b/scd/card.c @@ -59,7 +59,7 @@ map_sc_err (int rc) int card_help_get_keygrip (KsbaCert cert, unsigned char *array) { - GCRY_SEXP s_pkey; + gcry_sexp_t s_pkey; int rc; KsbaSexp p; size_t n; @@ -558,3 +558,4 @@ card_decipher (CARD card, const char *keyidstr, log_info ("card operation decipher result: %s\n", gpg_strerror (rc)); return rc; } + diff --git a/scd/command.c b/scd/command.c index 8267c13ab..9f164f28e 100644 --- a/scd/command.c +++ b/scd/command.c @@ -30,6 +30,7 @@ #include <assuan.h> #include "scdaemon.h" +#include "app-common.h" /* maximum length aloowed as a PIN; used for INQUIRE NEEDPIN */ #define MAXLEN_PIN 100 @@ -69,6 +70,12 @@ reset_notify (ASSUAN_CONTEXT ctx) xfree (ctrl->in_data.value); ctrl->in_data.value = NULL; } + if (ctrl->app_ctx) + { + /* FIXME: close the application. */ + xfree (ctrl->app_ctx); + ctrl->app_ctx = NULL; + } } @@ -85,8 +92,14 @@ option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value) static AssuanError open_card (CTRL ctrl) { - if (!ctrl->card_ctx) - { + if (ctrl->app_ctx) + return 0; /* Already initialized for one specific application. */ + if (ctrl->card_ctx) + return 0; /* Already initialized using a card context. */ + + ctrl->app_ctx = select_application (); + if (!ctrl->app_ctx) + { /* No application found - fall back to old mode. */ int rc = card_open (&ctrl->card_ctx); if (rc) return map_to_assuan_status (rc); @@ -95,6 +108,41 @@ open_card (CTRL ctrl) } +/* Do the percent and plus/space unescaping in place and return tghe + length of the valid buffer. */ +static size_t +percent_plus_unescape (unsigned char *string) +{ + unsigned char *p = string; + size_t n = 0; + + while (*string) + { + if (*string == '%' && string[1] && string[2]) + { + string++; + *p++ = xtoi_2 (string); + n++; + string+= 2; + } + else if (*string == '+') + { + *p++ = ' '; + n++; + string++; + } + else + { + *p++ = *string++; + n++; + } + } + + return n; +} + + + /* SERIALNO Return the serial number of the card using a status reponse. This @@ -106,7 +154,7 @@ open_card (CTRL ctrl) Background: We want to keep the client clear of handling card changes between operations; i.e. the client can assume that all - operations are doneon the same card unless he call this function. + operations are done on the same card unless he call this function. */ static int cmd_serialno (ASSUAN_CONTEXT ctx, char *line) @@ -120,7 +168,10 @@ cmd_serialno (ASSUAN_CONTEXT ctx, char *line) if ((rc = open_card (ctrl))) return rc; - rc = card_get_serial_and_stamp (ctrl->card_ctx, &serial, &stamp); + if (ctrl->app_ctx) + rc = app_get_serial_and_stamp (ctrl->app_ctx, &serial, &stamp); + else + 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); @@ -149,6 +200,16 @@ cmd_serialno (ASSUAN_CONTEXT ctx, char *line) error message. The response of this command is a list of status lines formatted as this: + S APPTYPE <apptype> + + This returns the type of the application, currently the strings: + + P15 = PKCS-15 structure used + DINSIG = DIN SIG + OPENPGP = OpenPGP card + + are implemented. These strings are aliases for the AID + S KEYPAIRINFO <hexstring_with_keygrip> <hexstring_with_id> If there is no certificate yet stored on the card a single "X" is @@ -157,13 +218,34 @@ cmd_serialno (ASSUAN_CONTEXT ctx, char *line) S CERTINFO <certtype> <hexstring_with_id> - Where CERTINFO is a number indicating the type of certificate: + Where CERTTYPE is a number indicating the type of certificate: 0 := Unknown 100 := Regular X.509 cert 101 := Trusted X.509 cert 102 := Useful X.509 cert + For certain cards, more information will be returned: + + S KEY-FPR <no> <hexstring> + For OpenPGP cards this returns the stored fingerprints of the + keys. This can be used check whether a key is available on the + card. NO may be 1, 2 or 3. + + S CA-FPR <no> <hexstring> + + Similar to above, these are the fingerprints of keys assumed to be + ultimately trusted. + + S DISP-NAME <name_of_card_holder> + + The name of the card holder as stored on the card; percent + aescaping takes place, spaces are encoded as '+' + + S PUBKEY-URL <url> + + The URL to be used for locating the entire public key. + */ static int cmd_learn (ASSUAN_CONTEXT ctx, char *line) @@ -183,8 +265,11 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line) char *serial_and_stamp; char *serial; time_t stamp; - - rc = card_get_serial_and_stamp (ctrl->card_ctx, &serial, &stamp); + + if (ctrl->app_ctx) + rc = app_get_serial_and_stamp (ctrl->app_ctx, &serial, &stamp); + else + 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); @@ -221,6 +306,8 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line) } /* Return information about the certificates. */ + if (ctrl->app_ctx) + rc = -1; /* This information is not yet available for applications. */ for (idx=0; !rc; idx++) { char *certid; @@ -248,6 +335,8 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line) /* Return information about the keys. */ + if (ctrl->app_ctx) + rc = -1; /* This information is not yet available for applications. */ for (idx=0; !rc; idx++) { unsigned char keygrip[20]; @@ -294,6 +383,9 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line) if (rc == -1) rc = 0; + if (!rc && ctrl->app_ctx) + rc = app_write_learn_status (ctrl->app_ctx, ctrl); + return map_to_assuan_status (rc); } @@ -314,6 +406,9 @@ cmd_readcert (ASSUAN_CONTEXT ctx, char *line) if ((rc = open_card (ctrl))) return rc; + if (ctrl->app_ctx) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert); if (rc) { @@ -348,6 +443,9 @@ cmd_readkey (ASSUAN_CONTEXT ctx, char *line) if ((rc = open_card (ctrl))) return rc; + if (ctrl->app_ctx) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert); if (rc) { @@ -480,11 +578,19 @@ cmd_pksign (ASSUAN_CONTEXT ctx, char *line) keyidstr = strdup (line); if (!keyidstr) return ASSUAN_Out_Of_Core; - rc = card_sign (ctrl->card_ctx, - keyidstr, GCRY_MD_SHA1, - pin_cb, ctx, - ctrl->in_data.value, ctrl->in_data.valuelen, - &outdata, &outdatalen); + + if (ctrl->app_ctx) + rc = app_sign (ctrl->app_ctx, + keyidstr, GCRY_MD_SHA1, + pin_cb, ctx, + ctrl->in_data.value, ctrl->in_data.valuelen, + &outdata, &outdatalen); + else + rc = card_sign (ctrl->card_ctx, + keyidstr, GCRY_MD_SHA1, + pin_cb, ctx, + ctrl->in_data.value, ctrl->in_data.valuelen, + &outdata, &outdatalen); free (keyidstr); if (rc) { @@ -519,11 +625,18 @@ cmd_pkdecrypt (ASSUAN_CONTEXT ctx, char *line) keyidstr = strdup (line); if (!keyidstr) return ASSUAN_Out_Of_Core; - rc = card_decipher (ctrl->card_ctx, - keyidstr, - pin_cb, ctx, - ctrl->in_data.value, ctrl->in_data.valuelen, - &outdata, &outdatalen); + if (ctrl->app_ctx) + rc = app_decipher (ctrl->app_ctx, + keyidstr, + pin_cb, ctx, + ctrl->in_data.value, ctrl->in_data.valuelen, + &outdata, &outdatalen); + else + rc = card_decipher (ctrl->card_ctx, + keyidstr, + pin_cb, ctx, + ctrl->in_data.value, ctrl->in_data.valuelen, + &outdata, &outdatalen); free (keyidstr); if (rc) { @@ -541,6 +654,99 @@ cmd_pkdecrypt (ASSUAN_CONTEXT ctx, char *line) } +/* SETATTR <name> <value> + + This command is used to store data on a a smartcard. The allowed + names and values are depend on the currently selected smartcard + application. NAME and VALUE must be percent and '+' escaped. + + However, the curent implementation assumes that Name is not escaped; + this works as long as noone uses arbitrary escaping. + + A PIN will be requested for most NAMEs. See the corresponding + setattr function of the actually used application (app-*.c) for + details. */ +static int +cmd_setattr (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int rc; + char *keyword; + int keywordlen; + size_t nbytes; + + if ((rc = open_card (ctrl))) + return rc; + + keyword = line; + for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) + ; + if (*line) + *line++ = 0; + while (spacep (line)) + line++; + nbytes = percent_plus_unescape (line); + + rc = app_setattr (ctrl->app_ctx, keyword, pin_cb, ctx, line, nbytes); + + return map_to_assuan_status (rc); +} + +/* GENKEY [--force] <no> + + Generate a key on-card identified by NO, which is application + specific. Return values are application specific. For OpenPGP + cards 2 status lines are returned: + + S KEY-FPR <hexstring> + S KEY-CREATED-AT <seconds_since_epoch> + S KEY-DATA [p|n] <hexdata> + + + --force is required to overwriet an already existing key. The + KEY-CREATED-AT is required for further processing because it is + part of the hashed key material for the fingerprint. + + The public part of the key can also later be retrieved using the + READKEY command. + + */ +static int +cmd_genkey (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int rc; + char *keyno; + int force = has_option (line, "--force"); + + /* Skip over options. */ + while ( *line == '-' && line[1] == '-' ) + { + while (!spacep (line)) + line++; + while (spacep (line)) + line++; + } + if (!*line) + return set_error (Parameter_Error, "no key number given"); + keyno = line; + while (!spacep (line)) + line++; + *line = 0; + + if ((rc = open_card (ctrl))) + return rc; + + if (!ctrl->app_ctx) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + + rc = app_genkey (ctrl->app_ctx, ctrl, keyno, force? 1:0, pin_cb, ctx); + + return map_to_assuan_status (rc); +} + + + /* Tell the assuan library about our commands */ @@ -560,6 +766,8 @@ register_commands (ASSUAN_CONTEXT ctx) { "PKDECRYPT", cmd_pkdecrypt }, { "INPUT", NULL }, { "OUTPUT", NULL }, + { "SETATTR", cmd_setattr }, + { "GENKEY", cmd_genkey }, { NULL } }; int i, rc; @@ -646,3 +854,51 @@ scd_command_handler (int listen_fd) assuan_deinit_server (ctx); } + + +/* Send a line with status information via assuan and escape all given + buffers. The variable elements are pairs of (char *, size_t), + terminated with a (NULL, 0). */ +void +send_status_info (CTRL 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; + + va_start (arg_ptr, keyword); + + p = buf; + n = 0; + while ( (value = va_arg (arg_ptr, const unsigned char *)) ) + { + valuelen = va_arg (arg_ptr, size_t); + if (!valuelen) + continue; /* empty buffer */ + if (n) + { + *p++ = ' '; + n++; + } + for ( ; valuelen && n < DIM (buf)-2; n++, valuelen--, value++) + { + if (*value < ' ' || *value == '+') + { + sprintf (p, "%%%02X", *value); + p += 3; + } + else if (*value == ' ') + *p++ = '+'; + else + *p++ = *value; + } + } + *p = 0; + assuan_write_status (ctx, keyword, buf); + + va_end (arg_ptr); +} + diff --git a/scd/sc-investigate.c b/scd/sc-investigate.c index e6b4c4afb..1100bdf43 100644 --- a/scd/sc-investigate.c +++ b/scd/sc-investigate.c @@ -25,11 +25,12 @@ #include <string.h> #define JNLIB_NEED_LOG_LOGV -#include <gcrypt.h> #include "scdaemon.h" +#include <gcrypt.h> #include "apdu.h" /* for open_reader */ #include "atr.h" +#include "app-common.h" #define _(a) (a) @@ -104,6 +105,9 @@ main (int argc, char **argv ) ARGPARSE_ARGS pargs; int slot, rc; int reader_port = 32768; /* First USB reader. */ + struct app_ctx_s appbuf; + + memset (&appbuf, 0, sizeof appbuf); set_strusage (my_strusage); gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); @@ -147,7 +151,8 @@ main (int argc, char **argv ) if (rc) log_error ("can't dump ATR: %s\n", gpg_strerror (rc)); - rc = app_select_openpgp (slot); + appbuf.slot = slot; + rc = app_select_openpgp (&appbuf, NULL, NULL); if (rc) log_error ("selecting openpgp failed: %s\n", gpg_strerror (rc)); else @@ -159,3 +164,8 @@ main (int argc, char **argv ) +void +send_status_info (CTRL ctrl, const char *keyword, ...) +{ + /* DUMMY */ +} diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 5ab50a5b4..8e0ef37c9 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -34,12 +34,12 @@ #include <unistd.h> #include <signal.h> +#define JNLIB_NEED_LOG_LOGV +#include "scdaemon.h" #include <ksba.h> #include <gcrypt.h> -#define JNLIB_NEED_LOG_LOGV #include <assuan.h> /* malloc hooks */ -#include "scdaemon.h" #include "i18n.h" #include "sysutils.h" diff --git a/scd/scdaemon.h b/scd/scdaemon.h index 59d40e519..394a13a1a 100644 --- a/scd/scdaemon.h +++ b/scd/scdaemon.h @@ -76,18 +76,22 @@ struct { struct server_local_s; struct card_ctx_s; +struct app_ctx_s; struct server_control_s { struct server_local_s *server_local; struct card_ctx_s *card_ctx; + struct app_ctx_s *app_ctx; struct { unsigned char *value; int valuelen; } in_data; /* helper to store the value we are going to sign */ }; + typedef struct server_control_s *CTRL; typedef struct card_ctx_s *CARD; +typedef struct app_ctx_s *APP; /*-- scdaemon.c --*/ void scd_exit (int rc); @@ -95,6 +99,7 @@ void scd_init_default_ctrl (CTRL ctrl); /*-- command.c --*/ void scd_command_handler (int); +void send_status_info (CTRL ctrl, const char *keyword, ...); /*-- card.c --*/ int card_open (CARD *rcard); |