CK_RV C_WaitForSlotEvent(CK_FLAGS flags, /* blocking/nonblocking flag */ CK_SLOT_ID_PTR pSlot, /* location that receives the slot ID */ CK_VOID_PTR pReserved) /* reserved. Should be NULL_PTR */ { sc_reader_t *found; unsigned int mask, events; void *reader_states = NULL; CK_SLOT_ID slot_id; CK_RV rv; int r; if (pReserved != NULL_PTR) return CKR_ARGUMENTS_BAD; sc_log(context, "C_WaitForSlotEvent(block=%d)", !(flags & CKF_DONT_BLOCK)); /* Not all pcsc-lite versions implement consistently used functions as they are */ /* FIXME: add proper checking into build to check correct pcsc-lite version for SCardStatusChange/SCardCancel */ if (!(flags & CKF_DONT_BLOCK)) return CKR_FUNCTION_NOT_SUPPORTED; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; mask = SC_EVENT_CARD_EVENTS; /* Detect and add new slots for added readers v2.20 */ if (sc_pkcs11_conf.plug_and_play) { mask |= SC_EVENT_READER_EVENTS; } rv = slot_find_changed(&slot_id, mask); if ((rv == CKR_OK) || (flags & CKF_DONT_BLOCK)) goto out; again: sc_log(context, "C_WaitForSlotEvent() reader_states:%p", reader_states); sc_pkcs11_unlock(); r = sc_wait_for_event(context, mask, &found, &events, -1, &reader_states); if (sc_pkcs11_conf.plug_and_play && events & SC_EVENT_READER_ATTACHED) { /* NSS/Firefox Triggers a C_GetSlotList(NULL) only if a slot ID is returned that it does not know yet Change the first hotplug slot id on every call to make this happen. */ sc_pkcs11_slot_t *hotplug_slot = list_get_at(&virtual_slots, 0); *pSlot= hotplug_slot->id -1; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; goto out; } /* Was C_Finalize called ? */ if (in_finalize == 1) return CKR_CRYPTOKI_NOT_INITIALIZED; if ((rv = sc_pkcs11_lock()) != CKR_OK) return rv; if (r != SC_SUCCESS) { sc_log(context, "sc_wait_for_event() returned %d\n", r); rv = sc_to_cryptoki_error(r, "C_WaitForSlotEvent"); goto out; } /* If no changed slot was found (maybe an unsupported card * was inserted/removed) then go waiting again */ rv = slot_find_changed(&slot_id, mask); if (rv != CKR_OK) goto again; out: if (pSlot) *pSlot = slot_id; /* Free allocated readers states holder */ if (reader_states) { sc_log(context, "free reader states"); sc_wait_for_event(context, 0, NULL, NULL, -1, &reader_states); } sc_log(context, "C_WaitForSlotEvent() = %s, event in 0x%lx", lookup_enum (RV_T, rv), *pSlot); sc_pkcs11_unlock(); return rv; }
CK_RV C_FindObjectsInit(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_ATTRIBUTE_PTR pTemplate, /* attribute values to match */ CK_ULONG ulCount) /* attributes in search template */ { CK_BBOOL is_private = TRUE; CK_ATTRIBUTE private_attribute = { CKA_PRIVATE, &is_private, sizeof(is_private) }; int rv, match, hide_private; unsigned int j; struct sc_pkcs11_session *session; struct sc_pkcs11_object *object; struct sc_pkcs11_find_operation *operation; struct sc_pkcs11_pool_item *item; struct sc_pkcs11_slot *slot; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; rv = pool_find(&session_pool, hSession, (void**) &session); if (rv != CKR_OK) goto out; sc_debug(context, "C_FindObjectsInit(slot = %d)\n", session->slot->id); dump_template("C_FindObjectsInit()", pTemplate, ulCount); rv = session_start_operation(session, SC_PKCS11_OPERATION_FIND, &find_mechanism, (struct sc_pkcs11_operation**) &operation); if (rv != CKR_OK) goto out; operation->current_handle = 0; operation->num_handles = 0; slot = session->slot; /* Check whether we should hide private objects */ hide_private = 0; if (slot->login_user != CKU_USER && (slot->token_info.flags & CKF_LOGIN_REQUIRED)) hide_private = 1; /* For each object in token do */ for (item = slot->object_pool.head; item != NULL; item = item->next) { object = (struct sc_pkcs11_object*) item->item; /* User not logged in and private object? */ if (hide_private) { if (object->ops->get_attribute(session, object, &private_attribute) != CKR_OK) continue; if (is_private) { sc_debug(context, "Object %d/%d: Private object and not logged in.\n", slot->id, item->handle); continue; } } /* Try to match every attribute */ match = 1; for (j = 0; j < ulCount; j++) { rv = object->ops->cmp_attribute(session, object, &pTemplate[j]); if (rv == 0) { if (context->debug >= 4) { sc_debug(context, "Object %d/%d: Attribute 0x%x does NOT match.\n", slot->id, item->handle, pTemplate[j].type); } match = 0; break; } if (context->debug >= 4) { sc_debug(context, "Object %d/%d: Attribute 0x%x matches.\n", slot->id, item->handle, pTemplate[j].type); } } if (match) { sc_debug(context, "Object %d/%d matches\n", slot->id, item->handle); /* Avoid buffer overflow --okir */ if (operation->num_handles >= SC_PKCS11_FIND_MAX_HANDLES) { sc_debug(context, "Too many matching objects\n"); break; } operation->handles[operation->num_handles++] = item->handle; } } rv = CKR_OK; sc_debug(context, "%d matching objects\n", operation->num_handles); out: sc_pkcs11_unlock(); return rv; }
CK_RV C_GetSlotList(CK_BBOOL tokenPresent, /* only slots with token present */ CK_SLOT_ID_PTR pSlotList, /* receives the array of slot IDs */ CK_ULONG_PTR pulCount) /* receives the number of slots */ { CK_SLOT_ID_PTR found = NULL; unsigned int i; CK_ULONG numMatches; sc_pkcs11_slot_t *slot; sc_reader_t *prev_reader = NULL; CK_RV rv; if (pulCount == NULL_PTR) return CKR_ARGUMENTS_BAD; if ((rv = sc_pkcs11_lock()) != CKR_OK) { return rv; } sc_log(context, "C_GetSlotList(token=%d, %s)", tokenPresent, (pSlotList==NULL_PTR && sc_pkcs11_conf.plug_and_play)? "plug-n-play":"refresh"); /* Slot list can only change in v2.20 */ if (pSlotList == NULL_PTR && sc_pkcs11_conf.plug_and_play) { /* Trick NSS into updating the slot list by changing the hotplug slot ID */ sc_pkcs11_slot_t *hotplug_slot = list_get_at(&virtual_slots, 0); hotplug_slot->id--; sc_ctx_detect_readers(context); } card_detect_all(); found = calloc(list_size(&virtual_slots), sizeof(CK_SLOT_ID)); if (found == NULL) { rv = CKR_HOST_MEMORY; goto out; } 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); /* the list of available slots contains: * - if present, virtual hotplug slot; * - any slot with token; * - without token(s), one empty slot per reader; */ if ((!tokenPresent && !slot->reader) || (!tokenPresent && slot->reader != prev_reader) || (slot->slot_info.flags & CKF_TOKEN_PRESENT)) found[numMatches++] = slot->id; prev_reader = slot->reader; } if (pSlotList == NULL_PTR) { sc_log(context, "was only a size inquiry (%d)\n", numMatches); *pulCount = numMatches; rv = CKR_OK; goto out; } if (*pulCount < numMatches) { sc_log(context, "buffer was too small (needed %d)\n", numMatches); *pulCount = numMatches; rv = CKR_BUFFER_TOO_SMALL; goto out; } memcpy(pSlotList, found, numMatches * sizeof(CK_SLOT_ID)); *pulCount = numMatches; rv = CKR_OK; sc_log(context, "returned %d slots\n", numMatches); out: if (found != NULL) { free (found); found = NULL; } sc_pkcs11_unlock(); return rv; }
CK_RV C_GetAttributeValue(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_OBJECT_HANDLE hObject, /* the object's handle */ CK_ATTRIBUTE_PTR pTemplate, /* specifies attributes, gets values */ CK_ULONG ulCount) /* attributes in template */ { static int precedence[] = { CKR_OK, CKR_BUFFER_TOO_SMALL, CKR_ATTRIBUTE_TYPE_INVALID, CKR_ATTRIBUTE_SENSITIVE, -1 }; char object_name[64]; int j, rv; struct sc_pkcs11_session *session; struct sc_pkcs11_object *object; int res, res_type; unsigned int i; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; rv = pool_find(&session_pool, hSession, (void**) &session); if (rv != CKR_OK) goto out; rv = pool_find(&session->slot->object_pool, hObject, (void**) &object); if (rv != CKR_OK) goto out; /* Debug printf */ snprintf(object_name, sizeof(object_name), "Object %lu", (unsigned long) hObject); res_type = 0; for (i = 0; i < ulCount; i++) { res = object->ops->get_attribute(session, object, &pTemplate[i]); if (res != CKR_OK) pTemplate[i].ulValueLen = (CK_ULONG) -1; dump_template(object_name, &pTemplate[i], 1); /* the pkcs11 spec has complicated rules on * what errors take precedence: * CKR_ATTRIBUTE_SENSITIVE * CKR_ATTRIBUTE_INVALID * CKR_BUFFER_TOO_SMALL * It does not exactly specify how other errors * should be handled - we give them highest * precedence */ for (j = 0; precedence[j] != -1; j++) { if (precedence[j] == res) break; } if (j > res_type) { res_type = j; rv = res; } } out: sc_pkcs11_unlock(); return rv; }
CK_RV C_WaitForSlotEvent(CK_FLAGS flags, /* blocking/nonblocking flag */ CK_SLOT_ID_PTR pSlot, /* location that receives the slot ID */ CK_VOID_PTR pReserved) /* reserved. Should be NULL_PTR */ { sc_reader_t *found; unsigned int mask, events; void *reader_states = NULL; CK_SLOT_ID slot_id; CK_RV rv; int r; if (pReserved != NULL_PTR) return CKR_ARGUMENTS_BAD; sc_log(context, "C_WaitForSlotEvent(block=%d)", !(flags & CKF_DONT_BLOCK)); #ifndef PCSCLITE_GOOD /* Not all pcsc-lite versions implement consistently used functions as they are */ if (!(flags & CKF_DONT_BLOCK)) return CKR_FUNCTION_NOT_SUPPORTED; #endif /* PCSCLITE_GOOD */ rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; mask = SC_EVENT_CARD_EVENTS | SC_EVENT_READER_EVENTS; /* Detect and add new slots for added readers v2.20 */ rv = slot_find_changed(&slot_id, mask); if ((rv == CKR_OK) || (flags & CKF_DONT_BLOCK)) goto out; again: sc_log(context, "C_WaitForSlotEvent() reader_states:%p", reader_states); sc_pkcs11_unlock(); r = sc_wait_for_event(context, mask, &found, &events, -1, &reader_states); if (events & SC_EVENT_READER_ATTACHED) { rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; goto out; } /* Was C_Finalize called ? */ if (in_finalize == 1) return CKR_CRYPTOKI_NOT_INITIALIZED; if ((rv = sc_pkcs11_lock()) != CKR_OK) return rv; if (r != SC_SUCCESS) { sc_log(context, "sc_wait_for_event() returned %d\n", r); rv = sc_to_cryptoki_error(r, "C_WaitForSlotEvent"); goto out; } /* If no changed slot was found (maybe an unsupported card * was inserted/removed) then go waiting again */ rv = slot_find_changed(&slot_id, mask); if (rv != CKR_OK) goto again; out: if (pSlot) *pSlot = slot_id; /* Free allocated readers states holder */ if (reader_states) { sc_log(context, "free reader states"); sc_wait_for_event(context, 0, NULL, NULL, -1, &reader_states); } sc_log(context, "C_WaitForSlotEvent() = %s", lookup_enum (RV_T, rv)); sc_pkcs11_unlock(); return rv; }
CK_RV C_FindObjectsInit(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_ATTRIBUTE_PTR pTemplate, /* attribute values to match */ CK_ULONG ulCount) /* attributes in search template */ { CK_RV rv; CK_BBOOL is_private = TRUE; CK_ATTRIBUTE private_attribute = { CKA_PRIVATE, &is_private, sizeof(is_private) }; int match, hide_private; unsigned int i, j; struct sc_pkcs11_session *session; struct sc_pkcs11_object *object; struct sc_pkcs11_find_operation *operation; struct sc_pkcs11_slot *slot; if (pTemplate == NULL_PTR && ulCount > 0) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; rv = get_session(hSession, &session); if (rv != CKR_OK) goto out; sc_log(context, "C_FindObjectsInit(slot = %d)\n", session->slot->id); dump_template(SC_LOG_DEBUG_NORMAL, "C_FindObjectsInit()", pTemplate, ulCount); rv = session_start_operation(session, SC_PKCS11_OPERATION_FIND, &find_mechanism, (struct sc_pkcs11_operation **)&operation); if (rv != CKR_OK) goto out; operation->current_handle = 0; operation->num_handles = 0; operation->allocated_handles = 0; operation->handles = NULL; slot = session->slot; /* Check whether we should hide private objects */ hide_private = 0; if (slot->login_user != CKU_USER && (slot->token_info.flags & CKF_LOGIN_REQUIRED)) hide_private = 1; /* For each object in token do */ for (i=0; i<list_size(&slot->objects); i++) { object = (struct sc_pkcs11_object *)list_get_at(&slot->objects, i); sc_log(context, "Object with handle 0x%lx", object->handle); /* User not logged in and private object? */ if (hide_private) { if (object->ops->get_attribute(session, object, &private_attribute) != CKR_OK) continue; if (is_private) { sc_log(context, "Object %d/%d: Private object and not logged in.", slot->id, object->handle); continue; } } /* Try to match every attribute */ match = 1; for (j = 0; j < ulCount; j++) { rv = object->ops->cmp_attribute(session, object, &pTemplate[j]); if (rv == 0) { sc_log(context, "Object %d/%d: Attribute 0x%x does NOT match.", slot->id, object->handle, pTemplate[j].type); match = 0; break; } if (context->debug >= 4) { sc_log(context, "Object %d/%d: Attribute 0x%x matches.", slot->id, object->handle, pTemplate[j].type); } } if (match) { sc_log(context, "Object %d/%d matches\n", slot->id, object->handle); /* Realloc handles - remove restriction on only 32 matching objects -dee */ if (operation->num_handles >= operation->allocated_handles) { operation->allocated_handles += SC_PKCS11_FIND_INC_HANDLES; sc_log(context, "realloc for %d handles", operation->allocated_handles); operation->handles = realloc(operation->handles, sizeof(CK_OBJECT_HANDLE) * operation->allocated_handles); if (operation->handles == NULL) { rv = CKR_HOST_MEMORY; goto out; } } operation->handles[operation->num_handles++] = object->handle; } } rv = CKR_OK; sc_log(context, "%d matching objects\n", operation->num_handles); out: sc_pkcs11_unlock(); return rv; }
CK_RV C_GetAttributeValue(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_OBJECT_HANDLE hObject, /* the object's handle */ CK_ATTRIBUTE_PTR pTemplate, /* specifies attributes, gets values */ CK_ULONG ulCount) /* attributes in template */ { static int precedence[] = { CKR_OK, CKR_BUFFER_TOO_SMALL, CKR_ATTRIBUTE_TYPE_INVALID, CKR_ATTRIBUTE_SENSITIVE, -1 }; char object_name[64]; int j; CK_RV rv; struct sc_pkcs11_session *session; struct sc_pkcs11_object *object; int res, res_type; unsigned int i; if (pTemplate == NULL_PTR || ulCount == 0) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; rv = get_object_from_session(hSession, hObject, &session, &object); if (rv != CKR_OK) goto out; /* Debug printf */ snprintf(object_name, sizeof(object_name), "Object %lu", (unsigned long)hObject); res_type = 0; for (i = 0; i < ulCount; i++) { res = object->ops->get_attribute(session, object, &pTemplate[i]); if (res != CKR_OK) pTemplate[i].ulValueLen = (CK_ULONG) - 1; dump_template(SC_LOG_DEBUG_NORMAL, object_name, &pTemplate[i], 1); /* the pkcs11 spec has complicated rules on * what errors take precedence: * CKR_ATTRIBUTE_SENSITIVE * CKR_ATTRIBUTE_INVALID * CKR_BUFFER_TOO_SMALL * It does not exactly specify how other errors * should be handled - we give them highest * precedence */ for (j = 0; precedence[j] != -1; j++) { if (precedence[j] == res) break; } if (j > res_type) { res_type = j; rv = res; } } out: sc_log(context, "C_GetAttributeValue(hSession=0x%lx, hObject=0x%lx) = %s", hSession, hObject, lookup_enum ( RV_T, rv )); sc_pkcs11_unlock(); return rv; }
CK_RV C_DeriveKey(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_MECHANISM_PTR pMechanism, /* the key derivation mechanism */ CK_OBJECT_HANDLE hBaseKey, /* handle of the base key */ CK_ATTRIBUTE_PTR pTemplate, /* template for the new key */ CK_ULONG ulAttributeCount, /* # of attributes in template */ CK_OBJECT_HANDLE_PTR phKey) /* gets handle of derived key */ { /* TODO: -DEE ECDH with Cofactor on PIV is an example */ /* TODO: need to do a lot of checking, will only support ECDH for now.*/ CK_RV rv; CK_BBOOL can_derive; CK_KEY_TYPE key_type; CK_ATTRIBUTE derive_attribute = { CKA_DERIVE, &can_derive, sizeof(can_derive) }; CK_ATTRIBUTE key_type_attr = { CKA_KEY_TYPE, &key_type, sizeof(key_type) }; struct sc_pkcs11_session *session; struct sc_pkcs11_object *object; struct sc_pkcs11_object *key_object; if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; rv = get_object_from_session(hSession, hBaseKey, &session, &object); if (rv != CKR_OK) { if (rv == CKR_OBJECT_HANDLE_INVALID) rv = CKR_KEY_HANDLE_INVALID; goto out; } if (object->ops->derive == NULL_PTR) { rv = CKR_KEY_TYPE_INCONSISTENT; goto out; } rv = object->ops->get_attribute(session, object, &derive_attribute); if (rv != CKR_OK || !can_derive) { rv = CKR_KEY_TYPE_INCONSISTENT; goto out; } rv = object->ops->get_attribute(session, object, &key_type_attr); if (rv != CKR_OK) { rv = CKR_KEY_TYPE_INCONSISTENT; goto out; } /* TODO DEE Should also check SENSITIVE, ALWAYS_SENSITIVE, EXTRACTABLE, NEVER_EXTRACTABLE of the BaseKey against the template for the newkey. */ switch(key_type) { case CKK_EC: rv = sc_create_object_int(hSession, pTemplate, ulAttributeCount, phKey, 0); if (rv != CKR_OK) goto out; rv = get_object_from_session(hSession, *phKey, &session, &key_object); if (rv != CKR_OK) { if (rv == CKR_OBJECT_HANDLE_INVALID) rv = CKR_KEY_HANDLE_INVALID; goto out; } rv = sc_pkcs11_deri(session, pMechanism, object, key_type, hSession, *phKey, key_object); /* TODO if (rv != CK_OK) need to destroy the object */ break; default: rv = CKR_KEY_TYPE_INCONSISTENT; } out: sc_pkcs11_unlock(); return rv; }