From: Nikos Mavrogiannopoulos Date: Sat, 15 May 2010 20:00:22 +0000 (+0200) Subject: Added gnutls_pkcs11_privkey_t and gnutls_privkey_t types. Those are X-Git-Tag: gnutls_2_11_3~304 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=be560a8138daced8f37f2b6012e4417311dd4983;p=thirdparty%2Fgnutls.git Added gnutls_pkcs11_privkey_t and gnutls_privkey_t types. Those are an abstract private key type that can be used to sign/encrypt any private key of pkcs11,x509 or openpgp types. Added support for PKCS11 in gnutls-cli/gnutls-serv. --- diff --git a/doc/manpages/Makefile.am b/doc/manpages/Makefile.am index 6afff0ae5a..49f13fd61e 100644 --- a/doc/manpages/Makefile.am +++ b/doc/manpages/Makefile.am @@ -178,6 +178,7 @@ APIMANS += gnutls_priority_deinit.3 APIMANS += gnutls_priority_set_direct.3 APIMANS += gnutls_set_default_priority.3 APIMANS += gnutls_set_default_export_priority.3 +APIMANS += gnutls_privkey_sign_data.3 APIMANS += gnutls_psk_free_client_credentials.3 APIMANS += gnutls_psk_allocate_client_credentials.3 APIMANS += gnutls_psk_set_client_credentials.3 @@ -273,6 +274,8 @@ APIMANS += gnutls_certificate_set_x509_crl_file.3 APIMANS += gnutls_certificate_set_x509_simple_pkcs12_file.3 APIMANS += gnutls_certificate_set_x509_simple_pkcs12_mem.3 APIMANS += gnutls_certificate_free_crls.3 +APIMANS += gnutls_pkcs11_privkey_sign_data.3 +APIMANS += gnutls_pkcs11_privkey_decrypt_data.3 APIMANS += gnutls_pem_base64_encode.3 APIMANS += gnutls_pem_base64_encode_alloc.3 APIMANS += gnutls_pem_base64_decode.3 @@ -422,8 +425,9 @@ APIMANS += gnutls_x509_privkey_export_rsa_raw.3 APIMANS += gnutls_x509_privkey_export_dsa_raw.3 APIMANS += gnutls_x509_privkey_generate.3 APIMANS += gnutls_x509_privkey_get_key_id.3 -APIMANS += gnutls_x509_privkey_sign_data.3 +APIMANS += gnutls_x509_privkey_sign_data2.3 APIMANS += gnutls_x509_privkey_sign_hash.3 +APIMANS += gnutls_x509_privkey_sign_data.3 APIMANS += gnutls_x509_privkey_verify_data.3 APIMANS += gnutls_x509_privkey_fix.3 APIMANS += gnutls_x509_privkey_export_pkcs8.3 @@ -523,6 +527,7 @@ APIMANS += gnutls_certificate_set_openpgp_keyring_file.3 APIMANS += gnutls_certificate_set_openpgp_keyring_mem.3 APIMANS += gnutls_openpgp_set_recv_key_function.3 APIMANS += gnutls_openpgp_privkey_sign_hash.3 +APIMANS += gnutls_openpgp_privkey_decrypt_data.3 APIMANS += gnutls_openpgp_crt_print.3 APIMANS += gnutls_openpgp_crt_init.3 APIMANS += gnutls_openpgp_crt_deinit.3 diff --git a/lib/Makefile.am b/lib/Makefile.am index e2cde09759..8716db6129 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -82,10 +82,10 @@ 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 pk-libgcrypt.c mpi-libgcrypt.c cryptodev.c \ rnd-libgcrypt.c cipher-libgcrypt.c mac-libgcrypt.c ext_signature.c \ - crypto-api.c ext_safe_renegotiation.c gnutls_mbuffers.c + crypto-api.c ext_safe_renegotiation.c gnutls_mbuffers.c gnutls_privkey.c if ENABLE_PKCS11 -COBJECTS += pkcs11.c +COBJECTS += pkcs11.c pkcs11_privkey.c endif if ENABLE_OPRFI diff --git a/lib/auth_cert.c b/lib/auth_cert.c index 89ec4bd23f..2b7169886c 100644 --- a/lib/auth_cert.c +++ b/lib/auth_cert.c @@ -45,21 +45,23 @@ #include #include #include +#include #include "debug.h" #ifdef ENABLE_OPENPGP # include "openpgp/gnutls_openpgp.h" -static gnutls_privkey *alloc_and_load_pgp_key (const gnutls_openpgp_privkey_t - key); +static gnutls_privkey_t alloc_and_load_pgp_key (const gnutls_openpgp_privkey_t + key, int deinit); static gnutls_cert *alloc_and_load_pgp_certs (gnutls_openpgp_crt_t cert); #endif static gnutls_cert *alloc_and_load_x509_certs (gnutls_x509_crt_t * certs, unsigned); -static gnutls_privkey *alloc_and_load_x509_key (gnutls_x509_privkey_t key); +static gnutls_privkey_t alloc_and_load_x509_key (gnutls_x509_privkey_t key, int deinit); +static gnutls_privkey_t alloc_and_load_pkcs11_key ( gnutls_pkcs11_privkey_t key, int deinit); /* Copies data from a internal certificate struct (gnutls_cert) to @@ -409,21 +411,47 @@ get_issuers (gnutls_session_t session, return 0; } +static void st_to_st2(gnutls_retr2_st *st2, gnutls_retr_st* st) +{ + st2->cert_type = st->type; + if (st->type == GNUTLS_CRT_OPENPGP) { + st2->key_type = GNUTLS_PRIVKEY_OPENPGP; + } else { + st2->key_type = GNUTLS_PRIVKEY_X509; + } + st2->ncerts = st->ncerts; + st2->deinit_all = st->deinit_all; + + switch(st2->cert_type) { + case GNUTLS_CRT_OPENPGP: + st2->cert.pgp = st->cert.pgp; + st2->key.pgp = st->key.pgp; + break; + case GNUTLS_CRT_X509: + st2->cert.x509 = st->cert.x509; + st2->key.x509 = st->key.x509; + break; + default: + return; + } + +} + /* Calls the client get callback. */ static int call_get_cert_callback (gnutls_session_t session, - gnutls_datum_t * issuers_dn, + const gnutls_datum_t * issuers_dn, int issuers_dn_length, gnutls_pk_algorithm_t * pk_algos, int pk_algos_length) { unsigned i; gnutls_cert *local_certs = NULL; - gnutls_privkey *local_key = NULL; - gnutls_retr_st st; + gnutls_privkey_t local_key = NULL; int ret = GNUTLS_E_INTERNAL_ERROR; gnutls_certificate_type_t type = gnutls_certificate_type_get (session); gnutls_certificate_credentials_t cred; + gnutls_retr2_st st2; cred = (gnutls_certificate_credentials_t) _gnutls_get_cred (session->key, GNUTLS_CRD_CERTIFICATE, NULL); @@ -433,29 +461,42 @@ call_get_cert_callback (gnutls_session_t session, return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } - memset (&st, 0, sizeof (st)); + memset (&st2, 0, sizeof (st2)); - if (session->security_parameters.entity == GNUTLS_SERVER) - { - if (cred->server_get_cert_callback == NULL) + if (cred->get_cert_callback) { - gnutls_assert (); - return GNUTLS_E_INTERNAL_ERROR; + ret = cred->get_cert_callback( session, issuers_dn, issuers_dn_length, + pk_algos, pk_algos_length, &st2); + } - ret = cred->server_get_cert_callback (session, &st); - } else - { /* CLIENT */ - if (cred->client_get_cert_callback == NULL) - { - gnutls_assert (); - return GNUTLS_E_INTERNAL_ERROR; + { /* compatibility mode */ + gnutls_retr_st st; + memset (&st, 0, sizeof (st)); + if (session->security_parameters.entity == GNUTLS_SERVER) + { + if (cred->server_get_cert_callback == NULL) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + ret = cred->server_get_cert_callback (session, &st); + if (ret >= 0) st_to_st2(&st2, &st); + } + else + { /* CLIENT */ + + if (cred->client_get_cert_callback == NULL) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + ret = cred->client_get_cert_callback (session, + issuers_dn, issuers_dn_length, + pk_algos, pk_algos_length, &st); + if (ret >= 0) st_to_st2(&st2, &st); + } } - ret = - cred->client_get_cert_callback (session, - issuers_dn, issuers_dn_length, - pk_algos, pk_algos_length, &st); - } if (ret < 0) { @@ -463,87 +504,119 @@ call_get_cert_callback (gnutls_session_t session, return GNUTLS_E_INTERNAL_ERROR; } - if (st.ncerts == 0) + if (st2.ncerts == 0) return 0; /* no certificate was selected */ - if (type != st.type) + if (type != st2.cert_type) { gnutls_assert (); ret = GNUTLS_E_INVALID_REQUEST; goto cleanup; } + if (type == GNUTLS_CRT_X509) { - local_certs = alloc_and_load_x509_certs (st.cert.x509, st.ncerts); - if (local_certs != NULL && st.key.x509 != NULL) - { - local_key = alloc_and_load_x509_key (st.key.x509); - if (local_key == NULL) - { - gnutls_assert (); - ret = GNUTLS_E_INTERNAL_ERROR; - goto cleanup; - } - } - + local_certs = alloc_and_load_x509_certs (st2.cert.x509, st2.ncerts); } else { /* PGP */ - if (st.ncerts > 1) - { - gnutls_assert (); - ret = GNUTLS_E_INVALID_REQUEST; - goto cleanup; - } - + if (st2.ncerts > 1) + { + gnutls_assert (); + ret = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } #ifdef ENABLE_OPENPGP - { - local_certs = alloc_and_load_pgp_certs (st.cert.pgp); - if (local_certs != NULL && st.key.pgp != NULL) { - local_key = alloc_and_load_pgp_key (st.key.pgp); - if (local_key == NULL) - { - gnutls_assert (); - ret = GNUTLS_E_INTERNAL_ERROR; - goto cleanup; - } - } + local_certs = alloc_and_load_pgp_certs (st2.cert.pgp); } +#else + ret = GNUTLS_E_UNIMPLEMENTED_FEATURE; + goto cleanup; #endif } + if (local_certs == NULL) { + gnutls_assert(); + ret = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + switch(st2.key_type) { + case GNUTLS_PRIVKEY_OPENPGP: +#ifdef ENABLE_OPENPGP + if (st2.key.pgp != NULL) + { + local_key = alloc_and_load_pgp_key (st2.key.pgp, st2.deinit_all); + if (local_key == NULL) + { + gnutls_assert (); + ret = GNUTLS_E_INTERNAL_ERROR; + goto cleanup; + } + } + break; +#endif + case GNUTLS_PRIVKEY_PKCS11: + if (st2.key.pkcs11 != NULL) + { + local_key = alloc_and_load_pkcs11_key (st2.key.pkcs11, st2.deinit_all); + if (local_key == NULL) + { + gnutls_assert (); + ret = GNUTLS_E_INTERNAL_ERROR; + goto cleanup; + } + } + break; + case GNUTLS_PRIVKEY_X509: + if (st2.key.x509 != NULL) + { + local_key = alloc_and_load_x509_key (st2.key.x509, st2.deinit_all); + if (local_key == NULL) + { + gnutls_assert (); + ret = GNUTLS_E_INTERNAL_ERROR; + goto cleanup; + } + } + break; + } + _gnutls_selected_certs_set (session, local_certs, - (local_certs != NULL) ? st.ncerts : 0, + (local_certs != NULL) ? st2.ncerts : 0, local_key, 1); ret = 0; cleanup: - if (st.type == GNUTLS_CRT_X509) + if (st2.cert_type == GNUTLS_CRT_X509) { - if (st.deinit_all) - { - for (i = 0; i < st.ncerts; i++) - { - gnutls_x509_crt_deinit (st.cert.x509[i]); - } - gnutls_free (st.cert.x509); - gnutls_x509_privkey_deinit (st.key.x509); - } + if (st2.deinit_all) + { + for (i = 0; i < st2.ncerts; i++) + { + gnutls_x509_crt_deinit (st2.cert.x509[i]); + } + } } else { #ifdef ENABLE_OPENPGP - if (st.deinit_all) - { - gnutls_openpgp_crt_deinit (st.cert.pgp); - gnutls_openpgp_privkey_deinit (st.key.pgp); - } + if (st2.deinit_all) + { + gnutls_openpgp_crt_deinit (st2.cert.pgp); + } #endif } + + if (ret < 0) + { + if (local_key != NULL) + gnutls_privkey_deinit(local_key); + } return ret; } @@ -576,7 +649,7 @@ _select_client_cert (gnutls_session_t session, return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } - if (cred->client_get_cert_callback != NULL) + if (cred->client_get_cert_callback != NULL || cred->get_cert_callback != NULL) { /* use a callback to get certificate @@ -646,7 +719,7 @@ _select_client_cert (gnutls_session_t session, _gnutls_selected_certs_set (session, &cred->cert_list[indx][0], cred->cert_list_length[indx], - &cred->pkey[indx], 0); + cred->pkey[indx], 0); } else { @@ -671,7 +744,7 @@ _gnutls_gen_x509_crt (gnutls_session_t session, opaque ** data) int ret, i; opaque *pdata; gnutls_cert *apr_cert_list; - gnutls_privkey *apr_pkey; + gnutls_privkey_t apr_pkey; int apr_cert_list_length; /* find the appropriate certificate @@ -730,7 +803,7 @@ _gnutls_gen_openpgp_certificate (gnutls_session_t session, opaque ** data) int ret; opaque *pdata; gnutls_cert *apr_cert_list; - gnutls_privkey *apr_pkey; + gnutls_privkey_t apr_pkey; int apr_cert_list_length; /* find the appropriate certificate */ @@ -804,7 +877,7 @@ _gnutls_gen_openpgp_certificate_fpr (gnutls_session_t session, opaque ** data) size_t fpr_size; opaque *pdata; gnutls_cert *apr_cert_list; - gnutls_privkey *apr_pkey; + gnutls_privkey_t apr_pkey; int apr_cert_list_length; /* find the appropriate certificate */ @@ -1443,7 +1516,7 @@ _gnutls_gen_cert_client_cert_vrfy (gnutls_session_t session, opaque ** data) { int ret; gnutls_cert *apr_cert_list; - gnutls_privkey *apr_pkey; + gnutls_privkey_t apr_pkey; int apr_cert_list_length, size; gnutls_datum_t signature; int total_data; @@ -1685,7 +1758,7 @@ int _gnutls_get_selected_cert (gnutls_session_t session, gnutls_cert ** apr_cert_list, int *apr_cert_list_length, - gnutls_privkey ** apr_pkey) + gnutls_privkey_t * apr_pkey) { if (session->security_parameters.entity == GNUTLS_SERVER) { @@ -1764,26 +1837,27 @@ alloc_and_load_x509_certs (gnutls_x509_crt_t * certs, unsigned ncerts) /* converts the given x509 key to gnutls_privkey* and allocates * space for it. */ -static gnutls_privkey * -alloc_and_load_x509_key (gnutls_x509_privkey_t key) +static gnutls_privkey_t +alloc_and_load_x509_key (gnutls_x509_privkey_t key, int deinit) { - gnutls_privkey *local_key; + gnutls_privkey_t local_key; int ret = 0; if (key == NULL) return NULL; - local_key = gnutls_malloc (sizeof (gnutls_privkey)); - if (local_key == NULL) + ret = gnutls_privkey_init(&local_key); + if (ret < 0) { gnutls_assert (); return NULL; } - ret = _gnutls_x509_privkey_to_gkey (local_key, key); + ret = gnutls_privkey_import_x509 (local_key, key, deinit?GNUTLS_PRIVKEY_IMPORT_AUTO_RELEASE:0); if (ret < 0) { gnutls_assert (); + gnutls_privkey_deinit(local_key); return NULL; } @@ -1838,26 +1912,27 @@ alloc_and_load_pgp_certs (gnutls_openpgp_crt_t cert) /* converts the given raw key to gnutls_privkey* and allocates * space for it. */ -static gnutls_privkey * -alloc_and_load_pgp_key (const gnutls_openpgp_privkey_t key) +static gnutls_privkey_t +alloc_and_load_pgp_key ( gnutls_openpgp_privkey_t key, int deinit) { - gnutls_privkey *local_key; + gnutls_privkey_t local_key; int ret = 0; if (key == NULL) return NULL; - local_key = gnutls_malloc (sizeof (gnutls_privkey)); - if (local_key == NULL) + ret = gnutls_privkey_init(&local_key); + if (ret < 0) { gnutls_assert (); return NULL; } - ret = _gnutls_openpgp_privkey_to_gkey (local_key, key); + ret = gnutls_privkey_import_openpgp (local_key, key, deinit?GNUTLS_PRIVKEY_IMPORT_AUTO_RELEASE:0); if (ret < 0) { gnutls_assert (); + gnutls_privkey_deinit(local_key); return NULL; } @@ -1865,6 +1940,36 @@ alloc_and_load_pgp_key (const gnutls_openpgp_privkey_t key) } #endif +/* converts the given raw key to gnutls_privkey* and allocates + * space for it. + */ +static gnutls_privkey_t +alloc_and_load_pkcs11_key ( gnutls_pkcs11_privkey_t key, int deinit) +{ + gnutls_privkey_t local_key; + int ret = 0; + + if (key == NULL) + return NULL; + + ret = gnutls_privkey_init(&local_key); + if (ret < 0) + { + gnutls_assert (); + return NULL; + } + + ret = gnutls_privkey_import_pkcs11 (local_key, key, deinit?GNUTLS_PRIVKEY_IMPORT_AUTO_RELEASE:0); + if (ret < 0) + { + gnutls_assert (); + gnutls_privkey_deinit(local_key); + return NULL; + } + + return local_key; +} + void _gnutls_selected_certs_deinit (gnutls_session_t session) { @@ -1880,12 +1985,7 @@ _gnutls_selected_certs_deinit (gnutls_session_t session) session->internals.selected_cert_list = NULL; session->internals.selected_cert_list_length = 0; - _gnutls_gkey_deinit (session->internals.selected_key); - if (session->internals.selected_key) - { - gnutls_free (session->internals.selected_key); - session->internals.selected_key = NULL; - } + session->internals.selected_key = NULL; } return; @@ -1894,7 +1994,7 @@ _gnutls_selected_certs_deinit (gnutls_session_t session) void _gnutls_selected_certs_set (gnutls_session_t session, gnutls_cert * certs, int ncerts, - gnutls_privkey * key, int need_free) + gnutls_privkey_t key, int need_free) { _gnutls_selected_certs_deinit (session); @@ -1979,7 +2079,7 @@ _gnutls_server_select_cert (gnutls_session_t session, _gnutls_selected_certs_set (session, &cred->cert_list[idx][0], cred->cert_list_length[idx], - &cred->pkey[idx], 0); + cred->pkey[idx], 0); } else /* Certificate does not support REQUESTED_ALGO. */ diff --git a/lib/auth_cert.h b/lib/auth_cert.h index cdc91c0336..a0a82e7213 100644 --- a/lib/auth_cert.h +++ b/lib/auth_cert.h @@ -30,6 +30,8 @@ # include "auth_dh_common.h" # include "x509/x509_int.h" # include "openpgp/openpgp_int.h" +# include +# include /* This structure may be complex, but it's the only way to * support a server that has multiple certificates @@ -57,7 +59,7 @@ typedef struct gnutls_certificate_credentials_st * This is the same with the number of pkeys. */ - gnutls_privkey *pkey; + gnutls_privkey_t *pkey; /* private keys. It contains ncerts private * keys. pkey[i] corresponds to certificate in * cert_list[i][0]. @@ -91,9 +93,11 @@ typedef struct gnutls_certificate_credentials_st */ gnutls_datum_t x509_rdn_sequence; - gnutls_certificate_client_retrieve_function *client_get_cert_callback; - gnutls_certificate_server_retrieve_function *server_get_cert_callback; + gnutls_certificate_client_retrieve_function *client_get_cert_callback; /* deprecated */ + gnutls_certificate_server_retrieve_function *server_get_cert_callback; /* deprecated */ gnutls_certificate_verify_function *verify_callback; + + gnutls_certificate_retrieve_function *get_cert_callback; } certificate_credentials_st; typedef struct rsa_info_st @@ -141,14 +145,14 @@ int _gnutls_proc_cert_server_certificate (gnutls_session_t, opaque *, size_t); int _gnutls_get_selected_cert (gnutls_session_t session, gnutls_cert ** apr_cert_list, int *apr_cert_list_length, - gnutls_privkey ** apr_pkey); + gnutls_privkey_t * apr_pkey); int _gnutls_server_select_cert (struct gnutls_session_int *, gnutls_pk_algorithm_t); void _gnutls_selected_certs_deinit (gnutls_session_t session); void _gnutls_selected_certs_set (gnutls_session_t session, gnutls_cert * certs, int ncerts, - gnutls_privkey * key, int need_free); + gnutls_privkey_t key, int need_free); #define _gnutls_proc_cert_client_certificate _gnutls_proc_cert_server_certificate diff --git a/lib/auth_dhe.c b/lib/auth_dhe.c index 224adfb621..0a3c286e00 100644 --- a/lib/auth_dhe.c +++ b/lib/auth_dhe.c @@ -87,7 +87,7 @@ gen_dhe_server_kx (gnutls_session_t session, opaque ** data) const bigint_t *mpis; int ret = 0, data_size; gnutls_cert *apr_cert_list; - gnutls_privkey *apr_pkey; + gnutls_privkey_t apr_pkey; int apr_cert_list_length; gnutls_datum_t signature, ddata; gnutls_certificate_credentials_t cred; diff --git a/lib/auth_rsa.c b/lib/auth_rsa.c index 7d22a9151f..261803952f 100644 --- a/lib/auth_rsa.c +++ b/lib/auth_rsa.c @@ -44,7 +44,7 @@ #include int _gnutls_gen_rsa_client_kx (gnutls_session_t, opaque **); -int _gnutls_proc_rsa_client_kx (gnutls_session_t, opaque *, size_t); +static int proc_rsa_client_kx (gnutls_session_t, opaque *, size_t); const mod_auth_st rsa_auth_struct = { "RSA", @@ -58,7 +58,7 @@ const mod_auth_st rsa_auth_struct = { _gnutls_proc_cert_server_certificate, _gnutls_proc_cert_client_certificate, NULL, /* proc server kx */ - _gnutls_proc_rsa_client_kx, /* proc client kx */ + proc_rsa_client_kx, /* proc client kx */ _gnutls_proc_cert_client_cert_vrfy, /* proc client cert vrfy */ _gnutls_proc_cert_cert_req /* proc server cert request */ }; @@ -144,76 +144,13 @@ _gnutls_get_public_rsa_params (gnutls_session_t session, return 0; } -/* This function reads the RSA parameters from the private key - */ static int -_gnutls_get_private_rsa_params (gnutls_session_t session, - bigint_t ** params, int *params_size) -{ - int bits; - gnutls_certificate_credentials_t cred; - gnutls_rsa_params_t rsa_params; - - cred = (gnutls_certificate_credentials_t) - _gnutls_get_cred (session->key, GNUTLS_CRD_CERTIFICATE, NULL); - if (cred == NULL) - { - gnutls_assert (); - return GNUTLS_E_INSUFFICIENT_CREDENTIALS; - } - - if (session->internals.selected_cert_list == NULL) - { - gnutls_assert (); - return GNUTLS_E_INSUFFICIENT_CREDENTIALS; - } - - bits = - _gnutls_mpi_get_nbits (session->internals. - selected_cert_list[0].params[0]); - - if (_gnutls_cipher_suite_get_kx_algo - (&session->security_parameters.current_cipher_suite) - == GNUTLS_KX_RSA_EXPORT && bits > 512) - { - - rsa_params = - _gnutls_certificate_get_rsa_params (cred->rsa_params, - cred->params_func, session); - /* EXPORT case: */ - if (rsa_params == NULL) - { - gnutls_assert (); - return GNUTLS_E_NO_TEMPORARY_RSA_PARAMS; - } - - /* In the export case, we do use temporary RSA params - * of 512 bits size. The params in the certificate are - * used to sign this temporary stuff. - */ - *params_size = RSA_PRIVATE_PARAMS; - *params = rsa_params->params; - - return 0; - } - - /* non export cipher suites. */ - - *params_size = session->internals.selected_key->params_size; - *params = session->internals.selected_key->params; - - return 0; -} - -int -_gnutls_proc_rsa_client_kx (gnutls_session_t session, opaque * data, +proc_rsa_client_kx (gnutls_session_t session, opaque * data, size_t _data_size) { gnutls_datum_t plaintext; gnutls_datum_t ciphertext; int ret, dsize; - bigint_t *params; - int params_len; int randomize_key = 0; ssize_t data_size = _data_size; @@ -240,14 +177,7 @@ _gnutls_proc_rsa_client_kx (gnutls_session_t session, opaque * data, ciphertext.size = dsize; } - ret = _gnutls_get_private_rsa_params (session, ¶ms, ¶ms_len); - if (ret < 0) - { - gnutls_assert (); - return ret; - } - - ret = _gnutls_pkcs1_rsa_decrypt (&plaintext, &ciphertext, params, params_len, 2); /* btype==2 */ + ret = gnutls_privkey_decrypt_data (session->internals.selected_key, 0, &ciphertext, &plaintext); if (ret < 0 || plaintext.size != GNUTLS_MASTER_SIZE) { diff --git a/lib/auth_rsa_export.c b/lib/auth_rsa_export.c index ba61db63d0..fc738b7146 100644 --- a/lib/auth_rsa_export.c +++ b/lib/auth_rsa_export.c @@ -42,11 +42,13 @@ #include #include #include +#include int _gnutls_gen_rsa_client_kx (gnutls_session_t, opaque **); -int _gnutls_proc_rsa_client_kx (gnutls_session_t, opaque *, size_t); static int gen_rsa_export_server_kx (gnutls_session_t, opaque **); static int proc_rsa_export_server_kx (gnutls_session_t, opaque *, size_t); +static int proc_rsa_export_client_kx (gnutls_session_t session, opaque * data, + size_t _data_size); const mod_auth_st rsa_export_auth_struct = { "RSA EXPORT", @@ -60,11 +62,176 @@ const mod_auth_st rsa_export_auth_struct = { _gnutls_proc_cert_server_certificate, _gnutls_proc_cert_client_certificate, proc_rsa_export_server_kx, - _gnutls_proc_rsa_client_kx, /* proc client kx */ + proc_rsa_export_client_kx, /* proc client kx */ _gnutls_proc_cert_client_cert_vrfy, /* proc client cert vrfy */ _gnutls_proc_cert_cert_req /* proc server cert request */ }; +/* This function reads the RSA parameters from the private key + */ +static int +_gnutls_get_private_rsa_params (gnutls_session_t session, + bigint_t ** params, int *params_size) +{ + int bits; + gnutls_certificate_credentials_t cred; + gnutls_rsa_params_t rsa_params; + + cred = (gnutls_certificate_credentials_t) + _gnutls_get_cred (session->key, GNUTLS_CRD_CERTIFICATE, NULL); + if (cred == NULL) + { + gnutls_assert (); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + if (session->internals.selected_cert_list == NULL) + { + gnutls_assert (); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + bits = + _gnutls_mpi_get_nbits (session->internals. + selected_cert_list[0].params[0]); + + if (_gnutls_cipher_suite_get_kx_algo + (&session->security_parameters.current_cipher_suite) + != GNUTLS_KX_RSA_EXPORT || bits < 512) + { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + rsa_params = + _gnutls_certificate_get_rsa_params (cred->rsa_params, + cred->params_func, session); + /* EXPORT case: */ + if (rsa_params == NULL) + { + gnutls_assert (); + return GNUTLS_E_NO_TEMPORARY_RSA_PARAMS; + } + + /* In the export case, we do use temporary RSA params + * of 512 bits size. The params in the certificate are + * used to sign this temporary stuff. + */ + *params_size = RSA_PRIVATE_PARAMS; + *params = rsa_params->params; + + return 0; +} + +int proc_rsa_export_client_kx (gnutls_session_t session, opaque * data, + size_t _data_size) +{ + gnutls_datum_t plaintext; + gnutls_datum_t ciphertext; + int ret, dsize; + bigint_t *params; + int params_len; + int randomize_key = 0; + ssize_t data_size = _data_size; + + if (gnutls_protocol_get_version (session) == GNUTLS_SSL3) + { + /* SSL 3.0 + */ + ciphertext.data = data; + ciphertext.size = data_size; + } + else + { + /* TLS 1.0 + */ + DECR_LEN (data_size, 2); + ciphertext.data = &data[2]; + dsize = _gnutls_read_uint16 (data); + + if (dsize != data_size) + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + ciphertext.size = dsize; + } + + ret = _gnutls_get_private_rsa_params (session, ¶ms, ¶ms_len); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = _gnutls_pkcs1_rsa_decrypt (&plaintext, &ciphertext, params, params_len, 2); /* btype==2 */ + + if (ret < 0 || plaintext.size != GNUTLS_MASTER_SIZE) + { + /* In case decryption fails then don't inform + * the peer. Just use a random key. (in order to avoid + * attack against pkcs-1 formating). + */ + gnutls_assert (); + _gnutls_x509_log ("auth_rsa: Possible PKCS #1 format attack\n"); + randomize_key = 1; + } + else + { + /* If the secret was properly formatted, then + * check the version number. + */ + if (_gnutls_get_adv_version_major (session) != plaintext.data[0] + || _gnutls_get_adv_version_minor (session) != plaintext.data[1]) + { + /* No error is returned here, if the version number check + * fails. We proceed normally. + * That is to defend against the attack described in the paper + * "Attacking RSA-based sessions in SSL/TLS" by Vlastimil Klima, + * Ondej Pokorny and Tomas Rosa. + */ + gnutls_assert (); + _gnutls_x509_log + ("auth_rsa: Possible PKCS #1 version check format attack\n"); + } + } + + if (randomize_key != 0) + { + session->key->key.size = GNUTLS_MASTER_SIZE; + session->key->key.data = gnutls_malloc (session->key->key.size); + if (session->key->key.data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + /* we do not need strong random numbers here. + */ + ret = _gnutls_rnd (GNUTLS_RND_NONCE, session->key->key.data, + session->key->key.size); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + } + else + { + session->key->key.data = plaintext.data; + session->key->key.size = plaintext.size; + } + + /* This is here to avoid the version check attack + * discussed above. + */ + session->key->key.data[0] = _gnutls_get_adv_version_major (session); + session->key->key.data[1] = _gnutls_get_adv_version_minor (session); + + return 0; +} + static int gen_rsa_export_server_kx (gnutls_session_t session, opaque ** data) { @@ -74,11 +241,12 @@ gen_rsa_export_server_kx (gnutls_session_t session, opaque ** data) uint8_t *data_e, *data_m; int ret = 0, data_size; gnutls_cert *apr_cert_list; - gnutls_privkey *apr_pkey; + gnutls_privkey_t apr_pkey; int apr_cert_list_length; gnutls_datum_t signature, ddata; gnutls_certificate_credentials_t cred; gnutls_sign_algorithm_t sign_algo; + unsigned int bits = 0; cred = (gnutls_certificate_credentials_t) _gnutls_get_cred (session->key, GNUTLS_CRD_CERTIFICATE, NULL); @@ -100,7 +268,8 @@ gen_rsa_export_server_kx (gnutls_session_t session, opaque ** data) /* abort sending this message if we have a certificate * of 512 bits or less. */ - if (apr_pkey && _gnutls_mpi_get_nbits (apr_pkey->params[0]) <= 512) + gnutls_privkey_get_pk_algorithm(apr_pkey, &bits); + if (apr_pkey && bits <= 512) { gnutls_assert (); return GNUTLS_E_INT_RET_0; diff --git a/lib/auth_srp_rsa.c b/lib/auth_srp_rsa.c index caf84a6410..3967c57211 100644 --- a/lib/auth_srp_rsa.c +++ b/lib/auth_srp_rsa.c @@ -86,7 +86,7 @@ gen_srp_cert_server_kx (gnutls_session_t session, opaque ** data) gnutls_datum_t signature, ddata; gnutls_certificate_credentials_t cred; gnutls_cert *apr_cert_list; - gnutls_privkey *apr_pkey; + gnutls_privkey_t apr_pkey; int apr_cert_list_length; gnutls_sign_algorithm_t sign_algo; diff --git a/lib/gnutls_cert.c b/lib/gnutls_cert.c index 5072c8ec17..2f41f28af3 100644 --- a/lib/gnutls_cert.c +++ b/lib/gnutls_cert.c @@ -76,7 +76,7 @@ gnutls_certificate_free_keys (gnutls_certificate_credentials_t sc) for (i = 0; i < sc->ncerts; i++) { - _gnutls_gkey_deinit (&sc->pkey[i]); + gnutls_privkey_deinit (sc->pkey[i]); } gnutls_free (sc->pkey); @@ -425,6 +425,46 @@ void gnutls_certificate_server_set_retrieve_function cred->server_get_cert_callback = func; } +/** + * gnutls_certificate_set_retrieve_function: + * @cred: is a #gnutls_certificate_credentials_t structure. + * @func: is the callback function + * + * This function sets a callback to be called in order to retrieve the + * certificate to be used in the handshake. + * + * The callback's function prototype is: + * int (*callback)(gnutls_session_t, const gnutls_datum_t* req_ca_dn, int nreqs, + * const gnutls_pk_algorithm_t* pk_algos, int pk_algos_length, gnutls_retr2_st* st); + * + * @req_ca_cert is only used in X.509 certificates. + * Contains a list with the CA names that the server considers trusted. + * Normally we should send a certificate that is signed + * by one of these CAs. These names are DER encoded. To get a more + * meaningful value use the function gnutls_x509_rdn_get(). + * + * @pk_algos contains a list with server's acceptable signature algorithms. + * The certificate returned should support the server's given algorithms. + * + * @st should contain the certificates and private keys. + * + * If the callback function is provided then gnutls will call it, in the + * handshake, after the certificate request message has been received. + * + * In server side pk_algos and req_ca_dn are NULL. + * + * The callback function should set the certificate list to be sent, + * and return 0 on success. If no certificate was selected then the + * number of certificates should be set to zero. The value (-1) + * indicates error and the handshake will be terminated. + **/ +void gnutls_certificate_set_retrieve_function + (gnutls_certificate_credentials_t cred, + gnutls_certificate_retrieve_function * func) +{ + cred->get_cert_callback = func; +} + /** * gnutls_certificate_set_verify_function: * @cred: is a #gnutls_certificate_credentials_t structure. diff --git a/lib/gnutls_cert.h b/lib/gnutls_cert.h index 7280899121..4217d298e6 100644 --- a/lib/gnutls_cert.h +++ b/lib/gnutls_cert.h @@ -82,28 +82,6 @@ typedef struct gnutls_cert #endif } gnutls_cert; -typedef struct gnutls_privkey_int -{ - /* the size of params depends on the public - * key algorithm - * RSA: [0] is modulus - * [1] is public exponent - * [2] is private exponent - * [3] is prime1 (p) - * [4] is prime2 (q) - * [5] is coefficient (u == inverse of p mod q) - * DSA: [0] is p - * [1] is q - * [2] is g - * [3] is y (public key) - * [4] is x (private key) - */ - bigint_t params[MAX_PRIV_PARAMS_SIZE]; - int params_size; /* holds the number of params */ - - gnutls_pk_algorithm_t pk_algorithm; -} gnutls_privkey; - /* because gnutls_session_t is not defined when this file is included */ struct gnutls_session_int; @@ -120,7 +98,6 @@ int _gnutls_x509_raw_cert_to_gcert (gnutls_cert * gcert, int _gnutls_x509_crt_to_gcert (gnutls_cert * gcert, gnutls_x509_crt_t cert, unsigned int flags); -void _gnutls_gkey_deinit (gnutls_privkey * key); void _gnutls_gcert_deinit (gnutls_cert * cert); int _gnutls_selected_cert_supported_kx (struct gnutls_session_int *session, diff --git a/lib/gnutls_global.c b/lib/gnutls_global.c index e74c161d12..360bf1c54f 100644 --- a/lib/gnutls_global.c +++ b/lib/gnutls_global.c @@ -29,6 +29,7 @@ #include #include #include +#include #include /* for _gnutls_ext_init */ #include @@ -249,6 +250,8 @@ gnutls_global_init (void) goto out; } + gnutls_pkcs11_init(GNUTLS_PKCS11_FLAG_AUTO, NULL); + _gnutls_cryptodev_init (); out: @@ -276,6 +279,7 @@ gnutls_global_deinit (void) asn1_delete_structure (&_gnutls_pkix1_asn); _gnutls_crypto_deregister (); _gnutls_cryptodev_deinit (); + gnutls_pkcs11_deinit(); } _gnutls_init--; } diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index a89f13741f..1ca2e12e4e 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -672,7 +672,7 @@ typedef struct */ gnutls_cert *selected_cert_list; int selected_cert_list_length; - gnutls_privkey *selected_key; + struct gnutls_privkey_st *selected_key; int selected_need_free; /* holds the extensions we sent to the peer diff --git a/lib/gnutls_privkey.c b/lib/gnutls_privkey.c new file mode 100644 index 0000000000..9917f71613 --- /dev/null +++ b/lib/gnutls_privkey.c @@ -0,0 +1,232 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +struct gnutls_privkey_st { + gnutls_privkey_type_t type; + gnutls_pk_algorithm_t pk_algorithm; + + union { + gnutls_x509_privkey_t x509; + gnutls_pkcs11_privkey_t pkcs11; + gnutls_openpgp_privkey_t openpgp; + } key; + + unsigned int flags; +}; + +int gnutls_privkey_get_type (gnutls_privkey_t key) +{ + return key->type; +} + +int gnutls_privkey_get_pk_algorithm (gnutls_privkey_t key, unsigned int* bits) +{ + switch(key->type) { + case GNUTLS_PRIVKEY_OPENPGP: + return gnutls_openpgp_privkey_get_pk_algorithm(key->key.openpgp, bits); + case GNUTLS_PRIVKEY_PKCS11: + return gnutls_pkcs11_privkey_get_pk_algorithm(key->key.pkcs11, bits); + case GNUTLS_PRIVKEY_X509: + if (bits) + *bits = _gnutls_mpi_get_nbits (key->key.x509->params[0]); + return gnutls_x509_privkey_get_pk_algorithm(key->key.x509); + default: + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + +} + +int gnutls_privkey_init(gnutls_privkey_t * key) +{ + *key = gnutls_calloc(1, sizeof(struct gnutls_privkey_st)); + if (*key == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + return 0; +} + +void gnutls_privkey_deinit(gnutls_privkey_t key) +{ + if (key->flags & GNUTLS_PRIVKEY_IMPORT_AUTO_RELEASE) + switch(key->type) { + case GNUTLS_PRIVKEY_OPENPGP: + return gnutls_openpgp_privkey_deinit(key->key.openpgp); + case GNUTLS_PRIVKEY_PKCS11: + return gnutls_pkcs11_privkey_deinit(key->key.pkcs11); + case GNUTLS_PRIVKEY_X509: + return gnutls_x509_privkey_deinit(key->key.x509); + } + gnutls_free(key); +} + +int gnutls_privkey_import_pkcs11 (gnutls_privkey_t pkey, gnutls_pkcs11_privkey_t key, unsigned int flags) +{ + pkey->key.pkcs11 = key; + pkey->type = GNUTLS_PRIVKEY_PKCS11; + pkey->pk_algorithm = gnutls_pkcs11_privkey_get_pk_algorithm(key, NULL); + pkey->flags = flags; + + return 0; +} + +int gnutls_privkey_import_x509 (gnutls_privkey_t pkey, gnutls_x509_privkey_t key, unsigned int flags) +{ + pkey->key.x509 = key; + pkey->type = GNUTLS_PRIVKEY_X509; + pkey->pk_algorithm = gnutls_x509_privkey_get_pk_algorithm(key); + pkey->flags = flags; + + return 0; +} + +int gnutls_privkey_import_openpgp (gnutls_privkey_t pkey, gnutls_openpgp_privkey_t key, unsigned int flags) +{ + pkey->key.openpgp = key; + pkey->type = GNUTLS_PRIVKEY_OPENPGP; + pkey->pk_algorithm = gnutls_openpgp_privkey_get_pk_algorithm(key, NULL); + pkey->flags = flags; + + return 0; +} + +/** + * gnutls_privkey_sign_data: + * @signer: Holds the key + * @digest: should be MD5 or SHA1 + * @flags: should be 0 for now + * @data: holds the data to be signed + * @signature: will contain the signature allocate with gnutls_malloc() + * + * This function will sign the given data using a signature algorithm + * supported by the private key. Signature algorithms are always used + * together with a hash functions. Different hash functions may be + * used for the RSA algorithm, but only SHA-1 for the DSA keys. + * + * If the buffer provided is not long enough to hold the output, then + * *@signature_size is updated and %GNUTLS_E_SHORT_MEMORY_BUFFER will + * be returned. + * + * Returns: On success, %GNUTLS_E_SUCCESS is returned, otherwise a + * negative error value. + **/ +int +gnutls_privkey_sign_data(gnutls_privkey_t signer, + gnutls_digest_algorithm_t hash, + unsigned int flags, + const gnutls_datum_t * data, + gnutls_datum_t * signature) +{ + int ret; + gnutls_datum_t digest; + + switch (signer->pk_algorithm) { + case GNUTLS_PK_RSA: + ret = pk_pkcs1_rsa_hash(hash, data, &digest); + if (ret < 0) { + gnutls_assert(); + return ret; + } + break; + case GNUTLS_PK_DSA: + ret = pk_dsa_hash(data, &digest); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + break; + default: + gnutls_assert(); + return GNUTLS_E_INTERNAL_ERROR; + } + + ret = gnutls_privkey_sign_hash(signer, &digest, signature); + _gnutls_free_datum(&digest); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +int gnutls_privkey_sign_hash (gnutls_privkey_t key, + const gnutls_datum_t * hash, + gnutls_datum_t * signature) +{ + switch(key->type) { + case GNUTLS_PRIVKEY_OPENPGP: + return gnutls_openpgp_privkey_sign_hash(key->key.openpgp, hash, signature); + case GNUTLS_PRIVKEY_PKCS11: + return gnutls_pkcs11_privkey_sign_hash(key->key.pkcs11, hash, signature); + case GNUTLS_PRIVKEY_X509: + return gnutls_x509_privkey_sign_hash(key->key.x509, hash, signature); + default: + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } +} + +int gnutls_privkey_decrypt_data(gnutls_privkey_t key, + unsigned int flags, + const gnutls_datum_t * ciphertext, + gnutls_datum_t * plaintext) +{ + if (key->pk_algorithm != GNUTLS_PK_RSA) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + switch(key->type) { + case GNUTLS_PRIVKEY_OPENPGP: + return gnutls_openpgp_privkey_decrypt_data(key->key.openpgp, flags, ciphertext, plaintext); + case GNUTLS_PRIVKEY_X509: + return _gnutls_pkcs1_rsa_decrypt (plaintext, ciphertext, key->key.x509->params, key->key.x509->params_size, 2); + case GNUTLS_PRIVKEY_PKCS11: + return gnutls_pkcs11_privkey_decrypt_data(key->key.pkcs11, flags, ciphertext, plaintext); + default: + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } +} + + + diff --git a/lib/gnutls_sig.c b/lib/gnutls_sig.c index e7285ce682..5c3ff36917 100644 --- a/lib/gnutls_sig.c +++ b/lib/gnutls_sig.c @@ -43,7 +43,7 @@ static int _gnutls_tls_sign (gnutls_session_t session, - gnutls_cert * cert, gnutls_privkey * pkey, + gnutls_cert * cert, gnutls_privkey_t pkey, const gnutls_datum_t * hash_concat, gnutls_datum_t * signature); @@ -127,7 +127,7 @@ _gnutls_rsa_encode_sig (gnutls_mac_algorithm_t algo, */ int _gnutls_handshake_sign_data (gnutls_session_t session, gnutls_cert * cert, - gnutls_privkey * pkey, gnutls_datum_t * params, + gnutls_privkey_t pkey, gnutls_datum_t * params, gnutls_datum_t * signature, gnutls_sign_algorithm_t * sign_algo) { @@ -232,7 +232,7 @@ _gnutls_handshake_sign_data (gnutls_session_t session, gnutls_cert * cert, * given data. The output will be allocated and be put in signature. */ int -_gnutls_sign (gnutls_pk_algorithm_t algo, bigint_t * params, +_gnutls_soft_sign (gnutls_pk_algorithm_t algo, bigint_t * params, int params_size, const gnutls_datum_t * data, gnutls_datum_t * signature) { @@ -273,7 +273,7 @@ _gnutls_sign (gnutls_pk_algorithm_t algo, bigint_t * params, */ static int _gnutls_tls_sign (gnutls_session_t session, - gnutls_cert * cert, gnutls_privkey * pkey, + gnutls_cert * cert, gnutls_privkey_t pkey, const gnutls_datum_t * hash_concat, gnutls_datum_t * signature) { @@ -291,7 +291,7 @@ _gnutls_tls_sign (gnutls_session_t session, } /* External signing. */ - if (!pkey || pkey->params_size == 0) + if (!pkey) { if (!session->internals.sign_func) return GNUTLS_E_INSUFFICIENT_CREDENTIALS; @@ -302,8 +302,7 @@ _gnutls_tls_sign (gnutls_session_t session, } } - return _gnutls_sign (pkey->pk_algorithm, pkey->params, - pkey->params_size, hash_concat, signature); + return gnutls_privkey_sign_hash(pkey, hash_concat, signature); } static int @@ -623,7 +622,7 @@ _gnutls_handshake_verify_cert_vrfy (gnutls_session_t session, */ static int _gnutls_handshake_sign_cert_vrfy12 (gnutls_session_t session, - gnutls_cert * cert, gnutls_privkey * pkey, + gnutls_cert * cert, gnutls_privkey_t pkey, gnutls_datum_t * signature) { gnutls_datum_t dconcat; @@ -705,7 +704,7 @@ _gnutls_handshake_sign_cert_vrfy12 (gnutls_session_t session, */ int _gnutls_handshake_sign_cert_vrfy (gnutls_session_t session, - gnutls_cert * cert, gnutls_privkey * pkey, + gnutls_cert * cert, gnutls_privkey_t pkey, gnutls_datum_t * signature) { gnutls_datum_t dconcat; diff --git a/lib/gnutls_sig.h b/lib/gnutls_sig.h index f4539fb034..ca8324ae29 100644 --- a/lib/gnutls_sig.h +++ b/lib/gnutls_sig.h @@ -26,14 +26,16 @@ #ifndef GNUTLS_SIG_H # define GNUTLS_SIG_H +#include + int _gnutls_handshake_sign_cert_vrfy (gnutls_session_t session, gnutls_cert * cert, - gnutls_privkey * pkey, + gnutls_privkey_t pkey, gnutls_datum_t * signature); int _gnutls_handshake_sign_data (gnutls_session_t session, gnutls_cert * cert, - gnutls_privkey * pkey, + gnutls_privkey_t pkey, gnutls_datum_t * params, gnutls_datum_t * signature, gnutls_sign_algorithm_t * algo); @@ -49,7 +51,7 @@ int _gnutls_handshake_verify_data (gnutls_session_t session, gnutls_datum_t * signature, gnutls_sign_algorithm_t algo); -int _gnutls_sign (gnutls_pk_algorithm_t algo, +int _gnutls_soft_sign (gnutls_pk_algorithm_t algo, bigint_t * params, int params_size, const gnutls_datum_t * data, gnutls_datum_t * signature); diff --git a/lib/gnutls_state.c b/lib/gnutls_state.c index c99150228b..daa25c29a1 100644 --- a/lib/gnutls_state.c +++ b/lib/gnutls_state.c @@ -158,7 +158,7 @@ _gnutls_session_cert_type_supported (gnutls_session_t session, if (cred == NULL) return GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; - if (cred->server_get_cert_callback == NULL) + if (cred->server_get_cert_callback == NULL && cred->get_cert_callback == NULL) { for (i = 0; i < cred->ncerts; i++) { diff --git a/lib/gnutls_str.c b/lib/gnutls_str.c index 4c8aca36f7..c5c8493934 100644 --- a/lib/gnutls_str.c +++ b/lib/gnutls_str.c @@ -372,8 +372,6 @@ int _gnutls_string_unescape(gnutls_string * dest) rv = 0; - cleanup: - return rv; } @@ -389,7 +387,6 @@ _gnutls_bin2hex (const void *_old, size_t oldlen, { unsigned int i, j; const opaque *old = _old; - int init = 0; int step = 2; const char empty[] = ""; diff --git a/lib/gnutls_x509.c b/lib/gnutls_x509.c index d93406a422..4e43ed1930 100644 --- a/lib/gnutls_x509.c +++ b/lib/gnutls_x509.c @@ -46,6 +46,9 @@ #include "x509/x509_int.h" #include "read-file.h" +static int append_crt_list( gnutls_certificate_credentials_t res, gnutls_cert *crt, int nr); +static int append_pkey( gnutls_certificate_credentials_t res, gnutls_privkey_t pkey); + /* * some x509 certificate parsing functions. */ @@ -209,127 +212,73 @@ _gnutls_x509_cert_verify_peers (gnutls_session_t session, static int _gnutls_check_key_cert_match (gnutls_certificate_credentials_t res) { - gnutls_datum_t cid; - gnutls_datum_t kid; - unsigned pk = res->cert_list[res->ncerts - 1][0].subject_pk_algorithm; - - if (res->pkey[res->ncerts - 1].pk_algorithm != pk) - { - gnutls_assert (); - return GNUTLS_E_CERTIFICATE_KEY_MISMATCH; - } - - if (pk == GNUTLS_PK_RSA) - { - _gnutls_x509_write_rsa_params (res->pkey[res->ncerts - 1].params, - res->pkey[res->ncerts - - 1].params_size, &kid); - + unsigned int pk = res->cert_list[res->ncerts - 1][0].subject_pk_algorithm; - _gnutls_x509_write_rsa_params (res-> - cert_list[res->ncerts - 1][0].params, - res->cert_list[res->ncerts - - 1][0].params_size, &cid); - } - else if (pk == GNUTLS_PK_DSA) - { - - _gnutls_x509_write_dsa_params (res->pkey[res->ncerts - 1].params, - res->pkey[res->ncerts - - 1].params_size, &kid); - - _gnutls_x509_write_dsa_params (res-> - cert_list[res->ncerts - 1][0].params, - res->cert_list[res->ncerts - - 1][0].params_size, &cid); - } - - if (cid.size != kid.size) + if (gnutls_privkey_get_pk_algorithm(res->pkey[res->ncerts - 1], NULL) != pk) { gnutls_assert (); - _gnutls_free_datum (&kid); - _gnutls_free_datum (&cid); return GNUTLS_E_CERTIFICATE_KEY_MISMATCH; } - if (memcmp (kid.data, cid.data, kid.size) != 0) - { - gnutls_assert (); - _gnutls_free_datum (&kid); - _gnutls_free_datum (&cid); - return GNUTLS_E_CERTIFICATE_KEY_MISMATCH; - } - - _gnutls_free_datum (&kid); - _gnutls_free_datum (&cid); return 0; } /* Reads a DER encoded certificate list from memory and stores it to a - * gnutls_cert structure. Returns the number of certificates parsed. + * gnutls_cert structure. Returns the number of certificates parsed. */ static int -parse_crt_mem (gnutls_cert ** cert_list, unsigned *ncerts, - gnutls_x509_crt_t cert) +parse_der_cert_mem (gnutls_certificate_credentials_t res, + const void *input_cert, int input_cert_size) { - int i; + gnutls_datum_t tmp; + gnutls_x509_crt_t crt; + gnutls_cert *ccert; int ret; - i = *ncerts + 1; - - *cert_list = - (gnutls_cert *) gnutls_realloc_fast (*cert_list, - i * sizeof (gnutls_cert)); - - if (*cert_list == NULL) + ccert = gnutls_malloc(sizeof(*ccert)); + if (ccert == NULL) { - gnutls_assert (); + gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } - ret = _gnutls_x509_crt_to_gcert (&cert_list[0][i - 1], cert, 0); + ret = gnutls_x509_crt_init (&crt); if (ret < 0) { gnutls_assert (); + gnutls_free(ccert); return ret; } - *ncerts = i; - - return 1; /* one certificate parsed */ -} - -/* Reads a DER encoded certificate list from memory and stores it to a - * gnutls_cert structure. Returns the number of certificates parsed. - */ -static int -parse_der_cert_mem (gnutls_cert ** cert_list, unsigned *ncerts, - const void *input_cert, int input_cert_size) -{ - gnutls_datum_t tmp; - gnutls_x509_crt_t cert; - int ret; + tmp.data = (opaque *) input_cert; + tmp.size = input_cert_size; - ret = gnutls_x509_crt_init (&cert); + ret = gnutls_x509_crt_import (crt, &tmp, GNUTLS_X509_FMT_DER); if (ret < 0) { gnutls_assert (); + gnutls_x509_crt_deinit (crt); + gnutls_free(ccert); return ret; } - tmp.data = (opaque *) input_cert; - tmp.size = input_cert_size; - - ret = gnutls_x509_crt_import (cert, &tmp, GNUTLS_X509_FMT_DER); + ret = _gnutls_x509_crt_to_gcert (ccert, crt, 0); + gnutls_x509_crt_deinit (crt); + if (ret < 0) { - gnutls_assert (); - gnutls_x509_crt_deinit (cert); + gnutls_assert(); + gnutls_free(ccert); + return ret; + } + + ret = append_crt_list(res, ccert, 1); + if (ret < 0) + { + gnutls_assert(); + gnutls_free(ccert); return ret; } - - ret = parse_crt_mem (cert_list, ncerts, cert); - gnutls_x509_crt_deinit (cert); return ret; } @@ -338,14 +287,15 @@ parse_der_cert_mem (gnutls_cert ** cert_list, unsigned *ncerts, * a gnutls_cert structure. Returns the number of certificate parsed. */ static int -parse_pem_cert_mem (gnutls_cert ** cert_list, unsigned *ncerts, +parse_pem_cert_mem (gnutls_certificate_credentials_t res, const char *input_cert, int input_cert_size) { - int size, siz2, i; + int size, siz2; const char *ptr; opaque *ptr2; gnutls_datum_t tmp; int ret, count; + gnutls_cert* certs = NULL; /* move to the certificate */ @@ -362,7 +312,6 @@ parse_pem_cert_mem (gnutls_cert ** cert_list, unsigned *ncerts, } size = input_cert_size - (ptr - input_cert); - i = *ncerts + 1; count = 0; do @@ -376,11 +325,9 @@ parse_pem_cert_mem (gnutls_cert ** cert_list, unsigned *ncerts, return GNUTLS_E_BASE64_DECODING_ERROR; } - *cert_list = - (gnutls_cert *) gnutls_realloc_fast (*cert_list, - i * sizeof (gnutls_cert)); + certs = gnutls_realloc_fast (certs, (count+1) * sizeof (gnutls_cert)); - if (*cert_list == NULL) + if (certs == NULL) { gnutls_assert (); return GNUTLS_E_MEMORY_ERROR; @@ -389,12 +336,14 @@ parse_pem_cert_mem (gnutls_cert ** cert_list, unsigned *ncerts, tmp.data = ptr2; tmp.size = siz2; - ret = _gnutls_x509_raw_cert_to_gcert (&cert_list[0][i - 1], &tmp, 0); + ret = _gnutls_x509_raw_cert_to_gcert (&certs[count], &tmp, 0); if (ret < 0) { gnutls_assert (); + gnutls_free(certs); return ret; } + _gnutls_free_datum (&tmp); /* free ptr2 */ /* now we move ptr after the pem header @@ -418,13 +367,18 @@ parse_pem_cert_mem (gnutls_cert ** cert_list, unsigned *ncerts, else ptr = NULL; - i++; count++; } while (ptr != NULL); - *ncerts = i - 1; + ret = append_crt_list(res, certs, count); + if (ret < 0) + { + gnutls_assert(); + gnutls_free(certs); + return ret; + } return count; } @@ -439,38 +393,12 @@ read_cert_mem (gnutls_certificate_credentials_t res, const void *cert, { int ret; - /* allocate space for the certificate to add - */ - res->cert_list = gnutls_realloc_fast (res->cert_list, - (1 + - res->ncerts) * - sizeof (gnutls_cert *)); - if (res->cert_list == NULL) - { - gnutls_assert (); - return GNUTLS_E_MEMORY_ERROR; - } - - res->cert_list_length = gnutls_realloc_fast (res->cert_list_length, - (1 + - res->ncerts) * sizeof (int)); - if (res->cert_list_length == NULL) - { - gnutls_assert (); - return GNUTLS_E_MEMORY_ERROR; - } - - res->cert_list[res->ncerts] = NULL; /* for realloc */ - res->cert_list_length[res->ncerts] = 0; - if (type == GNUTLS_X509_FMT_DER) - ret = parse_der_cert_mem (&res->cert_list[res->ncerts], - &res->cert_list_length[res->ncerts], + ret = parse_der_cert_mem (res, cert, cert_size); else - ret = parse_pem_cert_mem (&res->cert_list[res->ncerts], - &res->cert_list_length[res->ncerts], cert, - cert_size); + ret = parse_pem_cert_mem (res, + cert, cert_size); if (ret < 0) { @@ -481,55 +409,8 @@ read_cert_mem (gnutls_certificate_credentials_t res, const void *cert, return ret; } - -int -_gnutls_x509_privkey_to_gkey (gnutls_privkey * dest, - gnutls_x509_privkey_t src) -{ - int i, ret; - - memset (dest, 0, sizeof (gnutls_privkey)); - - for (i = 0; i < src->params_size; i++) - { - dest->params[i] = _gnutls_mpi_copy (src->params[i]); - if (dest->params[i] == NULL) - { - gnutls_assert (); - ret = GNUTLS_E_MEMORY_ERROR; - goto cleanup; - } - } - - dest->pk_algorithm = src->pk_algorithm; - dest->params_size = src->params_size; - - return 0; - -cleanup: - - for (i = 0; i < src->params_size; i++) - { - _gnutls_mpi_release (&dest->params[i]); - } - return ret; -} - -void -_gnutls_gkey_deinit (gnutls_privkey * key) -{ - int i; - if (key == NULL) - return; - - for (i = 0; i < key->params_size; i++) - { - _gnutls_mpi_release (&key->params[i]); - } -} - -int -_gnutls_x509_raw_privkey_to_gkey (gnutls_privkey * privkey, +static int +_gnutls_x509_raw_privkey_to_privkey (gnutls_privkey_t *privkey, const gnutls_datum_t * raw_key, gnutls_x509_crt_fmt_t type) { @@ -542,7 +423,7 @@ _gnutls_x509_raw_privkey_to_gkey (gnutls_privkey * privkey, gnutls_assert (); return ret; } - + ret = gnutls_x509_privkey_import (tmpkey, raw_key, type); #ifdef ENABLE_PKI @@ -559,16 +440,23 @@ _gnutls_x509_raw_privkey_to_gkey (gnutls_privkey * privkey, return ret; } - ret = _gnutls_x509_privkey_to_gkey (privkey, tmpkey); + ret = gnutls_privkey_init(privkey); + if (ret < 0) + { + gnutls_assert(); + gnutls_x509_privkey_deinit (tmpkey); + return ret; + } + + ret = gnutls_privkey_import_x509 (*privkey, tmpkey, GNUTLS_PRIVKEY_IMPORT_AUTO_RELEASE); if (ret < 0) { gnutls_assert (); gnutls_x509_privkey_deinit (tmpkey); + gnutls_privkey_deinit (*privkey); return ret; } - gnutls_x509_privkey_deinit (tmpkey); - return 0; } @@ -582,17 +470,7 @@ read_key_mem (gnutls_certificate_credentials_t res, { int ret; gnutls_datum_t tmp; - - /* allocate space for the pkey list - */ - res->pkey = - gnutls_realloc_fast (res->pkey, - (res->ncerts + 1) * sizeof (gnutls_privkey)); - if (res->pkey == NULL) - { - gnutls_assert (); - return GNUTLS_E_MEMORY_ERROR; - } + gnutls_privkey_t privkey; if (key) { @@ -600,18 +478,144 @@ read_key_mem (gnutls_certificate_credentials_t res, tmp.size = key_size; ret = - _gnutls_x509_raw_privkey_to_gkey (&res->pkey[res->ncerts], &tmp, - type); + _gnutls_x509_raw_privkey_to_privkey (&privkey, &tmp, type); if (ret < 0) { gnutls_assert (); return ret; } + + ret = append_pkey(res, privkey); + if (ret < 0) + { + gnutls_assert (); + gnutls_privkey_deinit(privkey); + return ret; + } + } else - memset (&res->pkey[res->ncerts], 0, sizeof (gnutls_privkey)); + { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + + return 0; +} + +/* Reads a private key from a token. + */ +static int read_key_url (gnutls_certificate_credentials_t res, const char* url) +{ + int ret; + gnutls_pkcs11_privkey_t key1 = NULL; + gnutls_privkey_t pkey = NULL; + + /* allocate space for the pkey list + */ + +fprintf(stderr, "%s:%d\n", __func__, __LINE__); + ret = gnutls_pkcs11_privkey_init(&key1); + if (ret < 0) + { + gnutls_assert(); + return ret; + } + + ret = gnutls_pkcs11_privkey_import_url(key1, url); + if (ret < 0) + { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_privkey_init(&pkey); + if (ret < 0) + { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_privkey_import_pkcs11(pkey, key1, GNUTLS_PRIVKEY_IMPORT_AUTO_RELEASE); + if (ret < 0) + { + gnutls_assert(); + goto cleanup; + } + + ret = append_pkey(res, pkey); + if (ret < 0) + { + gnutls_assert(); + goto cleanup; + } + + return 0; + +cleanup: + if (pkey) + gnutls_privkey_deinit(pkey); + + if (key1) + gnutls_pkcs11_privkey_deinit(key1); + + return ret; +} + +/* Reads a private key from a token. + */ +static int read_cert_url (gnutls_certificate_credentials_t res, const char* url) +{ +int ret; +gnutls_x509_crt_t crt; +gnutls_cert * ccert; + +fprintf(stderr, "%s:%d\n", __func__, __LINE__); + ccert = gnutls_malloc(sizeof(*ccert)); + if (ccert == NULL) + { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + ret = gnutls_x509_crt_init(&crt); + if (ret < 0) + { + gnutls_assert(); + gnutls_free(ccert); + return ret; + } + + ret = gnutls_x509_crt_import_pkcs11_url(crt, url); + if (ret < 0) + { + gnutls_assert(); + gnutls_free(ccert); + gnutls_x509_crt_deinit(crt); + return ret; + } + + ret = _gnutls_x509_crt_to_gcert (ccert, crt, 0); + gnutls_x509_crt_deinit(crt); + + if (ret < 0) + { + gnutls_assert(); + gnutls_free(ccert); + return ret; + } + + ret = append_crt_list(res, ccert, 1); + if (ret < 0) + { + gnutls_assert(); + gnutls_free(ccert); + return ret; + } return 0; + } /* Reads a certificate file @@ -622,7 +626,13 @@ read_cert_file (gnutls_certificate_credentials_t res, { int ret; size_t size; - char *data = read_binary_file (certfile, &size); + char *data; + + if (strncmp(certfile, "pkcs11:", 7)==0) { + return read_cert_url(res, certfile); + } + + data = read_binary_file (certfile, &size); if (data == NULL) { @@ -648,7 +658,13 @@ read_key_file (gnutls_certificate_credentials_t res, { int ret; size_t size; - char *data = read_binary_file (keyfile, &size); + char* data; + + if (strncmp(keyfile, "pkcs11:", 7)==0) { + return read_key_url(res, keyfile); + } + + data = read_binary_file (keyfile, &size); if (data == NULL) { @@ -720,6 +736,50 @@ gnutls_certificate_set_x509_key_mem (gnutls_certificate_credentials_t res, return 0; } +static int append_crt_list( gnutls_certificate_credentials_t res, gnutls_cert *crt, int nr) +{ + res->cert_list = gnutls_realloc_fast (res->cert_list, + (1 + + res->ncerts) * + sizeof (gnutls_cert *)); + if (res->cert_list == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + res->cert_list_length = gnutls_realloc_fast (res->cert_list_length, + (1 + + res->ncerts) * sizeof (int)); + if (res->cert_list_length == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + res->cert_list[res->ncerts] = crt; + res->cert_list_length[res->ncerts] = nr; + + return 0; + +} + +static int append_pkey( gnutls_certificate_credentials_t res, gnutls_privkey_t pkey) +{ + res->pkey = gnutls_realloc_fast (res->pkey, + (1 + res->ncerts) * + sizeof (gnutls_privkey_t)); + if (res->pkey == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + res->pkey[res->ncerts] = pkey; + return 0; + +} + /** * gnutls_certificate_set_x509_key: * @res: is a #gnutls_certificate_credentials_t structure. @@ -734,6 +794,8 @@ gnutls_certificate_set_x509_key_mem (gnutls_certificate_credentials_t res, * entity certificate (e.g., also an intermediate CA cert) then put * the certificate chain in @cert_list. * + * + * * Returns: %GNUTLS_E_SUCCESS on success, or an error code. * * Since: 2.4.0 @@ -745,59 +807,57 @@ gnutls_certificate_set_x509_key (gnutls_certificate_credentials_t res, gnutls_x509_privkey_t key) { int ret, i; + gnutls_privkey_t pkey; + gnutls_cert* pcerts = NULL; /* this should be first */ - - res->pkey = - gnutls_realloc_fast (res->pkey, - (res->ncerts + 1) * sizeof (gnutls_privkey)); - if (res->pkey == NULL) + ret = gnutls_privkey_init(&pkey); + if (ret < 0) { gnutls_assert (); - return GNUTLS_E_MEMORY_ERROR; + return ret; } - - ret = _gnutls_x509_privkey_to_gkey (&res->pkey[res->ncerts], key); + + ret = gnutls_privkey_import_x509 (pkey, key, 0); if (ret < 0) { gnutls_assert (); return ret; } - - res->cert_list = gnutls_realloc_fast (res->cert_list, - (1 + - res->ncerts) * - sizeof (gnutls_cert *)); - if (res->cert_list == NULL) + + ret = append_pkey(res, pkey); + if (ret < 0) { gnutls_assert (); - return GNUTLS_E_MEMORY_ERROR; + return ret; } - res->cert_list_length = gnutls_realloc_fast (res->cert_list_length, - (1 + - res->ncerts) * sizeof (int)); - if (res->cert_list_length == NULL) + /* load certificates */ + pcerts = gnutls_malloc(sizeof(gnutls_cert)*cert_list_size); + if (pcerts == NULL) { - gnutls_assert (); + gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } - res->cert_list[res->ncerts] = NULL; /* for realloc */ - res->cert_list_length[res->ncerts] = 0; - - for (i = 0; i < cert_list_size; i++) { - ret = parse_crt_mem (&res->cert_list[res->ncerts], - &res->cert_list_length[res->ncerts], cert_list[i]); + ret = _gnutls_x509_crt_to_gcert (&pcerts[i], cert_list[i], 0); if (ret < 0) { gnutls_assert (); return ret; } } + + ret = append_crt_list(res, pcerts, cert_list_size); + if (ret < 0) + { + gnutls_assert(); + return ret; + } + res->ncerts++; if ((ret = _gnutls_check_key_cert_match (res)) < 0) @@ -2021,13 +2081,6 @@ int } } - /* check if the key and certificate found match */ - if (key && (ret = _gnutls_check_key_cert_match (res)) < 0) - { - gnutls_assert (); - goto done; - } - ret = 0; done: diff --git a/lib/gnutls_x509.h b/lib/gnutls_x509.h index 0f56063fcf..bdc6a369a2 100644 --- a/lib/gnutls_x509.h +++ b/lib/gnutls_x509.h @@ -24,6 +24,7 @@ */ #include +#include int _gnutls_x509_cert_verify_peers (gnutls_session_t session, unsigned int *status); @@ -39,8 +40,6 @@ int _gnutls_x509_cert_verify_peers (gnutls_session_t session, int _gnutls_check_key_usage (const gnutls_cert * cert, gnutls_kx_algorithm_t alg); -int _gnutls_x509_raw_privkey_to_gkey (gnutls_privkey * privkey, +int _gnutls_x509_raw_privkey_to_gkey (gnutls_privkey_t * privkey, const gnutls_datum_t * raw_key, gnutls_x509_crt_fmt_t type); -int _gnutls_x509_privkey_to_gkey (gnutls_privkey * privkey, - gnutls_x509_privkey_t); diff --git a/lib/includes/gnutls/compat.h b/lib/includes/gnutls/compat.h index 0232103c7c..23cec7495b 100644 --- a/lib/includes/gnutls/compat.h +++ b/lib/includes/gnutls/compat.h @@ -1,7 +1,7 @@ /* Typedefs for more compatibility with older GnuTLS. */ -#ifndef GNUTLS_COMPAT_H -# define GNUTLS_COMPAT_H +#ifndef _GNUTLS_COMPAT_H +# define _GNUTLS_COMPAT_H #define gnutls_cipher_algorithm gnutls_cipher_algorithm_t #define gnutls_kx_algorithm gnutls_kx_algorithm_t @@ -98,4 +98,44 @@ #define LIBGNUTLS_VERSION_NUMBER GNUTLS_VERSION_NUMBER #define LIBGNUTLS_EXTRA_VERSION GNUTLS_VERSION -#endif /* GNUTLS_COMPAT_H */ + typedef struct gnutls_retr_st + { + gnutls_certificate_type_t type; + union + { + gnutls_x509_crt_t *x509; + gnutls_openpgp_crt_t pgp; + } cert; + unsigned int ncerts; /* one for pgp keys */ + + union + { + gnutls_x509_privkey_t x509; + gnutls_openpgp_privkey_t pgp; + } key; + + unsigned int deinit_all; /* if non zero all keys will be deinited */ + } gnutls_retr_st; + +typedef int gnutls_certificate_client_retrieve_function (gnutls_session_t, + const + gnutls_datum_t * + req_ca_rdn, + int nreqs, + const + gnutls_pk_algorithm_t + * pk_algos, + int + pk_algos_length, + gnutls_retr_st *); +typedef int gnutls_certificate_server_retrieve_function (gnutls_session_t, + gnutls_retr_st *); + +void gnutls_certificate_client_set_retrieve_function + (gnutls_certificate_credentials_t cred, + gnutls_certificate_client_retrieve_function * func); +void gnutls_certificate_server_set_retrieve_function + (gnutls_certificate_credentials_t cred, + gnutls_certificate_server_retrieve_function * func); + +#endif /* _GNUTLS_COMPAT_H */ diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index 9a129e3ccf..4f8305d2c2 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -48,7 +48,6 @@ #endif /* Get time_t. */ #include -#include #ifdef __cplusplus extern "C" { @@ -1428,40 +1427,36 @@ extern "C" struct gnutls_openpgp_privkey_int; typedef struct gnutls_openpgp_privkey_int *gnutls_openpgp_privkey_t; - typedef struct gnutls_retr_st + struct gnutls_pkcs11_privkey_st; + typedef struct gnutls_pkcs11_privkey_st* gnutls_pkcs11_privkey_t; + + typedef enum { + GNUTLS_PRIVKEY_X509, /* gnutls_x509_privkey_t */ + GNUTLS_PRIVKEY_OPENPGP, /* gnutls_openpgp_privkey_t */ + GNUTLS_PRIVKEY_PKCS11, /* gnutls_pkcs11_privkey_t */ + } gnutls_privkey_type_t; + + typedef struct gnutls_retr2_st { - gnutls_certificate_type_t type; - union cert + gnutls_certificate_type_t cert_type; + gnutls_privkey_type_t key_type; + + union { gnutls_x509_crt_t *x509; gnutls_openpgp_crt_t pgp; } cert; unsigned int ncerts; /* one for pgp keys */ - union key + union { gnutls_x509_privkey_t x509; gnutls_openpgp_privkey_t pgp; + gnutls_pkcs11_privkey_t pkcs11; } key; unsigned int deinit_all; /* if non zero all keys will be deinited */ - } gnutls_retr_st; - - typedef int gnutls_certificate_client_retrieve_function (gnutls_session_t, - const - gnutls_datum_t * - req_ca_rdn, - int nreqs, - const - gnutls_pk_algorithm_t - * pk_algos, - int - pk_algos_length, - gnutls_retr_st *); - typedef int gnutls_certificate_server_retrieve_function (gnutls_session_t, - gnutls_retr_st *); - - typedef int gnutls_certificate_verify_function (gnutls_session_t); + } gnutls_retr2_st; /* Functions that allow auth_info_t structures handling @@ -1508,13 +1503,25 @@ extern "C" /* These are set on the credentials structure. */ - void gnutls_certificate_client_set_retrieve_function - (gnutls_certificate_credentials_t cred, - gnutls_certificate_client_retrieve_function * func); - void gnutls_certificate_server_set_retrieve_function + + typedef int gnutls_certificate_retrieve_function (gnutls_session_t, + const + gnutls_datum_t * + req_ca_rdn, + int nreqs, + const + gnutls_pk_algorithm_t + * pk_algos, + int + pk_algos_length, + gnutls_retr2_st *); + + + void gnutls_certificate_set_retrieve_function (gnutls_certificate_credentials_t cred, - gnutls_certificate_server_retrieve_function * func); + gnutls_certificate_retrieve_function * func); + typedef int gnutls_certificate_verify_function (gnutls_session_t); void gnutls_certificate_set_verify_function (gnutls_certificate_credentials_t cred, @@ -1745,4 +1752,6 @@ extern "C" } #endif +#include + #endif /* GNUTLS_H */ diff --git a/lib/includes/gnutls/openpgp.h b/lib/includes/gnutls/openpgp.h index 78cec828a6..d45c81858e 100644 --- a/lib/includes/gnutls/openpgp.h +++ b/lib/includes/gnutls/openpgp.h @@ -165,6 +165,10 @@ extern "C" gnutls_openpgp_crt_fmt_t format, const char *password, unsigned int flags); + + int gnutls_openpgp_privkey_decrypt_data (gnutls_openpgp_privkey_t key, + unsigned int flags, const gnutls_datum_t * ciphertext, + gnutls_datum_t * plaintext); int gnutls_openpgp_privkey_sign_hash (gnutls_openpgp_privkey_t key, const gnutls_datum_t * hash, gnutls_datum_t * signature); diff --git a/lib/includes/gnutls/pkcs11.h b/lib/includes/gnutls/pkcs11.h index 814be1e120..d912d32010 100644 --- a/lib/includes/gnutls/pkcs11.h +++ b/lib/includes/gnutls/pkcs11.h @@ -68,11 +68,10 @@ typedef int (*gnutls_pkcs11_pin_callback_t)(void *userdata, int attempt, * @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 */ @@ -106,7 +105,7 @@ void gnutls_pkcs11_deinit (void); * @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); +void gnutls_pkcs11_set_token_function(gnutls_pkcs11_token_callback_t fn, void *userdata); /** * @brief Set PIN prompt callback. @@ -173,6 +172,7 @@ typedef enum { GNUTLS_PKCS11_CRT_TOKEN_SERIAL, GNUTLS_PKCS11_CRT_TOKEN_MANUFACTURER, GNUTLS_PKCS11_CRT_TOKEN_MODEL, + GNUTLS_PKCS11_CRT_ID, } gnutls_pkcs11_cert_info_t; int gnutls_pkcs11_crt_get_info(gnutls_pkcs11_crt_t crt, gnutls_pkcs11_cert_info_t itype, void* output, size_t* output_size); @@ -220,24 +220,27 @@ int gnutls_x509_crt_list_import_pkcs11 (gnutls_x509_crt_t * 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); +/* private key functions...*/ +int gnutls_pkcs11_privkey_init (gnutls_pkcs11_privkey_t * key); +void gnutls_pkcs11_privkey_deinit (gnutls_pkcs11_privkey_t key); +int gnutls_pkcs11_privkey_get_pk_algorithm (gnutls_pkcs11_privkey_t key, unsigned int* bits); +int gnutls_pkcs11_privkey_get_info(gnutls_pkcs11_privkey_t crt, gnutls_pkcs11_cert_info_t itype, void* output, size_t* output_size); +int gnutls_pkcs11_privkey_import_url (gnutls_pkcs11_privkey_t key, + const char* url); + +int +gnutls_pkcs11_privkey_sign_data(gnutls_pkcs11_privkey_t signer, + gnutls_digest_algorithm_t hash, + unsigned int flags, + const gnutls_datum_t * data, + gnutls_datum_t * signature); +int gnutls_pkcs11_privkey_sign_hash (gnutls_pkcs11_privkey_t key, + const gnutls_datum_t * hash, + gnutls_datum_t * signature); +int +gnutls_pkcs11_privkey_decrypt_data(gnutls_pkcs11_privkey_t key, + unsigned int flags, const gnutls_datum_t * ciphertext, + gnutls_datum_t * plaintext); /** @} */ diff --git a/lib/includes/gnutls/privkey.h b/lib/includes/gnutls/privkey.h new file mode 100644 index 0000000000..1f37325214 --- /dev/null +++ b/lib/includes/gnutls/privkey.h @@ -0,0 +1,37 @@ +#ifndef __GNUTLS_PRIVKEY_H +#define __GNUTLS_PRIVKEY_H + +#include +#include +#include +#include +#include + +struct gnutls_privkey_st; +typedef struct gnutls_privkey_st* gnutls_privkey_t; + +int gnutls_privkey_init (gnutls_privkey_t * key); +void gnutls_privkey_deinit (gnutls_privkey_t key); +int gnutls_privkey_get_pk_algorithm (gnutls_privkey_t key, unsigned int* bits); +int gnutls_privkey_get_type (gnutls_privkey_t key); + +#define GNUTLS_PRIVKEY_IMPORT_AUTO_RELEASE 1 +int gnutls_privkey_import_pkcs11 (gnutls_privkey_t pkey, gnutls_pkcs11_privkey_t key, unsigned int flags); +int gnutls_privkey_import_x509 (gnutls_privkey_t pkey, gnutls_x509_privkey_t key, unsigned int flags); +int gnutls_privkey_import_openpgp (gnutls_privkey_t pkey, gnutls_openpgp_privkey_t key, unsigned int flags); + +int gnutls_privkey_sign_data(gnutls_privkey_t signer, + gnutls_digest_algorithm_t hash, + unsigned int flags, + const gnutls_datum_t * data, + gnutls_datum_t * signature); +int gnutls_privkey_sign_hash (gnutls_privkey_t key, + const gnutls_datum_t * hash, + gnutls_datum_t * signature); + +int gnutls_privkey_decrypt_data(gnutls_privkey_t signer, + unsigned int flags, + const gnutls_datum_t * ciphertext, + gnutls_datum_t * plaintext); + +#endif diff --git a/lib/includes/gnutls/x509.h b/lib/includes/gnutls/x509.h index 7ff7bde8cb..54ba9e2793 100644 --- a/lib/includes/gnutls/x509.h +++ b/lib/includes/gnutls/x509.h @@ -684,6 +684,11 @@ extern "C" const gnutls_datum_t * data, void *signature, size_t * signature_size); + int gnutls_x509_privkey_sign_data2 (gnutls_x509_privkey_t key, + gnutls_digest_algorithm_t digest, + unsigned int flags, + const gnutls_datum_t * data, + gnutls_datum_t *signature); int gnutls_x509_privkey_verify_data (gnutls_x509_privkey_t key, unsigned int flags, const gnutls_datum_t * data, diff --git a/lib/libgnutls.map b/lib/libgnutls.map index fd9b9e4009..0cd6336b4b 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -605,6 +605,7 @@ GNUTLS_2_10 GNUTLS_2_11 { global: + gnutls_certificate_set_retrieve_function; gnutls_pkcs11_init; gnutls_pkcs11_deinit; gnutls_pkcs11_set_pin_function; @@ -622,6 +623,26 @@ GNUTLS_2_11 gnutls_pkcs11_crt_get_info; gnutls_pkcs11_token_get_info; gnutls_pkcs11_token_get_url; + gnutls_pkcs11_privkey_init; + gnutls_pkcs11_privkey_deinit; + gnutls_pkcs11_privkey_get_pk_algorithm; + gnutls_pkcs11_privkey_get_info; + gnutls_pkcs11_privkey_import_url; + gnutls_pkcs11_privkey_sign_data; + gnutls_pkcs11_privkey_sign_hash; + gnutls_pkcs11_privkey_decrypt_data; + + gnutls_privkey_init; + gnutls_privkey_deinit; + gnutls_privkey_get_pk_algorithm; + gnutls_privkey_get_type; + gnutls_privkey_import_pkcs11; + gnutls_privkey_import_x509; + gnutls_privkey_import_openpgp; + gnutls_privkey_sign_data; + gnutls_privkey_sign_hash; + gnutls_privkey_decrypt_data; + } GNUTLS_2_10; GNUTLS_PRIVATE { diff --git a/lib/openpgp/gnutls_openpgp.c b/lib/openpgp/gnutls_openpgp.c index 039a2e3f1d..0d798f1aba 100644 --- a/lib/openpgp/gnutls_openpgp.c +++ b/lib/openpgp/gnutls_openpgp.c @@ -142,16 +142,24 @@ gnutls_certificate_set_openpgp_key (gnutls_certificate_credentials_t res, res->pkey = gnutls_realloc_fast (res->pkey, (res->ncerts + 1) * - sizeof (gnutls_privkey)); + sizeof (gnutls_privkey_t)); if (res->pkey == NULL) { gnutls_assert (); return GNUTLS_E_MEMORY_ERROR; } - ret = _gnutls_openpgp_privkey_to_gkey (&res->pkey[res->ncerts], pkey); + ret = gnutls_privkey_init(&res->pkey[res->ncerts]); + if (ret < 0) + { + gnutls_assert(); + return ret; + } + + ret = gnutls_privkey_import_openpgp (res->pkey[res->ncerts], pkey, 0); if (ret < 0) { + gnutls_privkey_deinit(res->pkey[res->ncerts]); gnutls_assert (); return ret; } @@ -163,6 +171,7 @@ gnutls_certificate_set_openpgp_key (gnutls_certificate_credentials_t res, if (res->cert_list == NULL) { gnutls_assert (); + /* memory leak here? */ return GNUTLS_E_MEMORY_ERROR; } @@ -720,73 +729,6 @@ gnutls_openpgp_set_recv_key_function (gnutls_session_t session, } -/* Copies a gnutls_openpgp_privkey_t to a gnutls_privkey structure. */ -int -_gnutls_openpgp_privkey_to_gkey (gnutls_privkey * dest, - gnutls_openpgp_privkey_t src) -{ - int ret = 0; - gnutls_openpgp_keyid_t keyid; - char err_buf[33]; - - if (dest == NULL || src == NULL) - { - gnutls_assert (); - return GNUTLS_E_CERTIFICATE_ERROR; - } - - dest->params_size = MAX_PRIV_PARAMS_SIZE; - - ret = gnutls_openpgp_privkey_get_preferred_key_id (src, keyid); - - if (ret == 0) - { - int idx; - uint32_t kid32[2]; - - _gnutls_debug_log - ("Importing Openpgp key and using openpgp sub key: %s\n", - _gnutls_bin2hex (keyid, sizeof (keyid), err_buf, sizeof (err_buf), NULL)); - - KEYID_IMPORT (kid32, keyid); - - idx = gnutls_openpgp_privkey_get_subkey_idx (src, keyid); - if (idx < 0) - { - gnutls_assert (); - return idx; - } - - dest->pk_algorithm = - gnutls_openpgp_privkey_get_subkey_pk_algorithm (src, idx, NULL); - - ret = - _gnutls_openpgp_privkey_get_mpis (src, kid32, dest->params, - &dest->params_size); - } - else - { - _gnutls_debug_log - ("Importing Openpgp key and using main openpgp key.\n"); - - dest->pk_algorithm = - gnutls_openpgp_privkey_get_pk_algorithm (src, NULL); - ret = - _gnutls_openpgp_privkey_get_mpis (src, NULL, dest->params, - &dest->params_size); - } - - - if (ret < 0) - { - gnutls_assert (); - return ret; - } - - return 0; - -} - /* Converts a parsed gnutls_openpgp_crt_t to a gnutls_cert structure. */ int @@ -961,7 +903,81 @@ gnutls_openpgp_privkey_sign_hash (gnutls_openpgp_privkey_t key, pk_algorithm = gnutls_openpgp_privkey_get_pk_algorithm (key, NULL); - result = _gnutls_sign (pk_algorithm, params, params_size, hash, signature); + result = _gnutls_soft_sign (pk_algorithm, params, params_size, hash, signature); + + for (i = 0; i < params_size; i++) + _gnutls_mpi_release (¶ms[i]); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + return 0; +} + +/** + * gnutls_openpgp_privkey_decrypt_data: + * @key: Holds the key + * @flags: zero for now + * @ciphertext: holds the data to be decrypted + * @plaintext: will contain newly allocated plaintext + * + * This function will sign the given hash using the private key. You + * should use gnutls_openpgp_privkey_set_preferred_key_id() before + * calling this function to set the subkey to use. + * + * Returns: On success, %GNUTLS_E_SUCCESS is returned, otherwise a + * negative error value. + **/ +int +gnutls_openpgp_privkey_decrypt_data (gnutls_openpgp_privkey_t key, + unsigned int flags, const gnutls_datum_t * ciphertext, + gnutls_datum_t * plaintext) +{ + int result, i; + bigint_t params[MAX_PUBLIC_PARAMS_SIZE]; + int params_size = MAX_PUBLIC_PARAMS_SIZE; + int pk_algorithm; + gnutls_openpgp_keyid_t keyid; + + if (key == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + result = gnutls_openpgp_privkey_get_preferred_key_id (key, keyid); + if (result == 0) + { + uint32_t kid[2]; + + KEYID_IMPORT (kid, keyid); + result = _gnutls_openpgp_privkey_get_mpis (key, kid, + params, ¶ms_size); + } + else + { + result = _gnutls_openpgp_privkey_get_mpis (key, NULL, + params, ¶ms_size); + } + + if (result < 0) + { + gnutls_assert (); + return result; + } + + pk_algorithm = gnutls_openpgp_privkey_get_pk_algorithm (key, NULL); + + if (pk_algorithm != GNUTLS_PK_RSA) + { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = _gnutls_pkcs1_rsa_decrypt (plaintext, ciphertext, params, params_size, 2); for (i = 0; i < params_size; i++) _gnutls_mpi_release (¶ms[i]); diff --git a/lib/openpgp/gnutls_openpgp.h b/lib/openpgp/gnutls_openpgp.h index 36ab8539d3..411c5aa1d5 100644 --- a/lib/openpgp/gnutls_openpgp.h +++ b/lib/openpgp/gnutls_openpgp.h @@ -7,6 +7,7 @@ #include #include +#include /* OpenCDK compatible */ typedef enum @@ -29,7 +30,7 @@ int _gnutls_openpgp_raw_crt_to_gcert (gnutls_cert * cert, const gnutls_openpgp_keyid_t); int -_gnutls_openpgp_raw_privkey_to_gkey (gnutls_privkey * pkey, +_gnutls_openpgp_raw_privkey_to_gkey (gnutls_privkey_t * pkey, const gnutls_datum_t * raw_key); int diff --git a/lib/openpgp/openpgp_int.h b/lib/openpgp/openpgp_int.h index d5eb139cfb..8071aa0098 100644 --- a/lib/openpgp/openpgp_int.h +++ b/lib/openpgp/openpgp_int.h @@ -45,8 +45,6 @@ int _gnutls_openpgp_export (cdk_kbnode_t node, int _gnutls_openpgp_crt_to_gcert (gnutls_cert * gcert, gnutls_openpgp_crt_t cert); -int _gnutls_openpgp_privkey_to_gkey (gnutls_privkey * dest, - gnutls_openpgp_privkey_t src); cdk_packet_t _gnutls_get_valid_subkey (cdk_kbnode_t knode, int key_type); diff --git a/lib/pkcs11.c b/lib/pkcs11.c index 52fc4b5159..e9127a0e8e 100644 --- a/lib/pkcs11.c +++ b/lib/pkcs11.c @@ -1,35 +1,35 @@ /* - neon PKCS#11 support - Copyright (C) 2008, Joe Orton - - 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 + * 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 #include +#include #define MAX_PROVIDERS 16 -#define ID_SIZE 128 -#define LABEL_SIZE 128 /* XXX: try to eliminate this */ #define MAX_CERT_SIZE 8*1024 @@ -40,19 +40,6 @@ struct gnutls_pkcs11_provider_s { ck_slot_id_t *slots; }; - -struct pkcs11_url_info -{ - /* everything here is null terminated strings */ - opaque id[ID_SIZE*3+1]; /* hex with delimiters */ - 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]; - opaque label[LABEL_SIZE+1]; -}; - struct gnutls_pkcs11_crt_st { gnutls_datum_t raw; gnutls_certificate_type_t type; @@ -61,8 +48,6 @@ struct gnutls_pkcs11_crt_st { struct url_find_data_st { gnutls_pkcs11_crt_t crt; - char certid_raw[ID_SIZE]; - size_t certid_raw_size; }; struct crt_find_data_st { @@ -73,18 +58,6 @@ struct crt_find_data_st { struct pkcs11_url_info info; }; -struct token_info { - struct ck_token_info tinfo; - struct ck_slot_info sinfo; - ck_slot_id_t sid; - struct gnutls_pkcs11_provider_s* prov; -}; - -/* thus function is called for every token in the traverse_tokens - * function. Once everything is traversed it is called with NULL tinfo. - * It should return 0 if found what it was looking for. - */ -typedef int (*find_func_t)(pakchois_session_t *pks, struct token_info* tinfo, void* input); static struct gnutls_pkcs11_provider_s providers[MAX_PROVIDERS]; static int active_providers = 0; @@ -92,6 +65,9 @@ static int active_providers = 0; static gnutls_pkcs11_pin_callback_t pin_func; static void* pin_data; +gnutls_pkcs11_token_callback_t token_func; +void* token_data; + int gnutls_pkcs11_add_provider (const char * name, const char * params) { @@ -144,28 +120,46 @@ fail: int gnutls_pkcs11_crt_get_info(gnutls_pkcs11_crt_t crt, gnutls_pkcs11_cert_info_t itype, void* output, size_t* output_size) { - const char* str; + return pkcs11_get_info(&crt->info, itype, output, output_size); +} + +int pkcs11_get_info(struct pkcs11_url_info *info, gnutls_pkcs11_cert_info_t itype, + void* output, size_t* output_size) +{ + const char* str = NULL; size_t len; switch(itype) { + case GNUTLS_PKCS11_CRT_ID: + if (*output_size < info->certid_raw_size) { + *output_size = info->certid_raw_size; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + if (output) memcpy(output, info->certid_raw, info->certid_raw_size); + *output_size = info->certid_raw_size; + + return 0; case GNUTLS_PKCS11_CRT_ID_HEX: - str = crt->info.id; + str = info->id; break; case GNUTLS_PKCS11_CRT_LABEL: - str = crt->info.label; + str = info->label; break; case GNUTLS_PKCS11_CRT_TOKEN_LABEL: - str = crt->info.token; + str = info->token; break; case GNUTLS_PKCS11_CRT_TOKEN_SERIAL: - str = crt->info.serial; + str = info->serial; break; case GNUTLS_PKCS11_CRT_TOKEN_MANUFACTURER: - str = crt->info.manufacturer; + str = info->manufacturer; break; case GNUTLS_PKCS11_CRT_TOKEN_MODEL: - str = crt->info.model; + str = info->model; break; + default: + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; } len = strlen(str); @@ -182,11 +176,18 @@ int gnutls_pkcs11_crt_get_info(gnutls_pkcs11_crt_t crt, gnutls_pkcs11_cert_info_ return 0; } +static int init = 0; int gnutls_pkcs11_init(unsigned int flags, const char* configfile) { int ret; + if (init != 0) { + init++; + return 0; + } + init++; + if (flags == GNUTLS_PKCS11_FLAG_MANUAL) return 0; else { @@ -233,6 +234,15 @@ int gnutls_pkcs11_init(unsigned int flags, const char* configfile) void gnutls_pkcs11_deinit (void) { int i; + + init--; + if (init > 0) + return; + if (init < 0) + { + init = 0; + return; + } for (i=0;iid, p1, l); info->id[l] = 0; + + /* convert to raw */ + info->certid_raw_size = sizeof(info->certid_raw); + ret = _gnutls_hex2bin(info->id, strlen(info->id), info->certid_raw, &info->certid_raw_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } } ret = 0; @@ -423,7 +448,6 @@ 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); @@ -496,12 +520,13 @@ cleanup: int gnutls_pkcs11_crt_init(gnutls_pkcs11_crt_t * crt) { - *crt = gnutls_malloc(sizeof(struct gnutls_pkcs11_crt_st)); + *crt = gnutls_calloc(1, sizeof(struct gnutls_pkcs11_crt_st)); if (*crt == NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } + return 0; } void gnutls_pkcs11_crt_deinit(gnutls_pkcs11_crt_t crt) @@ -525,9 +550,8 @@ static void terminate_string(unsigned char *str, size_t len) } -static int traverse_tokens (find_func_t find_func, void* input) +int _pkcs11_traverse_tokens (find_func_t find_func, void* input, int leave_session) { - struct ck_attribute a[3]; ck_rv_t rv; int found = 0, x, z, ret; pakchois_session_t *pks = NULL; @@ -560,12 +584,12 @@ static int traverse_tokens (find_func_t find_func, void* input) ret = find_func(pks, &info, input); - pakchois_close_session(pks); - pks = NULL; - if (ret == 0) { found = 1; goto finish; + } else { + pakchois_close_session(pks); + pks = NULL; } } } @@ -579,8 +603,11 @@ finish: ret = 0; } -cleanup: - if (pks != NULL) pakchois_close_session(pks); + if (pks != NULL) { + if (leave_session==0 || ret != 0) { + pakchois_close_session(pks); + } + } return ret; } @@ -621,6 +648,9 @@ static int pkcs11_crt_import(gnutls_pkcs11_crt_t crt, const gnutls_datum_t* data gnutls_assert(); return GNUTLS_E_PKCS11_ERROR; } + + memmove(crt->info.certid_raw, id->data, id->size); + crt->info.certid_raw_size = id->size; return 0; } @@ -636,9 +666,8 @@ static int find_cert_url(pakchois_session_t *pks, struct token_info *info, void* ck_object_handle_t obj; unsigned long count; int found = 0, ret; - unsigned char value[MAX_CERT_SIZE]; - char certid_tmp[ID_SIZE]; - char label_tmp[LABEL_SIZE]; + opaque* cert_data = NULL; + char label_tmp[PKCS11_LABEL_SIZE]; if (info == NULL) { /* we don't support multiple calls */ gnutls_assert(); @@ -676,6 +705,12 @@ static int find_cert_url(pakchois_session_t *pks, struct token_info *info, void* /* search the token for the id */ + cert_data = gnutls_malloc(MAX_CERT_SIZE); + if (cert_data == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + /* Find objects with cert class and X.509 cert type. */ class = CKO_CERTIFICATE; type = CKC_X_509; @@ -686,9 +721,12 @@ static int find_cert_url(pakchois_session_t *pks, struct token_info *info, void* a[1].type = CKA_CERTIFICATE_TYPE; a[1].value = &type; a[1].value_len = sizeof type; + a[2].type = CKA_ID; + a[2].value = find_data->crt->info.certid_raw; + a[2].value_len = find_data->crt->info.certid_raw_size; - rv = pakchois_find_objects_init(pks, a, 2); + rv = pakchois_find_objects_init(pks, a, 3); if (rv != CKR_OK) { gnutls_assert(); _gnutls_debug_log("pk11: FindObjectsInit failed.\n"); @@ -700,31 +738,25 @@ static int find_cert_url(pakchois_session_t *pks, struct token_info *info, void* && 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_LABEL; - a[2].value = label_tmp; - a[2].value_len = sizeof(label_tmp); - - 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 }; - gnutls_datum_t label = { a[2].value, a[2].value_len }; - - ret = pkcs11_crt_import(find_data->crt, &data, &id, &label, &info->tinfo); - if (ret < 0) { - gnutls_assert(); - goto cleanup; - } - - found = 1; - break; + a[0].value = cert_data; + a[0].value_len = MAX_CERT_SIZE; + a[1].type = CKA_LABEL; + a[1].value = label_tmp; + a[1].value_len = sizeof(label_tmp); + + if (pakchois_get_attribute_value(pks, obj, a, 2) == CKR_OK) { + gnutls_datum_t id = { find_data->crt->info.certid_raw, find_data->crt->info.certid_raw_size }; + gnutls_datum_t data = { a[0].value, a[0].value_len }; + gnutls_datum_t label = { a[1].value, a[1].value_len }; + + ret = pkcs11_crt_import(find_data->crt, &data, &id, &label, &info->tinfo); + if (ret < 0) { + gnutls_assert(); + goto cleanup; } + + found = 1; + break; } else { _gnutls_debug_log("pk11: Skipped cert, missing attrs.\n"); @@ -739,6 +771,7 @@ static int find_cert_url(pakchois_session_t *pks, struct token_info *info, void* } cleanup: + gnutls_free(cert_data); pakchois_find_objects_final(pks); return ret; @@ -758,15 +791,7 @@ int gnutls_pkcs11_crt_import_url (gnutls_pkcs11_crt_t cert, const char * url) return ret; } - find_data.certid_raw_size = sizeof(find_data.certid_raw); - - ret = _gnutls_hex2bin(cert->info.id, strlen(cert->info.id), find_data.certid_raw, &find_data.certid_raw_size); - if (ret < 0) { - gnutls_assert(); - return ret; - } - - ret = traverse_tokens(find_cert_url, &find_data); + ret = _pkcs11_traverse_tokens(find_cert_url, &find_data, 0); if (ret < 0) { gnutls_assert(); return ret; @@ -813,8 +838,8 @@ int gnutls_pkcs11_token_get_url (unsigned int seq, char** url) memset(&tn, 0, sizeof(tn)); tn.seq = seq; - - ret = traverse_tokens(find_token_num, &tn); + + ret = _pkcs11_traverse_tokens(find_token_num, &tn, 0); if (ret < 0) { gnutls_assert(); return ret; @@ -856,6 +881,9 @@ int gnutls_pkcs11_token_get_info(const char* url, gnutls_pkcs11_token_info_t tty case GNUTLS_PKCS11_TOKEN_MODEL: str = info.model; break; + default: + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; } len = strlen(str); @@ -896,9 +924,8 @@ struct pkey_list { size_t key_ids_size; }; -static int pk11_login(pakchois_session_t *pks, struct token_info *info) +int pkcs11_login(pakchois_session_t *pks, struct token_info *info) { - struct ck_token_info tinfo; int attempt = 0; ck_rv_t rv; @@ -911,7 +938,8 @@ static int pk11_login(pakchois_session_t *pks, struct token_info *info) /* force login on HW tokens. Some tokens will not list private keys * if login has not been performed. */ - if (!(info->sinfo.flags & CKF_HW_SLOT) && (tinfo.flags & CKF_LOGIN_REQUIRED) == 0) { +// if (!(info->sinfo.flags & CKF_HW_SLOT) && (info->tinfo.flags & CKF_LOGIN_REQUIRED) == 0) { + if ((info->tinfo.flags & CKF_LOGIN_REQUIRED) == 0) { gnutls_assert(); _gnutls_debug_log( "pk11: No login required.\n"); return 0; @@ -920,7 +948,7 @@ static int pk11_login(pakchois_session_t *pks, struct token_info *info) /* 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 (info->tinfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH) { if (pakchois_login(pks, CKU_USER, NULL, 0) == CKR_OK) { return 0; } @@ -971,7 +999,6 @@ static int pk11_login(pakchois_session_t *pks, struct token_info *info) } 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); @@ -990,12 +1017,12 @@ static int find_privkeys(pakchois_session_t *pks, struct token_info* info, struc ck_rv_t rv; ck_object_handle_t obj; unsigned long count, current; - char certid_tmp[ID_SIZE]; + char certid_tmp[PKCS11_ID_SIZE]; int ret; class = CKO_PRIVATE_KEY; - ret = pk11_login(pks, info); + ret = pkcs11_login(pks, info); if (ret < 0) { gnutls_assert(); return ret; @@ -1083,9 +1110,9 @@ static int find_crts(pakchois_session_t *pks, struct token_info *info, void* inp ck_rv_t rv; ck_object_handle_t obj; unsigned long count; - unsigned char value[MAX_CERT_SIZE]; - char certid_tmp[ID_SIZE]; - char label_tmp[LABEL_SIZE]; + opaque *cert_data; + char certid_tmp[PKCS11_ID_SIZE]; + char label_tmp[PKCS11_LABEL_SIZE]; int ret, i; struct pkey_list plist; /* private key holder */ @@ -1143,6 +1170,12 @@ static int find_crts(pakchois_session_t *pks, struct token_info *info, void* inp } } + cert_data = gnutls_malloc(MAX_CERT_SIZE); + if (cert_data == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + /* Find objects with cert class and X.509 cert type. */ class = CKO_CERTIFICATE; type = CKC_X_509; @@ -1175,8 +1208,8 @@ static int find_crts(pakchois_session_t *pks, struct token_info *info, void* inp && count == 1) { a[0].type = CKA_VALUE; - a[0].value = value; - a[0].value_len = sizeof value; + a[0].value = cert_data; + a[0].value_len = MAX_CERT_SIZE; a[1].type = CKA_ID; a[1].value = certid_tmp; a[1].value_len = sizeof(certid_tmp); @@ -1223,11 +1256,13 @@ static int find_crts(pakchois_session_t *pks, struct token_info *info, void* inp } } + gnutls_free(cert_data); pakchois_find_objects_final(pks); return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; /* continue until all tokens have been checked */ fail: + gnutls_free(cert_data); pakchois_find_objects_final(pks); if (plist.key_ids != NULL) { for (i=0;isession || 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/pkcs11_int.h b/lib/pkcs11_int.h new file mode 100644 index 0000000000..5f475c0573 --- /dev/null +++ b/lib/pkcs11_int.h @@ -0,0 +1,49 @@ +#ifndef PKCS11_INT_H +# define PKCS11_INT_H + +#include +#include + +#define PKCS11_ID_SIZE 128 +#define PKCS11_LABEL_SIZE 128 + +struct token_info { + struct ck_token_info tinfo; + struct ck_slot_info sinfo; + ck_slot_id_t sid; + struct gnutls_pkcs11_provider_s* prov; +}; + +struct pkcs11_url_info +{ + /* everything here is null terminated strings */ + opaque id[PKCS11_ID_SIZE*3+1]; /* hex with delimiters */ + 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]; + opaque label[PKCS11_LABEL_SIZE+1]; + + opaque certid_raw[PKCS11_ID_SIZE]; /* same as ID but raw */ + size_t certid_raw_size; +}; + +/* thus function is called for every token in the traverse_tokens + * function. Once everything is traversed it is called with NULL tinfo. + * It should return 0 if found what it was looking for. + */ +typedef int (*find_func_t)(pakchois_session_t *pks, struct token_info* tinfo, void* input); + +int _pkcs11_traverse_tokens (find_func_t find_func, void* input, int leave_session); +int pkcs11_url_to_info(const char* url, struct pkcs11_url_info* info); + +int pkcs11_get_info(struct pkcs11_url_info *info, gnutls_pkcs11_cert_info_t itype, + void* output, size_t* output_size); +int pkcs11_login(pakchois_session_t *pks, struct token_info *info); + +extern gnutls_pkcs11_token_callback_t token_func; +extern void* token_data; + + +#endif diff --git a/lib/pkcs11_privkey.c b/lib/pkcs11_privkey.c new file mode 100644 index 0000000000..039bcf1015 --- /dev/null +++ b/lib/pkcs11_privkey.c @@ -0,0 +1,430 @@ +/* + * 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 +#include +#include +#include + +struct gnutls_pkcs11_privkey_st { + pakchois_session_t *pks; + ck_object_handle_t privkey; + gnutls_pk_algorithm_t pk_algorithm; + struct pkcs11_url_info info; +}; + +struct privkey_find_data_st { + gnutls_pkcs11_privkey_t privkey; +}; + +static int find_privkey_url(pakchois_session_t * pks, + struct token_info *info, void *input); + +int gnutls_pkcs11_privkey_init(gnutls_pkcs11_privkey_t * key) +{ + *key = gnutls_calloc(1, sizeof(struct gnutls_pkcs11_privkey_st)); + if (*key == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + (*key)->privkey = CK_INVALID_HANDLE; + + return 0; +} + +void gnutls_pkcs11_privkey_deinit(gnutls_pkcs11_privkey_t key) +{ + if (key->pks) { +fprintf(stderr, "xxx: close session %p at %d\n", key->pks, __LINE__); + pakchois_close_session(key->pks); + } + gnutls_free(key); +} + +int gnutls_pkcs11_privkey_get_pk_algorithm(gnutls_pkcs11_privkey_t key, unsigned int *bits) +{ + if (bits) + *bits = 0; /* FIXME */ + return key->pk_algorithm; +} + +int gnutls_pkcs11_privkey_get_info(gnutls_pkcs11_privkey_t pkey, + gnutls_pkcs11_cert_info_t itype, + void *output, size_t * output_size) +{ + return pkcs11_get_info(&pkey->info, itype, output, output_size); +} + +#define RETRY_BLOCK_START(key) struct privkey_find_data_st find_data; \ + int retries = 0; find_data.privkey = key; retry: + + + +#define RETRY_CHECK(rv, label) { \ + if (token_func && rv == CKR_SESSION_HANDLE_INVALID) { \ + ret = token_func(token_data, label, retries++); \ + if (ret == 0) { \ + _pkcs11_traverse_tokens(find_privkey_url, &find_data, 1); \ + goto retry; \ + } \ + } \ + } + +/** + * gnutls_pkcs11_privkey_sign_data: + * @signer: Holds the key + * @digest: should be MD5 or SHA1 + * @flags: should be 0 for now + * @data: holds the data to be signed + * @signature: will contain the signature allocated with gnutls_malloc() + * + * This function will sign the given data using a signature algorithm + * supported by the private key. Signature algorithms are always used + * together with a hash functions. Different hash functions may be + * used for the RSA algorithm, but only SHA-1 for the DSA keys. + * + * Returns: On success, %GNUTLS_E_SUCCESS is returned, otherwise a + * negative error value. + **/ +int +gnutls_pkcs11_privkey_sign_data(gnutls_pkcs11_privkey_t signer, + gnutls_digest_algorithm_t hash, + unsigned int flags, + const gnutls_datum_t * data, + gnutls_datum_t * signature) +{ + int ret; + gnutls_datum_t digest; + + switch (signer->pk_algorithm) { + case GNUTLS_PK_RSA: + ret = pk_pkcs1_rsa_hash(hash, data, &digest); + if (ret < 0) { + gnutls_assert(); + return ret; + } + break; + case GNUTLS_PK_DSA: + ret = pk_dsa_hash(data, &digest); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + break; + default: + gnutls_assert(); + return GNUTLS_E_INTERNAL_ERROR; + } + + ret = gnutls_pkcs11_privkey_sign_hash(signer, &digest, signature); + _gnutls_free_datum(&digest); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; + +} + + +int gnutls_pkcs11_privkey_sign_hash(gnutls_pkcs11_privkey_t key, + const gnutls_datum_t * hash, + gnutls_datum_t * signature) +{ + ck_rv_t rv; + int ret; + struct ck_mechanism mech; + unsigned long siglen; + + RETRY_BLOCK_START(key); + + if (key->privkey == CK_INVALID_HANDLE) { + gnutls_assert(); + return GNUTLS_E_PKCS11_ERROR; + } + + mech.mechanism = + key->pk_algorithm == GNUTLS_PK_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(key->pks, &mech, key->privkey); + if (rv != CKR_OK) { + RETRY_CHECK(rv, key->info.label); + gnutls_assert(); + return GNUTLS_E_PK_SIGN_FAILED; + } + + /* Work out how long the signature must be: */ + rv = pakchois_sign(key->pks, hash->data, hash->size, NULL, + &siglen); + if (rv != CKR_OK) { + RETRY_CHECK(rv, key->info.label); + gnutls_assert(); + return GNUTLS_E_PK_SIGN_FAILED; + } + + signature->data = gnutls_malloc(siglen); + signature->size = siglen; + + rv = pakchois_sign(key->pks, hash->data, hash->size, + signature->data, &siglen); + if (rv != CKR_OK) { + gnutls_free(signature->data); + gnutls_assert(); + return GNUTLS_E_PK_SIGN_FAILED; + } + + signature->size = siglen; + + return 0; +} + +static int find_privkey_url(pakchois_session_t * pks, + struct token_info *info, void *input) +{ + struct privkey_find_data_st *find_data = input; + struct ck_attribute a[4]; + ck_object_class_t class; + ck_rv_t rv; + ck_object_handle_t obj; + unsigned long count; + int found = 0, ret; + ck_key_type_t keytype; + + if (info == 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->privkey->info.manufacturer[0] != 0) { + if (strcmp + (find_data->privkey->info.manufacturer, + info->tinfo.manufacturer_id) != 0) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (find_data->privkey->info.token[0] != 0) { + if (strcmp + (find_data->privkey->info.token, + info->tinfo.label) != 0) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (find_data->privkey->info.model[0] != 0) { + if (strcmp + (find_data->privkey->info.model, + info->tinfo.model) != 0) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (find_data->privkey->info.serial[0] != 0) { + if (strcmp + (find_data->privkey->info.serial, + info->tinfo.serial_number) != 0) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (find_data->privkey->info.type[0] != 0) { + if (strcmp(find_data->privkey->info.type, "cert") != 0) { + gnutls_assert(); + return GNUTLS_E_UNIMPLEMENTED_FEATURE; + } + } + + /* search the token for the id */ + ret = pkcs11_login(pks, info); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + /* Find objects with cert class and X.509 cert type. */ + class = CKO_PRIVATE_KEY; + + a[0].type = CKA_CLASS; + a[0].value = &class; + a[0].value_len = sizeof class; + + a[1].type = CKA_ID; + a[1].value = find_data->privkey->info.certid_raw; + a[1].value_len = find_data->privkey->info.certid_raw_size; + + + 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_KEY_TYPE; + a[0].value = &keytype; + a[0].value_len = sizeof keytype; + + if (pakchois_get_attribute_value(pks, obj, a, 1) == CKR_OK) { + if (keytype == CKK_RSA) + find_data->privkey->pk_algorithm = GNUTLS_PK_RSA; + else if (keytype == CKK_DSA) + find_data->privkey->pk_algorithm = GNUTLS_PK_DSA; + else { + gnutls_assert(); + ret = + GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; + goto cleanup; + } + find_data->privkey->pks = pks; + find_data->privkey->privkey = obj; + found = 1; + } 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_privkey_import_url(gnutls_pkcs11_privkey_t pkey, + const char *url) +{ + int ret; + struct privkey_find_data_st find_data; + + /* fill in the find data structure */ + find_data.privkey = pkey; + + ret = pkcs11_url_to_info(url, &pkey->info); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + if (pkey->info.id[0] == 0) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + ret = _pkcs11_traverse_tokens(find_privkey_url, &find_data, 1); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +/** + * gnutls_pkcs11_privkey_decrypt_data: + * @key: Holds the key + * @flags: should be 0 for now + * @ciphertext: holds the data to be signed + * @plaintext: will contain the plaintext, allocated with gnutls_malloc() + * + * This function will decrypt the given data using the public key algorithm + * supported by the private key. + * + * Returns: On success, %GNUTLS_E_SUCCESS is returned, otherwise a + * negative error value. + **/ +int +gnutls_pkcs11_privkey_decrypt_data(gnutls_pkcs11_privkey_t key, + unsigned int flags, const gnutls_datum_t * ciphertext, + gnutls_datum_t * plaintext) +{ + ck_rv_t rv; + int ret; + struct ck_mechanism mech; + unsigned long siglen; + + RETRY_BLOCK_START(key); + + if (key->privkey == CK_INVALID_HANDLE) { + gnutls_assert(); + return GNUTLS_E_PKCS11_ERROR; + } + + mech.mechanism = + key->pk_algorithm == GNUTLS_PK_DSA ? CKM_DSA : CKM_RSA_PKCS; + mech.parameter = NULL; + mech.parameter_len = 0; + + /* Initialize signing operation; using the private key discovered + * earlier. */ + rv = pakchois_decrypt_init(key->pks, &mech, key->privkey); + if (rv != CKR_OK) { + RETRY_CHECK(rv, key->info.label); + gnutls_assert(); + return GNUTLS_E_PK_DECRYPTION_FAILED; + } + + /* Work out how long the plaintext must be: */ + rv = pakchois_decrypt(key->pks, ciphertext->data, ciphertext->size, NULL, + &siglen); + if (rv != CKR_OK) { + RETRY_CHECK(rv, key->info.label); + gnutls_assert(); + return GNUTLS_E_PK_DECRYPTION_FAILED; + } + + plaintext->data = gnutls_malloc(siglen); + plaintext->size = siglen; + + rv = pakchois_decrypt(key->pks, ciphertext->data, ciphertext->size, + plaintext->data, &siglen); + if (rv != CKR_OK) { + gnutls_free(plaintext->data); + gnutls_assert(); + return GNUTLS_E_PK_DECRYPTION_FAILED; + } + + plaintext->size = siglen; + + return 0; +} diff --git a/lib/x509/privkey.c b/lib/x509/privkey.c index f49380478b..ce633db45c 100644 --- a/lib/x509/privkey.c +++ b/lib/x509/privkey.c @@ -34,6 +34,7 @@ #include #include #include +#include static int _gnutls_asn1_encode_rsa (ASN1_TYPE * c2, bigint_t * params); @@ -90,7 +91,7 @@ gnutls_x509_privkey_deinit (gnutls_x509_privkey_t key) * @src: The source key * * This function will copy a private key from source to destination - * key. + * key. Destination has to be initialized. * * Returns: On success, %GNUTLS_E_SUCCESS is returned, otherwise a * negative error value. @@ -1463,17 +1464,14 @@ cleanup: return result; } -#ifdef ENABLE_PKI /** - * gnutls_x509_privkey_sign_data: - * @key: Holds the key - * @digest: should be MD5 or SHAx. May be ignored. + * gnutls_x509_privkey_sign_data2: + * @signer: Holds the key + * @digest: should be MD5 or SHA1 * @flags: should be 0 for now * @data: holds the data to be signed - * @signature: will contain the signature - * @signature_size: holds the size of signature (and will be replaced - * by the new size) + * @signature: will contain the signature allocate with gnutls_malloc() * * This function will sign the given data using a signature algorithm * supported by the private key. Signature algorithms are always used @@ -1488,41 +1486,51 @@ cleanup: * negative error value. **/ int -gnutls_x509_privkey_sign_data (gnutls_x509_privkey_t key, - gnutls_digest_algorithm_t digest, +gnutls_x509_privkey_sign_data2 (gnutls_x509_privkey_t signer, + gnutls_digest_algorithm_t hash, unsigned int flags, const gnutls_datum_t * data, - void *signature, size_t * signature_size) + gnutls_datum_t * signature) { - int result; - gnutls_datum_t sig = { NULL, 0 }; - - if (key == NULL) + int ret; + gnutls_datum_t digest; + + switch (signer->pk_algorithm) { - gnutls_assert (); - return GNUTLS_E_INVALID_REQUEST; - } + case GNUTLS_PK_RSA: + ret = + pk_pkcs1_rsa_hash (hash, data, &digest); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + break; + case GNUTLS_PK_DSA: + ret = pk_dsa_hash (data, &digest); + if (ret < 0) + { + gnutls_assert (); + return ret; + } - result = _gnutls_x509_sign (data, digest, key, &sig); - if (result < 0) - { + break; + default: gnutls_assert (); - return result; + return GNUTLS_E_INTERNAL_ERROR; } - - if (*signature_size < sig.size) + + ret = gnutls_x509_privkey_sign_hash(signer, &digest, signature); + _gnutls_free_datum (&digest); + + if (ret < 0) { - *signature_size = sig.size; - _gnutls_free_datum (&sig); - return GNUTLS_E_SHORT_MEMORY_BUFFER; + gnutls_assert (); + return ret; } - *signature_size = sig.size; - memcpy (signature, sig.data, sig.size); - - _gnutls_free_datum (&sig); - return 0; + } /** @@ -1552,7 +1560,7 @@ gnutls_x509_privkey_sign_hash (gnutls_x509_privkey_t key, return GNUTLS_E_INVALID_REQUEST; } - result = _gnutls_sign (key->pk_algorithm, key->params, + result = _gnutls_soft_sign (key->pk_algorithm, key->params, key->params_size, hash, signature); if (result < 0) { @@ -1563,6 +1571,68 @@ gnutls_x509_privkey_sign_hash (gnutls_x509_privkey_t key, return 0; } +#ifdef ENABLE_PKI +/** + * gnutls_x509_privkey_sign_data: + * @key: Holds the key + * @digest: should be MD5 or SHA1 + * @flags: should be 0 for now + * @data: holds the data to be signed + * @signature: will contain the signature + * @signature_size: holds the size of signature (and will be replaced + * by the new size) + * + * This function will sign the given data using a signature algorithm + * supported by the private key. Signature algorithms are always used + * together with a hash functions. Different hash functions may be + * used for the RSA algorithm, but only SHA-1 for the DSA keys. + * + * If the buffer provided is not long enough to hold the output, then + * *@signature_size is updated and %GNUTLS_E_SHORT_MEMORY_BUFFER will + * be returned. + * + * Returns: On success, %GNUTLS_E_SUCCESS is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_privkey_sign_data (gnutls_x509_privkey_t key, + gnutls_digest_algorithm_t digest, + unsigned int flags, + const gnutls_datum_t * data, + void *signature, size_t * signature_size) +{ + int result; + gnutls_datum_t sig = { NULL, 0 }; + + if (key == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + result = gnutls_x509_privkey_sign_data2(key, digest, flags, data, &sig); + if (result < 0) + { + gnutls_assert (); + return result; + } + + if (*signature_size < sig.size) + { + *signature_size = sig.size; + _gnutls_free_datum (&sig); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + *signature_size = sig.size; + memcpy (signature, sig.data, sig.size); + + _gnutls_free_datum (&sig); + + return 0; +} + + /** * gnutls_x509_privkey_verify_data: * @key: Holds the key diff --git a/lib/x509/sign.c b/lib/x509/sign.c index e74c48f00e..f272c08e6f 100644 --- a/lib/x509/sign.c +++ b/lib/x509/sign.c @@ -41,6 +41,7 @@ #include #include #include +#include /* Writes the digest information and the digest in a DER encoded * structure. The digest info is allocated and stored into the info structure. @@ -128,14 +129,13 @@ encode_ber_digest_info (gnutls_digest_algorithm_t hash, * params[0] is modulus * params[1] is public key */ -static int -pkcs1_rsa_sign (gnutls_digest_algorithm_t hash, const gnutls_datum_t * text, - bigint_t * params, int params_len, gnutls_datum_t * signature) +int +pk_pkcs1_rsa_hash (gnutls_digest_algorithm_t hash, const gnutls_datum_t * text, gnutls_datum_t * output) { int ret; opaque _digest[MAX_HASH_SIZE]; digest_hd_st hd; - gnutls_datum_t digest, info; + gnutls_datum_t digest; ret = _gnutls_hash_init (&hd, HASH2MAC (hash)); if (ret < 0) @@ -152,29 +152,17 @@ pkcs1_rsa_sign (gnutls_digest_algorithm_t hash, const gnutls_datum_t * text, /* Encode the digest as a DigestInfo */ - if ((ret = encode_ber_digest_info (hash, &digest, &info)) != 0) + if ((ret = encode_ber_digest_info (hash, &digest, output)) != 0) { gnutls_assert (); return ret; } - if ((ret = - _gnutls_sign (GNUTLS_PK_RSA, params, params_len, &info, - signature)) < 0) - { - gnutls_assert (); - _gnutls_free_datum (&info); - return ret; - } - - _gnutls_free_datum (&info); - return 0; } -static int -dsa_sign (const gnutls_datum_t * text, - bigint_t * params, int params_len, gnutls_datum_t * signature) +int +pk_dsa_hash (const gnutls_datum_t * text, gnutls_datum_t * hash) { int ret; opaque _digest[MAX_HASH_SIZE]; @@ -206,52 +194,6 @@ dsa_sign (const gnutls_datum_t * text, return 0; } -/* Signs the given data using the parameters from the signer's - * private key. - * - * returns 0 on success. - * - * 'tbs' is the data to be signed - * 'signature' will hold the signature! - * 'hash' is only used in PKCS1 RSA signing. - */ -int -_gnutls_x509_sign (const gnutls_datum_t * tbs, - gnutls_digest_algorithm_t hash, - gnutls_x509_privkey_t signer, gnutls_datum_t * signature) -{ - int ret; - - switch (signer->pk_algorithm) - { - case GNUTLS_PK_RSA: - ret = - pkcs1_rsa_sign (hash, tbs, signer->params, signer->params_size, - signature); - if (ret < 0) - { - gnutls_assert (); - return ret; - } - return 0; - break; - case GNUTLS_PK_DSA: - ret = dsa_sign (tbs, signer->params, signer->params_size, signature); - if (ret < 0) - { - gnutls_assert (); - return ret; - } - - return 0; - break; - default: - gnutls_assert (); - return GNUTLS_E_INTERNAL_ERROR; - } - -} - /* This is the same as the _gnutls_x509_sign, but this one will decode * the ASN1_TYPE given, and sign the DER data. Actually used to get the DER * of the TBS and sign it on the fly. @@ -289,7 +231,7 @@ _gnutls_x509_sign_tbs (ASN1_TYPE cert, const char *tbs_name, tbs.data = buf; tbs.size = buf_size; - result = _gnutls_x509_sign (&tbs, hash, signer, signature); + result = gnutls_x509_privkey_sign_data2 (signer, hash, 0, &tbs, signature); gnutls_free (buf); return result; diff --git a/lib/x509/sign.h b/lib/x509/sign.h new file mode 100644 index 0000000000..291458ddcd --- /dev/null +++ b/lib/x509/sign.h @@ -0,0 +1,7 @@ +#ifndef GNUTLS_SIGN_H +# define GNUTLS_SIGN_H + +int pk_pkcs1_rsa_hash (gnutls_digest_algorithm_t hash, const gnutls_datum_t * text, gnutls_datum_t * output); +int pk_dsa_hash (const gnutls_datum_t * text, gnutls_datum_t * hash); + +#endif diff --git a/lib/x509/x509_int.h b/lib/x509/x509_int.h index 6ea8ee880d..ede4814bac 100644 --- a/lib/x509/x509_int.h +++ b/lib/x509/x509_int.h @@ -134,10 +134,6 @@ int _gnutls_x509_crl_get_raw_issuer_dn (gnutls_x509_crl_t crl, gnutls_datum_t * dn); /* sign.c */ -int _gnutls_x509_sign (const gnutls_datum_t * tbs, - gnutls_digest_algorithm_t hash, - gnutls_x509_privkey_t signer, - gnutls_datum_t * signature); int _gnutls_x509_sign_tbs (ASN1_TYPE cert, const char *tbs_name, gnutls_digest_algorithm_t hash, gnutls_x509_privkey_t signer, diff --git a/src/cli.c b/src/cli.c index 0e53bdcb53..e256da22b3 100644 --- a/src/cli.c +++ b/src/cli.c @@ -37,6 +37,7 @@ #include #include #include +#include #include /* Gnulib portability files. */ @@ -144,6 +145,8 @@ static unsigned int x509_crt_size; static gnutls_x509_crt_t x509_crt[MAX_CRT]; static gnutls_x509_privkey_t x509_key = NULL; +static gnutls_pkcs11_privkey_t pkcs11_key = NULL; + static gnutls_openpgp_crt_t pgp_crt = NULL; static gnutls_openpgp_privkey_t pgp_key = NULL; @@ -179,58 +182,86 @@ load_keys (void) if (x509_certfile != NULL && x509_keyfile != NULL) { - data = load_file (x509_certfile); - if (data.data == NULL) + if (strncmp (x509_certfile, "pkcs11:", 7) == 0) { - fprintf (stderr, "*** Error loading cert file.\n"); - exit (1); - } + crt_num = 1; + gnutls_x509_crt_init (&x509_crt[0]); - crt_num = MAX_CRT; - ret = - gnutls_x509_crt_list_import (x509_crt, &crt_num, &data, - GNUTLS_X509_FMT_PEM, - GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED); - if (ret < 0) - { - if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) + ret = + gnutls_x509_crt_import_pkcs11_url (x509_crt[0], x509_certfile); + if (ret < 0) { - fprintf (stderr, - "*** Error loading cert file: Too many certs %d\n", - crt_num); + fprintf (stderr, "*** Error loading cert file.\n"); + exit (1); + } + x509_crt_size = 1; + } + else + { + data = load_file (x509_certfile); + if (data.data == NULL) + { + fprintf (stderr, "*** Error loading cert file.\n"); + exit (1); } - else + + crt_num = MAX_CRT; + ret = + gnutls_x509_crt_list_import (x509_crt, &crt_num, &data, + GNUTLS_X509_FMT_PEM, + GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED); + if (ret < 0) { - fprintf (stderr, - "*** Error loading cert file: %s\n", - gnutls_strerror (ret)); + if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) + { + fprintf (stderr, + "*** Error loading cert file: Too many certs %d\n", + crt_num); + + } + else + { + fprintf (stderr, + "*** Error loading cert file: %s\n", + gnutls_strerror (ret)); + } + exit (1); } - exit (1); + x509_crt_size = ret; } - x509_crt_size = ret; fprintf (stderr, "Processed %d client certificates...\n", ret); unload_file (data); - data = load_file (x509_keyfile); - if (data.data == NULL) + if (strncmp (x509_keyfile, "pkcs11:", 7) == 0) { - fprintf (stderr, "*** Error loading key file.\n"); - exit (1); + gnutls_pkcs11_privkey_init (&pkcs11_key); + + ret = gnutls_pkcs11_privkey_import_url (pkcs11_key, x509_keyfile); } + else + { + data = load_file (x509_keyfile); + if (data.data == NULL) + { + fprintf (stderr, "*** Error loading key file.\n"); + exit (1); + } - gnutls_x509_privkey_init (&x509_key); + gnutls_x509_privkey_init (&x509_key); - ret = gnutls_x509_privkey_import (x509_key, &data, GNUTLS_X509_FMT_PEM); - if (ret < 0) - { - fprintf (stderr, "*** Error loading key file: %s\n", - gnutls_strerror (ret)); - exit (1); - } + ret = + gnutls_x509_privkey_import (x509_key, &data, GNUTLS_X509_FMT_PEM); + if (ret < 0) + { + fprintf (stderr, "*** Error loading key file: %s\n", + gnutls_strerror (ret)); + exit (1); + } - unload_file (data); + unload_file (data); + } fprintf (stderr, "Processed %d client X.509 certificates...\n", x509_crt_size); @@ -259,27 +290,38 @@ load_keys (void) unload_file (data); - data = load_file (pgp_keyfile); - if (data.data == NULL) + if (strncmp (pgp_keyfile, "pkcs11:", 7) == 0) { - fprintf (stderr, "*** Error loading PGP key file.\n"); - exit (1); + gnutls_pkcs11_privkey_init (&pkcs11_key); + + ret = gnutls_pkcs11_privkey_import_url (pkcs11_key, pgp_keyfile); } + else + { - gnutls_openpgp_privkey_init (&pgp_key); + data = load_file (pgp_keyfile); + if (data.data == NULL) + { + fprintf (stderr, "*** Error loading PGP key file.\n"); + exit (1); + } - ret = - gnutls_openpgp_privkey_import (pgp_key, &data, - GNUTLS_OPENPGP_FMT_BASE64, NULL, 0); - if (ret < 0) - { - fprintf (stderr, - "*** Error loading PGP key file: %s\n", - gnutls_strerror (ret)); - exit (1); - } + gnutls_openpgp_privkey_init (&pgp_key); - unload_file (data); + ret = + gnutls_openpgp_privkey_import (pgp_key, &data, + GNUTLS_OPENPGP_FMT_BASE64, NULL, + 0); + if (ret < 0) + { + fprintf (stderr, + "*** Error loading PGP key file: %s\n", + gnutls_strerror (ret)); + exit (1); + } + + unload_file (data); + } if (info.pgp_subkey != NULL) { @@ -347,7 +389,7 @@ static int cert_callback (gnutls_session_t session, const gnutls_datum_t * req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t * sign_algos, - int sign_algos_length, gnutls_retr_st * st) + int sign_algos_length, gnutls_retr2_st * st) { char issuer_dn[256]; int i, ret; @@ -355,7 +397,6 @@ cert_callback (gnutls_session_t session, if (verbose) { - /* Print the server's trusted CAs */ if (nreqs > 0) @@ -381,12 +422,11 @@ cert_callback (gnutls_session_t session, * supported by the server. */ - st->type = gnutls_certificate_type_get (session); - + st->cert_type = gnutls_certificate_type_get (session); st->ncerts = 0; - if (st->type == GNUTLS_CRT_X509) + if (st->cert_type == GNUTLS_CRT_X509) { gnutls_sign_algorithm_t cert_algo, req_algo; int i, match = 0; @@ -430,26 +470,56 @@ cert_callback (gnutls_session_t session, } } - if (x509_crt != NULL && x509_key != NULL) + if (x509_key != NULL) + { + st->key.x509 = x509_key; + st->key_type = GNUTLS_PRIVKEY_X509; + } + else if (pkcs11_key != NULL) + { + st->key.pkcs11 = pkcs11_key; + st->key_type = GNUTLS_PRIVKEY_PKCS11; + } + else + { + printf ("- Could not find a suitable key to send to server\n"); + return -1; + } + + if (x509_crt != NULL) { st->ncerts = x509_crt_size; st->cert.x509 = x509_crt; - st->key.x509 = x509_key; st->deinit_all = 0; return 0; } } - else if (st->type == GNUTLS_CRT_OPENPGP) + else if (st->cert_type == GNUTLS_CRT_OPENPGP) { - if (pgp_key != NULL && pgp_crt != NULL) + if (pgp_key != NULL) + { + st->key.pgp = pgp_key; + st->key_type = GNUTLS_PRIVKEY_OPENPGP; + } + else if (pkcs11_key != NULL) + { + st->key.pkcs11 = pkcs11_key; + st->key_type = GNUTLS_PRIVKEY_PKCS11; + } + else + { + printf ("- Could not find a suitable key to send to server\n"); + return -1; + } + + if (pgp_crt != NULL) { st->ncerts = 1; st->cert.pgp = pgp_crt; - st->key.pgp = pgp_key; st->deinit_all = 0; @@ -510,7 +580,7 @@ init_tls_session (const char *hostname) gnutls_credentials_set (session, GNUTLS_CRD_PSK, psk_cred); gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); - gnutls_certificate_client_set_retrieve_function (xcred, cert_callback); + gnutls_certificate_set_retrieve_function (xcred, cert_callback); gnutls_certificate_set_verify_function (xcred, cert_verify_callback); /* send the fingerprint */ @@ -656,6 +726,7 @@ main (int argc, char **argv) exit (1); } + pkcs11_common (); gaa_parser (argc, argv); if (hostname == NULL) { diff --git a/src/common.c b/src/common.c index 5de74738be..04d1356ff7 100644 --- a/src/common.c +++ b/src/common.c @@ -36,8 +36,10 @@ #include #include #include +#include #define SU(x) (x!=NULL?x:"Unknown") +#define MIN(x,y) ((x)<(y))?(x):(y) int print_cert; extern int verbose; @@ -858,3 +860,35 @@ service_to_port (const char *service) return ntohs (server_port->s_port); } + +static int pin_callback(void* user, int attempt, const char *slot_descr, + const char *token_label, unsigned int flags, char* pin, size_t pin_max) +{ +const char* password; +int len; + + printf("PIN required for token '%s' in slot '%s'\n", token_label, slot_descr); + if (flags & GNUTLS_PKCS11_PIN_FINAL_TRY) + printf("*** This is the final try before locking!\n"); + if (flags & GNUTLS_PKCS11_PIN_COUNT_LOW) + printf("*** Only few tries left before locking!\n"); + + password = getpass("Enter pin: "); + if (password==NULL || password[0] == 0) { + fprintf(stderr, "No password given\n"); + exit(1); + } + + len = MIN(pin_max,strlen(password)); + memcpy(pin, password, len); + pin[len] = 0; + + return 0; +} + +void pkcs11_common(void) +{ + + gnutls_pkcs11_set_pin_function (pin_callback, NULL); + +} diff --git a/src/common.h b/src/common.h index 29a68a5e60..fb04fedd7e 100644 --- a/src/common.h +++ b/src/common.h @@ -40,5 +40,5 @@ void parse_protocols (char **protocols, int protocols_size, int *protocol_priority); const char *raw_to_string (const unsigned char *raw, size_t raw_size); int service_to_port (const char *service); - +void pkcs11_common(void); void sockets_init (void); diff --git a/src/pkcs11.c b/src/pkcs11.c index f7a9917aa4..b933799378 100644 --- a/src/pkcs11.c +++ b/src/pkcs11.c @@ -15,6 +15,7 @@ static int pin_callback(void* user, int attempt, const char *slot_descr, const char *token_label, unsigned int flags, char* pin, size_t pin_max) { const char* password; +int len; printf("PIN required for token '%s' in slot '%s'\n", token_label, slot_descr); if (flags & GNUTLS_PKCS11_PIN_FINAL_TRY) @@ -27,8 +28,11 @@ const char* password; fprintf(stderr, "No password given\n"); exit(1); } - memcpy(pin, password, MIN(pin_max,strlen(password))); + len = MIN(pin_max,strlen(password)); + memcpy(pin, password, len); + pin[len] = 0; + return 0; } diff --git a/src/serv.c b/src/serv.c index 0e95fcf9a4..8b270df64a 100644 --- a/src/serv.c +++ b/src/serv.c @@ -916,6 +916,7 @@ main (int argc, char **argv) exit (1); } + pkcs11_common(); gnutls_global_set_log_function (tls_log_func); gnutls_global_set_log_level (debug);