static int iso7816_read_binary(struct sc_card *card, unsigned int idx, u8 *buf, size_t count, unsigned long flags) { struct sc_context *ctx = card->ctx; struct sc_apdu apdu; int r; if (idx > 0x7fff) { sc_log(ctx, "invalid EF offset: 0x%X > 0x7FFF", idx); return SC_ERROR_OFFSET_TOO_LARGE; } sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0xB0, (idx >> 8) & 0x7F, idx & 0xFF); apdu.le = count; apdu.resplen = count; apdu.resp = buf; fixup_transceive_length(card, &apdu); r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, r, "APDU transmit failed"); if (apdu.resplen == 0) LOG_FUNC_RETURN(ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r == SC_ERROR_FILE_END_REACHED) LOG_FUNC_RETURN(ctx, apdu.resplen); LOG_TEST_RET(ctx, r, "Check SW error"); if (apdu.resplen < count) { r = iso7816_read_binary(card, idx + apdu.resplen, buf + apdu.resplen, count - apdu.resplen, flags); /* Ignore all but 'corrupted data' errors */ if (r == SC_ERROR_CORRUPTED_DATA) LOG_FUNC_RETURN(ctx, SC_ERROR_CORRUPTED_DATA); else if (r > 0) apdu.resplen += r; } LOG_FUNC_RETURN(ctx, apdu.resplen); }
/* If called with NAME as NULL, select the best fitting application and return a context; otherwise select the application with NAME and return a context. SLOT identifies the reader device. Returns an error code and stores NULL at R_APP if no application was found or no card is present. */ gpg_error_t select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app) { gpg_error_t err; app_t app = NULL; unsigned char *result = NULL; size_t resultlen; int want_undefined; (void)ctrl; *r_app = NULL; want_undefined = (name && !strcmp (name, "undefined")); err = lock_reader (slot, ctrl); if (err) return err; /* First check whether we already have an application to share. */ app = lock_table[slot].initialized ? lock_table[slot].app : NULL; if (app && name) if (!app->apptype || ascii_strcasecmp (app->apptype, name)) { unlock_reader (slot); if (app->apptype) log_info ("application '%s' in use by reader %d - can't switch\n", app->apptype, slot); return gpg_error (GPG_ERR_CONFLICT); } /* Don't use a non-reusable marked application. */ if (app && app->no_reuse) { unlock_reader (slot); log_info ("lingering application '%s' in use by reader %d" " - can't switch\n", app->apptype? app->apptype:"?", slot); return gpg_error (GPG_ERR_CONFLICT); } /* If we don't have an app, check whether we have a saved application for that slot. This is useful so that a card does not get reset even if only one session is using the card - this way the PIN cache and other cached data are preserved. */ if (!app && lock_table[slot].initialized && lock_table[slot].last_app) { app = lock_table[slot].last_app; if (!name || (app->apptype && !ascii_strcasecmp (app->apptype, name)) ) { /* Yes, we can reuse this application - either the caller requested an unspecific one or the requested one matches the saved one. */ lock_table[slot].app = app; lock_table[slot].last_app = NULL; } else { /* No, this saved application can't be used - deallocate it. */ lock_table[slot].last_app = NULL; deallocate_app (app); app = NULL; } } /* If we can reuse an application, bump the reference count and return it. */ if (app) { if (app->slot != slot) log_bug ("slot mismatch %d/%d\n", app->slot, slot); app->slot = slot; app->ref_count++; *r_app = app; unlock_reader (slot); return 0; /* Okay: We share that one. */ } /* Need to allocate a new one. */ app = xtrycalloc (1, sizeof *app); if (!app) { err = gpg_error_from_syserror (); log_info ("error allocating context: %s\n", gpg_strerror (err)); unlock_reader (slot); return err; } app->slot = slot; /* Fixme: We should now first check whether a card is at all present. */ /* Try to read the GDO file first to get a default serial number. We skip this if the undefined application has been requested. */ if (!want_undefined) { err = iso7816_select_file (slot, 0x3F00, 1, NULL, NULL); if (!err) err = iso7816_select_file (slot, 0x2F02, 0, NULL, NULL); if (!err) err = iso7816_read_binary (slot, 0, 0, &result, &resultlen); if (!err) { size_t n; const unsigned char *p; p = find_tlv_unchecked (result, resultlen, 0x5A, &n); if (p) resultlen -= (p-result); if (p && n > resultlen && n == 0x0d && resultlen+1 == n) { /* The object 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. */ log_info ("enabling BMI testcard workaround\n"); n--; } if (p && n <= resultlen) { /* The GDO file is pretty short, thus we simply reuse it for storing the serial number. */ memmove (result, p, n); app->serialno = result; app->serialnolen = n; err = app_munge_serialno (app); if (err) goto leave; } else xfree (result); result = NULL; } } /* For certain error codes, there is no need to try more. */ if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT || gpg_err_code (err) == GPG_ERR_ENODEV) goto leave; /* Figure out the application to use. */ if (want_undefined) { /* We switch to the "undefined" application only if explicitly requested. */ app->apptype = "UNDEFINED"; err = 0; } else err = gpg_error (GPG_ERR_NOT_FOUND); if (err && is_app_allowed ("openpgp") && (!name || !strcmp (name, "openpgp"))) err = app_select_openpgp (app); if (err && is_app_allowed ("nks") && (!name || !strcmp (name, "nks"))) err = app_select_nks (app); if (err && is_app_allowed ("p15") && (!name || !strcmp (name, "p15"))) err = app_select_p15 (app); if (err && is_app_allowed ("geldkarte") && (!name || !strcmp (name, "geldkarte"))) err = app_select_geldkarte (app); if (err && is_app_allowed ("dinsig") && (!name || !strcmp (name, "dinsig"))) err = app_select_dinsig (app); if (err && name) err = gpg_error (GPG_ERR_NOT_SUPPORTED); leave: if (err) { if (name) log_info ("can't select application '%s': %s\n", name, gpg_strerror (err)); else log_info ("no supported card application found: %s\n", gpg_strerror (err)); xfree (app); unlock_reader (slot); return err; } app->ref_count = 1; lock_table[slot].app = app; *r_app = app; unlock_reader (slot); return 0; }