diff --git a/src/p11-sign.c b/src/p11-sign.c index 870f08c..4f2ae7d 100644 --- a/src/p11-sign.c +++ b/src/p11-sign.c @@ -1,72 +1,69 @@ /* p11-sign.c - Cryptoki implementation. * Copyright (C) 2006 g10 Code GmbH * * This file is part of Scute. * * Scute 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. * * Scute 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 program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include "cryptoki.h" #include "locking.h" #include "slots.h" /* Sign the data (PDATA,ULDATALEN) using the information recorded in * the HSESSION by C_SignInit. PSIGNAURE is a buffer to receive the * signature. The length of that buffer must be stored in a variable * to which PULSIGNATURELEN points to; on success that length is * updated to the actual length of the signature in PULSIGNATURE. * * If the function returns CKR_BUFFER_TOO_SMALL no further C_SignInit * is required, instead the function can be called again with a larger * buffer. On a successful operation CKR_OK is returned and other - * signatures may be created without an new C_SignInit. On all other + * signatures may be created without a new C_SignInit. On all other * return codes a new C_SignInit is required. + * + * In contrast to the specs the return code CKR_ARGUMENTS_BAD may not + * require a new C_SignInit because this can be considered a bug in + * the caller's code. */ CK_RV CK_SPEC C_Sign (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { CK_RV err = CKR_OK; slot_iterator_t slot; - session_iterator_t session; + session_iterator_t sid; if (pData == NULL_PTR || pulSignatureLen == NULL_PTR) return CKR_ARGUMENTS_BAD; err = scute_global_lock (); if (err) return err; - err = slots_lookup_session (hSession, &slot, &session); - if (err) - goto out; - - /* FIXME: Check that C_SignInit has been called. */ - - err = session_sign (slot, session, pData, ulDataLen, - pSignature, pulSignatureLen); + err = slots_lookup_session (hSession, &slot, &sid); + if (!err) + err = session_sign (slot, sid, pData, ulDataLen, + pSignature, pulSignatureLen); - out: - /* FIXME: Update the flag which indicates whether C_SignInit has - * been called. */ scute_global_unlock (); return err; } diff --git a/src/p11-signinit.c b/src/p11-signinit.c index 2c54502..a529262 100644 --- a/src/p11-signinit.c +++ b/src/p11-signinit.c @@ -1,62 +1,59 @@ /* p11-signinit.c - Cryptoki implementation. * Copyright (C) 2006 g10 Code GmbH * * This file is part of Scute. * * Scute 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. * * Scute 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 program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include "cryptoki.h" #include "locking.h" #include "slots.h" /* Prepare a signature operation. HSESSION is the session's handle. * PMECHANISM describes the mechanism to be used. HKEY describes the * key to be used. After calling this function either C_Sign or * (C_SignUpdate, C_SignFinal) can be used to actually sign the data. * The preparation is valid until C_Sign or C_SignFinal. */ CK_RV CK_SPEC C_SignInit (CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { CK_RV err = CKR_OK; slot_iterator_t slot; - session_iterator_t session; + session_iterator_t sid; if (pMechanism == NULL_PTR || pMechanism->mechanism != CKM_RSA_PKCS) return CKR_ARGUMENTS_BAD; if (hKey == CK_INVALID_HANDLE) return CKR_ARGUMENTS_BAD; err = scute_global_lock (); if (err) return err; - err = slots_lookup_session (hSession, &slot, &session); - if (err) - goto out; - - err = session_set_signing_key (slot, session, hKey); + err = slots_lookup_session (hSession, &slot, &sid); + if (!err) + err = session_set_signing_key (slot, sid, hKey); - out: scute_global_unlock (); return err; } diff --git a/src/slots.c b/src/slots.c index c97bbb8..ca6a015 100644 --- a/src/slots.c +++ b/src/slots.c @@ -1,1091 +1,1107 @@ /* slots.c - Slot management. * Copyright (C) 2006 g10 Code GmbH * * This file is part of Scute. * * Scute 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. * * Scute 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 program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include "cryptoki.h" #include "table.h" #include "error-mapping.h" #include "slots.h" #include "agent.h" #include "support.h" #include "gpgsm.h" #include "debug.h" /* A session is just a slot identifier with a per-slot session identifier. */ /* Must be power of two. */ #define SLOT_MAX (1 << 15) #define SESSION_SLOT_MASK (SLOT_MAX - 1) #define SESSION_SLOT_SHIFT 16 #define SESSION_MAX (1 << SESSION_SLOT_SHIFT) #define SESSION_ID_MASK (SESSION_MAX - 1) /* Get slot ID from session. */ #define SESSION_SLOT(session) \ ((session >> SESSION_SLOT_SHIFT) & SESSION_SLOT_MASK) /* Get session ID from session. */ #define SESSION_ID(session) (session & SESSION_ID_MASK) /* Because the slot is already 1-based, we can make the session 0-based. */ #define SESSION_BUILD_ID(slot, session) \ (((slot & SESSION_SLOT_MASK) << SESSION_SLOT_SHIFT) \ | (session & SESSION_ID_MASK)) /* We use one-based IDs. */ #define OBJECT_ID_TO_IDX(id) (id - 1) #define OBJECT_IDX_TO_ID(idx) (idx + 1) struct object { CK_ATTRIBUTE_PTR attributes; CK_ULONG attributes_count; }; /* A mechanism. */ struct mechanism { CK_MECHANISM_TYPE type; CK_MECHANISM_INFO info; }; /* We use one-based IDs. */ #define MECHANISM_ID_TO_IDX(id) (id - 1) #define MECHANISM_IDX_TO_ID(idx) (idx + 1) /* The session state. */ struct session { /* True iff read-write session. */ bool rw; /* The list of objects for the current search. */ object_iterator_t *search_result; /* The length of the list of objects for the current search. */ int search_result_len; /* The signing key. */ CK_OBJECT_HANDLE signing_key; }; /* The slot status. */ typedef enum { SLOT_STATUS_USED = 0, SLOT_STATUS_DEAD = 1 } slot_status_t; struct slot { /* The slot status. Starts out as 0 (pristine). */ slot_status_t status; /* The slot login status. Starts out as 0 (public). */ slot_login_t login; /* True iff a token is present. */ bool token_present; /* The supported mechanisms. */ scute_table_t mechanisms; /* The sessions. */ scute_table_t sessions; /* The objects on the token. */ scute_table_t objects; /* The info about the current token. */ struct agent_card_info_s info; }; /* The slot table. */ static scute_table_t slots; /* Deallocator for mechanisms. */ static void mechanism_dealloc (void *data) { free (data); } /* Allocator for mechanisms. The hook must be a pointer to a CK_FLAGS that should be a combination of CKF_SIGN and/or CKF_DECRYPT. */ static gpg_error_t mechanism_alloc (void **data_r, void *hook) { struct mechanism *mechanism; CK_FLAGS *flags = hook; mechanism = calloc (1, sizeof (*mechanism)); if (mechanism == NULL) return gpg_error_from_syserror (); /* Set some default values. */ mechanism->type = CKM_RSA_PKCS; mechanism->info.ulMinKeySize = 1024; mechanism->info.ulMaxKeySize = 4096; mechanism->info.flags = CKF_HW | (*flags); *data_r = mechanism; return 0; } static void object_dealloc (void *data) { struct object *obj = data; while (0 < obj->attributes_count--) free (obj->attributes[obj->attributes_count].pValue); free (obj->attributes); free (obj); } /* Allocator for objects. The hook is currently unused. */ static gpg_error_t object_alloc (void **data_r, void *hook) { struct object *object; (void) hook; object = calloc (1, sizeof (*object)); if (object == NULL) return gpg_error_from_syserror (); *data_r = object; return 0; } static void session_dealloc (void *data) { struct session *session = data; if (session->search_result) free (session->search_result); free (session); } /* Allocator for sessions. The hook is currently unused. */ static gpg_error_t session_alloc (void **data_r, void *hook) { struct session *session; (void) hook; session = calloc (1, sizeof (*session)); if (session == NULL) return gpg_error_from_syserror (); *data_r = session; return 0; } /* Deallocator for slots. */ static void slot_dealloc (void *data) { struct slot *slot = data; scute_table_destroy (slot->sessions); scute_table_destroy (slot->mechanisms); scute_table_destroy (slot->objects); free (slot); } /* Allocator for slots. The hook does not indicate anything at this point. */ static gpg_error_t slot_alloc (void **data_r, void *hook) { gpg_error_t err; struct slot *slot; int idx; CK_FLAGS flags; (void) hook; slot = calloc (1, sizeof (*slot)); if (slot == NULL) return gpg_error_from_syserror (); err = scute_table_create (&slot->mechanisms, mechanism_alloc, mechanism_dealloc); if (err) goto slot_alloc_out; /* Register the signing mechanism. */ flags = CKF_SIGN; err = scute_table_alloc (slot->mechanisms, &idx, NULL, &flags); if (err) goto slot_alloc_out; err = scute_table_create (&slot->sessions, session_alloc, session_dealloc); if (err) goto slot_alloc_out; err = scute_table_create (&slot->objects, object_alloc, object_dealloc); if (err) goto slot_alloc_out; slot->status = SLOT_STATUS_USED; slot->token_present = false; slot->login = SLOT_LOGIN_PUBLIC; *data_r = slot; slot_alloc_out: if (err) slot_dealloc (slot); return err; } /* Initialize the slot list. */ CK_RV scute_slots_initialize (void) { gpg_error_t err; int slot_idx; err = scute_table_create (&slots, slot_alloc, slot_dealloc); if (err) return err; /* Allocate a new slot for authentication. */ err = scute_table_alloc (slots, &slot_idx, NULL, NULL); if (err) scute_slots_finalize (); /* FIXME: Allocate a new slot for signing and decryption of email. */ return scute_gpg_err_to_ck (err); } void scute_slots_finalize (void) { if (slots == NULL) return; /* This recursively releases all slots and any objects associated with them. */ scute_table_destroy (slots); slots = NULL; } /* Reset the slot SLOT after the token has been removed. */ static void slot_reset (slot_iterator_t id) { struct slot *slot = scute_table_data (slots, id); int oid; /* This also resets the login state. */ slot_close_all_sessions (id); oid = scute_table_first (slot->objects); while (!scute_table_last (slot->objects, oid)) scute_table_dealloc (slot->objects, &oid); assert (scute_table_used (slot->objects) == 0); scute_agent_release_card_info (&slot->info); slot->token_present = false; } static gpg_error_t add_object (void *hook, CK_ATTRIBUTE_PTR attrp, CK_ULONG attr_countp) { gpg_error_t err; struct slot *slot = hook; struct object *object; unsigned int oidx; void *objp; err = scute_table_alloc (slot->objects, &oidx, &objp, NULL); if (err) return err; object = objp; object->attributes = attrp; object->attributes_count = attr_countp; return 0; } /* Initialize the slot after a token has been inserted. SLOT->info must already be valid. */ static gpg_error_t slot_init (slot_iterator_t id) { gpg_error_t err = 0; struct slot *slot = scute_table_data (slots, id); key_info_t ki; for (ki = slot->info.kinfo; ki; ki = ki->next) { err = scute_gpgsm_get_cert (ki->grip, ki->keyref, add_object, slot); if (err) goto leave; } /* FIXME: Perform the rest of the initialization of the token. */ slot->token_present = true; leave: if (err) slot_reset (id); return err; } /* Update the slot SLOT. */ CK_RV slots_update_slot (slot_iterator_t id) { struct slot *slot = scute_table_data (slots, id); gpg_error_t err; if (slot->token_present) { err = scute_agent_check_status (); if (gpg_err_code (err) == GPG_ERR_CARD_REMOVED) slot_reset (id); else if (err) return scute_gpg_err_to_ck (err); else return 0; } /* At this point, the card was or is removed, and we need to reopen the session, if possible. */ err = scute_agent_learn (&slot->info); /* First check if this is really a PIV or an OpenPGP card. FIXME: * Should probably report the error in a better way and use a * generic way to identify cards without resorting to special-casing * PIV cards. */ if (!err && slot->info.is_piv) ; /* Okay, this is a PIV card. */ else if (!err && (!slot->info.serialno || strncmp (slot->info.serialno, "D27600012401", 12) || strlen (slot->info.serialno) != 32)) { DEBUG (DBG_INFO, "token not an OpenPGP card: %s", slot->info.serialno); err = gpg_error (GPG_ERR_CARD_NOT_PRESENT); scute_agent_release_card_info (&slot->info); } /* We also ignore card errors, because unusable cards should not affect slots, and firefox is quite unhappy about returning errors here. */ if (gpg_err_code (err) == GPG_ERR_CARD_REMOVED || gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT || gpg_err_code (err) == GPG_ERR_CARD || gpg_err_code (err) == GPG_ERR_ENODEV) /* Nothing to do. */ err = 0; else if (err == 0) err = slot_init (id); return scute_sys_to_ck (err); } /* Update the slot list by finding new devices. Please note that Mozilla NSS currently assumes that the slot list never shrinks (see TODO file for a discussion). This is the only function allowed to manipulate the slot list. */ CK_RV slots_update (void) { slot_iterator_t id = scute_table_first (slots); while (!scute_table_last (slots, id)) { CK_RV err; err = slots_update_slot (id); if (err) return err; id = scute_table_next (slots, id); } return CKR_OK; } /* Begin iterating over the list of slots. */ CK_RV slots_iterate_first (slot_iterator_t *slot) { *slot = scute_table_first (slots); return CKR_OK; } /* Continue iterating over the list of slots. */ CK_RV slots_iterate_next (slot_iterator_t *slot) { *slot = scute_table_next (slots, *slot); return CKR_OK; } /* Return true iff the previous slot was the last one. */ bool slots_iterate_last (slot_iterator_t *slot) { return scute_table_last (slots, *slot); } /* Acquire the slot for the slot ID ID. */ CK_RV slots_lookup (CK_SLOT_ID id, slot_iterator_t *id_r) { struct slot *slot = scute_table_data (slots, id); if (slot == NULL) return CKR_SLOT_ID_INVALID; *id_r = id; return CKR_OK; } /* Return true iff a token is present in slot SLOT. */ bool slot_token_present (slot_iterator_t id) { struct slot *slot = scute_table_data (slots, id); return slot->token_present; } /* Return the token label. We use the dispserialno here too because * Firefox prints that value in the prompt ("Stored at:"). */ const char * slot_token_label (slot_iterator_t id) { return slot_token_serial (id); } /* Get the manufacturer of the token. */ const char * slot_token_manufacturer (slot_iterator_t id) { struct slot *slot = scute_table_data (slots, id); unsigned int uval; if (slot->info.is_piv) { if (slot->info.cardtype && !strcmp (slot->info.cardtype, "yubikey")) return "Yubikey"; return "Unknown"; } /* slots_update() makes sure this is valid. */ uval = xtoi_2 (slot->info.serialno + 16) * 256 + xtoi_2 (slot->info.serialno + 18); /* Note: Make sure that there is no colon or linefeed in the string. */ switch (uval) { case 0x0001: return "PPC Card Systems"; case 0x0002: return "Prism"; case 0x0003: return "OpenFortress"; case 0x0004: return "Wewid AB"; case 0x0005: return "ZeitControl"; case 0x002A: return "Magrathea"; case 0x0000: case 0xffff: return "test card"; default: return (uval & 0xff00) == 0xff00? "unmanaged S/N range":"unknown"; } /* Not reached. */ } /* Get the application used on the token. */ const char * slot_token_application (slot_iterator_t id) { struct slot *slot = scute_table_data (slots, id); if (!slot) return "[ooops]"; /* slots_update() makes sure this is correct. */ if (slot->info.is_piv) return "PIV"; else return "OpenPGP"; } /* Get the serial number of the token. */ const char * slot_token_serial (slot_iterator_t id) { struct slot *slot = scute_table_data (slots, id); /* slots_update() makes sure this is valid. */ return slot->info.dispserialno? slot->info.dispserialno : slot->info.serialno; } /* Get the manufacturer of the token. */ void slot_token_version (slot_iterator_t id, CK_BYTE *hw_major, CK_BYTE *hw_minor, CK_BYTE *fw_major, CK_BYTE *fw_minor) { struct slot *slot = scute_table_data (slots, id); /* slots_update() makes sure serialno is valid. */ if (slot->info.is_piv) { *hw_major = 0; *hw_minor = 0; *fw_major = 0; *fw_minor = 0; } else { *hw_major = xtoi_2 (slot->info.serialno + 12); *hw_minor = xtoi_2 (slot->info.serialno + 14); *fw_major = 0; *fw_minor = 0; } } /* Get the maximum and minimum pin length. */ void slot_token_maxpinlen (slot_iterator_t id, CK_ULONG *max, CK_ULONG *min) { struct slot *slot = scute_table_data (slots, id); /* In version 2 of the OpenPGP card, the second counter is for the reset operation, so we only take the first counter. */ *max = slot->info.chvmaxlen[0]; /* FIXME: This is true at least for the user pin (CHV1 and CHV2). */ *min = 6; } /* Get the maximum and the actual pin count. */ void slot_token_pincount (slot_iterator_t id, int *max, int *len) { struct slot *slot = scute_table_data (slots, id); *max = 3; /* In version 2 of the OpenPGP card, the second counter is for the reset operation, so we only take the first counter. */ *len = slot->info.chvretry[0]; } /* Return the ID of slot SLOT. */ CK_SLOT_ID slot_get_id (slot_iterator_t slot) { return slot; } /* Return true if the token supports the GET CHALLENGE operation. */ bool slot_token_has_rng (slot_iterator_t id) { struct slot *slot = scute_table_data (slots, id); return slot->info.rng_available; } /* Mechanism management. */ /* Begin iterating over the list of mechanisms. */ CK_RV mechanisms_iterate_first (slot_iterator_t id, mechanism_iterator_t *mechanism) { struct slot *slot = scute_table_data (slots, id); *mechanism = scute_table_first (slot->mechanisms); return CKR_OK; } /* Continue iterating over the list of mechanisms. */ CK_RV mechanisms_iterate_next (slot_iterator_t id, mechanism_iterator_t *mechanism) { struct slot *slot = scute_table_data (slots, id); *mechanism = scute_table_next (slot->mechanisms, *mechanism); return CKR_OK; } /* Return true iff the previous slot was the last one. */ bool mechanisms_iterate_last (slot_iterator_t id, mechanism_iterator_t *mechanism) { struct slot *slot = scute_table_data (slots, id); return scute_table_last (slot->mechanisms, *mechanism); } /* Acquire the mechanism TYPE for the slot id ID. */ CK_RV mechanisms_lookup (slot_iterator_t id, mechanism_iterator_t *mid_r, CK_MECHANISM_TYPE type) { struct slot *slot = scute_table_data (slots, id); int mid = scute_table_first (slot->mechanisms); while (!scute_table_last (slot->mechanisms, mid)) { struct mechanism *mechanism = scute_table_data (slot->mechanisms, mid); if (mechanism->type == type) { *mid_r = mid; return CKR_OK; } mid = scute_table_next (slot->mechanisms, mid); } return CKR_MECHANISM_INVALID; } /* Return the type of mechanism MID in slot ID. */ CK_MECHANISM_TYPE mechanism_get_type (slot_iterator_t id, mechanism_iterator_t mid) { struct slot *slot = scute_table_data (slots, id); struct mechanism *mechanism = scute_table_data (slot->mechanisms, mid); return mechanism->type; } /* Return the info of mechanism MID. */ CK_MECHANISM_INFO_PTR mechanism_get_info (slot_iterator_t id, mechanism_iterator_t mid) { struct slot *slot = scute_table_data (slots, id); struct mechanism *mechanism = scute_table_data (slot->mechanisms, mid); return &mechanism->info; } /* Session management. */ /* Create a new session. */ CK_RV slot_create_session (slot_iterator_t id, session_iterator_t *session, bool rw) { int err; struct slot *slot = scute_table_data (slots, id); unsigned int tsid; void *rawp; struct session *session_p; assert (slot); if (scute_table_used (slot->sessions) == SESSION_MAX) return CKR_SESSION_COUNT; if (slot->login == SLOT_LOGIN_SO && !rw) return CKR_SESSION_READ_WRITE_SO_EXISTS; err = scute_table_alloc (slot->sessions, &tsid, &rawp, NULL); if (err) return scute_sys_to_ck (err); session_p = rawp; session_p->rw = rw; session_p->search_result = NULL; session_p->search_result_len = 0; session_p->signing_key = CK_INVALID_HANDLE; *session = SESSION_BUILD_ID (id, tsid); return CKR_OK; } /* Look up session. */ CK_RV slots_lookup_session (CK_SESSION_HANDLE sid, slot_iterator_t *id, session_iterator_t *session_id) { CK_RV err; unsigned int idx = SESSION_SLOT (sid); unsigned session_idx = SESSION_ID (sid); struct slot *slot; /* Verify the slot. */ err = slots_lookup (SESSION_SLOT (sid), id); if (err) return err; *session_id = session_idx; /* Verify the session. */ slot = scute_table_data (slots, idx); if (!scute_table_data (slot->sessions, session_idx)) return CKR_SESSION_HANDLE_INVALID; return 0; } /* Close the session. */ CK_RV slot_close_session (slot_iterator_t id, session_iterator_t sid) { struct slot *slot = scute_table_data (slots, id); scute_table_dealloc (slot->sessions, &sid); /* At last session closed, return to public sessions. */ if (!scute_table_used (slot->sessions)) slot->login = SLOT_LOGIN_PUBLIC; return CKR_OK; } /* Close all sessions. */ CK_RV slot_close_all_sessions (slot_iterator_t id) { struct slot *slot = scute_table_data (slots, id); int sid = scute_table_first (slot->sessions); while (!scute_table_last (slot->sessions, sid)) { slot_close_session (id, sid); sid = scute_table_next (slot->sessions, sid); } assert (scute_table_used (slot->sessions) == 0); return CKR_OK; } /* Get the RW flag from the session SID in slot ID. */ bool session_get_rw (slot_iterator_t id, session_iterator_t sid) { struct slot *slot = scute_table_data (slots, id); struct session *session = scute_table_data (slot->sessions, sid); return session->rw; } /* Get the login state from the slot ID. */ slot_login_t slot_get_status (slot_iterator_t id) { struct slot *slot = scute_table_data (slots, id); return slot->status; } /* Object management. */ /* Begin iterating over the list of objects. */ CK_RV objects_iterate_first (slot_iterator_t id, object_iterator_t *object) { struct slot *slot = scute_table_data (slots, id); *object = scute_table_first (slot->objects); return CKR_OK; } /* Continue iterating over the list of objects. */ CK_RV objects_iterate_next (slot_iterator_t id, object_iterator_t *object) { struct slot *slot = scute_table_data (slots, id); *object = scute_table_next (slot->objects, *object); return CKR_OK; } /* Return true iff the previous slot was the last one. */ bool objects_iterate_last (slot_iterator_t id, object_iterator_t *object) { struct slot *slot = scute_table_data (slots, id); return scute_table_last (slot->objects, *object); } /* Return the max. number of objects in the slot. May overcount somewhat. */ CK_RV slot_get_object_count (slot_iterator_t id, int *nr) { struct slot *slot = scute_table_data (slots, id); *nr = scute_table_used (slot->objects); return CKR_OK; } /* Get the object information for object OBJECT_ID in slot ID. */ CK_RV slot_get_object (slot_iterator_t id, object_iterator_t oid, CK_ATTRIBUTE_PTR *obj, CK_ULONG *obj_count) { struct slot *slot = scute_table_data (slots, id); struct object *object = scute_table_data (slot->objects, oid); if (!object) return CKR_OBJECT_HANDLE_INVALID; *obj = object->attributes; *obj_count = object->attributes_count; return 0; } /* Set the result of a search for session SID in slot ID to SEARCH_RESULT and SEARCH_RESULT_LEN. */ CK_RV session_set_search_result (slot_iterator_t id, session_iterator_t sid, object_iterator_t *search_result, int search_result_len) { struct slot *slot = scute_table_data (slots, id); struct session *session = scute_table_data (slot->sessions, sid); if (session->search_result && session->search_result != search_result) free (session->search_result); session->search_result = search_result; session->search_result_len = search_result_len; return 0; } /* Get the stored search result for the session SID in slot ID. */ CK_RV session_get_search_result (slot_iterator_t id, session_iterator_t sid, object_iterator_t **search_result, int *search_result_len) { struct slot *slot = scute_table_data (slots, id); struct session *session = scute_table_data (slot->sessions, sid); assert (search_result); assert (search_result_len); *search_result = session->search_result; *search_result_len = session->search_result_len; return 0; } -/* Set the signing key for session SID in slot ID to KEY. */ +/* Set the signing key for session SID in slot ID to KEY. This is the + * core of C_SignInit. */ CK_RV session_set_signing_key (slot_iterator_t id, session_iterator_t sid, object_iterator_t key) { struct slot *slot = scute_table_data (slots, id); struct session *session = scute_table_data (slot->sessions, sid); CK_RV err; CK_ATTRIBUTE_PTR attr; CK_ULONG attr_count; CK_OBJECT_CLASS key_class = CKO_PRIVATE_KEY; err = slot_get_object (id, key, &attr, &attr_count); if (err) return err; /* FIXME: What kind of strange loop is this? */ while (attr_count-- > 0) if (attr->type == CKA_CLASS) break; if (attr_count == (CK_ULONG) -1) return CKR_KEY_HANDLE_INVALID; if (attr->ulValueLen != sizeof (key_class) || memcmp (attr->pValue, &key_class, sizeof (key_class))) return CKR_KEY_HANDLE_INVALID; /* It's the private RSA key object. */ session->signing_key = key; return 0; } -/* FIXME: The description is wrong: - Set the signing key for session SID in slot ID to KEY. */ +/* The core of C_Sign - see there for a description. */ CK_RV session_sign (slot_iterator_t id, session_iterator_t sid, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { struct slot *slot = scute_table_data (slots, id); struct session *session = scute_table_data (slot->sessions, sid); + int rc; gpg_error_t err; CK_ATTRIBUTE_PTR attr; CK_ULONG attr_count; CK_OBJECT_CLASS key_class = CKO_PRIVATE_KEY; unsigned int sig_len; CK_BYTE key_id[100]; int i; const char *keyref; if (!pSignature) return CKR_ARGUMENTS_BAD; if (!session->signing_key) return CKR_OPERATION_NOT_INITIALIZED; - err = slot_get_object (id, session->signing_key, &attr, &attr_count); - if (err) - return err; + rc = slot_get_object (id, session->signing_key, &attr, &attr_count); + if (rc) + goto leave; if (attr_count == (CK_ULONG) -1) - return CKR_KEY_HANDLE_INVALID; + { + rc = CKR_KEY_HANDLE_INVALID; + goto leave; + } if (attr->ulValueLen != sizeof (key_class) || memcmp (attr->pValue, &key_class, sizeof (key_class))) - return CKR_KEY_HANDLE_INVALID; + { + rc = CKR_KEY_HANDLE_INVALID; + goto leave; + } /* Find the CKA_ID */ for (i = 0; i < attr_count; i++) if (attr[i].type == CKA_ID) break; if (i == attr_count) - return CKR_GENERAL_ERROR; + { + rc = CKR_GENERAL_ERROR; + goto leave; + } if (attr[i].ulValueLen >= sizeof key_id - 1) - return CKR_GENERAL_ERROR; + { + rc = CKR_GENERAL_ERROR; + goto leave; + } strncpy (key_id, attr[i].pValue, attr[i].ulValueLen); key_id[attr[i].ulValueLen] = 0; DEBUG (DBG_INFO, "Found CKA_ID '%s'", key_id); for (keyref=key_id; *keyref && *keyref != ' '; keyref++) ; if (*keyref) keyref++; /* Point to the grip. */ - DEBUG (DBG_INFO, "Using keyref '%s'", keyref); sig_len = *pulSignatureLen; err = scute_agent_sign (keyref, pData, ulDataLen, pSignature, &sig_len); - /* Take care of error codes which are not mapped by default. */ if (gpg_err_code (err) == GPG_ERR_INV_LENGTH) - return CKR_BUFFER_TOO_SMALL; + rc = CKR_BUFFER_TOO_SMALL; else if (gpg_err_code (err) == GPG_ERR_INV_ARG) - return CKR_ARGUMENTS_BAD; + rc = CKR_ARGUMENTS_BAD; else - return scute_gpg_err_to_ck (err); + rc = scute_gpg_err_to_ck (err); + + leave: + if (rc != CKR_OK && rc != CKR_BUFFER_TOO_SMALL) + session->signing_key = 0; + return rc; }