From 83ef7537998f2f307a7bfe7314d647d3242daf00 Mon Sep 17 00:00:00 2001 From: Frank Morgner Date: Thu, 26 Nov 2015 07:11:06 +0100 Subject: [PATCH] Implemented atomic PKCS#11 transactions --- etc/opensc.conf.in | 21 ++++++ src/libopensc/libopensc.exports | 1 + src/pkcs11/misc.c | 116 +++++++++++++++++++++++++++++++- src/pkcs11/pkcs11-global.c | 2 + src/pkcs11/pkcs11-object.c | 101 +++++++++++++++++++++------ src/pkcs11/pkcs11-session.c | 63 +++++++++++++---- src/pkcs11/sc-pkcs11.h | 10 +++ src/pkcs11/slot.c | 2 + 8 files changed, 281 insertions(+), 35 deletions(-) diff --git a/etc/opensc.conf.in b/etc/opensc.conf.in index fad82f7f..3e8c727a 100644 --- a/etc/opensc.conf.in +++ b/etc/opensc.conf.in @@ -594,6 +594,27 @@ app opensc-pkcs11 { # Default: false # lock_login = true; + # By default, interacting with the OpenSC PKCS#11 module may change the + # state of the token, e.g. whether a user is logged in or not. + # + # Thus other users or other applications may change or use the state of + # the token unknowingly. Other applications may create signatures + # abusing an existing login or they may logout unnoticed. + # + # With this setting enabled the login state of the token is tracked and + # cached (including the PIN). Every transaction is preceeded by + # restoring the login state. After every transaction a logout is + # performed. This setting by default also enables `lock_login` (see + # above) to disable access for other applications during the atomic + # transactions. + # + # Please note that any PIN-pad should be disabled (see `enable_pinpad` + # above), because the user would have to input his PIN for every + # transaction. + # + # Default: false + # atomic = true; + # With this setting disabled, the OpenSC PKCS#11 module will initialize # the slots available when the application calls `C_GetSlotList`. With # this setting enabled, the slots will also get initialized when diff --git a/src/libopensc/libopensc.exports b/src/libopensc/libopensc.exports index cea8d42f..e7f725bb 100644 --- a/src/libopensc/libopensc.exports +++ b/src/libopensc/libopensc.exports @@ -125,6 +125,7 @@ sc_list_files sc_lock sc_logout sc_make_cache_dir +sc_mem_alloc_secure sc_mem_clear sc_mem_reverse sc_match_atr_block diff --git a/src/pkcs11/misc.c b/src/pkcs11/misc.c index 2ade4f5d..f76ca819 100644 --- a/src/pkcs11/misc.c +++ b/src/pkcs11/misc.c @@ -127,6 +127,114 @@ CK_RV sc_to_cryptoki_error(int rc, const char *ctx) } +struct sc_pkcs11_login { + CK_USER_TYPE userType; + CK_CHAR_PTR pPin; + CK_ULONG ulPinLen; +}; + +CK_RV restore_login_state(struct sc_pkcs11_slot *slot) +{ + CK_RV r = CKR_OK; + + if (sc_pkcs11_conf.atomic && slot) { + if (list_iterator_start(&slot->logins)) { + struct sc_pkcs11_login *login = list_iterator_next(&slot->logins); + while (login) { + r = slot->p11card->framework->login(slot, login->userType, + login->pPin, login->ulPinLen); + if (r != CKR_OK) + break; + login = list_iterator_next(&slot->logins); + } + list_iterator_stop(&slot->logins); + } + } + + return r; +} + +CK_RV reset_login_state(struct sc_pkcs11_slot *slot) +{ + if (sc_pkcs11_conf.atomic + && slot && slot->p11card && slot->p11card->framework) { + slot->p11card->framework->logout(slot); + } + + return CKR_OK; +} + +CK_RV push_login_state(struct sc_pkcs11_slot *slot, + CK_USER_TYPE userType, CK_CHAR_PTR pPin, CK_ULONG ulPinLen) +{ + CK_RV r = CKR_HOST_MEMORY; + struct sc_pkcs11_login *login = NULL; + + if (!sc_pkcs11_conf.atomic || !slot) { + r = CKR_OK; + goto err; + } + + login = (struct sc_pkcs11_login *) malloc(sizeof *login); + if (login == NULL) { + goto err; + } + + login->pPin = sc_mem_alloc_secure(context, (sizeof *pPin)*ulPinLen); + if (login->pPin == NULL) { + goto err; + } + memcpy(login->pPin, pPin, (sizeof *pPin)*ulPinLen); + login->ulPinLen = ulPinLen; + login->userType = userType; + + if (0 > list_append(&slot->logins, login)) { + goto err; + } + + r = CKR_OK; + +err: + if (r != CKR_OK && login) { + sc_mem_clear(login->pPin, login->ulPinLen); + free(login->pPin); + free(login); + } + + return r; +} + +void pop_login_state(struct sc_pkcs11_slot *slot) +{ + if (slot) { + unsigned int size = list_size(&slot->logins); + if (size > 0) { + struct sc_pkcs11_login *login = list_get_at(&slot->logins, size-1); + if (login) { + sc_mem_clear(login->pPin, login->ulPinLen); + free(login->pPin); + free(login); + } + if (0 > list_delete_at(&slot->logins, size-1)) + sc_log(context, "Error deleting login state"); + } + } +} + +void pop_all_login_states(struct sc_pkcs11_slot *slot) +{ + if (sc_pkcs11_conf.atomic && slot) { + struct sc_pkcs11_login *login = list_fetch(&slot->logins); + while (login) { + sc_mem_clear(login->pPin, login->ulPinLen); + free(login->pPin); + free(login); + login = list_fetch(&slot->logins); + } + } +} + + /* Session manipulation */ CK_RV session_start_operation(struct sc_pkcs11_session * session, int type, sc_pkcs11_mechanism_type_t * mech, struct sc_pkcs11_operation ** operation) @@ -323,6 +431,7 @@ void load_pkcs11_parameters(struct sc_pkcs11_config *conf, sc_context_t * ctx) conf->slots_per_card = 4; } conf->hide_empty_tokens = 1; + conf->atomic = 0; conf->lock_login = 0; conf->init_sloppy = 1; conf->pin_unblock_style = SC_PKCS11_PIN_UNBLOCK_NOT_ALLOWED; @@ -339,6 +448,9 @@ void load_pkcs11_parameters(struct sc_pkcs11_config *conf, sc_context_t * ctx) conf->max_virtual_slots = scconf_get_int(conf_block, "max_virtual_slots", conf->max_virtual_slots); conf->slots_per_card = scconf_get_int(conf_block, "slots_per_card", conf->slots_per_card); conf->hide_empty_tokens = scconf_get_bool(conf_block, "hide_empty_tokens", conf->hide_empty_tokens); + conf->atomic = scconf_get_bool(conf_block, "atomic", conf->atomic); + if (conf->atomic) + conf->lock_login = 1; conf->lock_login = scconf_get_bool(conf_block, "lock_login", conf->lock_login); conf->init_sloppy = scconf_get_bool(conf_block, "init_sloppy", conf->init_sloppy); @@ -371,9 +483,9 @@ void load_pkcs11_parameters(struct sc_pkcs11_config *conf, sc_context_t * ctx) free(tmp); sc_log(ctx, "PKCS#11 options: plug_and_play=%d max_virtual_slots=%d slots_per_card=%d " - "hide_empty_tokens=%d lock_login=%d pin_unblock_style=%d " + "hide_empty_tokens=%d lock_login=%d atomic=%d pin_unblock_style=%d " "zero_ckaid_for_ca_certs=%d create_slots_flags=0x%X", conf->plug_and_play, conf->max_virtual_slots, conf->slots_per_card, - conf->hide_empty_tokens, conf->lock_login, conf->pin_unblock_style, + conf->hide_empty_tokens, conf->lock_login, conf->atomic, conf->pin_unblock_style, conf->zero_ckaid_for_ca_certs, conf->create_slots_flags); } diff --git a/src/pkcs11/pkcs11-global.c b/src/pkcs11/pkcs11-global.c index 86922b01..c492392f 100644 --- a/src/pkcs11/pkcs11-global.c +++ b/src/pkcs11/pkcs11-global.c @@ -324,6 +324,8 @@ CK_RV C_Finalize(CK_VOID_PTR pReserved) while ((slot = list_fetch(&virtual_slots))) { list_destroy(&slot->objects); + pop_all_login_states(slot); + list_destroy(&slot->logins); free(slot); } list_destroy(&virtual_slots); diff --git a/src/pkcs11/pkcs11-object.c b/src/pkcs11/pkcs11-object.c index 41bfe5c0..72bee2d7 100644 --- a/src/pkcs11/pkcs11-object.c +++ b/src/pkcs11/pkcs11-object.c @@ -541,7 +541,8 @@ C_Digest(CK_SESSION_HANDLE hSession, /* the session's handle */ if (rv == CKR_OK) rv = sc_pkcs11_md_final(session, pDigest, pulDigestLen); -out: sc_log(context, "C_Digest() = %s", lookup_enum ( RV_T, rv )); +out: + sc_log(context, "C_Digest() = %s", lookup_enum ( RV_T, rv )); sc_pkcs11_unlock(); return rv; } @@ -685,8 +686,16 @@ C_Sign(CK_SESSION_HANDLE hSession, /* the session's handle */ } rv = sc_pkcs11_sign_update(session, pData, ulDataLen); - if (rv == CKR_OK) - rv = sc_pkcs11_sign_final(session, pSignature, pulSignatureLen); + if (rv == CKR_OK) { + rv = restore_login_state(session->slot); + if (rv == CKR_OK) + rv = sc_pkcs11_sign_final(session, pSignature, pulSignatureLen); + if (rv == CKR_OK) { + rv = reset_login_state(session->slot); + } else { + reset_login_state(session->slot); + } + } out: sc_log(context, "C_Sign() = %s", lookup_enum ( RV_T, rv )); @@ -746,7 +755,14 @@ C_SignFinal(CK_SESSION_HANDLE hSession, /* the session's handle */ *pulSignatureLen = length; rv = pSignature ? CKR_BUFFER_TOO_SMALL : CKR_OK; } else { - rv = sc_pkcs11_sign_final(session, pSignature, pulSignatureLen); + rv = restore_login_state(session->slot); + if (rv == CKR_OK) + rv = sc_pkcs11_sign_final(session, pSignature, pulSignatureLen); + if (rv == CKR_OK) { + rv = reset_login_state(session->slot); + } else { + reset_login_state(session->slot); + } } out: @@ -859,7 +875,8 @@ CK_RV C_DecryptInit(CK_SESSION_HANDLE hSession, /* the session's handle */ rv = sc_pkcs11_decr_init(session, pMechanism, object, key_type); -out: sc_log(context, "C_DecryptInit() = %s", lookup_enum ( RV_T, rv )); +out: + sc_log(context, "C_DecryptInit() = %s", lookup_enum ( RV_T, rv )); sc_pkcs11_unlock(); return rv; } @@ -878,9 +895,18 @@ CK_RV C_Decrypt(CK_SESSION_HANDLE hSession, /* the session's handle */ return rv; rv = get_session(hSession, &session); - if (rv == CKR_OK) - rv = sc_pkcs11_decr(session, pEncryptedData, ulEncryptedDataLen, - pData, pulDataLen); + if (rv == CKR_OK) { + rv = restore_login_state(session->slot); + if (rv == CKR_OK) { + rv = sc_pkcs11_decr(session, pEncryptedData, + ulEncryptedDataLen, pData, pulDataLen); + } + if (rv == CKR_OK) { + rv = reset_login_state(session->slot); + } else { + reset_login_state(session->slot); + } + } sc_log(context, "C_Decrypt() = %s", lookup_enum ( RV_T, rv )); sc_pkcs11_unlock(); @@ -985,11 +1011,19 @@ CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE hSession, /* the session's handle */ slot = session->slot; if (slot->p11card->framework->gen_keypair == NULL) rv = CKR_FUNCTION_NOT_SUPPORTED; - else - rv = slot->p11card->framework->gen_keypair(slot, pMechanism, - pPublicKeyTemplate, ulPublicKeyAttributeCount, - pPrivateKeyTemplate, ulPrivateKeyAttributeCount, - phPublicKey, phPrivateKey); + else { + rv = restore_login_state(slot); + if (rv == CKR_OK) + rv = slot->p11card->framework->gen_keypair(slot, pMechanism, + pPublicKeyTemplate, ulPublicKeyAttributeCount, + pPrivateKeyTemplate, ulPrivateKeyAttributeCount, + phPublicKey, phPrivateKey); + if (rv == CKR_OK) { + rv = reset_login_state(session->slot); + } else { + reset_login_state(session->slot); + } + } out: sc_pkcs11_unlock(); @@ -1084,9 +1118,16 @@ CK_RV C_DeriveKey(CK_SESSION_HANDLE hSession, /* the session's handle */ goto out; } - rv = sc_pkcs11_deri(session, pMechanism, object, key_type, - hSession, *phKey, key_object); + rv = restore_login_state(session->slot); + if (rv == CKR_OK) + rv = sc_pkcs11_deri(session, pMechanism, object, key_type, + hSession, *phKey, key_object); /* TODO if (rv != CK_OK) need to destroy the object */ + if (rv == CKR_OK) { + rv = reset_login_state(session->slot); + } else { + reset_login_state(session->slot); + } break; default: @@ -1175,7 +1216,8 @@ CK_RV C_VerifyInit(CK_SESSION_HANDLE hSession, /* the session's handle */ rv = sc_pkcs11_verif_init(session, pMechanism, object, key_type); -out: sc_log(context, "C_VerifyInit() = %s", lookup_enum ( RV_T, rv )); +out: + sc_log(context, "C_VerifyInit() = %s", lookup_enum ( RV_T, rv )); sc_pkcs11_unlock(); return rv; #endif @@ -1202,10 +1244,19 @@ CK_RV C_Verify(CK_SESSION_HANDLE hSession, /* the session's handle */ goto out; rv = sc_pkcs11_verif_update(session, pData, ulDataLen); - if (rv == CKR_OK) - rv = sc_pkcs11_verif_final(session, pSignature, ulSignatureLen); + if (rv == CKR_OK) { + rv = restore_login_state(session->slot); + if (rv == CKR_OK) + rv = sc_pkcs11_verif_final(session, pSignature, ulSignatureLen); + if (rv == CKR_OK) { + rv = reset_login_state(session->slot); + } else { + reset_login_state(session->slot); + } + } -out: sc_log(context, "C_Verify() = %s", lookup_enum ( RV_T, rv )); +out: + sc_log(context, "C_Verify() = %s", lookup_enum ( RV_T, rv )); sc_pkcs11_unlock(); return rv; #endif @@ -1250,8 +1301,16 @@ CK_RV C_VerifyFinal(CK_SESSION_HANDLE hSession, /* the session's handle */ return rv; rv = get_session(hSession, &session); - if (rv == CKR_OK) - rv = sc_pkcs11_verif_final(session, pSignature, ulSignatureLen); + if (rv == CKR_OK) { + rv = restore_login_state(session->slot); + if (rv == CKR_OK) + rv = sc_pkcs11_verif_final(session, pSignature, ulSignatureLen); + if (rv == CKR_OK) { + rv = reset_login_state(session->slot); + } else { + reset_login_state(session->slot); + } + } sc_log(context, "C_VerifyFinal() = %s", lookup_enum ( RV_T, rv )); sc_pkcs11_unlock(); diff --git a/src/pkcs11/pkcs11-session.c b/src/pkcs11/pkcs11-session.c index c4ab857f..1ab9318f 100644 --- a/src/pkcs11/pkcs11-session.c +++ b/src/pkcs11/pkcs11-session.c @@ -107,7 +107,10 @@ static CK_RV sc_pkcs11_close_session(CK_SESSION_HANDLE hSession) slot->nsessions--; if (slot->nsessions == 0 && slot->login_user >= 0) { slot->login_user = -1; - slot->p11card->framework->logout(slot); + if (sc_pkcs11_conf.atomic) + pop_all_login_states(slot); + else + slot->p11card->framework->logout(slot); } if (list_delete(&sessions, session) != 0) @@ -166,7 +169,8 @@ CK_RV C_CloseAllSessions(CK_SLOT_ID slotID) rv = sc_pkcs11_close_all_sessions(slotID); - out:sc_pkcs11_unlock(); +out: + sc_pkcs11_unlock(); return rv; } @@ -269,10 +273,16 @@ CK_RV C_Login(CK_SESSION_HANDLE hSession, /* the session's handle */ if (userType == CKU_CONTEXT_SPECIFIC) { if (slot->login_user == -1) { rv = CKR_OPERATION_NOT_INITIALIZED; - goto out; } else { - rv = slot->p11card->framework->login(slot, userType, pPin, ulPinLen); + rv = restore_login_state(slot); + if (rv == CKR_OK) + rv = slot->p11card->framework->login(slot, userType, pPin, ulPinLen); + if (rv == CKR_OK) { + rv = reset_login_state(session->slot); + } else { + reset_login_state(session->slot); + } } } else { @@ -285,11 +295,20 @@ CK_RV C_Login(CK_SESSION_HANDLE hSession, /* the session's handle */ goto out; } - sc_log(context, "C_Login() userType %li", userType); - rv = slot->p11card->framework->login(slot, userType, pPin, ulPinLen); - sc_log(context, "fLogin() rv %li", rv); + rv = restore_login_state(slot); + if (rv == CKR_OK) { + sc_log(context, "C_Login() userType %li", userType); + rv = slot->p11card->framework->login(slot, userType, pPin, ulPinLen); + sc_log(context, "fLogin() rv %li", rv); + } if (rv == CKR_OK) + rv = push_login_state(slot, userType, pPin, ulPinLen); + if (rv == CKR_OK) { slot->login_user = userType; + rv = reset_login_state(session->slot); + } else { + reset_login_state(session->slot); + } } out: @@ -319,11 +338,15 @@ CK_RV C_Logout(CK_SESSION_HANDLE hSession) if (slot->login_user >= 0) { slot->login_user = -1; - rv = slot->p11card->framework->logout(slot); + if (sc_pkcs11_conf.atomic) + pop_all_login_states(slot); + else + rv = slot->p11card->framework->logout(slot); } else rv = CKR_USER_NOT_LOGGED_IN; - out:sc_pkcs11_unlock(); +out: + sc_pkcs11_unlock(); return rv; } @@ -358,8 +381,16 @@ CK_RV C_InitPIN(CK_SESSION_HANDLE hSession, CK_CHAR_PTR pPin, CK_ULONG ulPinLen) } else if (slot->p11card->framework->init_pin == NULL) { rv = CKR_FUNCTION_NOT_SUPPORTED; } else { - rv = slot->p11card->framework->init_pin(slot, pPin, ulPinLen); - sc_log(context, "C_InitPIN() init-pin result %li", rv); + rv = restore_login_state(slot); + if (rv == CKR_OK) { + rv = slot->p11card->framework->init_pin(slot, pPin, ulPinLen); + sc_log(context, "C_InitPIN() init-pin result %li", rv); + } + if (rv == CKR_OK) { + rv = reset_login_state(session->slot); + } else { + reset_login_state(session->slot); + } } out: @@ -395,7 +426,15 @@ CK_RV C_SetPIN(CK_SESSION_HANDLE hSession, goto out; } - rv = slot->p11card->framework->change_pin(slot, pOldPin, ulOldLen, pNewPin, ulNewLen); + rv = restore_login_state(slot); + if (rv == CKR_OK) + rv = slot->p11card->framework->change_pin(slot, pOldPin, ulOldLen, pNewPin, ulNewLen); + if (rv == CKR_OK) { + rv = reset_login_state(session->slot); + } else { + reset_login_state(session->slot); + } + out: sc_pkcs11_unlock(); return rv; diff --git a/src/pkcs11/sc-pkcs11.h b/src/pkcs11/sc-pkcs11.h index 22e08235..f71af94b 100644 --- a/src/pkcs11/sc-pkcs11.h +++ b/src/pkcs11/sc-pkcs11.h @@ -78,6 +78,7 @@ struct sc_pkcs11_config { unsigned int slots_per_card; unsigned char hide_empty_tokens; unsigned char lock_login; + unsigned char atomic; unsigned char init_sloppy; unsigned int pin_unblock_style; unsigned int create_puk_slot; @@ -218,6 +219,7 @@ struct sc_pkcs11_slot { int fw_data_idx; /* Index of framework data */ struct sc_app_info *app_info; /* Application assosiated to slot */ + list_t logins; /* tracks all calls to C_Login if atomic operations are requested */ }; typedef struct sc_pkcs11_slot sc_pkcs11_slot_t; @@ -348,6 +350,14 @@ CK_RV slot_token_removed(CK_SLOT_ID id); CK_RV slot_allocate(struct sc_pkcs11_slot **, struct sc_pkcs11_card *); CK_RV slot_find_changed(CK_SLOT_ID_PTR idp, int mask); +/* Login tracking functions */ +CK_RV restore_login_state(struct sc_pkcs11_slot *slot); +CK_RV reset_login_state(struct sc_pkcs11_slot *slot); +CK_RV push_login_state(struct sc_pkcs11_slot *slot, + CK_USER_TYPE userType, CK_CHAR_PTR pPin, CK_ULONG ulPinLen); +void pop_login_state(struct sc_pkcs11_slot *slot); +void pop_all_login_states(struct sc_pkcs11_slot *slot); + /* Session manipulation */ CK_RV get_session(CK_SESSION_HANDLE hSession, struct sc_pkcs11_session ** session); CK_RV session_start_operation(struct sc_pkcs11_session *, diff --git a/src/pkcs11/slot.c b/src/pkcs11/slot.c index 857252c3..6806da13 100644 --- a/src/pkcs11/slot.c +++ b/src/pkcs11/slot.c @@ -92,6 +92,8 @@ CK_RV create_slot(sc_reader_t *reader) list_init(&slot->objects); list_attributes_seeker(&slot->objects, object_list_seeker); + list_init(&slot->logins); + init_slot_info(&slot->slot_info); if (reader != NULL) { slot->reader = reader;