From 1d81b994b87df78e0ba259ee227c51ce3d5534ac Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 9 Aug 2002 18:12:01 +0000 Subject: [PATCH] * card.c (card_get_serial_and_stamp): Use the tokeinfo serial number as a fallback. Add a special prefix for serial numbers. --- scd/ChangeLog | 22 ++++++ scd/card-dinsig.c | 2 +- scd/card-p15.c | 3 +- scd/card.c | 179 +++++++++++++++++++++++++++++++++------------- scd/scdaemon.c | 4 +- 5 files changed, 158 insertions(+), 52 deletions(-) diff --git a/scd/ChangeLog b/scd/ChangeLog index b06603315..e0cfda805 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,25 @@ +2002-08-09 Werner Koch + + * card.c (card_get_serial_and_stamp): Use the tokeinfo serial + number as a fallback. Add a special prefix for serial numbers. + +2002-07-30 Werner Koch + + Changes to cope with OpenSC 0.7.0: + + * card.c: Removed the check for the packed opensc version. + Changed include file names of opensc. + (map_sc_err): Adjusted error codes for new opensc version. + * card-p15.c: Changed include filename of opensc. + * card-dinsig.c: Ditto. + + * card-p15.c (p15_decipher): Add flags argument to OpenSC call. + +2002-07-24 Werner Koch + + * card.c (find_simple_tlv, find_iccsn): New. + (card_get_serial_and_stamp): Improved serial number parser. + 2002-06-27 Werner Koch * scdaemon.c (main): Use GNUPG_DEFAULT_HOMEDIR constant. diff --git a/scd/card-dinsig.c b/scd/card-dinsig.c index 4080350c6..161d5dbfc 100644 --- a/scd/card-dinsig.c +++ b/scd/card-dinsig.c @@ -77,7 +77,7 @@ #include #include -#include +#include #include #include "scdaemon.h" diff --git a/scd/card-p15.c b/scd/card-p15.c index a70bc665e..6248ddfe2 100644 --- a/scd/card-p15.c +++ b/scd/card-p15.c @@ -25,7 +25,7 @@ #include #include -#include +#include #include #include "scdaemon.h" @@ -345,6 +345,7 @@ p15_decipher (CARD card, const char *keyidstr, return GNUPG_Out_Of_Core; rc = sc_pkcs15_decipher (card->p15card, keyobj, + 0, indata, indatalen, outbuf, outbuflen); if (rc < 0) diff --git a/scd/card.c b/scd/card.c index d17ae1947..9c22867a1 100644 --- a/scd/card.c +++ b/scd/card.c @@ -25,13 +25,9 @@ #include #include -#include +#include #include -#if SC_MAX_SEC_ATTR_SIZE < 36 -# error This is not the patched OpenSC version -#endif - #include "scdaemon.h" #include "card-common.h" @@ -42,38 +38,12 @@ map_sc_err (int rc) switch (rc) { case 0: rc = 0; break; - case SC_ERROR_CMD_TOO_SHORT: rc = GNUPG_Card_Error; break; - case SC_ERROR_CMD_TOO_LONG: rc = GNUPG_Card_Error; break; case SC_ERROR_NOT_SUPPORTED: rc = GNUPG_Not_Supported; break; - case SC_ERROR_TRANSMIT_FAILED: rc = GNUPG_Card_Error; break; - case SC_ERROR_FILE_NOT_FOUND: rc = GNUPG_Card_Error; break; - case SC_ERROR_INVALID_ARGUMENTS: rc = GNUPG_Card_Error; break; case SC_ERROR_PKCS15_APP_NOT_FOUND: rc = GNUPG_No_PKCS15_App; break; - case SC_ERROR_REQUIRED_PARAMETER_NOT_FOUND: rc = GNUPG_Card_Error; break; case SC_ERROR_OUT_OF_MEMORY: rc = GNUPG_Out_Of_Core; break; - case SC_ERROR_NO_READERS_FOUND: rc = GNUPG_Card_Error; break; - case SC_ERROR_OBJECT_NOT_VALID: rc = GNUPG_Card_Error; break; - case SC_ERROR_ILLEGAL_RESPONSE: rc = GNUPG_Card_Error; break; - case SC_ERROR_PIN_CODE_INCORRECT: rc = GNUPG_Card_Error; break; - case SC_ERROR_SECURITY_STATUS_NOT_SATISFIED: rc = GNUPG_Card_Error; break; - case SC_ERROR_CONNECTING_TO_RES_MGR: rc = GNUPG_Card_Error; break; - case SC_ERROR_INVALID_ASN1_OBJECT: rc = GNUPG_Card_Error; break; - case SC_ERROR_BUFFER_TOO_SMALL: rc = GNUPG_Card_Error; break; case SC_ERROR_CARD_NOT_PRESENT: rc = GNUPG_Card_Not_Present; break; - case SC_ERROR_RESOURCE_MANAGER: rc = GNUPG_Card_Error; break; case SC_ERROR_CARD_REMOVED: rc = GNUPG_Card_Removed; break; - case SC_ERROR_INVALID_PIN_LENGTH: rc = GNUPG_Card_Error; break; - case SC_ERROR_UNKNOWN_SMARTCARD: rc = GNUPG_Card_Error; break; - case SC_ERROR_UNKNOWN_REPLY: rc = GNUPG_Card_Error; break; - case SC_ERROR_OBJECT_NOT_FOUND: rc = GNUPG_Card_Error; break; - case SC_ERROR_CARD_RESET: rc = GNUPG_Card_Reset; break; - case SC_ERROR_ASN1_OBJECT_NOT_FOUND: rc = GNUPG_Card_Error; break; - case SC_ERROR_ASN1_END_OF_CONTENTS: rc = GNUPG_Card_Error; break; - case SC_ERROR_TOO_MANY_OBJECTS: rc = GNUPG_Card_Error; break; case SC_ERROR_INVALID_CARD: rc = GNUPG_Invalid_Card; break; - case SC_ERROR_WRONG_LENGTH: rc = GNUPG_Card_Error; break; - case SC_ERROR_RECORD_NOT_FOUND: rc = GNUPG_Card_Error; break; - case SC_ERROR_INTERNAL: rc = GNUPG_Card_Error; break; default: rc = GNUPG_Card_Error; break; } return rc; @@ -207,6 +177,84 @@ card_close (CARD card) } } +/* Locate a simple 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. */ +static const char * +find_simple_tlv (const unsigned char *buffer, size_t length, + int tag, size_t *nbytes) +{ + const char *s = buffer; + size_t n = length; + size_t len; + + for (;;) + { + buffer = s; + if (n < 2) + return NULL; /* buffer too short for tag and length. */ + len = s[1]; + s += 2; n -= 2; + if (len == 255) + { + if (n < 2) + return NULL; /* we expected 2 more bytes with the length. */ + len = (s[0] << 8) | s[1]; + s += 2; n -= 2; + } + if (*buffer == tag) + { + *nbytes = len; + return s; + } + if (len > n) + return NULL; /* buffer too short to skip to the next tag. */ + s += len; n -= len; + } +} + +/* Find the ICC Serial Number within the provided BUFFER of LENGTH + (which should contain the GDO file) and return it as a hex encoded + string and allocated string in SERIAL. Return an error code when + the ICCSN was not found. */ +static int +find_iccsn (const unsigned char *buffer, size_t length, char **serial) +{ + size_t n; + const unsigned char *s; + char *p; + + s = find_simple_tlv (buffer, length, 0x5A, &n); + if (!s) + return GNUPG_Card_Error; + length -= s - buffer; + if (n > length) + { + /* Oops, it does not fit into the buffer. This is an invalid + encoding (or the buffer is too short. However, I have some + test cards with such an invalid encoding and therefore I use + this ugly workaround to return something I can further + experiment with. */ + if (n == 0x0D && length+1 == n) + { + log_debug ("enabling BMI testcard workaround\n"); + n--; + } + else + return GNUPG_Card_Error; /* Bad encoding; does not fit into buffer. */ + } + if (!n) + return GNUPG_Card_Error; /* Well, that is too short. */ + + *serial = p = xtrymalloc (2*n+1); + if (!*serial) + return GNUPG_Out_Of_Core; + for (; n; n--, p += 2, s++) + sprintf (p, "%02X", *s); + *p = 0; + return 0; +} /* Retrieve the serial number and the time of the last update of the @@ -220,12 +268,11 @@ card_close (CARD card) int card_get_serial_and_stamp (CARD card, char **serial, time_t *stamp) { - char *s; int rc; struct sc_path path; struct sc_file *file; - unsigned char buf[12]; - int i; + unsigned char buf[256]; + int buflen; if (!card || !serial || !stamp) return GNUPG_Invalid_Value; @@ -274,36 +321,72 @@ card_get_serial_and_stamp (CARD card, char **serial, time_t *stamp) sc_file_free (file); return GNUPG_Card_Error; } - if (file->size != 12) + + if (!file->size || file->size >= DIM(buf) ) { /* FIXME: Use a real parser */ - log_error ("unsupported size of GDO file\n"); + log_error ("unsupported size of GDO file (%d)\n", file->size); sc_file_free (file); return GNUPG_Card_Error; } + buflen = file->size; - rc = sc_read_binary (card->scard, 0, buf, DIM (buf), 0); + rc = sc_read_binary (card->scard, 0, buf, buflen, 0); sc_file_free (file); if (rc < 0) { log_error ("error reading GDO file: %s\n", sc_strerror (rc)); return GNUPG_Card_Error; } - if (rc != file->size) + if (rc != buflen) { log_error ("short read on GDO file\n"); return GNUPG_Card_Error; } - if (buf[0] != 0x5a || buf[1] != 10) - { - log_error ("invalid structure of GDO file\n"); - return GNUPG_Card_Error; + + rc = find_iccsn (buf, buflen, serial); + if (rc == GNUPG_Card_Error) + log_error ("invalid structure of GDO file\n"); + if (!rc && card->p15card && !strcmp (*serial, "D27600000000000000000000")) + { /* This is a German card with a silly serial number. Try to get + the serial number from the EF(TokenInfo). We indicate such a + serial number by the using the prefix: "FF0100". */ + const char *efser = card->p15card->serial_number; + char *p; + + if (!efser) + efser = ""; + + xfree (*serial); + *serial = NULL; + p = xtrymalloc (strlen (efser) + 7); + if (!p) + rc = GNUPG_Out_Of_Core; + else + { + strcpy (p, "FF0100"); + strcpy (p+6, efser); + *serial = p; + } } - *serial = s = xtrymalloc (21); - if (!*serial) - return GNUPG_Out_Of_Core; - for (i=0; i < 10; i++, s += 2) - sprintf (s, "%02X", buf[2+i]); - return 0; + else if (!rc && **serial == 'F' && (*serial)[1] == 'F') + { /* The serial number starts with our special prefix. This + requires that we put our default prefix "FF0000" in front. */ + char *p = xtrymalloc (strlen (*serial) + 7); + if (!p) + { + xfree (*serial); + *serial = NULL; + rc = GNUPG_Out_Of_Core; + } + else + { + strcpy (p, "FF0000"); + strcpy (p+6, *serial); + xfree (*serial); + *serial = p; + } + } + return rc; } diff --git a/scd/scdaemon.c b/scd/scdaemon.c index d1402b5ee..1d8bad21d 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -87,9 +87,9 @@ static ARGPARSE_OPTS opts[] = { { oDebug, "debug" ,4|16, N_("set debugging flags")}, { oDebugAll, "debug-all" ,0, N_("enable full debugging")}, { oDebugWait,"debug-wait",1, "@"}, - { oDebugSC, "debug-sc", 1, N_("N|set OpenSC debug level to N")}, + { oDebugSC, "debug-sc", 1, N_("|N|set OpenSC debug level to N")}, { oNoDetach, "no-detach" ,0, N_("do not detach from the console")}, - { oLogFile, "log-file" ,2, N_("use a log file for the server")}, + { oLogFile, "log-file" ,2, N_("use a log file for the server")}, {0}