From 50b4cf92860d99cdd3815793ac29b5e40cdb2323 Mon Sep 17 00:00:00 2001 From: "kapil.gupta" Date: Thu, 23 Jul 2020 22:42:37 +0530 Subject: [PATCH] wpa_supplicant: Fix IOT issue with latest freeradius Fix inter operability issue with freeradius version 3.0.21 and openssl 1.1.1f when internal tls client is used which requires extension elements in client hello. closes https://github.com/espressif/esp-idf/issues/5273 closes https://github.com/espressif/esp-idf/issues/5627 --- .../wpa_supplicant/src/tls/tlsv1_client.c | 6 +- .../src/tls/tlsv1_client_read.c | 67 ++++++- .../src/tls/tlsv1_client_write.c | 33 +++- .../wpa_supplicant/src/tls/tlsv1_common.c | 182 ++++++++++++++++++ .../wpa_supplicant/src/tls/tlsv1_common.h | 13 ++ 5 files changed, 292 insertions(+), 9 deletions(-) diff --git a/components/wpa_supplicant/src/tls/tlsv1_client.c b/components/wpa_supplicant/src/tls/tlsv1_client.c index d9f038b9a3..6831410a59 100644 --- a/components/wpa_supplicant/src/tls/tlsv1_client.c +++ b/components/wpa_supplicant/src/tls/tlsv1_client.c @@ -706,18 +706,16 @@ int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type, if (data == NULL || data_len == 0) return 0; - pos = conn->client_hello_ext = os_malloc(6 + data_len); + pos = conn->client_hello_ext = os_malloc(4 + data_len); if (pos == NULL) return -1; - WPA_PUT_BE16(pos, 4 + data_len); - pos += 2; WPA_PUT_BE16(pos, ext_type); pos += 2; WPA_PUT_BE16(pos, data_len); pos += 2; os_memcpy(pos, data, data_len); - conn->client_hello_ext_len = 6 + data_len; + conn->client_hello_ext_len = 4 + data_len; if (ext_type == TLS_EXT_PAC_OPAQUE) { conn->session_ticket_included = 1; diff --git a/components/wpa_supplicant/src/tls/tlsv1_client_read.c b/components/wpa_supplicant/src/tls/tlsv1_client_read.c index a585f20603..ada19464ae 100644 --- a/components/wpa_supplicant/src/tls/tlsv1_client_read.c +++ b/components/wpa_supplicant/src/tls/tlsv1_client_read.c @@ -409,9 +409,11 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, - const u8 *buf, size_t len) + const u8 *buf, size_t len, + tls_key_exchange key_exchange) { - const u8 *pos, *end; + const u8 *pos, *end, *server_params, *server_params_end; + u8 alert; tlsv1_client_free_dh(conn); @@ -420,6 +422,7 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, if (end - pos < 3) goto fail; + server_params = pos; conn->dh_p_len = WPA_GET_BE16(pos); pos += 2; if (conn->dh_p_len == 0 || end - pos < (int) conn->dh_p_len) { @@ -464,6 +467,60 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, pos += conn->dh_ys_len; wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)", conn->dh_ys, conn->dh_ys_len); + server_params_end = pos; + + if (key_exchange == TLS_KEY_X_DHE_RSA) { + u8 hash[64]; + int hlen; + + if (conn->rl.tls_version == TLS_VERSION_1_2) { +#ifdef CONFIG_TLSV12 + /* + * RFC 5246, 4.7: + * TLS v1.2 adds explicit indication of the used + * signature and hash algorithms. + * + * struct { + * HashAlgorithm hash; + * SignatureAlgorithm signature; + * } SignatureAndHashAlgorithm; + */ + if (end - pos < 2) + goto fail; + if ((pos[0] != TLS_HASH_ALG_SHA256) || + pos[1] != TLS_SIGN_ALG_RSA) { + wpa_printf(MSG_DEBUG, "TLSv1.2: Unsupported hash(%u)/signature(%u) algorithm", + pos[0], pos[1]); + goto fail; + } + + hlen = tlsv12_key_x_server_params_hash( + conn->rl.tls_version, pos[0], + conn->client_random, + conn->server_random, server_params, + server_params_end - server_params, hash); + pos += 2; +#else /* CONFIG_TLSV12 */ + goto fail; +#endif /* CONFIG_TLSV12 */ + } else { + hlen = tls_key_x_server_params_hash( + conn->rl.tls_version, conn->client_random, + conn->server_random, server_params, + server_params_end - server_params, hash); + } + + if (hlen < 0) + goto fail; + wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerKeyExchange hash", + hash, hlen); + + if (tls_verify_signature(conn->rl.tls_version, + conn->server_rsa_key, + hash, hlen, pos, end - pos, + &alert) < 0) + goto fail; + } return 0; @@ -542,8 +599,10 @@ static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct, wpa_hexdump(MSG_DEBUG, "TLSv1: ServerKeyExchange", pos, len); suite = tls_get_cipher_suite(conn->rl.cipher_suite); - if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) { - if (tlsv1_process_diffie_hellman(conn, pos, len) < 0) { + if (suite && (suite->key_exchange == TLS_KEY_X_DH_anon || + suite->key_exchange == TLS_KEY_X_DHE_RSA)) { + if (tlsv1_process_diffie_hellman(conn, pos, len, + suite->key_exchange) < 0) { tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; diff --git a/components/wpa_supplicant/src/tls/tlsv1_client_write.c b/components/wpa_supplicant/src/tls/tlsv1_client_write.c index 53a1b33881..a28ce4fa35 100644 --- a/components/wpa_supplicant/src/tls/tlsv1_client_write.c +++ b/components/wpa_supplicant/src/tls/tlsv1_client_write.c @@ -48,6 +48,7 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) u8 *hello, *end, *pos, *hs_length, *hs_start, *rhdr; struct os_time now; size_t len, i; + u8 *ext_start; wpa_printf(MSG_DEBUG, "TLSv1: Send ClientHello"); *out_len = 0; @@ -62,7 +63,7 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random", conn->client_random, TLS_RANDOM_LEN); - len = 100 + conn->num_cipher_suites * 2 + conn->client_hello_ext_len; + len = 150 + conn->num_cipher_suites * 2 + conn->client_hello_ext_len; hello = os_malloc(len); if (hello == NULL) return NULL; @@ -102,12 +103,42 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) *pos++ = 1; *pos++ = TLS_COMPRESSION_NULL; + /* Extension */ + ext_start = pos; + pos += 2; + +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + /* + * Add signature_algorithms extension since we support only + * SHA256 (and not the default SHA1) with TLSv1.2. + */ + /* ExtensionsType extension_type = signature_algorithms(13) */ + WPA_PUT_BE16(pos, TLS_EXT_SIGNATURE_ALGORITHMS); + pos += 2; + /* opaque extension_data<0..2^16-1> length */ + WPA_PUT_BE16(pos, 4); + pos += 2; + /* supported_signature_algorithms<2..2^16-2> length */ + WPA_PUT_BE16(pos, 2); + pos += 2; + /* supported_signature_algorithms */ + *pos++ = TLS_HASH_ALG_SHA256; + *pos++ = TLS_SIGN_ALG_RSA; + } +#endif /* CONFIG_TLSV12 */ + if (conn->client_hello_ext) { os_memcpy(pos, conn->client_hello_ext, conn->client_hello_ext_len); pos += conn->client_hello_ext_len; } + if (pos == ext_start + 2) + pos -= 2; /* no extensions */ + else + WPA_PUT_BE16(ext_start, pos - ext_start - 2); + WPA_PUT_BE24(hs_length, pos - hs_length - 3); tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); diff --git a/components/wpa_supplicant/src/tls/tlsv1_common.c b/components/wpa_supplicant/src/tls/tlsv1_common.c index f0ba62f270..ca48b1b8b7 100644 --- a/components/wpa_supplicant/src/tls/tlsv1_common.c +++ b/components/wpa_supplicant/src/tls/tlsv1_common.c @@ -9,6 +9,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "crypto/md5.h" #include "crypto/sha1.h" #include "crypto/sha256.h" #include "tls/tls.h" @@ -333,3 +334,184 @@ int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label, return tls_prf_sha1_md5(secret, secret_len, label, seed, seed_len, out, outlen); } + + +#ifdef CONFIG_TLSV12 +int tlsv12_key_x_server_params_hash(u16 tls_version, u8 hash_alg, + const u8 *client_random, + const u8 *server_random, + const u8 *server_params, + size_t server_params_len, u8 *hash) +{ + size_t hlen; + struct crypto_hash *ctx; + enum crypto_hash_alg alg; + + switch (hash_alg) { + case TLS_HASH_ALG_SHA256: + alg = CRYPTO_HASH_ALG_SHA256; + hlen = SHA256_MAC_LEN; + break; + default: + return -1; + } + ctx = crypto_hash_init(alg, NULL, 0); + if (ctx == NULL) + return -1; + crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN); + crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN); + crypto_hash_update(ctx, server_params, server_params_len); + if (crypto_hash_finish(ctx, hash, &hlen) < 0) + return -1; + + return hlen; +} +#endif /* CONFIG_TLSV12 */ + + +int tls_key_x_server_params_hash(u16 tls_version, const u8 *client_random, + const u8 *server_random, + const u8 *server_params, + size_t server_params_len, u8 *hash) +{ + u8 *hpos; + size_t hlen; + struct crypto_hash *ctx; + + hpos = hash; + + ctx = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0); + if (ctx == NULL) + return -1; + crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN); + crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN); + crypto_hash_update(ctx, server_params, server_params_len); + hlen = MD5_MAC_LEN; + if (crypto_hash_finish(ctx, hash, &hlen) < 0) + return -1; + hpos += hlen; + + ctx = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0); + if (ctx == NULL) + return -1; + crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN); + crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN); + crypto_hash_update(ctx, server_params, server_params_len); + hlen = hash + sizeof(hash) - hpos; + if (crypto_hash_finish(ctx, hpos, &hlen) < 0) + return -1; + hpos += hlen; + return hpos - hash; +} + + +int tls_verify_signature(u16 tls_version, struct crypto_public_key *pk, + const u8 *data, size_t data_len, + const u8 *pos, size_t len, u8 *alert) +{ + u8 *buf; + const u8 *end = pos + len; + const u8 *decrypted; + u16 slen; + size_t buflen; + + if (end - pos < 2) { + *alert = TLS_ALERT_DECODE_ERROR; + return -1; + } + slen = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < slen) { + *alert = TLS_ALERT_DECODE_ERROR; + return -1; + } + if (end - pos > slen) { + wpa_hexdump(MSG_MSGDUMP, "Additional data after Signature", + pos + slen, end - pos - slen); + end = pos + slen; + } + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: Signature", pos, end - pos); + if (pk == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: No public key to verify signature"); + *alert = TLS_ALERT_INTERNAL_ERROR; + return -1; + } + + buflen = end - pos; + buf = os_malloc(end - pos); + if (buf == NULL) { + *alert = TLS_ALERT_INTERNAL_ERROR; + return -1; + } + if (crypto_public_key_decrypt_pkcs1(pk, pos, end - pos, buf, &buflen) < + 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt signature"); + os_free(buf); + *alert = TLS_ALERT_DECRYPT_ERROR; + return -1; + } + decrypted = buf; + + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Decrypted Signature", + decrypted, buflen); + +#ifdef CONFIG_TLSV12 + if (tls_version >= TLS_VERSION_1_2) { + /* + * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5 + * + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithm, + * digest OCTET STRING + * } + * + * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11} + * + * DER encoded DigestInfo for SHA256 per RFC 3447: + * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || + * H + */ + if (buflen >= 19 + 32 && + os_memcmp(buf, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01" + "\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19) == 0) + { + wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithm = SHA-256"); + decrypted = buf + 19; + buflen -= 19; + } else if (buflen >= 19 + 48 && + os_memcmp(buf, "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01" + "\x65\x03\x04\x02\x02\x05\x00\x04\x30", 19) == 0) + { + wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithm = SHA-384"); + decrypted = buf + 19; + buflen -= 19; + } else if (buflen >= 19 + 64 && + os_memcmp(buf, "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01" + "\x65\x03\x04\x02\x03\x05\x00\x04\x40", 19) == 0) + { + wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithm = SHA-512"); + decrypted = buf + 19; + buflen -= 19; + + } else { + wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized DigestInfo"); + os_free(buf); + *alert = TLS_ALERT_DECRYPT_ERROR; + return -1; + } + } +#endif /* CONFIG_TLSV12 */ + + if (buflen != data_len || + os_memcmp_const(decrypted, data, data_len) != 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid Signature in CertificateVerify - did not match calculated hash"); + os_free(buf); + *alert = TLS_ALERT_DECRYPT_ERROR; + return -1; + } + + os_free(buf); + + return 0; +} diff --git a/components/wpa_supplicant/src/tls/tlsv1_common.h b/components/wpa_supplicant/src/tls/tlsv1_common.h index f28c0cdc47..75a6274d3d 100644 --- a/components/wpa_supplicant/src/tls/tlsv1_common.h +++ b/components/wpa_supplicant/src/tls/tlsv1_common.h @@ -169,6 +169,7 @@ enum { #define TLS_EXT_TRUSTED_CA_KEYS 3 /* RFC 4366 */ #define TLS_EXT_TRUNCATED_HMAC 4 /* RFC 4366 */ #define TLS_EXT_STATUS_REQUEST 5 /* RFC 4366 */ +#define TLS_EXT_SIGNATURE_ALGORITHMS 13 /* RFC 5246 */ #define TLS_EXT_SESSION_TICKET 35 /* RFC 4507 */ #define TLS_EXT_PAC_OPAQUE TLS_EXT_SESSION_TICKET /* EAP-FAST terminology */ @@ -257,5 +258,17 @@ int tls_version_ok(u16 ver); const char * tls_version_str(u16 ver); int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, u8 *out, size_t outlen); +int tlsv12_key_x_server_params_hash(u16 tls_version, u8 hash_Alg, + const u8 *client_random, + const u8 *server_random, + const u8 *server_params, + size_t server_params_len, u8 *hash); +int tls_key_x_server_params_hash(u16 tls_version, const u8 *client_random, + const u8 *server_random, + const u8 *server_params, + size_t server_params_len, u8 *hash); +int tls_verify_signature(u16 tls_version, struct crypto_public_key *pk, + const u8 *data, size_t data_len, + const u8 *pos, size_t len, u8 *alert); #endif /* TLSV1_COMMON_H */