/* * slot.c: Smartcard and slot related management functions * * Copyright (C) 2002 Timo Teräs * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "sc-pkcs11.h" static struct sc_pkcs11_framework_ops *frameworks[] = { &framework_pkcs15, #ifdef USE_PKCS15_INIT /* This should be the last framework, because it * will assume the card is blank and try to initialize it */ &framework_pkcs15init, #endif NULL }; unsigned int first_free_slot = 0; static void init_slot_info(CK_SLOT_INFO_PTR pInfo) { strcpy_bp(pInfo->slotDescription, "Virtual slot", 64); strcpy_bp(pInfo->manufacturerID, "OpenSC project (www.opensc.org)", 32); pInfo->flags = CKF_REMOVABLE_DEVICE | CKF_HW_SLOT; pInfo->hardwareVersion.major = 0; pInfo->hardwareVersion.minor = 0; pInfo->firmwareVersion.major = 0; pInfo->firmwareVersion.minor = 0; } CK_RV card_initialize(int reader) { struct sc_pkcs11_card *card = card_table + reader; unsigned int avail; if (reader < 0 || reader >= SC_PKCS11_MAX_READERS) return CKR_FUNCTION_FAILED; memset(card, 0, sizeof(struct sc_pkcs11_card)); card->reader = reader; /* Always allocate a fixed slot range to one reader/card. * Some applications get confused if readers pop up in * different slots. */ if (sc_pkcs11_conf.num_slots == 0) avail = SC_PKCS11_DEF_SLOTS_PER_CARD; else avail = sc_pkcs11_conf.num_slots; if (first_free_slot + avail > SC_PKCS11_MAX_VIRTUAL_SLOTS) avail = SC_PKCS11_MAX_VIRTUAL_SLOTS - first_free_slot; card->first_slot = first_free_slot; card->max_slots = avail; card->num_slots = 0; first_free_slot += card->max_slots; return CKR_OK; } CK_RV card_detect(int reader) { struct sc_pkcs11_card *card = &card_table[reader]; int rc, rv, i, retry = 1; rv = CKR_OK; sc_debug(context, "%d: Detecting SmartCard\n", reader); for (i = card->max_slots; i--; ) { struct sc_pkcs11_slot *slot; slot = virtual_slots + card->first_slot + i; strcpy_bp(slot->slot_info.slotDescription, context->reader[reader]->name, 64); slot->reader = reader; } /* Check if someone inserted a card */ again: rc = sc_detect_card_presence(context->reader[reader], 0); if (rc < 0) { sc_debug(context, "Card detection failed for reader %d: %s\n", reader, sc_strerror(rc)); return sc_to_cryptoki_error(rc, reader); } if (rc == 0) { sc_debug(context, "%d: Card absent\n", reader); card_removed(reader); /* Release all resources */ return CKR_TOKEN_NOT_PRESENT; } /* If the card was changed, disconnect the current one */ if (rc & SC_SLOT_CARD_CHANGED) { sc_debug(context, "%d: Card changed\n", reader); /* The following should never happen - but if it * does we'll be stuck in an endless loop. * So better be fussy. */ if (!retry--) return CKR_TOKEN_NOT_PRESENT; card_removed(reader); goto again; } /* Detect the card if it's not known already */ if (card->card == NULL) { sc_debug(context, "%d: Connecting to SmartCard\n", reader); rc = sc_connect_card(context->reader[reader], 0, &card->card); if (rc != SC_SUCCESS) return sc_to_cryptoki_error(rc, reader); } /* Detect the framework */ if (card->framework == NULL) { sc_debug(context, "%d: Detecting Framework\n", reader); for (i = 0; frameworks[i]; i++) { if (frameworks[i]->bind == NULL) continue; rv = frameworks[i]->bind(card); if (rv == CKR_OK) break; } if (frameworks[i] == NULL) return CKR_TOKEN_NOT_RECOGNIZED; /* Initialize framework */ sc_debug(context, "%d: Detected framework %d. Creating tokens.\n", reader, i); rv = frameworks[i]->create_tokens(card); if (rv != CKR_OK) return rv; card->framework = frameworks[i]; } sc_debug(context, "%d: Detection ended\n", reader); return rv; } CK_RV __card_detect_all(int report_events) { int i; if (context == NULL_PTR) return CKR_CRYPTOKI_NOT_INITIALIZED; for (i = 0; i < context->reader_count; i++) card_detect(i); if (!report_events) { CK_SLOT_ID id; for (id = 0; id < SC_PKCS11_MAX_VIRTUAL_SLOTS; id++) virtual_slots[id].events = 0; } return CKR_OK; } CK_RV card_detect_all(void) { return __card_detect_all(1); } CK_RV card_removed(int reader) { int i; struct sc_pkcs11_card *card; sc_debug(context, "%d: SmartCard removed\n", reader); for (i=0; ireader == reader) slot_token_removed(i); } /* beware - do not clean the entire sc_pkcs11_card struct; * fields such as first_slot and max_slots are initialized * _once_ and need to be left untouched across card removal/ * insertion */ card = &card_table[reader]; if (card->framework) card->framework->unbind(card); card->framework = NULL; card->fw_data = NULL; if (card->card) sc_disconnect_card(card->card, 0); card->card = NULL; return CKR_OK; } CK_RV slot_initialize(int id, struct sc_pkcs11_slot *slot) { memset(slot, 0, sizeof(*slot)); slot->id = id; slot->login_user = -1; init_slot_info(&slot->slot_info); pool_initialize(&slot->object_pool, POOL_TYPE_OBJECT); return CKR_OK; } CK_RV slot_allocate(struct sc_pkcs11_slot **slot, struct sc_pkcs11_card *card) { unsigned int i, first, last; if (card->num_slots >= card->max_slots) return CKR_FUNCTION_FAILED; first = card->first_slot; last = first + card->max_slots; for (i = first; i < last; i++) { if (!virtual_slots[i].card) { sc_debug(context, "Allocated slot %d\n", i); virtual_slots[i].card = card; virtual_slots[i].events = SC_EVENT_CARD_INSERTED; *slot = &virtual_slots[i]; card->num_slots++; return CKR_OK; } } return CKR_FUNCTION_FAILED; } CK_RV slot_get_slot(int id, struct sc_pkcs11_slot **slot) { if (context == NULL) return CKR_CRYPTOKI_NOT_INITIALIZED; if (id < 0 || id >= SC_PKCS11_MAX_VIRTUAL_SLOTS) return CKR_SLOT_ID_INVALID; *slot = &virtual_slots[id]; return CKR_OK; } CK_RV slot_get_token(int id, struct sc_pkcs11_slot **slot) { int rv; rv = slot_get_slot(id, slot); if (rv != CKR_OK) return rv; if (!((*slot)->slot_info.flags & CKF_TOKEN_PRESENT)) return CKR_TOKEN_NOT_PRESENT; return CKR_OK; } CK_RV slot_token_removed(int id) { int rv, token_was_present; struct sc_pkcs11_slot *slot; struct sc_pkcs11_object *object; CK_SLOT_INFO saved_slot_info; int reader; rv = slot_get_slot(id, &slot); if (rv != CKR_OK) return rv; token_was_present = (slot->slot_info.flags & CKF_TOKEN_PRESENT); /* Terminate active sessions */ sc_pkcs11_close_all_sessions(id); /* Object pool */ while (pool_find_and_delete(&slot->object_pool, 0, (void**) &object) == CKR_OK) { if (object->ops->release) object->ops->release(object); } /* Release framework stuff */ if (slot->card != NULL) { if (slot->fw_data != NULL && slot->card->framework != NULL && slot->card->framework->release_token != NULL) slot->card->framework->release_token(slot->card, slot->fw_data); slot->card->num_slots--; } /* Zap everything else. Restore the slot_info afterwards (it contains the reader * name, for instance) but clear its flags */ saved_slot_info = slot->slot_info; reader = slot->reader; memset(slot, 0, sizeof(*slot)); slot->slot_info = saved_slot_info; slot->slot_info.flags = 0; slot->login_user = -1; slot->reader = reader; pool_initialize(&slot->object_pool, POOL_TYPE_OBJECT); if (token_was_present) slot->events = SC_EVENT_CARD_REMOVED; return CKR_OK; } CK_RV slot_find_changed(CK_SLOT_ID_PTR idp, int mask) { sc_pkcs11_slot_t *slot; CK_SLOT_ID id; card_detect_all(); for (id = 0; id < SC_PKCS11_MAX_VIRTUAL_SLOTS; id++) { slot = &virtual_slots[id]; if ((slot->events & SC_EVENT_CARD_INSERTED) && !(slot->slot_info.flags & CKF_TOKEN_PRESENT)) slot->events &= ~SC_EVENT_CARD_INSERTED; if (slot->events & mask) { slot->events &= ~mask; *idp = id; return CKR_OK; } } return CKR_NO_EVENT; }