]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
TLS client: Add support for server certificate probing
authorJouni Malinen <j@w1.fi>
Sun, 29 Nov 2015 16:59:27 +0000 (18:59 +0200)
committerJouni Malinen <j@w1.fi>
Sun, 29 Nov 2015 16:59:27 +0000 (18:59 +0200)
The internal TLS client implementation can now be used with
ca_cert="probe://" to probe the server certificate chain. This is also
adding the related CTRL-EVENT-EAP-TLS-CERT-ERROR and
CTRL-EVENT-EAP-PEER-CERT events.

Signed-off-by: Jouni Malinen <j@w1.fi>
src/crypto/tls_internal.c
src/tls/tlsv1_client.c
src/tls/tlsv1_client.h
src/tls/tlsv1_client_i.h
src/tls/tlsv1_client_read.c
src/tls/tlsv1_cred.c
src/tls/tlsv1_cred.h

index 1b82245e043a812f24e535fde5e0d31ac628ba9d..b91f181e2aae06e2f00f0d269d40b82f81a353a5 100644 (file)
@@ -23,6 +23,11 @@ struct tls_global {
        int server;
        struct tlsv1_credentials *server_cred;
        int check_crl;
+
+       void (*event_cb)(void *ctx, enum tls_event ev,
+                        union tls_event_data *data);
+       void *cb_ctx;
+       int cert_in_cb;
 };
 
 struct tls_connection {
@@ -51,6 +56,11 @@ void * tls_init(const struct tls_config *conf)
        global = os_zalloc(sizeof(*global));
        if (global == NULL)
                return NULL;
+       if (conf) {
+               global->event_cb = conf->event_cb;
+               global->cb_ctx = conf->cb_ctx;
+               global->cert_in_cb = conf->cert_in_cb;
+       }
 
        return global;
 }
@@ -97,6 +107,8 @@ struct tls_connection * tls_connection_init(void *tls_ctx)
                        os_free(conn);
                        return NULL;
                }
+               tlsv1_client_set_cb(conn->client, global->event_cb,
+                                   global->cb_ctx, global->cert_in_cb);
        }
 #endif /* CONFIG_TLS_INTERNAL_CLIENT */
 #ifdef CONFIG_TLS_INTERNAL_SERVER
index 2fa43add76fc9d182bbf68a56b4ace6636159766..49ebf2ff32264e6af8faa380a28d17ae137b1d6c 100644 (file)
@@ -826,3 +826,15 @@ void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn,
        conn->session_ticket_cb = cb;
        conn->session_ticket_cb_ctx = ctx;
 }
+
+
+void tlsv1_client_set_cb(struct tlsv1_client *conn,
+                        void (*event_cb)(void *ctx, enum tls_event ev,
+                                         union tls_event_data *data),
+                        void *cb_ctx,
+                        int cert_in_cb)
+{
+       conn->event_cb = event_cb;
+       conn->cb_ctx = cb_ctx;
+       conn->cert_in_cb = !!cert_in_cb;
+}
index a4e25e969937c6e3c2c578099ea2d2c98254ad48..bbfb8bdd99229ab86cf17062b9423f1663e6d602 100644 (file)
@@ -51,4 +51,10 @@ void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn,
                                        tlsv1_client_session_ticket_cb cb,
                                        void *ctx);
 
+void tlsv1_client_set_cb(struct tlsv1_client *conn,
+                        void (*event_cb)(void *ctx, enum tls_event ev,
+                                         union tls_event_data *data),
+                        void *cb_ctx,
+                        int cert_in_cb);
+
 #endif /* TLSV1_CLIENT_H */
index 55fdcf8d0435cf38e39c83babd225447c65c5a40..1c517a8f346444f6b92e66ccf627efc0f4cd8c90 100644 (file)
@@ -34,6 +34,7 @@ struct tlsv1_client {
        unsigned int session_ticket_included:1;
        unsigned int use_session_ticket:1;
        unsigned int disable_time_checks:1;
+       unsigned int cert_in_cb:1;
 
        struct crypto_public_key *server_rsa_key;
 
@@ -64,6 +65,10 @@ struct tlsv1_client {
        void *session_ticket_cb_ctx;
 
        struct wpabuf *partial_input;
+
+       void (*event_cb)(void *ctx, enum tls_event ev,
+                        union tls_event_data *data);
+       void *cb_ctx;
 };
 
 
index 294ae008194ea2d917f143080e9491a5f4b33ac1..a2cd478e87729199ce4f56a86a724e3cc1049638 100644 (file)
@@ -211,6 +211,47 @@ decode_error:
 }
 
 
+static void tls_peer_cert_event(struct tlsv1_client *conn, int depth,
+                               struct x509_certificate *cert)
+{
+       union tls_event_data ev;
+       struct wpabuf *cert_buf = NULL;
+#ifdef CONFIG_SHA256
+       u8 hash[32];
+#endif /* CONFIG_SHA256 */
+       char subject[128];
+
+       if (!conn->event_cb)
+               return;
+
+       os_memset(&ev, 0, sizeof(ev));
+       if (conn->cred->cert_probe || conn->cert_in_cb) {
+               cert_buf = wpabuf_alloc_copy(cert->cert_start,
+                                            cert->cert_len);
+               ev.peer_cert.cert = cert_buf;
+       }
+#ifdef CONFIG_SHA256
+       if (cert_buf) {
+               const u8 *addr[1];
+               size_t len[1];
+               addr[0] = wpabuf_head(cert_buf);
+               len[0] = wpabuf_len(cert_buf);
+               if (sha256_vector(1, addr, len, hash) == 0) {
+                       ev.peer_cert.hash = hash;
+                       ev.peer_cert.hash_len = sizeof(hash);
+               }
+       }
+#endif /* CONFIG_SHA256 */
+
+       ev.peer_cert.depth = depth;
+       x509_name_string(&cert->subject, subject, sizeof(subject));
+       ev.peer_cert.subject = subject;
+
+       conn->event_cb(conn->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
+       wpabuf_free(cert_buf);
+}
+
+
 static int tls_process_certificate(struct tlsv1_client *conn, u8 ct,
                                   const u8 *in_data, size_t *in_len)
 {
@@ -354,6 +395,8 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct,
                        return -1;
                }
 
+               tls_peer_cert_event(conn, idx, cert);
+
                if (last == NULL)
                        chain = cert;
                else
@@ -380,11 +423,45 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct,
                                   "TLSv1: Server certificate hash mismatch");
                        wpa_hexdump(MSG_MSGDUMP, "TLSv1: SHA256 hash",
                                    hash, SHA256_MAC_LEN);
+                       if (conn->event_cb) {
+                               union tls_event_data ev;
+
+                               os_memset(&ev, 0, sizeof(ev));
+                               ev.cert_fail.reason = TLS_FAIL_UNSPECIFIED;
+                               ev.cert_fail.reason_txt =
+                                       "Server certificate mismatch";
+                               ev.cert_fail.subject = buf;
+                               conn->event_cb(conn->cb_ctx,
+                                              TLS_CERT_CHAIN_FAILURE, &ev);
+                       }
                        tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                  TLS_ALERT_BAD_CERTIFICATE);
                        x509_certificate_chain_free(chain);
                        return -1;
                }
+       } else if (conn->cred && conn->cred->cert_probe) {
+               wpa_printf(MSG_DEBUG,
+                          "TLSv1: Reject server certificate on probe-only rune");
+               if (conn->event_cb) {
+                       union tls_event_data ev;
+                       char buf[128];
+
+                       os_memset(&ev, 0, sizeof(ev));
+                       ev.cert_fail.reason = TLS_FAIL_SERVER_CHAIN_PROBE;
+                       ev.cert_fail.reason_txt =
+                               "Server certificate chain probe";
+                       if (chain) {
+                               x509_name_string(&chain->subject, buf,
+                                                sizeof(buf));
+                               ev.cert_fail.subject = buf;
+                       }
+                       conn->event_cb(conn->cb_ctx, TLS_CERT_CHAIN_FAILURE,
+                                      &ev);
+               }
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_BAD_CERTIFICATE);
+               x509_certificate_chain_free(chain);
+               return -1;
        } else if (conn->cred && conn->cred->ca_cert_verify &&
                   x509_certificate_chain_validate(conn->cred->trusted_certs,
                                                   chain, &reason,
index 3ed21ec1c82a5ddcc58bcf02393c55a08ddaa5a1..067562b6b3d0c0f2d44ea9312a763354a9af8287 100644 (file)
@@ -218,6 +218,13 @@ int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert,
                return 0;
        }
 
+       if (cert && os_strncmp(cert, "probe://", 8) == 0) {
+               cred->cert_probe = 1;
+               cred->ca_cert_verify = 0;
+               wpa_printf(MSG_DEBUG, "TLSv1: Only probe server certificate");
+               return 0;
+       }
+
        cred->ca_cert_verify = cert || cert_blob || path;
 
        if (tlsv1_set_cert_chain(&cred->trusted_certs, cert,
index ac72b8c228a1aeadf464630a6963857af4abc5fd..b4bfe38d53bb3dbff138ef82bff8b95b3e1e6953 100644 (file)
@@ -14,6 +14,7 @@ struct tlsv1_credentials {
        struct x509_certificate *cert;
        struct crypto_private_key *key;
 
+       unsigned int cert_probe:1;
        unsigned int ca_cert_verify:1;
        unsigned int server_cert_only:1;
        u8 srv_cert_hash[32];