diff -udrNPp --exclude=.svn opensc.orig/src/libopensc/card-cardos.c opensc/src/libopensc/card-cardos.c --- opensc.orig/src/libopensc/card-cardos.c 2008-07-31 14:20:30.000000000 +0200 +++ opensc/src/libopensc/card-cardos.c 2008-07-31 14:35:18.000000000 +0200 @@ -389,7 +389,7 @@ static const int df_acl[9] = { SC_AC_OP_REHABILITATE, /* DF */ SC_AC_OP_DELETE, /* DF */ - -1, /* ADMIN DF */ + SC_AC_OP_UPDATE, /* ADMIN DF */ SC_AC_OP_CREATE, /* Files */ -1 /* Reserved */ }; @@ -404,7 +404,7 @@ static const int ef_acl[9] = { /* XXX: ADMIN should be an ACL type of its own, or mapped * to erase */ - -1, /* ADMIN EF (modify meta information?) */ + SC_AC_OP_UPDATE, /* ADMIN EF (modify meta information?) */ -1, /* INC (-> cylic fixed files) */ -1 /* DEC */ }; diff -udrNPp --exclude=.svn opensc.orig/src/tools/pkcs15-tool.c opensc/src/tools/pkcs15-tool.c --- opensc.orig/src/tools/pkcs15-tool.c 2008-07-31 14:20:28.000000000 +0200 +++ opensc/src/tools/pkcs15-tool.c 2008-07-31 15:03:05.000000000 +0200 @@ -2,6 +2,7 @@ * pkcs15-tool.c: Tool for poking with PKCS #15 smart cards * * Copyright (C) 2001 Juha Yrjölä + * Copyright (C) 2008 Andreas Jellinghaus * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -91,6 +92,8 @@ static const struct option options[] = { #if defined(ENABLE_OPENSSL) && (defined(_WIN32) || defined(HAVE_INTTYPES_H)) { "read-ssh-key", required_argument, NULL, OPT_READ_SSH }, #endif + { "test-update", no_argument, NULL, 'T' }, + { "update", no_argument, NULL, 'U' }, { "reader", required_argument, NULL, OPT_READER }, { "pin", required_argument, NULL, OPT_PIN }, { "new-pin", required_argument, NULL, OPT_NEWPIN }, @@ -117,6 +120,8 @@ static const char *option_help[] = { "Lists public keys", "Reads public key with ID ", "Reads public key with ID , outputs ssh format", + "Test if the card needs a security update", + "Update the card with a security update", "Uses reader number ", "Specify PIN", "Specify New PIN (when changing or unblocking)", @@ -1177,6 +1182,218 @@ static int learn_card(void) return 0; } +static int test_update(sc_card_t *in_card) +{ + sc_apdu_t apdu; + static u8 cmd1[2] = { 0x50, 0x15}; + u8 rbuf[258]; + int rc; + int r; + static u8 fci_bad[] = { 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static u8 fci_good[] = { 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00 }; + + + + if (strcmp("cardos",in_card->driver->short_name) != 0) { + printf("not using the cardos driver, card is fine."); + rc = 0; + goto end; + } + + if (strcmp("OpenSC Card",p15card->label) != 0) { + printf("not initialized by opensc, card is fine."); + rc = 0; + goto end; + } + + /* first select file on 5015 and get fci */ + sc_format_apdu(in_card, &apdu, SC_APDU_CASE_4_SHORT, 0xa4, 0x08, 0x00); + apdu.lc = sizeof(cmd1); + apdu.datalen = sizeof(cmd1); + apdu.data = cmd1; + apdu.le = 256; + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + + r = sc_transmit_apdu(card, &apdu); + if (r < 0) { + printf("selecting folder failed: %s\n", sc_strerror(r)); + rc = 2; + goto end; + } + + if (apdu.sw1 != 0x90) { + printf("apdu command select file failed: card returned %02X %02X\n", + apdu.sw1, apdu.sw2); + rc = 2; + goto end; + + } + + if (apdu.resplen < 6) { + printf("select file did not return enough data (length %d)\n", + (int) apdu.resplen); + goto bad_fci; + } + + if (rbuf[0] != 0x6f) { + printf("select file did not return the information we need\n"); + goto bad_fci; + } + + if (rbuf[1] != apdu.resplen -2) { + printf("select file returned inconsistent information\n"); + goto bad_fci; + } + + { + int i=0; + while(i < rbuf[1]) { + if (rbuf[2+i] == 0x86) { /* found our buffer */ + break; + } + /* other tag */ + i += 2 + rbuf[2+i+1]; /* length of this tag*/ + } + if (rbuf[2+i+1] < 9 || 2+i+2+9 > apdu.resplen) { + printf("select file returned short fci\n"); + goto bad_fci; + } + + if (memcmp(&rbuf[2+i+2],fci_good,sizeof(fci_good)) == 0) { + printf("fci is up-to-date, card is fine\n"); + rc = 0; + goto end; + } + + if (memcmp(&rbuf[2+i+2],fci_bad,sizeof(fci_bad)) == 0) { + printf("fci is out-of-date, card is vulnerable\n"); + rc = 1; + goto end; + } + + printf("select file returned fci with unknown data\n"); + goto bad_fci; + } +end: + /* 0 = card ok, 1 = card vulnerable, 2 = problem! */ + return rc; + +bad_fci: + util_hex_dump(stdout,rbuf,apdu.resplen," "); + printf("\n"); + return 2; +} + +static int update(sc_card_t *in_card) +{ + sc_apdu_t apdu; + u8 rbuf[258]; + static u8 cmd1[2] = { 0x50, 0x15}; + static u8 cmd3[11] = { 0x86, 0x09, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00}; + int r; + + /* first select file on 5015 */ + sc_format_apdu(in_card, &apdu, SC_APDU_CASE_3_SHORT, 0xa4, 0x08, 0x00); + apdu.lc = sizeof(cmd1); + apdu.datalen = sizeof(cmd1); + apdu.data = cmd1; + + r = sc_transmit_apdu(card, &apdu); + if (r < 0) { + printf("selecting folder failed: %s\n", sc_strerror(r)); + goto end; + } + + if (apdu.sw1 != 0x90) { + printf("apdu command select file: card returned %02X %02X\n", + apdu.sw1, apdu.sw2); + goto end; + + } + + /* next get lifecycle */ + memset(&apdu, 0, sizeof(apdu)); + sc_format_apdu(in_card, &apdu, SC_APDU_CASE_2, 0xca, 0x01, 0x83); + apdu.cla = 0x00; + apdu.le = 256; + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + + r = sc_transmit_apdu(card, &apdu); + if (r < 0) { + printf("get lifecycle failed: %s\n", sc_strerror(r)); + goto end; + } + + if (apdu.sw1 != 0x90) { + printf("get lifecycle failed: card returned %02X %02X\n", + apdu.sw1, apdu.sw2); + goto end; + + } + + if (apdu.resplen < 1) { + printf("get lifecycle failed: lifecycle byte not in response\n"); + goto end; + } + + if (rbuf[0] != 0x10 && rbuf[0] != 0x20) { + printf("lifecycle neither user nor admin, can't proceed\n"); + goto end; + } + + if (rbuf[0] == 0x20) + goto skip_change_lifecycle; + + /* next phase control / change lifecycle to operational */ + memset(&apdu, 0, sizeof(apdu)); + sc_format_apdu(in_card, &apdu, SC_APDU_CASE_1, 0x10, 0x00, 0x00); + apdu.cla = 0x80; + + r = sc_transmit_apdu(card, &apdu); + if (r < 0) { + printf("change lifecycle failed: %s\n", sc_strerror(r)); + goto end; + } + + if (apdu.sw1 != 0x90) { + printf("apdu command change lifecycle failed: card returned %02X %02X\n", + apdu.sw1, apdu.sw2); + goto end; + + } + +skip_change_lifecycle: + /* last update AC */ + memset(&apdu, 0, sizeof(apdu)); + sc_format_apdu(in_card, &apdu, SC_APDU_CASE_3_SHORT, 0xda, 0x01, 0x6f); + apdu.lc = sizeof(cmd3); + apdu.datalen = sizeof(cmd3); + apdu.data = cmd3; + apdu.le = 0; + apdu.resplen = 0; + apdu.resp = NULL; + + r = sc_transmit_apdu(card, &apdu); + if (r < 0) { + printf("update fci failed: %s\n", sc_strerror(r)); + goto end; + } + + if (apdu.sw1 != 0x90) { + printf("apdu command update fci failed: card returned %02X %02X\n", + apdu.sw1, apdu.sw2); + goto end; + + } + + printf("security update applied successfully.\n"); +end: + return 0; +} + int main(int argc, char * const argv[]) { int err = 0, r, c, long_optind = 0; @@ -1195,11 +1412,13 @@ int main(int argc, char * const argv[]) int do_change_pin = 0; int do_unblock_pin = 0; int do_learn_card = 0; + int do_test_update = 0; + int do_update = 0; int action_count = 0; sc_context_param_t ctx_param; while (1) { - c = getopt_long(argc, argv, "r:cuko:va:LR:CwD", options, &long_optind); + c = getopt_long(argc, argv, "r:cuko:va:LR:CwDTU", options, &long_optind); if (c == -1) break; if (c == '?') @@ -1263,6 +1482,14 @@ int main(int argc, char * const argv[]) do_learn_card = 1; action_count++; break; + case 'T': + do_test_update = 1; + action_count++; + break; + case 'U': + do_update = 1; + action_count++; + break; case OPT_READER: opt_reader = atoi(optarg); break; @@ -1390,6 +1617,18 @@ int main(int argc, char * const argv[]) goto end; action_count--; } + if (do_test_update || do_update) { + err = test_update(card); + action_count--; + if (err == 2) { /* problem */ + err =1; + goto end; + } + if (do_update && err == 1) { /* card vulnerable */ + if ((err = update(card))) + goto end; + } + } end: if (p15card) sc_pkcs15_unbind(p15card);