pkcs11: don't shrink the number of slots

... as required by PKCS#11 2.30, if the application doesn't call
`C_GetSlotList` with `NULL`.

Fixes ghost tokens in Firefox when detaching a reader that contained a
card.

Fixes https://github.com/OpenSC/OpenSC/issues/629
This commit is contained in:
Frank Morgner 2016-09-03 00:46:48 +02:00
parent 7e6dc25c40
commit 24b7507a69
4 changed files with 61 additions and 24 deletions

View File

@ -486,6 +486,9 @@ CK_RV C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo)
goto out;
}
if (slot->p11card == NULL)
return CKR_TOKEN_NOT_PRESENT;
fw_data = (struct pkcs15_fw_data *) slot->p11card->fws_data[slot->fw_data_idx];
if (!fw_data)
return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_GetTokenInfo");

View File

@ -405,16 +405,20 @@ CK_RV C_GetSlotList(CK_BBOOL tokenPresent, /* only slots with token prese
prev_reader = NULL;
numMatches = 0;
for (i=0; i<list_size(&virtual_slots); i++) {
slot = (sc_pkcs11_slot_t *) list_get_at(&virtual_slots, i);
slot = (sc_pkcs11_slot_t *) list_get_at(&virtual_slots, i);
/* the list of available slots contains:
* - if present, virtual hotplug slot;
* - any slot with token;
* - without token(s), one empty slot per reader;
* - any slot that has already been seen;
*/
if ((!tokenPresent && !slot->reader)
if ((!tokenPresent && !slot->reader)
|| (!tokenPresent && slot->reader != prev_reader)
|| (slot->slot_info.flags & CKF_TOKEN_PRESENT))
|| (slot->slot_info.flags & CKF_TOKEN_PRESENT)
|| (slot->flags & SC_PKCS11_SLOT_FLAG_SEEN)) {
found[numMatches++] = slot->id;
slot->flags |= SC_PKCS11_SLOT_FLAG_SEEN;
}
prev_reader = slot->reader;
}

View File

@ -205,6 +205,12 @@ struct sc_pkcs11_card {
unsigned int nmechanisms;
};
/* If the slot did already show with `C_GetSlotList`, then we need to keep this
* slot alive. PKCS#11 2.30 allows allows adding but not removing slots until
* the application calls `C_GetSlotList` with `NULL`. This flag tracks the
* visibility to the application */
#define SC_PKCS11_SLOT_FLAG_SEEN 1
struct sc_pkcs11_slot {
CK_SLOT_ID id; /* ID of the slot */
int login_user; /* Currently logged in user */
@ -221,6 +227,7 @@ struct sc_pkcs11_slot {
int fw_data_idx; /* Index of framework data */
struct sc_app_info *app_info; /* Application assosiated to slot */
list_t logins; /* tracks all calls to C_Login if atomic operations are requested */
int flags;
};
typedef struct sc_pkcs11_slot sc_pkcs11_slot_t;

View File

@ -75,26 +75,39 @@ static int object_list_seeker(const void *el, const void *key)
CK_RV create_slot(sc_reader_t *reader)
{
struct sc_pkcs11_slot *slot;
/* find unused virtual hotplug slots */
struct sc_pkcs11_slot *slot = reader_get_slot(NULL);
if (list_size(&virtual_slots) >= sc_pkcs11_conf.max_virtual_slots)
return CKR_FUNCTION_FAILED;
/* create a new slot if no empty slot is available */
if (!slot) {
if (list_size(&virtual_slots) >= sc_pkcs11_conf.max_virtual_slots)
return CKR_FUNCTION_FAILED;
slot = (struct sc_pkcs11_slot *)calloc(1, sizeof(struct sc_pkcs11_slot));
if (!slot)
return CKR_HOST_MEMORY;
slot = (struct sc_pkcs11_slot *)calloc(1, sizeof(struct sc_pkcs11_slot));
if (!slot)
return CKR_HOST_MEMORY;
list_append(&virtual_slots, slot);
list_init(&slot->objects);
list_attributes_seeker(&slot->objects, object_list_seeker);
list_init(&slot->logins);
} else {
/* reuse the old list of logins/objects since they should be empty */
list_t logins = slot->logins;
list_t objects = slot->objects;
memset(slot, 0, sizeof *slot);
slot->logins = logins;
slot->objects = objects;
}
list_append(&virtual_slots, slot);
slot->login_user = -1;
slot->id = (CK_SLOT_ID) list_locate(&virtual_slots, slot);
sc_log(context, "Creating slot with id 0x%lx", slot->id);
list_init(&slot->objects);
list_attributes_seeker(&slot->objects, object_list_seeker);
list_init(&slot->logins);
init_slot_info(&slot->slot_info);
sc_log(context, "Initializing slot with id 0x%lx", slot->id);
if (reader != NULL) {
slot->reader = reader;
strcpy_bp(slot->slot_info.manufacturerID, reader->vendor, 32);
@ -106,13 +119,21 @@ CK_RV create_slot(sc_reader_t *reader)
return CKR_OK;
}
void delete_slot(struct sc_pkcs11_slot *slot)
void empty_slot(struct sc_pkcs11_slot *slot)
{
if (slot) {
list_destroy(&slot->objects);
list_destroy(&slot->logins);
list_delete(&virtual_slots, slot);
free(slot);
if (slot->flags & SC_PKCS11_SLOT_FLAG_SEEN) {
/* Keep the slot visible to the application. The slot's state has
* already been reset by `slot_token_removed()`, lists have been
* emptied. We replace the reader with a virtual hotplug slot. */
slot->reader = NULL;
init_slot_info(&slot->slot_info);
} else {
list_destroy(&slot->objects);
list_destroy(&slot->logins);
list_delete(&virtual_slots, slot);
free(slot);
}
}
}
@ -343,7 +364,7 @@ card_detect_all(void)
struct sc_pkcs11_slot *slot;
card_removed(reader);
while ((slot = reader_get_slot(reader))) {
delete_slot(slot);
empty_slot(slot);
}
_sc_delete_reader(context, reader);
i--;
@ -444,17 +465,19 @@ CK_RV slot_token_removed(CK_SLOT_ID id)
slot->p11card->framework->release_token(slot->p11card, slot->fw_data);
slot->fw_data = NULL;
}
slot->p11card = NULL;
}
/* Reset relevant slot properties */
slot->slot_info.flags &= ~CKF_TOKEN_PRESENT;
slot->login_user = -1;
pop_all_login_states(slot);
slot->p11card = NULL;
if (token_was_present)
slot->events = SC_EVENT_CARD_REMOVED;
memset(&slot->token_info, 0, sizeof slot->token_info);
return CKR_OK;
}