diff options
-rw-r--r-- | macros/neon.m4 | 25 | ||||
-rw-r--r-- | src/ne_openssl.c | 42 | ||||
-rw-r--r-- | src/ne_pkcs11.c | 115 | ||||
-rw-r--r-- | src/ne_privssl.h | 6 |
4 files changed, 172 insertions, 16 deletions
diff --git a/macros/neon.m4 b/macros/neon.m4 index b219eb1..a6e28f5 100644 --- a/macros/neon.m4 +++ b/macros/neon.m4 @@ -979,19 +979,6 @@ gnutls) if test ${ac_cv_func_gnutls_x509_dn_get_rdn_ava}X${ac_cv_header_iconv_h} = yesXyes; then AC_CHECK_FUNCS(iconv) fi - - if test x${ac_cv_func_gnutls_sign_callback_set} = xyes; then - if test "$with_pakchois" != "no"; then - # PKCS#11... ho! - NE_PKG_CONFIG(NE_PK11, pakchois, - [AC_MSG_NOTICE(using pakchois for PKCS11 support) - AC_DEFINE(HAVE_PAKCHOIS, 1, [Define if pakchois library supported]) - CPPFLAGS="$CPPFLAGS ${NE_PK11_CFLAGS}" - NEON_LIBS="${NEON_LIBS} ${NE_PK11_LIBS}"], - [AC_MSG_NOTICE(pakchois library not found; no PKCS11 support)]) - fi - fi - ;; *) # Default to off; only create crypto-enabled binaries if requested. NE_DISABLE_SUPPORT(SSL, [SSL support is not enabled]) @@ -1035,6 +1022,18 @@ posix|yes) ;; esac +case ${with_pakchois}X${ac_cv_func_gnutls_sign_callback_set}Y${ne_cv_lib_ssl097} in +noX*Y*) ;; +*X*Yyes|*XyesX*) + # PKCS#11... ho! + NE_PKG_CONFIG(NE_PK11, pakchois, + [AC_MSG_NOTICE(using pakchois for PKCS11 support) + AC_DEFINE(HAVE_PAKCHOIS, 1, [Define if pakchois library supported]) + CPPFLAGS="$CPPFLAGS ${NE_PK11_CFLAGS}" + NEON_LIBS="${NEON_LIBS} ${NE_PK11_LIBS}"], + [AC_MSG_NOTICE(pakchois library not found; no PKCS11 support)]) + ;; +esac ]) dnl Check for Kerberos installation diff --git a/src/ne_openssl.c b/src/ne_openssl.c index 5ccfa76..91ef671 100644 --- a/src/ne_openssl.c +++ b/src/ne_openssl.c @@ -820,6 +820,48 @@ ne_ssl_client_cert *ne_ssl_clicert_read(const char *filename) } } +#ifdef HAVE_PAKCHOIS +ne_ssl_client_cert *ne__ssl_clicert_exkey_import(const unsigned char *der, + size_t der_len, + const RSA_METHOD *method) +{ + ne_ssl_client_cert *cc; + ne_d2i_uchar *p; + X509 *x5; + RSA *pk; + EVP_PKEY *epk, *tpk; + + p = der; + x5 = d2i_X509(NULL, &p, der_len); /* p is incremented */ + if (x5 == NULL) { + ERR_clear_error(); + return NULL; + } + + pk = RSA_new(); + RSA_set_method(pk, method); + epk = EVP_PKEY_new(); + EVP_PKEY_assign_RSA(epk, pk); + + /* It is necessary to initialize pk->n otherwise OpenSSL will barf + * later calling RSA_size() on this RSA structure. + * X509_get_pubkey() forces the relevant RSA parameters to be + * extracted from the certificate. */ + tpk = X509_get_pubkey(x5); + pk->n = BN_dup(tpk->pkey.rsa->n); + EVP_PKEY_free(tpk); + + cc = ne_calloc(sizeof *cc); + + cc->decrypted = 1; + cc->pkey = epk; + + populate_cert(&cc->cert, x5); + + return cc; +} +#endif + int ne_ssl_clicert_encrypted(const ne_ssl_client_cert *cc) { return !cc->decrypted; diff --git a/src/ne_pkcs11.c b/src/ne_pkcs11.c index 9a480a8..80b1f45 100644 --- a/src/ne_pkcs11.c +++ b/src/ne_pkcs11.c @@ -64,6 +64,101 @@ struct ne_ssl_pkcs11_provider_s { */ +#ifdef HAVE_OPENSSL + +#include <openssl/rsa.h> +#include <openssl/err.h> + +#define PK11_RSA_ERR (RSA_F_RSA_EAY_PRIVATE_ENCRYPT) + +/* RSA_METHOD ->rsa_sign calback. */ +static int pk11_rsa_sign(int type, + const unsigned char *m, unsigned int mlen, + unsigned char *sigret, unsigned int *siglen, + const RSA *r) +{ + ne_ssl_pkcs11_provider *prov = (ne_ssl_pkcs11_provider *)r->meth->app_data; + ck_rv_t rv; + struct ck_mechanism mech; + unsigned long len; + + if (!prov->session || prov->privkey == CK_INVALID_HANDLE) { + NE_DEBUG(NE_DBG_SSL, "pk11: Cannot sign, no session/key.\n"); + RSAerr(PK11_RSA_ERR,ERR_R_RSA_LIB); + return 0; + } + + mech.mechanism = 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); + RSAerr(PK11_RSA_ERR, ERR_R_RSA_LIB); + return 0; + } + + len = *siglen = RSA_size(r); + rv = pakchois_sign(prov->session, (unsigned char *)m, mlen, sigret, &len); + if (rv != CKR_OK) { + NE_DEBUG(NE_DBG_SSL, "pk11: Sign failed.\n"); + RSAerr(PK11_RSA_ERR, ERR_R_RSA_LIB); + return 0; + } + + NE_DEBUG(NE_DBG_SSL, "pk11: Signed successfully.\n"); + return 1; +} + +/* RSA_METHOD ->rsa_init implementation; called during RSA_new(rsa). */ +static int pk11_rsa_init(RSA *rsa) +{ + /* Ensures that RSA_sign() uses meth->rsa_sign: */ + rsa->flags |= RSA_FLAG_SIGN_VER; + return 1; +} + +/* RSA_METHOD ->rsa_finish implementation; called during + * RSA_free(rsa). */ +static int pk11_rsa_finish(RSA *rsa) +{ + RSA_METHOD *meth = (RSA_METHOD *)rsa->meth; + + /* Freeing the dynamically allocated method here works as well as + * doing anything else: */ + ne_free(meth); + /* Does not appear that rsa->meth will be used after this, but in + * case it is, ensure a NULL pointer dereference rather than a + * random pointer dereference. */ + rsa->meth = NULL; + + return 0; +} + +/* Return an RSA_METHOD which will use the PKCS#11 provider to + * implement the signing operation. */ +static RSA_METHOD *pk11_rsa_method(ne_ssl_pkcs11_provider *prov) +{ + RSA_METHOD *m = ne_calloc(sizeof *m); + + m->name = "neon PKCS#11"; + m->rsa_sign = pk11_rsa_sign; + + m->init = pk11_rsa_init; + m->finish = pk11_rsa_finish; + + /* This is hopefully under control of the RSA_METHOD. */ + m->app_data = (char *)prov; + + m->flags = RSA_METHOD_FLAG_NO_CHECK | RSA_FLAG_SIGN_VER; + + return m; +} +#endif + static int pk11_find_x509(ne_ssl_pkcs11_provider *prov, pakchois_session_t *pks, unsigned char *certid, unsigned long *cid_len) @@ -87,7 +182,7 @@ static int pk11_find_x509(ne_ssl_pkcs11_provider *prov, a[1].value = &type; a[1].value_len = sizeof type; - rv = pakchois_find_objects_init(pks, a, 2); + rv = pakchois_find_objects_init(pks, a, 1); if (rv != CKR_OK) { NE_DEBUG(NE_DBG_SSL, "pk11: FindObjectsInit failed.\n"); return 0; @@ -110,7 +205,11 @@ static int pk11_find_x509(ne_ssl_pkcs11_provider *prov, if (pakchois_get_attribute_value(pks, obj, a, 3) == CKR_OK) { ne_ssl_client_cert *cc; +#ifdef HAVE_GNUTLS cc = ne__ssl_clicert_exkey_import(value, a[0].value_len); +#else + cc = ne__ssl_clicert_exkey_import(value, a[0].value_len, pk11_rsa_method(prov)); +#endif if (cc) { NE_DEBUG(NE_DBG_SSL, "pk11: Imported X.509 cert.\n"); prov->clicert = cc; @@ -128,6 +227,13 @@ static int pk11_find_x509(ne_ssl_pkcs11_provider *prov, return found; } +#ifdef HAVE_OPENSSL +/* No DSA support for OpenSSL (yet, anyway). */ +#define KEYTYPE_IS_DSA(kt) (0) +#else +#define KEYTYPE_IS_DSA(kt) (kt == CKK_DSA) +#endif + static int pk11_find_pkey(ne_ssl_pkcs11_provider *prov, pakchois_session_t *pks, unsigned char *certid, unsigned long cid_len) @@ -167,7 +273,7 @@ static int pk11_find_pkey(ne_ssl_pkcs11_provider *prov, a[0].value_len = sizeof prov->keytype; if (pakchois_get_attribute_value(pks, obj, a, 1) == CKR_OK - && (prov->keytype == CKK_RSA || prov->keytype == CKK_DSA)) { + && (prov->keytype == CKK_RSA || KEYTYPE_IS_DSA(prov->keytype))) { found = 1; prov->privkey = obj; } @@ -192,6 +298,7 @@ static int find_client_cert(ne_ssl_pkcs11_provider *prov, && pk11_find_pkey(prov, pks, certid, cid_len); } +#ifdef HAVE_GNUTLS /* Callback invoked by GnuTLS to provide the signature. The signature * operation is handled here by the PKCS#11 provider. */ static int pk11_sign_callback(gnutls_session_t session, @@ -244,6 +351,7 @@ static int pk11_sign_callback(gnutls_session_t session, return 0; } +#endif static void terminate_string(unsigned char *str, size_t len) { @@ -460,14 +568,15 @@ void ne_ssl_pkcs11_provider_pin(ne_ssl_pkcs11_provider *provider, { provider->pin_fn = fn; provider->pin_data = userdata; - } void ne_ssl_set_pkcs11_provider(ne_session *sess, ne_ssl_pkcs11_provider *provider) { +#ifdef HAVE_GNUTLS sess->ssl_context->sign_func = pk11_sign_callback; sess->ssl_context->sign_data = provider; +#endif ne_ssl_provide_clicert(sess, pk11_provide, provider); } diff --git a/src/ne_privssl.h b/src/ne_privssl.h index 09fa7e3..6aab772 100644 --- a/src/ne_privssl.h +++ b/src/ne_privssl.h @@ -44,6 +44,12 @@ struct ne_ssl_context_s { typedef SSL *ne_ssl_socket; +/* Create a clicert object from cert DER {der, der_len}, using given + * RSA_METHOD for the RSA object. */ +ne_ssl_client_cert *ne__ssl_clicert_exkey_import(const unsigned char *der, + size_t der_len, + const RSA_METHOD *method); + #endif /* HAVE_OPENSSL */ #ifdef HAVE_GNUTLS |