diff options
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | lib/includes/gnutls/pkcs11.h | 4 | ||||
-rw-r--r-- | lib/pkcs11.c | 268 | ||||
-rw-r--r-- | lib/pkcs11_int.h | 4 | ||||
-rw-r--r-- | lib/pkcs11_privkey.c | 2 | ||||
-rw-r--r-- | lib/pkcs11_write.c | 2 |
6 files changed, 202 insertions, 80 deletions
diff --git a/configure.ac b/configure.ac index 14d15fbeba..15cc5db932 100644 --- a/configure.ac +++ b/configure.ac @@ -126,7 +126,7 @@ AC_ARG_WITH(p11-kit, [Build without p11-kit and PKCS#11 support])) AM_CONDITIONAL(ENABLE_PKCS11, test "$with_p11_kit" != "no") if test "$with_p11_kit" != "no"; then - PKG_CHECK_MODULES(P11_KIT, [p11-kit-1]) + PKG_CHECK_MODULES(P11_KIT, [p11-kit-1 >= 0.2]) AC_DEFINE(ENABLE_PKCS11, 1, [Build PKCS#11 support]) CFLAGS="$CFLAGS $P11_KIT_CFLAGS" LIBS="$LIBS $P11_KIT_LIBS" diff --git a/lib/includes/gnutls/pkcs11.h b/lib/includes/gnutls/pkcs11.h index edcea28bbb..647b2414c4 100644 --- a/lib/includes/gnutls/pkcs11.h +++ b/lib/includes/gnutls/pkcs11.h @@ -23,6 +23,7 @@ typedef int (*gnutls_pkcs11_token_callback_t) (void *const global_data, * gnutls_pkcs11_pin_flag_t: * @GNUTLS_PKCS11_PIN_USER: The PIN for the user. * @GNUTLS_PKCS11_PIN_SO: The PIN for the security officer. + * @GNUTLS_PKCS11_PIN_CONTEXT_SPECIFIC: The PIN is for a specific action and key like signing. * @GNUTLS_PKCS11_PIN_FINAL_TRY: This is the final try before blocking. * @GNUTLS_PKCS11_PIN_COUNT_LOW: Few tries remain before token blocks. * @@ -32,8 +33,9 @@ typedef enum { GNUTLS_PKCS11_PIN_USER = (1 << 0), GNUTLS_PKCS11_PIN_SO = (1 << 1), + GNUTLS_PKCS11_PIN_CONTEXT_SPECIFIC = (1 << 4), GNUTLS_PKCS11_PIN_FINAL_TRY = (1 << 2), - GNUTLS_PKCS11_PIN_COUNT_LOW = (1 << 3) + GNUTLS_PKCS11_PIN_COUNT_LOW = (1 << 3), } gnutls_pkcs11_pin_flag_t; typedef int (*gnutls_pkcs11_pin_callback_t) (void *userdata, int attempt, diff --git a/lib/pkcs11.c b/lib/pkcs11.c index 66bd3efe03..85ea78939b 100644 --- a/lib/pkcs11.c +++ b/lib/pkcs11.c @@ -31,6 +31,7 @@ #include <gnutls_datum.h> #include <pkcs11_int.h> #include <p11-kit/p11-kit.h> +#include <p11-kit/pin.h> #define MAX_PROVIDERS 16 @@ -891,7 +892,7 @@ pkcs11_open_session (struct ck_function_list ** _module, ck_session_handle_t * _ if (flags & SESSION_LOGIN) { - ret = pkcs11_login (module, pks, &tinfo, (flags & SESSION_SO) ? 1 : 0); + ret = pkcs11_login (module, pks, &tinfo, info, (flags & SESSION_SO) ? 1 : 0); if (ret < 0) { gnutls_assert (); @@ -909,7 +910,7 @@ pkcs11_open_session (struct ck_function_list ** _module, ck_session_handle_t * _ int _pkcs11_traverse_tokens (find_func_t find_func, void *input, - unsigned int flags) + struct p11_kit_uri *info, unsigned int flags) { ck_rv_t rv; int found = 0, x, z, ret; @@ -921,20 +922,20 @@ _pkcs11_traverse_tokens (find_func_t find_func, void *input, module = providers[x].module; for (z = 0; z < providers[x].nslots; z++) { - struct token_info info; + struct token_info tinfo; ret = GNUTLS_E_PKCS11_ERROR; if (pkcs11_get_token_info (module, providers[x].slots[z], - &info.tinfo) != CKR_OK) + &tinfo.tinfo) != CKR_OK) { continue; } - info.sid = providers[x].slots[z]; - info.prov = &providers[x]; + tinfo.sid = providers[x].slots[z]; + tinfo.prov = &providers[x]; if (pkcs11_get_slot_info (module, providers[x].slots[z], - &info.sinfo) != CKR_OK) + &tinfo.sinfo) != CKR_OK) { continue; } @@ -950,7 +951,7 @@ _pkcs11_traverse_tokens (find_func_t find_func, void *input, if (flags & SESSION_LOGIN) { - ret = pkcs11_login (module, pks, &info, (flags & SESSION_SO) ? 1 : 0); + ret = pkcs11_login (module, pks, &tinfo, info, (flags & SESSION_SO) ? 1 : 0); if (ret < 0) { gnutls_assert (); @@ -958,7 +959,7 @@ _pkcs11_traverse_tokens (find_func_t find_func, void *input, } } - ret = find_func (module, pks, &info, &providers[x].info, input); + ret = find_func (module, pks, &tinfo, &providers[x].info, input); if (ret == 0) { @@ -1478,7 +1479,7 @@ gnutls_pkcs11_obj_import_url (gnutls_pkcs11_obj_t cert, const char *url, } ret = - _pkcs11_traverse_tokens (find_obj_url, &find_data, + _pkcs11_traverse_tokens (find_obj_url, &find_data, cert->info, pkcs11_obj_flags_to_int (flags)); if (ret < 0) @@ -1549,7 +1550,7 @@ gnutls_pkcs11_token_get_url (unsigned int seq, tn.seq = seq; tn.info = p11_kit_uri_new (); - ret = _pkcs11_traverse_tokens (find_token_num, &tn, 0); + ret = _pkcs11_traverse_tokens (find_token_num, &tn, NULL, 0); if (ret < 0) { p11_kit_uri_free (tn.info); @@ -1689,39 +1690,193 @@ struct pkey_list size_t key_ids_size; }; -int -pkcs11_login (struct ck_function_list * module, ck_session_handle_t pks, - const struct token_info *info, int so) + +static int +retrieve_pin_for_pinfile (const char *pinfile, struct ck_token_info *token_info, + int attempts, ck_user_type_t user_type, struct p11_kit_pin **pin) { - int attempt = 0, ret; - ck_rv_t rv; - char *token_url; - int pin_len; - struct p11_kit_uri *uinfo; + unsigned int flags = 0; + struct p11_kit_uri *token_uri; + struct p11_kit_pin *result; char *label; - if (so == 0 && (info->tinfo.flags & CKF_LOGIN_REQUIRED) == 0) + label = p11_kit_space_strdup (token_info->label, sizeof (token_info->label)); + if (label == NULL) { gnutls_assert (); - _gnutls_debug_log ("pk11: No login required.\n"); - return 0; + return GNUTLS_E_MEMORY_ERROR; + } + + token_uri = p11_kit_uri_new (); + if (token_uri == NULL) + { + free (label); + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + memcpy (p11_kit_uri_get_token_info (token_uri), token_info, + sizeof (struct ck_token_info)); + + if (attempts) + flags |= P11_KIT_PIN_FLAGS_RETRY; + if (user_type == CKU_USER) + { + flags |= P11_KIT_PIN_FLAGS_USER_LOGIN; + if (token_info->flags & CKF_USER_PIN_COUNT_LOW) + flags |= P11_KIT_PIN_FLAGS_MANY_TRIES; + if (token_info->flags & CKF_USER_PIN_FINAL_TRY) + flags |= P11_KIT_PIN_FLAGS_FINAL_TRY; + } + else if (user_type == CKU_SO) + { + flags |= P11_KIT_PIN_FLAGS_SO_LOGIN; + if (token_info->flags & CKF_SO_PIN_COUNT_LOW) + flags |= P11_KIT_PIN_FLAGS_MANY_TRIES; + if (token_info->flags & CKF_SO_PIN_FINAL_TRY) + flags |= P11_KIT_PIN_FLAGS_FINAL_TRY; + } + else if (user_type == CKU_CONTEXT_SPECIFIC) + { + flags |= P11_KIT_PIN_FLAGS_CONTEXT_LOGIN; + } + + result = p11_kit_pin_request (pinfile, token_uri, label, flags); + p11_kit_uri_free (token_uri); + free (label); + + if (result == NULL) + { + gnutls_assert (); + return GNUTLS_E_PKCS11_PIN_ERROR; + } + + *pin = result; + return 0; +} + +static int +retrieve_pin_for_callback (struct ck_token_info *token_info, int attempts, + ck_user_type_t user_type, struct p11_kit_pin **pin) +{ + char pin_value[GNUTLS_PKCS11_MAX_PIN_LEN]; + unsigned int flags = 0; + char *token_str; + char *label; + struct p11_kit_uri *token_uri; + int ret = 0; + + label = p11_kit_space_strdup (token_info->label, sizeof (token_info->label)); + if (label == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; } - uinfo = p11_kit_uri_new (); - memcpy (p11_kit_uri_get_token_info (uinfo), &info->tinfo, sizeof (struct ck_token_info)); - ret = pkcs11_info_to_url (uinfo, 1, &token_url); - p11_kit_uri_free (uinfo); + token_uri = p11_kit_uri_new (); + if (token_uri == NULL) + { + free (label); + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + memcpy (p11_kit_uri_get_token_info (token_uri), token_info, + sizeof (struct ck_token_info)); + ret = pkcs11_info_to_url (token_uri, 1, &token_str); + p11_kit_uri_free (token_uri); if (ret < 0) { + free (label); gnutls_assert (); - return ret; + return GNUTLS_E_MEMORY_ERROR; + } + + if (user_type == CKU_USER) + { + flags |= GNUTLS_PKCS11_PIN_USER; + if (token_info->flags & CKF_USER_PIN_COUNT_LOW) + flags |= GNUTLS_PKCS11_PIN_COUNT_LOW; + if (token_info->flags & CKF_USER_PIN_FINAL_TRY) + flags |= GNUTLS_PKCS11_PIN_FINAL_TRY; + } + else if (user_type == CKU_SO) + { + flags |= GNUTLS_PKCS11_PIN_SO; + if (token_info->flags & CKF_SO_PIN_COUNT_LOW) + flags |= GNUTLS_PKCS11_PIN_COUNT_LOW; + if (token_info->flags & CKF_SO_PIN_FINAL_TRY) + flags |= GNUTLS_PKCS11_PIN_FINAL_TRY; + } + + ret = pin_func (pin_data, attempts, (char*)token_str, label, + flags, pin_value, GNUTLS_PKCS11_MAX_PIN_LEN); + free (token_str); + free (label); + + if (ret < 0) + { + gnutls_assert (); + return GNUTLS_E_PKCS11_PIN_ERROR; + } + + *pin = p11_kit_pin_new_for_string (pin_value); + + /* Try to scrub the pin off the stack. Clever compilers will + * probably optimize this away, oh well. */ + memset (pin, 0, sizeof pin); + + return 0; +} + +static int +retrieve_pin (struct p11_kit_uri *info, struct ck_token_info *token_info, + int attempts, ck_user_type_t user_type, struct p11_kit_pin **pin) +{ + const char *pinfile; + + *pin = NULL; + + /* Check if a pinfile is specified, and use that if possible */ + pinfile = p11_kit_uri_get_pinfile (info); + if (pinfile != NULL) + return retrieve_pin_for_pinfile (pinfile, token_info, attempts, user_type, pin); + + /* The global gnutls pin callback */ + else if (pin_func) + return retrieve_pin_for_callback (token_info, attempts, user_type, pin); + + /* Otherwise, PIN entry is necessary for login, so fail if there's + * no callback. */ + else + { + gnutls_assert (); + _gnutls_debug_log ("pk11: No pin callback but login required.\n"); + return GNUTLS_E_PKCS11_ERROR; + } +} + +int +pkcs11_login (struct ck_function_list * module, ck_session_handle_t pks, + const struct token_info *tokinfo, struct p11_kit_uri *info, int so) +{ + int attempt = 0, ret; + ck_user_type_t user_type; + ck_rv_t rv; + + user_type = (so == 0) ? CKU_USER : CKU_SO; + if (so == 0 && (tokinfo->tinfo.flags & CKF_LOGIN_REQUIRED) == 0) + { + gnutls_assert (); + _gnutls_debug_log ("pk11: No login required.\n"); + return 0; } /* For a token with a "protected" (out-of-band) authentication * path, calling login with a NULL username is all that is * required. */ - if (info->tinfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH) + if (tokinfo->tinfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH) { rv = (module)->C_Login (pks, (so == 0) ? CKU_USER : CKU_SO, NULL, 0); if (rv == CKR_OK || rv == CKR_USER_ALREADY_LOGGED_IN) @@ -1737,30 +1892,19 @@ pkcs11_login (struct ck_function_list * module, ck_session_handle_t pks, } } - /* Otherwise, PIN entry is necessary for login, so fail if there's - * no callback. */ - if (!pin_func) - { - gnutls_assert (); - _gnutls_debug_log ("pk11: No pin callback but login required.\n"); - ret = GNUTLS_E_PKCS11_ERROR; - goto cleanup; - } - do { + struct p11_kit_pin *pin; struct ck_token_info tinfo; - char pin[GNUTLS_PKCS11_MAX_PIN_LEN]; - unsigned int flags; - memcpy(&tinfo, &info->tinfo, sizeof(tinfo)); + memcpy (&tinfo, &tokinfo->tinfo, sizeof(tinfo)); /* If login has been attempted once already, check the token * status again, the flags might change. */ if (attempt) { if (pkcs11_get_token_info - (info->prov->module, info->sid, &tinfo) != CKR_OK) + (tokinfo->prov->module, tokinfo->sid, &tinfo) != CKR_OK) { gnutls_assert (); _gnutls_debug_log ("pk11: GetTokenInfo failed\n"); @@ -1769,43 +1913,18 @@ pkcs11_login (struct ck_function_list * module, ck_session_handle_t pks, } } - flags = 0; - if (so == 0) - { - flags |= GNUTLS_PKCS11_PIN_USER; - if (tinfo.flags & CKF_USER_PIN_COUNT_LOW) - flags |= GNUTLS_PKCS11_PIN_COUNT_LOW; - if (tinfo.flags & CKF_USER_PIN_FINAL_TRY) - flags |= GNUTLS_PKCS11_PIN_FINAL_TRY; - } - else - { - flags |= GNUTLS_PKCS11_PIN_SO; - if (tinfo.flags & CKF_SO_PIN_COUNT_LOW) - flags |= GNUTLS_PKCS11_PIN_COUNT_LOW; - if (tinfo.flags & CKF_SO_PIN_FINAL_TRY) - flags |= GNUTLS_PKCS11_PIN_FINAL_TRY; - } - - label = p11_kit_space_strdup (info->tinfo.label, sizeof (info->tinfo.label)); - ret = pin_func (pin_data, attempt++, - (char *) token_url, label, flags, pin, sizeof (pin)); - free (label); - + ret = retrieve_pin (info, &tinfo, attempt, user_type, &pin); if (ret < 0) { gnutls_assert (); - ret = GNUTLS_E_PKCS11_PIN_ERROR; goto cleanup; } - pin_len = strlen (pin); - rv = (module)->C_Login (pks, (so == 0) ? CKU_USER : CKU_SO, - (unsigned char *) pin, pin_len); + rv = (module)->C_Login (pks, user_type, + (unsigned char *)p11_kit_pin_get_value (pin, NULL), + p11_kit_pin_get_length (pin)); - /* Try to scrub the pin off the stack. Clever compilers will - * probably optimize this away, oh well. */ - memset (pin, 0, sizeof pin); + p11_kit_pin_unref (pin); } while (rv == CKR_PIN_INCORRECT); @@ -1816,7 +1935,6 @@ pkcs11_login (struct ck_function_list * module, ck_session_handle_t pks, || rv == CKR_USER_ALREADY_LOGGED_IN) ? 0 : pkcs11_rv_to_err (rv); cleanup: - gnutls_free (token_url); return ret; } @@ -2271,7 +2389,7 @@ gnutls_pkcs11_obj_list_import_url (gnutls_pkcs11_obj_t * p_list, } ret = - _pkcs11_traverse_tokens (find_objs, &find_data, + _pkcs11_traverse_tokens (find_objs, &find_data, find_data.info, pkcs11_obj_flags_to_int (flags)); p11_kit_uri_free (find_data.info); @@ -2453,7 +2571,7 @@ gnutls_pkcs11_token_get_flags (const char *url, unsigned int *flags) return ret; } - ret = _pkcs11_traverse_tokens (find_flags, &find_data, 0); + ret = _pkcs11_traverse_tokens (find_flags, &find_data, find_data.info, 0); p11_kit_uri_free (find_data.info); if (ret < 0) diff --git a/lib/pkcs11_int.h b/lib/pkcs11_int.h index 7ff7869c1e..cd62904889 100644 --- a/lib/pkcs11_int.h +++ b/lib/pkcs11_int.h @@ -53,7 +53,7 @@ int pkcs11_get_info (struct p11_kit_uri *info, gnutls_pkcs11_obj_info_t itype, void *output, size_t * output_size); int pkcs11_login (struct ck_function_list * module, ck_session_handle_t pks, - const struct token_info *info, int admin); + const struct token_info *tinfo, struct p11_kit_uri *info, int admin); int pkcs11_call_token_func (struct p11_kit_uri *info, const unsigned retry); @@ -70,7 +70,7 @@ int pkcs11_info_to_url (struct p11_kit_uri *info, int pkcs11_open_session (struct ck_function_list **_module, ck_session_handle_t * _pks, struct p11_kit_uri *info, unsigned int flags); int _pkcs11_traverse_tokens (find_func_t find_func, void *input, - unsigned int flags); + struct p11_kit_uri *info, unsigned int flags); ck_object_class_t pkcs11_strtype_to_class (const char *type); int pkcs11_token_matches_info (struct p11_kit_uri *info, diff --git a/lib/pkcs11_privkey.c b/lib/pkcs11_privkey.c index cef8a60dc7..e1eea0fd02 100644 --- a/lib/pkcs11_privkey.c +++ b/lib/pkcs11_privkey.c @@ -33,6 +33,8 @@ struct gnutls_pkcs11_privkey_st gnutls_pk_algorithm_t pk_algorithm; unsigned int flags; struct p11_kit_uri *info; + gnutls_pkcs11_pin_callback_t pin_func; + void *pin_data; }; /** diff --git a/lib/pkcs11_write.c b/lib/pkcs11_write.c index ed1ee08e90..3665454096 100644 --- a/lib/pkcs11_write.c +++ b/lib/pkcs11_write.c @@ -626,7 +626,7 @@ gnutls_pkcs11_delete_url (const char *object_url, unsigned int flags) } ret = - _pkcs11_traverse_tokens (delete_obj_url, &find_data, + _pkcs11_traverse_tokens (delete_obj_url, &find_data, find_data.info, SESSION_WRITE | pkcs11_obj_flags_to_int (flags)); p11_kit_uri_free (find_data.info); |