diff options
-rw-r--r-- | lib/Makefile.am | 8 | ||||
-rw-r--r-- | lib/auth_srp.c | 4 | ||||
-rw-r--r-- | lib/configure.ac | 17 | ||||
-rw-r--r-- | lib/gnutls.pc.in | 2 | ||||
-rw-r--r-- | lib/gnutls_constate.c | 6 | ||||
-rw-r--r-- | lib/gnutls_errors.c | 2 | ||||
-rw-r--r-- | lib/gnutls_handshake.c | 6 | ||||
-rw-r--r-- | lib/gnutls_kx.c | 12 | ||||
-rw-r--r-- | lib/gnutls_psk.c | 2 | ||||
-rw-r--r-- | lib/gnutls_str.c | 121 | ||||
-rw-r--r-- | lib/gnutls_str.h | 5 | ||||
-rw-r--r-- | lib/includes/Makefile.am | 2 | ||||
-rw-r--r-- | lib/includes/gnutls/pkcs11.h | 229 | ||||
-rw-r--r-- | lib/libgnutls.map | 18 | ||||
-rw-r--r-- | lib/openpgp/gnutls_openpgp.c | 4 | ||||
-rw-r--r-- | lib/pkcs11.c | 1252 | ||||
-rw-r--r-- | lib/x509/common.c | 2 | ||||
-rw-r--r-- | lib/x509/dn.c | 2 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/certtool-common.h | 5 | ||||
-rw-r--r-- | src/certtool-gaa.c | 252 | ||||
-rw-r--r-- | src/certtool-gaa.h | 30 | ||||
-rw-r--r-- | src/certtool.c | 26 | ||||
-rw-r--r-- | src/certtool.gaa | 11 | ||||
-rw-r--r-- | src/pkcs11.c | 50 |
25 files changed, 1938 insertions, 132 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index 8c88dd72db..e2cde09759 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -84,6 +84,10 @@ COBJECTS = gnutls_record.c gnutls_compress.c debug.c gnutls_cipher.c \ rnd-libgcrypt.c cipher-libgcrypt.c mac-libgcrypt.c ext_signature.c \ crypto-api.c ext_safe_renegotiation.c gnutls_mbuffers.c +if ENABLE_PKCS11 +COBJECTS += pkcs11.c +endif + if ENABLE_OPRFI COBJECTS += $(OPRFI_COBJECTS) endif @@ -134,6 +138,10 @@ else libgnutls_la_LDFLAGS += $(LTLIBTASN1) endif +if ENABLE_PKCS11 +libgnutls_la_LDFLAGS += $(LTLIBPAKCHOIS) +endif + if HAVE_LD_OUTPUT_DEF libgnutls_la_LDFLAGS += -Wl,--output-def,libgnutls-$(DLL_VERSION).def defexecdir = $(bindir) diff --git a/lib/auth_srp.c b/lib/auth_srp.c index d6c401bb9b..26f6cb9492 100644 --- a/lib/auth_srp.c +++ b/lib/auth_srp.c @@ -247,7 +247,7 @@ _gnutls_gen_srp_server_kx (gnutls_session_t session, opaque ** data) _gnutls_write_uint16 (n_b, data_b); _gnutls_hard_log ("INT: SRP B[%d]: %s\n", (int) n_b, - _gnutls_bin2hex (&data_b[2], n_b, buf, sizeof (buf))); + _gnutls_bin2hex (&data_b[2], n_b, buf, sizeof (buf), NULL)); _gnutls_srp_entry_free (pwd_entry); @@ -366,7 +366,7 @@ _gnutls_gen_srp_client_kx (gnutls_session_t session, opaque ** data) } _gnutls_hard_log ("INT: SRP A[%d]: %s\n", (int) n_a, - _gnutls_bin2hex (&data_a[2], n_a, buf, sizeof (buf))); + _gnutls_bin2hex (&data_a[2], n_a, buf, sizeof (buf), NULL)); _gnutls_mpi_release (&A); diff --git a/lib/configure.ac b/lib/configure.ac index c28931c2c1..af744502b5 100644 --- a/lib/configure.ac +++ b/lib/configure.ac @@ -79,6 +79,23 @@ else AC_MSG_RESULT(no) fi +AC_ARG_WITH(pakchois, AS_HELP_STRING([--without-pakchois], + [disable pakchois PKCS11 support]), + ac_pakchois=$withval, ac_pakchois=yes) +AC_MSG_CHECKING([whether to include pakchois PKCS11 support]) +if test x$ac_pakchois != xno; then + AC_MSG_RESULT(yes) + AC_LIB_HAVE_LINKFLAGS(pakchois,, [#include <pakchois/pakchois.h>], [pakchois_module_load(0,0);]) + if test "$ac_cv_libpakchois" != yes; then + AC_MSG_WARN( +*** +*** Pakchois was not found. You will not be able to use PKCS11 support.) + fi +else + AC_MSG_RESULT(no) +fi +AM_CONDITIONAL(ENABLE_PKCS11, test "$ac_cv_libpakchois" = "yes") + lgl_INIT LIBGNUTLS_LIBS="-L${libdir} -lgnutls $LIBS" diff --git a/lib/gnutls.pc.in b/lib/gnutls.pc.in index a4a34954ff..5898ca1a72 100644 --- a/lib/gnutls.pc.in +++ b/lib/gnutls.pc.in @@ -20,5 +20,5 @@ Description: Transport Security Layer implementation for the GNU system URL: http://www.gnu.org/software/gnutls/ Version: @VERSION@ Libs: -L${libdir} -lgnutls -Libs.private: @LIBGNUTLS_LIBS@ @LTLIBTASN1@ +Libs.private: @LIBGNUTLS_LIBS@ @LTLIBTASN1@ @LTLIBPAKCHOIS@ Cflags: -I${includedir} diff --git a/lib/gnutls_constate.c b/lib/gnutls_constate.c index bd3622b8ea..f6d58cc430 100644 --- a/lib/gnutls_constate.c +++ b/lib/gnutls_constate.c @@ -116,7 +116,7 @@ _gnutls_set_keys (gnutls_session_t session, int hash_size, int IV_size, _gnutls_hard_log ("INT: KEY BLOCK[%d]: %s\n", block_size, _gnutls_bin2hex (key_block, block_size, buf, - sizeof (buf))); + sizeof (buf), NULL)); _gnutls_free_datum (&session->cipher_specs.server_write_mac_secret); _gnutls_free_datum (&session->cipher_specs.client_write_mac_secret); @@ -242,7 +242,7 @@ _gnutls_set_keys (gnutls_session_t session, int hash_size, int IV_size, client_write_key_size, _gnutls_bin2hex (client_write_key, client_write_key_size, buf, - sizeof (buf))); + sizeof (buf), NULL)); if (_gnutls_sset_datum (&session->cipher_specs.server_write_key, @@ -256,7 +256,7 @@ _gnutls_set_keys (gnutls_session_t session, int hash_size, int IV_size, server_write_key_size, _gnutls_bin2hex (server_write_key, server_write_key_size, buf, - sizeof (buf))); + sizeof (buf), NULL)); } diff --git a/lib/gnutls_errors.c b/lib/gnutls_errors.c index 8b0afd3bce..1913420f0f 100644 --- a/lib/gnutls_errors.c +++ b/lib/gnutls_errors.c @@ -487,7 +487,7 @@ _gnutls_mpi_log (const char *prefix, bigint_t a) return; } - _gnutls_bin2hex (binbuf, binlen, hexbuf, hexlen); + _gnutls_bin2hex (binbuf, binlen, hexbuf, hexlen, NULL); _gnutls_hard_log ("MPI: length: %d\n\t%s%s\n", (int) binlen, prefix, hexbuf); diff --git a/lib/gnutls_handshake.c b/lib/gnutls_handshake.c index 7bf79b4af3..d5b8ab18ff 100644 --- a/lib/gnutls_handshake.c +++ b/lib/gnutls_handshake.c @@ -1662,7 +1662,7 @@ _gnutls_client_check_if_resuming (gnutls_session_t session, session_id_len); _gnutls_handshake_log ("HSK[%p]: SessionID: %s\n", session, _gnutls_bin2hex (session_id, session_id_len, buf, - sizeof (buf))); + sizeof (buf), NULL)); if (session_id_len > 0 && session->internals.resumed_security_parameters.session_id_size == @@ -2249,7 +2249,7 @@ _gnutls_send_server_hello (gnutls_session_t session, int again) _gnutls_handshake_log ("HSK[%p]: SessionID: %s\n", session, _gnutls_bin2hex (session->security_parameters. session_id, session_id_len, buf, - sizeof (buf))); + sizeof (buf), NULL)); memcpy (&data[pos], session->security_parameters.current_cipher_suite.suite, 2); @@ -2757,7 +2757,7 @@ _gnutls_handshake_client (gnutls_session_t session) internals.resumed_security_parameters.session_id, session-> internals.resumed_security_parameters.session_id_size, - buf, sizeof (buf))); + buf, sizeof (buf), NULL)); #endif switch (STATE) diff --git a/lib/gnutls_kx.c b/lib/gnutls_kx.c index bec2ca34db..1173160fd3 100644 --- a/lib/gnutls_kx.c +++ b/lib/gnutls_kx.c @@ -95,15 +95,15 @@ generate_normal_master (gnutls_session_t session, int keep_premaster) _gnutls_hard_log ("INT: PREMASTER SECRET[%d]: %s\n", PREMASTER.size, _gnutls_bin2hex (PREMASTER.data, PREMASTER.size, buf, - sizeof (buf))); + sizeof (buf), NULL)); _gnutls_hard_log ("INT: CLIENT RANDOM[%d]: %s\n", 32, _gnutls_bin2hex (session-> security_parameters.client_random, 32, - buf, sizeof (buf))); + buf, sizeof (buf), NULL)); _gnutls_hard_log ("INT: SERVER RANDOM[%d]: %s\n", 32, _gnutls_bin2hex (session-> security_parameters.server_random, 32, - buf, sizeof (buf))); + buf, sizeof (buf), NULL)); if (gnutls_protocol_get_version (session) == GNUTLS_SSL3) { @@ -147,7 +147,7 @@ generate_normal_master (gnutls_session_t session, int keep_premaster) session-> security_parameters.extensions. oprfi_client_len, buf, - sizeof (buf))); + sizeof (buf), NULL)); _gnutls_hard_log ("INT: SERVER OPRFI[%d]: %s\n", session->security_parameters.extensions. oprfi_server_len, @@ -157,7 +157,7 @@ generate_normal_master (gnutls_session_t session, int keep_premaster) session-> security_parameters.extensions. oprfi_server_len, buf, - sizeof (buf))); + sizeof (buf), NULL)); memcpy (rnd, session->security_parameters.client_random, GNUTLS_RANDOM_SIZE); @@ -209,7 +209,7 @@ generate_normal_master (gnutls_session_t session, int keep_premaster) _gnutls_hard_log ("INT: MASTER SECRET: %s\n", _gnutls_bin2hex (session-> security_parameters.master_secret, - GNUTLS_MASTER_SIZE, buf, sizeof (buf))); + GNUTLS_MASTER_SIZE, buf, sizeof (buf), NULL)); return ret; } diff --git a/lib/gnutls_psk.c b/lib/gnutls_psk.c index 1a8d87dccf..a1f27740ac 100644 --- a/lib/gnutls_psk.c +++ b/lib/gnutls_psk.c @@ -416,7 +416,7 @@ gnutls_hex_encode (const gnutls_datum_t * data, char *result, return GNUTLS_E_SHORT_MEMORY_BUFFER; } - _gnutls_bin2hex (data->data, data->size, result, *result_size); + _gnutls_bin2hex (data->data, data->size, result, *result_size, NULL); *result_size = res; return 0; diff --git a/lib/gnutls_str.c b/lib/gnutls_str.c index 8e2ef85639..2efe4c643f 100644 --- a/lib/gnutls_str.c +++ b/lib/gnutls_str.c @@ -283,6 +283,101 @@ _gnutls_string_append_printf (gnutls_string * dest, const char *fmt, ...) return len; } +static int _gnutls_string_insert_data(gnutls_string * dest, int pos, const void* str, size_t str_size) +{ + size_t orig_length = dest->length; + int ret; + + ret = _gnutls_string_resize(dest, dest->length+str_size); /* resize to make space */ + if (ret < 0) + return ret; + + memmove(&dest->data[pos+str_size], &dest->data[pos], orig_length-pos); + + memcpy(&dest->data[pos], str, str_size); + dest->length += str_size; + + return 0; +} + +static void _gnutls_string_delete_data(gnutls_string * dest, int pos, size_t str_size) +{ + memmove(&dest->data[pos], &dest->data[pos+str_size], dest->length-pos-str_size); + + dest->length -= str_size; + + return; +} + + +int _gnutls_string_escape(gnutls_string * dest, const char *const invalid_chars) +{ + static const char *x = "0123456789ABCDEF"; + int rv = -1; + char t[5]; + int pos = 0; + + /*_PKCS11H_ASSERT (target!=NULL); Not required*/ + + while (pos < dest->length) { + + if (dest->data[pos] == '\\' || strchr(invalid_chars, dest->data[pos]) + || !isgraph(dest->data[pos])) { + + t[0] = '%'; + t[1] = x[(dest->data[pos] & 0xf0) >> 4]; + t[2] = x[(dest->data[pos] & 0x0f) >> 0]; + + _gnutls_string_delete_data(dest, pos, 1); + + if (_gnutls_string_insert_data(dest, pos, t, 3) < 0) { + rv = -1; + goto cleanup; + } + + } + pos++; + } + + rv = 0; + + cleanup: + + return rv; +} + +int _gnutls_string_unescape(gnutls_string * dest) +{ + int rv = -1; + int pos = 0; + + /*_PKCS11H_ASSERT (target!=NULL); Not required*/ + + while (pos < dest->length) { + if (dest->data[pos] == '%') { + char b[3]; + unsigned u; + char x; + b[0] = dest->data[pos+1]; + b[1] = dest->data[pos+2]; + b[2] = '\x0'; + + sscanf(b, "%08x", &u); + x = u & 0xff; + _gnutls_string_delete_data(dest, pos, 3); + _gnutls_string_insert_data(dest, pos, &x, 1); + } + pos++; + } + + rv = 0; + + cleanup: + + return rv; +} + + /* Converts the given string (old) to hex. A buffer must be provided * to hold the new hex string. The new string will be null terminated. * If the buffer does not have enough space to hold the string, a @@ -290,14 +385,27 @@ _gnutls_string_append_printf (gnutls_string * dest, const char *fmt, ...) */ char * _gnutls_bin2hex (const void *_old, size_t oldlen, - char *buffer, size_t buffer_size) + char *buffer, size_t buffer_size, const char *separator) { unsigned int i, j; const opaque *old = _old; + int init = 0; + int step = 2; + const char empty[] = ""; + + if (separator != NULL && separator[0] != 0) + step = 3; + else + separator = empty; - for (i = j = 0; i < oldlen && j + 2 < buffer_size; j += 2) + i = j = 0; + sprintf (&buffer[j], "%.2x", old[i]); + j+=2; + i++; + + for (; i < oldlen && j + step < buffer_size; j += step) { - sprintf (&buffer[j], "%.2x", old[i]); + sprintf (&buffer[j], "%s%.2x", separator, old[i]); i++; } buffer[j] = '\0'; @@ -344,11 +452,16 @@ _gnutls_hex2bin (const opaque * hex_data, int hex_size, opaque * bin_data, return GNUTLS_E_SHORT_MEMORY_BUFFER; } - for (i = j = 0; j < hex_size; i += 2, j++) + for (i = j = 0; j < hex_size; j++) { + + if (!isxdigit(hex_data[i])) /* skip non-hex such as the ':' in 00:FF */ + continue; + hex2_data[0] = hex_data[i]; hex2_data[1] = hex_data[i + 1]; hex2_data[2] = 0; + i+=2; val = strtoul ((char *) hex2_data, NULL, 16); if (val == ULONG_MAX) { diff --git a/lib/gnutls_str.h b/lib/gnutls_str.h index 53bd240583..a011f4a9eb 100644 --- a/lib/gnutls_str.h +++ b/lib/gnutls_str.h @@ -57,6 +57,9 @@ void _gnutls_string_get_data (gnutls_string *, void *, size_t * size); void _gnutls_string_get_datum (gnutls_string *, gnutls_datum_t *, size_t max_size); +int _gnutls_string_escape(gnutls_string * dest, const char *const invalid_chars); +int _gnutls_string_unescape(gnutls_string * dest); + #ifndef __attribute__ /* This feature is available in gcc versions 2.5 and later. */ # if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) @@ -77,7 +80,7 @@ typedef gnutls_string gnutls_buffer; #define _gnutls_buffer_resize _gnutls_string_resize char *_gnutls_bin2hex (const void *old, size_t oldlen, char *buffer, - size_t buffer_size); + size_t buffer_size, const char* separator); int _gnutls_hex2bin (const opaque * hex_data, int hex_size, opaque * bin_data, size_t * bin_size); diff --git a/lib/includes/Makefile.am b/lib/includes/Makefile.am index 5c67d4331a..7798d463f6 100644 --- a/lib/includes/Makefile.am +++ b/lib/includes/Makefile.am @@ -22,7 +22,7 @@ # MA 02110-1301, USA nobase_include_HEADERS = gnutls/x509.h gnutls/pkcs12.h gnutls/compat.h \ - gnutls/openpgp.h gnutls/crypto.h + gnutls/openpgp.h gnutls/crypto.h gnutls/pkcs11.h if ENABLE_CXX nobase_include_HEADERS += gnutls/gnutlsxx.h diff --git a/lib/includes/gnutls/pkcs11.h b/lib/includes/gnutls/pkcs11.h new file mode 100644 index 0000000000..092333a14c --- /dev/null +++ b/lib/includes/gnutls/pkcs11.h @@ -0,0 +1,229 @@ +#ifndef __GNUTLS_PKCS11_H +#define __GNUTLS_PKCS11_H + +/** + * @addtogroup gnutls_pkcs11 GnuTLS PKCS#11 interface. + * + * @{ + */ + +/** + * @file gnutls-pkcs11.h + * @brief gnutls-pkcs11 interface. + * @author Alon Bar-Lev <alon.barlev@gmail.com> + * @see @ref gnutls_pkcs11 + */ + +#include <stdarg.h> +#include <gnutls/gnutls.h> +#include <gnutls/x509.h> + +/** + * @brief PKCS#11 error + */ +#define GNUTLS_E_PKCS11_ERROR (GNUTLS_E_APPLICATION_ERROR_MIN+529) +#define GNUTLS_E_PKCS11_LOAD_ERROR 1821 +#define GNUTLS_E_PARSING_ERROR 1822 +#define GNUTLS_E_PKCS11_PIN_ERROR 1823 + +#define GNUTLS_PKCS11_MAX_PIN_LEN 32 + +/** + * @brief Token prompt callback. + * @param global_data Callback data. + * @param label Token label. + * @param retry Retry counter. + * @return none zero on success. + */ +typedef int (*gnutls_pkcs11_token_callback_t)( + void * const global_data, + const char * const label, + const unsigned retry +); + +/* flags */ +#define GNUTLS_PKCS11_PIN_FINAL_TRY 1 +#define GNUTLS_PKCS11_PIN_COUNT_LOW 2 + +/* Callback for PKCS#11 PIN entry. The callback provides the PIN code + * to unlock the token with label 'token_label' in the slot described + * by 'slot_descr'. + * + * The PIN code, as a NUL-terminated ASCII string, should be copied + * into the 'pin' buffer (of fixed length NE_SSL_P11PINLEN), and + * return 0 to indicate success. Alternatively, the callback may + * return -1 to indicate failure and cancel PIN entry (in which case, + * the contents of the 'pin' parameter are ignored). + * + * When a PIN is required, the callback will be invoked repeatedly + * (and indefinitely) until either the returned PIN code is correct, + * the callback returns failure, or the token refuses login (e.g. when + * the token is locked due to too many incorrect PINs!). For the + * first such invocation, the 'attempt' counter will have value zero; + * it will increase by one for each subsequent attempt. + * + * The NE_SSL_P11PIN_COUNT_LOW and/or NE_SSL_P11PIN_FINAL_TRY hints + * may be set in the 'flags' argument, if these hints are made + * available by the token; not all tokens expose these hints. */ +typedef int (*gnutls_pkcs11_pin_callback_t)(void *userdata, int attempt, + const char *slot_descr, + const char *token_label, + unsigned int flags, + char *pin, size_t pin_max); + +/** + * @brief PKCS#11 certificate reference. + */ +struct gnutls_pkcs11_crt_st; + + +typedef struct gnutls_pkcs11_crt_st* gnutls_pkcs11_crt_t; + + +#define GNUTLS_PKCS11_FLAG_MANUAL 0 /* Manual loading of libraries */ +#define GNUTLS_PKCS11_FLAG_AUTO 1 /* Automatically load libraries by reading /etc/gnutls/pkcs11.conf */ + +/* pkcs11.conf format: + * load = /lib/xxx-pkcs11.so + * load = /lib/yyy-pkcs11.so + */ + +/** + * @brief Initialize gnutls-pkcs11. + * @param params Misc values to use. + * @return gnutls status. + * @note gnutls-pkcs11 must be uninitialize. + * @see gnutls_pkcs11_deinit() + * @todo params is not implemented yet. + * + * params is in the format of: + * name=value;name=value; + */ +int gnutls_pkcs11_init (unsigned int flags, const char* configfile); + +/** + * @brief Deinitialize gnutls-pkcs11. + * @return gnutls status. + */ +void gnutls_pkcs11_deinit (void); + +/** + * @brief Set token prompt callback. + * @param callback Callback to use. + * @param data Data to use when calling callback. + * @return gnutls status. + */ +//int gnutls_pkcs11_set_token_function (const gnutls_pkcs11_token_callback_t callback, void * const data); + +/** + * @brief Set PIN prompt callback. + * @param callback Callback to use. + * @param data Data to use when calling callback. + * @return gnutls status. + */ +void gnutls_pkcs11_set_pin_function (const gnutls_pkcs11_pin_callback_t callback, void * const data); + +/** + * @brief Add PKCS#11 provider. + * @param name Library to load. + * @param params Misc provider parameters. + * @return gnutls status. + * @todo params is not implemented yet. + * + * params is in the format of: + * name=value;name=value; + */ +int gnutls_pkcs11_add_provider (const char * name, const char * params); + +/** + * @brief Free certificate reference. + * @param certificate Certificate reference to free. + * @return gnutls stauts. + */ +int gnutls_pkcs11_crt_init ( gnutls_pkcs11_crt_t *certificate); + +/** + * @brief Deserialize a certificate reference. + * @param serialized Serialized certificate. + * @param p_certificate Certificate reference. + * @return gnutls status. + */ +int gnutls_pkcs11_crt_import_url (gnutls_pkcs11_crt_t p_certificate, const char * url); + +/** + * @brief Serialize a certificate reference. + * @param certificate Certificate reference to serialize. + * @param serialized Serialize result (string). Use gnutls_free() to free it. + * @return gnutls status. + */ +int gnutls_pkcs11_crt_export_url (gnutls_pkcs11_crt_t certificate, char** url); + +/** + * @brief Free certificate reference. + * @param certificate Certificate reference to free. + * @return gnutls stauts. + */ +void gnutls_pkcs11_crt_deinit ( gnutls_pkcs11_crt_t certificate); + +/** + * @brief Release array of certificate references. + * @param certificates Array to free. + * @param ncertificates Array size. + * @return gnutls status. + */ +int gnutls_pkcs11_crt_list_deinit (gnutls_pkcs11_crt_t * certificates, const unsigned int ncertificates); + +typedef enum { + GNUTLS_PKCS11_CRT_ATTR_ALL, + GNUTLS_PKCS11_CRT_ATTR_TRUSTED, /* marked as trusted */ + GNUTLS_PKCS11_CRT_ATTR_WITH_PK, /* with corresponding private key */ +} pkcs11_crt_attributes; + +/** + * @brief Enumerate available certificates. + * @param p_list Location to store the list. + * @param n_list Location to store end list length. + * @param url Enumerate only certificates found in token(s) pointed by url + * @param attributes Only export certificates that match this attribute + * @return gnutls status. + * @note the p_list is should not be initialized. + */ +int gnutls_pkcs11_crt_list_import (gnutls_pkcs11_crt_t * p_list, unsigned int *const n_list, const char* url, pkcs11_crt_attributes flags); + +int gnutls_x509_crt_import_pkcs11( gnutls_x509_crt_t crt, gnutls_pkcs11_crt_t pkcs11_crt); + +/** + * @brief Return the type of the certificate + * @param certificate Certificate reference. + * @return gnutls status. + */ +gnutls_certificate_type_t gnutls_pkcs11_crt_get_type (gnutls_pkcs11_crt_t certificate); + +int gnutls_x509_crt_list_import_pkcs11 (gnutls_x509_crt_t * certs, + unsigned int cert_max, + gnutls_pkcs11_crt_t * const pkcs11_certs, + unsigned int flags); + + +/* XXX: private key functions...*/ + +/** + * @brief Setup session to be used with gnutls-pkcs11. + * @param session Session to setup. + * @param certificate Certificate to use in this session. + * @return gnutls status. + * @see gnutls_pkcs11_cleanup_session() + * @note Resources must be released using @ref gnutls_pkcs11_cleanup_session(). + */ +//int gnutls_pkcs11_setup_session (gnutls_session session, gnutls_pkcs11_crt_t certificate); + +/** + * @brief Cleanup session. + * @param session Session to cleanup. + * @return gnutls status. + */ +//int gnutls_pkcs11_cleanup_session (gnutls_session session); + +/** @} */ + +#endif diff --git a/lib/libgnutls.map b/lib/libgnutls.map index ad61d61ac6..3e501e04fa 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -602,6 +602,24 @@ GNUTLS_2_10 gnutls_safe_renegotiation_status; } GNUTLS_2_8; +GNUTLS_2_11 +{ + global: + gnutls_pkcs11_init; + gnutls_pkcs11_deinit; + gnutls_pkcs11_set_pin_function; + gnutls_pkcs11_add_provider; + gnutls_pkcs11_crt_init; + gnutls_pkcs11_crt_import_url; + gnutls_pkcs11_crt_export_url; + gnutls_pkcs11_crt_deinit; + gnutls_pkcs11_crt_list_deinit; + gnutls_pkcs11_crt_list_import; + gnutls_x509_crt_import_pkcs11; + gnutls_pkcs11_crt_get_type; + gnutls_x509_crt_list_import_pkcs11; +} GNUTLS_2_10; + GNUTLS_PRIVATE { global: # Internal symbols needed by libgnutls-extra: diff --git a/lib/openpgp/gnutls_openpgp.c b/lib/openpgp/gnutls_openpgp.c index 90246f8964..039a2e3f1d 100644 --- a/lib/openpgp/gnutls_openpgp.c +++ b/lib/openpgp/gnutls_openpgp.c @@ -746,7 +746,7 @@ _gnutls_openpgp_privkey_to_gkey (gnutls_privkey * dest, _gnutls_debug_log ("Importing Openpgp key and using openpgp sub key: %s\n", - _gnutls_bin2hex (keyid, sizeof (keyid), err_buf, sizeof (err_buf))); + _gnutls_bin2hex (keyid, sizeof (keyid), err_buf, sizeof (err_buf), NULL)); KEYID_IMPORT (kid32, keyid); @@ -812,7 +812,7 @@ _gnutls_openpgp_crt_to_gcert (gnutls_cert * gcert, gnutls_openpgp_crt_t cert) _gnutls_debug_log ("Importing Openpgp cert and using openpgp sub key: %s\n", - _gnutls_bin2hex (keyid, sizeof (keyid), err_buf, sizeof (err_buf))); + _gnutls_bin2hex (keyid, sizeof (keyid), err_buf, sizeof (err_buf), NULL)); KEYID_IMPORT (kid32, keyid); diff --git a/lib/pkcs11.c b/lib/pkcs11.c new file mode 100644 index 0000000000..c49e628fd9 --- /dev/null +++ b/lib/pkcs11.c @@ -0,0 +1,1252 @@ +/* + neon PKCS#11 support + Copyright (C) 2008, Joe Orton <joe@manyfish.co.uk> + + 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 <gnutls_int.h> +#include <pakchois/pakchois.h> +#include <gnutls/pkcs11.h> +#include <stdio.h> +#include <stdbool.h> +#include <string.h> +#include <gnutls_errors.h> +#include <gnutls_datum.h> + +#define MAX_PROVIDERS 16 +#define ID_SIZE 128 + +typedef int (*find_func_t)(pakchois_session_t *pks, struct ck_token_info* tinfo, void* input); + +struct gnutls_pkcs11_provider_s { + pakchois_module_t *module; + unsigned long nslots; + ck_slot_id_t *slots; +}; + +struct pkcs11_url_info +{ + /* everything here is null terminated strings */ + opaque id[ID_SIZE]; + opaque type[16]; /* cert/key etc. */ + opaque manufacturer[sizeof (((struct ck_token_info *)NULL)->manufacturer_id)+1]; + opaque token[sizeof (((struct ck_token_info *)NULL)->label)+1]; + opaque serial[sizeof (((struct ck_token_info *)NULL)->serial_number)+1]; + opaque model[sizeof (((struct ck_token_info *)NULL)->model)+1]; +}; + +struct gnutls_pkcs11_crt_st { + gnutls_datum_t raw; + gnutls_certificate_type_t type; + struct pkcs11_url_info info; +}; + +struct url_find_data_st { + gnutls_pkcs11_crt_t crt; + char certid_raw[ID_SIZE/2]; + size_t certid_raw_size; +}; + +struct crt_find_data_st { + gnutls_pkcs11_crt_t *p_list; + unsigned int* n_list; + unsigned int current; + pkcs11_crt_attributes flags; + struct pkcs11_url_info info; +}; + +static struct gnutls_pkcs11_provider_s providers[MAX_PROVIDERS]; +static int active_providers = 0; + +static gnutls_pkcs11_pin_callback_t pin_func; +static void* pin_data; + +int gnutls_pkcs11_add_provider (const char * name, const char * params) +{ + + if (active_providers >= MAX_PROVIDERS) { + gnutls_assert(); + return GNUTLS_E_CONSTRAINT_ERROR; + } + + active_providers++; + if (pakchois_module_load(&providers[active_providers-1].module, name) != CKR_OK) { + gnutls_assert(); + _gnutls_debug_log("p11: Cannot load provider %s\n", name); + active_providers--; + return GNUTLS_E_PKCS11_LOAD_ERROR; + } + + /* cache the number of slots in this module */ + if (pakchois_get_slot_list(providers[active_providers-1].module, 0, NULL, &providers[active_providers-1].nslots) != CKR_OK) { + gnutls_assert(); + goto fail; + } + + providers[active_providers-1].slots = gnutls_malloc(sizeof(*providers[active_providers-1].slots)*providers[active_providers-1].nslots); + if (providers[active_providers-1].slots==NULL) { + gnutls_assert(); + goto fail; + } + + if (pakchois_get_slot_list(providers[active_providers-1].module, 0, providers[active_providers-1].slots, &providers[active_providers-1].nslots) != CKR_OK) { + gnutls_assert(); + gnutls_free(providers[active_providers-1].slots); + goto fail; + } + + _gnutls_debug_log("p11: loaded provider '%s' with %d slots\n", name, providers[active_providers-1].nslots); + + return 0; + +fail: + pakchois_module_destroy(providers[active_providers-1].module); + active_providers--; + return GNUTLS_E_PKCS11_LOAD_ERROR; + +} + +int gnutls_pkcs11_init(unsigned int flags, const char* configfile) +{ + int ret; + + if (flags == GNUTLS_PKCS11_FLAG_MANUAL) + return 0; + else { + FILE *fp; + char line[512]; + const char* library; + + if (configfile == NULL) + configfile = "/etc/gnutls/pkcs11.conf"; + + fp = fopen(configfile, "r"); + if (fp == NULL) { + gnutls_assert(); + _gnutls_debug_log("Cannot load %s\n", configfile); + return GNUTLS_E_FILE_ERROR; + } + + while (fgets (line, sizeof (line), fp) != NULL) { + if (strncmp(line, "load", sizeof("load")-1) == 0) { + char* p; + p = strchr(line, '='); + if (p==NULL) continue; + + library = ++p; + + p = strchr(line, '\n'); + if (p!=NULL) { + *p=0; + } + + ret = gnutls_pkcs11_add_provider(library, NULL); + if (ret < 0) { + gnutls_assert(); + _gnutls_debug_log("Cannot load provider: %s\n", library); + continue; + } + } + } + } + + return 0; +} + +void gnutls_pkcs11_deinit (void) +{ + int i; + + for (i=0;i<active_providers;i++) { + pakchois_module_destroy(providers[i].module); + } + active_providers = 0; +} + +void gnutls_pkcs11_set_pin_function(gnutls_pkcs11_pin_callback_t fn, + void *userdata) +{ + pin_func = fn; + pin_data = userdata; +} + +static int unescape_string (char* output, const char* input, size_t* size, char terminator) +{ + gnutls_string str; + int ret = 0; + char* p; + int len; + + _gnutls_string_init(&str, gnutls_malloc, gnutls_realloc, gnutls_free); + + /* find terminator */ + p = strchr(input, terminator); + if (p!=NULL) + len = p-input; + else + len = strlen(input); + + ret = _gnutls_string_append_data(&str, input, len); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = _gnutls_string_unescape(&str); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = _gnutls_string_append_data(&str, "", 1); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + _gnutls_string_get_data(&str, output, size); + + _gnutls_string_clear(&str); + + return ret; +} + +static int pkcs11_url_to_info(const char* url, struct pkcs11_url_info* info) +{ +int ret; +char* p1; +char* hexid = NULL; +size_t l; + + memset( info, 0, sizeof(*info)); + + if (strstr(url, "pkcs11:")==NULL) { + ret = GNUTLS_E_PARSING_ERROR; + goto cleanup; + } + + if ((p1=strstr(url, "manufacturer="))!= NULL) { + p1+=sizeof("manufacturer=")-1; + l=sizeof (info->manufacturer); + + ret = unescape_string(info->manufacturer, p1, &l, ';'); + if (ret < 0) { + goto cleanup; + } + } + + if ((p1=strstr(url, "token="))!= NULL) { + p1+=sizeof("token=")-1; + l=sizeof (info->token); + + ret = unescape_string(info->token, p1, &l, ';'); + if (ret < 0) { + goto cleanup; + } + } + + if ((p1=strstr(url, "serial="))!= NULL) { + p1+=sizeof("serial=")-1; + l=sizeof (info->serial); + + ret = unescape_string (info->serial, p1, &l, ';'); + if (ret < 0) { + goto cleanup; + } + } + + if ((p1=strstr(url, "model="))!= NULL) { + p1+=sizeof("model=")-1; + l=sizeof (info->model); + + ret = unescape_string (info->model, + p1, &l, ';'); + if (ret < 0) { + goto cleanup; + } + } + + + if (((p1=strstr(url, ";id="))!= NULL) || ((p1=strstr(url, ":id="))!= NULL)) { + p1+=sizeof(";id=")-1; + hexid = gnutls_strdup(p1); + if (hexid == NULL) { + goto cleanup; + } + + if ((p1=strchr(hexid, ';'))!= NULL) { + *p1 = 0; + } else { + ret = GNUTLS_E_PARSING_ERROR; + goto cleanup; + } + + l = sizeof(info->id); + ret = _gnutls_hex2bin(hexid, strlen(hexid), info->id, &l); + if (ret < 0) { + gnutls_assert(); + return ret; + } + } + + ret = 0; + +cleanup: + gnutls_free(hexid); + + return ret; + +} + +#define INVALID_CHARS "\\/\"'%&#@!?$* <>{}[]()`|:;,.+-" + +static int append(gnutls_string* dest, const char* tname, const char* p11name, int init) +{ + gnutls_string tmpstr; + int ret; + + _gnutls_string_init(&tmpstr, gnutls_malloc, gnutls_realloc, gnutls_free); + if ((ret=_gnutls_string_append_str(&tmpstr, tname))<0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_string_escape(&tmpstr, INVALID_CHARS); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if ((ret=_gnutls_string_append_data(&tmpstr, "", 1)) < 0) { + gnutls_assert(); + goto cleanup; + } + + if ((ret=_gnutls_string_append_printf(dest, "%s%s=%s", (init!=0)?";":"", p11name, tmpstr.data)) < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + +cleanup: + _gnutls_string_clear(&tmpstr); + + return ret; + +} + + +static int pkcs11_info_to_url(const struct pkcs11_url_info* info, char** url) +{ + gnutls_string str; + int init = 0; + int ret; + char *s; + + _gnutls_string_init (&str, gnutls_malloc, gnutls_realloc, gnutls_free); + + _gnutls_string_append_str(&str, "pkcs11:"); + + if (info->token[0]) { + ret = append(&str, info->token, "token", init); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + init = 1; + } + + if (info->model[0]) { + ret = append(&str, info->model, "model", init); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + init = 1; + } + + if (info->manufacturer[0]) { + ret = append(&str, info->manufacturer, "manufacturer", init); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + init = 1; + } + + if (info->serial[0]) { + ret = append(&str, info->serial, "serial", init); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + init = 1; + } + + ret = _gnutls_string_append_printf(&str, ";id=%s", info->id); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + *url = str.data; + + return 0; + +cleanup: + _gnutls_string_clear(&str); + return ret; +} + +int gnutls_pkcs11_crt_init(gnutls_pkcs11_crt_t * crt) +{ + *crt = gnutls_malloc(sizeof(struct gnutls_pkcs11_crt_st)); + if (*crt == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + +} + +void gnutls_pkcs11_crt_deinit(gnutls_pkcs11_crt_t crt) +{ + free(crt); +} + +static void terminate_string(unsigned char *str, size_t len) +{ + unsigned char *ptr = str + len - 1; + + while ((*ptr == ' ' || *ptr == '\t' || *ptr == '\0') && ptr >= str) + ptr--; + + if (ptr == str - 1) + str[0] = '\0'; + else if (ptr == str + len - 1) + str[len-1] = '\0'; + else + ptr[1] = '\0'; +} + +static int pk11_login(struct gnutls_pkcs11_provider_s *prov, ck_slot_id_t slot_id, + pakchois_session_t *pks, struct ck_slot_info *sinfo, struct ck_token_info* tinfo) +{ + int attempt = 0; + ck_rv_t rv; + + if (pakchois_get_token_info(prov->module, slot_id, tinfo) != CKR_OK) { + gnutls_assert(); + _gnutls_debug_log("pk11: GetTokenInfo failed\n"); + return GNUTLS_E_PKCS11_ERROR; + } + + if ((tinfo->flags & CKF_LOGIN_REQUIRED) == 0) { + _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 (tinfo->flags & CKF_PROTECTED_AUTHENTICATION_PATH) { + if (pakchois_login(pks, CKU_USER, NULL, 0) == CKR_OK) { + return 0; + } + else { + gnutls_assert(); + _gnutls_debug_log("pk11: Protected login failed.\n"); + return GNUTLS_E_PKCS11_ERROR; + } + } + + /* 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"); + return GNUTLS_E_PKCS11_PIN_ERROR; + } + + terminate_string(sinfo->slot_description, sizeof sinfo->slot_description); + + do { + char pin[GNUTLS_PKCS11_MAX_PIN_LEN]; + unsigned int flags = 0; + + /* If login has been attempted once already, check the token + * status again, the flags might change. */ + if (attempt) { + if (pakchois_get_token_info(prov->module, slot_id, + tinfo) != CKR_OK) { + _gnutls_debug_log("pk11: GetTokenInfo failed\n"); + gnutls_assert(); + return GNUTLS_E_PKCS11_ERROR; + } + } + + 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; + + terminate_string(tinfo->label, sizeof tinfo->label); + + if (pin_func(pin_data, attempt++, + (char *)sinfo->slot_description, + (char *)tinfo->label, flags, pin, sizeof(pin))) { + return GNUTLS_E_PKCS11_PIN_ERROR; + } + + rv = pakchois_login(pks, CKU_USER, (unsigned char *)pin, strlen(pin)); + + /* Try to scrub the pin off the stack. Clever compilers will + * probably optimize this away, oh well. */ + memset(pin, 0, sizeof pin); + } while (rv == CKR_PIN_INCORRECT); + + _gnutls_debug_log("pk11: Login result = %lu\n", rv); + + return (rv == CKR_OK || rv == CKR_USER_ALREADY_LOGGED_IN) ? 0 : GNUTLS_E_PKCS11_ERROR; +} + +static int traverse_tokens (find_func_t find_func, void* input) +{ + struct ck_attribute a[3]; + ck_rv_t rv; + int found = 0, x, z, ret; + pakchois_session_t *pks = NULL; + + for (x=0;x<active_providers;x++) { + for (z=0;z<providers[x].nslots;z++) { + struct ck_token_info tinfo; + rv = pakchois_open_session(providers[x].module, providers[x].slots[z], + CKF_SERIAL_SESSION, NULL, NULL, &pks); + if (rv != CKR_OK) { + continue; + } + + if (pakchois_get_token_info(providers[x].module, providers[x].slots[z], &tinfo) != CKR_OK) { + continue; + } + + /* XXX make wrapper for token_info? */ + terminate_string(tinfo.manufacturer_id, sizeof tinfo.manufacturer_id); + terminate_string(tinfo.label, sizeof tinfo.label); + terminate_string(tinfo.model, sizeof tinfo.model); + terminate_string(tinfo.serial_number, sizeof tinfo.serial_number); + + ret = find_func(pks, &tinfo, input); + + pakchois_close_session(pks); + pks = NULL; + + if (ret == 0) { + found = 1; + break; + } + } + } + + /* final call */ + + if (found == 0) { + ret = find_func(pks, NULL, input); + } else { + ret = 0; + } + +cleanup: + if (pks != NULL) pakchois_close_session(pks); + + return ret; +} + +/* imports a raw certificate from a token to a pkcs11_crt_t structure. + */ +static int pkcs11_crt_import(gnutls_pkcs11_crt_t crt, const gnutls_datum_t* data, + const gnutls_datum_t * id, struct ck_token_info* tinfo) +{ + char *s; + int ret; + + crt->type = GNUTLS_CRT_X509; + ret = _gnutls_set_datum(&crt->raw, data->data, data->size); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + terminate_string(tinfo->manufacturer_id, sizeof tinfo->manufacturer_id); + terminate_string(tinfo->label, sizeof tinfo->label); + terminate_string(tinfo->model, sizeof tinfo->model); + terminate_string(tinfo->serial_number, sizeof tinfo->serial_number); + + /* write data */ + snprintf(crt->info.manufacturer, sizeof(crt->info.manufacturer), "%s", tinfo->manufacturer_id); + snprintf(crt->info.token, sizeof(crt->info.token), "%s", tinfo->label); + snprintf(crt->info.model, sizeof(crt->info.model), "%s", tinfo->model); + snprintf(crt->info.serial, sizeof(crt->info.serial), "%s", tinfo->serial_number); + strcpy(crt->info.type, "cert"); + + s = _gnutls_bin2hex(id->data, id->size, crt->info.id, sizeof(crt->info.id), ":"); + if (s == NULL) { + gnutls_assert(); + return GNUTLS_E_PKCS11_ERROR; + } + + return 0; +} + + +static int find_url(pakchois_session_t *pks, struct ck_token_info *tinfo, void* input) +{ + struct url_find_data_st* find_data = input; + struct ck_attribute a[3]; + ck_object_class_t class; + ck_certificate_type_t type; + ck_rv_t rv; + ck_object_handle_t obj; + unsigned long count; + int found = 0, ret; + unsigned char value[8192], subject[8192]; + char certid_tmp[ID_SIZE/2]; + + if (tinfo == NULL) { /* we don't support multiple calls */ + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + /* do not bother reading the token if basic fields do not match + */ + if (find_data->crt->info.manufacturer[0] != 0) { + if (strcmp(find_data->crt->info.manufacturer, tinfo->manufacturer_id) != 0) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (find_data->crt->info.token[0] != 0) { + if (strcmp(find_data->crt->info.token, tinfo->label) != 0) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (find_data->crt->info.model[0] != 0) { + if (strcmp(find_data->crt->info.model, tinfo->model) != 0) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (find_data->crt->info.serial[0] != 0) { + if (strcmp(find_data->crt->info.serial, tinfo->serial_number) != 0) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (find_data->crt->info.type[0] != 0) { + if (strcmp(find_data->crt->info.type, "cert") != 0) { + gnutls_assert(); + return GNUTLS_E_UNIMPLEMENTED_FEATURE; + } + } + + /* search the token for the id */ + + /* Find objects with cert class and X.509 cert type. */ + class = CKO_CERTIFICATE; + type = CKC_X_509; + + a[0].type = CKA_CLASS; + a[0].value = &class; + a[0].value_len = sizeof class; + a[1].type = CKA_CERTIFICATE_TYPE; + a[1].value = &type; + a[1].value_len = sizeof type; + + + rv = pakchois_find_objects_init(pks, a, 2); + if (rv != CKR_OK) { + gnutls_assert(); + _gnutls_debug_log("pk11: FindObjectsInit failed.\n"); + ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + goto cleanup; + } + + while (pakchois_find_objects(pks, &obj, 1, &count) == CKR_OK + && count == 1) { + + a[0].type = CKA_VALUE; + a[0].value = value; + a[0].value_len = sizeof value; + a[1].type = CKA_ID; + a[1].value = certid_tmp; + a[1].value_len = sizeof(certid_tmp); + a[2].type = CKA_SUBJECT; + a[2].value = subject; + a[2].value_len = sizeof subject; + + if (pakchois_get_attribute_value(pks, obj, a, 3) == CKR_OK) { + + if (a[1].value_len == find_data->certid_raw_size && + memcmp(certid_tmp, find_data->certid_raw, find_data->certid_raw_size)==0) { + gnutls_datum_t id = { a[1].value, a[1].value_len }; + gnutls_datum_t data = { a[0].value, a[0].value_len }; + + ret = pkcs11_crt_import(find_data->crt, &data, &id, tinfo); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + found = 1; + break; + } + } + else { + _gnutls_debug_log("pk11: Skipped cert, missing attrs.\n"); + } + } + + if (found == 0) { + gnutls_assert(); + ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } else { + ret = 0; + } + +cleanup: + pakchois_find_objects_final(pks); + + return ret; +} + +int gnutls_pkcs11_crt_import_url (gnutls_pkcs11_crt_t cert, const char * url) +{ + int ret; + struct url_find_data_st find_data; + size_t size; + + /* fill in the find data structure */ + find_data.crt = cert; + + ret = pkcs11_url_to_info(url, &cert->info); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + find_data.certid_raw_size = sizeof(find_data.certid_raw); + + size = find_data.certid_raw_size; + ret = _gnutls_hex2bin(cert->info.id, sizeof cert->info.id, find_data.certid_raw, &size); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = traverse_tokens(find_url, &find_data); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +int gnutls_pkcs11_crt_export_url (gnutls_pkcs11_crt_t cert, char ** url) +{ +int ret; + + ret = pkcs11_info_to_url(&cert->info, url); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +gnutls_certificate_type_t gnutls_pkcs11_crt_get_type (gnutls_pkcs11_crt_t certificate) +{ + return certificate->type; +} + +/* Recover certificate list from tokens */ + +static int find_crts(pakchois_session_t *pks, struct ck_token_info *tinfo, void* input) +{ + struct crt_find_data_st* find_data = input; + struct ck_attribute a[3]; + ck_object_class_t class; + ck_certificate_type_t type; + bool trusted; + ck_rv_t rv; + ck_object_handle_t obj; + unsigned long count; + unsigned char value[8192], subject[8192]; + char certid_tmp[ID_SIZE/2]; + int ret, i; + + if (tinfo == NULL) { /* final call */ + if (find_data->current <= *find_data->n_list) + ret = 0; + else + ret = GNUTLS_E_SHORT_MEMORY_BUFFER; + + *find_data->n_list = find_data->current; + + return ret; + } + + /* do not bother reading the token if basic fields do not match + */ + if (find_data->info.manufacturer[0] != 0) { + if (strcmp(find_data->info.manufacturer, tinfo->manufacturer_id) != 0) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (find_data->info.token[0] != 0) { + if (strcmp(find_data->info.token, tinfo->label) != 0) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (find_data->info.model[0] != 0) { + if (strcmp(find_data->info.model, tinfo->model) != 0) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (find_data->info.serial[0] != 0) { + if (strcmp(find_data->info.serial, tinfo->serial_number) != 0) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (find_data->info.type[0] != 0) { + if (strcmp(find_data->info.type, "cert") != 0) { + gnutls_assert(); + return GNUTLS_E_UNIMPLEMENTED_FEATURE; + } + } + + /* Find objects with cert class and X.509 cert type. */ + class = CKO_CERTIFICATE; + type = CKC_X_509; + trusted = 1; + + a[0].type = CKA_CLASS; + a[0].value = &class; + a[0].value_len = sizeof class; + + if (find_data->flags == GNUTLS_PKCS11_CRT_ATTR_ALL || find_data->flags==GNUTLS_PKCS11_CRT_ATTR_WITH_PK) { + a[1].type = CKA_CERTIFICATE_TYPE; + a[1].value = &type; + a[1].value_len = sizeof type; + } + + if (find_data->flags == GNUTLS_PKCS11_CRT_ATTR_TRUSTED) { + a[1].type = CKA_TRUSTED; + a[1].value = &trusted; + a[1].value_len = sizeof trusted; + } + + rv = pakchois_find_objects_init(pks, a, 2); + if (rv != CKR_OK) { + gnutls_assert(); + _gnutls_debug_log("pk11: FindObjectsInit failed.\n"); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + while (pakchois_find_objects(pks, &obj, 1, &count) == CKR_OK + && count == 1) { + + a[0].type = CKA_VALUE; + a[0].value = value; + a[0].value_len = sizeof value; + a[1].type = CKA_ID; + a[1].value = certid_tmp; + a[1].value_len = sizeof(certid_tmp); + a[2].type = CKA_SUBJECT; + a[2].value = subject; + a[2].value_len = sizeof subject; + + if (pakchois_get_attribute_value(pks, obj, a, 3) == CKR_OK) { + gnutls_datum_t data = { a[0].value, a[0].value_len }; + gnutls_datum_t id = { a[1].value, a[1].value_len }; + + /* XXX check also ID with find_data->info.id */ + + if (find_data->flags == GNUTLS_PKCS11_CRT_ATTR_WITH_PK) { + gnutls_assert(); + /* XXX verify that certificate has a corresponding private key */ + //not yet + } + + if (find_data->current < *find_data->n_list) { + + ret = gnutls_pkcs11_crt_init(&find_data->p_list[find_data->current]); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + ret = pkcs11_crt_import(find_data->p_list[find_data->current], &data, &id, tinfo); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + } + + find_data->current++; + + } + else { + _gnutls_debug_log("pk11: Skipped cert, missing attrs.\n"); + } + } + + pakchois_find_objects_final(pks); + + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; /* continue until all tokens have been checked */ + +fail: + pakchois_find_objects_final(pks); + for (i=0;i<find_data->current;i++) { + gnutls_pkcs11_crt_deinit(find_data->p_list[i]); + } + find_data->current = 0; + + return ret; +} + +int gnutls_pkcs11_crt_list_import (gnutls_pkcs11_crt_t * p_list, unsigned int *n_list, const char* url, pkcs11_crt_attributes flags) +{ + int ret; + struct crt_find_data_st find_data; + + /* fill in the find data structure */ + find_data.p_list = p_list; + find_data.n_list = n_list; + find_data.flags = flags; + find_data.current = 0; + + if (url == NULL || url[0] == 0) { + url = "pkcs11:"; + } + + ret = pkcs11_url_to_info(url, &find_data.info); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = traverse_tokens(find_crts, &find_data); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +int gnutls_x509_crt_import_pkcs11( gnutls_x509_crt_t crt, gnutls_pkcs11_crt_t pkcs11_crt) +{ + return gnutls_x509_crt_import(crt, &pkcs11_crt->raw, GNUTLS_X509_FMT_DER); +} + +int gnutls_x509_crt_list_import_pkcs11 (gnutls_x509_crt_t * certs, + unsigned int cert_max, gnutls_pkcs11_crt_t * const pkcs11_certs, + unsigned int flags) +{ + int i, j; + int ret; + + for (i=0;i<cert_max;i++) { + ret = gnutls_x509_crt_init(&certs[i]); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_crt_import_pkcs11( certs[i], pkcs11_certs[i]); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + return 0; + +cleanup: + for (j=0;j<i;j++) { + gnutls_x509_crt_deinit(certs[j]); + } + + return ret; +} + + +/* To do list for PKCS#11 support: + + - propagate error strings back to ne_session; use new + pakchois_error() for pakchois API 0.2 + - add API to specify a particular slot number to use for clicert + - add API to specify a particular cert ID for clicert + - find a certificate which has an issuer matching the + CA dnames given by GnuTLS + - make sure subject name matches between pubkey and privkey + - check error handling & fail gracefully if the token is + ejected mid-session + - add API to enumerate/search provided certs and allow + direct choice? (or just punt) + - the session<->provider interface requires that + one clicert is used for all sessions. remove this limitation + - add API to import all CA certs as trusted + (CKA_CERTIFICATE_CATEGORY seems to be unused unfortunately; + just add all X509 certs with CKA_TRUSTED set to true)) + - make DSA work + +*/ + + +#if 0 +#define KEYTYPE_IS_DSA(kt) (kt == CKK_DSA) + +static int pk11_find_pkey(ne_ssl_pkcs11_provider *prov, + pakchois_session_t *pks, + unsigned char *certid, unsigned long cid_len) +{ + struct ck_attribute a[3]; + ck_object_class_t class; + ck_rv_t rv; + ck_object_handle_t obj; + unsigned long count; + int found = 0; + + class = CKO_PRIVATE_KEY; + + /* Find an object with private key class and a certificate ID + * which matches the certificate. */ + /* FIXME: also match the cert subject. */ + a[0].type = CKA_CLASS; + a[0].value = &class; + a[0].value_len = sizeof class; + a[1].type = CKA_ID; + a[1].value = certid; + a[1].value_len = cid_len; + + rv = pakchois_find_objects_init(pks, a, 2); + if (rv != CKR_OK) { + NE_DEBUG(NE_DBG_SSL, "pk11: FindObjectsInit failed.\n"); + /* TODO: error propagation */ + return 0; + } + + rv = pakchois_find_objects(pks, &obj, 1, &count); + if (rv == CKR_OK && count == 1) { + NE_DEBUG(NE_DBG_SSL, "pk11: Found private key.\n"); + + a[0].type = CKA_KEY_TYPE; + a[0].value = &prov->keytype; + a[0].value_len = sizeof prov->keytype; + + if (pakchois_get_attribute_value(pks, obj, a, 1) == CKR_OK + && (prov->keytype == CKK_RSA || KEYTYPE_IS_DSA(prov->keytype))) { + found = 1; + prov->privkey = obj; + } + else { + NE_DEBUG(NE_DBG_SSL, "pk11: Could not determine key type.\n"); + } + } + + pakchois_find_objects_final(pks); + + return found; +} + +static int find_client_cert(ne_ssl_pkcs11_provider *prov, + pakchois_session_t *pks) +{ + unsigned char certid[8192]; + unsigned long cid_len = sizeof certid; + + /* TODO: match cert subject too. */ + return pk11_find_x509(prov, pks, certid, &cid_len) + && pk11_find_pkey(prov, pks, certid, cid_len); +} + +/* Callback invoked by GnuTLS to provide the signature. The signature + * operation is handled here by the PKCS#11 provider. */ +static int pk11_sign_callback(gnutls_session_t session, + void *userdata, + gnutls_certificate_type_t cert_type, + const gnutls_datum_t *cert, + const gnutls_datum_t *hash, + gnutls_datum_t *signature) +{ + ne_ssl_pkcs11_provider *prov = userdata; + ck_rv_t rv; + struct ck_mechanism mech; + unsigned long siglen; + + if (!prov->session || prov->privkey == CK_INVALID_HANDLE) { + NE_DEBUG(NE_DBG_SSL, "pk11: Cannot sign, no session/key.\n"); + return GNUTLS_E_NO_CERTIFICATE_FOUND; + } + + mech.mechanism = prov->keytype == CKK_DSA ? CKM_DSA : CKM_RSA_PKCS; + mech.parameter = NULL; + mech.parameter_len = 0; + + /* Initialize signing operation; using the private key discovered + * earlier. */ + rv = pakchois_sign_init(prov->session, &mech, prov->privkey); + if (rv != CKR_OK) { + NE_DEBUG(NE_DBG_SSL, "pk11: SignInit failed: %lx.\n", rv); + return GNUTLS_E_PK_SIGN_FAILED; + } + + /* Work out how long the signature must be: */ + rv = pakchois_sign(prov->session, hash->data, hash->size, NULL, &siglen); + if (rv != CKR_OK) { + NE_DEBUG(NE_DBG_SSL, "pk11: Sign1 failed.\n"); + return GNUTLS_E_PK_SIGN_FAILED; + } + + signature->data = gnutls_malloc(siglen); + signature->size = siglen; + + rv = pakchois_sign(prov->session, hash->data, hash->size, + signature->data, &siglen); + if (rv != CKR_OK) { + NE_DEBUG(NE_DBG_SSL, "pk11: Sign2 failed.\n"); + return GNUTLS_E_PK_SIGN_FAILED; + } + + NE_DEBUG(NE_DBG_SSL, "pk11: Signed successfully.\n"); + + return 0; +} + +static void terminate_string(unsigned char *str, size_t len) +{ + unsigned char *ptr = str + len - 1; + + while ((*ptr == ' ' || *ptr == '\t' || *ptr == '\0') && ptr >= str) + ptr--; + + if (ptr == str - 1) + str[0] = '\0'; + else if (ptr == str + len - 1) + str[len-1] = '\0'; + else + ptr[1] = '\0'; +} + + +static void pk11_provide(void *userdata, ne_session *sess, + const ne_ssl_dname *const *dnames, + int dncount) +{ + ne_ssl_pkcs11_provider *prov = userdata; + ck_slot_id_t *slots; + unsigned long scount, n; + + if (prov->clicert) { + NE_DEBUG(NE_DBG_SSL, "pk11: Using existing clicert.\n"); + ne_ssl_set_clicert(sess, prov->clicert); + return; + } + + if (pakchois_get_slot_list(prov->module, 1, NULL, &scount) != CKR_OK + || scount == 0) { + NE_DEBUG(NE_DBG_SSL, "pk11: No slots.\n"); + /* TODO: propagate error. */ + return; + } + + slots = ne_malloc(scount * sizeof *slots); + if (pakchois_get_slot_list(prov->module, 1, slots, &scount) != CKR_OK) { + ne_free(slots); + NE_DEBUG(NE_DBG_SSL, "pk11: Really, no slots?\n"); + /* TODO: propagate error. */ + return; + } + + NE_DEBUG(NE_DBG_SSL, "pk11: Found %ld slots.\n", scount); + + for (n = 0; n < scount; n++) { + pakchois_session_t *pks; + ck_rv_t rv; + struct ck_slot_info sinfo; + + if (pakchois_get_slot_info(prov->module, slots[n], &sinfo) != CKR_OK) { + NE_DEBUG(NE_DBG_SSL, "pk11: GetSlotInfo failed\n"); + continue; + } + + if ((sinfo.flags & CKF_TOKEN_PRESENT) == 0) { + NE_DEBUG(NE_DBG_SSL, "pk11: slot empty, ignoring\n"); + continue; + } + + rv = pakchois_open_session(prov->module, slots[n], + CKF_SERIAL_SESSION, + NULL, NULL, &pks); + if (rv != CKR_OK) { + NE_DEBUG(NE_DBG_SSL, "pk11: could not open slot, %ld (%ld: %ld)\n", + rv, n, slots[n]); + continue; + } + + if (pk11_login(prov, slots[n], pks, &sinfo) == 0) { + if (find_client_cert(prov, pks)) { + NE_DEBUG(NE_DBG_SSL, "pk11: Setup complete.\n"); + prov->session = pks; + ne_ssl_set_clicert(sess, prov->clicert); + ne_free(slots); + return; + } + } + + pakchois_close_session(pks); + } + + ne_free(slots); +} + + + +void ne_ssl_set_pkcs11_provider(ne_session *sess, + ne_ssl_pkcs11_provider *provider) +{ + sess->ssl_context->sign_func = pk11_sign_callback; + sess->ssl_context->sign_data = provider; + + ne_ssl_provide_clicert(sess, pk11_provide, provider); +} + +void ne_ssl_pkcs11_provider_destroy(ne_ssl_pkcs11_provider *prov) +{ + if (prov->session) { + pakchois_close_session(prov->session); + } + if (prov->clicert) { + ne_ssl_clicert_free(prov->clicert); + } + pakchois_module_destroy(prov->module); + ne_free(prov); +} + +#endif + + diff --git a/lib/x509/common.c b/lib/x509/common.c index c3e52f0867..58aefda57a 100644 --- a/lib/x509/common.c +++ b/lib/x509/common.c @@ -342,7 +342,7 @@ _gnutls_x509_data2hex (const opaque * data, size_t data_size, return GNUTLS_E_INTERNAL_ERROR; } - res = _gnutls_bin2hex (data, data_size, escaped, sizeof (escaped)); + res = _gnutls_bin2hex (data, data_size, escaped, sizeof (escaped), NULL); if (!res) { gnutls_assert (); diff --git a/lib/x509/dn.c b/lib/x509/dn.c index 726d3e49f4..c97d4814f3 100644 --- a/lib/x509/dn.c +++ b/lib/x509/dn.c @@ -281,7 +281,7 @@ _gnutls_x509_parse_dn (ASN1_TYPE asn1_struct, gnutls_assert (); _gnutls_x509_log ("Found OID: '%s' with value '%s'\n", - oid, _gnutls_bin2hex (value2, len, escaped, sizeof_escaped)); + oid, _gnutls_bin2hex (value2, len, escaped, sizeof_escaped, NULL)); goto cleanup; } STR_APPEND (str_escape (string, escaped, sizeof_escaped)); diff --git a/src/Makefile.am b/src/Makefile.am index f518dc95f9..edd72231b0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -80,7 +80,7 @@ noinst_LTLIBRARIES += libcmd-cli-debug.la libcmd_cli_debug_la_CFLAGS = libcmd_cli_debug_la_SOURCES = tls_test.gaa tls_test-gaa.h tls_test-gaa.c -certtool_SOURCES = certtool.c prime.c +certtool_SOURCES = certtool.c prime.c pkcs11.c certtool_LDADD = ../lib/libgnutls.la ../libextra/libgnutls-extra.la certtool_LDADD += libcmd-certtool.la ../gl/libgnu.la certtool_LDADD += $(LTLIBGCRYPT) diff --git a/src/certtool-common.h b/src/certtool-common.h index cb2e9ab1d7..07be84c5fc 100644 --- a/src/certtool-common.h +++ b/src/certtool-common.h @@ -22,10 +22,13 @@ enum ACTION_PGP_INFO, ACTION_PGP_PRIVKEY_INFO, ACTION_RING_INFO, - ACTION_REQUEST + ACTION_REQUEST, + ACTION_PKCS11_LIST, }; #define TYPE_CRT 1 #define TYPE_CRQ 2 void certtool_version (void); +void pkcs11_list( const char* url); + diff --git a/src/certtool-gaa.c b/src/certtool-gaa.c index 8ff1d81fcd..b0bfd3f73c 100644 --- a/src/certtool-gaa.c +++ b/src/certtool-gaa.c @@ -161,6 +161,9 @@ void gaa_help(void) __gaa_helpsingle(0, "v1", "", "Generate an X.509 version 1 certificate (no extensions)."); __gaa_helpsingle(0, "to-p12", "", "Generate a PKCS #12 structure."); __gaa_helpsingle(0, "to-p8", "", "Generate a PKCS #8 key structure."); + __gaa_helpsingle(0, "pkcs11-provider", "Library ", "Specify the pkcs11 provider library"); + __gaa_helpsingle(0, "pkcs11-url", "URL ", "Specify a pkcs11 URL"); + __gaa_helpsingle(0, "pkcs11-list", "", "List objects specified by a PKCS#11 URL"); __gaa_helpsingle('8', "pkcs8", "", "Use PKCS #8 format for private keys."); __gaa_helpsingle(0, "dsa", "", "Use DSA keys."); __gaa_helpsingle(0, "hash", "STR ", "Hash algorithm to use for signing (MD5,SHA1,RMD160,SHA256,SHA384,SHA512)."); @@ -192,32 +195,36 @@ typedef struct _gaainfo gaainfo; struct _gaainfo { -#line 131 "certtool.gaa" +#line 139 "certtool.gaa" int debug; -#line 127 "certtool.gaa" +#line 135 "certtool.gaa" char *pkcs_cipher; -#line 124 "certtool.gaa" +#line 132 "certtool.gaa" char *template; -#line 121 "certtool.gaa" +#line 129 "certtool.gaa" char *infile; -#line 118 "certtool.gaa" +#line 126 "certtool.gaa" char *outfile; -#line 115 "certtool.gaa" +#line 123 "certtool.gaa" int quick_random; -#line 112 "certtool.gaa" +#line 120 "certtool.gaa" int bits; -#line 108 "certtool.gaa" +#line 116 "certtool.gaa" int outcert_format; -#line 104 "certtool.gaa" +#line 112 "certtool.gaa" int incert_format; -#line 101 "certtool.gaa" +#line 109 "certtool.gaa" int export; -#line 98 "certtool.gaa" +#line 106 "certtool.gaa" char *hash; -#line 95 "certtool.gaa" +#line 103 "certtool.gaa" int dsa; -#line 92 "certtool.gaa" +#line 100 "certtool.gaa" int pkcs8; +#line 95 "certtool.gaa" + char* pkcs11_url; +#line 92 "certtool.gaa" + char* pkcs11_provider; #line 85 "certtool.gaa" int v1_cert; #line 82 "certtool.gaa" @@ -294,7 +301,7 @@ static int gaa_error = 0; #define GAA_MULTIPLE_OPTION 3 #define GAA_REST 0 -#define GAA_NB_OPTION 49 +#define GAA_NB_OPTION 52 #define GAAOPTID_version 1 #define GAAOPTID_help 2 #define GAAOPTID_debug 3 @@ -312,38 +319,41 @@ static int gaa_error = 0; #define GAAOPTID_hash 15 #define GAAOPTID_dsa 16 #define GAAOPTID_pkcs8 17 -#define GAAOPTID_to_p8 18 -#define GAAOPTID_to_p12 19 -#define GAAOPTID_v1 20 -#define GAAOPTID_fix_key 21 -#define GAAOPTID_pgp_key_info 22 -#define GAAOPTID_key_info 23 -#define GAAOPTID_smime_to_p7 24 -#define GAAOPTID_p7_info 25 -#define GAAOPTID_p12_info 26 -#define GAAOPTID_no_crq_extensions 27 -#define GAAOPTID_crq_info 28 -#define GAAOPTID_crl_info 29 -#define GAAOPTID_pgp_ring_info 30 -#define GAAOPTID_pgp_certificate_info 31 -#define GAAOPTID_certificate_info 32 -#define GAAOPTID_password 33 -#define GAAOPTID_load_ca_certificate 34 -#define GAAOPTID_load_ca_privkey 35 -#define GAAOPTID_load_certificate 36 -#define GAAOPTID_load_request 37 -#define GAAOPTID_load_privkey 38 -#define GAAOPTID_get_dh_params 39 -#define GAAOPTID_generate_dh_params 40 -#define GAAOPTID_verify_crl 41 -#define GAAOPTID_verify_chain 42 -#define GAAOPTID_generate_request 43 -#define GAAOPTID_generate_privkey 44 -#define GAAOPTID_update_certificate 45 -#define GAAOPTID_generate_crl 46 -#define GAAOPTID_generate_proxy 47 -#define GAAOPTID_generate_certificate 48 -#define GAAOPTID_generate_self_signed 49 +#define GAAOPTID_pkcs11_list 18 +#define GAAOPTID_pkcs11_url 19 +#define GAAOPTID_pkcs11_provider 20 +#define GAAOPTID_to_p8 21 +#define GAAOPTID_to_p12 22 +#define GAAOPTID_v1 23 +#define GAAOPTID_fix_key 24 +#define GAAOPTID_pgp_key_info 25 +#define GAAOPTID_key_info 26 +#define GAAOPTID_smime_to_p7 27 +#define GAAOPTID_p7_info 28 +#define GAAOPTID_p12_info 29 +#define GAAOPTID_no_crq_extensions 30 +#define GAAOPTID_crq_info 31 +#define GAAOPTID_crl_info 32 +#define GAAOPTID_pgp_ring_info 33 +#define GAAOPTID_pgp_certificate_info 34 +#define GAAOPTID_certificate_info 35 +#define GAAOPTID_password 36 +#define GAAOPTID_load_ca_certificate 37 +#define GAAOPTID_load_ca_privkey 38 +#define GAAOPTID_load_certificate 39 +#define GAAOPTID_load_request 40 +#define GAAOPTID_load_privkey 41 +#define GAAOPTID_get_dh_params 42 +#define GAAOPTID_generate_dh_params 43 +#define GAAOPTID_verify_crl 44 +#define GAAOPTID_verify_chain 45 +#define GAAOPTID_generate_request 46 +#define GAAOPTID_generate_privkey 47 +#define GAAOPTID_update_certificate 48 +#define GAAOPTID_generate_crl 49 +#define GAAOPTID_generate_proxy 50 +#define GAAOPTID_generate_certificate 51 +#define GAAOPTID_generate_self_signed 52 #line 168 "gaa.skel" @@ -503,12 +513,31 @@ static int gaa_getint(char *arg) return tmp; } +static char gaa_getchar(char *arg) +{ + if(strlen(arg) != 1) + { + printf("Option %s: '%s' isn't an character\n", gaa_current_option, arg); + GAAERROR(-1); + } + return arg[0]; +} static char* gaa_getstr(char *arg) { return arg; } - +static float gaa_getfloat(char *arg) +{ + float tmp; + char a; + if(sscanf(arg, "%f%c", &tmp, &a) < 1) + { + printf("Option %s: '%s' isn't a float number\n", gaa_current_option, arg); + GAAERROR(-1); + } + return tmp; +} /* option structures */ struct GAAOPTION_debug @@ -553,6 +582,18 @@ struct GAAOPTION_hash int size1; }; +struct GAAOPTION_pkcs11_url +{ + char* arg1; + int size1; +}; + +struct GAAOPTION_pkcs11_provider +{ + char* arg1; + int size1; +}; + struct GAAOPTION_password { char* arg1; @@ -625,6 +666,8 @@ static int gaa_get_option_num(char *str, int status) GAA_CHECK1STR("", GAAOPTID_outfile); GAA_CHECK1STR("", GAAOPTID_bits); GAA_CHECK1STR("", GAAOPTID_hash); + GAA_CHECK1STR("", GAAOPTID_pkcs11_url); + GAA_CHECK1STR("", GAAOPTID_pkcs11_provider); GAA_CHECK1STR("", GAAOPTID_password); GAA_CHECK1STR("", GAAOPTID_load_ca_certificate); GAA_CHECK1STR("", GAAOPTID_load_ca_privkey); @@ -643,6 +686,7 @@ static int gaa_get_option_num(char *str, int status) GAA_CHECK1STR("", GAAOPTID_export_ciphers); GAA_CHECK1STR("", GAAOPTID_dsa); GAA_CHECK1STR("8", GAAOPTID_pkcs8); + GAA_CHECK1STR("", GAAOPTID_pkcs11_list); GAA_CHECK1STR("", GAAOPTID_to_p8); GAA_CHECK1STR("", GAAOPTID_to_p12); GAA_CHECK1STR("", GAAOPTID_v1); @@ -690,6 +734,9 @@ static int gaa_get_option_num(char *str, int status) GAA_CHECKSTR("hash", GAAOPTID_hash); GAA_CHECKSTR("dsa", GAAOPTID_dsa); GAA_CHECKSTR("pkcs8", GAAOPTID_pkcs8); + GAA_CHECKSTR("pkcs11-list", GAAOPTID_pkcs11_list); + GAA_CHECKSTR("pkcs11-url", GAAOPTID_pkcs11_url); + GAA_CHECKSTR("pkcs11-provider", GAAOPTID_pkcs11_provider); GAA_CHECKSTR("to-p8", GAAOPTID_to_p8); GAA_CHECKSTR("to-p12", GAAOPTID_to_p12); GAA_CHECKSTR("v1", GAAOPTID_v1); @@ -741,6 +788,8 @@ static int gaa_try(int gaa_num, int gaa_index, gaainfo *gaaval, char *opt_list) struct GAAOPTION_outfile GAATMP_outfile; struct GAAOPTION_bits GAATMP_bits; struct GAAOPTION_hash GAATMP_hash; + struct GAAOPTION_pkcs11_url GAATMP_pkcs11_url; + struct GAAOPTION_pkcs11_provider GAATMP_pkcs11_provider; struct GAAOPTION_password GAATMP_password; struct GAAOPTION_load_ca_certificate GAATMP_load_ca_certificate; struct GAAOPTION_load_ca_privkey GAATMP_load_ca_privkey; @@ -769,14 +818,14 @@ static int gaa_try(int gaa_num, int gaa_index, gaainfo *gaaval, char *opt_list) { case GAAOPTID_version: OK = 0; -#line 136 "certtool.gaa" +#line 144 "certtool.gaa" { certtool_version(); exit(0); ;}; return GAA_OK; break; case GAAOPTID_help: OK = 0; -#line 134 "certtool.gaa" +#line 142 "certtool.gaa" { gaa_help(); exit(0); ;}; return GAA_OK; @@ -786,7 +835,7 @@ static int gaa_try(int gaa_num, int gaa_index, gaainfo *gaaval, char *opt_list) GAA_TESTMOREARGS; GAA_FILL(GAATMP_debug.arg1, gaa_getint, GAATMP_debug.size1); gaa_index++; -#line 132 "certtool.gaa" +#line 140 "certtool.gaa" { gaaval->debug = GAATMP_debug.arg1 ;}; return GAA_OK; @@ -796,7 +845,7 @@ static int gaa_try(int gaa_num, int gaa_index, gaainfo *gaaval, char *opt_list) GAA_TESTMOREARGS; GAA_FILL(GAATMP_pkcs_cipher.arg1, gaa_getstr, GAATMP_pkcs_cipher.size1); gaa_index++; -#line 128 "certtool.gaa" +#line 136 "certtool.gaa" { gaaval->pkcs_cipher = GAATMP_pkcs_cipher.arg1 ;}; return GAA_OK; @@ -806,7 +855,7 @@ static int gaa_try(int gaa_num, int gaa_index, gaainfo *gaaval, char *opt_list) GAA_TESTMOREARGS; GAA_FILL(GAATMP_template.arg1, gaa_getstr, GAATMP_template.size1); gaa_index++; -#line 125 "certtool.gaa" +#line 133 "certtool.gaa" { gaaval->template = GAATMP_template.arg1 ;}; return GAA_OK; @@ -816,7 +865,7 @@ static int gaa_try(int gaa_num, int gaa_index, gaainfo *gaaval, char *opt_list) GAA_TESTMOREARGS; GAA_FILL(GAATMP_infile.arg1, gaa_getstr, GAATMP_infile.size1); gaa_index++; -#line 122 "certtool.gaa" +#line 130 "certtool.gaa" { gaaval->infile = GAATMP_infile.arg1 ;}; return GAA_OK; @@ -826,14 +875,14 @@ static int gaa_try(int gaa_num, int gaa_index, gaainfo *gaaval, char *opt_list) GAA_TESTMOREARGS; GAA_FILL(GAATMP_outfile.arg1, gaa_getstr, GAATMP_outfile.size1); gaa_index++; -#line 119 "certtool.gaa" +#line 127 "certtool.gaa" { gaaval->outfile = GAATMP_outfile.arg1 ;}; return GAA_OK; break; case GAAOPTID_disable_quick_random: OK = 0; -#line 116 "certtool.gaa" +#line 124 "certtool.gaa" { gaaval->quick_random = 0; ;}; return GAA_OK; @@ -843,42 +892,42 @@ static int gaa_try(int gaa_num, int gaa_index, gaainfo *gaaval, char *opt_list) GAA_TESTMOREARGS; GAA_FILL(GAATMP_bits.arg1, gaa_getint, GAATMP_bits.size1); gaa_index++; -#line 113 "certtool.gaa" +#line 121 "certtool.gaa" { gaaval->bits = GAATMP_bits.arg1 ;}; return GAA_OK; break; case GAAOPTID_outraw: OK = 0; -#line 110 "certtool.gaa" +#line 118 "certtool.gaa" { gaaval->outcert_format=1 ;}; return GAA_OK; break; case GAAOPTID_outder: OK = 0; -#line 109 "certtool.gaa" +#line 117 "certtool.gaa" { gaaval->outcert_format=1 ;}; return GAA_OK; break; case GAAOPTID_inraw: OK = 0; -#line 106 "certtool.gaa" +#line 114 "certtool.gaa" { gaaval->incert_format=1 ;}; return GAA_OK; break; case GAAOPTID_inder: OK = 0; -#line 105 "certtool.gaa" +#line 113 "certtool.gaa" { gaaval->incert_format=1 ;}; return GAA_OK; break; case GAAOPTID_export_ciphers: OK = 0; -#line 102 "certtool.gaa" +#line 110 "certtool.gaa" { gaaval->export=1 ;}; return GAA_OK; @@ -888,25 +937,52 @@ static int gaa_try(int gaa_num, int gaa_index, gaainfo *gaaval, char *opt_list) GAA_TESTMOREARGS; GAA_FILL(GAATMP_hash.arg1, gaa_getstr, GAATMP_hash.size1); gaa_index++; -#line 99 "certtool.gaa" +#line 107 "certtool.gaa" { gaaval->hash = GAATMP_hash.arg1 ;}; return GAA_OK; break; case GAAOPTID_dsa: OK = 0; -#line 96 "certtool.gaa" +#line 104 "certtool.gaa" { gaaval->dsa=1 ;}; return GAA_OK; break; case GAAOPTID_pkcs8: OK = 0; -#line 93 "certtool.gaa" +#line 101 "certtool.gaa" { gaaval->pkcs8=1 ;}; return GAA_OK; break; + case GAAOPTID_pkcs11_list: + OK = 0; +#line 98 "certtool.gaa" +{ gaaval->action = ACTION_PKCS11_LIST ;}; + + return GAA_OK; + break; + case GAAOPTID_pkcs11_url: + OK = 0; + GAA_TESTMOREARGS; + GAA_FILL(GAATMP_pkcs11_url.arg1, gaa_getstr, GAATMP_pkcs11_url.size1); + gaa_index++; +#line 96 "certtool.gaa" +{ gaaval->pkcs11_url = GAATMP_pkcs11_url.arg1 ;}; + + return GAA_OK; + break; + case GAAOPTID_pkcs11_provider: + OK = 0; + GAA_TESTMOREARGS; + GAA_FILL(GAATMP_pkcs11_provider.arg1, gaa_getstr, GAATMP_pkcs11_provider.size1); + gaa_index++; +#line 93 "certtool.gaa" +{ gaaval->pkcs11_provider = GAATMP_pkcs11_provider.arg1 ;}; + + return GAA_OK; + break; case GAAOPTID_to_p8: OK = 0; #line 90 "certtool.gaa" @@ -1159,29 +1235,27 @@ static int gaa_try(int gaa_num, int gaa_index, gaainfo *gaaval, char *opt_list) int gaa(int argc, char **argv, gaainfo *gaaval) { int tmp1, tmp2; - int l; - size_t i, j; + int i, j; char *opt_list; - i = 0; - GAAargv = argv; GAAargc = argc; opt_list = (char*) gaa_malloc(GAA_NB_OPTION + 1); - for(l = 0; l < GAA_NB_OPTION + 1; l++) - opt_list[l] = 0; + for(i = 0; i < GAA_NB_OPTION + 1; i++) + opt_list[i] = 0; /* initialization */ if(inited == 0) { -#line 138 "certtool.gaa" +#line 146 "certtool.gaa" { gaaval->bits = 2048; gaaval->pkcs8 = 0; gaaval->privkey = NULL; gaaval->ca=NULL; gaaval->ca_privkey = NULL; gaaval->debug=1; gaaval->request = NULL; gaaval->infile = NULL; gaaval->outfile = NULL; gaaval->cert = NULL; gaaval->incert_format = 0; gaaval->outcert_format = 0; gaaval->action=-1; gaaval->pass = NULL; gaaval->v1_cert = 0; gaaval->export = 0; gaaval->template = NULL; gaaval->hash=NULL; gaaval->fix_key = 0; gaaval->quick_random=1; - gaaval->privkey_op = 0; gaaval->pkcs_cipher = "3des"; gaaval->crq_extensions=1; ;}; + gaaval->privkey_op = 0; gaaval->pkcs_cipher = "3des"; gaaval->crq_extensions=1; gaaval->pkcs11_provider= NULL; + gaaval->pkcs11_url = NULL; ;}; } inited = 1; @@ -1192,27 +1266,27 @@ int gaa(int argc, char **argv, gaainfo *gaaval) gaa_arg_used = gaa_malloc(argc * sizeof(char)); } - for(l = 1; l < argc; l++) - gaa_arg_used[l] = 0; - for(l = 1; l < argc; l++) + for(i = 1; i < argc; i++) + gaa_arg_used[i] = 0; + for(i = 1; i < argc; i++) { - if(gaa_arg_used[l] == 0) + if(gaa_arg_used[i] == 0) { j = 0; - tmp1 = gaa_is_an_argument(GAAargv[l]); + tmp1 = gaa_is_an_argument(GAAargv[i]); switch(tmp1) { case GAA_WORD_OPTION: j++; case GAA_LETTER_OPTION: j++; - tmp2 = gaa_get_option_num(argv[l]+j, tmp1); + tmp2 = gaa_get_option_num(argv[i]+j, tmp1); if(tmp2 == GAA_ERROR_NOMATCH) { - printf("Invalid option '%s'\n", argv[l]+j); + printf("Invalid option '%s'\n", argv[i]+j); return 0; } - switch(gaa_try(tmp2, l+1, gaaval, opt_list)) + switch(gaa_try(tmp2, i+1, gaaval, opt_list)) { case GAA_ERROR_NOTENOUGH_ARGS: printf("'%s': not enough arguments\n",gaa_current_option); @@ -1225,18 +1299,18 @@ int gaa(int argc, char **argv, gaainfo *gaaval) default: printf("Unknown error\n"); } - gaa_arg_used[l] = 1; + gaa_arg_used[i] = 1; break; case GAA_MULTIPLE_OPTION: - for(j = 1; j < strlen(argv[l]); j++) + for(j = 1; j < strlen(argv[i]); j++) { - tmp2 = gaa_get_option_num(argv[l]+j, tmp1); + tmp2 = gaa_get_option_num(argv[i]+j, tmp1); if(tmp2 == GAA_ERROR_NOMATCH) { - printf("Invalid option '%c'\n", *(argv[l]+j)); + printf("Invalid option '%c'\n", *(argv[i]+j)); return 0; } - switch(gaa_try(tmp2, l+1, gaaval, opt_list)) + switch(gaa_try(tmp2, i+1, gaaval, opt_list)) { case GAA_ERROR_NOTENOUGH_ARGS: printf("'%s': not enough arguments\n",gaa_current_option); @@ -1250,7 +1324,7 @@ int gaa(int argc, char **argv, gaainfo *gaaval) printf("Unknown error\n"); } } - gaa_arg_used[l] = 1; + gaa_arg_used[i] = 1; break; default: break; } @@ -1276,9 +1350,9 @@ if(gaa_processing_file == 0) } #endif } - for(l = 1; l < argc; l++) + for(i = 1; i < argc; i++) { - if(gaa_arg_used[l] == 0) + if(gaa_arg_used[i] == 0) { printf("Too many arguments\n"); return 0; @@ -1329,7 +1403,7 @@ static int gaa_internal_get_next_str(FILE *file, gaa_str_node *tmp_str, int argc len++; a = fgetc( file); - if(a==EOF) return 0; /* a = ' '; */ + if(a==EOF) return 0; //a = ' '; } len += 1; diff --git a/src/certtool-gaa.h b/src/certtool-gaa.h index 3d4ee83a0f..28e3cce57c 100644 --- a/src/certtool-gaa.h +++ b/src/certtool-gaa.h @@ -8,32 +8,36 @@ typedef struct _gaainfo gaainfo; struct _gaainfo { -#line 131 "certtool.gaa" +#line 139 "certtool.gaa" int debug; -#line 127 "certtool.gaa" +#line 135 "certtool.gaa" char *pkcs_cipher; -#line 124 "certtool.gaa" +#line 132 "certtool.gaa" char *template; -#line 121 "certtool.gaa" +#line 129 "certtool.gaa" char *infile; -#line 118 "certtool.gaa" +#line 126 "certtool.gaa" char *outfile; -#line 115 "certtool.gaa" +#line 123 "certtool.gaa" int quick_random; -#line 112 "certtool.gaa" +#line 120 "certtool.gaa" int bits; -#line 108 "certtool.gaa" +#line 116 "certtool.gaa" int outcert_format; -#line 104 "certtool.gaa" +#line 112 "certtool.gaa" int incert_format; -#line 101 "certtool.gaa" +#line 109 "certtool.gaa" int export; -#line 98 "certtool.gaa" +#line 106 "certtool.gaa" char *hash; -#line 95 "certtool.gaa" +#line 103 "certtool.gaa" int dsa; -#line 92 "certtool.gaa" +#line 100 "certtool.gaa" int pkcs8; +#line 95 "certtool.gaa" + char* pkcs11_url; +#line 92 "certtool.gaa" + char* pkcs11_provider; #line 85 "certtool.gaa" int v1_cert; #line 82 "certtool.gaa" diff --git a/src/certtool.c b/src/certtool.c index 860ab9012b..b65386e027 100644 --- a/src/certtool.c +++ b/src/certtool.c @@ -25,6 +25,7 @@ #include <gnutls/x509.h> #include <gnutls/openpgp.h> #include <gnutls/pkcs12.h> +#include <gnutls/pkcs11.h> #include <gcrypt.h> @@ -941,6 +942,25 @@ gaa_parser (int argc, char **argv) if ((ret = gnutls_global_init ()) < 0) error (EXIT_FAILURE, 0, "global_init: %s", gnutls_strerror (ret)); + + if (info.pkcs11_provider != NULL) + { + ret = gnutls_pkcs11_init(GNUTLS_PKCS11_FLAG_MANUAL, NULL); + if (ret < 0) + fprintf (stderr, "pkcs11_init: %s", gnutls_strerror (ret)); + else { + ret = gnutls_pkcs11_add_provider(info.pkcs11_provider, NULL); + if (ret < 0) + error (EXIT_FAILURE, 0, "pkcs11_add_provider: %s", gnutls_strerror (ret)); + } + } + else + { + ret = gnutls_pkcs11_init(GNUTLS_PKCS11_FLAG_AUTO, NULL); + if (ret < 0) + fprintf (stderr, "pkcs11_init: %s", gnutls_strerror (ret)); + } + if ((ret = gnutls_global_init_extra ()) < 0) error (EXIT_FAILURE, 0, "global_init_extra: %s", gnutls_strerror (ret)); @@ -1007,6 +1027,9 @@ gaa_parser (int argc, char **argv) case ACTION_GENERATE_PKCS8: generate_pkcs8 (); break; + case ACTION_PKCS11_LIST: + pkcs11_list(info.pkcs11_url); + break; #ifdef ENABLE_OPENPGP case ACTION_PGP_INFO: pgp_certificate_info (); @@ -1026,6 +1049,9 @@ gaa_parser (int argc, char **argv) exit (0); } fclose (outfile); + + gnutls_pkcs11_deinit(); + gnutls_global_deinit(); } #define MAX_CRTS 500 diff --git a/src/certtool.gaa b/src/certtool.gaa index 096f119613..53d3ac169a 100644 --- a/src/certtool.gaa +++ b/src/certtool.gaa @@ -89,6 +89,14 @@ option (to-p12) { $action = ACTION_TO_PKCS12; } "Generate a PKCS #12 structure." option (to-p8) { $action = ACTION_GENERATE_PKCS8; } "Generate a PKCS #8 key structure." +#char* pkcs11_provider; +option (pkcs11-provider) STR "Library" { $pkcs11_provider = $1 } "Specify the pkcs11 provider library" + +#char* pkcs11_url; +option (pkcs11-url) STR "URL" { $pkcs11_url = $1 } "Specify a pkcs11 URL" + +option (pkcs11-list) { $action = ACTION_PKCS11_LIST } "List objects specified by a PKCS#11 URL" + #int pkcs8; option (8, pkcs8) { $pkcs8=1 } "Use PKCS #8 format for private keys." @@ -139,4 +147,5 @@ init { $bits = 2048; $pkcs8 = 0; $privkey = NULL; $ca=NULL; $ca_privkey = NULL; $debug=1; $request = NULL; $infile = NULL; $outfile = NULL; $cert = NULL; $incert_format = 0; $outcert_format = 0; $action=-1; $pass = NULL; $v1_cert = 0; $export = 0; $template = NULL; $hash=NULL; $fix_key = 0; $quick_random=1; - $privkey_op = 0; $pkcs_cipher = "3des"; $crq_extensions=1; } + $privkey_op = 0; $pkcs_cipher = "3des"; $crq_extensions=1; $pkcs11_provider= NULL; + $pkcs11_url = NULL; } diff --git a/src/pkcs11.c b/src/pkcs11.c new file mode 100644 index 0000000000..fc3953d5dc --- /dev/null +++ b/src/pkcs11.c @@ -0,0 +1,50 @@ +#include <config.h> + +#include <gnutls/gnutls.h> +#include <gnutls/extra.h> +#include <gnutls/pkcs11.h> +#include <stdio.h> +#include <stdlib.h> +#include "certtool-common.h" + +void pkcs11_list( const char* url) +{ +gnutls_pkcs11_crt_t *crt_list; +unsigned int crt_list_size = 0; +int ret; +char* output; +int i; + + if (url == NULL) + url = "pkcs11:"; + + ret = gnutls_pkcs11_crt_list_import( NULL, &crt_list_size, url, GNUTLS_PKCS11_CRT_ATTR_ALL); + if (ret != 0 && ret != GNUTLS_E_SHORT_MEMORY_BUFFER) { + fprintf(stderr, "Error in crt_list_import (1): %s\n", gnutls_strerror(ret)); + exit(1); + } + + if (crt_list_size == 0) { + fprintf(stderr, "No matching certificates found\n"); + exit(0); + } + + crt_list = malloc(sizeof(*crt_list)*crt_list_size); + if (crt_list == NULL) { + fprintf(stderr, "Memory error\n"); + exit(1); + } + + ret = gnutls_pkcs11_crt_list_import( crt_list, &crt_list_size, url, GNUTLS_PKCS11_CRT_ATTR_ALL); + if (ret < 0) { + fprintf(stderr, "Error in crt_list_import: %s\n", gnutls_strerror(ret)); + exit(1); + } + + for (i=0;i<crt_list_size;i++) { + gnutls_pkcs11_crt_export_url(crt_list[i], &output); + fprintf(stderr, "cert[%d]: %s\n\n", i, output); + } + + return; +} |