diff --git a/src/crypto/tls.h b/src/crypto/tls.h index 0a7916633..dafe8bb44 100644 --- a/src/crypto/tls.h +++ b/src/crypto/tls.h @@ -63,7 +63,10 @@ struct tls_config { * @engine_id: engine id string (this is OpenSSL specific for now) * @ppin: pointer to the pin variable in the configuration * (this is OpenSSL specific for now) - * @key_id: the private key's key id (this is OpenSSL specific for now) + * @key_id: the private key's id when using engine (this is OpenSSL + * specific for now) + * @cert_id: the certificate's id when using engine + * @ca_cert_id: the CA certificate's id when using engine * @tls_ia: Whether to enable TLS/IA (for EAP-TTLSv1) * * TLS connection parameters to be configured with tls_connection_set_params() @@ -98,6 +101,8 @@ struct tls_connection_params { const char *engine_id; const char *pin; const char *key_id; + const char *cert_id; + const char *ca_cert_id; }; diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index 0bbe88e50..7e565b4ab 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -777,7 +777,8 @@ void tls_deinit(void *ssl_ctx) static int tls_engine_init(struct tls_connection *conn, const char *engine_id, - const char *pin, const char *key_id) + const char *pin, const char *key_id, + const char *cert_id, const char *ca_cert_id) { #ifndef OPENSSL_NO_ENGINE int ret = -1; @@ -814,6 +815,7 @@ static int tls_engine_init(struct tls_connection *conn, const char *engine_id, ERR_error_string(ERR_get_error(), NULL)); goto err; } + /* load private key first in-case PIN is required for cert */ conn->private_key = ENGINE_load_private_key(conn->engine, key_id, NULL, NULL); if (!conn->private_key) { @@ -823,6 +825,21 @@ static int tls_engine_init(struct tls_connection *conn, const char *engine_id, ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; goto err; } + + /* handle a certificate and/or CA certificate */ + if (cert_id || ca_cert_id) { + const char *cmd_name = "LOAD_CERT_CTRL"; + + /* test if the engine supports a LOAD_CERT_CTRL */ + if (!ENGINE_ctrl(conn->engine, ENGINE_CTRL_GET_CMD_FROM_NAME, + 0, (void *)cmd_name, NULL)) { + wpa_printf(MSG_ERROR, "ENGINE: engine does not support" + " loading certificates"); + ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; + goto err; + } + } + return 0; err: @@ -1495,6 +1512,112 @@ static int tls_read_pkcs12_blob(SSL_CTX *ssl_ctx, SSL *ssl, } +static int tls_engine_get_cert(struct tls_connection *conn, + const char *cert_id, + X509 **cert) +{ +#ifndef OPENSSL_NO_ENGINE + /* this runs after the private key is loaded so no PIN is required */ + struct { + const char *cert_id; + X509 *cert; + } params; + params.cert_id = cert_id; + params.cert = NULL; + + if (!ENGINE_ctrl_cmd(conn->engine, "LOAD_CERT_CTRL", + 0, ¶ms, NULL, 1)) { + wpa_printf(MSG_ERROR, "ENGINE: cannot load client cert with id" + " '%s' [%s]", cert_id, + ERR_error_string(ERR_get_error(), NULL)); + return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; + } + if (!params.cert) { + wpa_printf(MSG_ERROR, "ENGINE: did not properly cert with id" + " '%s'", cert_id); + return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; + } + *cert = params.cert; + return 0; +#else /* OPENSSL_NO_ENGINE */ + return -1; +#endif /* OPENSSL_NO_ENGINE */ +} + + +static int tls_connection_engine_client_cert(struct tls_connection *conn, + const char *cert_id) +{ +#ifndef OPENSSL_NO_ENGINE + X509 *cert; + + if (tls_engine_get_cert(conn, cert_id, &cert)) + return -1; + + if (!SSL_use_certificate(conn->ssl, cert)) { + tls_show_errors(MSG_ERROR, __func__, + "SSL_use_certificate failed"); + X509_free(cert); + return -1; + } + X509_free(cert); + wpa_printf(MSG_DEBUG, "ENGINE: SSL_use_certificate --> " + "OK"); + return 0; + +#else /* OPENSSL_NO_ENGINE */ + return -1; +#endif /* OPENSSL_NO_ENGINE */ +} + + +static int tls_connection_engine_ca_cert(void *_ssl_ctx, + struct tls_connection *conn, + const char *ca_cert_id) +{ +#ifndef OPENSSL_NO_ENGINE + X509 *cert; + SSL_CTX *ssl_ctx = _ssl_ctx; + + if (tls_engine_get_cert(conn, ca_cert_id, &cert)) + return -1; + + /* start off the same as tls_connection_ca_cert */ + X509_STORE_free(ssl_ctx->cert_store); + ssl_ctx->cert_store = X509_STORE_new(); + if (ssl_ctx->cert_store == NULL) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new " + "certificate store", __func__); + X509_free(cert); + return -1; + } + if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { + unsigned long err = ERR_peek_error(); + tls_show_errors(MSG_WARNING, __func__, + "Failed to add CA certificate from engine " + "to certificate store"); + if (ERR_GET_LIB(err) == ERR_LIB_X509 && + ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring cert" + " already in hash table error", + __func__); + } else { + X509_free(cert); + return -1; + } + } + X509_free(cert); + wpa_printf(MSG_DEBUG, "OpenSSL: %s - added CA certificate from engine " + "to certificate store", __func__); + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + return 0; + +#else /* OPENSSL_NO_ENGINE */ + return -1; +#endif /* OPENSSL_NO_ENGINE */ +} + + static int tls_connection_engine_private_key(struct tls_connection *conn) { #ifndef OPENSSL_NO_ENGINE @@ -2233,26 +2356,39 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, __func__, ERR_error_string(err, NULL)); } + if (params->engine) { + wpa_printf(MSG_DEBUG, "SSL: Initializing TLS engine"); + ret = tls_engine_init(conn, params->engine_id, params->pin, + params->key_id, params->cert_id, + params->ca_cert_id); + if (ret) + return ret; + } if (tls_connection_set_subject_match(conn, params->subject_match, params->altsubject_match)) return -1; - if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert, - params->ca_cert_blob, - params->ca_cert_blob_len, - params->ca_path)) - return -1; - if (tls_connection_client_cert(conn, params->client_cert, - params->client_cert_blob, - params->client_cert_blob_len)) + + if (params->engine && params->ca_cert_id) { + if (tls_connection_engine_ca_cert(tls_ctx, conn, + params->ca_cert_id)) + return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; + } else if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert, + params->ca_cert_blob, + params->ca_cert_blob_len, + params->ca_path)) return -1; - if (params->engine) { - wpa_printf(MSG_DEBUG, "SSL: Initializing TLS engine"); - ret = tls_engine_init(conn, params->engine_id, params->pin, - params->key_id); - if (ret) - return ret; + if (params->engine && params->cert_id) { + if (tls_connection_engine_client_cert(conn, params->cert_id)) + return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; + } else if (tls_connection_client_cert(conn, params->client_cert, + params->client_cert_blob, + params->client_cert_blob_len)) + return -1; + + if (params->engine && params->key_id) { + wpa_printf(MSG_DEBUG, "TLS: Using private key from engine"); if (tls_connection_engine_private_key(conn)) return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; } else if (tls_connection_private_key(tls_ctx, conn,