diff options
Diffstat (limited to 'scd/apdu.c')
-rw-r--r-- | scd/apdu.c | 55 |
1 files changed, 44 insertions, 11 deletions
diff --git a/scd/apdu.c b/scd/apdu.c index d63157ce7..219dda04b 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -2817,8 +2817,6 @@ send_apdu (int slot, unsigned char *apdu, size_t apdulen, length limit. n > 1 := Use extended length with up to N bytes. - FIXME: We don't support extended length return values larger - than 256 bytes due to a static buffer. */ static int send_le (int slot, int class, int ins, int p0, int p1, @@ -2826,9 +2824,12 @@ send_le (int slot, int class, int ins, int p0, int p1, unsigned char **retbuf, size_t *retbuflen, struct pininfo_s *pininfo, int extended_mode) { -#define RESULTLEN 258 - unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in - the driver. */ +#define SHORT_RESULT_BUFFER_SIZE 258 + /* We allocate 8 extra bytes as a safety margin towards a driver bug. */ + unsigned char short_result_buffer[SHORT_RESULT_BUFFER_SIZE+10]; + unsigned char *result_buffer = NULL; + size_t result_buffer_size; + unsigned char *result; size_t resultlen; unsigned char short_apdu_buffer[5+256+1]; unsigned char *apdu_buffer = NULL; @@ -2873,8 +2874,22 @@ send_le (int slot, int class, int ins, int p0, int p1, else if (lc == -1 && extended_mode > 0) use_extended_length = 1; - if (le != -1 && (le > 256 || le < 0)) - return SW_WRONG_LENGTH; + if (le != -1 && (le > (extended_mode > 0? 255:256) || le < 0)) + { + /* Expected Data does not fit into an APDU. What we do now + depends on the EXTENDED_MODE parameter. Note that a check + for command chaining does not make sense because we are + looking at Le. */ + if (!extended_mode) + return SW_WRONG_LENGTH; /* No way to send such an APDU. */ + else if (use_extended_length) + ; /* We are already using extended length. */ + else if (extended_mode > 0) + use_extended_length = 1; + else + return SW_HOST_INV_VALUE; + } + if ((!data && lc != -1) || (data && lc == -1)) return SW_HOST_INV_VALUE; @@ -2885,7 +2900,7 @@ send_le (int slot, int class, int ins, int p0, int p1, /* Space for: cls/ins/p1/p2+Z+2_byte_Lc+Lc+2_byte_Le. */ apdu_buffer_size = 4 + 1 + (lc >= 0? (2+lc):0) + 2; - apdu_buffer = xtrymalloc (apdu_buffer_size); + apdu_buffer = xtrymalloc (apdu_buffer_size + 10); if (!apdu_buffer) return SW_HOST_OUT_OF_CORE; apdu = apdu_buffer; @@ -2896,6 +2911,24 @@ send_le (int slot, int class, int ins, int p0, int p1, apdu = short_apdu_buffer; } + if (use_extended_length && (le > 256 || le < 0)) + { + result_buffer_size = le < 0? 4096 : le; + result_buffer = xtrymalloc (result_buffer_size + 10); + if (!result_buffer) + { + xfree (apdu_buffer); + return SW_HOST_OUT_OF_CORE; + } + result = result_buffer; + } + else + { + result_buffer_size = SHORT_RESULT_BUFFER_SIZE; + result = short_result_buffer; + } +#undef SHORT_RESULT_BUFFER_SIZE + if ((sw = lock_slot (slot))) return sw; @@ -2963,7 +2996,7 @@ send_le (int slot, int class, int ins, int p0, int p1, /* As a safeguard don't pass any garbage to the driver. */ assert (apdulen <= apdu_buffer_size); memset (apdu+apdulen, 0, apdu_buffer_size - apdulen); - resultlen = RESULTLEN; + resultlen = result_buffer_size; rc = send_apdu (slot, apdu, apdulen, result, &resultlen, pininfo); if (rc || resultlen < 2) { @@ -3051,7 +3084,7 @@ send_le (int slot, int class, int ins, int p0, int p1, apdu[apdulen++] = len; assert (apdulen <= apdu_buffer_size); memset (apdu+apdulen, 0, apdu_buffer_size - apdulen); - resultlen = RESULTLEN; + resultlen = result_buffer_size; rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL); if (rc || resultlen < 2) { @@ -3114,7 +3147,6 @@ send_le (int slot, int class, int ins, int p0, int p1, log_printhex (" dump: ", *retbuf, *retbuflen); return sw; -#undef RESULTLEN } /* Send an APDU to the card in SLOT. The APDU is created from all @@ -3210,6 +3242,7 @@ apdu_send_direct (int slot, int extended_mode, unsigned char **retbuf, size_t *retbuflen) { #define RESULTLEN 258 + /* FIXME: Implement dynamic result buffer and extended Le. */ unsigned char apdu[5+256+1]; size_t apdulen; unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in |