From e209ea3c39e7110d94e4420513f397d8656a75b7 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 16 Mar 2004 18:59:21 +0000 Subject: [PATCH] * app-dinsig.c: Implemented. Based on app-nks.c and card-dinsig.c * app-nks.c (get_length_of_cert): Removed. * app-help.c: New. (app_help_read_length_of_cert): New. Code taken from above. New optional arg R_CERTOFF. * card-dinsig.c: Removed. * card.c (card_get_serial_and_stamp): Do not bind to the old and never finsiged card-dinsig.c. * iso7816.c (iso7816_read_binary): Allow for an NMAX > 254. --- scd/ChangeLog | 14 ++ scd/Makefile.am | 14 +- scd/app-common.h | 7 + scd/app-dinsig.c | 316 ++++++++++++++++++++++++++++++++++++++++++++-- scd/app-help.c | 157 +++++++++++++++++++++++ scd/app-nks.c | 66 +--------- scd/app-openpgp.c | 2 +- scd/card.c | 2 - scd/iso7816.c | 20 ++- scd/scdaemon.c | 67 +++++++--- 10 files changed, 562 insertions(+), 103 deletions(-) create mode 100644 scd/app-help.c diff --git a/scd/ChangeLog b/scd/ChangeLog index a9296cbbb..679f867fe 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,17 @@ +2004-03-16 Werner Koch + + * app-dinsig.c: Implemented. Based on app-nks.c and card-dinsig.c + * app-nks.c (get_length_of_cert): Removed. + * app-help.c: New. + (app_help_read_length_of_cert): New. Code taken from above. New + optional arg R_CERTOFF. + + * card-dinsig.c: Removed. + * card.c (card_get_serial_and_stamp): Do not bind to the old and + never finsiged card-dinsig.c. + + * iso7816.c (iso7816_read_binary): Allow for an NMAX > 254. + 2004-03-11 Werner Koch * scdaemon.h (out_of_core): Removed. Replaced callers by standard diff --git a/scd/Makefile.am b/scd/Makefile.am index 89ebf4e05..da59a1997 100644 --- a/scd/Makefile.am +++ b/scd/Makefile.am @@ -34,12 +34,12 @@ scdaemon_SOURCES = \ scdaemon.c scdaemon.h \ command.c card.c \ card-common.h \ - card-p15.c card-dinsig.c \ + card-p15.c \ apdu.c apdu.h \ ccid-driver.c ccid-driver.h \ iso7816.c iso7816.h \ tlv.c tlv.h \ - app.c app-common.h $(card_apps) + app.c app-common.h app-help.c $(card_apps) scdaemon_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \ @@ -53,12 +53,12 @@ sc_investigate_SOURCES = \ iso7816.c iso7816.h \ tlv.c tlv.h \ atr.c atr.h \ - app.c app-common.h $(card_apps) + app.c app-common.h app-help.c $(card_apps) sc_investigate_LDADD = \ ../jnlib/libjnlib.a ../common/libcommon.a \ - $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(LIBUSB_LIBS) @INTLLIBS@ \ - -lgpg-error -ldl + $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBUSB_LIBS) \ + @INTLLIBS@ -lgpg-error -ldl sc_copykeys_SOURCES = \ @@ -68,10 +68,10 @@ sc_copykeys_SOURCES = \ iso7816.c iso7816.h \ tlv.c tlv.h \ atr.c atr.h \ - app.c app-common.h $(card_apps) + app.c app-common.h app-help.c $(card_apps) sc_copykeys_LDADD = \ ../jnlib/libjnlib.a ../common/libcommon.a \ ../common/libsimple-pwquery.a \ - $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(LIBUSB_LIBS) \ + $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBUSB_LIBS) \ -lgpg-error @INTLLIBS@ -ldl diff --git a/scd/app-common.h b/scd/app-common.h index cda17700f..c16a15719 100644 --- a/scd/app-common.h +++ b/scd/app-common.h @@ -21,6 +21,8 @@ #ifndef GNUPG_SCD_APP_COMMON_H #define GNUPG_SCD_APP_COMMON_H +#include + struct app_ctx_s { int initialized; /* The application has been initialied and the function pointers may be used. Note that for @@ -80,6 +82,11 @@ struct app_ctx_s { int app_select_openpgp (app_t app); int app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp); #else +/*-- app-help.c --*/ +gpg_error_t app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip); +size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff); + + /*-- app.c --*/ app_t select_application (ctrl_t ctrl, int slot, const char *name); void release_application (app_t app); diff --git a/scd/app-dinsig.c b/scd/app-dinsig.c index 4b5b517eb..38fbc79ee 100644 --- a/scd/app-dinsig.c +++ b/scd/app-dinsig.c @@ -1,5 +1,5 @@ /* app-dinsig.c - The DINSIG (DIN V 66291-1) card application. - * Copyright (C) 2004 Free Software Foundation, Inc. + * Copyright (C) 2002, 2004 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -65,9 +65,6 @@ 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. - */ @@ -85,18 +82,318 @@ #include "iso7816.h" #include "app-common.h" - +#include "tlv.h" static int -do_learn_status (APP app, CTRL ctrl) +do_learn_status (app_t app, ctrl_t ctrl) { + gpg_error_t err; + char ct_buf[100], id_buf[100]; + char hexkeygrip[41]; + size_t len, certoff; + unsigned char *der; + size_t derlen; + ksba_cert_t cert; + int fid; + + /* Return the certificate of the card holder. */ + fid = 0xC000; + len = app_help_read_length_of_cert (app->slot, fid, &certoff); + if (!len) + return 0; /* Card has not been personalized. */ + + sprintf (ct_buf, "%d", 101); + sprintf (id_buf, "DINSIG.%04X", fid); + send_status_info (ctrl, "CERTINFO", + ct_buf, strlen (ct_buf), + id_buf, strlen (id_buf), + NULL, (size_t)0); + + /* Now we need to read the certificate, so that we can get the + public key out of it. */ + err = iso7816_read_binary (app->slot, certoff, len-certoff, &der, &derlen); + if (err) + { + log_info ("error reading entire certificate from FID 0x%04X: %s\n", + fid, gpg_strerror (err)); + return 0; + } + + err = ksba_cert_new (&cert); + if (err) + { + xfree (der); + return err; + } + err = ksba_cert_init_from_mem (cert, der, derlen); + xfree (der); der = NULL; + if (err) + { + log_error ("failed to parse the certificate at FID 0x%04X: %s\n", + fid, gpg_strerror (err)); + ksba_cert_release (cert); + return err; + } + err = app_help_get_keygrip_string (cert, hexkeygrip); + if (err) + { + log_error ("failed to calculate the keygrip for FID 0x%04X\n", fid); + ksba_cert_release (cert); + return gpg_error (GPG_ERR_CARD); + } + ksba_cert_release (cert); + + sprintf (id_buf, "DINSIG.%04X", fid); + send_status_info (ctrl, "KEYPAIRINFO", + hexkeygrip, 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. + + FIXME: This needs some cleanups and caching with do_learn_status. +*/ +static int +do_readcert (app_t app, const char *certid, + unsigned char **cert, size_t *certlen) +{ + int 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, "DINSIG.", 7) ) + return gpg_error (GPG_ERR_INV_ID); + certid += 7; + 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); + if (fid != 0xC000 ) + return gpg_error (GPG_ERR_NOT_FOUND); + + /* 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; +} + + +/* Verify the PIN if required. */ +static int +verify_pin (app_t app, + int (pincb)(void*, const char *, char **), + void *pincb_arg) +{ + if (!app->did_chv1 || app->force_chv1 ) + { + char *pinvalue; + int rc; + + rc = pincb (pincb_arg, "PIN", &pinvalue); + if (rc) + { + log_info ("PIN callback returned error: %s\n", gpg_strerror (rc)); + return rc; + } + + /* We require the PIN to be at least 6 and at max 8 bytes. + According to the specs, this should all be ASCII but we don't + check this. */ + if (strlen (pinvalue) < 6) + { + log_error ("PIN is too short; minimum length is 6\n"); + xfree (pinvalue); + return gpg_error (GPG_ERR_BAD_PIN); + } + else if (strlen (pinvalue) > 8) + { + log_error ("PIN is too large; maximum length is 8\n"); + xfree (pinvalue); + return gpg_error (GPG_ERR_BAD_PIN); + } + + rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue)); + if (rc) + { + log_error ("verify PIN failed\n"); + xfree (pinvalue); + return rc; + } + app->did_chv1 = 1; + xfree (pinvalue); + } + + return 0; +} + + + +/* 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; + that callback should return the PIN in an allocated buffer and + store that in the 3rd argument. */ +static int +do_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 ) +{ + 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; + int fid; + unsigned char data[35]; /* Must be large enough for a SHA-1 digest + + the largest OID _prefix above. */ + + if (!keyidstr || !*keyidstr) + return gpg_error (GPG_ERR_INV_VALUE); + if (indatalen != 20 && indatalen != 16 && indatalen != 35) + return gpg_error (GPG_ERR_INV_VALUE); + + /* Check that the provided ID is vaid. This is not really needed + but we do it to to enforce correct usage by the caller. */ + if (strncmp (keyidstr, "DINSIG.", 7) ) + return gpg_error (GPG_ERR_INV_ID); + keyidstr += 7; + if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1) + || !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3) + || keyidstr[4]) + return gpg_error (GPG_ERR_INV_ID); + fid = xtoi_4 (keyidstr); + if (fid != 0xC000) + return gpg_error (GPG_ERR_NOT_FOUND); + + /* Prepare the DER object from INDATA. */ + if (indatalen == 35) + { + /* Alright, the caller was so kind to send us an already + prepared DER object. Check that it is what we want and that + it matches the hash algorithm. */ + if (hashalgo == GCRY_MD_SHA1 && !memcmp (indata, sha1_prefix, 15)) + ; + else if (hashalgo == GCRY_MD_RMD160 && !memcmp (indata, rmd160_prefix,15)) + ; + else + return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); + memcpy (data, indata, indatalen); + } + else + { + 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); + } + + rc = verify_pin (app, pincb, pincb_arg); + if (!rc) + rc = iso7816_compute_ds (app->slot, data, 35, outdata, outdatalen); + return rc; +} + + /* Select the DINSIG application on the card in SLOT. This function must be used before any other DINSIG application functions. */ @@ -113,17 +410,18 @@ app_select_dinsig (APP app) app->apptype = "DINSIG"; 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.sign = do_sign; app->fnc.auth = NULL; app->fnc.decipher = NULL; app->fnc.change_pin = NULL; app->fnc.check_pin = NULL; + + app->force_chv1 = 1; } return rc; } - - diff --git a/scd/app-help.c b/scd/app-help.c new file mode 100644 index 000000000..c6695635f --- /dev/null +++ b/scd/app-help.c @@ -0,0 +1,157 @@ +/* app-help.c - Application helper functions + * 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 +#include +#include +#include +#include + +#include "scdaemon.h" +#include "app-common.h" +#include "iso7816.h" +#include "tlv.h" + +/* Return the KEYGRIP for the certificate CERT as an hex encoded + string in the user provided buffer HEXKEYGRIP which must be of at + least 41 bytes. */ +gpg_error_t +app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip) +{ + gpg_error_t err; + gcry_sexp_t s_pkey; + ksba_sexp_t p; + size_t n; + unsigned char array[20]; + int i; + + p = ksba_cert_get_public_key (cert); + if (!p) + return gpg_error (GPG_ERR_BUG); + n = gcry_sexp_canon_len (p, 0, NULL, NULL); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + err = gcry_sexp_sscan (&s_pkey, NULL, p, n); + xfree (p); + if (err) + return err; /* Can't parse that S-expression. */ + if (!gcry_pk_get_keygrip (s_pkey, array)) + { + gcry_sexp_release (s_pkey); + return gpg_error (GPG_ERR_GENERAL); /* Failed to calculate the keygrip.*/ + } + gcry_sexp_release (s_pkey); + + for (i=0; i < 20; i++) + sprintf (hexkeygrip+i*2, "%02X", array[i]); + + return 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. If R_CERTOFF is not + NULL, the length the header will be stored at this address; thus to + parse the X.509 certificate a read should start at that offset. + + On success the file is still selected. +*/ +size_t +app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff) +{ + gpg_error_t err; + unsigned char *buffer; + const unsigned char *p; + size_t buflen, n; + int class, tag, constructed, ndef; + size_t resultlen, 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 except for 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; + } + + resultlen = objlen + hdrlen; + if (r_certoff) + { + /* The callers want the offset to the actual certificate. */ + *r_certoff = hdrlen; + + err = parse_ber_header (&p, &n, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (err) + return 0; + + if (class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !constructed) + { + /* The certificate seems to be contained in a + userCertificate container. Assume the following sequence + is the certificate. */ + *r_certoff += hdrlen + objlen; + if (*r_certoff > resultlen) + return 0; /* That should never happen. */ + } + } + + return resultlen; +} + + diff --git a/scd/app-nks.c b/scd/app-nks.c index a4b6e3a15..e69b59879 100644 --- a/scd/app-nks.c +++ b/scd/app-nks.c @@ -53,68 +53,6 @@ static struct { -/* 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 @@ -191,8 +129,10 @@ do_learn_status (APP app, CTRL ctrl) { if (filelist[i].certtype) { - size_t len = get_length_of_cert (app->slot, filelist[i].fid); + size_t len; + len = app_help_read_length_of_cert (app->slot, + filelist[i].fid, NULL); if (len) { /* FIXME: We should store the length in the application's diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 7782b8e1c..af5f5a27f 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -1201,7 +1201,7 @@ app_select_openpgp (APP app) /* 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 + replace 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); diff --git a/scd/card.c b/scd/card.c index 8366dcb1c..9ec2a52c5 100644 --- a/scd/card.c +++ b/scd/card.c @@ -325,8 +325,6 @@ card_get_serial_and_stamp (CARD card, char **serial, time_t *stamp) } if (card->p15card) card_p15_bind (card); - else - card_dinsig_bind (card); card->fnc.initialized = 1; } diff --git a/scd/iso7816.c b/scd/iso7816.c index d7d3c126b..24361d148 100644 --- a/scd/iso7816.c +++ b/scd/iso7816.c @@ -488,6 +488,8 @@ iso7816_read_binary (int slot, size_t offset, size_t nmax, int sw; unsigned char *buffer; size_t bufferlen; + int read_all = !nmax; + size_t n; if (!result || !resultlen) return gpg_error (GPG_ERR_INV_VALUE); @@ -496,18 +498,22 @@ iso7816_read_binary (int slot, size_t offset, size_t nmax, /* We can only encode 15 bits in p0,p1 to indicate an offset. Thus we check for this limit. */ - if (offset > 32767 || nmax > 254) + if (offset > 32767) return gpg_error (GPG_ERR_INV_VALUE); do { buffer = NULL; bufferlen = 0; - /* Fixme: Either the ccid driver of the TCOS cards have problems + /* Fixme: Either the ccid driver or the TCOS cards have problems with an Le of 0. */ + if (read_all || nmax > 254) + n = 254; + else + n = nmax; sw = apdu_send_le (slot, 0x00, CMD_READ_BINARY, - ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL, - nmax? nmax : 254, &buffer, &bufferlen); + ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL, + n, &buffer, &bufferlen); if (sw != SW_SUCCESS && sw != SW_EOF_REACHED) { @@ -545,8 +551,12 @@ iso7816_read_binary (int slot, size_t offset, size_t nmax, if (offset > 32767) break; /* We simply truncate the result for too large files. */ + if (nmax > bufferlen) + nmax -= bufferlen; + else + nmax = 0; } - while (!nmax && sw != SW_EOF_REACHED); + while ((read_all && sw != SW_EOF_REACHED) || (!read_all && nmax)); return 0; } diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 5665237ca..93746ec35 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -103,8 +103,8 @@ static ARGPARSE_OPTS opts[] = { { oNoDetach, "no-detach" ,0, N_("do not detach from the console")}, { oLogFile, "log-file" ,2, N_("use a log file for the server")}, { 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")}, + { 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-ccid", 0, #ifdef HAVE_LIBUSB N_("do not use the internal CCID driver") @@ -126,6 +126,9 @@ static ARGPARSE_OPTS opts[] = { }; +#define DEFAULT_PCSC_DRIVER "libpcsclite.so" + + static volatile int caught_fatal_sig = 0; /* It is possible that we are currently running under setuid permissions */ @@ -302,6 +305,7 @@ main (int argc, char **argv ) char *logfile = NULL; int debug_wait = 0; int gpgconf_list = 0; + const char *config_filename = NULL; set_strusage (my_strusage); gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); @@ -334,7 +338,7 @@ main (int argc, char **argv ) may_coredump = disable_core_dumps (); /* Set default options. */ - opt.pcsc_driver = "libpcsclite.so"; + opt.pcsc_driver = DEFAULT_PCSC_DRIVER; shell = getenv ("SHELL"); @@ -466,7 +470,8 @@ main (int argc, char **argv ) { fclose( configfp ); configfp = NULL; - xfree(configname); + /* Keep a copy of the config name for use by --gpgconf-list. */ + config_filename = configname; configname = NULL; goto next_pass; } @@ -507,19 +512,49 @@ main (int argc, char **argv ) if (gpgconf_list) { /* List options and default values in the GPG Conf format. */ - char *filename; - filename = make_filename (opt.homedir, "scdaemon.conf", NULL); - printf ("gpgconf-scdaemon.conf:\"%s\n", filename); - xfree (filename); - - printf ("verbose:\n" - "quiet:\n" - "debug-level:none\n" - "log-file:\n" - "force:\n" - "faked-system-time:\n" - "no-greeting:\n"); + /* The following list is taken from gnupg/tools/gpgconf-comp.c. */ + /* Option flags. YOU MUST NOT CHANGE THE NUMBERS OF THE EXISTING + FLAGS, AS THEY ARE PART OF THE EXTERNAL INTERFACE. */ +#define GC_OPT_FLAG_NONE 0UL + /* The RUNTIME flag for an option indicates that the option can be + changed at runtime. */ +#define GC_OPT_FLAG_RUNTIME (1UL << 3) + /* The DEFAULT flag for an option indicates that the option has a + default value. */ +#define GC_OPT_FLAG_DEFAULT (1UL << 4) + /* The DEF_DESC flag for an option indicates that the option has a + default, which is described by the value of the default field. */ +#define GC_OPT_FLAG_DEF_DESC (1UL << 5) + /* The NO_ARG_DESC flag for an option indicates that the argument has + a default, which is described by the value of the ARGDEF field. */ +#define GC_OPT_FLAG_NO_ARG_DESC (1UL << 6) + + printf ("gpgconf-scdaemon.conf:%lu:\"%s\"\n", + GC_OPT_FLAG_DEFAULT, + config_filename?config_filename:"/dev/null"); + + printf ("verbose:%lu:\n" + "quiet:%lu:\n" + "debug-level:%lu:\"none\":\n" + "log-file:%lu:\n", + GC_OPT_FLAG_NONE, + GC_OPT_FLAG_NONE, + GC_OPT_FLAG_DEFAULT, + GC_OPT_FLAG_NONE ); + + printf ("reader-port:%lu:\n", GC_OPT_FLAG_NONE ); + printf ("ctapi-driver:%lu:\n", GC_OPT_FLAG_NONE ); + printf ("pcsc-driver:%lu:\"%s\":\n", + GC_OPT_FLAG_DEFAULT, DEFAULT_PCSC_DRIVER ); +#ifdef HAVE_LIBUSB + printf ("disable-ccid:%lu:\n", GC_OPT_FLAG_NONE ); +#endif +#ifdef HAVE_LIBUSB + printf ("disable-opensc:%lu:\n", GC_OPT_FLAG_NONE ); +#endif + printf ("allow-admin:%lu:\n", GC_OPT_FLAG_NONE ); + scd_exit (0); }