From fdc16142642b702105bf769cbe95db3370690ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Sun, 22 Nov 2015 02:02:56 +0100 Subject: [PATCH] TLS client: Add support for validating server certificate hash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds support for "hash://server/sha256/cert_hash_in_hex" scheme in ca_cert property for the internal TLS implementation. Signed-off-by: Pali Rohár --- src/tls/tlsv1_client_read.c | 30 ++++++++++++++++++++++++++---- src/tls/tlsv1_cred.c | 28 ++++++++++++++++++++++++++++ src/tls/tlsv1_cred.h | 2 ++ 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c index 4fe95807a..48120b683 100644 --- a/src/tls/tlsv1_client_read.c +++ b/src/tls/tlsv1_client_read.c @@ -364,10 +364,32 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, pos += cert_len; } - if (conn->cred && conn->cred->ca_cert_verify && - x509_certificate_chain_validate(conn->cred->trusted_certs, chain, - &reason, conn->disable_time_checks) - < 0) { + if (conn->cred && conn->cred->server_cert_only && chain) { + u8 hash[SHA256_MAC_LEN]; + char buf[128]; + + wpa_printf(MSG_DEBUG, + "TLSv1: Validate server certificate hash"); + x509_name_string(&chain->subject, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "TLSv1: 0: %s", buf); + if (sha256_vector(1, &chain->cert_start, &chain->cert_len, + hash) < 0 || + os_memcmp(conn->cred->srv_cert_hash, hash, + SHA256_MAC_LEN) != 0) { + wpa_printf(MSG_DEBUG, + "TLSv1: Server certificate hash mismatch"); + wpa_hexdump(MSG_MSGDUMP, "TLSv1: SHA256 hash", + hash, SHA256_MAC_LEN); + 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, + conn->disable_time_checks) + < 0) { int tls_reason; wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain " "validation failed (reason=%d)", reason); diff --git a/src/tls/tlsv1_cred.c b/src/tls/tlsv1_cred.c index fbac96511..3ed21ec1c 100644 --- a/src/tls/tlsv1_cred.c +++ b/src/tls/tlsv1_cred.c @@ -190,6 +190,34 @@ int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert, const u8 *cert_blob, size_t cert_blob_len, const char *path) { + if (cert && os_strncmp(cert, "hash://", 7) == 0) { + const char *pos = cert + 7; + if (os_strncmp(pos, "server/sha256/", 14) != 0) { + wpa_printf(MSG_DEBUG, + "TLSv1: Unsupported ca_cert hash value '%s'", + cert); + return -1; + } + pos += 14; + if (os_strlen(pos) != 32 * 2) { + wpa_printf(MSG_DEBUG, + "TLSv1: Unexpected SHA256 hash length in ca_cert '%s'", + cert); + return -1; + } + if (hexstr2bin(pos, cred->srv_cert_hash, 32) < 0) { + wpa_printf(MSG_DEBUG, + "TLSv1: Invalid SHA256 hash value in ca_cert '%s'", + cert); + return -1; + } + cred->server_cert_only = 1; + cred->ca_cert_verify = 0; + wpa_printf(MSG_DEBUG, + "TLSv1: Checking only server certificate match"); + return 0; + } + cred->ca_cert_verify = cert || cert_blob || path; if (tlsv1_set_cert_chain(&cred->trusted_certs, cert, diff --git a/src/tls/tlsv1_cred.h b/src/tls/tlsv1_cred.h index b1e3e00bd..ac72b8c22 100644 --- a/src/tls/tlsv1_cred.h +++ b/src/tls/tlsv1_cred.h @@ -15,6 +15,8 @@ struct tlsv1_credentials { struct crypto_private_key *key; unsigned int ca_cert_verify:1; + unsigned int server_cert_only:1; + u8 srv_cert_hash[32]; /* Diffie-Hellman parameters */ u8 *dh_p; /* prime */