diff options
author | Werner Koch <[email protected]> | 2020-11-25 14:27:21 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2020-11-25 14:27:21 +0000 |
commit | c7b9a4ee439eca5a4bde4781f7d8983af3b9201e (patch) | |
tree | 91c9e32dd9b3465e28108e5f5f30dc3dd5a46f78 | |
parent | g13: Add missing header (diff) | |
download | gnupg-c7b9a4ee439eca5a4bde4781f7d8983af3b9201e.tar.gz gnupg-c7b9a4ee439eca5a4bde4781f7d8983af3b9201e.zip |
scd:p15: Improve support for some CardOS based cards.
* scd/iso7816.c (iso7816_read_binary_ext): Add optional arg r_sw and
change callers.
(iso7816_read_record): Factor all code out to ...
(iso7816_read_record_ext): new.
* scd/app-p15.c (select_and_read_binary): Fallback to record reading.
(read_ef_aodf): Clear EOF error.
-rw-r--r-- | scd/app-p15.c | 35 | ||||
-rw-r--r-- | scd/iso7816.c | 40 | ||||
-rw-r--r-- | scd/iso7816.h | 7 |
3 files changed, 68 insertions, 14 deletions
diff --git a/scd/app-p15.c b/scd/app-p15.c index 602e97e2c..3737b85ca 100644 --- a/scd/app-p15.c +++ b/scd/app-p15.c @@ -466,6 +466,7 @@ select_and_read_binary (int slot, unsigned short efid, const char *efid_desc, unsigned char **buffer, size_t *buflen) { gpg_error_t err; + int sw; err = iso7816_select_file (slot, efid, 0); if (err) @@ -474,11 +475,33 @@ select_and_read_binary (int slot, unsigned short efid, const char *efid_desc, efid_desc, efid, gpg_strerror (err)); return err; } - err = iso7816_read_binary (slot, 0, 0, buffer, buflen); - if (err) + + err = iso7816_read_binary_ext (slot, 0, 0, 0, buffer, buflen, &sw); + if (err && sw == 0x6981) { - log_error ("p15: error reading %s (0x%04X): %s\n", - efid_desc, efid, gpg_strerror (err)); + /* Command was not possible for file structure. Try to read the + * first record instead. */ + err = iso7816_read_record_ext (slot, 1, 1, 0, buffer, buflen, &sw); + if (err) + { + log_error ("p15: error reading %s (0x%04X)" + " after fallback to record mode: %s (sw=%04X)\n", + efid_desc, efid, gpg_strerror (err), sw); + return err; + } + /* On a CardOS based card I noticed that the record started with + * a byte 0x01 followed by another byte with the length of the + * record. Detect this here and remove this prefix. */ + if (*buflen > 2 && (*buffer)[0] == 1 && (*buffer)[1] == *buflen - 2) + { + memmove (*buffer, *buffer + 2, *buflen - 2); + *buflen = *buflen - 2; + } + } + else if (err) + { + log_error ("p15: error reading %s (0x%04X): %s (sw=%04X)\n", + efid_desc, efid, gpg_strerror (err), sw); return err; } return 0; @@ -2180,6 +2203,8 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result) extensions of pkcs#15. */ ready: + if (gpg_err_code (err) == GPG_ERR_EOF) + err = 0; if (opt.verbose) { log_info ("p15: AODF %04hX: id=", fid); @@ -2922,7 +2947,7 @@ readcert_by_cdf (app_t app, cdf_object_t cdf, goto leave; err = iso7816_read_binary_ext (app_get_slot (app), 1, cdf->off, cdf->len, - &buffer, &buflen); + &buffer, &buflen, NULL); if (!err && (!buflen || *buffer == 0xff)) err = gpg_error (GPG_ERR_NOT_FOUND); if (err) diff --git a/scd/iso7816.c b/scd/iso7816.c index 5316d0b3b..ef02d64cc 100644 --- a/scd/iso7816.c +++ b/scd/iso7816.c @@ -875,13 +875,15 @@ iso7816_get_challenge (int slot, int length, unsigned char *buffer) } /* 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. */ + * 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. If + * R_SW is not NULL the last status word is stored there. */ gpg_error_t iso7816_read_binary_ext (int slot, int extended_mode, size_t offset, size_t nmax, - unsigned char **result, size_t *resultlen) + unsigned char **result, size_t *resultlen, + int *r_sw) { int sw; unsigned char *buffer; @@ -889,6 +891,9 @@ iso7816_read_binary_ext (int slot, int extended_mode, int read_all = !nmax; size_t n; + if (r_sw) + *r_sw = 0; + if (!result || !resultlen) return gpg_error (GPG_ERR_INV_VALUE); *result = NULL; @@ -914,6 +919,8 @@ iso7816_read_binary_ext (int slot, int extended_mode, ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL, n, &buffer, &bufferlen); } + if (r_sw) + *r_sw = sw; if (*result && sw == SW_BAD_P0_P1) { @@ -974,7 +981,8 @@ gpg_error_t iso7816_read_binary (int slot, size_t offset, size_t nmax, unsigned char **result, size_t *resultlen) { - return iso7816_read_binary_ext (slot, 0, offset, nmax, result, resultlen); + return iso7816_read_binary_ext (slot, 0, offset, nmax, + result, resultlen, NULL); } @@ -984,15 +992,20 @@ iso7816_read_binary (int slot, size_t offset, size_t nmax, should be 0 to read the current EF or contain a short EF. 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. */ + RESULTLEN. If R_SW is not NULL the last status word is stored + there. */ gpg_error_t -iso7816_read_record (int slot, int recno, int reccount, int short_ef, - unsigned char **result, size_t *resultlen) +iso7816_read_record_ext (int slot, int recno, int reccount, int short_ef, + unsigned char **result, size_t *resultlen, + int *r_sw) { int sw; unsigned char *buffer; size_t bufferlen; + if (r_sw) + *r_sw = 0; + if (!result || !resultlen) return gpg_error (GPG_ERR_INV_VALUE); *result = NULL; @@ -1011,6 +1024,8 @@ iso7816_read_record (int slot, int recno, int reccount, int short_ef, short_ef? short_ef : 0x04, -1, NULL, 0, &buffer, &bufferlen); + if (r_sw) + *r_sw = sw; if (sw != SW_SUCCESS && sw != SW_EOF_REACHED) { @@ -1028,6 +1043,15 @@ iso7816_read_record (int slot, int recno, int reccount, int short_ef, } +gpg_error_t +iso7816_read_record (int slot, int recno, int reccount, int short_ef, + unsigned char **result, size_t *resultlen) +{ + return iso7816_read_record_ext (slot, recno, reccount, short_ef, + result, resultlen, NULL); +} + + /* Perform an UPDATE BINARY command on card in SLOT. Write DATA of * length DATALEN to a transparent file at OFFSET. */ gpg_error_t diff --git a/scd/iso7816.h b/scd/iso7816.h index 10a1aa8d1..d68ee57f2 100644 --- a/scd/iso7816.h +++ b/scd/iso7816.h @@ -142,9 +142,14 @@ gpg_error_t iso7816_get_challenge (int slot, gpg_error_t iso7816_read_binary_ext (int slot, int extended_mode, size_t offset, size_t nmax, - unsigned char **result, size_t *resultlen); + unsigned char **result, size_t *resultlen, + int *r_sw); 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_ext (int slot, int recno, int reccount, + int short_ef, + unsigned char **result, size_t *resultlen, + int *r_sw); gpg_error_t iso7816_read_record (int slot, int recno, int reccount, int short_ef, unsigned char **result, size_t *resultlen); |