diff options
Diffstat (limited to 'scd')
-rw-r--r-- | scd/ChangeLog | 65 | ||||
-rw-r--r-- | scd/Makefile.am | 24 | ||||
-rw-r--r-- | scd/apdu.c | 2 | ||||
-rw-r--r-- | scd/apdu.h | 5 | ||||
-rw-r--r-- | scd/app-common.h | 70 | ||||
-rw-r--r-- | scd/app-dinsig.c | 129 | ||||
-rw-r--r-- | scd/app-nks.c | 388 | ||||
-rw-r--r-- | scd/app-openpgp.c | 139 | ||||
-rw-r--r-- | scd/app.c | 131 | ||||
-rw-r--r-- | scd/card.c | 6 | ||||
-rw-r--r-- | scd/ccid-driver.c | 9 | ||||
-rw-r--r-- | scd/command.c | 127 | ||||
-rw-r--r-- | scd/iso7816.c | 192 | ||||
-rw-r--r-- | scd/iso7816.h | 8 | ||||
-rw-r--r-- | scd/sc-copykeys.c | 2 | ||||
-rw-r--r-- | scd/sc-investigate.c | 480 | ||||
-rw-r--r-- | scd/scdaemon.c | 4 | ||||
-rw-r--r-- | scd/scdaemon.h | 3 | ||||
-rw-r--r-- | scd/tlv.c | 219 | ||||
-rw-r--r-- | scd/tlv.h | 84 |
20 files changed, 1818 insertions, 269 deletions
diff --git a/scd/ChangeLog b/scd/ChangeLog index 0862d356b..3a6a6aea4 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,68 @@ +2004-01-27 Werner Koch <wk@gnupg.org> + + * command.c (cmd_readcert, cmd_readkey): Work on a copy of LINE. + + * app-common.h (app_ctx_s): Added readcert field. + * app.c (app_readcert): New. + * tlv.c (parse_ber_header): Added; taken from libksba. + +2004-01-26 Werner Koch <wk@gnupg.org> + + * card.c (map_sc_err): Use SCD as the error source. + + * command.c (open_card): ADD arg NAME to allow requesting a + specific application. Changed all callers. + (cmd_serialno): Allow optional argument to select the desired + application. + + * app-nks.c: New. + + * scdaemon.h (opt): Add READER_PORT. + * scdaemon.c (main): Set it here. + * app.c (app_set_default_reader_port): Removed. + (select_application): Add NAME arg and figure out a + default serial number from the GDO. Add SLOT arg and remove all + reader management. + (release_application): New. + (app_write_learn_status): Output an APPTYPE status line. + * command.c (open_card): Adapt for select_application change. + * app-openpgp.c (app_select_openpgp): Removed SN and SNLEN args + and set it directly. Changed all callers. + +2004-01-25 Werner Koch <wk@gnupg.org> + + * iso7816.c (iso7816_select_application): P1 kludge for OpenPGP + card. + * app-openpgp.c (find_tlv): Factor out this function to .. + * tlv.c, tlv.h: .. new. + + * scdaemon.h: Introduced app_t and ctrl_t as the new types for APP + and CTRL. + +2004-01-21 Werner Koch <wk@gnupg.org> + + * apdu.c (apdu_send_le): Treat SW_EOF_REACHED as a warning. + +2004-01-20 Werner Koch <wk@gnupg.org> + + * iso7816.c (iso7816_read_binary): New. + (iso7816_select_file): New. + (iso7816_list_directory): New. + + * sc-investigate.c: Add option -i. + (select_app, read_line, interactive_shell): New. + +2004-01-16 Werner Koch <wk@gnupg.org> + + * apdu.h: Add SW_FILE_NOT_FOUND. + * iso7816.c (map_sw): Map it to GPG_ERR_ENOENT. + * iso7816.c (iso7816_select_file): New. + + * app-dinsig.c: New file w/o any real code yet. + * Makefile.am (scdaemon_SOURCES,sc_investigate_SOURCES): Add file. + + * sc-investigate.c: Add option --disable-ccid. + 2003-12-19 Werner Koch <wk@gnupg.org> * apdu.c (apdu_send_le): Send a get_response with the indicated diff --git a/scd/Makefile.am b/scd/Makefile.am index a2ecd3a81..c8bf3d0de 100644 --- a/scd/Makefile.am +++ b/scd/Makefile.am @@ -26,6 +26,8 @@ bin_PROGRAMS = scdaemon sc-investigate sc-copykeys AM_CPPFLAGS = -I$(top_srcdir)/common $(OPENSC_CFLAGS) $(LIBGCRYPT_CFLAGS) \ $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) +card_apps = app-openpgp.c app-nks.c app-dinsig.c + scdaemon_SOURCES = \ scdaemon.c scdaemon.h \ command.c card.c \ @@ -34,8 +36,9 @@ scdaemon_SOURCES = \ apdu.c apdu.h \ ccid-driver.c ccid-driver.h \ iso7816.c iso7816.h \ - app.c app-common.h \ - app-openpgp.c + tlv.c tlv.h \ + app.c app-common.h $(card_apps) + scdaemon_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \ $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) \ @@ -46,9 +49,9 @@ sc_investigate_SOURCES = \ apdu.c apdu.h \ ccid-driver.c ccid-driver.h \ iso7816.c iso7816.h \ - app.c app-common.h \ - app-openpgp.c \ - atr.c atr.h + tlv.c tlv.h \ + atr.c atr.h \ + app.c app-common.h $(card_apps) sc_investigate_LDADD = \ ../jnlib/libjnlib.a ../common/libcommon.a \ @@ -61,17 +64,12 @@ sc_copykeys_SOURCES = \ apdu.c apdu.h \ ccid-driver.c ccid-driver.h \ iso7816.c iso7816.h \ - app.c app-common.h \ - app-openpgp.c \ - atr.c atr.h + tlv.c tlv.h \ + atr.c atr.h \ + app.c app-common.h $(card_apps) sc_copykeys_LDADD = \ ../jnlib/libjnlib.a ../common/libcommon.a \ ../common/libsimple-pwquery.a \ $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(LIBUSB_LIBS) \ -lgpg-error @INTLLIBS@ -ldl - - - - - diff --git a/scd/apdu.c b/scd/apdu.c index 02038b65c..e5295f566 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -1168,7 +1168,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1, log_printhex (" dump: ", result, resultlen); } - if (sw == SW_SUCCESS) + if (sw == SW_SUCCESS || sw == SW_EOF_REACHED) { if (retbuf) { diff --git a/scd/apdu.h b/scd/apdu.h index 21e2b9840..fd7634f13 100644 --- a/scd/apdu.h +++ b/scd/apdu.h @@ -27,13 +27,16 @@ enum { SW_MORE_DATA = 0x6100, /* Note: that the low byte must be masked of.*/ + SW_EOF_REACHED = 0x6282, SW_EEPROM_FAILURE = 0x6581, SW_WRONG_LENGTH = 0x6700, SW_CHV_WRONG = 0x6982, SW_CHV_BLOCKED = 0x6983, SW_USE_CONDITIONS = 0x6985, - SW_NOT_SUPPORTED = 0x6a81, SW_BAD_PARAMETER = 0x6a80, /* (in the data field) */ + SW_NOT_SUPPORTED = 0x6a81, + SW_FILE_NOT_FOUND = 0x6a82, + SW_RECORD_NOT_FOUND = 0x6a83, SW_REF_NOT_FOUND = 0x6a88, SW_BAD_P0_P1 = 0x6b00, SW_INS_NOT_SUP = 0x6d00, diff --git a/scd/app-common.h b/scd/app-common.h index de1e02cac..cda17700f 100644 --- a/scd/app-common.h +++ b/scd/app-common.h @@ -29,43 +29,46 @@ struct app_ctx_s { int slot; /* Used reader. */ unsigned char *serialno; /* Serialnumber in raw form, allocated. */ size_t serialnolen; /* Length in octets of serialnumber. */ + const char *apptype; unsigned int card_version; int did_chv1; int force_chv1; /* True if the card does not cache CHV1. */ int did_chv2; int did_chv3; struct { - int (*learn_status) (APP app, CTRL ctrl); - int (*getattr) (APP app, CTRL ctrl, const char *name); - int (*setattr) (APP app, const char *name, + int (*learn_status) (app_t app, ctrl_t ctrl); + int (*readcert) (app_t app, const char *certid, + unsigned char **cert, size_t *certlen); + int (*getattr) (app_t app, ctrl_t ctrl, const char *name); + int (*setattr) (app_t app, const char *name, int (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *value, size_t valuelen); - int (*sign) (APP app, + int (*sign) (app_t app, const char *keyidstr, int hashalgo, int (pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ); - int (*auth) (APP app, const char *keyidstr, + int (*auth) (app_t app, const char *keyidstr, int (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen); - int (*decipher) (APP app, const char *keyidstr, + int (*decipher) (app_t app, const char *keyidstr, int (pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen); - int (*genkey) (APP app, CTRL ctrl, + int (*genkey) (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags, int (*pincb)(void*, const char *, char **), void *pincb_arg); - int (*change_pin) (APP app, CTRL ctrl, + int (*change_pin) (app_t app, ctrl_t ctrl, const char *chvnostr, int reset_mode, int (*pincb)(void*, const char *, char **), void *pincb_arg); - int (*check_pin) (APP app, const char *keyidstr, + int (*check_pin) (app_t app, const char *keyidstr, int (pincb)(void*, const char *, char **), void *pincb_arg); } fnc; @@ -74,66 +77,77 @@ struct app_ctx_s { }; #if GNUPG_MAJOR_VERSION == 1 -int app_select_openpgp (APP app, unsigned char **sn, size_t *snlen); -int app_get_serial_and_stamp (APP app, char **serial, time_t *stamp); +int app_select_openpgp (app_t app); +int app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp); #else /*-- app.c --*/ -void app_set_default_reader_port (const char *portstr); -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_getattr (APP app, CTRL ctrl, const char *name); -int app_setattr (APP app, const char *name, +app_t select_application (ctrl_t ctrl, int slot, const char *name); +void release_application (app_t app); +int app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp); +int app_write_learn_status (app_t app, ctrl_t ctrl); +int app_readcert (app_t app, const char *certid, + unsigned char **cert, size_t *certlen); +int app_getattr (app_t app, ctrl_t ctrl, const char *name); +int app_setattr (app_t 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 app_sign (app_t app, const char *keyidstr, int hashalgo, int (pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ); -int app_auth (APP app, const char *keyidstr, +int app_auth (app_t app, const char *keyidstr, int (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen); -int app_decipher (APP app, const char *keyidstr, +int app_decipher (app_t app, const char *keyidstr, int (pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ); -int app_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags, +int app_genkey (app_t app, ctrl_t ctrl, + const char *keynostr, unsigned int flags, int (*pincb)(void*, const char *, char **), void *pincb_arg); -int app_get_challenge (APP app, size_t nbytes, unsigned char *buffer); -int app_change_pin (APP app, CTRL ctrl, const char *chvnostr, int reset_mode, +int app_get_challenge (app_t app, size_t nbytes, unsigned char *buffer); +int app_change_pin (app_t app, ctrl_t ctrl, + const char *chvnostr, int reset_mode, int (*pincb)(void*, const char *, char **), void *pincb_arg); -int app_check_pin (APP app, const char *keyidstr, +int app_check_pin (app_t app, const char *keyidstr, int (*pincb)(void*, const char *, char **), void *pincb_arg); /*-- app-openpgp.c --*/ -int app_select_openpgp (APP app, unsigned char **sn, size_t *snlen); +int app_select_openpgp (app_t app); -int app_openpgp_cardinfo (APP app, +int app_openpgp_cardinfo (app_t app, char **serialno, char **disp_name, char **pubkey_url, unsigned char **fpr1, unsigned char **fpr2, unsigned char **fpr3); -int app_openpgp_storekey (APP app, int keyno, +int app_openpgp_storekey (app_t app, int keyno, unsigned char *template, size_t template_len, time_t created_at, const unsigned char *m, size_t mlen, const unsigned char *e, size_t elen, int (*pincb)(void*, const char *, char **), void *pincb_arg); -int app_openpgp_readkey (APP app, int keyno, +int app_openpgp_readkey (app_t app, int keyno, unsigned char **m, size_t *mlen, unsigned char **e, size_t *elen); +/*-- app-nks.c --*/ +int app_select_nks (app_t app); + +/*-- app-dinsig.c --*/ +int app_select_dinsig (app_t app); + + #endif diff --git a/scd/app-dinsig.c b/scd/app-dinsig.c new file mode 100644 index 000000000..4b5b517eb --- /dev/null +++ b/scd/app-dinsig.c @@ -0,0 +1,129 @@ +/* app-dinsig.c - The DINSIG (DIN V 66291-1) card application. + * Copyright (C) 2004 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 + */ + + +/* The German signature law and its bylaw (SigG and SigV) is currently + used with an interface specification described in DIN V 66291-1. + The AID to be used is: 'D27600006601'. + + The file IDs for certificates utilize the generic format: + Cxyz + C being the hex digit 'C' (12). + x being the service indicator: + '0' := SigG conform digital signature. + '1' := entity authentication. + '2' := key encipherment. + '3' := data encipherment. + '4' := key agreement. + other values are reserved for future use. + y being the security environment number using '0' for cards + not supporting a SE number. + z being the certificate type: + '0' := C.CH (base certificate of card holder) or C.ICC. + '1' .. '7' := C.CH (business or professional certificate + of card holder. + '8' .. 'D' := C.CA (certificate of a CA issue by the Root-CA). + 'E' := C.RCA (self certified certificate of the Root-CA). + 'F' := reserved. + + The file IDs used by default are: + '1F00' EF.SSD (security service descriptor). [o,o] + '2F02' EF.GDO (global data objects) [m,m] + 'A000' EF.PROT (signature log). Cyclic file with 20 records of 53 byte. + Read and update after user authentication. [o,o] + 'B000' EF.PK.RCA.DS (public keys of Root-CA). Size is 512b or size + of keys. [m (unless a 'C00E' is present),m] + 'B001' EF.PK.CA.DS (public keys of CAs). Size is 512b or size + of keys. [o,o] + 'C00n' EF.C.CH.DS (digital signature certificate of card holder) + with n := 0 .. 7. Size is 2k or size of cert. Read and + update allowed after user authentication. [m,m] + 'C00m' EF.C.CA.DS (digital signature certificate of CA) + with m := 8 .. E. Size is 1k or size of cert. Read always + allowed, update after user authentication. [o,o] + 'C100' EF.C.ICC.AUT (AUT certificate of ICC) [o,m] + 'C108' EF.C.CA.AUT (AUT certificate of CA) [o,m] + 'D000' EF.DM (display message) [-,m] + + The letters in brackets indicate optional or mandatory files: The + first for card terminals under full control and the second for + "business" card terminals. + + FIXME: Needs a lot more explanation. + +*/ + + + + +#include <config.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <time.h> + +#include "scdaemon.h" + +#include "iso7816.h" +#include "app-common.h" + + + +static int +do_learn_status (APP app, CTRL ctrl) +{ + return 0; +} + + + + + +/* Select the DINSIG application on the card in SLOT. This function + must be used before any other DINSIG application functions. */ +int +app_select_dinsig (APP app) +{ + static char const aid[] = { 0xD2, 0x76, 0x00, 0x00, 0x66, 0x01 }; + int slot = app->slot; + int rc; + + rc = iso7816_select_application (slot, aid, sizeof aid); + if (!rc) + { + app->apptype = "DINSIG"; + + app->fnc.learn_status = do_learn_status; + app->fnc.getattr = NULL; + app->fnc.setattr = NULL; + app->fnc.genkey = NULL; + app->fnc.sign = NULL; + app->fnc.auth = NULL; + app->fnc.decipher = NULL; + app->fnc.change_pin = NULL; + app->fnc.check_pin = NULL; + } + + return rc; +} + + diff --git a/scd/app-nks.c b/scd/app-nks.c new file mode 100644 index 000000000..0a04f7511 --- /dev/null +++ b/scd/app-nks.c @@ -0,0 +1,388 @@ +/* app-nks.c - The Telesec NKS 2.0 card application. + * Copyright (C) 2004 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 <assert.h> +#include <time.h> + +#include "scdaemon.h" + +#include "iso7816.h" +#include "app-common.h" +#include "tlv.h" + +static struct { + int fid; /* File ID. */ + int certtype; /* Type of certificate or 0 if it is not a certificate. */ + int iskeypair; /* If true has the FID of the correspoding certificate. */ +} filelist[] = { + { 0x4531, 0, 0xC000 }, + { 0xC000, 101 }, + { 0x4331, 100 }, + { 0x4332, 100 }, + { 0xB000, 110 }, + { 0x45B1, 0, 0xC200 }, + { 0xC200, 101 }, + { 0x43B1, 100 }, + { 0x43B2, 100 }, + { 0, 0 } +}; + + + +/* Given the slot and the File Id FID, return the length of the + certificate contained in that file. Returns 0 if the file does not + exists or does not contain a certificate. */ +static size_t +get_length_of_cert (int slot, int fid) +{ + gpg_error_t err; + unsigned char *buffer; + const unsigned char *p; + size_t buflen, n; + int class, tag, constructed, ndef; + size_t objlen, hdrlen; + + err = iso7816_select_file (slot, fid, 0, NULL, NULL); + if (err) + { + log_info ("error selecting FID 0x%04X: %s\n", fid, gpg_strerror (err)); + return 0; + } + + err = iso7816_read_binary (slot, 0, 32, &buffer, &buflen); + if (err) + { + log_info ("error reading certificate from FID 0x%04X: %s\n", + fid, gpg_strerror (err)); + return 0; + } + + if (!buflen || *buffer == 0xff) + { + log_info ("no certificate contained in FID 0x%04X\n", fid); + xfree (buffer); + return 0; + } + + p = buffer; + n = buflen; + err = parse_ber_header (&p, &n, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (err) + { + log_info ("error parsing certificate in FID 0x%04X: %s\n", + fid, gpg_strerror (err)); + xfree (buffer); + return 0; + } + + /* All certificates should commence with a SEQUENCE expect fro the + special ROOT CA which are enclosed in a SET. */ + if ( !(class == CLASS_UNIVERSAL && constructed + && (tag == TAG_SEQUENCE || tag == TAG_SET))) + { + log_info ("contents of FID 0x%04X does not look like a certificate\n", + fid); + return 0; + } + + return objlen + hdrlen; +} + + + +/* Read the file with FID, assume it contains a public key and return + its keygrip in the caller provided 41 byte buffer R_GRIPSTR. */ +static gpg_error_t +keygripstr_from_pk_file (int slot, int fid, char *r_gripstr) +{ + gpg_error_t err; + unsigned char grip[20]; + unsigned char *buffer[2]; + size_t buflen[2]; + gcry_sexp_t sexp; + int i; + + err = iso7816_select_file (slot, fid, 0, NULL, NULL); + if (err) + return err; + err = iso7816_read_record (slot, 1, 1, &buffer[0], &buflen[0]); + if (err) + return err; + err = iso7816_read_record (slot, 2, 1, &buffer[1], &buflen[1]); + if (err) + { + xfree (buffer[0]); + return err; + } + + for (i=0; i < 2; i++) + { + /* Check that the value appears like an integer encoded as + Simple-TLV. We don't check the tag because the tests cards I + have use 1 for both, the modulus and the exponent - the + example in the documentation gives 2 for the exponent. */ + if (buflen[i] < 3) + err = gpg_error (GPG_ERR_TOO_SHORT); + else if (buffer[i][1] != buflen[i]-2 ) + err = gpg_error (GPG_ERR_INV_OBJ); + } + + if (!err) + err = gcry_sexp_build (&sexp, NULL, + "(public-key (rsa (n %b) (e %b)))", + (int)buflen[0]-2, buffer[0]+2, + (int)buflen[1]-2, buffer[1]+2); + + xfree (buffer[0]); + xfree (buffer[1]); + if (err) + return err; + + if (!gcry_pk_get_keygrip (sexp, grip)) + { + err = gpg_error (GPG_ERR_INTERNAL); /* i.e. RSA not supported by + libgcrypt. */ + } + else + { + for (i=0; i < 20; i++) + sprintf (r_gripstr+i*2, "%02X", grip[i]); + } + gcry_sexp_release (sexp); + return err; +} + + + +static int +do_learn_status (APP app, CTRL ctrl) +{ + gpg_error_t err; + char ct_buf[100], id_buf[100]; + int i; + + /* Output information about all useful objects. */ + for (i=0; filelist[i].fid; i++) + { + if (filelist[i].certtype) + { + size_t len = get_length_of_cert (app->slot, filelist[i].fid); + + if (len) + { + /* FIXME: We should store the length in the application's + context so that a following readcert does only need to + read that many bytes. */ + sprintf (ct_buf, "%d", filelist[i].certtype); + sprintf (id_buf, "NKS-DF01.%04X", filelist[i].fid); + send_status_info (ctrl, "CERTINFO", + ct_buf, strlen (ct_buf), + id_buf, strlen (id_buf), + NULL, (size_t)0); + } + } + else if (filelist[i].iskeypair) + { + char gripstr[40+1]; + + err = keygripstr_from_pk_file (app->slot, filelist[i].fid, gripstr); + if (err) + log_error ("can't get keygrip from FID 0x%04X: %s\n", + filelist[i].fid, gpg_strerror (err)); + else + { + sprintf (id_buf, "NKS-DF01.%04X", filelist[i].fid); + send_status_info (ctrl, "KEYPAIRINFO", + gripstr, 40, + id_buf, strlen (id_buf), + NULL, (size_t)0); + } + } + } + + return 0; +} + + + + +/* Read the certificate with id CERTID (as returned by learn_status in + the CERTINFO status lines) and return it in the freshly allocated + buffer put into CERT and the length of the certificate put into + CERTLEN. */ +static int +do_readcert (app_t app, const char *certid, + unsigned char **cert, size_t *certlen) +{ + int i, fid; + gpg_error_t err; + unsigned char *buffer; + const unsigned char *p; + size_t buflen, n; + int class, tag, constructed, ndef; + size_t totobjlen, objlen, hdrlen; + int rootca = 0; + + *cert = NULL; + *certlen = 0; + if (strncmp (certid, "NKS-DF01.", 9) ) + return gpg_error (GPG_ERR_INV_ID); + certid += 9; + if (!hexdigitp (certid) || !hexdigitp (certid+1) + || !hexdigitp (certid+2) || !hexdigitp (certid+3) + || certid[4]) + return gpg_error (GPG_ERR_INV_ID); + fid = xtoi_4 (certid); + for (i=0; filelist[i].fid; i++) + if ((filelist[i].certtype || filelist[i].iskeypair) + && filelist[i].fid == fid) + break; + if (!filelist[i].fid) + return gpg_error (GPG_ERR_NOT_FOUND); + + /* If the requested objects is a plain public key, redirect it to + the corresponding certificate. The whole system is a bit messy + becuase we sometime use the key directly or let the caller + retrieve the key from the certificate. The valid point behind + that is to support not-yet stored certificates. */ + if (filelist[i].iskeypair) + fid = filelist[i].iskeypair; + + + /* Read the entire file. fixme: This could be optimized by first + reading the header to figure out how long the certificate + actually is. */ + err = iso7816_select_file (app->slot, fid, 0, NULL, NULL); + if (err) + { + log_error ("error selecting FID 0x%04X: %s\n", fid, gpg_strerror (err)); + return err; + } + + err = iso7816_read_binary (app->slot, 0, 0, &buffer, &buflen); + if (err) + { + log_error ("error reading certificate from FID 0x%04X: %s\n", + fid, gpg_strerror (err)); + return err; + } + + if (!buflen || *buffer == 0xff) + { + log_info ("no certificate contained in FID 0x%04X\n", fid); + err = gpg_error (GPG_ERR_NOT_FOUND); + goto leave; + } + + /* Now figure something out about the object. */ + p = buffer; + n = buflen; + err = parse_ber_header (&p, &n, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (err) + goto leave; + if ( class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed ) + ; + else if ( class == CLASS_UNIVERSAL && tag == TAG_SET && constructed ) + rootca = 1; + else + return gpg_error (GPG_ERR_INV_OBJ); + totobjlen = objlen + hdrlen; + assert (totobjlen <= buflen); + + err = parse_ber_header (&p, &n, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (err) + goto leave; + + if (rootca) + ; + else if (class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !constructed) + { + const unsigned char *save_p; + + /* The certificate seems to be contained in a userCertificate + container. Skip this and assume the following sequence is + the certificate. */ + if (n < objlen) + { + err = gpg_error (GPG_ERR_INV_OBJ); + goto leave; + } + p += objlen; + n -= objlen; + save_p = p; + err = parse_ber_header (&p, &n, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (err) + goto leave; + if ( !(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed) ) + return gpg_error (GPG_ERR_INV_OBJ); + totobjlen = objlen + hdrlen; + assert (save_p + totobjlen <= buffer + buflen); + memmove (buffer, save_p, totobjlen); + } + + *cert = buffer; + buffer = NULL; + *certlen = totobjlen; + + leave: + xfree (buffer); + return err; +} + + + +/* Select the NKS 2.0 application on the card in SLOT. */ +int +app_select_nks (APP app) +{ + static char const aid[] = { 0xD2, 0x76, 0x00, 0x00, 0x03, 0x01, 0x02 }; + int slot = app->slot; + int rc; + + rc = iso7816_select_application (slot, aid, sizeof aid); + if (!rc) + { + app->apptype = "NKS"; + + app->fnc.learn_status = do_learn_status; + app->fnc.readcert = do_readcert; + app->fnc.getattr = NULL; + app->fnc.setattr = NULL; + app->fnc.genkey = NULL; + app->fnc.sign = NULL; + app->fnc.auth = NULL; + app->fnc.decipher = NULL; + app->fnc.change_pin = NULL; + app->fnc.check_pin = NULL; + } + + return rc; +} + + diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 8f9a303fe..75e3e299e 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -1,5 +1,5 @@ /* app-openpgp.c - The OpenPGP card application. - * Copyright (C) 2003 Free Software Foundation, Inc. + * Copyright (C) 2003, 2004 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -42,7 +42,7 @@ #include "iso7816.h" #include "app-common.h" - +#include "tlv.h" static struct { @@ -80,94 +80,6 @@ static unsigned long convert_sig_counter_value (const unsigned char *value, static unsigned long get_sig_counter (APP app); -/* Locate a TLV encoded data object in BUFFER of LENGTH and - return a pointer to value as well as its length in NBYTES. Return - NULL if it was not found. Note, that the function does not check - whether the value fits into the provided buffer. - - FIXME: Move this to an extra file, it is mostly duplicated from card.c. -*/ -static const unsigned char * -find_tlv (const unsigned char *buffer, size_t length, - int tag, size_t *nbytes, int nestlevel) -{ - const unsigned char *s = buffer; - size_t n = length; - size_t len; - int this_tag; - int composite; - - for (;;) - { - 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 */ - s++; - n--; - if (n < 2) - return NULL; /* buffer definitely too short for tag and length. */ - if ((*s & 0x1f) == 0x1f) - return NULL; /* We support only up to 2 bytes. */ - this_tag = (s[-1] << 8) | (s[0] & 0x7f); - } - else - this_tag = s[0]; - len = s[1]; - s += 2; n -= 2; - 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. */ - const unsigned char *tmp_s; - size_t tmp_len; - - tmp_s = find_tlv (s, len, tag, &tmp_len, nestlevel+1); - if (tmp_s) - { - *nbytes = tmp_len; - return tmp_s; - } - } - - if (this_tag == tag) - { - *nbytes = len; - return s; - } - if (len > n) - return NULL; /* buffer too short to skip to the next tag. */ - s += len; n -= len; - } -} - /* 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 @@ -197,7 +109,7 @@ get_one_do (int slot, int tag, unsigned char **result, size_t *nbytes) { const unsigned char *s; - s = find_tlv (buffer, buflen, tag, &valuelen, 0); + s = find_tlv (buffer, buflen, tag, &valuelen); if (!s) value = NULL; /* not found */ else if (valuelen > buflen - (s - buffer)) @@ -271,7 +183,7 @@ dump_all_do (int slot) if (j==i || data_objects[i].tag != data_objects[j].get_from) continue; value = find_tlv (buffer, buflen, - data_objects[j].tag, &valuelen, 0); + data_objects[j].tag, &valuelen); if (!value) ; /* not found */ else if (valuelen > buflen - (value - buffer)) @@ -443,7 +355,7 @@ do_getattr (APP app, CTRL ctrl, const char *name) { /* The serial number is very special. We could have used the AID DO to retrieve it, but we have it already in the app - context and the stanmp argument is required anyway which we + context and the stamp argument is required anyway which we can't by other means. The AID DO is available anyway but not hex formatted. */ char *serial; @@ -772,7 +684,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags, log_error ("error reading application data\n"); return gpg_error (GPG_ERR_GENERAL); } - fpr = find_tlv (buffer, buflen, 0x00C5, &n, 0); + fpr = find_tlv (buffer, buflen, 0x00C5, &n); if (!fpr || n != 60) { rc = gpg_error (GPG_ERR_GENERAL); @@ -820,7 +732,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags, } log_info ("key generation completed (%d seconds)\n", (int)(time (NULL) - start_at)); - keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen, 0); + keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen); if (!keydata) { rc = gpg_error (GPG_ERR_CARD); @@ -828,7 +740,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags, goto leave; } - m = find_tlv (keydata, keydatalen, 0x0081, &mlen, 0); + m = find_tlv (keydata, keydatalen, 0x0081, &mlen); if (!m) { rc = gpg_error (GPG_ERR_CARD); @@ -838,7 +750,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags, /* log_printhex ("RSA n:", m, mlen); */ send_key_data (ctrl, "n", m, mlen); - e = find_tlv (keydata, keydatalen, 0x0082, &elen, 0); + e = find_tlv (keydata, keydatalen, 0x0082, &elen); if (!e) { rc = gpg_error (GPG_ERR_CARD); @@ -913,7 +825,7 @@ compare_fingerprint (APP app, int keyno, unsigned char *sha1fpr) log_error ("error reading application data\n"); return gpg_error (GPG_ERR_GENERAL); } - fpr = find_tlv (buffer, buflen, 0x00C5, &n, 0); + fpr = find_tlv (buffer, buflen, 0x00C5, &n); if (!fpr || n != 60) { xfree (buffer); @@ -1268,7 +1180,7 @@ do_check_pin (APP app, const char *keyidstr, /* Select the OpenPGP application on the card in SLOT. This function must be used before any other OpenPGP application functions. */ int -app_select_openpgp (APP app, unsigned char **sn, size_t *snlen) +app_select_openpgp (APP app) { static char const aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 }; int slot = app->slot; @@ -1280,10 +1192,17 @@ app_select_openpgp (APP app, unsigned char **sn, size_t *snlen) rc = iso7816_select_application (slot, aid, sizeof aid); if (!rc) { + app->apptype = "OPENPGP"; + app->did_chv1 = 0; app->did_chv2 = 0; app->did_chv3 = 0; + /* The OpenPGP card returns the serial number as part of the + AID; because we prefer to use OpenPGP serial numbers, we + repalce a possibly already set one from a EF.GDO with this + one. Note, that for current OpenPGP cards, no EF.GDO exists + and thus it won't matter at all. */ rc = iso7816_get_data (slot, 0x004F, &buffer, &buflen); if (rc) goto leave; @@ -1293,15 +1212,12 @@ app_select_openpgp (APP app, unsigned char **sn, size_t *snlen) log_printhex ("", buffer, buflen); } - if (sn) - { - *sn = buffer; - *snlen = buflen; - app->card_version = buffer[6] << 8; - app->card_version |= buffer[7]; - } - else - xfree (buffer); + app->card_version = buffer[6] << 8; + app->card_version |= buffer[7]; + xfree (app->serialno); + app->serialno = buffer; + app->serialnolen = buflen; + buffer = NULL; relptr = get_one_do (app->slot, 0x00C4, &buffer, &buflen); if (!relptr) @@ -1316,6 +1232,7 @@ app_select_openpgp (APP app, unsigned char **sn, size_t *snlen) dump_all_do (slot); app->fnc.learn_status = do_learn_status; + app->fnc.readcert = NULL; app->fnc.getattr = do_getattr; app->fnc.setattr = do_setattr; app->fnc.genkey = do_genkey; @@ -1498,7 +1415,7 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen, goto leave; } - keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen, 0); + keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen); if (!keydata) { log_error ("response does not contain the public key data\n"); @@ -1506,7 +1423,7 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen, goto leave; } - a = find_tlv (keydata, keydatalen, 0x0081, &alen, 0); + a = find_tlv (keydata, keydatalen, 0x0081, &alen); if (!a) { log_error ("response does not contain the RSA modulus\n"); @@ -1517,7 +1434,7 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen, *m = xmalloc (alen); memcpy (*m, a, alen); - a = find_tlv (keydata, keydatalen, 0x0082, &alen, 0); + a = find_tlv (keydata, keydatalen, 0x0082, &alen); if (!e) { log_error ("response does not contain the RSA public exponent\n"); @@ -29,49 +29,78 @@ #include "app-common.h" #include "apdu.h" #include "iso7816.h" -#include "dynload.h" +#include "tlv.h" -static char *default_reader_port; -void -app_set_default_reader_port (const char *portstr) -{ - xfree (default_reader_port); - default_reader_port = portstr? xstrdup (portstr): NULL; -} - - -/* The select the best fitting application and return a context. - Returns NULL if no application was found or no card is present. */ +/* If called with NAME as NULL, select the best fitting application + and return a context; otherwise select the application with NAME + and return a context. SLOT identifies the reader device. Returns + NULL if no application was found or no card is present. */ APP -select_application (void) +select_application (ctrl_t ctrl, int slot, const char *name) { - int slot; int rc; APP app; - - slot = apdu_open_reader (default_reader_port); - if (slot == -1) - { - log_error ("card reader not available\n"); - return NULL; - } + unsigned char *result = NULL; + size_t resultlen; 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); + + /* Fixme: We should now first check whether a card is at all + present. */ + + /* Try to read the GDO file first to get a default serial number. */ + rc = iso7816_select_file (slot, 0x3F00, 1, NULL, NULL); + if (!rc) + rc = iso7816_select_file (slot, 0x2F02, 0, NULL, NULL); + if (!rc) + rc = iso7816_read_binary (slot, 0, 0, &result, &resultlen); + if (!rc) + { + size_t n; + const unsigned char *p; + + p = find_tlv (result, resultlen, 0x5A, &n); + if (p && n && n >= (resultlen - (p - result))) + { + /* The GDO file is pretty short, thus we simply reuse it for + storing the serial number. */ + memmove (result, p, n); + app->serialno = result; + app->serialnolen = n; + } + else + xfree (result); + result = NULL; + } + + + rc = gpg_error (GPG_ERR_NOT_FOUND); + + if (!name || !strcmp (name, "openpgp")) + rc = app_select_openpgp (app); + if (rc && (!name || !strcmp (name, "nks"))) + rc = app_select_nks (app); + if (rc && (!name || !strcmp (name, "dinsig"))) + rc = app_select_dinsig (app); + if (rc && name) + rc = gpg_error (GPG_ERR_NOT_SUPPORTED); + if (rc) { -/* apdu_close_reader (slot); */ - log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc)); + if (name) + log_info ("can't select application `%s': %s\n", + name, gpg_strerror (rc)); + else + log_info ("no supported card application found: %s\n", + gpg_strerror (rc)); xfree (app); return NULL; } @@ -81,23 +110,36 @@ select_application (void) } +void +release_application (app_t app) +{ + if (!app) + return; + + xfree (app->serialno); + xfree (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. */ + free SERIAL unless the function returns an error. If STAMP is not + of interest, NULL may be passed. */ int -app_get_serial_and_stamp (APP app, char **serial, time_t *stamp) +app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp) { unsigned char *buf, *p; int i; - if (!app || !serial || !stamp) + if (!app || !serial) return gpg_error (GPG_ERR_INV_VALUE); *serial = NULL; - *stamp = 0; /* not available */ + if (stamp) + *stamp = 0; /* not available */ buf = xtrymalloc (app->serialnolen * 2 + 1); if (!buf) @@ -121,10 +163,34 @@ app_write_learn_status (APP app, CTRL ctrl) return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.learn_status) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + + if (app->apptype) + send_status_info (ctrl, "APPTYPE", + app->apptype, strlen (app->apptype), NULL, 0); + return app->fnc.learn_status (app, ctrl); } +/* Read the certificate with id CERTID (as returned by learn_status in + the CERTINFO status lines) and return it in the freshly allocated + buffer put into CERT and the length of the certificate put into + CERTLEN. */ +int +app_readcert (app_t app, const char *certid, + unsigned char **cert, size_t *certlen) +{ + if (!app) + return gpg_error (GPG_ERR_INV_VALUE); + if (!app->initialized) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if (!app->fnc.readcert) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + + return app->fnc.readcert (app, certid, cert, certlen); +} + + /* Perform a GETATTR operation. */ int app_getattr (APP app, CTRL ctrl, const char *name) @@ -317,8 +383,3 @@ app_check_pin (APP app, const char *keyidstr, return rc; } - - - - - diff --git a/scd/card.c b/scd/card.c index 95149074d..53c89f3a4 100644 --- a/scd/card.c +++ b/scd/card.c @@ -53,7 +53,10 @@ map_sc_err (int rc) #endif default: e = GPG_ERR_CARD; break; } - return gpg_err_make (GPG_ERR_SOURCE_UNKNOWN, e); + /* It does not make much sense to further distingusih the error + source between OpenSC and SCD. Thus we use SCD as source + here. */ + return gpg_err_make (GPG_ERR_SOURCE_SCD, e); } /* Get the keygrip from CERT, return 0 on success */ @@ -462,6 +465,7 @@ card_enum_keypairs (CARD card, int idx, 100 := Regular X.509 cert 101 := Trusted X.509 cert 102 := Useful X.509 cert + 110 := Root CA cert (DINSIG) */ int card_enum_certs (CARD card, int idx, char **certid, int *certtype) diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c index 936672cc6..f910722eb 100644 --- a/scd/ccid-driver.c +++ b/scd/ccid-driver.c @@ -98,6 +98,11 @@ # include "scdaemon.h" # endif +/* Disable all debgging output for now. */ +#undef DBG_CARD_IO +#define DBG_CARD_IO 0 + + # define DEBUGOUT(t) do { if (DBG_CARD_IO) \ log_debug (DRVNAME t); } while (0) # define DEBUGOUT_1(t,a) do { if (DBG_CARD_IO) \ @@ -944,7 +949,9 @@ ccid_transceive (ccid_driver_t handle, { if (n > maxresplen) { - DEBUGOUT ("provided buffer too short for received data\n"); + DEBUGOUT_2 ("provided buffer too short for received data " + "(%u/%u)\n", + (unsigned int)n, (unsigned int)maxresplen); return -1; } diff --git a/scd/command.c b/scd/command.c index bc3132a0b..9e571f228 100644 --- a/scd/command.c +++ b/scd/command.c @@ -1,5 +1,5 @@ /* command.c - SCdaemon command handler - * Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. + * Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -31,6 +31,7 @@ #include "scdaemon.h" #include <ksba.h> #include "app-common.h" +#include "apdu.h" /* Required for apdu_*_reader (). */ /* maximum length aloowed as a PIN; used for INQUIRE NEEDPIN */ #define MAXLEN_PIN 100 @@ -90,17 +91,34 @@ option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value) function returns an Assuan error, so don't map the error a second time */ static AssuanError -open_card (CTRL ctrl) +open_card (CTRL ctrl, const char *apptype) { + int slot; + 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 (); + slot = apdu_open_reader (opt.reader_port); + if (slot != -1) + { + ctrl->app_ctx = select_application (ctrl, slot, apptype); + if (!ctrl->app_ctx) + apdu_close_reader (slot); + } if (!ctrl->app_ctx) { /* No application found - fall back to old mode. */ - int rc = card_open (&ctrl->card_ctx); + /* Note that we should rework the old code to use the + application paradigma too. */ + int rc; + + /* If an APPTYPE was requested and it is not pkcs#15, we return + an error here. */ + if (apptype && !(!strcmp (apptype, "P15") || !strcmp (apptype, "p15"))) + rc = gpg_error (GPG_ERR_NOT_SUPPORTED); + else + rc = card_open (&ctrl->card_ctx); if (rc) return map_to_assuan_status (rc); } @@ -143,11 +161,17 @@ percent_plus_unescape (unsigned char *string) -/* SERIALNO +/* SERIALNO [APPTYPE] Return the serial number of the card using a status reponse. This functon should be used to check for the presence of a card. + If APPTYPE is given, an application of that type is selected and an + error is returned if the application is not supported or available. + The default is to auto-select the application using a hardwired + preference system. Note, that a future extension to this function + may allow to specify a list and order of applications to try. + This function is special in that it can be used to reset the card. Most other functions will return an error when a card change has been detected and the use of this function is therefore required. @@ -165,7 +189,7 @@ cmd_serialno (ASSUAN_CONTEXT ctx, char *line) char *serial; time_t stamp; - if ((rc = open_card (ctrl))) + if ((rc = open_card (ctrl, *line? line:NULL))) return rc; if (ctrl->app_ctx) @@ -223,6 +247,7 @@ cmd_serialno (ASSUAN_CONTEXT ctx, char *line) 100 := Regular X.509 cert 101 := Trusted X.509 cert 102 := Useful X.509 cert + 110 := Root CA cert (DINSIG) For certain cards, more information will be returned: @@ -240,7 +265,7 @@ cmd_serialno (ASSUAN_CONTEXT ctx, char *line) 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 '+' + escaping takes place, spaces are encoded as '+' S PUBKEY-URL <url> @@ -254,7 +279,7 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line) int rc = 0; int idx; - if ((rc = open_card (ctrl))) + if ((rc = open_card (ctrl, NULL))) return rc; /* Unless the force option is used we try a shortcut by identifying @@ -305,10 +330,15 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line) free (serial_and_stamp); } - /* Return information about the certificates. */ - if (ctrl->app_ctx) - rc = -1; /* This information is not yet available for applications. */ - for (idx=0; !rc; idx++) + /* If we are using the modern application paradigma, let the + application print out its collection of useful status + information. */ + if (!rc && ctrl->app_ctx) + rc = app_write_learn_status (ctrl->app_ctx, ctrl); + + /* Return information about the certificates. FIXME: Move this into + an app-p15.c*/ + for (idx=0; !rc && !ctrl->app_ctx; idx++) { char *certid; int certtype; @@ -333,11 +363,9 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line) if (rc == -1) rc = 0; - - /* Return information about the keys. */ - if (ctrl->app_ctx) - rc = -1; /* This information is not yet available for applications. */ - for (idx=0; !rc; idx++) + /* Return information about the keys. FIXME: Move this into an + app-p15.c */ + for (idx=0; !rc && !ctrl->app_ctx; idx++) { unsigned char keygrip[20]; char *keyid; @@ -346,7 +374,7 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line) rc = card_enum_keypairs (ctrl->card_ctx, idx, keygrip, &keyid); if (gpg_err_code (rc) == GPG_ERR_MISSING_CERT && keyid) { - /* this does happen with an incomplete personalized + /* 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 @@ -383,10 +411,6 @@ 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); } @@ -403,17 +427,24 @@ cmd_readcert (ASSUAN_CONTEXT ctx, char *line) unsigned char *cert; size_t ncert; - if ((rc = open_card (ctrl))) + if ((rc = open_card (ctrl, NULL))) return rc; + line = xstrdup (line); /* Need a copy of the line. */ if (ctrl->app_ctx) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - - rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert); - if (rc) { - log_error ("card_read_cert failed: %s\n", gpg_strerror (rc)); + rc = app_readcert (ctrl->app_ctx, line, &cert, &ncert); + if (rc) + log_error ("app_readcert failed: %s\n", gpg_strerror (rc)); + } + else + { + rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert); + if (rc) + log_error ("card_read_cert failed: %s\n", gpg_strerror (rc)); } + xfree (line); + line = NULL; if (!rc) { rc = assuan_send_data (ctx, cert, ncert); @@ -440,18 +471,26 @@ cmd_readkey (ASSUAN_CONTEXT ctx, char *line) ksba_cert_t kc = NULL; ksba_sexp_t p; - if ((rc = open_card (ctrl))) + if ((rc = open_card (ctrl, NULL))) return rc; + line = xstrdup (line); /* Need a copy of the line. */ if (ctrl->app_ctx) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - - rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert); - if (rc) { - log_error ("card_read_cert failed: %s\n", gpg_strerror (rc)); - goto leave; + rc = app_readcert (ctrl->app_ctx, line, &cert, &ncert); + if (rc) + log_error ("app_readcert failed: %s\n", gpg_strerror (rc)); + } + else + { + rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert); + if (rc) + log_error ("card_read_cert failed: %s\n", gpg_strerror (rc)); } + xfree (line); + line = NULL; + if (rc) + goto leave; rc = ksba_cert_new (&kc); if (rc) @@ -569,7 +608,7 @@ cmd_pksign (ASSUAN_CONTEXT ctx, char *line) size_t outdatalen; char *keyidstr; - if ((rc = open_card (ctrl))) + if ((rc = open_card (ctrl, NULL))) return rc; /* We have to use a copy of the key ID because the function may use @@ -619,7 +658,7 @@ cmd_pkauth (ASSUAN_CONTEXT ctx, char *line) size_t outdatalen; char *keyidstr; - if ((rc = open_card (ctrl))) + if ((rc = open_card (ctrl, NULL))) return rc; if (!ctrl->app_ctx) @@ -665,7 +704,7 @@ cmd_pkdecrypt (ASSUAN_CONTEXT ctx, char *line) size_t outdatalen; char *keyidstr; - if ((rc = open_card (ctrl))) + if ((rc = open_card (ctrl, NULL))) return rc; keyidstr = xtrystrdup (line); @@ -718,7 +757,7 @@ cmd_getattr (ASSUAN_CONTEXT ctx, char *line) int rc; char *keyword; - if ((rc = open_card (ctrl))) + if ((rc = open_card (ctrl, NULL))) return rc; keyword = line; @@ -757,7 +796,7 @@ cmd_setattr (ASSUAN_CONTEXT ctx, char *orig_line) size_t nbytes; char *line, *linebuf; - if ((rc = open_card (ctrl))) + if ((rc = open_card (ctrl, NULL))) return rc; /* We need to use a copy of LINE, because PIN_CB uses the same @@ -823,7 +862,7 @@ cmd_genkey (ASSUAN_CONTEXT ctx, char *line) line++; *line = 0; - if ((rc = open_card (ctrl))) + if ((rc = open_card (ctrl, NULL))) return rc; if (!ctrl->app_ctx) @@ -854,7 +893,7 @@ cmd_random (ASSUAN_CONTEXT ctx, char *line) return set_error (Parameter_Error, "number of requested bytes missing"); nbytes = strtoul (line, NULL, 0); - if ((rc = open_card (ctrl))) + if ((rc = open_card (ctrl, NULL))) return rc; if (!ctrl->app_ctx) @@ -904,7 +943,7 @@ cmd_passwd (ASSUAN_CONTEXT ctx, char *line) line++; *line = 0; - if ((rc = open_card (ctrl))) + if ((rc = open_card (ctrl, NULL))) return rc; if (!ctrl->app_ctx) @@ -931,7 +970,7 @@ cmd_checkpin (ASSUAN_CONTEXT ctx, char *line) int rc; char *keyidstr; - if ((rc = open_card (ctrl))) + if ((rc = open_card (ctrl, NULL))) return rc; if (!ctrl->app_ctx) diff --git a/scd/iso7816.c b/scd/iso7816.c index f4aa18c6f..9a15ce953 100644 --- a/scd/iso7816.c +++ b/scd/iso7816.c @@ -51,6 +51,8 @@ #define CMD_INTERNAL_AUTHENTICATE 0x88 #define CMD_GENERATE_KEYPAIR 0x47 #define CMD_GET_CHALLENGE 0x84 +#define CMD_READ_BINARY 0xB0 +#define CMD_READ_RECORD 0xB2 static gpg_error_t map_sw (int sw) @@ -66,6 +68,8 @@ map_sw (int sw) case SW_USE_CONDITIONS: ec = GPG_ERR_USE_CONDITIONS; break; case SW_NOT_SUPPORTED: ec = GPG_ERR_NOT_SUPPORTED; break; case SW_BAD_PARAMETER: ec = GPG_ERR_INV_VALUE; break; + case SW_FILE_NOT_FOUND: ec = GPG_ERR_ENOENT; break; + case SW_RECORD_NOT_FOUND:ec= GPG_ERR_NOT_FOUND; break; case SW_REF_NOT_FOUND: ec = GPG_ERR_NO_OBJ; break; case SW_BAD_P0_P1: ec = GPG_ERR_INV_VALUE; break; case SW_INS_NOT_SUP: ec = GPG_ERR_CARD; break; @@ -91,18 +95,79 @@ map_sw (int sw) apdu_open_reader (), AID is a buffer of size AIDLEN holding the requested application ID. The function can't be used to enumerate AIDs and won't return the AID on success. The return value is 0 - for okay or GNUPG error code. Note that ISO error codes are + for okay or a GPG error code. Note that ISO error codes are internally mapped. */ gpg_error_t iso7816_select_application (int slot, const char *aid, size_t aidlen) { + static char const openpgp_aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 }; int sw; + int p1 = 0x0C; /* No FCI to be returned. */ + + if (aidlen == sizeof openpgp_aid + && !memcmp (aid, openpgp_aid, sizeof openpgp_aid)) + p1 = 0; /* The current openpgp cards don't allow 0x0c. */ - sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, 0, aidlen, aid); + sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, p1, aidlen, aid); return map_sw (sw); } +gpg_error_t +iso7816_select_file (int slot, int tag, int is_dir, + unsigned char **result, size_t *resultlen) +{ + int sw, p0, p1; + unsigned char tagbuf[2]; + + tagbuf[0] = (tag >> 8) & 0xff; + tagbuf[1] = tag & 0xff; + + if (result || resultlen) + { + *result = NULL; + *resultlen = 0; + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + } + else + { + p0 = (tag == 0x3F00)? 0: is_dir? 1:2; + p1 = 0x0c; /* No FC return. */ + sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, + p0, p1, 2, tagbuf ); + return map_sw (sw); + } + + return 0; +} + + +/* This is a private command currently only working for TCOS cards. */ +gpg_error_t +iso7816_list_directory (int slot, int list_dirs, + unsigned char **result, size_t *resultlen) +{ + int sw; + + if (!result || !resultlen) + return gpg_error (GPG_ERR_INV_VALUE); + *result = NULL; + *resultlen = 0; + + sw = apdu_send (slot, 0x80, 0xAA, list_dirs? 1:2, 0, -1, NULL, + result, resultlen); + if (sw != SW_SUCCESS) + { + /* Make sure that pending buffers are released. */ + xfree (*result); + *result = NULL; + *resultlen = 0; + } + return map_sw (sw); +} + + + /* Perform a VERIFY command on SLOT using the card holder verification vector CHVNO with a CHV of lenght CHVLEN. Returns 0 on success. */ gpg_error_t @@ -381,3 +446,126 @@ iso7816_get_challenge (int slot, int length, unsigned char *buffer) return 0; } + +/* Perform a READ BINARY command requesting a maximum of NMAX bytes + from OFFSET. With NMAX = 0 the entire file is read. The result is + stored in a newly allocated buffer at the address passed by RESULT. + Returns the length of this data at the address of RESULTLEN. */ +gpg_error_t +iso7816_read_binary (int slot, size_t offset, size_t nmax, + unsigned char **result, size_t *resultlen) +{ + int sw; + unsigned char *buffer; + size_t bufferlen; + + if (!result || !resultlen) + return gpg_error (GPG_ERR_INV_VALUE); + *result = NULL; + *resultlen = 0; + + /* We can only encode 15 bits in p0,p1 to indicate an offset. Thus + we check for this limit. */ + if (offset > 32767 || nmax > 254) + return gpg_error (GPG_ERR_INV_VALUE); + + do + { + buffer = NULL; + bufferlen = 0; + /* Fixme: Either the ccid driver of the TCOS cards have problems + with an Le of 0. */ + sw = apdu_send_le (slot, 0x00, CMD_READ_BINARY, + ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL, + nmax? nmax : 254, &buffer, &bufferlen); + + if (sw != SW_SUCCESS && sw != SW_EOF_REACHED) + { + /* Make sure that pending buffers are released. */ + xfree (buffer); + xfree (*result); + *result = NULL; + *resultlen = 0; + return map_sw (sw); + } + if (*result) /* Need to extend the buffer. */ + { + unsigned char *p = xtryrealloc (*result, *resultlen + bufferlen); + if (!p) + { + gpg_error_t err = gpg_error_from_errno (errno); + xfree (buffer); + xfree (*result); + *result = NULL; + *resultlen = 0; + return err; + } + *result = p; + memcpy (*result + *resultlen, buffer, bufferlen); + *resultlen += bufferlen; + xfree (buffer); + buffer = NULL; + } + else /* Transfer the buffer into our result. */ + { + *result = buffer; + *resultlen = bufferlen; + } + offset += bufferlen; + if (offset > 32767) + break; /* We simply truncate the result for too large + files. */ + } + while (!nmax && sw != SW_EOF_REACHED); + + return 0; +} + +/* Perform a READ RECORD command. RECNO gives the record number to + read with 0 indicating the current record. RECCOUNT must be 1 (not + all cards support reading of more than one record). The result is + stored in a newly allocated buffer at the address passed by RESULT. + Returns the length of this data at the address of RESULTLEN. */ +gpg_error_t +iso7816_read_record (int slot, int recno, int reccount, + unsigned char **result, size_t *resultlen) +{ + int sw; + unsigned char *buffer; + size_t bufferlen; + + if (!result || !resultlen) + return gpg_error (GPG_ERR_INV_VALUE); + *result = NULL; + *resultlen = 0; + + /* We can only encode 15 bits in p0,p1 to indicate an offset. Thus + we check for this limit. */ + if (recno < 0 || recno > 255 || reccount != 1) + return gpg_error (GPG_ERR_INV_VALUE); + + buffer = NULL; + bufferlen = 0; + /* Fixme: Either the ccid driver of the TCOS cards have problems + with an Le of 0. */ + sw = apdu_send_le (slot, 0x00, CMD_READ_RECORD, + recno, + 0x04, + -1, NULL, + 254, &buffer, &bufferlen); + + if (sw != SW_SUCCESS && sw != SW_EOF_REACHED) + { + /* Make sure that pending buffers are released. */ + xfree (buffer); + xfree (*result); + *result = NULL; + *resultlen = 0; + return map_sw (sw); + } + *result = buffer; + *resultlen = bufferlen; + + return 0; +} + diff --git a/scd/iso7816.h b/scd/iso7816.h index 26b8d6aba..98e688693 100644 --- a/scd/iso7816.h +++ b/scd/iso7816.h @@ -27,6 +27,10 @@ gpg_error_t iso7816_select_application (int slot, const char *aid, size_t aidlen); +gpg_error_t iso7816_select_file (int slot, int tag, int is_dir, + unsigned char **result, size_t *resultlen); +gpg_error_t iso7816_list_directory (int slot, int list_dirs, + unsigned char **result, size_t *resultlen); gpg_error_t iso7816_verify (int slot, int chvno, const char *chv, size_t chvlen); gpg_error_t iso7816_change_reference_data (int slot, int chvno, @@ -56,5 +60,9 @@ gpg_error_t iso7816_read_public_key (int slot, gpg_error_t iso7816_get_challenge (int slot, int length, unsigned char *buffer); +gpg_error_t iso7816_read_binary (int slot, size_t offset, size_t nmax, + unsigned char **result, size_t *resultlen); +gpg_error_t iso7816_read_record (int slot, int recno, int reccount, + unsigned char **result, size_t *resultlen); #endif /*ISO7816_H*/ diff --git a/scd/sc-copykeys.c b/scd/sc-copykeys.c index b56b88590..78cb2acc8 100644 --- a/scd/sc-copykeys.c +++ b/scd/sc-copykeys.c @@ -165,7 +165,7 @@ main (int argc, char **argv ) /* FIXME: Use select_application. */ appbuf.slot = slot; - rc = app_select_openpgp (&appbuf, &appbuf.serialno, &appbuf.serialnolen); + rc = app_select_openpgp (&appbuf); if (rc) { log_error ("selecting openpgp failed: %s\n", gpg_strerror (rc)); diff --git a/scd/sc-investigate.c b/scd/sc-investigate.c index ecd385690..acef86ead 100644 --- a/scd/sc-investigate.c +++ b/scd/sc-investigate.c @@ -24,6 +24,13 @@ #include <stdlib.h> #include <string.h> #include <errno.h> +#include <ctype.h> +#include <unistd.h> + +#ifdef HAVE_READLINE_READLINE_H +#include <readline/readline.h> +#include <readline/history.h> +#endif #define JNLIB_NEED_LOG_LOGV #include "scdaemon.h" @@ -32,17 +39,25 @@ #include "apdu.h" /* for open_reader */ #include "atr.h" #include "app-common.h" +#include "iso7816.h" #define _(a) (a) +#define CONTROL_D ('D' - 'A' + 1) + enum cmd_and_opt_values -{ oVerbose = 'v', +{ + oInteractive = 'i', + oVerbose = 'v', oReaderPort = 500, octapiDriver, oDebug, oDebugAll, + oDisableCCID, + + oGenRandom, aTest }; @@ -52,15 +67,27 @@ static ARGPARSE_OPTS opts[] = { { 301, NULL, 0, "@Options:\n " }, + { oInteractive, "interactive", 0, "start in interactive explorer mode"}, { oVerbose, "verbose", 0, "verbose" }, { oReaderPort, "reader-port", 2, "|N|connect to reader at port N"}, { octapiDriver, "ctapi-driver", 2, "NAME|use NAME as ctAPI driver"}, + { oDisableCCID, "disable-ccid", 0, +#ifdef HAVE_LIBUSB + "do not use the internal CCID driver" +#else + "@" +#endif + }, { oDebug, "debug" ,4|16, "set debugging flags"}, { oDebugAll, "debug-all" ,0, "enable full debugging"}, { oGenRandom, "gen-random", 4, "|N|generate N bytes of random"}, {0} }; + +static void interactive_shell (int slot); + + static const char * my_strusage (int level) { @@ -111,10 +138,8 @@ main (int argc, char **argv ) ARGPARSE_ARGS pargs; int slot, rc; const char *reader_port = NULL; - struct app_ctx_s appbuf; unsigned long gen_random = 0; - - memset (&appbuf, 0, sizeof appbuf); + int interactive = 0; set_strusage (my_strusage); gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); @@ -143,7 +168,9 @@ main (int argc, char **argv ) case oDebugAll: opt.debug = ~0; break; case oReaderPort: reader_port = pargs.r.ret_str; break; case octapiDriver: opt.ctapi_driver = pargs.r.ret_str; break; + case oDisableCCID: opt.disable_ccid = 1; break; case oGenRandom: gen_random = pargs.r.ret_ulong; break; + case oInteractive: interactive = 1; break; default : pargs.err = 2; break; } } @@ -151,7 +178,7 @@ main (int argc, char **argv ) exit(2); if (opt.verbose < 2) - opt.verbose = 2; /* hack to let select_openpgp print some info. */ + opt.verbose = 2; /* Hack to let select_openpgp print some info. */ if (argc) usage (1); @@ -167,40 +194,61 @@ main (int argc, char **argv ) log_error ("can't dump ATR: %s\n", gpg_strerror (rc)); } - appbuf.slot = slot; - rc = app_select_openpgp (&appbuf, NULL, NULL); - if (rc) - log_error ("selecting openpgp failed: %s\n", gpg_strerror (rc)); + if (interactive) + interactive_shell (slot); else { - appbuf.initialized = 1; - log_info ("openpgp application selected\n"); + struct app_ctx_s appbuf; - if (gen_random) + /* Fixme: We better use app.c directly. */ + memset (&appbuf, 0, sizeof appbuf); + appbuf.slot = slot; + rc = app_select_openpgp (&appbuf); + if (rc) { - size_t nbytes; - unsigned char *buffer; - - buffer = xmalloc (4096); - do + log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc)); + memset (&appbuf, 0, sizeof appbuf); + appbuf.slot = slot; + rc = app_select_dinsig (&appbuf); + if (rc) + log_info ("selecting dinsig failed: %s\n", gpg_strerror (rc)); + else { - nbytes = gen_random > 4096? 4096 : gen_random; - rc = app_get_challenge (&appbuf, nbytes, buffer); - if (rc) - log_error ("app_get_challenge failed: %s\n",gpg_strerror (rc)); - else + appbuf.initialized = 1; + log_info ("dinsig application selected\n"); + } + } + else + { + appbuf.initialized = 1; + log_info ("openpgp application selected\n"); + + if (gen_random) + { + size_t nbytes; + unsigned char *buffer; + + buffer = xmalloc (4096); + do { - if (fwrite (buffer, nbytes, 1, stdout) != 1) - log_error ("writing to stdout failed: %s\n", - strerror (errno)); - gen_random -= nbytes; + nbytes = gen_random > 4096? 4096 : gen_random; + rc = app_get_challenge (&appbuf, nbytes, buffer); + if (rc) + log_error ("app_get_challenge failed: %s\n",gpg_strerror (rc)); + else + { + if (fwrite (buffer, nbytes, 1, stdout) != 1) + log_error ("writing to stdout failed: %s\n", + strerror (errno)); + gen_random -= nbytes; + } } + while (gen_random && !log_get_errorcount (0)); + xfree (buffer); } - while (gen_random && !log_get_errorcount (0)); - xfree (buffer); } } - + return log_get_errorcount (0)? 2:0; } @@ -211,3 +259,377 @@ send_status_info (CTRL ctrl, const char *keyword, ...) { /* DUMMY */ } + + + +/* Dump BUFFER of length NBYTES in a nicely human readable format. */ +static void +dump_buffer (const unsigned char *buffer, size_t nbytes) +{ + int i; + + while (nbytes) + { + for (i=0; i < 16 && i < nbytes; i++) + printf ("%02X%s ", buffer[i], i==8? " ":""); + for (; i < 16; i++) + printf (" %s ", i==8? " ":""); + putchar (' '); + putchar (' '); + for (i=0; i < 16 && i < nbytes; i++) + if (isprint (buffer[i])) + putchar (buffer[i]); + else + putchar ('.'); + nbytes -= i; + buffer += i; + for (; i < 16; i++) + putchar (' '); + putchar ('\n'); + } +} + + +static void +dump_or_store_buffer (const char *arg, + const unsigned char *buffer, size_t nbytes) +{ + const char *s = strchr (arg, '>'); + int append; + FILE *fp; + + if (!s) + { + dump_buffer (buffer, nbytes); + return; + } + if ((append = (*++s == '>'))) + s++; + fp = fopen (s, append? "ab":"wb"); + if (!fp) + { + log_error ("failed to create `%s': %s\n", s, strerror (errno)); + return; + } + if (nbytes && fwrite (buffer, nbytes, 1, fp) != 1) + log_error ("failed to write to `%s': %s\n", s, strerror (errno)); + if (fclose (fp)) + log_error ("failed to close `%s': %s\n", s, strerror (errno)); +} + + +/* Convert STRING into a a newly allocated buffer and return the + length of the buffer in R_LENGTH. Detect xx:xx:xx... sequence and + unhexify that one. */ +static unsigned char * +pin_to_buffer (const char *string, size_t *r_length) +{ + unsigned char *buffer = xmalloc (strlen (string)+1); + const char *s; + size_t n; + + for (s=string, n=0; *s; s += 3) + { + if (hexdigitp (s) && hexdigitp (s+1) && (s[2]==':'||!s[2])) + { + buffer[n++] = xtoi_2 (s); + if (!s[2]) + break; + } + else + { + memcpy (buffer, string, strlen (string)); + *r_length = strlen (string); + return buffer; + } + } + *r_length = n; + return buffer; +} + + +static char * +read_line (int use_readline, char *prompt) +{ + static char buf[256]; + +#ifdef HAVE_READLINE + if (use_readline) + { + char *line = readline (prompt); + if (line) + trim_spaces (line); + if (line && strlen (line) > 2 ) + add_history (line); + return line; + } +#endif + /* Either we don't have readline or we are not running + interactively */ +#ifndef HAVE_READLINE + printf ("%s", prompt ); +#endif + fflush(stdout); + if (!fgets(buf, sizeof(buf), stdin)) + return NULL; + if (!strlen(buf)) + return NULL; + if (buf[strlen (buf)-1] == '\n') + buf[strlen (buf)-1] = 0; + trim_spaces (buf); + return buf; +} + +/* Run a shell for interactive exploration of the card. */ +static void +interactive_shell (int slot) +{ + enum cmdids + { + cmdNOP = 0, + cmdQUIT, cmdHELP, + cmdSELECT, + cmdCHDIR, + cmdLS, + cmdAPP, + cmdREAD, + cmdREADREC, + cmdDEBUG, + cmdVERIFY, + cmdCHANGEREF, + + cmdINVCMD + }; + static struct + { + const char *name; + enum cmdids id; + const char *desc; + } cmds[] = { + { "quit" , cmdQUIT , "quit this menu" }, + { "q" , cmdQUIT , NULL }, + { "help" , cmdHELP , "show this help" }, + { "?" , cmdHELP , NULL }, + { "debug" , cmdDEBUG, "set debugging flags" }, + { "select" , cmdSELECT, "select file (EF)" }, + { "s" , cmdSELECT, NULL }, + { "chdir" , cmdCHDIR, "change directory (select DF)"}, + { "cd" , cmdCHDIR, NULL }, + { "ls" , cmdLS, "list directory (some cards only)"}, + { "app" , cmdAPP, "select application"}, + { "read" , cmdREAD, "read binary" }, + { "rb" , cmdREAD, NULL }, + { "readrec", cmdREADREC, "read record(s)" }, + { "rr" , cmdREADREC, NULL }, + { "verify" , cmdVERIFY, "verify CHVNO PIN" }, + { "ver" , cmdVERIFY, NULL }, + { "changeref", cmdCHANGEREF, "change reference data" }, + { NULL, cmdINVCMD } + }; + enum cmdids cmd = cmdNOP; + int use_readline = isatty (fileno(stdin)); + char *line; + gpg_error_t err = 0; + unsigned char *result = NULL; + size_t resultlen; + +#ifdef HAVE_READLINE + if (use_readline) + using_history (); +#endif + + for (;;) + { + int arg_number; + const char *arg_string = ""; + const char *arg_next = ""; + char *p; + int i; + + if (err) + printf ("command failed: %s\n", gpg_strerror (err)); + err = 0; + xfree (result); + result = NULL; + + printf ("\n"); + do + { + line = read_line (use_readline, "cmd> "); + } + while ( line && *line == '#' ); + + arg_number = 0; + if (!line || *line == CONTROL_D) + cmd = cmdQUIT; + else if (!*line) + cmd = cmdNOP; + else { + if ((p=strchr (line,' '))) + { + char *endp; + + *p++ = 0; + trim_spaces (line); + trim_spaces (p); + arg_number = strtol (p, &endp, 0); + arg_string = p; + if (endp != p) + { + arg_next = endp; + while ( spacep (arg_next) ) + arg_next++; + } + } + + for (i=0; cmds[i].name; i++ ) + if (!ascii_strcasecmp (line, cmds[i].name )) + break; + + cmd = cmds[i].id; + } + + switch (cmd) + { + case cmdHELP: + for (i=0; cmds[i].name; i++ ) + if (cmds[i].desc) + printf("%-10s %s\n", cmds[i].name, _(cmds[i].desc) ); + break; + + case cmdQUIT: + goto leave; + + case cmdNOP: + break; + + case cmdDEBUG: + if (!*arg_string) + opt.debug = opt.debug? 0 : 2048; + else + opt.debug = arg_number; + break; + + case cmdSELECT: + err = iso7816_select_file (slot, arg_number, 0, NULL, NULL); + break; + + case cmdCHDIR: + err = iso7816_select_file (slot, arg_number, 1, NULL, NULL); + break; + + case cmdLS: + err = iso7816_list_directory (slot, 1, &result, &resultlen); + if (!err || gpg_err_code (err) == GPG_ERR_ENOENT) + err = iso7816_list_directory (slot, 0, &result, &resultlen); + /* FIXME: Do something with RESULT. */ + break; + + case cmdAPP: + { + app_t app; + + app = select_application (NULL, slot, *arg_string? arg_string:NULL); + if (app) + { + char *sn; + + app_get_serial_and_stamp (app, &sn, NULL); + log_info ("application `%s' ready; sn=%s\n", + app->apptype?app->apptype:"?", sn? sn:"[none]"); + release_application (app); + } + } + break; + + case cmdREAD: + err = iso7816_read_binary (slot, 0, 0, &result, &resultlen); + if (!err) + dump_or_store_buffer (arg_string, result, resultlen); + break; + + case cmdREADREC: + if (*arg_string == '*' && (!arg_string[1] || arg_string[1] == ' ')) + { + /* Fixme: Can't write to a file yet. */ + for (i=1, err=0; !err; i++) + { + xfree (result); result = NULL; + err = iso7816_read_record (slot, i, 1, &result, &resultlen); + if (!err) + dump_buffer (result, resultlen); + } + if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) + err = 0; + } + else + { + err = iso7816_read_record (slot, arg_number, 1, + &result, &resultlen); + if (!err) + dump_or_store_buffer (arg_string, result, resultlen); + } + break; + + case cmdVERIFY: + if (arg_number < 0 || arg_number > 255 || (arg_number & 127) > 31) + printf ("error: invalid CHVNO\n"); + else + { + unsigned char *pin; + size_t pinlen; + + pin = pin_to_buffer (arg_next, &pinlen); + err = iso7816_verify (slot, arg_number, pin, pinlen); + xfree (pin); + } + break; + + case cmdCHANGEREF: + { + const char *newpin = arg_next; + + while ( *newpin && !spacep (newpin) ) + newpin++; + while ( spacep (newpin) ) + newpin++; + + if (arg_number < 0 || arg_number > 255 || (arg_number & 127) > 31) + printf ("error: invalid CHVNO\n"); + else if (!*arg_next || !*newpin || newpin == arg_next) + printf ("usage: changeref CHVNO OLDPIN NEWPIN\n"); + else + { + char *oldpin = xstrdup (arg_next); + unsigned char *oldpin_buf, *newpin_buf; + size_t oldpin_len, newpin_len; + + for (p=oldpin; *p && !spacep (p); p++ ) + ; + *p = 0; + oldpin_buf = pin_to_buffer (oldpin, &oldpin_len); + newpin_buf = pin_to_buffer (newpin, &newpin_len); + + err = iso7816_change_reference_data (slot, arg_number, + oldpin_buf, oldpin_len, + newpin_buf, newpin_len); + + xfree (newpin_buf); + xfree (oldpin_buf); + xfree (oldpin); + } + } + break; + + case cmdINVCMD: + default: + printf ("\n"); + printf ("Invalid command (try \"help\")\n"); + break; + } /* End command switch. */ + } /* End of main menu loop. */ + + leave: + ; +} + diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 91ac93227..c6652c8dc 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -100,7 +100,7 @@ static ARGPARSE_OPTS opts[] = { { oReaderPort, "reader-port", 2, N_("|N|connect to reader at port N")}, { octapiDriver, "ctapi-driver", 2, N_("NAME|use NAME as ct-API driver")}, { opcscDriver, "pcsc-driver", 2, N_("NAME|use NAME as PC/SC driver")}, - { oDisableCCID, "disable-ccidc", 0, + { oDisableCCID, "disable-ccid", 0, #ifdef HAVE_LIBUSB N_("do not use the internal CCID driver") #else @@ -397,7 +397,7 @@ main (int argc, char **argv ) case oServer: pipe_server = 1; break; case oDaemon: is_daemon = 1; break; - case oReaderPort: app_set_default_reader_port (pargs.r.ret_str); break; + case oReaderPort: opt.reader_port = pargs.r.ret_str; break; case octapiDriver: opt.ctapi_driver = pargs.r.ret_str; break; case opcscDriver: opt.pcsc_driver = pargs.r.ret_str; break; case oDisableCCID: opt.disable_ccid = 1; break; diff --git a/scd/scdaemon.h b/scd/scdaemon.h index e13377af7..2bbf271da 100644 --- a/scd/scdaemon.h +++ b/scd/scdaemon.h @@ -55,6 +55,7 @@ struct { const char *homedir; /* configuration directory name */ const char *ctapi_driver; /* Library to access the ctAPI. */ const char *pcsc_driver; /* Library to access the PC/SC system. */ + const char *reader_port; /* NULL or reder port to use. */ int disable_opensc; /* Disable the use of the OpenSC framework. */ int disable_ccid; /* Disable the use of the internal CCID driver. */ int allow_admin; /* Allow the use of admin commands for certain @@ -96,8 +97,10 @@ struct server_control_s { }; typedef struct server_control_s *CTRL; +typedef struct server_control_s *ctrl_t; typedef struct card_ctx_s *CARD; typedef struct app_ctx_s *APP; +typedef struct app_ctx_s *app_t; /*-- scdaemon.c --*/ void scd_exit (int rc); diff --git a/scd/tlv.c b/scd/tlv.c new file mode 100644 index 000000000..dbcd24546 --- /dev/null +++ b/scd/tlv.c @@ -0,0 +1,219 @@ +/* tlv.c - Tag-Length-Value Utilities + * Copyright (C) 2003, 2004 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include <gpg-error.h> + +#include "tlv.h" + +static const unsigned char * +do_find_tlv (const unsigned char *buffer, size_t length, + int tag, size_t *nbytes, int nestlevel) +{ + const unsigned char *s = buffer; + size_t n = length; + size_t len; + int this_tag; + int composite; + + for (;;) + { + 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 */ + s++; + n--; + if (n < 2) + return NULL; /* buffer definitely too short for tag and length. */ + if ((*s & 0x1f) == 0x1f) + return NULL; /* We support only up to 2 bytes. */ + this_tag = (s[-1] << 8) | (s[0] & 0x7f); + } + else + this_tag = s[0]; + len = s[1]; + s += 2; n -= 2; + 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 a too deep + nesting. */ + const unsigned char *tmp_s; + size_t tmp_len; + + tmp_s = do_find_tlv (s, len, tag, &tmp_len, nestlevel+1); + if (tmp_s) + { + *nbytes = tmp_len; + return tmp_s; + } + } + + if (this_tag == tag) + { + *nbytes = len; + return s; + } + if (len > n) + return NULL; /* Buffer too short to skip to the next tag. */ + s += len; n -= len; + } +} + + +/* Locate a TLV encoded data object in BUFFER of LENGTH and + return a pointer to value as well as its length in NBYTES. Return + NULL if it was not found. Note, that the function does not check + whether the value fits into the provided buffer. */ +const unsigned char * +find_tlv (const unsigned char *buffer, size_t length, + int tag, size_t *nbytes) +{ + return do_find_tlv (buffer, length, tag, nbytes, 0); +} + + + + +/* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag + and the length part from the TLV triplet. Update BUFFER and SIZE + on success. */ +gpg_error_t +parse_ber_header (unsigned char const **buffer, size_t *size, + int *r_class, int *r_tag, + int *r_constructed, int *r_ndef, + size_t *r_length, size_t *r_nhdr) +{ + int c; + unsigned long tag; + const unsigned char *buf = *buffer; + size_t length = *size; + + *r_ndef = 0; + *r_length = 0; + *r_nhdr = 0; + + /* Get the tag. */ + if (!length) + return gpg_error (GPG_ERR_EOF); + c = *buf++; length--; ++*r_nhdr; + + *r_class = (c & 0xc0) >> 6; + *r_constructed = !!(c & 0x20); + tag = c & 0x1f; + + if (tag == 0x1f) + { + tag = 0; + do + { + /* Simple check against overflow. We limit our maximim tag + value more than needed but that should not be a problem + because I have nver encountered such large value. We + assume at least 32 bit integers. */ + if (tag > (1 << 24)) + return gpg_error (GPG_ERR_TOO_LARGE); + tag <<= 7; + if (!length) + return gpg_error (GPG_ERR_EOF); + c = *buf++; length--; ++*r_nhdr; + tag |= c & 0x7f; + + } + while (c & 0x80); + } + *r_tag = tag; + + /* Get the length. */ + if (!length) + return gpg_error (GPG_ERR_EOF); + c = *buf++; length--; ++*r_nhdr; + + if ( !(c & 0x80) ) + *r_length = c; + else if (c == 0x80) + *r_ndef = 1; + else if (c == 0xff) + return gpg_error (GPG_ERR_BAD_BER); + else + { + unsigned long len = 0; + int count = c & 0x7f; + + for (; count; count--) + { + /* Simple check against overflow. We limit our maximim + length more than needed but that should not be a problem + because I have never encountered such large value and + well they are managed in memory and thus we would run + into memory problems anyway. We assume at least 32 bit + integers. */ + if (len > (1 << 24)) + return gpg_error (GPG_ERR_TOO_LARGE); + len <<= 8; + if (!length) + return gpg_error (GPG_ERR_EOF); + c = *buf++; length--; ++*r_nhdr; + len |= c & 0xff; + } + *r_length = len; + } + + /* Without this kludge some example certs can't be parsed. */ + if (*r_class == CLASS_UNIVERSAL && !*r_tag) + *r_length = 0; + + *buffer = buf; + *size = length; + return 0; +} diff --git a/scd/tlv.h b/scd/tlv.h new file mode 100644 index 000000000..26a9905f7 --- /dev/null +++ b/scd/tlv.h @@ -0,0 +1,84 @@ +/* tlv.h - Tag-Length-Value Utilities + * Copyright (C) 2004 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 SCD_TLV_H +#define SCD_TLV_H 1 + + +enum tlv_tag_class { + CLASS_UNIVERSAL = 0, + CLASS_APPLICATION = 1, + CLASS_CONTEXT = 2, + CLASS_PRIVATE =3 +}; + +enum tlv_tag_type { + TAG_NONE = 0, + TAG_BOOLEAN = 1, + TAG_INTEGER = 2, + TAG_BIT_STRING = 3, + TAG_OCTET_STRING = 4, + TAG_NULL = 5, + TAG_OBJECT_ID = 6, + TAG_OBJECT_DESCRIPTOR = 7, + TAG_EXTERNAL = 8, + TAG_REAL = 9, + TAG_ENUMERATED = 10, + TAG_EMBEDDED_PDV = 11, + TAG_UTF8_STRING = 12, + TAG_REALTIVE_OID = 13, + TAG_SEQUENCE = 16, + TAG_SET = 17, + TAG_NUMERIC_STRING = 18, + TAG_PRINTABLE_STRING = 19, + TAG_TELETEX_STRING = 20, + TAG_VIDEOTEX_STRING = 21, + TAG_IA5_STRING = 22, + TAG_UTC_TIME = 23, + TAG_GENERALIZED_TIME = 24, + TAG_GRAPHIC_STRING = 25, + TAG_VISIBLE_STRING = 26, + TAG_GENERAL_STRING = 27, + TAG_UNIVERSAL_STRING = 28, + TAG_CHARACTER_STRING = 29, + TAG_BMP_STRING = 30 +}; + + + +/* Locate a TLV encoded data object in BUFFER of LENGTH and return a + pointer to value as well as its length in NBYTES. Return NULL if + it was not found. Note, that the function does not check whether + the value fits into the provided buffer.*/ +const unsigned char *find_tlv (const unsigned char *buffer, size_t length, + int tag, size_t *nbytes); + + +/* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag + and the length part from the TLV triplet. Update BUFFER and SIZE + on success. */ +gpg_error_t parse_ber_header (unsigned char const **buffer, size_t *size, + int *r_class, int *r_tag, + int *r_constructed, + int *r_ndef, size_t *r_length, size_t *r_nhdr); + + + +#endif /* SCD_TLV_H */ |