diff --git a/scd/app-common.h b/scd/app-common.h index ed2a549ae..ad96fc2d6 100644 --- a/scd/app-common.h +++ b/scd/app-common.h @@ -235,9 +235,10 @@ const char *strapptype (apptype_t t); void app_update_priority_list (const char *arg); gpg_error_t app_send_card_list (ctrl_t ctrl); gpg_error_t app_send_active_apps (card_t card, ctrl_t ctrl); -char *card_get_serialno (card_t card, int is_canonical); +char *card_get_serialno (card_t card); char *app_get_serialno (app_t app); -char *yubikey_get_serialno (app_t app); +char *card_get_dispserialno (card_t card, int nofallback); +char *app_get_dispserialno (app_t app, int nofallback); void app_dump_state (void); void application_notify_card_reset (int slot); diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 0e3b3daad..440c4d027 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -1102,9 +1102,10 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) if (table[idx].special == -1) { - /* The serial number is very special. We could have used the - AID DO to retrieve it. The AID DO is available anyway but - not hex formatted. */ + /* The serial number is very special. We can't use the the AID + DO (0x4f) becuase this is the serialno per specs with the + correct appversion. We might however use a serialno with the + version set to 0.0 and that is what we need to return. */ char *serial = app_get_serialno (app); if (serial) @@ -1147,17 +1148,14 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) } if (table[idx].special == -4) { - char *serial = app_get_serialno (app); + char *serial; - if (serial) + if ((serial = app_get_dispserialno (app, 0))) { - if (strlen (serial) > 16+12) - { - send_status_info (ctrl, table[idx].name, serial+16, 12, NULL, 0); - xfree (serial); - return 0; - } + send_status_info (ctrl, table[idx].name, + serial, strlen (serial), NULL, 0); xfree (serial); + return 0; } return gpg_error (GPG_ERR_INV_NAME); } @@ -1383,39 +1381,6 @@ get_disp_name (app_t app) } -/* - * Yubikey has its own serial number at app->serialno. When Yubikey - * is used for OpenPGP card app, we get the serial number for OpenPGP - * card from its AID data object. - */ -char * -yubikey_get_serialno (app_t app) -{ - void *relptr; - unsigned char *buffer; - size_t buflen; - char *serial; - - relptr = get_one_do (app, 0x004F, &buffer, &buflen, NULL); - if (!relptr) - return NULL; - if (buflen != 16) - { - xfree (relptr); - return NULL; - } - - serial = xtrymalloc (32 + 1); - if (!serial) - return NULL; - - serial[32] = 0; - bin2hex (buffer, buflen, serial); - xfree (relptr); - return serial; -} - - /* Return the pretty formatted serialnumber. On error NULL is * returned. */ static char * @@ -6113,7 +6078,15 @@ app_select_openpgp (app_t app) app->appversion |= buffer[7]; manufacturer = (buffer[8]<<8 | buffer[9]); - /* For Yubikey, serialno is set in app.c, already. */ + /* For Yubikey, serialno is set in app.c, already. The problem + * is that the OpenPGP appversion has been set to 0.0 because we + * are not able to deduce this if the OpenPGP app has not been + * enabled. Thus we here to to use the appversion from DO 0x4f + * but return a serialno with a version 0.0 as set by app.c. + * Users of scdaemon taking the version from the serialno won't + * work anymore and need to be modified. Recall that our + * architecture requires exactly one serilano per card. + */ if (app->card->cardtype == CARDTYPE_YUBIKEY) xfree (buffer); else diff --git a/scd/app-piv.c b/scd/app-piv.c index 0288d8528..36324e630 100644 --- a/scd/app-piv.c +++ b/scd/app-piv.c @@ -746,38 +746,6 @@ parse_chv_keyref (const char *keyrefstr) } -/* Return an allocated string with the serial number in a format to be - * show to the user. With FAILMODE is true return NULL if such an - * abbreviated S/N is not available, else return the full serial - * number as a hex string. May return NULL on malloc problem. */ -static char * -get_dispserialno (app_t app, int failmode) -{ - char *result; - - if (app->card && app->card->serialno && app->card->serialnolen == 3+1+4 - && !memcmp (app->card->serialno, "\xff\x02\x00", 3)) - { - /* This is a 4 byte S/N of a Yubikey which seems to be printed - * on the token in decimal. Maybe they will print larger S/N - * also in decimal but we can't be sure, thus do it only for - * these 32 bit numbers. */ - unsigned long sn; - sn = app->card->serialno[4] * 16777216; - sn += app->card->serialno[5] * 65536; - sn += app->card->serialno[6] * 256; - sn += app->card->serialno[7]; - result = xtryasprintf ("yk-%lu", sn); - } - else if (failmode) - result = NULL; /* No Abbreviated S/N. */ - else - result = app_get_serialno (app); - - return result; -} - - /* The verify command can be used to retrieve the security status of * the card. Given the PIN name (e.g. "PIV.80" for the application * pin, a ISO7817_VERIFY_* code is returned or a non-negative number @@ -842,7 +810,7 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) } else if (table[idx].special == -3) { - char *tmp = get_dispserialno (app, 1); + char *tmp = app_get_dispserialno (app, 1); if (tmp) { @@ -1789,7 +1757,7 @@ make_prompt (app_t app, int remaining, const char *firstline) { char *serial, *tmpbuf, *result; - serial = get_dispserialno (app, 0); + serial = app_get_dispserialno (app, 0); if (!serial) return NULL; diff --git a/scd/app.c b/scd/app.c index 872eb0ab2..f17e2d62b 100644 --- a/scd/app.c +++ b/scd/app.c @@ -302,7 +302,7 @@ app_send_devinfo (ctrl_t ctrl) char *serialno; char card_info[80]; - serialno = card_get_serialno (c, 0); + serialno = card_get_serialno (c); snprintf (card_info, sizeof card_info, "DEVICE %s %s", strcardtype (c->cardtype), serialno); xfree (serialno); @@ -514,10 +514,7 @@ app_new_register (int slot, ctrl_t ctrl, const char *name, card->serialno[2] = 0x0; card->serialno[3] = formfactor; memcpy (card->serialno + 4, s0, n); - /* Note that we do not clear the error - * so that no further serial number - * testing is done. After all we just - * set the serial number. */ + err = app_munge_serialno (card); } } @@ -559,11 +556,11 @@ app_new_register (int slot, ctrl_t ctrl, const char *name, } } - if (!err) + if (!err && card->cardtype != CARDTYPE_YUBIKEY) err = iso7816_select_file (slot, 0x2F02, 0); - if (!err) + if (!err && card->cardtype != CARDTYPE_YUBIKEY) err = iso7816_read_binary (slot, 0, 0, &result, &resultlen); - if (!err) + if (!err && card->cardtype != CARDTYPE_YUBIKEY) { size_t n; const unsigned char *p; @@ -1142,7 +1139,9 @@ card_unref_locked (card_t card) FF 00 00 = For serial numbers starting with an FF FF 01 00 = Some german p15 cards return an empty serial number so the serial number from the EF(TokenInfo) is used instead. - FF 02 00 = Serial number from Yubikey config + FF 02 00 = Serial number from Yubikey config. + This is normally not seen because we modify this here + to an OpenPGP Card s/n. FF 7F 00 = No serialno. All other serial numbers not starting with FF are used as they are. @@ -1150,7 +1149,48 @@ card_unref_locked (card_t card) gpg_error_t app_munge_serialno (card_t card) { - if (card->serialnolen && card->serialno[0] == 0xff) + if (card->cardtype == CARDTYPE_YUBIKEY + && card->serialnolen == 3 + 1 + 4 + && !memcmp (card->serialno, "\xff\x02\x00", 3)) + { + /* An example for a serial number is + * FF020001008A77C1 + * ~~~~~~--~~~~~~~~ + * ! ! !--------- 4 byte s/n + * ! !----------- Form factor + * !----------------- Our prefix + * Yubico seems to use the decimalized version of their S/N + * as the OpenPGP card S/N. Thus in theory we can contruct the + * number from this information so that we do not rely on having + * the OpenPGP app enabled. + */ + unsigned long sn; + sn = card->serialno[4] * 16777216; + sn += card->serialno[5] * 65536; + sn += card->serialno[6] * 256; + sn += card->serialno[7]; + if (sn <= 99999999ul) + { + char *buf = xtrymalloc (16); + if (!buf) + return gpg_error_from_syserror (); + memcpy (buf, "\xD2\x76\x00\x01\x24\x01", 6); + buf[6] = 0; /* Application version which we don't know */ + buf[7] = 0; /* thus we use 0.0 and don't use this directly. */ + buf[8] = 0; /* Manufacturer: Yubico (0x0006). */ + buf[9] = 6; + buf[10] = (sn >> 24); + buf[11] = (sn >> 16); + buf[12] = (sn >> 8); + buf[13] = sn; + buf[14] = 0; /* Last two bytes are RFU. */ + buf[15] = 0; + xfree (card->serialno); + card->serialno = buf; + card->serialnolen = 16; + } + } + else if (card->serialnolen && card->serialno[0] == 0xff) { /* The serial number starts with our special prefix. This requires that we put our default prefix "FF0000" in front. */ @@ -1182,7 +1222,7 @@ app_munge_serialno (card_t card) returned as a malloced string (hex encoded) in SERIAL. Caller must free SERIAL unless the function returns an error. */ char * -card_get_serialno (card_t card, int is_canonical) +card_get_serialno (card_t card) { char *serial; @@ -1191,31 +1231,7 @@ card_get_serialno (card_t card, int is_canonical) if (!card->serialnolen) serial = xtrystrdup ("FF7F00"); - else if (card->cardtype == CARDTYPE_YUBIKEY && !is_canonical - && card->app && card->app->apptype == APPTYPE_OPENPGP) - { - app_t a; - - for (a = card->app; a; a = a->next) - if (a->apptype == APPTYPE_OPENPGP) - break; - - /* Found the OpenPGP app */ - if (a->apptype == APPTYPE_OPENPGP) - { - if (card->app != a) - run_reselect (NULL, card, a, card->app); - - serial = yubikey_get_serialno (a); - - if (card->app != a) - run_reselect (NULL, card, card->app, a); - } - else - goto other; - } else - other: serial = bin2hex (card->serialno, card->serialnolen, NULL); return serial; @@ -1226,8 +1242,109 @@ char * app_get_serialno (app_t app) { if (!app || !app->card) - return NULL; - return card_get_serialno (app->card, 0); + { + gpg_err_set_errno (0); + return NULL; + } + return card_get_serialno (app->card); +} + + +/* Return an allocated string with the serial number in a format to be + * show to the user. With NOFALLBACK set to true return NULL if such an + * abbreviated S/N is not available, else return the full serial + * number as a hex string. May return NULL on malloc problem. */ +char * +card_get_dispserialno (card_t card, int nofallback) +{ + char *result, *p; + unsigned long sn; + + if (card && card->serialno && card->serialnolen == 3+1+4 + && !memcmp (card->serialno, "\xff\x02\x00", 3)) + { + /* This is a 4 byte S/N of a Yubikey which seems to be printed + * on the token in decimal. Maybe they will print larger S/N + * also in decimal but we can't be sure, thus do it only for + * these 32 bit numbers. */ + sn = card->serialno[4] * 16777216; + sn += card->serialno[5] * 65536; + sn += card->serialno[6] * 256; + sn += card->serialno[7]; + if ((card->cardversion >> 16) >= 5) + result = xtryasprintf ("%lu %03lu %03lu", + (sn/1000000ul), + (sn/1000ul % 1000ul), + (sn % 1000ul)); + else + result = xtryasprintf ("%lu", sn); + } + else if (card && card->cardtype == CARDTYPE_YUBIKEY) + { + /* Get back the printed Yubikey number from the OpenPGP AID + * Example: D2760001240100000006008A77C10000 + */ + result = card_get_serialno (card); + if (result && strlen (result) >= 28 && !strncmp (result+16, "0006", 4)) + { + sn = xtoi_2 (result+20) * 16777216; + sn += xtoi_2 (result+22) * 65536; + sn += xtoi_2 (result+24) * 256; + sn += xtoi_2 (result+26); + if ((card->cardversion >> 16) >= 5) + p = xtryasprintf ("%lu %03lu %03lu", + (sn/1000000ul), + (sn/1000ul % 1000ul), + (sn % 1000ul)); + else + p = xtryasprintf ("%lu", sn); + if (p) + { + xfree (result); + result = p; + } + } + else if (nofallback) + { + xfree (result); + result = NULL; + } + } + else if (card && card->app && card->app->apptype == APPTYPE_OPENPGP) + { + /* Extract number from standard OpenPGP AID. */ + result = card_get_serialno (card); + if (result && strlen (result) > 16+12) + { + memcpy (result, result+16, 4); + result[4] = ' '; + memcpy (result+5, result+20, 8); + result[13] = 0; + } + else if (nofallback) + { + xfree (result); + result = NULL; + } + } + else if (nofallback) + result = NULL; /* No Abbreviated S/N. */ + else + result = card_get_serialno (card); + + return result; +} + +/* Same as card_get_dispserialno but takes an APP object. */ +char * +app_get_dispserialno (app_t app, int nofallback) +{ + if (!app || !app->card) + { + gpg_err_set_errno (0); + return NULL; + } + return card_get_dispserialno (app->card, nofallback); } @@ -2136,7 +2253,7 @@ send_serialno_and_app_status (card_t card, int with_apps, ctrl_t ctrl) membuf_t mb; int any = 0; - serial = card_get_serialno (card, 1); + serial = card_get_serialno (card); if (!serial) return 0; /* Oops. */ diff --git a/scd/command.c b/scd/command.c index 5eb97fb20..457347121 100644 --- a/scd/command.c +++ b/scd/command.c @@ -345,7 +345,7 @@ cmd_serialno (assuan_context_t ctx, char *line) return rc; } - serial = card_get_serialno (ctrl->card_ctx, 0); + serial = card_get_serialno (ctrl->card_ctx); if (!serial) return gpg_error (GPG_ERR_INV_VALUE); @@ -521,7 +521,7 @@ cmd_learn (assuan_context_t ctx, char *line) send_status_direct (ctrl, "READER", reader); /* No need to free the string of READER. */ - serial = card_get_serialno (ctrl->card_ctx, 1); + serial = card_get_serialno (ctrl->card_ctx); if (!serial) return gpg_error (GPG_ERR_INV_VALUE);