Browse Source

TLS client: Add support for server certificate probing

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>
Jouni Malinen 9 years ago
parent
commit
f2a6ad01a9

+ 12 - 0
src/crypto/tls_internal.c

@@ -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

+ 12 - 0
src/tls/tlsv1_client.c

@@ -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;
+}

+ 6 - 0
src/tls/tlsv1_client.h

@@ -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 */

+ 5 - 0
src/tls/tlsv1_client_i.h

@@ -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;
 };
 
 

+ 77 - 0
src/tls/tlsv1_client_read.c

@@ -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,

+ 7 - 0
src/tls/tlsv1_cred.c

@@ -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,

+ 1 - 0
src/tls/tlsv1_cred.h

@@ -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];