/* Support of smartcards and cryptotokens * Copyright (C) 2003 Christoph Gysin, Simon Zwahlen * Copyright (C) 2004 David Buechi, Michael Meier * Zuercher Hochschule Winterthur, Switzerland * * Copyright (C) 2005 Michael Joosten * * Copyright (C) 2005 Andreas Steffen * Hochschule fuer Technik Rapperswil, Switzerland * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program 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 General Public License * for more details. */ #include #include #include #include #include #include #include #include #include #include #include "constants.h" #ifdef SMARTCARD #include "rsaref/unix.h" #include "rsaref/pkcs11.h" #endif #include "defs.h" #include "log.h" #include "x509.h" #include "ca.h" #include "certs.h" #include "keys.h" #include "smartcard.h" #include "whack.h" #include "fetch.h" #define DEFAULT_BASE 16 /* chained list of smartcard records */ static smartcard_t *smartcards = NULL; /* number of generated sc objects */ static int sc_number = 0; const smartcard_t empty_sc = { NULL , /* next */ 0 , /* last_load */ { CERT_NONE, {NULL} }, /* last_cert */ 0 , /* count */ 0 , /* number */ 999999 , /* slot */ NULL , /* id */ NULL , /* label */ { NULL, 0 } , /* pin */ FALSE , /* pinpad */ FALSE , /* valid */ FALSE , /* session_opened */ FALSE , /* logged_in */ TRUE , /* any_slot */ 0L , /* session */ }; #ifdef SMARTCARD /* compile with smartcard support */ #define SCX_MAGIC 0xd00bed00 struct scx_pkcs11_module { u_int _magic; void *handle; }; typedef struct scx_pkcs11_module scx_pkcs11_module_t; /* PKCS #11 cryptoki context */ static bool scx_initialized = FALSE; static scx_pkcs11_module_t *pkcs11_module = NULL_PTR; static CK_FUNCTION_LIST_PTR pkcs11_functions = NULL_PTR; /* crytoki v2.11 - return values of PKCS #11 functions*/ static const char *const pkcs11_return_name[] = { "CKR_OK", "CKR_CANCEL", "CKR_HOST_MEMORY", "CKR_SLOT_ID_INVALID", "CKR_FLAGS_INVALID", "CKR_GENERAL_ERROR", "CKR_FUNCTION_FAILED", "CKR_ARGUMENTS_BAD", "CKR_NO_EVENT", "CKR_NEED_TO_CREATE_THREADS", "CKR_CANT_LOCK" }; static const char *const pkcs11_return_name_10[] = { "CKR_ATTRIBUTE_READ_ONLY", "CKR_ATTRIBUTE_SENSITIVE", "CKR_ATTRIBUTE_TYPE_INVALID", "CKR_ATTRIBUTE_VALUE_INVALID" }; static const char *const pkcs11_return_name_20[] = { "CKR_DATA_INVALID", "CKR_DATA_LEN_RANGE" }; static const char *const pkcs11_return_name_30[] = { "CKR_DEVICE_ERROR", "CKR_DEVICE_MEMORY", "CKR_DEVICE_REMOVED" }; static const char *const pkcs11_return_name_40[] = { "CKR_ENCRYPTED_DATA_INVALID", "CKR_ENCRYPTED_DATA_LEN_RANGE" }; static const char *const pkcs11_return_name_50[] = { "CKR_FUNCTION_CANCELED", "CKR_FUNCTION_NOT_PARALLEL", "CKR_0x52_UNDEFINED", "CKR_0x53_UNDEFINED", "CKR_FUNCTION_NOT_SUPPORTED" }; static const char *const pkcs11_return_name_60[] = { "CKR_KEY_HANDLE_INVALID", "CKR_KEY_SENSITIVE", "CKR_KEY_SIZE_RANGE", "CKR_KEY_TYPE_INCONSISTENT", "CKR_KEY_NOT_NEEDED", "CKR_KEY_CHANGED", "CKR_KEY_NEEDED", "CKR_KEY_INDIGESTIBLE", "CKR_KEY_FUNCTION_NOT_PERMITTED", "CKR_KEY_NOT_WRAPPABLE", "CKR_KEY_UNEXTRACTABLE" }; static const char *const pkcs11_return_name_70[] = { "CKR_MECHANISM_INVALID", "CKR_MECHANISM_PARAM_INVALID" }; static const char *const pkcs11_return_name_80[] = { "CKR_OBJECT_HANDLE_INVALID" }; static const char *const pkcs11_return_name_90[] = { "CKR_OPERATION_ACTIVE", "CKR_OPERATION_NOT_INITIALIZED" }; static const char *const pkcs11_return_name_A0[] = { "CKR_PIN_INCORRECT", "CKR_PIN_INVALID", "CKR_PIN_LEN_RANGE", "CKR_PIN_EXPIRED", "CKR_PIN_LOCKED" }; static const char *const pkcs11_return_name_B0[] = { "CKR_SESSION_CLOSED", "CKR_SESSION_COUNT", "CKR_0xB2_UNDEFINED", "CKR_SESSION_HANDLE_INVALID", "CKR_SESSION_PARALLEL_NOT_SUPPORTED", "CKR_SESSION_READ_ONLY", "CKR_SESSION_EXISTS", "CKR_SESSION_READ_ONLY_EXISTS", "CKR_SESSION_READ_WRITE_SO_EXISTS" }; static const char *const pkcs11_return_name_C0[] = { "CKR_SIGNATURE_INVALID", "CKR_SIGNATURE_LEN_RANGE" }; static const char *const pkcs11_return_name_D0[] = { "CKR_TEMPLATE_INCOMPLETE", "CKR_TEMPLATE_INCONSISTENT" }; static const char *const pkcs11_return_name_E0[] = { "CKR_TOKEN_NOT_PRESENT", "CKR_TOKEN_NOT_RECOGNIZED", "CKR_TOKEN_WRITE_PROTECTED" }; static const char *const pkcs11_return_name_F0[] = { "CKR_UNWRAPPING_KEY_HANDLE_INVALID", "CKR_UNWRAPPING_KEY_SIZE_RANGE", "CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT" }; static const char *const pkcs11_return_name_100[] = { "CKR_USER_ALREADY_LOGGED_IN", "CKR_USER_NOT_LOGGED_IN", "CKR_USER_PIN_NOT_INITIALIZED", "CKR_USER_TYPE_INVALID", "CKR_USER_ANOTHER_ALREADY_LOGGED_IN", "CKR_USER_TOO_MANY_TYPES" }; static const char *const pkcs11_return_name_110[] = { "CKR_WRAPPED_KEY_INVALID", "CKR_0x111_UNDEFINED", "CKR_WRAPPED_KEY_LEN_RANGE", "CKR_WRAPPING_KEY_HANDLE_INVALID", "CKR_WRAPPING_KEY_SIZE_RANGE", "CKR_WRAPPING_KEY_TYPE_INCONSISTENT" }; static const char *const pkcs11_return_name_120[] = { "CKR_RANDOM_SEED_NOT_SUPPORTED", "CKR_RANDOM_NO_RNG" }; static const char *const pkcs11_return_name_130[] = { "CKR_DOMAIN_PARAMS_INVALID" }; static const char *const pkcs11_return_name_150[] = { "CKR_BUFFER_TOO_SMALL" }; static const char *const pkcs11_return_name_160[] = { "CKR_SAVED_STATE_INVALID" }; static const char *const pkcs11_return_name_170[] = { "CKR_INFORMATION_SENSITIVE" }; static const char *const pkcs11_return_name_180[] = { "CKR_STATE_UNSAVEABLE" }; static const char *const pkcs11_return_name_190[] = { "CKR_CRYPTOKI_NOT_INITIALIZED", "CKR_CRYPTOKI_ALREADY_INITIALIZED" }; static const char *const pkcs11_return_name_1A0[] = { "CKR_MUTEX_BAD", "CKR_MUTEX_NOT_LOCKED" }; static const char *const pkcs11_return_name_200[] = { "CKR_FUNCTION_REJECTED" }; static const char *const pkcs11_return_name_vendor[] = { "CKR_VENDOR_DEFINED" }; static enum_names pkcs11_return_names_vendor = { CKR_VENDOR_DEFINED, CKR_VENDOR_DEFINED , pkcs11_return_name_vendor, NULL }; static enum_names pkcs11_return_names_200 = { CKR_FUNCTION_REJECTED, CKR_FUNCTION_REJECTED , pkcs11_return_name_200, &pkcs11_return_names_vendor }; static enum_names pkcs11_return_names_1A0 = { CKR_MUTEX_BAD, CKR_MUTEX_NOT_LOCKED , pkcs11_return_name_1A0, &pkcs11_return_names_200 }; static enum_names pkcs11_return_names_190 = { CKR_CRYPTOKI_NOT_INITIALIZED, CKR_CRYPTOKI_ALREADY_INITIALIZED , pkcs11_return_name_190, &pkcs11_return_names_1A0 }; static enum_names pkcs11_return_names_180 = { CKR_STATE_UNSAVEABLE, CKR_STATE_UNSAVEABLE , pkcs11_return_name_180, &pkcs11_return_names_190 }; static enum_names pkcs11_return_names_170 = { CKR_INFORMATION_SENSITIVE, CKR_INFORMATION_SENSITIVE , pkcs11_return_name_170, &pkcs11_return_names_180 }; static enum_names pkcs11_return_names_160 = { CKR_SAVED_STATE_INVALID, CKR_SAVED_STATE_INVALID , pkcs11_return_name_160, &pkcs11_return_names_170 }; static enum_names pkcs11_return_names_150 = { CKR_BUFFER_TOO_SMALL, CKR_BUFFER_TOO_SMALL , pkcs11_return_name_150, &pkcs11_return_names_160 }; static enum_names pkcs11_return_names_130 = { CKR_DOMAIN_PARAMS_INVALID, CKR_DOMAIN_PARAMS_INVALID , pkcs11_return_name_130, &pkcs11_return_names_150 }; static enum_names pkcs11_return_names_120 = { CKR_RANDOM_SEED_NOT_SUPPORTED, CKR_RANDOM_NO_RNG , pkcs11_return_name_120, &pkcs11_return_names_130 }; static enum_names pkcs11_return_names_110 = { CKR_WRAPPED_KEY_INVALID, CKR_WRAPPING_KEY_TYPE_INCONSISTENT , pkcs11_return_name_110, &pkcs11_return_names_120 }; static enum_names pkcs11_return_names_100 = { CKR_USER_ALREADY_LOGGED_IN, CKR_USER_TOO_MANY_TYPES , pkcs11_return_name_100, &pkcs11_return_names_110 }; static enum_names pkcs11_return_names_F0 = { CKR_UNWRAPPING_KEY_HANDLE_INVALID, CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT , pkcs11_return_name_F0, &pkcs11_return_names_100 }; static enum_names pkcs11_return_names_E0 = { CKR_TOKEN_NOT_PRESENT, CKR_TOKEN_WRITE_PROTECTED , pkcs11_return_name_E0, &pkcs11_return_names_F0 }; static enum_names pkcs11_return_names_D0 = { CKR_TEMPLATE_INCOMPLETE, CKR_TEMPLATE_INCONSISTENT , pkcs11_return_name_D0,&pkcs11_return_names_E0 }; static enum_names pkcs11_return_names_C0 = { CKR_SIGNATURE_INVALID, CKR_SIGNATURE_LEN_RANGE , pkcs11_return_name_C0, &pkcs11_return_names_D0 }; static enum_names pkcs11_return_names_B0 = { CKR_SESSION_CLOSED, CKR_SESSION_READ_WRITE_SO_EXISTS , pkcs11_return_name_B0, &pkcs11_return_names_C0 }; static enum_names pkcs11_return_names_A0 = { CKR_PIN_INCORRECT, CKR_PIN_LOCKED , pkcs11_return_name_A0, &pkcs11_return_names_B0 }; static enum_names pkcs11_return_names_90 = { CKR_OPERATION_ACTIVE, CKR_OPERATION_NOT_INITIALIZED , pkcs11_return_name_90, &pkcs11_return_names_A0 }; static enum_names pkcs11_return_names_80 = { CKR_OBJECT_HANDLE_INVALID, CKR_OBJECT_HANDLE_INVALID , pkcs11_return_name_80, &pkcs11_return_names_90 }; static enum_names pkcs11_return_names_70 = { CKR_MECHANISM_INVALID, CKR_MECHANISM_PARAM_INVALID , pkcs11_return_name_70, &pkcs11_return_names_80 }; static enum_names pkcs11_return_names_60 = { CKR_KEY_HANDLE_INVALID, CKR_KEY_UNEXTRACTABLE , pkcs11_return_name_60, &pkcs11_return_names_70 }; static enum_names pkcs11_return_names_50 = { CKR_FUNCTION_CANCELED, CKR_FUNCTION_NOT_SUPPORTED , pkcs11_return_name_50, &pkcs11_return_names_60 }; static enum_names pkcs11_return_names_40 = { CKR_ENCRYPTED_DATA_INVALID, CKR_ENCRYPTED_DATA_LEN_RANGE , pkcs11_return_name_40, &pkcs11_return_names_50 }; static enum_names pkcs11_return_names_30 = { CKR_DEVICE_ERROR, CKR_DEVICE_REMOVED , pkcs11_return_name_30, &pkcs11_return_names_40 }; static enum_names pkcs11_return_names_20 = { CKR_DATA_INVALID, CKR_DATA_LEN_RANGE , pkcs11_return_name_20, &pkcs11_return_names_30 }; static enum_names pkcs11_return_names_10 = { CKR_ATTRIBUTE_READ_ONLY, CKR_ATTRIBUTE_VALUE_INVALID , pkcs11_return_name_10, &pkcs11_return_names_20}; static enum_names pkcs11_return_names = { CKR_OK, CKR_CANT_LOCK , pkcs11_return_name, &pkcs11_return_names_10}; /* * Unload a PKCS#11 module. * The calling application is responsible for cleaning up * and calling C_Finalize() */ static CK_RV scx_unload_pkcs11_module(scx_pkcs11_module_t *mod) { if (!mod || mod->_magic != SCX_MAGIC) return CKR_ARGUMENTS_BAD; if (dlclose(mod->handle) < 0) return CKR_FUNCTION_FAILED; memset(mod, 0, sizeof(*mod)); free(mod); return CKR_OK; } static scx_pkcs11_module_t* scx_load_pkcs11_module(const char *name, CK_FUNCTION_LIST_PTR_PTR funcs) { CK_RV (*c_get_function_list)(CK_FUNCTION_LIST_PTR_PTR); scx_pkcs11_module_t *mod; void *handle; int rv; if (name == NULL || *name == '\0') return NULL; /* Try to load PKCS#11 library module*/ handle = dlopen(name, RTLD_NOW); if (handle == NULL) return NULL; mod = malloc_thing(scx_pkcs11_module_t); mod->_magic = SCX_MAGIC; mod->handle = handle; /* Get the list of function pointers */ c_get_function_list = (CK_RV (*)(CK_FUNCTION_LIST_PTR_PTR)) dlsym(mod->handle, "C_GetFunctionList"); if (!c_get_function_list) goto failed; rv = c_get_function_list(funcs); if (rv == CKR_OK) return mod; failed: scx_unload_pkcs11_module(mod); return NULL; } /* * retrieve a certificate object */ static bool scx_find_cert_object(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object , smartcard_t *sc, cert_t *cert) { size_t hex_len, label_len; u_char *hex_id = NULL; chunk_t blob; x509cert_t *x509cert; CK_ATTRIBUTE attr[] = { { CKA_ID, NULL_PTR, 0L }, { CKA_LABEL, NULL_PTR, 0L }, { CKA_VALUE, NULL_PTR, 0L } }; /* initialize the return argument */ *cert = cert_empty; /* get the length of the attributes first */ CK_RV rv = pkcs11_functions->C_GetAttributeValue(session, object, attr, 3); if (rv != CKR_OK) { plog("couldn't read the attribute sizes: %s" , enum_show(&pkcs11_return_names, rv)); return FALSE; } free(sc->label); hex_id = malloc(attr[0].ulValueLen); hex_len = attr[0].ulValueLen; sc->label = malloc(attr[1].ulValueLen + 1); label_len = attr[1].ulValueLen; blob.ptr = malloc(attr[2].ulValueLen); blob.len = attr[2].ulValueLen; attr[0].pValue = hex_id; attr[1].pValue = sc->label; attr[2].pValue = blob.ptr; /* now get the attributes */ rv = pkcs11_functions->C_GetAttributeValue(session, object, attr, 3); if (rv != CKR_OK) { plog("couldn't read the attributes: %s" , enum_show(&pkcs11_return_names, rv)); free(hex_id); free(sc->label); free(blob.ptr); return FALSE; } free(sc->id); /* convert id from hex to ASCII */ sc->id = malloc(2*hex_len + 1); datatot(hex_id, hex_len, 16, sc->id, 2*hex_len + 1); free(hex_id); /* safeguard in case the label is not null terminated */ sc->label[label_len] = '\0'; /* parse the retrieved cert */ x509cert = malloc_thing(x509cert_t); *x509cert = empty_x509cert; x509cert->smartcard = TRUE; if (!parse_x509cert(blob, 0, x509cert)) { plog("failed to load cert from smartcard, error in X.509 certificate"); free_x509cert(x509cert); return FALSE; } cert->type = CERT_X509_SIGNATURE; cert->u.x509 = x509cert; return TRUE; } /* * search a given slot for PKCS#11 certificate objects */ static void scx_find_cert_objects(CK_SLOT_ID slot, CK_SESSION_HANDLE session) { CK_RV rv; CK_OBJECT_CLASS class = CKO_CERTIFICATE; CK_ATTRIBUTE attr[] = {{ CKA_CLASS, &class, sizeof(class) }}; rv = pkcs11_functions->C_FindObjectsInit(session, attr, 1); if (rv != CKR_OK) { plog("error in C_FindObjectsInit: %s" , enum_show(&pkcs11_return_names, rv)); return; } for (;;) { CK_OBJECT_HANDLE object; CK_ULONG obj_count = 0; err_t ugh; time_t valid_until; smartcard_t *sc; x509cert_t *cert; rv = pkcs11_functions->C_FindObjects(session, &object, 1, &obj_count); if (rv != CKR_OK) { plog("error in C_FindObjects: %s" , enum_show(&pkcs11_return_names, rv)); break; } /* no objects left */ if (obj_count == 0) break; /* create and initialize a new smartcard object */ sc = malloc_thing(smartcard_t); *sc = empty_sc; sc->any_slot = FALSE; sc->slot = slot; if (!scx_find_cert_object(session, object, sc, &sc->last_cert)) { scx_free(sc); continue; } DBG(DBG_CONTROL, DBG_log("found cert in %s with id: %s, label: '%s'" , scx_print_slot(sc, ""), sc->id, sc->label) ) /* check validity of certificate */ cert = sc->last_cert.u.x509; valid_until = cert->notAfter; ugh = check_validity(cert, &valid_until); if (ugh != NULL) { plog(" %s", ugh); free_x509cert(cert); scx_free(sc); continue; } else { DBG(DBG_CONTROL, DBG_log(" certificate is valid") ) } sc = scx_add(sc); /* put end entity and ca certificates into different chains */ if (cert->isCA) { sc->last_cert.u.x509 = add_authcert(cert, AUTH_CA); } else { add_x509_public_key(cert, valid_until, DAL_LOCAL); sc->last_cert.u.x509 = add_x509cert(cert); } share_cert(sc->last_cert); time(&sc->last_load); } rv = pkcs11_functions->C_FindObjectsFinal(session); if (rv != CKR_OK) { plog("error in C_FindObjectsFinal: %s" , enum_show(&pkcs11_return_names, rv)); } } /* * search all slots for PKCS#11 certificate objects */ static void scx_find_all_cert_objects(void) { CK_RV rv; CK_SLOT_ID_PTR slots = NULL_PTR; CK_ULONG slot_count = 0; CK_ULONG i; if (!scx_initialized) { plog("pkcs11 module not initialized"); return; } /* read size, always returns CKR_OK ! */ rv = pkcs11_functions->C_GetSlotList(FALSE, NULL_PTR, &slot_count); /* allocate memory for the slots */ slots = (CK_SLOT_ID *)malloc(slot_count * sizeof(CK_SLOT_ID)); rv = pkcs11_functions->C_GetSlotList(FALSE, slots, &slot_count); if (rv != CKR_OK) { plog("error in C_GetSlotList: %s", enum_show(&pkcs11_return_names, rv)); free(slots); return; } /* look in every slot for certificate objects */ for (i = 0; i < slot_count; i++) { CK_SLOT_ID slot = slots[i]; CK_SLOT_INFO info; CK_SESSION_HANDLE session; rv = pkcs11_functions->C_GetSlotInfo(slot, &info); if (rv != CKR_OK) { plog("error in C_GetSlotInfo: %s" , enum_show(&pkcs11_return_names, rv)); continue; } if (!(info.flags & CKF_TOKEN_PRESENT)) { plog("no token present in slot %lu", slot); continue; } rv = pkcs11_functions->C_OpenSession(slot , CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &session); if (rv != CKR_OK) { plog("failed to open a session on slot %lu: %s" , slot, enum_show(&pkcs11_return_names, rv)); continue; } DBG(DBG_CONTROLMORE, DBG_log("pkcs11 session #%ld for searching slot %lu", session, slot) ) scx_find_cert_objects(slot, session); rv = pkcs11_functions->C_CloseSession(session); if (rv != CKR_OK) { plog("error in C_CloseSession: %s" , enum_show(&pkcs11_return_names, rv)); } } free(slots); } #endif /* * load and initialize PKCS#11 cryptoki module * * init_args should be unused when we have a PKCS#11 compliant module, * but NSS softoken breaks that API. */ void scx_init(const char* module, const char *init_args) { #ifdef SMARTCARD CK_C_INITIALIZE_ARGS args = { .pReserved = (char *)init_args, }; CK_RV rv; if (scx_initialized) { plog("weird - pkcs11 module seems already to be initialized"); return; } if (module == NULL) #ifdef PKCS11_DEFAULT_LIB module = PKCS11_DEFAULT_LIB; #else { plog("no pkcs11 module defined"); return; } #endif DBG(DBG_CONTROL | DBG_CRYPT, DBG_log("pkcs11 module '%s' loading...", module) ) pkcs11_module = scx_load_pkcs11_module(module, &pkcs11_functions); if (pkcs11_module == NULL) { plog("failed to load pkcs11 module '%s'", module); return; } DBG(DBG_CONTROL | DBG_CRYPT, DBG_log("pkcs11 module initializing...") ) rv = pkcs11_functions->C_Initialize(init_args ? &args : NULL); if (rv != CKR_OK) { plog("failed to initialize pkcs11 module: %s" , enum_show(&pkcs11_return_names, rv)); return; } scx_initialized = TRUE; DBG(DBG_CONTROL | DBG_CRYPT, DBG_log("pkcs11 module loaded and initialized") ) scx_find_all_cert_objects(); #endif } /* * finalize and unload PKCS#11 cryptoki module */ void scx_finalize(void) { #ifdef SMARTCARD while (smartcards != NULL) { scx_release(smartcards); } if (pkcs11_functions != NULL_PTR) { pkcs11_functions->C_Finalize(NULL_PTR); pkcs11_functions = NULL_PTR; } if (pkcs11_module != NULL) { scx_unload_pkcs11_module(pkcs11_module); pkcs11_module = NULL; } scx_initialized = FALSE; DBG(DBG_CONTROL | DBG_CRYPT, DBG_log("pkcs11 module finalized and unloaded") ) #endif } /* * does a filename contain the token %smartcard? */ bool scx_on_smartcard(const char *filename) { return strneq(filename, SCX_TOKEN, strlen(SCX_TOKEN)); } #ifdef SMARTCARD /* * find a specific object on the smartcard */ static bool scx_pkcs11_find_object( CK_SESSION_HANDLE session, CK_OBJECT_HANDLE_PTR object, CK_OBJECT_CLASS class, const char* id) { size_t len; char buf[BUF_LEN]; CK_RV rv; CK_ULONG obj_count = 0; CK_ULONG attr_count = 1; CK_ATTRIBUTE attr[] = { { CKA_CLASS, &class, sizeof(class) }, { CKA_ID, &buf, 0L } }; if (id != NULL) { ttodata(id, strlen(id), 16, buf, BUF_LEN, &len); attr[1].ulValueLen = len; attr_count = 2; } /* get info for certificate with id */ rv = pkcs11_functions->C_FindObjectsInit(session, attr, attr_count); if (rv != CKR_OK) { plog("error in C_FindObjectsInit: %s" , enum_show(&pkcs11_return_names, rv)); return FALSE; } rv = pkcs11_functions->C_FindObjects(session, object, 1, &obj_count); if (rv != CKR_OK) { plog("error in C_FindObjects: %s" , enum_show(&pkcs11_return_names, rv)); return FALSE; } rv = pkcs11_functions->C_FindObjectsFinal(session); if (rv != CKR_OK) { plog("error in C_FindObjectsFinal: %s" , enum_show(&pkcs11_return_names, rv)); return FALSE; } return (obj_count != 0); } /* * check if a given certificate object id is found in a slot */ static bool scx_find_cert_id_in_slot(smartcard_t *sc, CK_SLOT_ID slot) { CK_SESSION_HANDLE session; CK_OBJECT_HANDLE object; CK_SLOT_INFO info; CK_RV rv = pkcs11_functions->C_GetSlotInfo(slot, &info); if (rv != CKR_OK) { plog("error in C_GetSlotInfo: %s" , enum_show(&pkcs11_return_names, rv)); return FALSE; } if (!(info.flags & CKF_TOKEN_PRESENT)) { plog("no token present in slot %lu", slot); return FALSE; } rv = pkcs11_functions->C_OpenSession(slot , CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &session); if (rv != CKR_OK) { plog("failed to open a session on slot %lu: %s" , slot, enum_show(&pkcs11_return_names, rv)); return FALSE; } DBG(DBG_CONTROLMORE, DBG_log("pkcs11 session #%ld for searching slot %lu", session, slot) ) /* check if there is a certificate on the card in the specified slot */ if (scx_pkcs11_find_object(session, &object, CKO_CERTIFICATE, sc->id)) { sc->slot = slot; sc->any_slot = FALSE; sc->session = session; sc->session_opened = TRUE; return TRUE; } rv = pkcs11_functions->C_CloseSession(session); if (rv != CKR_OK) { plog("error in C_CloseSession: %s" , enum_show(&pkcs11_return_names, rv)); } return FALSE; } #endif /* * Connect to the smart card in the reader and select the correct slot */ bool scx_establish_context(smartcard_t *sc) { #ifdef SMARTCARD bool id_found = FALSE; if (!scx_initialized) { plog("pkcs11 module not initialized"); return FALSE; } if (sc->session_opened) { DBG(DBG_CONTROL | DBG_CRYPT, DBG_log("pkcs11 session #%ld already open", sc->session) ) return TRUE; } if (!sc->any_slot) id_found = scx_find_cert_id_in_slot(sc, sc->slot); if (!id_found) { CK_RV rv; CK_SLOT_ID slot; CK_SLOT_ID_PTR slots = NULL_PTR; CK_ULONG slot_count = 0; CK_ULONG i; /* read size, always returns CKR_OK ! */ rv = pkcs11_functions->C_GetSlotList(FALSE, NULL_PTR, &slot_count); /* allocate memory for the slots */ slots = (CK_SLOT_ID *)malloc(slot_count * sizeof(CK_SLOT_ID)); rv = pkcs11_functions->C_GetSlotList(FALSE, slots, &slot_count); if (rv != CKR_OK) { plog("error in C_GetSlotList: %s" , enum_show(&pkcs11_return_names, rv)); free(slots); return FALSE; } /* look in every slot for a certificate with a given object ID */ for (i = 0; i < slot_count; i++) { slot = slots[i]; id_found = scx_find_cert_id_in_slot(sc, slot); if (id_found) break; } free(slots); } if (id_found) { DBG(DBG_CONTROL | DBG_CRYPT, DBG_log("found token with id %s in slot %lu", sc->id, sc->slot); DBG_log("pkcs11 session #%ld opened", sc->session) ) } else { plog(" no certificate with id %s found on smartcard", sc->id); } return id_found; #else plog("warning: SMARTCARD support is deactivated in pluto/Makefile!"); return FALSE; #endif } /* * log in to a session */ bool scx_login(smartcard_t *sc) { #ifdef SMARTCARD CK_RV rv; if (sc->logged_in) { DBG(DBG_CONTROL | DBG_CRYPT, DBG_log("pkcs11 session #%ld login already done", sc->session) ) return TRUE; } if (sc->pin.ptr == NULL) { plog("unable to log in without PIN!"); return FALSE; } if (!sc->session_opened) { plog("session not opened"); return FALSE; } rv = pkcs11_functions->C_Login(sc->session, CKU_USER , (CK_UTF8CHAR *) sc->pin.ptr, sc->pin.len); if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) { plog("unable to login: %s" , enum_show(&pkcs11_return_names, rv)); return FALSE; } DBG(DBG_CONTROL | DBG_CRYPT, DBG_log("pkcs11 session #%ld login successful", sc->session) ) sc->logged_in = TRUE; return TRUE; #else return FALSE; #endif } #ifdef SMARTCARD /* * logout from a session */ static void scx_logout(smartcard_t *sc) { CK_RV rv; rv = pkcs11_functions->C_Logout(sc->session); if (rv != CKR_OK) plog("error in C_Logout: %s" , enum_show(&pkcs11_return_names, rv)); else DBG(DBG_CONTROL | DBG_CRYPT, DBG_log("pkcs11 session #%ld logout", sc->session) ) sc->logged_in = FALSE; } #endif /* * Release context and disconnect from card */ void scx_release_context(smartcard_t *sc) { #ifdef SMARTCARD CK_RV rv; if (!scx_initialized) return; if (sc->session_opened) { if (sc->logged_in) scx_logout(sc); sc->session_opened = FALSE; rv = pkcs11_functions->C_CloseSession(sc->session); if (rv != CKR_OK) plog("error in C_CloseSession: %s" , enum_show(&pkcs11_return_names, rv)); else DBG(DBG_CONTROL | DBG_CRYPT, DBG_log("pkcs11 session #%ld closed", sc->session) ) } #endif } /* * Load host certificate from smartcard */ bool scx_load_cert(const char *filename, smartcard_t **scp, cert_t *cert , bool *cached) { #ifdef SMARTCARD /* compile with smartcard support */ CK_OBJECT_HANDLE object; const char *number_slot_id = filename + strlen(SCX_TOKEN); smartcard_t *sc = scx_add(scx_parse_number_slot_id(number_slot_id)); /* return the smartcard object */ *scp = sc; /* is there a cached smartcard certificate? */ *cached = sc->last_cert.type != CERT_NONE && (time(NULL) - sc->last_load) < SCX_CERT_CACHE_INTERVAL; if (*cached) { *cert = sc->last_cert; plog(" using cached cert from smartcard #%d (%s, id: %s, label: '%s')" , sc->number , scx_print_slot(sc, "") , sc->id , sc->label); return TRUE; } if (!scx_establish_context(sc)) { scx_release_context(sc); return FALSE; } /* find the certificate object */ if (!scx_pkcs11_find_object(sc->session, &object, CKO_CERTIFICATE, sc->id)) { scx_release_context(sc); return FALSE; } /* retrieve the certificate object */ if (!scx_find_cert_object(sc->session, object, sc, cert)) { scx_release_context(sc); return FALSE; } if (!pkcs11_keep_state) scx_release_context(sc); plog(" loaded cert from smartcard #%d (%s, id: %s, label: '%s')" , sc->number , scx_print_slot(sc, "") , sc->id , sc->label); return TRUE; #else plog(" warning: SMARTCARD support is deactivated in pluto/Makefile!"); return FALSE; #endif } /* * parse slot number and key id * the following syntax is allowed * number slot id * %smartcard 1 - - * %smartcard#2 2 - - * %smartcard0 - 0 - * %smartcard:45 - - 45 * %smartcard0:45 - 0 45 */ smartcard_t* scx_parse_number_slot_id(const char *number_slot_id) { int len = strlen(number_slot_id); smartcard_t *sc = malloc_thing(smartcard_t); /* assign default values */ *sc = empty_sc; if (len == 0) /* default: use certificate #1 */ { sc->number = 1; } else if (*number_slot_id == '#') /* #number scheme */ { err_t ugh; unsigned long ul; ugh = atoul(number_slot_id+1, len-1 , 10, &ul); if (ugh == NULL) sc->number = (int)ul; else plog("error parsing smartcard number: %s", ugh); } else /* slot:id scheme */ { int slot_len = len; char *p = strchr(number_slot_id, ':'); if (p != NULL) { int id_len = len - (p + 1 - number_slot_id); slot_len -= (1 + id_len); if (id_len > 0) /* we have an id */ sc->id = p + 1; } if (slot_len > 0) /* we have a slot */ { err_t ugh = NULL; unsigned long ul; ugh = atoul(number_slot_id, slot_len, 10, &ul); if (ugh == NULL) { sc->slot = ul; sc->any_slot = FALSE; } else plog("error parsing smartcard slot number: %s", ugh); } } /* unshare the id string */ sc->id = clone_str(sc->id); return sc; } /* * Verify pin on card */ bool scx_verify_pin(smartcard_t *sc) { #ifdef SMARTCARD CK_RV rv; if (!sc->pinpad) sc->valid = FALSE; if (sc->pin.ptr == NULL) { plog("unable to verify without PIN"); return FALSE; } /* establish context */ if (!scx_establish_context(sc)) { scx_release_context(sc); return FALSE; } rv = pkcs11_functions->C_Login(sc->session, CKU_USER, (CK_UTF8CHAR *) sc->pin.ptr, sc->pin.len); if (rv == CKR_OK || rv == CKR_USER_ALREADY_LOGGED_IN) { sc->valid = TRUE; sc->logged_in = TRUE; DBG(DBG_CONTROL | DBG_CRYPT, DBG_log((rv == CKR_OK) ? "PIN code correct" : "already logged in, no PIN entry required"); DBG_log("pkcs11 session #%ld login successful", sc->session) ) } else { DBG(DBG_CONTROL | DBG_CRYPT, DBG_log("PIN code incorrect") ) } if (!pkcs11_keep_state) scx_release_context(sc); #else sc->valid = FALSE; #endif return sc->valid; } /* * Sign hash on smartcard */ bool scx_sign_hash(smartcard_t *sc, const u_char *in, size_t inlen , u_char *out, size_t outlen) { #ifdef SMARTCARD CK_RV rv; CK_OBJECT_HANDLE object; CK_ULONG siglen = (CK_ULONG)outlen; CK_BBOOL sign_flag, decrypt_flag; CK_ATTRIBUTE attr[] = { { CKA_SIGN, &sign_flag, sizeof(sign_flag) }, { CKA_DECRYPT, &decrypt_flag, sizeof(decrypt_flag) } }; if (!sc->logged_in) return FALSE; if (!scx_pkcs11_find_object(sc->session, &object, CKO_PRIVATE_KEY, sc->id)) { plog("unable to find private key with id '%s'", sc->id); return FALSE; } rv = pkcs11_functions->C_GetAttributeValue(sc->session, object, attr, 2); if (rv != CKR_OK) { plog("couldn't read the private key attributes: %s" , enum_show(&pkcs11_return_names, rv)); return FALSE; } DBG(DBG_CONTROL, DBG_log("RSA key flags: sign = %s, decrypt = %s" , (sign_flag)? "true":"false" , (decrypt_flag)? "true":"false") ) if (sign_flag) { CK_MECHANISM mech = { CKM_RSA_PKCS, NULL_PTR, 0 }; rv = pkcs11_functions->C_SignInit(sc->session, &mech, object); if (rv != CKR_OK) { plog("error in C_SignInit: %s" , enum_show(&pkcs11_return_names, rv)); return FALSE; } rv = pkcs11_functions->C_Sign(sc->session, (CK_BYTE_PTR)in, inlen , out, &siglen); if (rv != CKR_OK) { plog("error in C_Sign: %s" , enum_show(&pkcs11_return_names, rv)); return FALSE; } } else if (decrypt_flag) { CK_MECHANISM mech = { CKM_RSA_X_509, NULL_PTR, 0 }; size_t padlen; u_char *p = out ; /* PKCS#1 v1.5 8.1 encryption-block formatting */ *p++ = 0x00; *p++ = 0x01; /* BT (block type) 01 */ padlen = outlen - 3 - inlen; memset(p, 0xFF, padlen); p += padlen; *p++ = 0x00; memcpy(p, in, inlen); rv = pkcs11_functions->C_DecryptInit(sc->session, &mech, object); if (rv != CKR_OK) { plog("error in C_DecryptInit: %s" , enum_show(&pkcs11_return_names, rv)); return FALSE; } rv = pkcs11_functions->C_Decrypt(sc->session, out, outlen , out, &siglen); if (rv != CKR_OK) { plog("error in C_Decrypt: %s" , enum_show(&pkcs11_return_names, rv)); return FALSE; } } else { plog("private key has neither sign nor decrypt flag set"); return FALSE; } if (siglen > (CK_ULONG)outlen) { plog("signature length (%lu) larger than allocated buffer (%d)" , siglen, (int)outlen); return FALSE; } return TRUE; #else return FALSE; #endif } /* * encrypt data block with an RSA public key */ bool scx_encrypt(smartcard_t *sc, const u_char *in, size_t inlen , u_char *out, size_t *outlen) { #ifdef SMARTCARD CK_RV rv; CK_OBJECT_HANDLE object; CK_ULONG len = (CK_ULONG)(*outlen); CK_BBOOL encrypt_flag; CK_ATTRIBUTE attr[] = { { CKA_MODULUS, NULL_PTR, 0L }, { CKA_PUBLIC_EXPONENT, NULL_PTR, 0L }, { CKA_ENCRYPT, &encrypt_flag, sizeof(encrypt_flag) } }; CK_MECHANISM mech = { CKM_RSA_PKCS, NULL_PTR, 0 }; if (!scx_establish_context(sc)) { scx_release_context(sc); return FALSE; } if (!scx_pkcs11_find_object(sc->session, &object, CKO_PUBLIC_KEY, sc->id)) { plog("unable to find public key with id '%s'", sc->id); return FALSE; } rv = pkcs11_functions->C_GetAttributeValue(sc->session, object, attr, 3); if (rv != CKR_OK) { plog("couldn't read the public key attributes: %s" , enum_show(&pkcs11_return_names, rv)); scx_release_context(sc); return FALSE; } if (!encrypt_flag) { plog("public key cannot be used for encryption"); scx_release_context(sc); return FALSE; } /* there must be enough space left for the PKCS#1 v1.5 padding */ if (inlen > attr[0].ulValueLen - 11) { plog("smartcard input data length (%d) exceeds maximum of %lu bytes" , (int)inlen, attr[0].ulValueLen - 11); if (!pkcs11_keep_state) scx_release_context(sc); return FALSE; } rv = pkcs11_functions->C_EncryptInit(sc->session, &mech, object); if (rv != CKR_OK) { if (rv == CKR_FUNCTION_NOT_SUPPORTED) { public_key_t *key; chunk_t rsa_modulus, rsa_exponent, rsa_key, cipher_text; chunk_t plain_text = {(u_char*)in, inlen}; DBG(DBG_CONTROL, DBG_log("doing RSA encryption in software") ) attr[0].pValue = malloc(attr[0].ulValueLen); attr[1].pValue = malloc(attr[1].ulValueLen); rv = pkcs11_functions->C_GetAttributeValue(sc->session, object, attr, 2); if (rv != CKR_OK) { plog("couldn't read modulus and public exponent: %s" , enum_show(&pkcs11_return_names, rv)); free(attr[0].pValue); free(attr[1].pValue); scx_release_context(sc); return FALSE; } rsa_modulus = chunk_create((u_char*) attr[0].pValue, (size_t) attr[0].ulValueLen); rsa_exponent = chunk_create((u_char*) attr[1].pValue, (size_t) attr[1].ulValueLen); rsa_key = asn1_wrap(ASN1_SEQUENCE, "mm", asn1_integer("m", rsa_modulus), asn1_integer("m", rsa_exponent)); key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA, BUILD_BLOB_ASN1_DER, rsa_key, BUILD_END); free(rsa_key.ptr); if (key == NULL) { return FALSE; } key->encrypt(key, plain_text, &cipher_text); key->destroy(key); if (cipher_text.ptr == NULL) { plog("smartcard input data length is too large"); if (!pkcs11_keep_state) { scx_release_context(sc); } return FALSE; } memcpy(out, cipher_text.ptr, cipher_text.len); *outlen = cipher_text.len; free(cipher_text.ptr); if (!pkcs11_keep_state) { scx_release_context(sc); } return TRUE; } else { plog("error in C_EncryptInit: %s" , enum_show(&pkcs11_return_names, rv)); scx_release_context(sc); return FALSE; } } DBG(DBG_CONTROL, DBG_log("doing RSA encryption on smartcard") ) rv = pkcs11_functions->C_Encrypt(sc->session, (u_char*)in, inlen , out, &len); if (rv != CKR_OK) { plog("error in C_Encrypt: %s" , enum_show(&pkcs11_return_names, rv)); scx_release_context(sc); return FALSE; } if (!pkcs11_keep_state) scx_release_context(sc); *outlen = (size_t)len; return TRUE; #else return FALSE; #endif } /* * decrypt a data block with an RSA private key */ bool scx_decrypt(smartcard_t *sc, const u_char *in, size_t inlen , u_char *out, size_t *outlen) { #ifdef SMARTCARD CK_RV rv; CK_OBJECT_HANDLE object; CK_ULONG len = (CK_ULONG)(*outlen); CK_BBOOL decrypt_flag; CK_ATTRIBUTE attr[] = { { CKA_DECRYPT, &decrypt_flag, sizeof(decrypt_flag) } }; CK_MECHANISM mech = { CKM_RSA_PKCS, NULL_PTR, 0 }; if (!scx_establish_context(sc) || !scx_login(sc)) { scx_release_context(sc); return FALSE; } if (!scx_pkcs11_find_object(sc->session, &object, CKO_PRIVATE_KEY, sc->id)) { plog("unable to find private key with id '%s'", sc->id); return FALSE; } rv = pkcs11_functions->C_GetAttributeValue(sc->session, object, attr, 1); if (rv != CKR_OK) { plog("couldn't read the private key attributes: %s" , enum_show(&pkcs11_return_names, rv)); return FALSE; } if (!decrypt_flag) { plog("private key cannot be used for decryption"); scx_release_context(sc); return FALSE; } DBG(DBG_CONTROL, DBG_log("doing RSA decryption on smartcard") ) rv = pkcs11_functions->C_DecryptInit(sc->session, &mech, object); if (rv != CKR_OK) { plog("error in C_DecryptInit: %s" , enum_show(&pkcs11_return_names, rv)); scx_release_context(sc); return FALSE; } rv = pkcs11_functions->C_Decrypt(sc->session, (u_char*)in, inlen , out, &len); if (rv != CKR_OK) { plog("error in C_Decrypt: %s" , enum_show(&pkcs11_return_names, rv)); scx_release_context(sc); return FALSE; } if (!pkcs11_keep_state) scx_release_context(sc); *outlen = (size_t)len; return TRUE; #else return FALSE; #endif } /* receive an encrypted data block via whack, * decrypt it using a private RSA key and * return the decrypted data block via whack */ bool scx_op_via_whack(const char* msg, int inbase, int outbase, sc_op_t op , const char* keyid, int whackfd) { char inbuf[RSA_MAX_OCTETS]; char outbuf[2*RSA_MAX_OCTETS + 1]; size_t outlen = sizeof(inbuf); size_t inlen; smartcard_t *sc,*sc_new; const char *number_slot_id = ""; err_t ugh = ttodata(msg, 0, inbase, inbuf, sizeof(inbuf), &inlen); /* no prefix - use default base */ if (ugh != NULL && inbase == 0) ugh = ttodata(msg, 0, DEFAULT_BASE, inbuf, sizeof(inbuf), &inlen); if (ugh != NULL) { plog("format error in smartcard input data: %s", ugh); return FALSE; } if (keyid != NULL) { number_slot_id = (strneq(keyid, SCX_TOKEN, strlen(SCX_TOKEN))) ? keyid + strlen(SCX_TOKEN) : keyid; } sc_new = scx_parse_number_slot_id(number_slot_id); sc = scx_add(sc_new); if (sc == sc_new) scx_share(sc); DBG((op == SC_OP_ENCRYPT)? DBG_PRIVATE:DBG_RAW, DBG_dump("smartcard input data:\n", inbuf, inlen) ) if (op == SC_OP_DECRYPT) { if (!sc->valid && whackfd != NULL_FD) scx_get_pin(sc, whackfd); if (!sc->valid) { loglog(RC_NOVALIDPIN, "cannot decrypt without valid PIN"); return FALSE; } } DBG(DBG_CONTROL | DBG_CRYPT, DBG_log("using RSA key from smartcard (slot: %d, id: %s)" , (int)sc->slot, sc->id) ) switch (op) { case SC_OP_ENCRYPT: if (!scx_encrypt(sc, inbuf, inlen, inbuf, &outlen)) return FALSE; break; case SC_OP_DECRYPT: if (!scx_decrypt(sc, inbuf, inlen, inbuf, &outlen)) return FALSE; break; default: break; } DBG((op == SC_OP_DECRYPT)? DBG_PRIVATE:DBG_RAW, DBG_dump("smartcard output data:\n", inbuf, outlen) ) if (outbase == 0) /* use default base */ outbase = DEFAULT_BASE; if (outbase == 256) /* ascii plain text */ whack_log(RC_COMMENT, "%.*s", (int)outlen, inbuf); else { outlen = datatot(inbuf, outlen, outbase, outbuf, sizeof(outbuf)); if (outlen == 0) { plog("error in output format conversion"); return FALSE; } whack_log(RC_COMMENT, "%s", outbuf); } return TRUE; } /* * get length of RSA key in bytes */ size_t scx_get_keylength(smartcard_t *sc) { #ifdef SMARTCARD CK_RV rv; CK_OBJECT_HANDLE object; CK_ATTRIBUTE attr[] = {{ CKA_MODULUS, NULL_PTR, 0}}; if (!sc->logged_in) return FALSE; if (!scx_pkcs11_find_object(sc->session, &object, CKO_PRIVATE_KEY, sc->id)) { plog("unable to find private key with id '%s'", sc->id); return FALSE; } /* get the length of the private key */ rv = pkcs11_functions->C_GetAttributeValue(sc->session, object , (CK_ATTRIBUTE_PTR)&attr, 1); if (rv != CKR_OK) { plog("failed to get key length: %s" , enum_show(&pkcs11_return_names, rv)); return FALSE; } return attr[0].ulValueLen; /*Return key length in bytes */ #else return 0; #endif } /* * prompt for pin and verify it */ bool scx_get_pin(smartcard_t *sc, int whackfd) { #ifdef SMARTCARD char pin[BUF_LEN]; int i, n; whack_log(RC_ENTERSECRET, "need PIN for #%d (%s, id: %s, label: '%s')" , sc->number, scx_print_slot(sc, ""), sc->id, sc->label); for (i = 0; i < SCX_MAX_PIN_TRIALS; i++) { if (i > 0) whack_log(RC_ENTERSECRET, "invalid PIN, please try again"); n = read(whackfd, pin, BUF_LEN); if (n == -1) { whack_log(RC_LOG_SERIOUS, "read(whackfd) failed"); return FALSE; } if (strlen(pin) == 0) { whack_log(RC_LOG_SERIOUS, "no PIN entered, aborted"); return FALSE; } sc->pin.ptr = pin; sc->pin.len = strlen(pin); /* verify the pin */ if (scx_verify_pin(sc)) { sc->pin = chunk_create(pin, strlen(pin)); sc->pin = chunk_clone(sc->pin); break; } /* wrong pin - we try another round */ sc->pin = chunk_empty; } if (sc->valid) whack_log(RC_SUCCESS, "valid PIN"); else whack_log(RC_LOG_SERIOUS, "invalid PIN, too many trials"); #else sc->valid = FALSE; whack_log(RC_LOG_SERIOUS, "SMARTCARD support is deactivated in pluto/Makefile!"); #endif return sc->valid; } /* * free the pin code */ void scx_free_pin(chunk_t *pin) { if (pin->ptr != NULL) { /* clear pin field in memory */ memset(pin->ptr, '\0', pin->len); free(pin->ptr); *pin = chunk_empty; } } /* * frees a smartcard record */ void scx_free(smartcard_t *sc) { if (sc != NULL) { scx_release_context(sc); free(sc->id); free(sc->label); scx_free_pin(&sc->pin); free(sc); } } /* release of a smartcard record decreases the count by one " the record is freed when the counter reaches zero */ void scx_release(smartcard_t *sc) { if (sc != NULL && --sc->count == 0) { smartcard_t **pp = &smartcards; while (*pp != sc) pp = &(*pp)->next; *pp = sc->next; release_cert(sc->last_cert); scx_free(sc); } } /* * compare two smartcard records by comparing their slots and ids */ static bool scx_same(smartcard_t *a, smartcard_t *b) { if (a->number && b->number) { /* same number */ return a->number == b->number; } else { /* same id and/or same slot */ return (!a->id || (b->id && streq(a->id, b->id))) && (a->any_slot || b->any_slot || a->slot == b->slot); } } /* for each link pointing to the smartcard record " increase the count by one */ void scx_share(smartcard_t *sc) { if (sc != NULL) sc->count++; } /* * adds a smartcard record to the chained list */ smartcard_t* scx_add(smartcard_t *smartcard) { smartcard_t *sc = smartcards; smartcard_t **psc = &smartcards; while (sc != NULL) { if (scx_same(smartcard, sc)) /* already in chain, free smartcard record */ { scx_free(smartcard); return sc; } psc = &sc->next; sc = sc->next; } /* insert new smartcard record at the end of the chain */ *psc = smartcard; smartcard->number = ++sc_number; smartcard->count = 1; DBG(DBG_CONTROL | DBG_PARSING, DBG_log(" smartcard #%d added", sc_number) ) return smartcard; } /* * get the smartcard that belongs to an X.509 certificate */ smartcard_t* scx_get(x509cert_t *cert) { smartcard_t *sc = smartcards; while (sc != NULL) { if (sc->last_cert.u.x509 == cert) return sc; sc = sc->next; } return NULL; } /* * prints either the slot number or 'any slot' */ char * scx_print_slot(smartcard_t *sc, const char *whitespace) { char *buf = temporary_cyclic_buffer(); if (sc->any_slot) snprintf(buf, BUF_LEN, "any slot"); else snprintf(buf, BUF_LEN, "slot: %s%lu", whitespace, sc->slot); return buf; } /* * list all smartcard info records in a chained list */ void scx_list(bool utc) { smartcard_t *sc = smartcards; if (sc != NULL) { whack_log(RC_COMMENT, " "); whack_log(RC_COMMENT, "List of Smartcard Objects:"); whack_log(RC_COMMENT, " "); } while (sc != NULL) { whack_log(RC_COMMENT, "%T, #%d, count: %d" , &sc->last_load, utc , sc->number , sc->count); whack_log(RC_COMMENT, " %s, session %s, logged %s, has %s" , scx_print_slot(sc, " ") , sc->session_opened? "opened" : "closed" , sc->logged_in? "in" : "out" , sc->pinpad? "pin pad" : ((sc->pin.ptr == NULL)? "no pin" : sc->valid? "valid pin" : "invalid pin")); if (sc->id != NULL) whack_log(RC_COMMENT, " id: %s", sc->id); if (sc->label != NULL) whack_log(RC_COMMENT, " label: '%s'", sc->label); if (sc->last_cert.type == CERT_X509_SIGNATURE) { char buf[BUF_LEN]; dntoa(buf, BUF_LEN, sc->last_cert.u.x509->subject); whack_log(RC_COMMENT, " subject: '%s'", buf); } sc = sc->next; } }