From: Nikos Mavrogiannopoulos Date: Sun, 7 Nov 2010 10:08:51 +0000 (+0100) Subject: Added gnutls_pkcs11_token_set_pin() and gnutls_pkcs11_token_init() to enable X-Git-Tag: gnutls_2_11_5~58 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=dcb25ecf88baa1096df91ebf915e984c7a406c22;p=thirdparty%2Fgnutls.git Added gnutls_pkcs11_token_set_pin() and gnutls_pkcs11_token_init() to enable manipulating tokens purely from PKCS #11. --- diff --git a/NEWS b/NEWS index 6887cef58c..65e721eee1 100644 --- a/NEWS +++ b/NEWS @@ -5,12 +5,15 @@ See the end for copying conditions. * Version 2.11.5 (unreleased) +** p11tool: Introduced. It allows manipulating pkcs 11 tokens. + ** gnutls-cli: Print channel binding only in verbose mode. Before it printed it after the 'Compression:' output, thus breaking Emacs starttls.el string searches. ** API and ABI modifications: -No changes since last version. +gnutls_pkcs11_token_init: New function +gnutls_pkcs11_token_set_pin: New function * Version 2.11.4 (released 2010-10-15) diff --git a/lib/Makefile.am b/lib/Makefile.am index 13531d9857..d177253363 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -80,7 +80,8 @@ COBJECTS = gnutls_record.c gnutls_compress.c debug.c gnutls_cipher.c \ auth_dh_common.c gnutls_helper.c gnutls_supplemental.c \ crypto.c random.c ext_signature.c cryptodev.c system.c \ crypto-api.c ext_safe_renegotiation.c gnutls_privkey.c \ - pkcs11.c pkcs11_privkey.c gnutls_pubkey.c pkcs11_write.c locks.c + pkcs11.c pkcs11_privkey.c gnutls_pubkey.c pkcs11_write.c locks.c \ + pkcs11_secret.c if ENABLE_NETTLE diff --git a/lib/includes/gnutls/pkcs11.h b/lib/includes/gnutls/pkcs11.h index 95c74d62d0..d801f0a6c5 100644 --- a/lib/includes/gnutls/pkcs11.h +++ b/lib/includes/gnutls/pkcs11.h @@ -20,13 +20,19 @@ typedef int (*gnutls_pkcs11_token_callback_t) (void *const global_data, const unsigned retry); /* flags */ -#define GNUTLS_PKCS11_PIN_FINAL_TRY (1<<0) -#define GNUTLS_PKCS11_PIN_COUNT_LOW (1<<1) +typedef enum +{ + GNUTLS_PKCS11_PIN_USER = (1<<0), + GNUTLS_PKCS11_PIN_SO = (1<<1), + GNUTLS_PKCS11_PIN_FINAL_TRY = (1<<2), + GNUTLS_PKCS11_PIN_COUNT_LOW = (1<<3) +} gnutls_pkcs11_pin_flag_t; typedef int (*gnutls_pkcs11_pin_callback_t) (void *userdata, int attempt, const char *token_url, const char *token_label, - unsigned int flags, char *pin, + unsigned int flags /*gnutls_pkcs11_pin_flag_t*/, + char *pin, size_t pin_max); struct gnutls_pkcs11_obj_st; @@ -82,6 +88,12 @@ int gnutls_pkcs11_copy_x509_privkey (const char *token_url, gnutls_x509_privkey_ int gnutls_pkcs11_delete_url (const char *object_url, unsigned int flags /* GNUTLS_PKCS11_OBJ_FLAG_* */ ); +int gnutls_pkcs11_copy_secret_key (const char *token_url, gnutls_datum_t* key, + const char *label, + unsigned int key_usage /* GNUTLS_KEY_* */, + unsigned int flags + /* GNUTLS_PKCS11_OBJ_FLAG_* */ ); + typedef enum { GNUTLS_PKCS11_OBJ_ID_HEX = 1, @@ -130,6 +142,15 @@ typedef enum GNUTLS_PKCS11_OBJ_DATA } gnutls_pkcs11_obj_type_t; +int +gnutls_pkcs11_token_init (const char *token_url, + const char* so_pin, + const char *label); + +int +gnutls_pkcs11_token_set_pin (const char *token_url, + const char* oldpin, const char* newpin, unsigned int flags/*gnutls_pkcs11_pin_flag_t*/); + int gnutls_pkcs11_token_get_url (unsigned int seq, gnutls_pkcs11_url_type_t detailed, char **url); diff --git a/lib/libgnutls.map b/lib/libgnutls.map index 18981a1485..c9474f3eec 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -689,8 +689,10 @@ GNUTLS_2_12 gnutls_cipher_decrypt2; gnutls_openpgp_privkey_sec_param; gnutls_x509_privkey_sec_param; - gnutls_session_channel_binding; + gnutls_pkcs11_copy_secret_key; + gnutls_pkcs11_token_init; + gnutls_pkcs11_token_set_pin; } GNUTLS_2_10; GNUTLS_PRIVATE { diff --git a/lib/pkcs11.c b/lib/pkcs11.c index c825fb42a5..170835fb73 100644 --- a/lib/pkcs11.c +++ b/lib/pkcs11.c @@ -1088,12 +1088,10 @@ fix_strings (struct token_info *info) } int -pkcs11_open_session (pakchois_session_t ** _pks, - struct pkcs11_url_info *info, unsigned int flags) +pkcs11_find_slot (pakchois_module_t** module, ck_slot_id_t *slot, + struct pkcs11_url_info *info, struct token_info* _tinfo) { - ck_rv_t rv; - int x, z, ret; - pakchois_session_t *pks = NULL; + int x, z; for (x = 0; x < active_providers; x++) { @@ -1101,21 +1099,11 @@ pkcs11_open_session (pakchois_session_t ** _pks, { struct token_info tinfo; - rv = pakchois_open_session (providers[x].module, - providers[x].slots[z], - ((flags & SESSION_WRITE) - ? CKF_RW_SESSION : 0) | - CKF_SERIAL_SESSION, NULL, NULL, &pks); - if (rv != CKR_OK) - { - continue; - } - if (pakchois_get_token_info (providers[x].module, providers[x].slots[z], &tinfo.tinfo) != CKR_OK) { - goto next; + continue; } tinfo.sid = providers[x].slots[z]; tinfo.prov = &providers[x]; @@ -1124,7 +1112,7 @@ pkcs11_open_session (pakchois_session_t ** _pks, (providers[x].module, providers[x].slots[z], &tinfo.sinfo) != CKR_OK) { - goto next; + continue; } /* XXX make wrapper for token_info? */ @@ -1133,30 +1121,67 @@ pkcs11_open_session (pakchois_session_t ** _pks, if (pkcs11_token_matches_info (info, &tinfo.tinfo, &providers[x].info) < 0) { - goto next; - } - - if (flags & SESSION_LOGIN) - { - ret = pkcs11_login (pks, &tinfo); - if (ret < 0) - { - gnutls_assert (); - pakchois_close_session (pks); - return ret; - } + continue; } /* ok found */ - *_pks = pks; + *module = providers[x].module; + *slot = providers[x].slots[z]; + + if (_tinfo != NULL) + memcpy(_tinfo, &tinfo, sizeof(tinfo)); + return 0; + } + } - next: + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; +} + +int +pkcs11_open_session (pakchois_session_t ** _pks, + struct pkcs11_url_info *info, unsigned int flags) +{ + ck_rv_t rv; + int ret; + pakchois_session_t *pks = NULL; + pakchois_module_t *module; + ck_slot_id_t slot; + struct token_info tinfo; + + ret = pkcs11_find_slot(&module, &slot, info, &tinfo); + if (ret < 0) + { + gnutls_assert(); + return ret; + } + + rv = pakchois_open_session (module, + slot, + ((flags & SESSION_WRITE) + ? CKF_RW_SESSION : 0) | + CKF_SERIAL_SESSION, NULL, NULL, &pks); + if (rv != CKR_OK) + { + gnutls_assert(); + return pkcs11_rv_to_err (rv); + } + + if (flags & SESSION_LOGIN) + { + ret = pkcs11_login (pks, &tinfo, (flags & SESSION_SO)?1:0); + if (ret < 0) + { + gnutls_assert (); pakchois_close_session (pks); + return ret; } } - return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + /* ok found */ + *_pks = pks; + return 0; } @@ -1176,21 +1201,11 @@ _pkcs11_traverse_tokens (find_func_t find_func, void *input, ret = GNUTLS_E_PKCS11_ERROR; - rv = pakchois_open_session (providers[x].module, - providers[x].slots[z], - ((flags & SESSION_WRITE) - ? CKF_RW_SESSION : 0) | - CKF_SERIAL_SESSION, NULL, NULL, &pks); - if (rv != CKR_OK) - { - continue; - } - if (pakchois_get_token_info (providers[x].module, providers[x].slots[z], &info.tinfo) != CKR_OK) { - goto next; + continue; } info.sid = providers[x].slots[z]; info.prov = &providers[x]; @@ -1199,15 +1214,25 @@ _pkcs11_traverse_tokens (find_func_t find_func, void *input, (providers[x].module, providers[x].slots[z], &info.sinfo) != CKR_OK) { - goto next; + continue; } /* XXX make wrapper for token_info? */ fix_strings (&info); + rv = pakchois_open_session (providers[x].module, + providers[x].slots[z], + ((flags & SESSION_WRITE) + ? CKF_RW_SESSION : 0) | + CKF_SERIAL_SESSION, NULL, NULL, &pks); + if (rv != CKR_OK) + { + continue; + } + if (flags & SESSION_LOGIN) { - ret = pkcs11_login (pks, &info); + ret = pkcs11_login (pks, &info, (flags & SESSION_SO)?1:0); if (ret < 0) { gnutls_assert (); @@ -1217,8 +1242,6 @@ _pkcs11_traverse_tokens (find_func_t find_func, void *input, ret = find_func (pks, &info, &providers[x].info, input); - next: - if (ret == 0) { found = 1; @@ -2005,8 +2028,8 @@ struct pkey_list size_t key_ids_size; }; -int -pkcs11_login (pakchois_session_t * pks, const struct token_info *info) +int pkcs11_login (pakchois_session_t * pks, + const struct token_info *info, int so) { int attempt = 0, ret; ck_rv_t rv; @@ -2015,7 +2038,7 @@ pkcs11_login (pakchois_session_t * pks, const struct token_info *info) struct pkcs11_url_info uinfo; - if ((info->tinfo.flags & CKF_LOGIN_REQUIRED) == 0) + if (so == 0 && (info->tinfo.flags & CKF_LOGIN_REQUIRED) == 0) { gnutls_assert (); _gnutls_debug_log ("pk11: No login required.\n"); @@ -2039,7 +2062,7 @@ pkcs11_login (pakchois_session_t * pks, const struct token_info *info) * required. */ if (info->tinfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH) { - if (pakchois_login (pks, CKU_USER, NULL, 0) == CKR_OK) + if (pakchois_login (pks, (so==0)?CKU_USER:CKU_SO, NULL, 0) == CKR_OK) { return 0; } @@ -2083,10 +2106,22 @@ pkcs11_login (pakchois_session_t * pks, const struct token_info *info) } flags = 0; - 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; + 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; + } ret = pin_func (pin_data, attempt++, (char *) token_url, @@ -2099,7 +2134,8 @@ pkcs11_login (pakchois_session_t * pks, const struct token_info *info) } pin_len = strlen (pin); - rv = pakchois_login (pks, CKU_USER, (unsigned char *) pin, pin_len); + rv = pakchois_login (pks, (so==0)?CKU_USER:CKU_SO, + (unsigned char *) pin, pin_len); /* Try to scrub the pin off the stack. Clever compilers will * probably optimize this away, oh well. */ diff --git a/lib/pkcs11_int.h b/lib/pkcs11_int.h index 354bba3be7..11306e0dee 100644 --- a/lib/pkcs11_int.h +++ b/lib/pkcs11_int.h @@ -60,11 +60,15 @@ typedef int (*find_func_t) (pakchois_session_t * pks, int pkcs11_rv_to_err (ck_rv_t rv); int pkcs11_url_to_info (const char *url, struct pkcs11_url_info *info); +int +pkcs11_find_slot (pakchois_module_t** module, ck_slot_id_t *slot, + struct pkcs11_url_info *info, struct token_info* _tinfo); int pkcs11_get_info (struct pkcs11_url_info *info, gnutls_pkcs11_obj_info_t itype, void *output, size_t * output_size); -int pkcs11_login (pakchois_session_t * pks, const struct token_info *info); +int pkcs11_login (pakchois_session_t * pks, + const struct token_info *info, int admin); extern gnutls_pkcs11_token_callback_t token_func; extern void *token_data; @@ -73,8 +77,9 @@ void pkcs11_rescan_slots (void); int pkcs11_info_to_url (const struct pkcs11_url_info *info, gnutls_pkcs11_url_type_t detailed, char **url); -#define SESSION_WRITE 1 -#define SESSION_LOGIN 2 +#define SESSION_WRITE (1<<0) +#define SESSION_LOGIN (1<<1) +#define SESSION_SO (1<<2) /* security officer session */ int pkcs11_open_session (pakchois_session_t ** _pks, struct pkcs11_url_info *info, unsigned int flags); int _pkcs11_traverse_tokens (find_func_t find_func, void *input, diff --git a/lib/pkcs11_secret.c b/lib/pkcs11_secret.c new file mode 100644 index 0000000000..554359ea7a --- /dev/null +++ b/lib/pkcs11_secret.c @@ -0,0 +1,133 @@ +/* + * GnuTLS PKCS#11 support + * Copyright (C) 2010 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA +*/ + +#include +#include +#include +#include +#include +#include +#include + +/** + * gnutls_pkcs11_copy_x509_crt: + * @token_url: A PKCS #11 URL specifying a token + * @key: The raw key + * @key_size: the size of the key + * @label: A name to be used for the stored data + * @key_usage: One of GNUTLS_KEY_* + * @flags: One of GNUTLS_PKCS11_OBJ_FLAG_* + * + * This function will copy a raw secret (symmetric) key into a PKCS #11 + * token specified by a URL. The key can be marked as sensitive or not. + * + * Returns: On success, %GNUTLS_E_SUCCESS is returned, otherwise a + * negative error value. + **/ +int gnutls_pkcs11_copy_secret_key (const char *token_url, gnutls_datum_t* key, + const char *label, + unsigned int key_usage, + unsigned int flags + /* GNUTLS_PKCS11_OBJ_FLAG_* */ ) +{ + int ret; + pakchois_session_t *pks; + struct pkcs11_url_info info; + ck_rv_t rv; + struct ck_attribute a[8]; + ck_object_class_t class = CKO_SECRET_KEY; + ck_object_handle_t obj; + unsigned int tval = 1; + int a_val; + + ret = pkcs11_url_to_info (token_url, &info); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = + pkcs11_open_session (&pks, &info, + SESSION_WRITE | pkcs11_obj_flags_to_int (flags)); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + /* FIXME: copy key usage flags */ + + a[0].type = CKA_CLASS; + a[0].value = &class; + a[0].value_len = sizeof (class); + a[1].type = CKA_VALUE; + a[1].value = key->data; + a[1].value_len = key->size; + a[2].type = CKA_TOKEN; + a[2].value = &tval; + a[2].value_len = sizeof (tval); + a[3].type = CKA_PRIVATE; + a[3].value = &tval; + a[3].value_len = sizeof (tval); + + a_val = 4; + + if (label) + { + a[a_val].type = CKA_LABEL; + a[a_val].value = (void *) label; + a[a_val].value_len = strlen (label); + a_val++; + } + + if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_SENSITIVE) + tval = 1; + else + tval = 0; + + a[a_val].type = CKA_SENSITIVE; + a[a_val].value = &tval; + a[a_val].value_len = sizeof (tval); + a_val++; + + rv = pakchois_create_object (pks, a, a_val, &obj); + if (rv != CKR_OK) + { + gnutls_assert (); + _gnutls_debug_log ("pkcs11: %s\n", pakchois_error (rv)); + ret = pkcs11_rv_to_err (rv); + goto cleanup; + } + + /* generated! + */ + + ret = 0; + +cleanup: + pakchois_close_session (pks); + + return ret; + +} + diff --git a/lib/pkcs11_write.c b/lib/pkcs11_write.c index 15fd10c599..34fb921ace 100644 --- a/lib/pkcs11_write.c +++ b/lib/pkcs11_write.c @@ -243,6 +243,16 @@ gnutls_pkcs11_copy_x509_privkey (const char *token_url, a[a_val].value_len = sizeof (type); a_val++; + a[a_val].type = CKA_TOKEN; + a[a_val].value = &tval; + a[a_val].value_len = sizeof (tval); + a_val++; + + a[a_val].type = CKA_PRIVATE; + a[a_val].value = &tval; + a[a_val].value_len = sizeof (tval); + a_val++; + if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_SENSITIVE) tval = 1; else @@ -567,3 +577,138 @@ gnutls_pkcs11_delete_url (const char *object_url, unsigned int flags) return find_data.deleted; } + +/** + * gnutls_pkcs11_token_init: + * @token_url: A PKCS #11 URL specifying a token + * @so_pin: Security Officer's PIN + * @label: A name to be used for the token + * + * This function will initialize (format) a token. If the token is + * at a factory defaults state the security officer's PIN given will be + * set to be the default. Otherwise it should match the officer's PIN. + * + * Returns: On success, %GNUTLS_E_SUCCESS is returned, otherwise a + * negative error value. + **/ +int +gnutls_pkcs11_token_init (const char *token_url, + const char* so_pin, + const char *label) +{ + int ret; + struct pkcs11_url_info info; + ck_rv_t rv; + pakchois_module_t *module; + ck_slot_id_t slot; + char flabel[32]; + + ret = pkcs11_url_to_info (token_url, &info); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = pkcs11_find_slot(&module, &slot, &info, NULL); + if (ret < 0) + { + gnutls_assert(); + return ret; + } + + /* so it seems memset has other uses than zeroing! */ + memset(flabel, ' ', sizeof(flabel)); + if (label != NULL) + memcpy(flabel, label, strlen(label)); + + rv = pakchois_init_token(module, slot, (char*)so_pin, strlen(so_pin), flabel); + if (rv != CKR_OK) + { + gnutls_assert (); + _gnutls_debug_log ("pkcs11: %s\n", pakchois_error (rv)); + return pkcs11_rv_to_err (rv); + } + + return 0; + +} + +/** + * gnutls_pkcs11_token_set_pin: + * @token_url: A PKCS #11 URL specifying a token + * @oldpin: old user's PIN + * @newpin: new user's PIN + * @flags: one of gnutls_pkcs11_pin_flag_t + * + * This function will modify or set a user's PIN for the given token. + * If it is called to set a user pin for first time the oldpin must + * be NULL. + * + * Returns: On success, %GNUTLS_E_SUCCESS is returned, otherwise a + * negative error value. + **/ +int +gnutls_pkcs11_token_set_pin (const char *token_url, + const char* oldpin, + const char* newpin, + unsigned int flags) +{ + int ret; + pakchois_session_t *pks; + struct pkcs11_url_info info; + ck_rv_t rv; + unsigned int ses_flags; + + ret = pkcs11_url_to_info (token_url, &info); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + if (((flags & GNUTLS_PKCS11_PIN_USER) && oldpin == NULL) || + (flags & GNUTLS_PKCS11_PIN_SO)) + ses_flags = SESSION_WRITE|SESSION_LOGIN|SESSION_SO; + else + ses_flags = SESSION_WRITE|SESSION_LOGIN; + + ret = pkcs11_open_session (&pks, &info, ses_flags); + if (ret < 0) + { + gnutls_assert(); + return ret; + } + + if (oldpin == NULL) + { + rv = pakchois_init_pin(pks, (char*)newpin, strlen(newpin)); + if (rv != CKR_OK) + { + gnutls_assert (); + _gnutls_debug_log ("pkcs11: %s\n", pakchois_error (rv)); + ret = pkcs11_rv_to_err (rv); + goto finish; + } + } + else + { + rv = pakchois_set_pin(pks, + (char*)oldpin, strlen(oldpin), + (char*)newpin, strlen(newpin)); + if (rv != CKR_OK) + { + gnutls_assert (); + _gnutls_debug_log ("pkcs11: %s\n", pakchois_error (rv)); + ret = pkcs11_rv_to_err (rv); + goto finish; + } + } + + ret = 0; + +finish: + pakchois_close_session (pks); + return ret; + +} diff --git a/src/pkcs11.c b/src/pkcs11.c index 9a5de9fd0b..6dad19beed 100644 --- a/src/pkcs11.c +++ b/src/pkcs11.c @@ -614,7 +614,16 @@ pkcs11_init (FILE * outfile, const char *url, const char *label, common_info_st* exit(0); ret = - gnutls_pkcs11_token_init (url, so_pin, pin, label); + gnutls_pkcs11_token_init (url, so_pin, label); + if (ret < 0) + { + fprintf (stderr, "Error in %s:%d: %s\n", __func__, __LINE__, + gnutls_strerror (ret)); + exit (1); + } + + ret = + gnutls_pkcs11_token_set_pin (url, NULL, pin, GNUTLS_PKCS11_PIN_USER); if (ret < 0) { fprintf (stderr, "Error in %s:%d: %s\n", __func__, __LINE__,