Add support to crypto/tls for client cert and CA cert from smartcard
authorDavid Smith <dds@google.com>
Fri, 23 May 2008 07:49:59 +0000 (10:49 +0300)
committerJouni Malinen <j@w1.fi>
Fri, 23 May 2008 07:49:59 +0000 (10:49 +0300)
src/crypto/tls.h
src/crypto/tls_openssl.c

index 0a79166..dafe8bb 100644 (file)
@@ -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;
 };
 
 
index 0bbe88e..7e565b4 100644 (file)
@@ -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, &params, 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))
+
+       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 (tls_connection_client_cert(conn, params->client_cert,
-                                      params->client_cert_blob,
-                                      params->client_cert_blob_len))
+
+       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) {
-               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->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,