diff options
Diffstat (limited to 'src/crypto')
-rw-r--r-- | src/crypto/Makefile | 2 | ||||
-rw-r--r-- | src/crypto/aes-internal-enc.c | 4 | ||||
-rw-r--r-- | src/crypto/crypto.h | 9 | ||||
-rw-r--r-- | src/crypto/crypto_gnutls.c | 43 | ||||
-rw-r--r-- | src/crypto/crypto_internal-modexp.c | 41 | ||||
-rw-r--r-- | src/crypto/crypto_internal.c | 3 | ||||
-rw-r--r-- | src/crypto/crypto_libtomcrypt.c | 5 | ||||
-rw-r--r-- | src/crypto/crypto_linux.c | 3 | ||||
-rw-r--r-- | src/crypto/crypto_nettle.c | 36 | ||||
-rw-r--r-- | src/crypto/crypto_openssl.c | 122 | ||||
-rw-r--r-- | src/crypto/crypto_wolfssl.c | 15 | ||||
-rw-r--r-- | src/crypto/dh_groups.c | 1 | ||||
-rw-r--r-- | src/crypto/md4-internal.c | 2 | ||||
-rw-r--r-- | src/crypto/random.c | 72 | ||||
-rw-r--r-- | src/crypto/sha1-tlsprf.c | 3 | ||||
-rw-r--r-- | src/crypto/sha512-internal.c | 8 | ||||
-rw-r--r-- | src/crypto/sha512.c | 104 | ||||
-rw-r--r-- | src/crypto/tls.h | 35 | ||||
-rw-r--r-- | src/crypto/tls_gnutls.c | 75 | ||||
-rw-r--r-- | src/crypto/tls_internal.c | 46 | ||||
-rw-r--r-- | src/crypto/tls_none.c | 5 | ||||
-rw-r--r-- | src/crypto/tls_openssl.c | 524 | ||||
-rw-r--r-- | src/crypto/tls_wolfssl.c | 46 |
23 files changed, 1081 insertions, 123 deletions
diff --git a/src/crypto/Makefile b/src/crypto/Makefile index ee93e41fbbb17..ab108daac835e 100644 --- a/src/crypto/Makefile +++ b/src/crypto/Makefile @@ -62,7 +62,9 @@ LIB_OBJS += crypto_internal-modexp.o LIB_OBJS += crypto_internal-rsa.o LIB_OBJS += tls_internal.o LIB_OBJS += fips_prf_internal.o +ifndef TEST_FUZZ LIB_OBJS += random.o +endif libcrypto.a: $(LIB_OBJS) diff --git a/src/crypto/aes-internal-enc.c b/src/crypto/aes-internal-enc.c index 9fdb4f35cb9b7..baeffcaf630c3 100644 --- a/src/crypto/aes-internal-enc.c +++ b/src/crypto/aes-internal-enc.c @@ -99,6 +99,10 @@ void * aes_encrypt_init(const u8 *key, size_t len) { u32 *rk; int res; + + if (TEST_FAIL()) + return NULL; + rk = os_malloc(AES_PRIV_SIZE); if (rk == NULL) return NULL; diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 507b7cab86fc7..12109ce83a9ad 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -420,6 +420,7 @@ int __must_check crypto_public_key_decrypt_pkcs1( int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, u8 *pubkey); int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len); @@ -703,14 +704,6 @@ struct crypto_ec * crypto_ec_init(int group); void crypto_ec_deinit(struct crypto_ec *e); /** - * crypto_ec_cofactor - Set the cofactor into the big number - * @e: EC context from crypto_ec_init() - * @cofactor: Cofactor of curve. - * Returns: 0 on success, -1 on failure - */ -int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor); - -/** * crypto_ec_prime_len - Get length of the prime in octets * @e: EC context from crypto_ec_init() * Returns: Length of the prime defining the group diff --git a/src/crypto/crypto_gnutls.c b/src/crypto/crypto_gnutls.c index 7a797b5c359d1..4ef11462b36e8 100644 --- a/src/crypto/crypto_gnutls.c +++ b/src/crypto/crypto_gnutls.c @@ -310,12 +310,51 @@ int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len) { - return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, - prime, prime_len, secret, len); + gcry_mpi_t pub = NULL; + int res = -1; + + if (pubkey_len > prime_len || + (pubkey_len == prime_len && + os_memcmp(pubkey, prime, prime_len) >= 0)) + return -1; + + if (gcry_mpi_scan(&pub, GCRYMPI_FMT_USG, pubkey, pubkey_len, NULL) != + GPG_ERR_NO_ERROR || + gcry_mpi_cmp_ui(pub, 1) <= 0) + goto fail; + + if (order) { + gcry_mpi_t p = NULL, q = NULL, tmp; + int failed; + + /* verify: pubkey^q == 1 mod p */ + tmp = gcry_mpi_new(prime_len * 8); + failed = !tmp || + gcry_mpi_scan(&p, GCRYMPI_FMT_USG, prime, prime_len, + NULL) != GPG_ERR_NO_ERROR || + gcry_mpi_scan(&q, GCRYMPI_FMT_USG, order, order_len, + NULL) != GPG_ERR_NO_ERROR; + if (!failed) { + gcry_mpi_powm(tmp, pub, q, p); + failed = gcry_mpi_cmp_ui(tmp, 1) != 0; + } + gcry_mpi_release(p); + gcry_mpi_release(q); + gcry_mpi_release(tmp); + if (failed) + goto fail; + } + + res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +fail: + gcry_mpi_release(pub); + return res; } diff --git a/src/crypto/crypto_internal-modexp.c b/src/crypto/crypto_internal-modexp.c index 92581ac676d31..6819f1a6ab6a7 100644 --- a/src/crypto/crypto_internal-modexp.c +++ b/src/crypto/crypto_internal-modexp.c @@ -40,12 +40,49 @@ int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len) { - return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, - prime, prime_len, secret, len); + struct bignum *pub; + int res = -1; + + if (pubkey_len > prime_len || + (pubkey_len == prime_len && + os_memcmp(pubkey, prime, prime_len) >= 0)) + return -1; + + pub = bignum_init(); + if (!pub || bignum_set_unsigned_bin(pub, pubkey, pubkey_len) < 0 || + bignum_cmp_d(pub, 1) <= 0) + goto fail; + + if (order) { + struct bignum *p, *q, *tmp; + int failed; + + /* verify: pubkey^q == 1 mod p */ + p = bignum_init(); + q = bignum_init(); + tmp = bignum_init(); + failed = !p || !q || !tmp || + bignum_set_unsigned_bin(p, prime, prime_len) < 0 || + bignum_set_unsigned_bin(q, order, order_len) < 0 || + bignum_exptmod(pub, q, p, tmp) < 0 || + bignum_cmp_d(tmp, 1) != 0; + bignum_deinit(p); + bignum_deinit(q); + bignum_deinit(tmp); + if (failed) + goto fail; + } + + res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +fail: + bignum_deinit(pub); + return res; } diff --git a/src/crypto/crypto_internal.c b/src/crypto/crypto_internal.c index d391f48ab5b18..aad40af16e06e 100644 --- a/src/crypto/crypto_internal.c +++ b/src/crypto/crypto_internal.c @@ -310,6 +310,9 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) os_free(ctx); + if (TEST_FAIL()) + return -1; + return 0; } diff --git a/src/crypto/crypto_libtomcrypt.c b/src/crypto/crypto_libtomcrypt.c index 259f99500bcd3..ed30efa021d76 100644 --- a/src/crypto/crypto_libtomcrypt.c +++ b/src/crypto/crypto_libtomcrypt.c @@ -278,6 +278,9 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) os_free(ctx); + if (TEST_FAIL()) + return -1; + return ret; } @@ -721,10 +724,12 @@ int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len) { + /* TODO: check pubkey */ return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, prime, prime_len, secret, len); } diff --git a/src/crypto/crypto_linux.c b/src/crypto/crypto_linux.c index 8099193bf0682..17244561b372f 100644 --- a/src/crypto/crypto_linux.c +++ b/src/crypto/crypto_linux.c @@ -386,6 +386,9 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) } crypto_hash_deinit(ctx); + + if (TEST_FAIL()) + return -1; return 0; } diff --git a/src/crypto/crypto_nettle.c b/src/crypto/crypto_nettle.c index 4e31bc8011325..f85d36532ea19 100644 --- a/src/crypto/crypto_nettle.c +++ b/src/crypto/crypto_nettle.c @@ -331,12 +331,44 @@ int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len) { - return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, - prime, prime_len, secret, len); + mpz_t pub; + int res = -1; + + if (pubkey_len > prime_len || + (pubkey_len == prime_len && + os_memcmp(pubkey, prime, prime_len) >= 0)) + return -1; + + mpz_init(pub); + mpz_import(pub, pubkey_len, 1, 1, 1, 0, pubkey); + if (mpz_cmp_d(pub, 1) <= 0) + goto fail; + + if (order) { + mpz_t p, q, tmp; + int failed; + + /* verify: pubkey^q == 1 mod p */ + mpz_inits(p, q, tmp, NULL); + mpz_import(p, prime_len, 1, 1, 1, 0, prime); + mpz_import(q, order_len, 1, 1, 1, 0, order); + mpz_powm(tmp, pub, q, p); + failed = mpz_cmp_d(tmp, 1) != 0; + mpz_clears(p, q, tmp, NULL); + if (failed) + goto fail; + } + + res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +fail: + mpz_clear(pub); + return res; } diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c index f89053a89d67e..1b0c1ec96b36c 100644 --- a/src/crypto/crypto_openssl.c +++ b/src/crypto/crypto_openssl.c @@ -24,6 +24,7 @@ #endif /* CONFIG_ECC */ #include "common.h" +#include "utils/const_time.h" #include "wpabuf.h" #include "dh_group5.h" #include "sha1.h" @@ -111,6 +112,31 @@ static BIGNUM * get_group5_prime(void) #endif } + +static BIGNUM * get_group5_order(void) +{ + static const unsigned char RFC3526_ORDER_1536[] = { + 0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE4,0x87,0xED,0x51, + 0x10,0xB4,0x61,0x1A,0x62,0x63,0x31,0x45,0xC0,0x6E,0x0E,0x68, + 0x94,0x81,0x27,0x04,0x45,0x33,0xE6,0x3A,0x01,0x05,0xDF,0x53, + 0x1D,0x89,0xCD,0x91,0x28,0xA5,0x04,0x3C,0xC7,0x1A,0x02,0x6E, + 0xF7,0xCA,0x8C,0xD9,0xE6,0x9D,0x21,0x8D,0x98,0x15,0x85,0x36, + 0xF9,0x2F,0x8A,0x1B,0xA7,0xF0,0x9A,0xB6,0xB6,0xA8,0xE1,0x22, + 0xF2,0x42,0xDA,0xBB,0x31,0x2F,0x3F,0x63,0x7A,0x26,0x21,0x74, + 0xD3,0x1B,0xF6,0xB5,0x85,0xFF,0xAE,0x5B,0x7A,0x03,0x5B,0xF6, + 0xF7,0x1C,0x35,0xFD,0xAD,0x44,0xCF,0xD2,0xD7,0x4F,0x92,0x08, + 0xBE,0x25,0x8F,0xF3,0x24,0x94,0x33,0x28,0xF6,0x72,0x2D,0x9E, + 0xE1,0x00,0x3E,0x5C,0x50,0xB1,0xDF,0x82,0xCC,0x6D,0x24,0x1B, + 0x0E,0x2A,0xE9,0xCD,0x34,0x8B,0x1F,0xD4,0x7E,0x92,0x67,0xAF, + 0xC1,0xB2,0xAE,0x91,0xEE,0x51,0xD6,0xCB,0x0E,0x31,0x79,0xAB, + 0x10,0x42,0xA9,0x5D,0xCF,0x6A,0x94,0x83,0xB8,0x4B,0x4B,0x36, + 0xB3,0x86,0x1A,0xA7,0x25,0x5E,0x4C,0x02,0x78,0xBA,0x36,0x04, + 0x65,0x11,0xB9,0x93,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + }; + return BN_bin2bn(RFC3526_ORDER_1536, sizeof(RFC3526_ORDER_1536), NULL); +} + + #ifdef OPENSSL_NO_SHA256 #define NO_SHA256_WRAPPER #endif @@ -518,12 +544,45 @@ int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len) { - return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, - prime, prime_len, secret, len); + BIGNUM *pub, *p; + int res = -1; + + pub = BN_bin2bn(pubkey, pubkey_len, NULL); + p = BN_bin2bn(prime, prime_len, NULL); + if (!pub || !p || BN_is_zero(pub) || BN_is_one(pub) || + BN_cmp(pub, p) >= 0) + goto fail; + + if (order) { + BN_CTX *ctx; + BIGNUM *q, *tmp; + int failed; + + /* verify: pubkey^q == 1 mod p */ + q = BN_bin2bn(order, order_len, NULL); + ctx = BN_CTX_new(); + tmp = BN_new(); + failed = !q || !ctx || !tmp || + !BN_mod_exp(tmp, pub, q, p, ctx) || + !BN_is_one(tmp); + BN_clear(q); + BN_clear(tmp); + BN_CTX_free(ctx); + if (failed) + goto fail; + } + + res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +fail: + BN_clear(pub); + BN_clear(p); + return res; } @@ -549,7 +608,8 @@ int crypto_mod_exp(const u8 *base, size_t base_len, bn_result == NULL) goto error; - if (BN_mod_exp(bn_result, bn_base, bn_exp, bn_modulus, ctx) != 1) + if (BN_mod_exp_mont_consttime(bn_result, bn_base, bn_exp, bn_modulus, + ctx, NULL) != 1) goto error; *result_len = BN_bn2bin(bn_result, result); @@ -709,6 +769,10 @@ void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) if (dh->p == NULL) goto err; + dh->q = get_group5_order(); + if (!dh->q) + goto err; + if (DH_generate_key(dh) != 1) goto err; @@ -737,7 +801,7 @@ err: DH *dh; struct wpabuf *pubkey = NULL, *privkey = NULL; size_t publen, privlen; - BIGNUM *p = NULL, *g; + BIGNUM *p, *g, *q; const BIGNUM *priv_key = NULL, *pub_key = NULL; *priv = NULL; @@ -750,10 +814,12 @@ err: g = BN_new(); p = get_group5_prime(); - if (!g || BN_set_word(g, 2) != 1 || !p || - DH_set0_pqg(dh, p, NULL, g) != 1) + q = get_group5_order(); + if (!g || BN_set_word(g, 2) != 1 || !p || !q || + DH_set0_pqg(dh, p, q, g) != 1) goto err; p = NULL; + q = NULL; g = NULL; if (DH_generate_key(dh) != 1) @@ -778,6 +844,7 @@ err: err: BN_free(p); + BN_free(q); BN_free(g); wpabuf_clear_free(pubkey); wpabuf_clear_free(privkey); @@ -987,6 +1054,9 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) HMAC_CTX_free(ctx->ctx); bin_clear_free(ctx, sizeof(*ctx)); + if (TEST_FAIL()) + return -1; + if (res == 1) { *len = mdlen; return 0; @@ -1250,6 +1320,8 @@ int crypto_bignum_to_bin(const struct crypto_bignum *a, int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m) { + if (TEST_FAIL()) + return -1; return BN_rand_range((BIGNUM *) r, (const BIGNUM *) m) == 1 ? 0 : -1; } @@ -1295,8 +1367,9 @@ int crypto_bignum_exptmod(const struct crypto_bignum *a, bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; - res = BN_mod_exp((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b, - (const BIGNUM *) c, bnctx); + res = BN_mod_exp_mont_consttime((BIGNUM *) d, (const BIGNUM *) a, + (const BIGNUM *) b, (const BIGNUM *) c, + bnctx, NULL); BN_CTX_free(bnctx); return res ? 0 : -1; @@ -1315,6 +1388,11 @@ int crypto_bignum_inverse(const struct crypto_bignum *a, bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; +#ifdef OPENSSL_IS_BORINGSSL + /* TODO: use BN_mod_inverse_blinded() ? */ +#else /* OPENSSL_IS_BORINGSSL */ + BN_set_flags((BIGNUM *) a, BN_FLG_CONSTTIME); +#endif /* OPENSSL_IS_BORINGSSL */ res = BN_mod_inverse((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b, bnctx); BN_CTX_free(bnctx); @@ -1348,6 +1426,9 @@ int crypto_bignum_div(const struct crypto_bignum *a, bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; +#ifndef OPENSSL_IS_BORINGSSL + BN_set_flags((BIGNUM *) a, BN_FLG_CONSTTIME); +#endif /* OPENSSL_IS_BORINGSSL */ res = BN_div((BIGNUM *) c, NULL, (const BIGNUM *) a, (const BIGNUM *) b, bnctx); BN_CTX_free(bnctx); @@ -1425,6 +1506,7 @@ int crypto_bignum_legendre(const struct crypto_bignum *a, BN_CTX *bnctx; BIGNUM *exp = NULL, *tmp = NULL; int res = -2; + unsigned int mask; if (TEST_FAIL()) return -2; @@ -1439,16 +1521,17 @@ int crypto_bignum_legendre(const struct crypto_bignum *a, /* exp = (p-1) / 2 */ !BN_sub(exp, (const BIGNUM *) p, BN_value_one()) || !BN_rshift1(exp, exp) || - !BN_mod_exp(tmp, (const BIGNUM *) a, exp, (const BIGNUM *) p, - bnctx)) + !BN_mod_exp_mont_consttime(tmp, (const BIGNUM *) a, exp, + (const BIGNUM *) p, bnctx, NULL)) goto fail; - if (BN_is_word(tmp, 1)) - res = 1; - else if (BN_is_zero(tmp)) - res = 0; - else - res = -1; + /* Return 1 if tmp == 1, 0 if tmp == 0, or -1 otherwise. Need to use + * constant time selection to avoid branches here. */ + res = -1; + mask = const_time_eq(BN_is_word(tmp, 1), 1); + res = const_time_select_int(mask, 1, res); + mask = const_time_eq(BN_is_zero(tmp), 1); + res = const_time_select_int(mask, 0, res); fail: BN_clear_free(tmp); @@ -1553,13 +1636,6 @@ void crypto_ec_deinit(struct crypto_ec *e) } -int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor) -{ - return EC_GROUP_get_cofactor(e->group, (BIGNUM *) cofactor, - e->bnctx) == 0 ? -1 : 0; -} - - struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e) { if (TEST_FAIL()) diff --git a/src/crypto/crypto_wolfssl.c b/src/crypto/crypto_wolfssl.c index b5a1e3fa31bc1..976a008651b75 100644 --- a/src/crypto/crypto_wolfssl.c +++ b/src/crypto/crypto_wolfssl.c @@ -826,6 +826,7 @@ done: int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len) @@ -952,6 +953,8 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) ret = 0; done: bin_clear_free(ctx, sizeof(*ctx)); + if (TEST_FAIL()) + return -1; return ret; } @@ -1082,6 +1085,8 @@ int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m) int ret = 0; WC_RNG rng; + if (TEST_FAIL()) + return -1; if (wc_InitRng(&rng) != 0) return -1; if (mp_rand_prime((mp_int *) r, @@ -1347,16 +1352,6 @@ void crypto_ec_deinit(struct crypto_ec* e) } -int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor) -{ - if (!e || !cofactor) - return -1; - - mp_set((mp_int *) cofactor, e->key.dp->cofactor); - return 0; -} - - struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e) { if (TEST_FAIL()) diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c index a9b770ec1f160..5e421b24f5a56 100644 --- a/src/crypto/dh_groups.c +++ b/src/crypto/dh_groups.c @@ -1249,6 +1249,7 @@ struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public, if (shared == NULL) return NULL; if (crypto_dh_derive_secret(*dh->generator, dh->prime, dh->prime_len, + dh->order, dh->order_len, wpabuf_head(own_private), wpabuf_len(own_private), wpabuf_head(peer_public), diff --git a/src/crypto/md4-internal.c b/src/crypto/md4-internal.c index d9c737a2970b6..cf408e84fae6a 100644 --- a/src/crypto/md4-internal.c +++ b/src/crypto/md4-internal.c @@ -85,7 +85,7 @@ MD4Transform(u32 state[4], const u8 block[MD4_BLOCK_LENGTH]); (cp)[1] = (value) >> 8; \ (cp)[0] = (value); } while (0) -static u8 PADDING[MD4_BLOCK_LENGTH] = { +static const u8 PADDING[MD4_BLOCK_LENGTH] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 diff --git a/src/crypto/random.c b/src/crypto/random.c index c278d9cb9de94..1cabf3f4b9a42 100644 --- a/src/crypto/random.c +++ b/src/crypto/random.c @@ -25,6 +25,9 @@ #include "utils/includes.h" #ifdef __linux__ #include <fcntl.h> +#ifdef CONFIG_GETRANDOM +#include <sys/random.h> +#endif /* CONFIG_GETRANDOM */ #endif /* __linux__ */ #include "utils/common.h" @@ -228,30 +231,52 @@ int random_pool_ready(void) return 1; /* Already initialized - good to continue */ /* - * Try to fetch some more data from the kernel high quality - * /dev/random. There may not be enough data available at this point, + * Try to fetch some more data from the kernel high quality RNG. + * There may not be enough data available at this point, * so use non-blocking read to avoid blocking the application * completely. */ - fd = open("/dev/random", O_RDONLY | O_NONBLOCK); - if (fd < 0) { - wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s", - strerror(errno)); - return -1; - } - res = read(fd, dummy_key + dummy_key_avail, - sizeof(dummy_key) - dummy_key_avail); +#ifdef CONFIG_GETRANDOM + res = getrandom(dummy_key + dummy_key_avail, + sizeof(dummy_key) - dummy_key_avail, GRND_NONBLOCK); if (res < 0) { - wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: " - "%s", strerror(errno)); - res = 0; + if (errno == ENOSYS) { + wpa_printf(MSG_DEBUG, + "random: getrandom() not supported, falling back to /dev/random"); + } else { + wpa_printf(MSG_INFO, + "random: no data from getrandom(): %s", + strerror(errno)); + res = 0; + } + } +#else /* CONFIG_GETRANDOM */ + res = -1; +#endif /* CONFIG_GETRANDOM */ + if (res < 0) { + fd = open("/dev/random", O_RDONLY | O_NONBLOCK); + if (fd < 0) { + wpa_printf(MSG_ERROR, + "random: Cannot open /dev/random: %s", + strerror(errno)); + return -1; + } + + res = read(fd, dummy_key + dummy_key_avail, + sizeof(dummy_key) - dummy_key_avail); + if (res < 0) { + wpa_printf(MSG_ERROR, + "random: Cannot read from /dev/random: %s", + strerror(errno)); + res = 0; + } + close(fd); } - wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from " - "/dev/random", (unsigned) res, + + wpa_printf(MSG_DEBUG, "random: Got %u/%u random bytes", (unsigned) res, (unsigned) (sizeof(dummy_key) - dummy_key_avail)); dummy_key_avail += res; - close(fd); if (dummy_key_avail == sizeof(dummy_key)) { if (own_pool_ready < MIN_READY_MARK) @@ -261,7 +286,7 @@ int random_pool_ready(void) } wpa_printf(MSG_INFO, "random: Only %u/%u bytes of strong " - "random data available from /dev/random", + "random data available", (unsigned) dummy_key_avail, (unsigned) sizeof(dummy_key)); if (own_pool_ready >= MIN_READY_MARK || @@ -413,6 +438,19 @@ void random_init(const char *entropy_file) if (random_fd >= 0) return; +#ifdef CONFIG_GETRANDOM + { + u8 dummy; + + if (getrandom(&dummy, 0, GRND_NONBLOCK) == 0 || + errno != ENOSYS) { + wpa_printf(MSG_DEBUG, + "random: getrandom() support available"); + return; + } + } +#endif /* CONFIG_GETRANDOM */ + random_fd = open("/dev/random", O_RDONLY | O_NONBLOCK); if (random_fd < 0) { wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s", diff --git a/src/crypto/sha1-tlsprf.c b/src/crypto/sha1-tlsprf.c index f9bc0ebf6e3d0..a11649a933eb3 100644 --- a/src/crypto/sha1-tlsprf.c +++ b/src/crypto/sha1-tlsprf.c @@ -40,9 +40,6 @@ int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label, const unsigned char *SHA1_addr[3]; size_t SHA1_len[3]; - if (secret_len & 1) - return -1; - MD5_addr[0] = A_MD5; MD5_len[0] = MD5_MAC_LEN; MD5_addr[1] = (unsigned char *) label; diff --git a/src/crypto/sha512-internal.c b/src/crypto/sha512-internal.c index 76c4fe750b65f..c0263941c123c 100644 --- a/src/crypto/sha512-internal.c +++ b/src/crypto/sha512-internal.c @@ -109,9 +109,14 @@ static const u64 K[80] = { /* compress 1024-bits */ static int sha512_compress(struct sha512_state *md, unsigned char *buf) { - u64 S[8], W[80], t0, t1; + u64 S[8], t0, t1; + u64 *W; int i; + W = os_malloc(80 * sizeof(u64)); + if (!W) + return -1; + /* copy state into S */ for (i = 0; i < 8; i++) { S[i] = md->state[i]; @@ -146,6 +151,7 @@ static int sha512_compress(struct sha512_state *md, unsigned char *buf) md->state[i] = md->state[i] + S[i]; } + os_free(W); return 0; } diff --git a/src/crypto/sha512.c b/src/crypto/sha512.c new file mode 100644 index 0000000000000..66311c3739208 --- /dev/null +++ b/src/crypto/sha512.c @@ -0,0 +1,104 @@ +/* + * SHA-512 hash implementation and interface functions + * Copyright (c) 2003-2018, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha512.h" +#include "crypto.h" + + +/** + * hmac_sha512_vector - HMAC-SHA512 over data vector (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash (64 bytes) + * Returns: 0 on success, -1 on failure + */ +int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + unsigned char k_pad[128]; /* padding - key XORd with ipad/opad */ + unsigned char tk[64]; + const u8 *_addr[6]; + size_t _len[6], i; + + if (num_elem > 5) { + /* + * Fixed limit on the number of fragments to avoid having to + * allocate memory (which could fail). + */ + return -1; + } + + /* if key is longer than 128 bytes reset it to key = SHA512(key) */ + if (key_len > 128) { + if (sha512_vector(1, &key, &key_len, tk) < 0) + return -1; + key = tk; + key_len = 64; + } + + /* the HMAC_SHA512 transform looks like: + * + * SHA512(K XOR opad, SHA512(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 128 times + * opad is the byte 0x5c repeated 128 times + * and text is the data being protected */ + + /* start out by storing key in ipad */ + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + /* XOR key with ipad values */ + for (i = 0; i < 128; i++) + k_pad[i] ^= 0x36; + + /* perform inner SHA512 */ + _addr[0] = k_pad; + _len[0] = 128; + for (i = 0; i < num_elem; i++) { + _addr[i + 1] = addr[i]; + _len[i + 1] = len[i]; + } + if (sha512_vector(1 + num_elem, _addr, _len, mac) < 0) + return -1; + + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + /* XOR key with opad values */ + for (i = 0; i < 128; i++) + k_pad[i] ^= 0x5c; + + /* perform outer SHA512 */ + _addr[0] = k_pad; + _len[0] = 128; + _addr[1] = mac; + _len[1] = SHA512_MAC_LEN; + return sha512_vector(2, _addr, _len, mac); +} + + +/** + * hmac_sha512 - HMAC-SHA512 over data buffer (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @data: Pointers to the data area + * @data_len: Length of the data area + * @mac: Buffer for the hash (64 bytes) + * Returns: 0 on success, -1 on failure + */ +int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac); +} diff --git a/src/crypto/tls.h b/src/crypto/tls.h index 481b34681d7ba..8bdb91ff2469c 100644 --- a/src/crypto/tls.h +++ b/src/crypto/tls.h @@ -42,6 +42,7 @@ enum tls_fail_reason { TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9, TLS_FAIL_DOMAIN_MISMATCH = 10, TLS_FAIL_INSUFFICIENT_KEY_LEN = 11, + TLS_FAIL_DN_MISMATCH = 12, }; @@ -82,6 +83,7 @@ struct tls_config { int cert_in_cb; const char *openssl_ciphers; unsigned int tls_session_lifetime; + unsigned int crl_reload_interval; unsigned int tls_flags; void (*event_cb)(void *ctx, enum tls_event ev, @@ -103,6 +105,9 @@ struct tls_config { #define TLS_CONN_SUITEB BIT(11) #define TLS_CONN_SUITEB_NO_ECDH BIT(12) #define TLS_CONN_DISABLE_TLSv1_3 BIT(13) +#define TLS_CONN_ENABLE_TLSv1_0 BIT(14) +#define TLS_CONN_ENABLE_TLSv1_1 BIT(15) +#define TLS_CONN_ENABLE_TLSv1_2 BIT(16) /** * struct tls_connection_params - Parameters for TLS connection @@ -115,12 +120,19 @@ struct tls_config { * %NULL to allow all subjects * @altsubject_match: String to match in the alternative subject of the peer * certificate or %NULL to allow all alternative subjects - * @suffix_match: String to suffix match in the dNSName or CN of the peer - * certificate or %NULL to allow all domain names. This may allow subdomains an - * wildcard certificates. Each domain name label must have a full match. + * @suffix_match: Semicolon deliminated string of values to suffix match against + * the dNSName or CN of the peer certificate or %NULL to allow all domain names. + * This may allow subdomains and wildcard certificates. Each domain name label + * must have a full case-insensitive match. * @domain_match: String to match in the dNSName or CN of the peer * certificate or %NULL to allow all domain names. This requires a full, * case-insensitive match. + * + * More than one match string can be provided by using semicolons to + * separate the strings (e.g., example.org;example.com). When multiple + * strings are specified, a match with any one of the values is + * considered a sufficient match for the certificate, i.e., the + * conditions are ORed together. * @client_cert: File or reference name for client X.509 certificate in PEM or * DER format * @client_cert_blob: client_cert as inlined data or %NULL if not used @@ -144,12 +156,15 @@ struct tls_config { * @cert_id: the certificate's id when using engine * @ca_cert_id: the CA certificate's id when using engine * @openssl_ciphers: OpenSSL cipher configuration + * @openssl_ecdh_curves: OpenSSL ECDH curve configuration. %NULL for auto if + * supported, empty string to disable, or a colon-separated curve list. * @flags: Parameter options (TLS_CONN_*) * @ocsp_stapling_response: DER encoded file with cached OCSP stapling response * or %NULL if OCSP is not enabled * @ocsp_stapling_response_multi: DER encoded file with cached OCSP stapling * response list (OCSPResponseList for ocsp_multi in RFC 6961) or %NULL if * ocsp_multi is not enabled + * @check_cert_subject: Client certificate subject name matching string * * TLS connection parameters to be configured with tls_connection_set_params() * and tls_global_set_params(). @@ -187,10 +202,12 @@ struct tls_connection_params { const char *cert_id; const char *ca_cert_id; const char *openssl_ciphers; + const char *openssl_ecdh_curves; unsigned int flags; const char *ocsp_stapling_response; const char *ocsp_stapling_response_multi; + const char *check_cert_subject; }; @@ -321,9 +338,11 @@ int __must_check tls_global_set_params( * @tls_ctx: TLS context data from tls_init() * @check_crl: 0 = do not verify CRLs, 1 = verify CRL for the user certificate, * 2 = verify CRL for all certificates + * @strict: 0 = allow CRL time errors, 1 = do not allow CRL time errors * Returns: 0 on success, -1 on failure */ -int __must_check tls_global_set_verify(void *tls_ctx, int check_crl); +int __must_check tls_global_set_verify(void *tls_ctx, int check_crl, + int strict); /** * tls_connection_set_verify - Set certificate verification options @@ -358,15 +377,21 @@ int __must_check tls_connection_get_random(void *tls_ctx, * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @label: Label (e.g., description of the key) for PRF + * @context: Optional extra upper-layer context (max len 2^16) + * @context_len: The length of the context value * @out: Buffer for output data from TLS-PRF * @out_len: Length of the output buffer * Returns: 0 on success, -1 on failure * - * Exports keying material using the mechanism described in RFC 5705. + * Exports keying material using the mechanism described in RFC 5705. If + * context is %NULL, context is not provided; otherwise, context is provided + * (including the case of empty context with context_len == 0). */ int __must_check tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, const char *label, + const u8 *context, + size_t context_len, u8 *out, size_t out_len); /** diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c index 36dafd2603f0c..daa01d9ed4f64 100644 --- a/src/crypto/tls_gnutls.c +++ b/src/crypto/tls_gnutls.c @@ -461,6 +461,12 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, } } + if (params->openssl_ecdh_curves) { + wpa_printf(MSG_INFO, + "GnuTLS: openssl_ecdh_curves not supported"); + return -1; + } + /* TODO: gnutls_certificate_set_verify_flags(xcred, flags); * to force peer validation(?) */ @@ -733,6 +739,9 @@ int tls_global_set_params(void *tls_ctx, struct tls_global *global = tls_ctx; int ret; + if (params->check_cert_subject) + return -1; /* not yet supported */ + /* Currently, global parameters are only set when running in server * mode. */ global->server = 1; @@ -842,7 +851,7 @@ fail: } -int tls_global_set_verify(void *ssl_ctx, int check_crl) +int tls_global_set_verify(void *ssl_ctx, int check_crl, int strict) { /* TODO */ return 0; @@ -889,14 +898,23 @@ int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, - const char *label, u8 *out, size_t out_len) + const char *label, const u8 *context, + size_t context_len, u8 *out, size_t out_len) { if (conn == NULL || conn->session == NULL) return -1; +#if GNUTLS_VERSION_NUMBER >= 0x030404 + return gnutls_prf_rfc5705(conn->session, os_strlen(label), label, + context_len, (const char *) context, + out_len, (char *) out); +#else /* 3.4.4 */ + if (context) + return -1; return gnutls_prf(conn->session, os_strlen(label), label, 0 /* client_random first */, 0, NULL, out_len, (char *) out); +#endif /* 3.4.4 */ } @@ -1068,6 +1086,52 @@ ocsp_error: } +static int tls_match_suffix_helper(gnutls_x509_crt_t cert, const char *match, + int full) +{ + int res = -1; + +#if GNUTLS_VERSION_NUMBER >= 0x030300 + if (full) + res = gnutls_x509_crt_check_hostname2( + cert, match, + GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS); +#endif /* >= 3.3.0 */ + if (res == -1) + res = gnutls_x509_crt_check_hostname(cert, match); + + wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s --> res=%d", + full ? "": "suffix ", match, res); + return res; +} + + +static int tls_match_suffix(gnutls_x509_crt_t cert, const char *match, + int full) +{ + char *values, *token, *context = NULL; + int ret = 0; + + if (!os_strchr(match, ';')) + return tls_match_suffix_helper(cert, match, full); + + values = os_strdup(match); + if (!values) + return 0; + + /* Process each match alternative separately until a match is found */ + while ((token = str_token(values, ";", &context))) { + if (tls_match_suffix_helper(cert, token, full)) { + ret = 1; + break; + } + } + + os_free(values); + return ret; +} + + static int tls_connection_verify_peer(gnutls_session_t session) { struct tls_connection *conn; @@ -1263,8 +1327,7 @@ static int tls_connection_verify_peer(gnutls_session_t session) if (i == 0) { if (conn->suffix_match && - !gnutls_x509_crt_check_hostname( - cert, conn->suffix_match)) { + !tls_match_suffix(cert, conn->suffix_match, 0)) { wpa_printf(MSG_WARNING, "TLS: Domain suffix match '%s' not found", conn->suffix_match); @@ -1280,9 +1343,7 @@ static int tls_connection_verify_peer(gnutls_session_t session) #if GNUTLS_VERSION_NUMBER >= 0x030300 if (conn->domain_match && - !gnutls_x509_crt_check_hostname2( - cert, conn->domain_match, - GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS)) { + !tls_match_suffix(cert, conn->domain_match, 1)) { wpa_printf(MSG_WARNING, "TLS: Domain match '%s' not found", conn->domain_match); diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c index d289c9442ceb3..8095b43bd21bc 100644 --- a/src/crypto/tls_internal.c +++ b/src/crypto/tls_internal.c @@ -1,6 +1,6 @@ /* * TLS interface functions and an internal TLS implementation - * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -248,6 +248,12 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } + if (params->openssl_ecdh_curves) { + wpa_printf(MSG_INFO, "TLS: openssl_ecdh_curves not supported"); + tlsv1_cred_free(cred); + return -1; + } + if (tlsv1_set_ca_cert(cred, params->ca_cert, params->ca_cert_blob, params->ca_cert_blob_len, params->ca_path)) { @@ -303,6 +309,9 @@ int tls_global_set_params(void *tls_ctx, struct tls_global *global = tls_ctx; struct tlsv1_credentials *cred; + if (params->check_cert_subject) + return -1; /* not yet supported */ + /* Currently, global parameters are only set when running in server * mode. */ global->server = 1; @@ -353,7 +362,7 @@ int tls_global_set_params(void *tls_ctx, } -int tls_global_set_verify(void *tls_ctx, int check_crl) +int tls_global_set_verify(void *tls_ctx, int check_crl, int strict) { struct tls_global *global = tls_ctx; global->check_crl = check_crl; @@ -403,7 +412,8 @@ static int tls_get_keyblock_size(struct tls_connection *conn) static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, - const char *label, int server_random_first, + const char *label, const u8 *context, + size_t context_len, int server_random_first, int skip_keyblock, u8 *out, size_t out_len) { int ret = -1, skip = 0; @@ -422,15 +432,15 @@ static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) { - ret = tlsv1_client_prf(conn->client, label, - server_random_first, + ret = tlsv1_client_prf(conn->client, label, context, + context_len, server_random_first, _out, skip + out_len); } #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) { - ret = tlsv1_server_prf(conn->server, label, - server_random_first, + ret = tlsv1_server_prf(conn->server, label, context, + context_len, server_random_first, _out, skip + out_len); } #endif /* CONFIG_TLS_INTERNAL_SERVER */ @@ -443,17 +453,19 @@ static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, - const char *label, u8 *out, size_t out_len) + const char *label, const u8 *context, + size_t context_len, u8 *out, size_t out_len) { - return tls_connection_prf(tls_ctx, conn, label, 0, 0, out, out_len); + return tls_connection_prf(tls_ctx, conn, label, context, context_len, + 0, 0, out, out_len); } int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn, u8 *out, size_t out_len) { - return tls_connection_prf(tls_ctx, conn, "key expansion", 1, 1, out, - out_len); + return tls_connection_prf(tls_ctx, conn, "key expansion", NULL, 0, + 1, 1, out, out_len); } @@ -720,12 +732,20 @@ int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn, int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn) { +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_failed(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ return 0; } int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn) { +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_read_alerts(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ return 0; } @@ -733,6 +753,10 @@ int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn) int tls_connection_get_write_alerts(void *tls_ctx, struct tls_connection *conn) { +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_write_alerts(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ return 0; } diff --git a/src/crypto/tls_none.c b/src/crypto/tls_none.c index 5d0c6bda15479..6d6fb0cafd31c 100644 --- a/src/crypto/tls_none.c +++ b/src/crypto/tls_none.c @@ -72,7 +72,7 @@ int tls_global_set_params(void *tls_ctx, } -int tls_global_set_verify(void *tls_ctx, int check_crl) +int tls_global_set_verify(void *tls_ctx, int check_crl, int strict) { return -1; } @@ -94,7 +94,8 @@ int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn, int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, - const char *label, u8 *out, size_t out_len) + const char *label, const u8 *context, + size_t context_len, u8 *out, size_t out_len) { return -1; } diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index 0d5ebda699f02..b0c23ae6c9b1f 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -104,7 +104,9 @@ static size_t SSL_SESSION_get_master_key(const SSL_SESSION *session, #endif -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) #ifdef CONFIG_SUITEB static int RSA_bits(const RSA *r) { @@ -212,10 +214,17 @@ static struct tls_context *tls_global = NULL; struct tls_data { SSL_CTX *ssl; unsigned int tls_session_lifetime; + int check_crl; + int check_crl_strict; + char *ca_cert; + unsigned int crl_reload_interval; + struct os_reltime crl_last_reload; + char *check_cert_subject; }; struct tls_connection { struct tls_context *context; + struct tls_data *data; SSL_CTX *ssl_ctx; SSL *ssl; BIO *ssl_in, *ssl_out; @@ -224,6 +233,7 @@ struct tls_connection { EVP_PKEY *private_key; /* the private key if using engine */ #endif /* OPENSSL_NO_ENGINE */ char *subject_match, *altsubject_match, *suffix_match, *domain_match; + char *check_cert_subject; int read_alerts, write_alerts, failed; tls_session_ticket_cb session_ticket_cb; @@ -301,6 +311,36 @@ static void tls_show_errors(int level, const char *func, const char *txt) #endif /* CONFIG_NO_STDOUT_DEBUG */ +static X509_STORE * tls_crl_cert_reload(const char *ca_cert, int check_crl) +{ + int flags; + X509_STORE *store; + + store = X509_STORE_new(); + if (!store) { + wpa_printf(MSG_DEBUG, + "OpenSSL: %s - failed to allocate new certificate store", + __func__); + return NULL; + } + + if (ca_cert && X509_STORE_load_locations(store, ca_cert, NULL) != 1) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to load root certificates"); + X509_STORE_free(store); + return NULL; + } + + flags = check_crl ? X509_V_FLAG_CRL_CHECK : 0; + if (check_crl == 2) + flags |= X509_V_FLAG_CRL_CHECK_ALL; + + X509_STORE_set_flags(store, flags); + + return store; +} + + #ifdef CONFIG_NATIVE_WINDOWS /* Windows CryptoAPI and access to certificate stores */ @@ -989,8 +1029,10 @@ void * tls_init(const struct tls_config *conf) return NULL; } data->ssl = ssl; - if (conf) + if (conf) { data->tls_session_lifetime = conf->tls_session_lifetime; + data->crl_reload_interval = conf->crl_reload_interval; + } SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv2); SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv3); @@ -1072,6 +1114,7 @@ void tls_deinit(void *ssl_ctx) os_free(context); if (data->tls_session_lifetime > 0) SSL_CTX_flush_sessions(ssl, 0); + os_free(data->ca_cert); SSL_CTX_free(ssl); tls_openssl_ref_count--; @@ -1093,6 +1136,7 @@ void tls_deinit(void *ssl_ctx) tls_global = NULL; } + os_free(data->check_cert_subject); os_free(data); } @@ -1305,8 +1349,16 @@ static const char * openssl_handshake_type(int content_type, const u8 *buf, return "client hello"; case 2: return "server hello"; + case 3: + return "hello verify request"; case 4: return "new session ticket"; + case 5: + return "end of early data"; + case 6: + return "hello retry request"; + case 8: + return "encrypted extensions"; case 11: return "certificate"; case 12: @@ -1325,6 +1377,12 @@ static const char * openssl_handshake_type(int content_type, const u8 *buf, return "certificate url"; case 22: return "certificate status"; + case 23: + return "supplemental data"; + case 24: + return "key update"; + case 254: + return "message hash"; default: return "?"; } @@ -1467,11 +1525,32 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) SSL_CTX *ssl = data->ssl; struct tls_connection *conn; long options; + X509_STORE *new_cert_store; + struct os_reltime now; struct tls_context *context = SSL_CTX_get_app_data(ssl); + /* Replace X509 store if it is time to update CRL. */ + if (data->crl_reload_interval > 0 && os_get_reltime(&now) == 0 && + os_reltime_expired(&now, &data->crl_last_reload, + data->crl_reload_interval)) { + wpa_printf(MSG_INFO, + "OpenSSL: Flushing X509 store with ca_cert file"); + new_cert_store = tls_crl_cert_reload(data->ca_cert, + data->check_crl); + if (!new_cert_store) { + wpa_printf(MSG_ERROR, + "OpenSSL: Error replacing X509 store with ca_cert file"); + } else { + /* Replace old store */ + SSL_CTX_set_cert_store(ssl, new_cert_store); + data->crl_last_reload = now; + } + } + conn = os_zalloc(sizeof(*conn)); if (conn == NULL) return NULL; + conn->data = data; conn->ssl_ctx = ssl; conn->ssl = SSL_new(ssl); if (conn->ssl == NULL) { @@ -1535,6 +1614,7 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) os_free(conn->altsubject_match); os_free(conn->suffix_match); os_free(conn->domain_match); + os_free(conn->check_cert_subject); os_free(conn->session_ticket); os_free(conn); } @@ -1655,9 +1735,9 @@ static int tls_match_altsubject(X509 *cert, const char *match) #ifndef CONFIG_NATIVE_WINDOWS static int domain_suffix_match(const u8 *val, size_t len, const char *match, - int full) + size_t match_len, int full) { - size_t i, match_len; + size_t i; /* Check for embedded nuls that could mess up suffix matching */ for (i = 0; i < len; i++) { @@ -1667,7 +1747,6 @@ static int domain_suffix_match(const u8 *val, size_t len, const char *match, } } - match_len = os_strlen(match); if (match_len > len || (full && match_len != len)) return 0; @@ -1687,12 +1766,223 @@ static int domain_suffix_match(const u8 *val, size_t len, const char *match, #endif /* CONFIG_NATIVE_WINDOWS */ -static int tls_match_suffix(X509 *cert, const char *match, int full) +struct tls_dn_field_order_cnt { + u8 cn; + u8 c; + u8 l; + u8 st; + u8 o; + u8 ou; + u8 email; +}; + + +static int get_dn_field_index(const struct tls_dn_field_order_cnt *dn_cnt, + int nid) +{ + switch (nid) { + case NID_commonName: + return dn_cnt->cn; + case NID_countryName: + return dn_cnt->c; + case NID_localityName: + return dn_cnt->l; + case NID_stateOrProvinceName: + return dn_cnt->st; + case NID_organizationName: + return dn_cnt->o; + case NID_organizationalUnitName: + return dn_cnt->ou; + case NID_pkcs9_emailAddress: + return dn_cnt->email; + default: + wpa_printf(MSG_ERROR, + "TLS: Unknown NID '%d' in check_cert_subject", + nid); + return -1; + } +} + + +/** + * match_dn_field - Match configuration DN field against Certificate DN field + * @cert: Certificate + * @nid: NID of DN field + * @field: Field name + * @value DN field value which is passed from configuration + * e.g., if configuration have C=US and this argument will point to US. + * @dn_cnt: DN matching context + * Returns: 1 on success and 0 on failure + */ +static int match_dn_field(const X509 *cert, int nid, const char *field, + const char *value, + const struct tls_dn_field_order_cnt *dn_cnt) +{ + int i, ret = 0, len, config_dn_field_index, match_index = 0; + X509_NAME *name; + + len = os_strlen(value); + name = X509_get_subject_name((X509 *) cert); + + /* Assign incremented cnt for every field of DN to check DN field in + * right order */ + config_dn_field_index = get_dn_field_index(dn_cnt, nid); + if (config_dn_field_index < 0) + return 0; + + /* Fetch value based on NID */ + for (i = -1; (i = X509_NAME_get_index_by_NID(name, nid, i)) > -1;) { + X509_NAME_ENTRY *e; + ASN1_STRING *cn; + + e = X509_NAME_get_entry(name, i); + if (!e) + continue; + + cn = X509_NAME_ENTRY_get_data(e); + if (!cn) + continue; + + match_index++; + + /* check for more than one DN field with same name */ + if (match_index != config_dn_field_index) + continue; + + /* Check wildcard at the right end side */ + /* E.g., if OU=develop* mentioned in configuration, allow 'OU' + * of the subject in the client certificate to start with + * 'develop' */ + if (len > 0 && value[len - 1] == '*') { + /* Compare actual certificate DN field value with + * configuration DN field value up to the specified + * length. */ + ret = ASN1_STRING_length(cn) >= len - 1 && + os_memcmp(ASN1_STRING_get0_data(cn), value, + len - 1) == 0; + } else { + /* Compare actual certificate DN field value with + * configuration DN field value */ + ret = ASN1_STRING_length(cn) == len && + os_memcmp(ASN1_STRING_get0_data(cn), value, + len) == 0; + } + if (!ret) { + wpa_printf(MSG_ERROR, + "OpenSSL: Failed to match %s '%s' with certificate DN field value '%s'", + field, value, ASN1_STRING_get0_data(cn)); + } + break; + } + + return ret; +} + + +/** + * get_value_from_field - Get value from DN field + * @cert: Certificate + * @field_str: DN field string which is passed from configuration file (e.g., + * C=US) + * @dn_cnt: DN matching context + * Returns: 1 on success and 0 on failure + */ +static int get_value_from_field(const X509 *cert, char *field_str, + struct tls_dn_field_order_cnt *dn_cnt) +{ + int nid; + char *context = NULL, *name, *value; + + if (os_strcmp(field_str, "*") == 0) + return 1; /* wildcard matches everything */ + + name = str_token(field_str, "=", &context); + if (!name) + return 0; + + /* Compare all configured DN fields and assign nid based on that to + * fetch correct value from certificate subject */ + if (os_strcmp(name, "CN") == 0) { + nid = NID_commonName; + dn_cnt->cn++; + } else if(os_strcmp(name, "C") == 0) { + nid = NID_countryName; + dn_cnt->c++; + } else if (os_strcmp(name, "L") == 0) { + nid = NID_localityName; + dn_cnt->l++; + } else if (os_strcmp(name, "ST") == 0) { + nid = NID_stateOrProvinceName; + dn_cnt->st++; + } else if (os_strcmp(name, "O") == 0) { + nid = NID_organizationName; + dn_cnt->o++; + } else if (os_strcmp(name, "OU") == 0) { + nid = NID_organizationalUnitName; + dn_cnt->ou++; + } else if (os_strcmp(name, "emailAddress") == 0) { + nid = NID_pkcs9_emailAddress; + dn_cnt->email++; + } else { + wpa_printf(MSG_ERROR, + "TLS: Unknown field '%s' in check_cert_subject", name); + return 0; + } + + value = str_token(field_str, "=", &context); + if (!value) { + wpa_printf(MSG_ERROR, + "TLS: Distinguished Name field '%s' value is not defined in check_cert_subject", + name); + return 0; + } + + return match_dn_field(cert, nid, name, value, dn_cnt); +} + + +/** + * tls_match_dn_field - Match subject DN field with check_cert_subject + * @cert: Certificate + * @match: check_cert_subject string + * Returns: Return 1 on success and 0 on failure +*/ +static int tls_match_dn_field(X509 *cert, const char *match) +{ + const char *token, *last = NULL; + char field[256]; + struct tls_dn_field_order_cnt dn_cnt; + + os_memset(&dn_cnt, 0, sizeof(dn_cnt)); + + /* Maximum length of each DN field is 255 characters */ + + /* Process each '/' delimited field */ + while ((token = cstr_token(match, "/", &last))) { + if (last - token >= (int) sizeof(field)) { + wpa_printf(MSG_ERROR, + "OpenSSL: Too long DN matching field value in '%s'", + match); + return 0; + } + os_memcpy(field, token, last - token); + field[last - token] = '\0'; + + if (!get_value_from_field(cert, field, &dn_cnt)) { + wpa_printf(MSG_DEBUG, "OpenSSL: No match for DN '%s'", + field); + return 0; + } + } + + return 1; +} + + +#ifndef CONFIG_NATIVE_WINDOWS +static int tls_match_suffix_helper(X509 *cert, const char *match, + size_t match_len, int full) { -#ifdef CONFIG_NATIVE_WINDOWS - /* wincrypt.h has conflicting X509_NAME definition */ - return -1; -#else /* CONFIG_NATIVE_WINDOWS */ GENERAL_NAME *gen; void *ext; int i; @@ -1714,8 +2004,8 @@ static int tls_match_suffix(X509 *cert, const char *match, int full) gen->d.dNSName->data, gen->d.dNSName->length); if (domain_suffix_match(gen->d.dNSName->data, - gen->d.dNSName->length, match, full) == - 1) { + gen->d.dNSName->length, + match, match_len, full) == 1) { wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found", full ? "Match" : "Suffix match"); sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free); @@ -1746,8 +2036,8 @@ static int tls_match_suffix(X509 *cert, const char *match, int full) continue; wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName", cn->data, cn->length); - if (domain_suffix_match(cn->data, cn->length, match, full) == 1) - { + if (domain_suffix_match(cn->data, cn->length, + match, match_len, full) == 1) { wpa_printf(MSG_DEBUG, "TLS: %s in commonName found", full ? "Match" : "Suffix match"); return 1; @@ -1757,6 +2047,25 @@ static int tls_match_suffix(X509 *cert, const char *match, int full) wpa_printf(MSG_DEBUG, "TLS: No CommonName %smatch found", full ? "": "suffix "); return 0; +} +#endif /* CONFIG_NATIVE_WINDOWS */ + + +static int tls_match_suffix(X509 *cert, const char *match, int full) +{ +#ifdef CONFIG_NATIVE_WINDOWS + /* wincrypt.h has conflicting X509_NAME definition */ + return -1; +#else /* CONFIG_NATIVE_WINDOWS */ + const char *token, *last = NULL; + + /* Process each match alternative separately until a match is found */ + while ((token = cstr_token(match, ";", &last))) { + if (tls_match_suffix_helper(cert, token, last - token, full)) + return 1; + } + + return 0; #endif /* CONFIG_NATIVE_WINDOWS */ } @@ -1951,6 +2260,7 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) struct tls_connection *conn; struct tls_context *context; char *match, *altmatch, *suffix_match, *domain_match; + const char *check_cert_subject; const char *err_str; err_cert = X509_STORE_CTX_get_current_cert(x509_ctx); @@ -1991,6 +2301,13 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) "time mismatch"); preverify_ok = 1; } + if (!preverify_ok && !conn->data->check_crl_strict && + (err == X509_V_ERR_CRL_HAS_EXPIRED || + err == X509_V_ERR_CRL_NOT_YET_VALID)) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Ignore certificate validity CRL time mismatch"); + preverify_ok = 1; + } err_str = X509_verify_cert_error_string(err); @@ -2044,6 +2361,18 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) "err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'", preverify_ok, err, err_str, conn->ca_cert_verify, depth, buf); + check_cert_subject = conn->check_cert_subject; + if (!check_cert_subject) + check_cert_subject = conn->data->check_cert_subject; + if (check_cert_subject) { + if (depth == 0 && + !tls_match_dn_field(err_cert, check_cert_subject)) { + preverify_ok = 0; + openssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Distinguished Name", + TLS_FAIL_DN_MISMATCH); + } + } if (depth == 0 && match && os_strstr(buf, match) == NULL) { wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not " "match with '%s'", buf, match); @@ -2381,13 +2710,16 @@ static int tls_global_ca_cert(struct tls_data *data, const char *ca_cert) SSL_CTX_set_client_CA_list(ssl_ctx, SSL_load_client_CA_file(ca_cert)); #endif /* OPENSSL_NO_STDIO */ + + os_free(data->ca_cert); + data->ca_cert = os_strdup(ca_cert); } return 0; } -int tls_global_set_verify(void *ssl_ctx, int check_crl) +int tls_global_set_verify(void *ssl_ctx, int check_crl, int strict) { int flags; @@ -2404,6 +2736,10 @@ int tls_global_set_verify(void *ssl_ctx, int check_crl) if (check_crl == 2) flags |= X509_V_FLAG_CRL_CHECK_ALL; X509_STORE_set_flags(cs, flags); + + data->check_crl = check_crl; + data->check_crl_strict = strict; + os_get_reltime(&data->crl_last_reload); } return 0; } @@ -2413,7 +2749,8 @@ static int tls_connection_set_subject_match(struct tls_connection *conn, const char *subject_match, const char *altsubject_match, const char *suffix_match, - const char *domain_match) + const char *domain_match, + const char *check_cert_subject) { os_free(conn->subject_match); conn->subject_match = NULL; @@ -2447,6 +2784,14 @@ static int tls_connection_set_subject_match(struct tls_connection *conn, return -1; } + os_free(conn->check_cert_subject); + conn->check_cert_subject = NULL; + if (check_cert_subject) { + conn->check_cert_subject = os_strdup(check_cert_subject); + if (!conn->check_cert_subject) + return -1; + } + return 0; } @@ -2519,6 +2864,38 @@ static int tls_set_conn_flags(struct tls_connection *conn, unsigned int flags, else SSL_clear_options(ssl, SSL_OP_NO_TLSv1_3); #endif /* SSL_OP_NO_TLSv1_3 */ +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + if (flags & (TLS_CONN_ENABLE_TLSv1_0 | + TLS_CONN_ENABLE_TLSv1_1 | + TLS_CONN_ENABLE_TLSv1_2)) { + int version = 0; + + /* Explicit request to enable TLS versions even if needing to + * override systemwide policies. */ + if (flags & TLS_CONN_ENABLE_TLSv1_0) { + version = TLS1_VERSION; + } else if (flags & TLS_CONN_ENABLE_TLSv1_1) { + if (!(flags & TLS_CONN_DISABLE_TLSv1_0)) + version = TLS1_1_VERSION; + } else if (flags & TLS_CONN_ENABLE_TLSv1_2) { + if (!(flags & (TLS_CONN_DISABLE_TLSv1_0 | + TLS_CONN_DISABLE_TLSv1_1))) + version = TLS1_2_VERSION; + } + if (!version) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Invalid TLS version configuration"); + return -1; + } + + if (SSL_set_min_proto_version(ssl, version) != 1) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Failed to set minimum TLS version"); + return -1; + } + } +#endif /* >= 1.1.0 */ + #ifdef CONFIG_SUITEB #ifdef OPENSSL_IS_BORINGSSL /* Start with defaults from BoringSSL */ @@ -2621,7 +2998,22 @@ static int tls_set_conn_flags(struct tls_connection *conn, unsigned int flags, return -1; } } +#else /* OPENSSL_IS_BORINGSSL */ + if (!(flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) && + openssl_ciphers && SSL_set_cipher_list(ssl, openssl_ciphers) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set openssl_ciphers '%s'", + openssl_ciphers); + return -1; + } #endif /* OPENSSL_IS_BORINGSSL */ +#else /* CONFIG_SUITEB */ + if (openssl_ciphers && SSL_set_cipher_list(ssl, openssl_ciphers) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set openssl_ciphers '%s'", + openssl_ciphers); + return -1; + } #endif /* CONFIG_SUITEB */ return 0; @@ -2743,6 +3135,15 @@ static int tls_connection_client_cert(struct tls_connection *conn, return 0; } +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_IS_BORINGSSL) + if (SSL_use_certificate_chain_file(conn->ssl, client_cert) == 1) { + ERR_clear_error(); + wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_chain_file" + " --> OK"); + return 0; + } +#else if (SSL_use_certificate_file(conn->ssl, client_cert, SSL_FILETYPE_PEM) == 1) { ERR_clear_error(); @@ -2750,6 +3151,7 @@ static int tls_connection_client_cert(struct tls_connection *conn, " --> OK"); return 0; } +#endif tls_show_errors(MSG_DEBUG, __func__, "SSL_use_certificate_file failed"); @@ -3523,11 +3925,13 @@ static int openssl_get_keyblock_size(SSL *ssl) int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, - const char *label, u8 *out, size_t out_len) + const char *label, const u8 *context, + size_t context_len, u8 *out, size_t out_len) { if (!conn || SSL_export_keying_material(conn->ssl, out, out_len, label, - os_strlen(label), NULL, 0, 0) != 1) + os_strlen(label), context, context_len, + context != NULL) != 1) return -1; return 0; } @@ -4445,7 +4849,8 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, params->subject_match, params->altsubject_match, params->suffix_match, - params->domain_match)) + params->domain_match, + params->check_cert_subject)) return -1; if (engine_id && ca_cert_id) { @@ -4503,6 +4908,40 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } + if (!params->openssl_ecdh_curves) { +#ifndef OPENSSL_IS_BORINGSSL +#ifndef OPENSSL_NO_EC +#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) && \ + (OPENSSL_VERSION_NUMBER < 0x10100000L) + if (SSL_set_ecdh_auto(conn->ssl, 1) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set ECDH curves to auto"); + return -1; + } +#endif /* >= 1.0.2 && < 1.1.0 */ +#endif /* OPENSSL_NO_EC */ +#endif /* OPENSSL_IS_BORINGSSL */ + } else if (params->openssl_ecdh_curves[0]) { +#if defined(OPENSSL_IS_BORINGSSL) || (OPENSSL_VERSION_NUMBER < 0x10002000L) + wpa_printf(MSG_INFO, + "OpenSSL: ECDH configuration nnot supported"); + return -1; +#else /* OPENSSL_IS_BORINGSSL || < 1.0.2 */ +#ifndef OPENSSL_NO_EC + if (SSL_set1_curves_list(conn->ssl, + params->openssl_ecdh_curves) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set ECDH curves '%s'", + params->openssl_ecdh_curves); + return -1; + } +#else /* OPENSSL_NO_EC */ + wpa_printf(MSG_INFO, "OpenSSL: ECDH not supported"); + return -1; +#endif /* OPENSSL_NO_EC */ +#endif /* OPENSSL_IS_BORINGSSL */ + } + if (tls_set_conn_flags(conn, params->flags, params->openssl_ciphers) < 0) return -1; @@ -4552,6 +4991,15 @@ int tls_global_set_params(void *tls_ctx, __func__, ERR_error_string(err, NULL)); } + os_free(data->check_cert_subject); + data->check_cert_subject = NULL; + if (params->check_cert_subject) { + data->check_cert_subject = + os_strdup(params->check_cert_subject); + if (!data->check_cert_subject) + return -1; + } + if (tls_global_ca_cert(data, params->ca_cert) || tls_global_client_cert(data, params->client_cert) || tls_global_private_key(data, params->private_key, @@ -4569,6 +5017,44 @@ int tls_global_set_params(void *tls_ctx, return -1; } + if (!params->openssl_ecdh_curves) { +#ifndef OPENSSL_IS_BORINGSSL +#ifndef OPENSSL_NO_EC +#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) && \ + (OPENSSL_VERSION_NUMBER < 0x10100000L) + if (SSL_CTX_set_ecdh_auto(ssl_ctx, 1) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set ECDH curves to auto"); + return -1; + } +#endif /* >= 1.0.2 && < 1.1.0 */ +#endif /* OPENSSL_NO_EC */ +#endif /* OPENSSL_IS_BORINGSSL */ + } else if (params->openssl_ecdh_curves[0]) { +#if defined(OPENSSL_IS_BORINGSSL) || (OPENSSL_VERSION_NUMBER < 0x10002000L) + wpa_printf(MSG_INFO, + "OpenSSL: ECDH configuration nnot supported"); + return -1; +#else /* OPENSSL_IS_BORINGSSL || < 1.0.2 */ +#ifndef OPENSSL_NO_EC +#if OPENSSL_VERSION_NUMBER < 0x10100000L + SSL_CTX_set_ecdh_auto(ssl_ctx, 1); +#endif + if (SSL_CTX_set1_curves_list(ssl_ctx, + params->openssl_ecdh_curves) != + 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set ECDH curves '%s'", + params->openssl_ecdh_curves); + return -1; + } +#else /* OPENSSL_NO_EC */ + wpa_printf(MSG_INFO, "OpenSSL: ECDH not supported"); + return -1; +#endif /* OPENSSL_NO_EC */ +#endif /* OPENSSL_IS_BORINGSSL */ + } + #ifdef SSL_OP_NO_TICKET if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET) SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET); diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c index cc8c704466d2b..e9cb425c115a0 100644 --- a/src/crypto/tls_wolfssl.c +++ b/src/crypto/tls_wolfssl.c @@ -643,9 +643,9 @@ static int tls_match_alt_subject(WOLFSSL_X509 *cert, const char *match) static int domain_suffix_match(const char *val, size_t len, const char *match, - int full) + size_t match_len, int full) { - size_t i, match_len; + size_t i; /* Check for embedded nuls that could mess up suffix matching */ for (i = 0; i < len; i++) { @@ -656,7 +656,6 @@ static int domain_suffix_match(const char *val, size_t len, const char *match, } } - match_len = os_strlen(match); if (match_len > len || (full && match_len != len)) return 0; @@ -674,7 +673,8 @@ static int domain_suffix_match(const char *val, size_t len, const char *match, } -static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full) +static int tls_match_suffix_helper(WOLFSSL_X509 *cert, const char *match, + size_t match_len, int full) { WOLFSSL_ASN1_OBJECT *gen; void *ext; @@ -690,14 +690,14 @@ static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full) for (j = 0; ext && j < wolfSSL_sk_num(ext); j++) { gen = wolfSSL_sk_value(ext, j); - if (gen->type != ALT_NAMES_OID) + if (gen->type != ASN_DNS_TYPE) continue; dns_name++; wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName", gen->obj, os_strlen((char *)gen->obj)); if (domain_suffix_match((const char *) gen->obj, os_strlen((char *) gen->obj), match, - full) == 1) { + match_len, full) == 1) { wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found", full ? "Match" : "Suffix match"); wolfSSL_sk_ASN1_OBJECT_free(ext); @@ -729,8 +729,8 @@ static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full) continue; wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName", cn->data, cn->length); - if (domain_suffix_match(cn->data, cn->length, match, full) == 1) - { + if (domain_suffix_match(cn->data, cn->length, + match, match_len, full) == 1) { wpa_printf(MSG_DEBUG, "TLS: %s in commonName found", full ? "Match" : "Suffix match"); return 1; @@ -743,6 +743,20 @@ static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full) } +static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full) +{ + const char *token, *last = NULL; + + /* Process each match alternative separately until a match is found */ + while ((token = cstr_token(match, ";", &last))) { + if (tls_match_suffix_helper(cert, token, last - token, full)) + return 1; + } + + return 0; +} + + static enum tls_fail_reason wolfssl_tls_fail_reason(int err) { switch (err) { @@ -1487,6 +1501,9 @@ int tls_global_set_params(void *tls_ctx, { wpa_printf(MSG_DEBUG, "SSL: global set params"); + if (params->check_cert_subject) + return -1; /* not yet supported */ + if (tls_global_ca_cert(tls_ctx, params->ca_cert) < 0) { wpa_printf(MSG_INFO, "SSL: Failed to load ca cert file '%s'", params->ca_cert); @@ -1524,6 +1541,12 @@ int tls_global_set_params(void *tls_ctx, return -1; } + if (params->openssl_ecdh_curves) { + wpa_printf(MSG_INFO, + "wolfSSL: openssl_ecdh_curves not supported"); + return -1; + } + #ifdef HAVE_SESSION_TICKET /* Session ticket is off by default - can't disable once on. */ if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET)) @@ -1543,7 +1566,7 @@ int tls_global_set_params(void *tls_ctx, } -int tls_global_set_verify(void *tls_ctx, int check_crl) +int tls_global_set_verify(void *tls_ctx, int check_crl, int strict) { wpa_printf(MSG_DEBUG, "SSL: global set verify: %d", check_crl); @@ -1964,8 +1987,11 @@ int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, - const char *label, u8 *out, size_t out_len) + const char *label, const u8 *context, + size_t context_len, u8 *out, size_t out_len) { + if (context) + return -1; if (!conn || wolfSSL_make_eap_keys(conn->ssl, out, out_len, label) != 0) return -1; return 0; |