3314 lines
104 KiB
C
3314 lines
104 KiB
C
|
/*
|
||
|
* SSL/TLS interface functions for mbed TLS
|
||
|
*
|
||
|
* SPDX-FileCopyrightText: 2022 Glenn Strauss <gstrauss@gluelogic.com>
|
||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||
|
*
|
||
|
* This software may be distributed under the terms of the BSD license.
|
||
|
* See README for more details.
|
||
|
*
|
||
|
* template: src/crypto/tls_none.c
|
||
|
* reference: src/crypto/tls_*.c
|
||
|
*
|
||
|
* Known Limitations:
|
||
|
* - no TLSv1.3 (not available in mbedtls 2.x; experimental in mbedtls 3.x)
|
||
|
* - no OCSP (not yet available in mbedtls)
|
||
|
* - mbedtls does not support all certificate encodings used by hwsim tests
|
||
|
* PCKS#5 v1.5
|
||
|
* PCKS#12
|
||
|
* DH DSA
|
||
|
* - EAP-FAST, EAP-TEAP session ticket support not implemented in tls_mbedtls.c
|
||
|
* - mbedtls does not currently provide way to set an attribute in a CSR
|
||
|
* https://github.com/Mbed-TLS/mbedtls/issues/4886
|
||
|
* so tests/hwsim dpp_enterprise tests fail
|
||
|
* - DPP2 not supported
|
||
|
* PKCS#7 parsing is not supported in mbedtls
|
||
|
* See crypto_mbedtls.c:crypto_pkcs7_get_certificates() comments
|
||
|
* - DPP3 not supported
|
||
|
* hpke_base_seal() and hpke_base_seal() not implemented in crypto_mbedtls.c
|
||
|
*
|
||
|
* Status:
|
||
|
* - code written to be compatible with mbedtls 2.x and mbedtls 3.x
|
||
|
* (currently requires mbedtls >= 2.27.0 for mbedtls_mpi_random())
|
||
|
* (currently requires mbedtls >= 2.18.0 for mbedtls_ssl_tls_prf())
|
||
|
* - builds with tests/build/build-wpa_supplicant-mbedtls.config
|
||
|
* - passes all tests/ crypto module tests (incomplete coverage)
|
||
|
* ($ cd tests; make clean; make -j 4 run-tests CONFIG_TLS=mbedtls)
|
||
|
* - passes almost all tests/hwsim tests
|
||
|
* (hwsim tests skipped for missing features)
|
||
|
*
|
||
|
* RFE:
|
||
|
* - EAP-FAST, EAP-TEAP session ticket support not implemented in tls_mbedtls.c
|
||
|
* - client/server session resumption, and/or save client session ticket
|
||
|
*/
|
||
|
|
||
|
#include "includes.h"
|
||
|
#include "common.h"
|
||
|
|
||
|
#include <mbedtls/version.h>
|
||
|
#include <mbedtls/ctr_drbg.h>
|
||
|
#include <mbedtls/error.h>
|
||
|
#include <mbedtls/oid.h>
|
||
|
#include <mbedtls/pem.h>
|
||
|
#include <mbedtls/platform.h> /* mbedtls_calloc() mbedtls_free() */
|
||
|
#include <mbedtls/platform_util.h> /* mbedtls_platform_zeroize() */
|
||
|
#include <mbedtls/ssl.h>
|
||
|
#include <mbedtls/ssl_ticket.h>
|
||
|
#include <mbedtls/x509.h>
|
||
|
#include <mbedtls/x509_crt.h>
|
||
|
|
||
|
#if MBEDTLS_VERSION_NUMBER >= 0x02040000 /* mbedtls 2.4.0 */
|
||
|
#include <mbedtls/net_sockets.h>
|
||
|
#else
|
||
|
#include <mbedtls/net.h>
|
||
|
#endif
|
||
|
|
||
|
#ifndef MBEDTLS_PRIVATE
|
||
|
#define MBEDTLS_PRIVATE(x) x
|
||
|
#endif
|
||
|
|
||
|
#if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
|
||
|
#define mbedtls_ssl_get_ciphersuite_id_from_ssl(ssl) \
|
||
|
((ssl)->MBEDTLS_PRIVATE(session) \
|
||
|
?(ssl)->MBEDTLS_PRIVATE(session)->MBEDTLS_PRIVATE(ciphersuite) \
|
||
|
: 0)
|
||
|
#define mbedtls_ssl_ciphersuite_get_name(info) \
|
||
|
(info)->MBEDTLS_PRIVATE(name)
|
||
|
#endif
|
||
|
|
||
|
#include "crypto.h" /* sha256_vector() */
|
||
|
#include "tls.h"
|
||
|
|
||
|
#ifndef SHA256_DIGEST_LENGTH
|
||
|
#define SHA256_DIGEST_LENGTH 32
|
||
|
#endif
|
||
|
|
||
|
#ifndef MBEDTLS_EXPKEY_FIXED_SECRET_LEN
|
||
|
#define MBEDTLS_EXPKEY_FIXED_SECRET_LEN 48
|
||
|
#endif
|
||
|
|
||
|
#ifndef MBEDTLS_EXPKEY_RAND_LEN
|
||
|
#define MBEDTLS_EXPKEY_RAND_LEN 32
|
||
|
#endif
|
||
|
|
||
|
#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
|
||
|
static mbedtls_ssl_export_keys_t tls_connection_export_keys_cb;
|
||
|
#elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
|
||
|
static mbedtls_ssl_export_keys_ext_t tls_connection_export_keys_cb;
|
||
|
#else /*(not implemented; return error)*/
|
||
|
#define mbedtls_ssl_tls_prf(a,b,c,d,e,f,g,h) (-1)
|
||
|
typedef mbedtls_tls_prf_types int;
|
||
|
#endif
|
||
|
|
||
|
|
||
|
/* hostapd/wpa_supplicant provides forced_memzero(),
|
||
|
* but prefer mbedtls_platform_zeroize() */
|
||
|
#define forced_memzero(ptr,sz) mbedtls_platform_zeroize(ptr,sz)
|
||
|
|
||
|
|
||
|
#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) \
|
||
|
|| defined(EAP_TEAP) || defined(EAP_SERVER_TEAP)
|
||
|
#ifdef MBEDTLS_SSL_SESSION_TICKETS
|
||
|
#ifdef MBEDTLS_SSL_TICKET_C
|
||
|
#define TLS_MBEDTLS_SESSION_TICKETS
|
||
|
#if defined(EAP_TEAP) || defined(EAP_SERVER_TEAP)
|
||
|
#define TLS_MBEDTLS_EAP_TEAP
|
||
|
#endif
|
||
|
#if !defined(CONFIG_FIPS) /* EAP-FAST keys cannot be exported in FIPS mode */
|
||
|
#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
|
||
|
#define TLS_MBEDTLS_EAP_FAST
|
||
|
#endif
|
||
|
#endif
|
||
|
#endif
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
|
||
|
struct tls_conf {
|
||
|
mbedtls_ssl_config conf;
|
||
|
|
||
|
unsigned int verify_peer:1;
|
||
|
unsigned int verify_depth0_only:1;
|
||
|
unsigned int check_crl:2; /*(needs :2 bits for 0, 1, 2)*/
|
||
|
unsigned int check_crl_strict:1; /*(needs :1 bit for 0, 1)*/
|
||
|
unsigned int ca_cert_probe:1;
|
||
|
unsigned int has_ca_cert:1;
|
||
|
unsigned int has_client_cert:1;
|
||
|
unsigned int has_private_key:1;
|
||
|
unsigned int suiteb128:1;
|
||
|
unsigned int suiteb192:1;
|
||
|
mbedtls_x509_crl *crl;
|
||
|
mbedtls_x509_crt ca_cert;
|
||
|
mbedtls_x509_crt client_cert;
|
||
|
mbedtls_pk_context private_key;
|
||
|
|
||
|
uint32_t refcnt;
|
||
|
|
||
|
unsigned int flags;
|
||
|
char *subject_match;
|
||
|
char *altsubject_match;
|
||
|
char *suffix_match;
|
||
|
char *domain_match;
|
||
|
char *check_cert_subject;
|
||
|
u8 ca_cert_hash[SHA256_DIGEST_LENGTH];
|
||
|
|
||
|
int *ciphersuites; /* list of ciphersuite ids for mbedtls_ssl_config */
|
||
|
#if MBEDTLS_VERSION_NUMBER < 0x03010000 /* mbedtls 3.1.0 */
|
||
|
mbedtls_ecp_group_id *curves;
|
||
|
#else
|
||
|
uint16_t *curves; /* list of curve ids for mbedtls_ssl_config */
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
|
||
|
struct tls_global {
|
||
|
struct tls_conf *tls_conf;
|
||
|
char *ocsp_stapling_response;
|
||
|
mbedtls_ctr_drbg_context *ctr_drbg; /*(see crypto_mbedtls.c)*/
|
||
|
#ifdef MBEDTLS_SSL_SESSION_TICKETS
|
||
|
mbedtls_ssl_ticket_context ticket_ctx;
|
||
|
#endif
|
||
|
char *ca_cert_file;
|
||
|
struct os_reltime crl_reload_previous;
|
||
|
unsigned int crl_reload_interval;
|
||
|
uint32_t refcnt;
|
||
|
struct tls_config init_conf;
|
||
|
};
|
||
|
|
||
|
static struct tls_global tls_ctx_global;
|
||
|
|
||
|
|
||
|
struct tls_connection {
|
||
|
struct tls_conf *tls_conf;
|
||
|
struct wpabuf *push_buf;
|
||
|
struct wpabuf *pull_buf;
|
||
|
size_t pull_buf_offset;
|
||
|
|
||
|
unsigned int established:1;
|
||
|
unsigned int resumed:1;
|
||
|
unsigned int verify_peer:1;
|
||
|
unsigned int is_server:1;
|
||
|
|
||
|
mbedtls_ssl_context ssl;
|
||
|
|
||
|
mbedtls_tls_prf_types tls_prf_type;
|
||
|
size_t expkey_keyblock_size;
|
||
|
size_t expkey_secret_len;
|
||
|
#if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
|
||
|
unsigned char expkey_secret[MBEDTLS_EXPKEY_FIXED_SECRET_LEN];
|
||
|
#else
|
||
|
unsigned char expkey_secret[MBEDTLS_MD_MAX_SIZE];
|
||
|
#endif
|
||
|
unsigned char expkey_randbytes[MBEDTLS_EXPKEY_RAND_LEN*2];
|
||
|
|
||
|
int read_alerts, write_alerts, failed;
|
||
|
|
||
|
#ifdef TLS_MBEDTLS_SESSION_TICKETS
|
||
|
tls_session_ticket_cb session_ticket_cb;
|
||
|
void *session_ticket_cb_ctx;
|
||
|
unsigned char *clienthello_session_ticket;
|
||
|
size_t clienthello_session_ticket_len;
|
||
|
#endif
|
||
|
char *peer_subject; /* peer subject info for authenticated peer */
|
||
|
struct wpabuf *success_data;
|
||
|
};
|
||
|
|
||
|
|
||
|
#ifndef __has_attribute
|
||
|
#define __has_attribute(x) 0
|
||
|
#endif
|
||
|
|
||
|
#ifndef __GNUC_PREREQ
|
||
|
#define __GNUC_PREREQ(maj,min) 0
|
||
|
#endif
|
||
|
|
||
|
#ifndef __attribute_cold__
|
||
|
#if __has_attribute(cold) \
|
||
|
|| __GNUC_PREREQ(4,3)
|
||
|
#define __attribute_cold__ __attribute__((__cold__))
|
||
|
#else
|
||
|
#define __attribute_cold__
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
#ifndef __attribute_noinline__
|
||
|
#if __has_attribute(noinline) \
|
||
|
|| __GNUC_PREREQ(3,1)
|
||
|
#define __attribute_noinline__ __attribute__((__noinline__))
|
||
|
#else
|
||
|
#define __attribute_noinline__
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
|
||
|
__attribute_cold__
|
||
|
__attribute_noinline__
|
||
|
static void emsg(int level, const char * const msg)
|
||
|
{
|
||
|
wpa_printf(level, "MTLS: %s", msg);
|
||
|
}
|
||
|
|
||
|
|
||
|
__attribute_cold__
|
||
|
__attribute_noinline__
|
||
|
static void emsgrc(int level, const char * const msg, int rc)
|
||
|
{
|
||
|
#ifdef MBEDTLS_ERROR_C
|
||
|
/* error logging convenience function that decodes mbedtls result codes */
|
||
|
char buf[256];
|
||
|
mbedtls_strerror(rc, buf, sizeof(buf));
|
||
|
wpa_printf(level, "MTLS: %s: %s (-0x%04x)", msg, buf, -rc);
|
||
|
#else
|
||
|
wpa_printf(level, "MTLS: %s: (-0x%04x)", msg, -rc);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
#define elog(rc, msg) emsgrc(MSG_ERROR, (msg), (rc))
|
||
|
#define ilog(rc, msg) emsgrc(MSG_INFO, (msg), (rc))
|
||
|
|
||
|
|
||
|
struct tls_conf * tls_conf_init(void *tls_ctx)
|
||
|
{
|
||
|
struct tls_conf *tls_conf = os_zalloc(sizeof(*tls_conf));
|
||
|
if (tls_conf == NULL)
|
||
|
return NULL;
|
||
|
tls_conf->refcnt = 1;
|
||
|
|
||
|
mbedtls_ssl_config_init(&tls_conf->conf);
|
||
|
mbedtls_ssl_conf_rng(&tls_conf->conf,
|
||
|
mbedtls_ctr_drbg_random, tls_ctx_global.ctr_drbg);
|
||
|
mbedtls_x509_crt_init(&tls_conf->ca_cert);
|
||
|
mbedtls_x509_crt_init(&tls_conf->client_cert);
|
||
|
mbedtls_pk_init(&tls_conf->private_key);
|
||
|
|
||
|
return tls_conf;
|
||
|
}
|
||
|
|
||
|
|
||
|
void tls_conf_deinit(struct tls_conf *tls_conf)
|
||
|
{
|
||
|
if (tls_conf == NULL || --tls_conf->refcnt != 0)
|
||
|
return;
|
||
|
|
||
|
mbedtls_x509_crt_free(&tls_conf->ca_cert);
|
||
|
mbedtls_x509_crt_free(&tls_conf->client_cert);
|
||
|
if (tls_conf->crl) {
|
||
|
mbedtls_x509_crl_free(tls_conf->crl);
|
||
|
os_free(tls_conf->crl);
|
||
|
}
|
||
|
mbedtls_pk_free(&tls_conf->private_key);
|
||
|
mbedtls_ssl_config_free(&tls_conf->conf);
|
||
|
os_free(tls_conf->curves);
|
||
|
os_free(tls_conf->ciphersuites);
|
||
|
os_free(tls_conf->subject_match);
|
||
|
os_free(tls_conf->altsubject_match);
|
||
|
os_free(tls_conf->suffix_match);
|
||
|
os_free(tls_conf->domain_match);
|
||
|
os_free(tls_conf->check_cert_subject);
|
||
|
os_free(tls_conf);
|
||
|
}
|
||
|
|
||
|
|
||
|
mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void); /*(not in header)*/
|
||
|
|
||
|
__attribute_cold__
|
||
|
void * tls_init(const struct tls_config *conf)
|
||
|
{
|
||
|
/* RFE: review struct tls_config *conf (different from tls_conf) */
|
||
|
|
||
|
if (++tls_ctx_global.refcnt > 1)
|
||
|
return &tls_ctx_global;
|
||
|
|
||
|
tls_ctx_global.ctr_drbg = crypto_mbedtls_ctr_drbg();
|
||
|
#ifdef MBEDTLS_SSL_SESSION_TICKETS
|
||
|
mbedtls_ssl_ticket_init(&tls_ctx_global.ticket_ctx);
|
||
|
mbedtls_ssl_ticket_setup(&tls_ctx_global.ticket_ctx,
|
||
|
mbedtls_ctr_drbg_random,
|
||
|
tls_ctx_global.ctr_drbg,
|
||
|
MBEDTLS_CIPHER_AES_256_GCM,
|
||
|
43200); /* ticket timeout: 12 hours */
|
||
|
#endif
|
||
|
/* copy struct for future use */
|
||
|
tls_ctx_global.init_conf = *conf;
|
||
|
if (conf->openssl_ciphers)
|
||
|
tls_ctx_global.init_conf.openssl_ciphers =
|
||
|
os_strdup(conf->openssl_ciphers);
|
||
|
|
||
|
tls_ctx_global.crl_reload_interval = conf->crl_reload_interval;
|
||
|
os_get_reltime(&tls_ctx_global.crl_reload_previous);
|
||
|
|
||
|
return &tls_ctx_global;
|
||
|
}
|
||
|
|
||
|
|
||
|
__attribute_cold__
|
||
|
void tls_deinit(void *tls_ctx)
|
||
|
{
|
||
|
if (tls_ctx == NULL || --tls_ctx_global.refcnt != 0)
|
||
|
return;
|
||
|
|
||
|
tls_conf_deinit(tls_ctx_global.tls_conf);
|
||
|
os_free(tls_ctx_global.ca_cert_file);
|
||
|
os_free(tls_ctx_global.ocsp_stapling_response);
|
||
|
char *openssl_ciphers; /*(allocated in tls_init())*/
|
||
|
*(const char **)&openssl_ciphers =
|
||
|
tls_ctx_global.init_conf.openssl_ciphers;
|
||
|
os_free(openssl_ciphers);
|
||
|
#ifdef MBEDTLS_SSL_SESSION_TICKETS
|
||
|
mbedtls_ssl_ticket_free(&tls_ctx_global.ticket_ctx);
|
||
|
#endif
|
||
|
os_memset(&tls_ctx_global, 0, sizeof(tls_ctx_global));
|
||
|
}
|
||
|
|
||
|
|
||
|
int tls_get_errors(void *tls_ctx)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void tls_connection_deinit_expkey(struct tls_connection *conn)
|
||
|
{
|
||
|
conn->tls_prf_type = 0; /* MBEDTLS_SSL_TLS_PRF_NONE; */
|
||
|
conn->expkey_keyblock_size = 0;
|
||
|
conn->expkey_secret_len = 0;
|
||
|
forced_memzero(conn->expkey_secret, sizeof(conn->expkey_secret));
|
||
|
forced_memzero(conn->expkey_randbytes, sizeof(conn->expkey_randbytes));
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef TLS_MBEDTLS_SESSION_TICKETS
|
||
|
void tls_connection_deinit_clienthello_session_ticket(struct tls_connection *conn)
|
||
|
{
|
||
|
if (conn->clienthello_session_ticket) {
|
||
|
mbedtls_platform_zeroize(conn->clienthello_session_ticket,
|
||
|
conn->clienthello_session_ticket_len);
|
||
|
mbedtls_free(conn->clienthello_session_ticket);
|
||
|
conn->clienthello_session_ticket = NULL;
|
||
|
conn->clienthello_session_ticket_len = 0;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
|
||
|
{
|
||
|
if (conn == NULL)
|
||
|
return;
|
||
|
|
||
|
#if 0 /*(good intention, but never sent since we destroy self below)*/
|
||
|
if (conn->established)
|
||
|
mbedtls_ssl_close_notify(&conn->ssl);
|
||
|
#endif
|
||
|
|
||
|
if (conn->tls_prf_type)
|
||
|
tls_connection_deinit_expkey(conn);
|
||
|
|
||
|
#ifdef TLS_MBEDTLS_SESSION_TICKETS
|
||
|
if (conn->clienthello_session_ticket)
|
||
|
tls_connection_deinit_clienthello_session_ticket(conn);
|
||
|
#endif
|
||
|
|
||
|
os_free(conn->peer_subject);
|
||
|
wpabuf_free(conn->success_data);
|
||
|
wpabuf_free(conn->push_buf);
|
||
|
wpabuf_free(conn->pull_buf);
|
||
|
mbedtls_ssl_free(&conn->ssl);
|
||
|
tls_conf_deinit(conn->tls_conf);
|
||
|
os_free(conn);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void tls_mbedtls_refresh_crl(void);
|
||
|
static int tls_mbedtls_ssl_setup(struct tls_connection *conn);
|
||
|
|
||
|
struct tls_connection * tls_connection_init(void *tls_ctx)
|
||
|
{
|
||
|
struct tls_connection *conn = os_zalloc(sizeof(*conn));
|
||
|
if (conn == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
mbedtls_ssl_init(&conn->ssl);
|
||
|
|
||
|
conn->tls_conf = tls_ctx_global.tls_conf; /*(inherit global conf, if set)*/
|
||
|
if (conn->tls_conf) {
|
||
|
++conn->tls_conf->refcnt;
|
||
|
/* check for CRL refresh if inheriting from global config */
|
||
|
tls_mbedtls_refresh_crl();
|
||
|
|
||
|
conn->verify_peer = conn->tls_conf->verify_peer;
|
||
|
if (tls_mbedtls_ssl_setup(conn) != 0) {
|
||
|
tls_connection_deinit(&tls_ctx_global, conn);
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return conn;
|
||
|
}
|
||
|
|
||
|
|
||
|
int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
|
||
|
{
|
||
|
return conn ? conn->established : 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
__attribute_noinline__
|
||
|
char * tls_mbedtls_peer_serial_num(const mbedtls_x509_crt *crt, char *serial_num, size_t len)
|
||
|
{
|
||
|
/* mbedtls_x509_serial_gets() inefficiently formats to hex separated by
|
||
|
* colons, so generate the hex serial number here. The func
|
||
|
* wpa_snprintf_hex_uppercase() is similarly inefficient. */
|
||
|
size_t i = 0; /* skip leading 0's per Distinguished Encoding Rules (DER) */
|
||
|
while (i < crt->serial.len && crt->serial.p[i] == 0) ++i;
|
||
|
if (i == crt->serial.len) --i;
|
||
|
|
||
|
const unsigned char *s = crt->serial.p + i;
|
||
|
const size_t e = (crt->serial.len - i) * 2;
|
||
|
if (e >= len)
|
||
|
return NULL;
|
||
|
#if 0
|
||
|
wpa_snprintf_hex_uppercase(serial_num, len, s, crt->serial.len-i);
|
||
|
#else
|
||
|
for (i = 0; i < e; i+=2, ++s) {
|
||
|
serial_num[i+0] = "0123456789ABCDEF"[(*s >> 4)];
|
||
|
serial_num[i+1] = "0123456789ABCDEF"[(*s & 0xF)];
|
||
|
}
|
||
|
serial_num[e] = '\0';
|
||
|
#endif
|
||
|
return serial_num;
|
||
|
}
|
||
|
|
||
|
|
||
|
char * tls_connection_peer_serial_num(void *tls_ctx,
|
||
|
struct tls_connection *conn)
|
||
|
{
|
||
|
const mbedtls_x509_crt *crt = mbedtls_ssl_get_peer_cert(&conn->ssl);
|
||
|
if (crt == NULL)
|
||
|
return NULL;
|
||
|
size_t len = crt->serial.len * 2 + 1;
|
||
|
char *serial_num = os_malloc(len);
|
||
|
if (!serial_num)
|
||
|
return NULL;
|
||
|
return tls_mbedtls_peer_serial_num(crt, serial_num, len);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void tls_pull_buf_reset(struct tls_connection *conn);
|
||
|
|
||
|
int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
|
||
|
{
|
||
|
/* Note: this function called from eap_peer_tls_reauth_init()
|
||
|
* for session resumption, not for connection shutdown */
|
||
|
|
||
|
if (conn == NULL)
|
||
|
return -1;
|
||
|
|
||
|
tls_pull_buf_reset(conn);
|
||
|
wpabuf_free(conn->push_buf);
|
||
|
conn->push_buf = NULL;
|
||
|
conn->established = 0;
|
||
|
conn->resumed = 0;
|
||
|
if (conn->tls_prf_type)
|
||
|
tls_connection_deinit_expkey(conn);
|
||
|
|
||
|
/* RFE: prepare for session resumption? (see doc in crypto/tls.h) */
|
||
|
|
||
|
return mbedtls_ssl_session_reset(&conn->ssl);
|
||
|
}
|
||
|
|
||
|
|
||
|
static int tls_wpabuf_resize_put_data(struct wpabuf **buf,
|
||
|
const unsigned char *data, size_t dlen)
|
||
|
{
|
||
|
if (wpabuf_resize(buf, dlen) < 0)
|
||
|
return 0;
|
||
|
wpabuf_put_data(*buf, data, dlen);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int tls_pull_buf_append(struct tls_connection *conn,
|
||
|
const struct wpabuf *in_data)
|
||
|
{
|
||
|
/*(interface does not lend itself to move semantics)*/
|
||
|
return tls_wpabuf_resize_put_data(&conn->pull_buf,
|
||
|
wpabuf_head(in_data),
|
||
|
wpabuf_len(in_data));
|
||
|
}
|
||
|
|
||
|
|
||
|
static void tls_pull_buf_reset(struct tls_connection *conn)
|
||
|
{
|
||
|
/*(future: might consider reusing conn->pull_buf)*/
|
||
|
wpabuf_free(conn->pull_buf);
|
||
|
conn->pull_buf = NULL;
|
||
|
conn->pull_buf_offset = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
__attribute_cold__
|
||
|
static void tls_pull_buf_discard(struct tls_connection *conn, const char *func)
|
||
|
{
|
||
|
size_t discard = wpabuf_len(conn->pull_buf) - conn->pull_buf_offset;
|
||
|
if (discard)
|
||
|
wpa_printf(MSG_DEBUG,
|
||
|
"%s - %zu bytes remaining in pull_buf; discarding",
|
||
|
func, discard);
|
||
|
tls_pull_buf_reset(conn);
|
||
|
}
|
||
|
|
||
|
|
||
|
static int tls_pull_func(void *ptr, unsigned char *buf, size_t len)
|
||
|
{
|
||
|
struct tls_connection *conn = (struct tls_connection *) ptr;
|
||
|
if (conn->pull_buf == NULL)
|
||
|
return MBEDTLS_ERR_SSL_WANT_READ;
|
||
|
const size_t dlen = wpabuf_len(conn->pull_buf) - conn->pull_buf_offset;
|
||
|
if (dlen == 0)
|
||
|
return MBEDTLS_ERR_SSL_WANT_READ;
|
||
|
|
||
|
if (len > dlen)
|
||
|
len = dlen;
|
||
|
os_memcpy(buf, wpabuf_head(conn->pull_buf)+conn->pull_buf_offset, len);
|
||
|
|
||
|
if (len == dlen) {
|
||
|
tls_pull_buf_reset(conn);
|
||
|
/*wpa_printf(MSG_DEBUG, "%s - emptied pull_buf", __func__);*/
|
||
|
}
|
||
|
else {
|
||
|
conn->pull_buf_offset += len;
|
||
|
/*wpa_printf(MSG_DEBUG, "%s - %zu bytes remaining in pull_buf",
|
||
|
__func__, dlen - len);*/
|
||
|
}
|
||
|
return (int)len;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int tls_push_func(void *ptr, const unsigned char *buf, size_t len)
|
||
|
{
|
||
|
struct tls_connection *conn = (struct tls_connection *) ptr;
|
||
|
return tls_wpabuf_resize_put_data(&conn->push_buf, buf, len)
|
||
|
? (int)len
|
||
|
: MBEDTLS_ERR_SSL_ALLOC_FAILED;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
tls_mbedtls_verify_cb (void *arg, mbedtls_x509_crt *crt, int depth, uint32_t *flags);
|
||
|
|
||
|
|
||
|
static int tls_mbedtls_ssl_setup(struct tls_connection *conn)
|
||
|
{
|
||
|
#if 0
|
||
|
/* mbedtls_ssl_setup() must be called only once */
|
||
|
/* If this func might be called multiple times (e.g. via set_params),
|
||
|
* then we should set a flag in conn that ssl was initialized */
|
||
|
if (conn->ssl_is_init) {
|
||
|
mbedtls_ssl_free(&conn->ssl);
|
||
|
mbedtls_ssl_init(&conn->ssl);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
int ret = mbedtls_ssl_setup(&conn->ssl, &conn->tls_conf->conf);
|
||
|
if (ret != 0) {
|
||
|
elog(ret, "mbedtls_ssl_setup");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
mbedtls_ssl_set_bio(&conn->ssl, conn, tls_push_func, tls_pull_func, NULL);
|
||
|
#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
|
||
|
mbedtls_ssl_set_export_keys_cb(
|
||
|
&conn->ssl, tls_connection_export_keys_cb, conn);
|
||
|
#elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
|
||
|
mbedtls_ssl_conf_export_keys_ext_cb(
|
||
|
&conn->tls_conf->conf, tls_connection_export_keys_cb, conn);
|
||
|
#endif
|
||
|
if (conn->verify_peer)
|
||
|
mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int tls_mbedtls_data_is_pem(const u8 *data)
|
||
|
{
|
||
|
return (NULL != os_strstr((char *)data, "-----"));
|
||
|
}
|
||
|
|
||
|
|
||
|
static void tls_mbedtls_set_allowed_tls_vers(struct tls_conf *tls_conf,
|
||
|
mbedtls_ssl_config *conf)
|
||
|
{
|
||
|
#if !defined(MBEDTLS_SSL_PROTO_TLS1_3)
|
||
|
tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_3;
|
||
|
#endif
|
||
|
|
||
|
/* unconditionally require TLSv1.2+ for TLS_CONN_SUITEB */
|
||
|
if (tls_conf->flags & TLS_CONN_SUITEB) {
|
||
|
tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_0;
|
||
|
tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_1;
|
||
|
}
|
||
|
|
||
|
const unsigned int flags = tls_conf->flags;
|
||
|
|
||
|
/* attempt to map flags to min and max TLS protocol version */
|
||
|
|
||
|
int min = (flags & TLS_CONN_DISABLE_TLSv1_0)
|
||
|
? (flags & TLS_CONN_DISABLE_TLSv1_1)
|
||
|
? (flags & TLS_CONN_DISABLE_TLSv1_2)
|
||
|
? (flags & TLS_CONN_DISABLE_TLSv1_3)
|
||
|
? 4
|
||
|
: 3
|
||
|
: 2
|
||
|
: 1
|
||
|
: 0;
|
||
|
|
||
|
int max = (flags & TLS_CONN_DISABLE_TLSv1_3)
|
||
|
? (flags & TLS_CONN_DISABLE_TLSv1_2)
|
||
|
? (flags & TLS_CONN_DISABLE_TLSv1_1)
|
||
|
? (flags & TLS_CONN_DISABLE_TLSv1_0)
|
||
|
? -1
|
||
|
: 0
|
||
|
: 1
|
||
|
: 2
|
||
|
: 3;
|
||
|
|
||
|
if ((flags & TLS_CONN_ENABLE_TLSv1_2) && min > 2) min = 2;
|
||
|
if ((flags & TLS_CONN_ENABLE_TLSv1_1) && min > 1) min = 1;
|
||
|
if ((flags & TLS_CONN_ENABLE_TLSv1_0) && min > 0) min = 0;
|
||
|
if (max < min) {
|
||
|
emsg(MSG_ERROR, "invalid tls_disable_tlsv* params; ignoring");
|
||
|
return;
|
||
|
}
|
||
|
#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
|
||
|
/* mbed TLS 3.0.0 removes support for protocols < TLSv1.2 */
|
||
|
if (min < 2 || max < 2) {
|
||
|
emsg(MSG_ERROR, "invalid tls_disable_tlsv* params; ignoring");
|
||
|
if (min < 2) min = 2;
|
||
|
if (max < 2) max = 2;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
|
||
|
/* MBEDTLS_SSL_VERSION_TLS1_2 = 0x0303 *//*!< (D)TLS 1.2 */
|
||
|
/* MBEDTLS_SSL_VERSION_TLS1_3 = 0x0304 *//*!< (D)TLS 1.3 */
|
||
|
min = (min == 2) ? MBEDTLS_SSL_VERSION_TLS1_2 : MBEDTLS_SSL_VERSION_TLS1_3;
|
||
|
max = (max == 2) ? MBEDTLS_SSL_VERSION_TLS1_2 : MBEDTLS_SSL_VERSION_TLS1_3;
|
||
|
mbedtls_ssl_conf_min_tls_version(conf, min);
|
||
|
mbedtls_ssl_conf_max_tls_version(conf, max);
|
||
|
#else
|
||
|
#ifndef MBEDTLS_SSL_MINOR_VERSION_4
|
||
|
if (min == 3) min = 2;
|
||
|
if (max == 3) max = 2;
|
||
|
#endif
|
||
|
/* MBEDTLS_SSL_MINOR_VERSION_0 0 *//*!< SSL v3.0 */
|
||
|
/* MBEDTLS_SSL_MINOR_VERSION_1 1 *//*!< TLS v1.0 */
|
||
|
/* MBEDTLS_SSL_MINOR_VERSION_2 2 *//*!< TLS v1.1 */
|
||
|
/* MBEDTLS_SSL_MINOR_VERSION_3 3 *//*!< TLS v1.2 */
|
||
|
/* MBEDTLS_SSL_MINOR_VERSION_4 4 *//*!< TLS v1.3 */
|
||
|
mbedtls_ssl_conf_min_version(conf, MBEDTLS_SSL_MAJOR_VERSION_3, min+1);
|
||
|
mbedtls_ssl_conf_max_version(conf, MBEDTLS_SSL_MAJOR_VERSION_3, max+1);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
__attribute_noinline__
|
||
|
static int tls_mbedtls_readfile(const char *path, u8 **buf, size_t *n);
|
||
|
|
||
|
|
||
|
static int
|
||
|
tls_mbedtls_set_dhparams(struct tls_conf *tls_conf, const char *dh_file)
|
||
|
{
|
||
|
size_t len;
|
||
|
u8 *data;
|
||
|
if (tls_mbedtls_readfile(dh_file, &data, &len))
|
||
|
return 0;
|
||
|
|
||
|
/* parse only if DH parameters if in PEM format */
|
||
|
if (tls_mbedtls_data_is_pem(data)
|
||
|
&& NULL == os_strstr((char *)data, "-----BEGIN DH PARAMETERS-----")) {
|
||
|
if (os_strstr((char *)data, "-----BEGIN DSA PARAMETERS-----"))
|
||
|
wpa_printf(MSG_WARNING, "DSA parameters not handled (%s)", dh_file);
|
||
|
else
|
||
|
wpa_printf(MSG_WARNING, "unexpected DH param content (%s)",dh_file);
|
||
|
forced_memzero(data, len);
|
||
|
os_free(data);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* mbedtls_dhm_parse_dhm() expects "-----BEGIN DH PARAMETERS-----" if PEM */
|
||
|
mbedtls_dhm_context dhm;
|
||
|
mbedtls_dhm_init(&dhm);
|
||
|
int rc = mbedtls_dhm_parse_dhm(&dhm, data, len);
|
||
|
if (0 == rc)
|
||
|
rc = mbedtls_ssl_conf_dh_param_ctx(&tls_conf->conf, &dhm);
|
||
|
if (0 != rc)
|
||
|
elog(rc, dh_file);
|
||
|
mbedtls_dhm_free(&dhm);
|
||
|
|
||
|
forced_memzero(data, len);
|
||
|
os_free(data);
|
||
|
return (0 == rc);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* reference: lighttpd src/mod_mbedtls.c:mod_mbedtls_ssl_append_curve()
|
||
|
* (same author: gstrauss@gluelogic.com; same license: BSD-3-Clause) */
|
||
|
#if MBEDTLS_VERSION_NUMBER < 0x03010000 /* mbedtls 3.1.0 */
|
||
|
static int
|
||
|
tls_mbedtls_append_curve (mbedtls_ecp_group_id *ids, int nids, int idsz, const mbedtls_ecp_group_id id)
|
||
|
{
|
||
|
if (1 >= idsz - (nids + 1)) {
|
||
|
emsg(MSG_ERROR, "error: too many curves during list expand");
|
||
|
return -1;
|
||
|
}
|
||
|
ids[++nids] = id;
|
||
|
return nids;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
tls_mbedtls_set_curves(struct tls_conf *tls_conf, const char *curvelist)
|
||
|
{
|
||
|
mbedtls_ecp_group_id ids[512];
|
||
|
int nids = -1;
|
||
|
const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
|
||
|
const mbedtls_ecp_curve_info * const curve_info = mbedtls_ecp_curve_list();
|
||
|
|
||
|
for (const char *e = curvelist-1; e; ) {
|
||
|
const char * const n = e+1;
|
||
|
e = os_strchr(n, ':');
|
||
|
size_t len = e ? (size_t)(e - n) : os_strlen(n);
|
||
|
mbedtls_ecp_group_id grp_id = MBEDTLS_ECP_DP_NONE;
|
||
|
switch (len) {
|
||
|
case 5:
|
||
|
if (0 == os_memcmp("P-521", n, 5))
|
||
|
grp_id = MBEDTLS_ECP_DP_SECP521R1;
|
||
|
else if (0 == os_memcmp("P-384", n, 5))
|
||
|
grp_id = MBEDTLS_ECP_DP_SECP384R1;
|
||
|
else if (0 == os_memcmp("P-256", n, 5))
|
||
|
grp_id = MBEDTLS_ECP_DP_SECP256R1;
|
||
|
break;
|
||
|
case 6:
|
||
|
if (0 == os_memcmp("BP-521", n, 6))
|
||
|
grp_id = MBEDTLS_ECP_DP_BP512R1;
|
||
|
else if (0 == os_memcmp("BP-384", n, 6))
|
||
|
grp_id = MBEDTLS_ECP_DP_BP384R1;
|
||
|
else if (0 == os_memcmp("BP-256", n, 6))
|
||
|
grp_id = MBEDTLS_ECP_DP_BP256R1;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
if (grp_id != MBEDTLS_ECP_DP_NONE) {
|
||
|
nids = tls_mbedtls_append_curve(ids, nids, idsz, grp_id);
|
||
|
if (-1 == nids) return 0;
|
||
|
continue;
|
||
|
}
|
||
|
/* similar to mbedtls_ecp_curve_info_from_name() */
|
||
|
const mbedtls_ecp_curve_info *info;
|
||
|
for (info = curve_info; info->grp_id != MBEDTLS_ECP_DP_NONE; ++info) {
|
||
|
if (0 == os_strncmp(info->name, n, len) && info->name[len] == '\0')
|
||
|
break;
|
||
|
}
|
||
|
if (info->grp_id == MBEDTLS_ECP_DP_NONE) {
|
||
|
wpa_printf(MSG_ERROR, "MTLS: unrecognized curve: %.*s",(int)len,n);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
nids = tls_mbedtls_append_curve(ids, nids, idsz, info->grp_id);
|
||
|
if (-1 == nids) return 0;
|
||
|
}
|
||
|
|
||
|
/* mod_openssl configures "prime256v1" if curve list not specified,
|
||
|
* but mbedtls provides a list of supported curves if not explicitly set */
|
||
|
if (-1 == nids) return 1; /* empty list; no-op */
|
||
|
|
||
|
ids[++nids] = MBEDTLS_ECP_DP_NONE; /* terminate list */
|
||
|
++nids;
|
||
|
|
||
|
/* curves list must be persistent for lifetime of mbedtls_ssl_config */
|
||
|
tls_conf->curves = os_malloc(nids * sizeof(mbedtls_ecp_group_id));
|
||
|
if (tls_conf->curves == NULL)
|
||
|
return 0;
|
||
|
os_memcpy(tls_conf->curves, ids, nids * sizeof(mbedtls_ecp_group_id));
|
||
|
|
||
|
mbedtls_ssl_conf_curves(&tls_conf->conf, tls_conf->curves);
|
||
|
return 1;
|
||
|
}
|
||
|
#else
|
||
|
static int
|
||
|
tls_mbedtls_append_curve (uint16_t *ids, int nids, int idsz, const uint16_t id)
|
||
|
{
|
||
|
if (1 >= idsz - (nids + 1)) {
|
||
|
emsg(MSG_ERROR, "error: too many curves during list expand");
|
||
|
return -1;
|
||
|
}
|
||
|
ids[++nids] = id;
|
||
|
return nids;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
tls_mbedtls_set_curves(struct tls_conf *tls_conf, const char *curvelist)
|
||
|
{
|
||
|
/* TLS Supported Groups (renamed from "EC Named Curve Registry")
|
||
|
* https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
|
||
|
*/
|
||
|
uint16_t ids[512];
|
||
|
int nids = -1;
|
||
|
const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
|
||
|
const mbedtls_ecp_curve_info * const curve_info = mbedtls_ecp_curve_list();
|
||
|
|
||
|
for (const char *e = curvelist-1; e; ) {
|
||
|
const char * const n = e+1;
|
||
|
e = os_strchr(n, ':');
|
||
|
size_t len = e ? (size_t)(e - n) : os_strlen(n);
|
||
|
uint16_t tls_id = 0;
|
||
|
switch (len) {
|
||
|
case 5:
|
||
|
if (0 == os_memcmp("P-521", n, 5))
|
||
|
tls_id = 25; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP521R1 */
|
||
|
else if (0 == os_memcmp("P-384", n, 5))
|
||
|
tls_id = 24; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP384R1 */
|
||
|
else if (0 == os_memcmp("P-256", n, 5))
|
||
|
tls_id = 23; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP256R1 */
|
||
|
break;
|
||
|
case 6:
|
||
|
if (0 == os_memcmp("BP-521", n, 6))
|
||
|
tls_id = 28; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP512R1 */
|
||
|
else if (0 == os_memcmp("BP-384", n, 6))
|
||
|
tls_id = 27; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP384R1 */
|
||
|
else if (0 == os_memcmp("BP-256", n, 6))
|
||
|
tls_id = 26; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP256R1 */
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
if (tls_id != 0) {
|
||
|
nids = tls_mbedtls_append_curve(ids, nids, idsz, tls_id);
|
||
|
if (-1 == nids) return 0;
|
||
|
continue;
|
||
|
}
|
||
|
/* similar to mbedtls_ecp_curve_info_from_name() */
|
||
|
const mbedtls_ecp_curve_info *info;
|
||
|
for (info = curve_info; info->tls_id != 0; ++info) {
|
||
|
if (0 == os_strncmp(info->name, n, len) && info->name[len] == '\0')
|
||
|
break;
|
||
|
}
|
||
|
if (info->tls_id == 0) {
|
||
|
wpa_printf(MSG_ERROR, "MTLS: unrecognized curve: %.*s",(int)len,n);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
nids = tls_mbedtls_append_curve(ids, nids, idsz, info->tls_id);
|
||
|
if (-1 == nids) return 0;
|
||
|
}
|
||
|
|
||
|
/* mod_openssl configures "prime256v1" if curve list not specified,
|
||
|
* but mbedtls provides a list of supported curves if not explicitly set */
|
||
|
if (-1 == nids) return 1; /* empty list; no-op */
|
||
|
|
||
|
ids[++nids] = 0; /* terminate list */
|
||
|
++nids;
|
||
|
|
||
|
/* curves list must be persistent for lifetime of mbedtls_ssl_config */
|
||
|
tls_conf->curves = os_malloc(nids * sizeof(uint16_t));
|
||
|
if (tls_conf->curves == NULL)
|
||
|
return 0;
|
||
|
os_memcpy(tls_conf->curves, ids, nids * sizeof(uint16_t));
|
||
|
|
||
|
mbedtls_ssl_conf_groups(&tls_conf->conf, tls_conf->curves);
|
||
|
return 1;
|
||
|
}
|
||
|
#endif /* MBEDTLS_VERSION_NUMBER >= 0x03010000 */ /* mbedtls 3.1.0 */
|
||
|
|
||
|
|
||
|
/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
|
||
|
static const int suite_AES_256_ephemeral[] = {
|
||
|
/* All AES-256 ephemeral suites */
|
||
|
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||
|
MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||
|
MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
|
||
|
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM,
|
||
|
MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM,
|
||
|
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
|
||
|
MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
|
||
|
MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
|
||
|
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||
|
MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||
|
MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
|
||
|
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8,
|
||
|
MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8
|
||
|
};
|
||
|
|
||
|
/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
|
||
|
static const int suite_AES_128_ephemeral[] = {
|
||
|
/* All AES-128 ephemeral suites */
|
||
|
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||
|
MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||
|
MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
|
||
|
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
|
||
|
MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM,
|
||
|
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||
|
MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||
|
MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
|
||
|
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||
|
MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||
|
MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
|
||
|
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
|
||
|
MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8
|
||
|
};
|
||
|
|
||
|
/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
|
||
|
/* HIGH cipher list (mapped from openssl list to mbedtls) */
|
||
|
static const int suite_HIGH[] = {
|
||
|
MBEDTLS_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||
|
MBEDTLS_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||
|
MBEDTLS_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||
|
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||
|
MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||
|
MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
|
||
|
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM,
|
||
|
MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM,
|
||
|
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
|
||
|
MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
|
||
|
MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
|
||
|
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||
|
MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||
|
MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
|
||
|
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8,
|
||
|
MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8,
|
||
|
MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384,
|
||
|
MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384,
|
||
|
MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256,
|
||
|
MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,
|
||
|
MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384,
|
||
|
MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384,
|
||
|
MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384,
|
||
|
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||
|
MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||
|
MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
|
||
|
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
|
||
|
MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM,
|
||
|
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||
|
MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||
|
MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
|
||
|
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||
|
MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||
|
MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
|
||
|
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
|
||
|
MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8,
|
||
|
MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||
|
MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||
|
MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||
|
MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,
|
||
|
MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256,
|
||
|
MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256,
|
||
|
MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256,
|
||
|
MBEDTLS_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
|
||
|
MBEDTLS_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
|
||
|
MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384,
|
||
|
MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM,
|
||
|
MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384,
|
||
|
MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384,
|
||
|
MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA,
|
||
|
MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
|
||
|
MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
|
||
|
MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
|
||
|
MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM_8,
|
||
|
MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384,
|
||
|
MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256,
|
||
|
MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM,
|
||
|
MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
|
||
|
MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
|
||
|
MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
|
||
|
MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA,
|
||
|
MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
|
||
|
MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
|
||
|
MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM_8,
|
||
|
MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256,
|
||
|
MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||
|
MBEDTLS_TLS_RSA_WITH_AES_256_CCM,
|
||
|
MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256,
|
||
|
MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA,
|
||
|
MBEDTLS_TLS_RSA_WITH_AES_256_CCM_8,
|
||
|
MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256,
|
||
|
MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,
|
||
|
MBEDTLS_TLS_RSA_WITH_ARIA_256_GCM_SHA384,
|
||
|
MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||
|
MBEDTLS_TLS_RSA_WITH_AES_128_CCM,
|
||
|
MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||
|
MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA,
|
||
|
MBEDTLS_TLS_RSA_WITH_AES_128_CCM_8,
|
||
|
MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||
|
MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA,
|
||
|
MBEDTLS_TLS_RSA_WITH_ARIA_128_GCM_SHA256,
|
||
|
MBEDTLS_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256,
|
||
|
MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384,
|
||
|
MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
|
||
|
MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA,
|
||
|
MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384,
|
||
|
MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384,
|
||
|
MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256,
|
||
|
MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256,
|
||
|
MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
|
||
|
MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256,
|
||
|
MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256,
|
||
|
MBEDTLS_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256,
|
||
|
MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384,
|
||
|
MBEDTLS_TLS_PSK_WITH_AES_256_CCM,
|
||
|
MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384,
|
||
|
MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA,
|
||
|
MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384,
|
||
|
MBEDTLS_TLS_PSK_WITH_AES_256_CCM_8,
|
||
|
MBEDTLS_TLS_PSK_WITH_ARIA_256_GCM_SHA384,
|
||
|
MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256,
|
||
|
MBEDTLS_TLS_PSK_WITH_AES_128_CCM,
|
||
|
MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256,
|
||
|
MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA,
|
||
|
MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256,
|
||
|
MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8,
|
||
|
MBEDTLS_TLS_PSK_WITH_ARIA_128_GCM_SHA256
|
||
|
};
|
||
|
|
||
|
|
||
|
__attribute_noinline__
|
||
|
static int
|
||
|
tls_mbedtls_append_ciphersuite (int *ids, int nids, int idsz, const int *x, int xsz)
|
||
|
{
|
||
|
if (xsz >= idsz - (nids + 1)) {
|
||
|
emsg(MSG_ERROR, "error: too many ciphers during list expand");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < xsz; ++i)
|
||
|
ids[++nids] = x[i];
|
||
|
|
||
|
return nids;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
tls_mbedtls_translate_ciphername(int id, char *buf, size_t buflen)
|
||
|
{
|
||
|
const mbedtls_ssl_ciphersuite_t *info =
|
||
|
mbedtls_ssl_ciphersuite_from_id(id);
|
||
|
if (info == NULL)
|
||
|
return 0;
|
||
|
const char *name = mbedtls_ssl_ciphersuite_get_name(info);
|
||
|
const size_t len = os_strlen(name);
|
||
|
if (len == 7 && 0 == os_memcmp(name, "unknown", 7))
|
||
|
return 0;
|
||
|
if (len >= buflen)
|
||
|
return 0;
|
||
|
os_strlcpy(buf, name, buflen);
|
||
|
|
||
|
/* attempt to translate mbedtls string to openssl string
|
||
|
* (some heuristics; incomplete) */
|
||
|
size_t i = 0, j = 0;
|
||
|
if (buf[0] == 'T') {
|
||
|
if (os_strncmp(buf, "TLS1-3-", 7) == 0) {
|
||
|
buf[3] = '-';
|
||
|
j = 4; /* remove "1-3" from "TLS1-3-" prefix */
|
||
|
i = 7;
|
||
|
}
|
||
|
else if (os_strncmp(buf, "TLS-", 4) == 0)
|
||
|
i = 4; /* remove "TLS-" prefix */
|
||
|
}
|
||
|
for (; buf[i]; ++i) {
|
||
|
if (buf[i] == '-') {
|
||
|
if (i >= 3) {
|
||
|
if (0 == os_memcmp(buf+i-3, "AES", 3))
|
||
|
continue; /* "AES-" -> "AES" */
|
||
|
}
|
||
|
if (i >= 4) {
|
||
|
if (0 == os_memcmp(buf+i-4, "WITH", 4)) {
|
||
|
j -= 4; /* remove "WITH-" */
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
buf[j++] = buf[i];
|
||
|
}
|
||
|
buf[j] = '\0';
|
||
|
|
||
|
return j;
|
||
|
}
|
||
|
|
||
|
|
||
|
__attribute_noinline__
|
||
|
static int
|
||
|
tls_mbedtls_set_ciphersuites(struct tls_conf *tls_conf, int *ids, int nids)
|
||
|
{
|
||
|
/* ciphersuites list must be persistent for lifetime of mbedtls_ssl_config*/
|
||
|
os_free(tls_conf->ciphersuites);
|
||
|
tls_conf->ciphersuites = os_malloc(nids * sizeof(int));
|
||
|
if (tls_conf->ciphersuites == NULL)
|
||
|
return 0;
|
||
|
os_memcpy(tls_conf->ciphersuites, ids, nids * sizeof(int));
|
||
|
mbedtls_ssl_conf_ciphersuites(&tls_conf->conf, tls_conf->ciphersuites);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
tls_mbedtls_set_ciphers(struct tls_conf *tls_conf, const char *ciphers)
|
||
|
{
|
||
|
char buf[64];
|
||
|
int ids[512];
|
||
|
int nids = -1;
|
||
|
const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
|
||
|
const char *next;
|
||
|
size_t blen, clen;
|
||
|
do {
|
||
|
next = os_strchr(ciphers, ':');
|
||
|
clen = next ? (size_t)(next - ciphers) : os_strlen(ciphers);
|
||
|
if (!clen)
|
||
|
continue;
|
||
|
|
||
|
/* special-case a select set of openssl group names for hwsim tests */
|
||
|
/* (review; remove excess code if tests are not run for non-OpenSSL?) */
|
||
|
if (clen == 9 && os_memcmp(ciphers, "SUITEB192", 9) == 0) {
|
||
|
static int ssl_preset_suiteb192_ciphersuites[] = {
|
||
|
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||
|
0
|
||
|
};
|
||
|
return tls_mbedtls_set_ciphersuites(tls_conf,
|
||
|
ssl_preset_suiteb192_ciphersuites,
|
||
|
2);
|
||
|
}
|
||
|
if (clen == 9 && os_memcmp(ciphers, "SUITEB128", 9) == 0) {
|
||
|
static int ssl_preset_suiteb128_ciphersuites[] = {
|
||
|
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||
|
0
|
||
|
};
|
||
|
return tls_mbedtls_set_ciphersuites(tls_conf,
|
||
|
ssl_preset_suiteb128_ciphersuites,
|
||
|
2);
|
||
|
}
|
||
|
if (clen == 7 && os_memcmp(ciphers, "DEFAULT", 7) == 0)
|
||
|
continue;
|
||
|
if (clen == 6 && os_memcmp(ciphers, "AES128", 6) == 0) {
|
||
|
nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz,
|
||
|
suite_AES_128_ephemeral,
|
||
|
(int)ARRAY_SIZE(suite_AES_128_ephemeral));
|
||
|
if (nids == -1)
|
||
|
return 0;
|
||
|
continue;
|
||
|
}
|
||
|
if (clen == 6 && os_memcmp(ciphers, "AES256", 6) == 0) {
|
||
|
nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz,
|
||
|
suite_AES_256_ephemeral,
|
||
|
(int)ARRAY_SIZE(suite_AES_256_ephemeral));
|
||
|
if (nids == -1)
|
||
|
return 0;
|
||
|
continue;
|
||
|
}
|
||
|
if (clen == 4 && os_memcmp(ciphers, "HIGH", 4) == 0) {
|
||
|
nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz, suite_HIGH,
|
||
|
(int)ARRAY_SIZE(suite_HIGH));
|
||
|
if (nids == -1)
|
||
|
return 0;
|
||
|
continue;
|
||
|
}
|
||
|
/* ignore anonymous cipher group names (?not supported by mbedtls?) */
|
||
|
if (clen == 4 && os_memcmp(ciphers, "!ADH", 4) == 0)
|
||
|
continue;
|
||
|
if (clen == 6 && os_memcmp(ciphers, "-aECDH", 6) == 0)
|
||
|
continue;
|
||
|
if (clen == 7 && os_memcmp(ciphers, "-aECDSA", 7) == 0)
|
||
|
continue;
|
||
|
|
||
|
/* attempt to match mbedtls cipher names
|
||
|
* nb: does not support openssl group names or list manipulation syntax
|
||
|
* (alt: could copy almost 1200 lines (!!!) of lighttpd mod_mbedtls.c
|
||
|
* mod_mbedtls_ssl_conf_ciphersuites() to translate strings)
|
||
|
* note: not efficient to rewrite list for each ciphers entry,
|
||
|
* but this code is expected to run only at startup
|
||
|
*/
|
||
|
const int *list = mbedtls_ssl_list_ciphersuites();
|
||
|
for (; *list; ++list) {
|
||
|
blen = tls_mbedtls_translate_ciphername(*list,buf,sizeof(buf));
|
||
|
if (!blen)
|
||
|
continue;
|
||
|
|
||
|
/* matching heuristics additional to translate_ciphername above */
|
||
|
if (blen == clen+4) {
|
||
|
char *cbc = os_strstr(buf, "CBC-");
|
||
|
if (cbc) {
|
||
|
os_memmove(cbc, cbc+4, blen-(cbc+4-buf)+1); /*(w/ '\0')*/
|
||
|
blen -= 4;
|
||
|
}
|
||
|
}
|
||
|
if (blen >= clen && os_memcmp(ciphers, buf, clen) == 0
|
||
|
&& (blen == clen
|
||
|
|| (blen == clen+7 && os_memcmp(buf+clen, "-SHA256", 7)))) {
|
||
|
if (1 >= idsz - (nids + 1)) {
|
||
|
emsg(MSG_ERROR,
|
||
|
"error: too many ciphers during list expand");
|
||
|
return 0;
|
||
|
}
|
||
|
ids[++nids] = *list;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (*list == 0) {
|
||
|
wpa_printf(MSG_ERROR,
|
||
|
"MTLS: unrecognized cipher: %.*s", (int)clen, ciphers);
|
||
|
return 0;
|
||
|
}
|
||
|
} while ((ciphers = next ? next+1 : NULL));
|
||
|
|
||
|
if (-1 == nids) return 1; /* empty list; no-op */
|
||
|
|
||
|
ids[++nids] = 0; /* terminate list */
|
||
|
++nids;
|
||
|
|
||
|
return tls_mbedtls_set_ciphersuites(tls_conf, ids, nids);
|
||
|
}
|
||
|
|
||
|
|
||
|
__attribute_noinline__
|
||
|
static int tls_mbedtls_set_item(char **config_item, const char *item)
|
||
|
{
|
||
|
os_free(*config_item);
|
||
|
*config_item = NULL;
|
||
|
return item ? (*config_item = os_strdup(item)) != NULL : 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int tls_connection_set_subject_match(struct tls_conf *tls_conf,
|
||
|
const struct tls_connection_params *params)
|
||
|
{
|
||
|
int rc = 1;
|
||
|
rc &= tls_mbedtls_set_item(&tls_conf->subject_match,
|
||
|
params->subject_match);
|
||
|
rc &= tls_mbedtls_set_item(&tls_conf->altsubject_match,
|
||
|
params->altsubject_match);
|
||
|
rc &= tls_mbedtls_set_item(&tls_conf->suffix_match,
|
||
|
params->suffix_match);
|
||
|
rc &= tls_mbedtls_set_item(&tls_conf->domain_match,
|
||
|
params->domain_match);
|
||
|
rc &= tls_mbedtls_set_item(&tls_conf->check_cert_subject,
|
||
|
params->check_cert_subject);
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* duplicated in crypto_mbedtls.c:crypto_mbedtls_readfile()*/
|
||
|
__attribute_noinline__
|
||
|
static int tls_mbedtls_readfile(const char *path, u8 **buf, size_t *n)
|
||
|
{
|
||
|
#if 0 /* #ifdef MBEDTLS_FS_IO */
|
||
|
/*(includes +1 for '\0' needed by mbedtls PEM parsing funcs)*/
|
||
|
if (mbedtls_pk_load_file(path, (unsigned char **)buf, n) != 0) {
|
||
|
wpa_printf(MSG_ERROR, "error: mbedtls_pk_load_file %s", path);
|
||
|
return -1;
|
||
|
}
|
||
|
#else
|
||
|
/*(use os_readfile() so that we can use os_free()
|
||
|
*(if we use mbedtls_pk_load_file() above, macros prevent calling free()
|
||
|
* directly #if defined(OS_REJECT_C_LIB_FUNCTIONS) and calling os_free()
|
||
|
* on buf aborts in tests if buf not allocated via os_malloc())*/
|
||
|
*buf = (u8 *)os_readfile(path, n);
|
||
|
if (!*buf) {
|
||
|
wpa_printf(MSG_ERROR, "error: os_readfile %s", path);
|
||
|
return -1;
|
||
|
}
|
||
|
u8 *buf0 = os_realloc(*buf, *n+1);
|
||
|
if (!buf0) {
|
||
|
bin_clear_free(*buf, *n);
|
||
|
*buf = NULL;
|
||
|
return -1;
|
||
|
}
|
||
|
buf0[(*n)++] = '\0';
|
||
|
*buf = buf0;
|
||
|
#endif
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int tls_mbedtls_set_crl(struct tls_conf *tls_conf, const u8 *data, size_t len)
|
||
|
{
|
||
|
/* do not use mbedtls_x509_crl_parse() on PEM unless it contains CRL */
|
||
|
if (len && data[len-1] == '\0'
|
||
|
&& NULL == os_strstr((const char *)data,"-----BEGIN X509 CRL-----")
|
||
|
&& tls_mbedtls_data_is_pem(data))
|
||
|
return 0;
|
||
|
|
||
|
mbedtls_x509_crl crl;
|
||
|
mbedtls_x509_crl_init(&crl);
|
||
|
int rc = mbedtls_x509_crl_parse(&crl, data, len);
|
||
|
if (rc < 0) {
|
||
|
mbedtls_x509_crl_free(&crl);
|
||
|
return rc == MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT ? 0 : rc;
|
||
|
}
|
||
|
|
||
|
mbedtls_x509_crl *crl_new = os_malloc(sizeof(crl));
|
||
|
if (crl_new == NULL) {
|
||
|
mbedtls_x509_crl_free(&crl);
|
||
|
return MBEDTLS_ERR_X509_ALLOC_FAILED;
|
||
|
}
|
||
|
os_memcpy(crl_new, &crl, sizeof(crl));
|
||
|
|
||
|
mbedtls_x509_crl *crl_old = tls_conf->crl;
|
||
|
tls_conf->crl = crl_new;
|
||
|
if (crl_old) {
|
||
|
mbedtls_x509_crl_free(crl_old);
|
||
|
os_free(crl_old);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int tls_mbedtls_set_ca(struct tls_conf *tls_conf, u8 *data, size_t len)
|
||
|
{
|
||
|
/* load crt struct onto stack and then copy into tls_conf in
|
||
|
* order to preserve existing tls_conf value if error occurs
|
||
|
*
|
||
|
* hostapd is not threaded, or else should allocate memory and swap in
|
||
|
* pointer reduce race condition. (If threaded, would also need to
|
||
|
* keep reference count of use to avoid freeing while still in use.) */
|
||
|
|
||
|
mbedtls_x509_crt crt;
|
||
|
mbedtls_x509_crt_init(&crt);
|
||
|
int rc = mbedtls_x509_crt_parse(&crt, data, len);
|
||
|
if (rc < 0) {
|
||
|
mbedtls_x509_crt_free(&crt);
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
mbedtls_x509_crt_free(&tls_conf->ca_cert);
|
||
|
os_memcpy(&tls_conf->ca_cert, &crt, sizeof(crt));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int tls_mbedtls_set_ca_and_crl(struct tls_conf *tls_conf, const char *ca_cert_file)
|
||
|
{
|
||
|
size_t len;
|
||
|
u8 *data;
|
||
|
if (tls_mbedtls_readfile(ca_cert_file, &data, &len))
|
||
|
return -1;
|
||
|
|
||
|
int rc;
|
||
|
if (0 == (rc = tls_mbedtls_set_ca(tls_conf, data, len))
|
||
|
&& (!tls_mbedtls_data_is_pem(data) /*skip parse for CRL if not PEM*/
|
||
|
|| 0 == (rc = tls_mbedtls_set_crl(tls_conf, data, len)))) {
|
||
|
mbedtls_ssl_conf_ca_chain(&tls_conf->conf,
|
||
|
&tls_conf->ca_cert,
|
||
|
tls_conf->crl);
|
||
|
}
|
||
|
else {
|
||
|
elog(rc, __func__);
|
||
|
emsg(MSG_ERROR, ca_cert_file);
|
||
|
}
|
||
|
|
||
|
forced_memzero(data, len);
|
||
|
os_free(data);
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void tls_mbedtls_refresh_crl(void)
|
||
|
{
|
||
|
/* check for CRL refresh
|
||
|
* continue even if error occurs; continue with previous cert, CRL */
|
||
|
unsigned int crl_reload_interval = tls_ctx_global.crl_reload_interval;
|
||
|
const char *ca_cert_file = tls_ctx_global.ca_cert_file;
|
||
|
if (!crl_reload_interval || !ca_cert_file)
|
||
|
return;
|
||
|
|
||
|
struct os_reltime *previous = &tls_ctx_global.crl_reload_previous;
|
||
|
struct os_reltime now;
|
||
|
if (os_get_reltime(&now) != 0
|
||
|
|| !os_reltime_expired(&now, previous, crl_reload_interval))
|
||
|
return;
|
||
|
|
||
|
/* Note: modifying global state is not thread-safe
|
||
|
* if in use by existing connections
|
||
|
*
|
||
|
* src/utils/os.h does not provide a portable stat()
|
||
|
* or else it would be a good idea to check mtime and size,
|
||
|
* and avoid reloading if file has not changed */
|
||
|
|
||
|
if (tls_mbedtls_set_ca_and_crl(tls_ctx_global.tls_conf, ca_cert_file) == 0)
|
||
|
*previous = now;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int tls_mbedtls_set_ca_cert(struct tls_conf *tls_conf,
|
||
|
const struct tls_connection_params *params)
|
||
|
{
|
||
|
if (params->ca_cert) {
|
||
|
if (os_strncmp(params->ca_cert, "probe://", 8) == 0) {
|
||
|
tls_conf->ca_cert_probe = 1;
|
||
|
tls_conf->has_ca_cert = 1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (os_strncmp(params->ca_cert, "hash://", 7) == 0) {
|
||
|
const char *pos = params->ca_cert + 7;
|
||
|
if (os_strncmp(pos, "server/sha256/", 14) != 0) {
|
||
|
emsg(MSG_ERROR, "unsupported ca_cert hash value");
|
||
|
return -1;
|
||
|
}
|
||
|
pos += 14;
|
||
|
if (os_strlen(pos) != SHA256_DIGEST_LENGTH*2) {
|
||
|
emsg(MSG_ERROR, "unexpected ca_cert hash length");
|
||
|
return -1;
|
||
|
}
|
||
|
if (hexstr2bin(pos, tls_conf->ca_cert_hash,
|
||
|
SHA256_DIGEST_LENGTH) < 0) {
|
||
|
emsg(MSG_ERROR, "invalid ca_cert hash value");
|
||
|
return -1;
|
||
|
}
|
||
|
emsg(MSG_DEBUG, "checking only server certificate match");
|
||
|
tls_conf->verify_depth0_only = 1;
|
||
|
tls_conf->has_ca_cert = 1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (tls_mbedtls_set_ca_and_crl(tls_conf, params->ca_cert) != 0)
|
||
|
return -1;
|
||
|
}
|
||
|
if (params->ca_cert_blob) {
|
||
|
size_t len = params->ca_cert_blob_len;
|
||
|
int is_pem = tls_mbedtls_data_is_pem(params->ca_cert_blob);
|
||
|
if (len && params->ca_cert_blob[len-1] != '\0' && is_pem)
|
||
|
++len; /*(include '\0' in len for PEM)*/
|
||
|
int ret = mbedtls_x509_crt_parse(&tls_conf->ca_cert,
|
||
|
params->ca_cert_blob, len);
|
||
|
if (ret != 0) {
|
||
|
elog(ret, "mbedtls_x509_crt_parse");
|
||
|
return -1;
|
||
|
}
|
||
|
if (is_pem) { /*(ca_cert_blob in DER format contains ca cert only)*/
|
||
|
ret = tls_mbedtls_set_crl(tls_conf, params->ca_cert_blob, len);
|
||
|
if (ret != 0) {
|
||
|
elog(ret, "mbedtls_x509_crl_parse");
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (mbedtls_x509_time_is_future(&tls_conf->ca_cert.valid_from)
|
||
|
|| mbedtls_x509_time_is_past(&tls_conf->ca_cert.valid_to)) {
|
||
|
emsg(MSG_WARNING, "ca_cert expired or not yet valid");
|
||
|
if (params->ca_cert)
|
||
|
emsg(MSG_WARNING, params->ca_cert);
|
||
|
}
|
||
|
|
||
|
tls_conf->has_ca_cert = 1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int tls_mbedtls_set_certs(struct tls_conf *tls_conf,
|
||
|
const struct tls_connection_params *params)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
if (params->ca_cert || params->ca_cert_blob) {
|
||
|
if (tls_mbedtls_set_ca_cert(tls_conf, params) != 0)
|
||
|
return -1;
|
||
|
}
|
||
|
else if (params->ca_path) {
|
||
|
emsg(MSG_INFO, "ca_path support not implemented");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (!tls_conf->has_ca_cert)
|
||
|
mbedtls_ssl_conf_authmode(&tls_conf->conf, MBEDTLS_SSL_VERIFY_NONE);
|
||
|
else {
|
||
|
/* Initial setting: REQUIRED for client, OPTIONAL for server
|
||
|
* (see also tls_connection_set_verify()) */
|
||
|
tls_conf->verify_peer = (tls_ctx_global.tls_conf == NULL);
|
||
|
int authmode = tls_conf->verify_peer
|
||
|
? MBEDTLS_SSL_VERIFY_REQUIRED
|
||
|
: MBEDTLS_SSL_VERIFY_OPTIONAL;
|
||
|
mbedtls_ssl_conf_authmode(&tls_conf->conf, authmode);
|
||
|
mbedtls_ssl_conf_ca_chain(&tls_conf->conf,
|
||
|
&tls_conf->ca_cert,
|
||
|
tls_conf->crl);
|
||
|
|
||
|
if (!tls_connection_set_subject_match(tls_conf, params))
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (params->client_cert2) /*(yes, server_cert2 in msg below)*/
|
||
|
emsg(MSG_INFO, "server_cert2 support not implemented");
|
||
|
|
||
|
if (params->client_cert) {
|
||
|
size_t len;
|
||
|
u8 *data;
|
||
|
if (tls_mbedtls_readfile(params->client_cert, &data, &len))
|
||
|
return -1;
|
||
|
ret = mbedtls_x509_crt_parse(&tls_conf->client_cert, data, len);
|
||
|
forced_memzero(data, len);
|
||
|
os_free(data);
|
||
|
}
|
||
|
if (params->client_cert_blob) {
|
||
|
size_t len = params->client_cert_blob_len;
|
||
|
if (len && params->client_cert_blob[len-1] != '\0'
|
||
|
&& tls_mbedtls_data_is_pem(params->client_cert_blob))
|
||
|
++len; /*(include '\0' in len for PEM)*/
|
||
|
ret = mbedtls_x509_crt_parse(&tls_conf->client_cert,
|
||
|
params->client_cert_blob, len);
|
||
|
}
|
||
|
if (params->client_cert || params->client_cert_blob) {
|
||
|
if (ret < 0) {
|
||
|
elog(ret, "mbedtls_x509_crt_parse");
|
||
|
if (params->client_cert)
|
||
|
emsg(MSG_ERROR, params->client_cert);
|
||
|
return -1;
|
||
|
}
|
||
|
if (mbedtls_x509_time_is_future(&tls_conf->client_cert.valid_from)
|
||
|
|| mbedtls_x509_time_is_past(&tls_conf->client_cert.valid_to)) {
|
||
|
emsg(MSG_WARNING, "cert expired or not yet valid");
|
||
|
if (params->client_cert)
|
||
|
emsg(MSG_WARNING, params->client_cert);
|
||
|
}
|
||
|
tls_conf->has_client_cert = 1;
|
||
|
}
|
||
|
|
||
|
if (params->private_key || params->private_key_blob) {
|
||
|
size_t len = params->private_key_blob_len;
|
||
|
u8 *data;
|
||
|
*(const u8 **)&data = params->private_key_blob;
|
||
|
if (len && data[len-1] != '\0' && tls_mbedtls_data_is_pem(data))
|
||
|
++len; /*(include '\0' in len for PEM)*/
|
||
|
if (params->private_key
|
||
|
&& tls_mbedtls_readfile(params->private_key, &data, &len)) {
|
||
|
return -1;
|
||
|
}
|
||
|
const char *pwd = params->private_key_passwd;
|
||
|
#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
|
||
|
ret = mbedtls_pk_parse_key(&tls_conf->private_key,
|
||
|
data, len,
|
||
|
(const unsigned char *)pwd,
|
||
|
pwd ? os_strlen(pwd) : 0,
|
||
|
mbedtls_ctr_drbg_random,
|
||
|
tls_ctx_global.ctr_drbg);
|
||
|
#else
|
||
|
ret = mbedtls_pk_parse_key(&tls_conf->private_key,
|
||
|
data, len,
|
||
|
(const unsigned char *)pwd,
|
||
|
pwd ? os_strlen(pwd) : 0);
|
||
|
#endif
|
||
|
if (params->private_key) {
|
||
|
forced_memzero(data, len);
|
||
|
os_free(data);
|
||
|
}
|
||
|
if (ret < 0) {
|
||
|
elog(ret, "mbedtls_pk_parse_key");
|
||
|
return -1;
|
||
|
}
|
||
|
tls_conf->has_private_key = 1;
|
||
|
}
|
||
|
|
||
|
if (tls_conf->has_client_cert && tls_conf->has_private_key) {
|
||
|
ret = mbedtls_ssl_conf_own_cert(
|
||
|
&tls_conf->conf, &tls_conf->client_cert, &tls_conf->private_key);
|
||
|
if (ret < 0) {
|
||
|
elog(ret, "mbedtls_ssl_conf_own_cert");
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* mbedtls_x509_crt_profile_suiteb plus rsa_min_bitlen 2048 */
|
||
|
/* (reference: see also mbedtls_x509_crt_profile_next) */
|
||
|
/* ??? should permit SHA-512, too, and additional curves ??? */
|
||
|
static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb128 =
|
||
|
{
|
||
|
/* Only SHA-256 and 384 */
|
||
|
MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA256 ) |
|
||
|
MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
|
||
|
/* Only ECDSA */
|
||
|
MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ) |
|
||
|
MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECKEY ),
|
||
|
#if defined(MBEDTLS_ECP_C)
|
||
|
/* Only NIST P-256 and P-384 */
|
||
|
MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP256R1 ) |
|
||
|
MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
|
||
|
#else
|
||
|
0,
|
||
|
#endif
|
||
|
2048,
|
||
|
};
|
||
|
|
||
|
|
||
|
/* stricter than mbedtls_x509_crt_profile_suiteb */
|
||
|
/* (reference: see also mbedtls_x509_crt_profile_next) */
|
||
|
/* ??? should permit SHA-512, too, and additional curves ??? */
|
||
|
static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb192 =
|
||
|
{
|
||
|
/* Only SHA-384 */
|
||
|
MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
|
||
|
/* Only ECDSA */
|
||
|
MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ) |
|
||
|
MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECKEY ),
|
||
|
#if defined(MBEDTLS_ECP_C)
|
||
|
/* Only NIST P-384 */
|
||
|
MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
|
||
|
#else
|
||
|
0,
|
||
|
#endif
|
||
|
3072,
|
||
|
};
|
||
|
|
||
|
|
||
|
/* stricter than mbedtls_x509_crt_profile_suiteb except allow any PK alg */
|
||
|
/* (reference: see also mbedtls_x509_crt_profile_next) */
|
||
|
/* ??? should permit SHA-512, too, and additional curves ??? */
|
||
|
static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb192_anypk =
|
||
|
{
|
||
|
/* Only SHA-384 */
|
||
|
MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
|
||
|
0xFFFFFFF, /* Any PK alg */
|
||
|
#if defined(MBEDTLS_ECP_C)
|
||
|
/* Only NIST P-384 */
|
||
|
MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
|
||
|
#else
|
||
|
0,
|
||
|
#endif
|
||
|
3072,
|
||
|
};
|
||
|
|
||
|
|
||
|
static int tls_mbedtls_set_params(struct tls_conf *tls_conf,
|
||
|
const struct tls_connection_params *params)
|
||
|
{
|
||
|
tls_conf->flags = params->flags;
|
||
|
|
||
|
if (tls_conf->flags & TLS_CONN_REQUIRE_OCSP_ALL) {
|
||
|
emsg(MSG_INFO, "ocsp=3 not supported");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (tls_conf->flags & TLS_CONN_REQUIRE_OCSP) {
|
||
|
emsg(MSG_INFO, "ocsp not supported");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
int suiteb128 = 0;
|
||
|
int suiteb192 = 0;
|
||
|
if (params->openssl_ciphers) {
|
||
|
if (os_strcmp(params->openssl_ciphers, "SUITEB192") == 0) {
|
||
|
suiteb192 = 1;
|
||
|
tls_conf->flags |= TLS_CONN_SUITEB;
|
||
|
}
|
||
|
if (os_strcmp(params->openssl_ciphers, "SUITEB128") == 0) {
|
||
|
suiteb128 = 1;
|
||
|
tls_conf->flags |= TLS_CONN_SUITEB;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int ret = mbedtls_ssl_config_defaults(
|
||
|
&tls_conf->conf, tls_ctx_global.tls_conf ? MBEDTLS_SSL_IS_SERVER
|
||
|
: MBEDTLS_SSL_IS_CLIENT,
|
||
|
MBEDTLS_SSL_TRANSPORT_STREAM,
|
||
|
(tls_conf->flags & TLS_CONN_SUITEB) ? MBEDTLS_SSL_PRESET_SUITEB
|
||
|
: MBEDTLS_SSL_PRESET_DEFAULT);
|
||
|
if (ret != 0) {
|
||
|
elog(ret, "mbedtls_ssl_config_defaults");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (suiteb128) {
|
||
|
mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
|
||
|
&tls_mbedtls_crt_profile_suiteb128);
|
||
|
mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 2048);
|
||
|
}
|
||
|
else if (suiteb192) {
|
||
|
mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
|
||
|
&tls_mbedtls_crt_profile_suiteb192);
|
||
|
mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 3072);
|
||
|
}
|
||
|
else if (tls_conf->flags & TLS_CONN_SUITEB) {
|
||
|
/* treat as suiteb192 while allowing any PK algorithm */
|
||
|
mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
|
||
|
&tls_mbedtls_crt_profile_suiteb192_anypk);
|
||
|
mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 3072);
|
||
|
}
|
||
|
|
||
|
tls_mbedtls_set_allowed_tls_vers(tls_conf, &tls_conf->conf);
|
||
|
ret = tls_mbedtls_set_certs(tls_conf, params);
|
||
|
if (ret != 0)
|
||
|
return -1;
|
||
|
|
||
|
if (params->dh_file
|
||
|
&& !tls_mbedtls_set_dhparams(tls_conf, params->dh_file)) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (params->openssl_ecdh_curves
|
||
|
&& !tls_mbedtls_set_curves(tls_conf, params->openssl_ecdh_curves)) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (params->openssl_ciphers) {
|
||
|
if (!tls_mbedtls_set_ciphers(tls_conf, params->openssl_ciphers))
|
||
|
return -1;
|
||
|
}
|
||
|
else if (tls_conf->flags & TLS_CONN_SUITEB) {
|
||
|
/* special-case a select set of ciphers for hwsim tests */
|
||
|
if (!tls_mbedtls_set_ciphers(tls_conf,
|
||
|
(tls_conf->flags & TLS_CONN_SUITEB_NO_ECDH)
|
||
|
? "DHE-RSA-AES256-GCM-SHA384"
|
||
|
: "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384"))
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
|
||
|
const struct tls_connection_params *params)
|
||
|
{
|
||
|
if (conn == NULL || params == NULL)
|
||
|
return -1;
|
||
|
|
||
|
tls_conf_deinit(conn->tls_conf);
|
||
|
struct tls_conf *tls_conf = conn->tls_conf = tls_conf_init(tls_ctx);
|
||
|
if (tls_conf == NULL)
|
||
|
return -1;
|
||
|
|
||
|
if (tls_ctx_global.tls_conf) {
|
||
|
tls_conf->check_crl = tls_ctx_global.tls_conf->check_crl;
|
||
|
tls_conf->check_crl_strict = tls_ctx_global.tls_conf->check_crl_strict;
|
||
|
/*(tls_openssl.c inherits check_cert_subject from global conf)*/
|
||
|
if (tls_ctx_global.tls_conf->check_cert_subject) {
|
||
|
tls_conf->check_cert_subject =
|
||
|
os_strdup(tls_ctx_global.tls_conf->check_cert_subject);
|
||
|
if (tls_conf->check_cert_subject == NULL)
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (tls_mbedtls_set_params(tls_conf, params) != 0)
|
||
|
return -1;
|
||
|
conn->verify_peer = tls_conf->verify_peer;
|
||
|
|
||
|
return tls_mbedtls_ssl_setup(conn);
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef TLS_MBEDTLS_SESSION_TICKETS
|
||
|
|
||
|
static int tls_mbedtls_clienthello_session_ticket_prep (struct tls_connection *conn,
|
||
|
const u8 *data, size_t len)
|
||
|
{
|
||
|
if (conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)
|
||
|
return -1;
|
||
|
if (conn->clienthello_session_ticket)
|
||
|
tls_connection_deinit_clienthello_session_ticket(conn);
|
||
|
if (len) {
|
||
|
conn->clienthello_session_ticket = mbedtls_calloc(1, len);
|
||
|
if (conn->clienthello_session_ticket == NULL)
|
||
|
return -1;
|
||
|
conn->clienthello_session_ticket_len = len;
|
||
|
os_memcpy(conn->clienthello_session_ticket, data, len);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void tls_mbedtls_clienthello_session_ticket_set (struct tls_connection *conn)
|
||
|
{
|
||
|
mbedtls_ssl_session *sess = conn->ssl.MBEDTLS_PRIVATE(session_negotiate);
|
||
|
if (sess->MBEDTLS_PRIVATE(ticket)) {
|
||
|
mbedtls_platform_zeroize(sess->MBEDTLS_PRIVATE(ticket),
|
||
|
sess->MBEDTLS_PRIVATE(ticket_len));
|
||
|
mbedtls_free(sess->MBEDTLS_PRIVATE(ticket));
|
||
|
}
|
||
|
sess->MBEDTLS_PRIVATE(ticket) = conn->clienthello_session_ticket;
|
||
|
sess->MBEDTLS_PRIVATE(ticket_len) = conn->clienthello_session_ticket_len;
|
||
|
sess->MBEDTLS_PRIVATE(ticket_lifetime) = 86400;/* XXX: can hint be 0? */
|
||
|
|
||
|
conn->clienthello_session_ticket = NULL;
|
||
|
conn->clienthello_session_ticket_len = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int tls_mbedtls_ssl_ticket_write(void *p_ticket,
|
||
|
const mbedtls_ssl_session *session,
|
||
|
unsigned char *start,
|
||
|
const unsigned char *end,
|
||
|
size_t *tlen,
|
||
|
uint32_t *lifetime)
|
||
|
{
|
||
|
struct tls_connection *conn = p_ticket;
|
||
|
if (conn && conn->session_ticket_cb) {
|
||
|
/* see tls_mbedtls_clienthello_session_ticket_prep() */
|
||
|
/* see tls_mbedtls_clienthello_session_ticket_set() */
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return mbedtls_ssl_ticket_write(&tls_ctx_global.ticket_ctx,
|
||
|
session, start, end, tlen, lifetime);
|
||
|
}
|
||
|
|
||
|
|
||
|
static int tls_mbedtls_ssl_ticket_parse(void *p_ticket,
|
||
|
mbedtls_ssl_session *session,
|
||
|
unsigned char *buf,
|
||
|
size_t len)
|
||
|
{
|
||
|
/* XXX: TODO: not implemented in client;
|
||
|
* mbedtls_ssl_conf_session_tickets_cb() callbacks only for TLS server*/
|
||
|
|
||
|
if (len == 0)
|
||
|
return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
|
||
|
|
||
|
struct tls_connection *conn = p_ticket;
|
||
|
if (conn && conn->session_ticket_cb) {
|
||
|
/* XXX: have random and secret been initialized yet?
|
||
|
* or must keys first be exported?
|
||
|
* EAP-FAST uses all args, EAP-TEAP only uses secret */
|
||
|
struct tls_random data;
|
||
|
if (tls_connection_get_random(NULL, conn, &data) != 0)
|
||
|
return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
|
||
|
int ret =
|
||
|
conn->session_ticket_cb(conn->session_ticket_cb_ctx,
|
||
|
buf, len,
|
||
|
data.client_random,
|
||
|
data.server_random,
|
||
|
conn->expkey_secret);
|
||
|
if (ret == 1) {
|
||
|
conn->resumed = 1;
|
||
|
return 0;
|
||
|
}
|
||
|
emsg(MSG_ERROR, "EAP session ticket ext not implemented");
|
||
|
return MBEDTLS_ERR_SSL_INVALID_MAC;
|
||
|
/*(non-zero return used for mbedtls debug logging)*/
|
||
|
}
|
||
|
|
||
|
/* XXX: TODO always use tls_mbedtls_ssl_ticket_parse() for callback? */
|
||
|
int rc = mbedtls_ssl_ticket_parse(&tls_ctx_global.ticket_ctx,
|
||
|
session, buf, len);
|
||
|
if (conn)
|
||
|
conn->resumed = (rc == 0);
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
#endif /* TLS_MBEDTLS_SESSION_TICKETS */
|
||
|
|
||
|
|
||
|
__attribute_cold__
|
||
|
int tls_global_set_params(void *tls_ctx,
|
||
|
const struct tls_connection_params *params)
|
||
|
{
|
||
|
/* XXX: why might global_set_params be called more than once? */
|
||
|
if (tls_ctx_global.tls_conf)
|
||
|
tls_conf_deinit(tls_ctx_global.tls_conf);
|
||
|
tls_ctx_global.tls_conf = tls_conf_init(tls_ctx);
|
||
|
if (tls_ctx_global.tls_conf == NULL)
|
||
|
return -1;
|
||
|
|
||
|
#ifdef MBEDTLS_SSL_SESSION_TICKETS
|
||
|
#ifdef MBEDTLS_SSL_TICKET_C
|
||
|
if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET))
|
||
|
#ifdef TLS_MBEDTLS_SESSION_TICKETS
|
||
|
mbedtls_ssl_conf_session_tickets_cb(&tls_ctx_global.tls_conf->conf,
|
||
|
tls_mbedtls_ssl_ticket_write,
|
||
|
tls_mbedtls_ssl_ticket_parse,
|
||
|
NULL);
|
||
|
#else
|
||
|
mbedtls_ssl_conf_session_tickets_cb(&tls_ctx_global.tls_conf->conf,
|
||
|
mbedtls_ssl_ticket_write,
|
||
|
mbedtls_ssl_ticket_parse,
|
||
|
&tls_ctx_global.ticket_ctx);
|
||
|
#endif
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
os_free(tls_ctx_global.ocsp_stapling_response);
|
||
|
tls_ctx_global.ocsp_stapling_response = NULL;
|
||
|
if (params->ocsp_stapling_response)
|
||
|
tls_ctx_global.ocsp_stapling_response =
|
||
|
os_strdup(params->ocsp_stapling_response);
|
||
|
|
||
|
os_free(tls_ctx_global.ca_cert_file);
|
||
|
tls_ctx_global.ca_cert_file = NULL;
|
||
|
if (params->ca_cert)
|
||
|
tls_ctx_global.ca_cert_file = os_strdup(params->ca_cert);
|
||
|
return tls_mbedtls_set_params(tls_ctx_global.tls_conf, params);
|
||
|
}
|
||
|
|
||
|
|
||
|
int tls_global_set_verify(void *tls_ctx, int check_crl, int strict)
|
||
|
{
|
||
|
tls_ctx_global.tls_conf->check_crl = check_crl;
|
||
|
tls_ctx_global.tls_conf->check_crl_strict = strict; /*(time checks)*/
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
|
||
|
int verify_peer, unsigned int flags,
|
||
|
const u8 *session_ctx, size_t session_ctx_len)
|
||
|
{
|
||
|
/*(EAP server-side calls this from eap_server_tls_ssl_init())*/
|
||
|
if (conn == NULL)
|
||
|
return -1;
|
||
|
|
||
|
conn->tls_conf->flags |= flags;/* TODO: reprocess flags, if necessary */
|
||
|
|
||
|
int authmode;
|
||
|
switch (verify_peer) {
|
||
|
case 2: authmode = MBEDTLS_SSL_VERIFY_OPTIONAL; break;/*(eap_teap_init())*/
|
||
|
case 1: authmode = MBEDTLS_SSL_VERIFY_REQUIRED; break;
|
||
|
default: authmode = MBEDTLS_SSL_VERIFY_NONE; break;
|
||
|
}
|
||
|
mbedtls_ssl_set_hs_authmode(&conn->ssl, authmode);
|
||
|
|
||
|
if ((conn->verify_peer = (authmode != MBEDTLS_SSL_VERIFY_NONE)))
|
||
|
mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
|
||
|
else
|
||
|
mbedtls_ssl_set_verify(&conn->ssl, NULL, NULL);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
|
||
|
static void tls_connection_export_keys_cb(
|
||
|
void *p_expkey, mbedtls_ssl_key_export_type secret_type,
|
||
|
const unsigned char *secret, size_t secret_len,
|
||
|
const unsigned char client_random[MBEDTLS_EXPKEY_RAND_LEN],
|
||
|
const unsigned char server_random[MBEDTLS_EXPKEY_RAND_LEN],
|
||
|
mbedtls_tls_prf_types tls_prf_type)
|
||
|
{
|
||
|
struct tls_connection *conn = p_expkey;
|
||
|
conn->tls_prf_type = tls_prf_type;
|
||
|
if (!tls_prf_type)
|
||
|
return;
|
||
|
if (secret_len > sizeof(conn->expkey_secret)) {
|
||
|
emsg(MSG_ERROR, "tls_connection_export_keys_cb secret too long");
|
||
|
conn->tls_prf_type = MBEDTLS_SSL_TLS_PRF_NONE; /* 0 */
|
||
|
return;
|
||
|
}
|
||
|
conn->expkey_secret_len = secret_len;
|
||
|
os_memcpy(conn->expkey_secret, secret, secret_len);
|
||
|
os_memcpy(conn->expkey_randbytes,
|
||
|
client_random, MBEDTLS_EXPKEY_RAND_LEN);
|
||
|
os_memcpy(conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
|
||
|
server_random, MBEDTLS_EXPKEY_RAND_LEN);
|
||
|
}
|
||
|
#elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
|
||
|
static int tls_connection_export_keys_cb(
|
||
|
void *p_expkey,
|
||
|
const unsigned char *ms,
|
||
|
const unsigned char *kb,
|
||
|
size_t maclen,
|
||
|
size_t keylen,
|
||
|
size_t ivlen,
|
||
|
const unsigned char client_random[MBEDTLS_EXPKEY_RAND_LEN],
|
||
|
const unsigned char server_random[MBEDTLS_EXPKEY_RAND_LEN],
|
||
|
mbedtls_tls_prf_types tls_prf_type )
|
||
|
{
|
||
|
struct tls_connection *conn = p_expkey;
|
||
|
conn->tls_prf_type = tls_prf_type;
|
||
|
if (!tls_prf_type)
|
||
|
return -1; /*(return value ignored by mbedtls)*/
|
||
|
conn->expkey_keyblock_size = maclen + keylen + ivlen;
|
||
|
conn->expkey_secret_len = MBEDTLS_EXPKEY_FIXED_SECRET_LEN;
|
||
|
os_memcpy(conn->expkey_secret, ms, MBEDTLS_EXPKEY_FIXED_SECRET_LEN);
|
||
|
os_memcpy(conn->expkey_randbytes,
|
||
|
client_random, MBEDTLS_EXPKEY_RAND_LEN);
|
||
|
os_memcpy(conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
|
||
|
server_random, MBEDTLS_EXPKEY_RAND_LEN);
|
||
|
return 0;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn,
|
||
|
struct tls_random *data)
|
||
|
{
|
||
|
if (!conn || !conn->tls_prf_type)
|
||
|
return -1;
|
||
|
data->client_random = conn->expkey_randbytes;
|
||
|
data->client_random_len = MBEDTLS_EXPKEY_RAND_LEN;
|
||
|
data->server_random = conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN;
|
||
|
data->server_random_len = MBEDTLS_EXPKEY_RAND_LEN;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
int 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)
|
||
|
{
|
||
|
/* (EAP-PEAP EAP-TLS EAP-TTLS) */
|
||
|
#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
|
||
|
return (conn && conn->established && conn->tls_prf_type)
|
||
|
? mbedtls_ssl_tls_prf(conn->tls_prf_type,
|
||
|
conn->expkey_secret, conn->expkey_secret_len, label,
|
||
|
conn->expkey_randbytes,
|
||
|
sizeof(conn->expkey_randbytes), out, out_len)
|
||
|
: -1;
|
||
|
#else
|
||
|
/* not implemented here for mbedtls < 2.18.0 */
|
||
|
return -1;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef TLS_MBEDTLS_EAP_FAST
|
||
|
|
||
|
#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
|
||
|
/* keyblock size info is not exposed in mbed TLS 3.0.0 */
|
||
|
/* extracted from mbedtls library/ssl_tls.c:ssl_tls12_populate_transform() */
|
||
|
#include <mbedtls/ssl_ciphersuites.h>
|
||
|
#include <mbedtls/cipher.h>
|
||
|
static size_t tls_mbedtls_ssl_keyblock_size (mbedtls_ssl_context *ssl)
|
||
|
{
|
||
|
#if !defined(MBEDTLS_USE_PSA_CRYPTO) /* XXX: (not extracted for PSA crypto) */
|
||
|
#if defined(MBEDTLS_SSL_PROTO_TLS1_3)
|
||
|
if (mbedtls_ssl_get_version_number(ssl) == MBEDTLS_SSL_VERSION_TLS1_3)
|
||
|
return 0; /* (calculation not extracted) */
|
||
|
#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */
|
||
|
|
||
|
int ciphersuite = mbedtls_ssl_get_ciphersuite_id_from_ssl(ssl);
|
||
|
const mbedtls_ssl_ciphersuite_t *ciphersuite_info =
|
||
|
mbedtls_ssl_ciphersuite_from_id(ciphersuite);
|
||
|
if (ciphersuite_info == NULL)
|
||
|
return 0;
|
||
|
|
||
|
const mbedtls_cipher_info_t *cipher_info =
|
||
|
mbedtls_cipher_info_from_type(ciphersuite_info->MBEDTLS_PRIVATE(cipher));
|
||
|
if (cipher_info == NULL)
|
||
|
return 0;
|
||
|
|
||
|
#if MBEDTLS_VERSION_NUMBER >= 0x03010000 /* mbedtls 3.1.0 */
|
||
|
size_t keylen = mbedtls_cipher_info_get_key_bitlen(cipher_info) / 8;
|
||
|
mbedtls_cipher_mode_t mode = mbedtls_cipher_info_get_mode(cipher_info);
|
||
|
#else
|
||
|
size_t keylen = cipher_info->MBEDTLS_PRIVATE(key_bitlen) / 8;
|
||
|
mbedtls_cipher_mode_t mode = cipher_info->MBEDTLS_PRIVATE(mode);
|
||
|
#endif
|
||
|
#if defined(MBEDTLS_GCM_C) || \
|
||
|
defined(MBEDTLS_CCM_C) || \
|
||
|
defined(MBEDTLS_CHACHAPOLY_C)
|
||
|
if (mode == MBEDTLS_MODE_GCM || mode == MBEDTLS_MODE_CCM)
|
||
|
return keylen + 4;
|
||
|
else if (mode == MBEDTLS_MODE_CHACHAPOLY)
|
||
|
return keylen + 12;
|
||
|
else
|
||
|
#endif /* MBEDTLS_GCM_C || MBEDTLS_CCM_C || MBEDTLS_CHACHAPOLY_C */
|
||
|
#if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC)
|
||
|
{
|
||
|
const mbedtls_md_info_t *md_info =
|
||
|
mbedtls_md_info_from_type(ciphersuite_info->MBEDTLS_PRIVATE(mac));
|
||
|
if (md_info == NULL)
|
||
|
return 0;
|
||
|
size_t mac_key_len = mbedtls_md_get_size(md_info);
|
||
|
size_t ivlen = mbedtls_cipher_info_get_iv_size(cipher_info);
|
||
|
return keylen + mac_key_len + ivlen;
|
||
|
}
|
||
|
#endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */
|
||
|
#endif /* !MBEDTLS_USE_PSA_CRYPTO *//* (not extracted for PSA crypto) */
|
||
|
return 0;
|
||
|
}
|
||
|
#endif /* MBEDTLS_VERSION_NUMBER >= 0x03000000 *//* mbedtls 3.0.0 */
|
||
|
|
||
|
|
||
|
int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
|
||
|
u8 *out, size_t out_len)
|
||
|
{
|
||
|
/* XXX: has export keys callback been run? */
|
||
|
if (!conn || !conn->tls_prf_type)
|
||
|
return -1;
|
||
|
|
||
|
#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
|
||
|
conn->expkey_keyblock_size = tls_mbedtls_ssl_keyblock_size(&conn->ssl);
|
||
|
if (conn->expkey_keyblock_size == 0)
|
||
|
return -1;
|
||
|
#endif
|
||
|
size_t skip = conn->expkey_keyblock_size * 2;
|
||
|
unsigned char *tmp_out = os_malloc(skip + out_len);
|
||
|
if (!tmp_out)
|
||
|
return -1;
|
||
|
|
||
|
/* server_random and then client_random */
|
||
|
unsigned char seed[MBEDTLS_EXPKEY_RAND_LEN*2];
|
||
|
os_memcpy(seed, conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
|
||
|
MBEDTLS_EXPKEY_RAND_LEN);
|
||
|
os_memcpy(seed + MBEDTLS_EXPKEY_RAND_LEN, conn->expkey_randbytes,
|
||
|
MBEDTLS_EXPKEY_RAND_LEN);
|
||
|
|
||
|
#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
|
||
|
int ret = mbedtls_ssl_tls_prf(conn->tls_prf_type,
|
||
|
conn->expkey_secret, conn->expkey_secret_len,
|
||
|
"key expansion", seed, sizeof(seed),
|
||
|
tmp_out, skip + out_len);
|
||
|
if (ret == 0)
|
||
|
os_memcpy(out, tmp_out + skip, out_len);
|
||
|
#else
|
||
|
int ret = -1; /*(not reached if not impl; return -1 at top of func)*/
|
||
|
#endif
|
||
|
|
||
|
bin_clear_free(tmp_out, skip + out_len);
|
||
|
forced_memzero(seed, sizeof(seed));
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
#endif /* TLS_MBEDTLS_EAP_FAST */
|
||
|
|
||
|
|
||
|
__attribute_cold__
|
||
|
static void tls_mbedtls_suiteb_handshake_alert (struct tls_connection *conn)
|
||
|
{
|
||
|
/* tests/hwsim/test_suite_b.py test_suite_b_192_rsa_insufficient_dh */
|
||
|
if (!(conn->tls_conf->flags & TLS_CONN_SUITEB))
|
||
|
return;
|
||
|
if (tls_ctx_global.tls_conf) /*(is server; want issue event on client)*/
|
||
|
return;
|
||
|
#if 0
|
||
|
/*(info not available on client;
|
||
|
* mbed TLS library enforces dhm min bitlen in ServerKeyExchange)*/
|
||
|
if (MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ==
|
||
|
#if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
|
||
|
mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl)
|
||
|
#else
|
||
|
mbedtls_ssl_get_ciphersuite_id(
|
||
|
mbedtls_ssl_get_ciphersuite(&conn->ssl))
|
||
|
#endif
|
||
|
&& mbedtls_mpi_size(&conn->tls_conf->conf.MBEDTLS_PRIVATE(dhm_P))
|
||
|
< 384 /*(3072/8)*/)
|
||
|
#endif
|
||
|
{
|
||
|
struct tls_config *init_conf = &tls_ctx_global.init_conf;
|
||
|
if (init_conf->event_cb) {
|
||
|
union tls_event_data ev;
|
||
|
os_memset(&ev, 0, sizeof(ev));
|
||
|
ev.alert.is_local = 1;
|
||
|
ev.alert.type = "fatal";
|
||
|
/*"internal error" string for tests/hwsim/test_suiteb.py */
|
||
|
ev.alert.description = "internal error: handshake failure";
|
||
|
/*ev.alert.description = "insufficient security";*/
|
||
|
init_conf->event_cb(init_conf->cb_ctx, TLS_ALERT, &ev);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
struct wpabuf * tls_connection_handshake(void *tls_ctx,
|
||
|
struct tls_connection *conn,
|
||
|
const struct wpabuf *in_data,
|
||
|
struct wpabuf **appl_data)
|
||
|
{
|
||
|
if (appl_data)
|
||
|
*appl_data = NULL;
|
||
|
|
||
|
if (in_data && wpabuf_len(in_data)) {
|
||
|
/*(unsure why tls_gnutls.c discards buffer contents; skip here)*/
|
||
|
if (conn->pull_buf && 0) /* disable; appears unwise */
|
||
|
tls_pull_buf_discard(conn, __func__);
|
||
|
if (!tls_pull_buf_append(conn, in_data))
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if (conn->tls_conf == NULL) {
|
||
|
struct tls_connection_params params;
|
||
|
os_memset(¶ms, 0, sizeof(params));
|
||
|
params.openssl_ciphers =
|
||
|
tls_ctx_global.init_conf.openssl_ciphers;
|
||
|
params.flags = tls_ctx_global.tls_conf->flags;
|
||
|
if (tls_connection_set_params(tls_ctx, conn, ¶ms) != 0)
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if (conn->verify_peer) /*(call here might be redundant; nbd)*/
|
||
|
mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
|
||
|
|
||
|
#ifdef TLS_MBEDTLS_SESSION_TICKETS
|
||
|
if (conn->clienthello_session_ticket)
|
||
|
/*(starting handshake for EAP-FAST and EAP-TEAP)*/
|
||
|
tls_mbedtls_clienthello_session_ticket_set(conn);
|
||
|
|
||
|
/* (not thread-safe due to need to set userdata 'conn' for callback) */
|
||
|
/* (unable to use mbedtls_ssl_set_user_data_p() with mbedtls 3.2.0+
|
||
|
* since ticket write and parse callbacks take (mbedtls_ssl_session *)
|
||
|
* param instead of (mbedtls_ssl_context *) param) */
|
||
|
if (conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)
|
||
|
mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
|
||
|
NULL, NULL, NULL);
|
||
|
else
|
||
|
mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
|
||
|
tls_mbedtls_ssl_ticket_write,
|
||
|
tls_mbedtls_ssl_ticket_parse,
|
||
|
conn);
|
||
|
#endif
|
||
|
|
||
|
#if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
|
||
|
int ret = mbedtls_ssl_handshake(&conn->ssl);
|
||
|
#else
|
||
|
int ret = 0;
|
||
|
while (conn->ssl.MBEDTLS_PRIVATE(state) != MBEDTLS_SSL_HANDSHAKE_OVER) {
|
||
|
ret = mbedtls_ssl_handshake_step(&conn->ssl);
|
||
|
if (ret != 0)
|
||
|
break;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#ifdef TLS_MBEDTLS_SESSION_TICKETS
|
||
|
mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
|
||
|
tls_mbedtls_ssl_ticket_write,
|
||
|
tls_mbedtls_ssl_ticket_parse,
|
||
|
NULL);
|
||
|
#endif
|
||
|
|
||
|
switch (ret) {
|
||
|
case 0:
|
||
|
conn->established = 1;
|
||
|
if (conn->push_buf == NULL)
|
||
|
/* Need to return something to get final TLS ACK. */
|
||
|
conn->push_buf = wpabuf_alloc(0);
|
||
|
|
||
|
if (appl_data /*&& conn->pull_buf && wpabuf_len(conn->pull_buf)*/)
|
||
|
*appl_data = NULL; /* RFE: check for application data */
|
||
|
break;
|
||
|
case MBEDTLS_ERR_SSL_WANT_WRITE:
|
||
|
case MBEDTLS_ERR_SSL_WANT_READ:
|
||
|
case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS:
|
||
|
case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS:
|
||
|
if (tls_ctx_global.tls_conf /*(is server)*/
|
||
|
&& conn->established && conn->push_buf == NULL)
|
||
|
/* Need to return something to trigger completion of EAP-TLS. */
|
||
|
conn->push_buf = wpabuf_alloc(0);
|
||
|
break;
|
||
|
default:
|
||
|
++conn->failed;
|
||
|
switch (ret) {
|
||
|
case MBEDTLS_ERR_SSL_CLIENT_RECONNECT:
|
||
|
case MBEDTLS_ERR_NET_CONN_RESET:
|
||
|
case MBEDTLS_ERR_NET_SEND_FAILED:
|
||
|
++conn->write_alerts;
|
||
|
break;
|
||
|
#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
|
||
|
case MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE:
|
||
|
#else
|
||
|
case MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE:
|
||
|
#endif
|
||
|
tls_mbedtls_suiteb_handshake_alert(conn);
|
||
|
/* fall through */
|
||
|
case MBEDTLS_ERR_NET_RECV_FAILED:
|
||
|
case MBEDTLS_ERR_SSL_CONN_EOF:
|
||
|
case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY:
|
||
|
case MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE:
|
||
|
++conn->read_alerts;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
ilog(ret, "mbedtls_ssl_handshake");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
struct wpabuf *out_data = conn->push_buf;
|
||
|
conn->push_buf = NULL;
|
||
|
return out_data;
|
||
|
}
|
||
|
|
||
|
|
||
|
struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
|
||
|
struct tls_connection *conn,
|
||
|
const struct wpabuf *in_data,
|
||
|
struct wpabuf **appl_data)
|
||
|
{
|
||
|
conn->is_server = 1;
|
||
|
return tls_connection_handshake(tls_ctx, conn, in_data, appl_data);
|
||
|
}
|
||
|
|
||
|
|
||
|
struct wpabuf * tls_connection_encrypt(void *tls_ctx,
|
||
|
struct tls_connection *conn,
|
||
|
const struct wpabuf *in_data)
|
||
|
{
|
||
|
int res = mbedtls_ssl_write(&conn->ssl,
|
||
|
wpabuf_head_u8(in_data), wpabuf_len(in_data));
|
||
|
if (res < 0) {
|
||
|
elog(res, "mbedtls_ssl_write");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
struct wpabuf *buf = conn->push_buf;
|
||
|
conn->push_buf = NULL;
|
||
|
return buf;
|
||
|
}
|
||
|
|
||
|
|
||
|
struct wpabuf * tls_connection_decrypt(void *tls_ctx,
|
||
|
struct tls_connection *conn,
|
||
|
const struct wpabuf *in_data)
|
||
|
{
|
||
|
int res;
|
||
|
struct wpabuf *out;
|
||
|
|
||
|
/*assert(in_data != NULL);*/
|
||
|
if (!tls_pull_buf_append(conn, in_data))
|
||
|
return NULL;
|
||
|
|
||
|
#if defined(MBEDTLS_ZLIB_SUPPORT) /* removed in mbedtls 3.x */
|
||
|
/* Add extra buffer space to handle the possibility of decrypted
|
||
|
* data being longer than input data due to TLS compression. */
|
||
|
out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
|
||
|
#else /* TLS compression is disabled in mbedtls 3.x */
|
||
|
out = wpabuf_alloc(wpabuf_len(in_data));
|
||
|
#endif
|
||
|
if (out == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
res = mbedtls_ssl_read(&conn->ssl, wpabuf_mhead(out), wpabuf_size(out));
|
||
|
if (res < 0) {
|
||
|
#if 1 /*(seems like a different error if wpabuf_len(in_data) == 0)*/
|
||
|
if (res == MBEDTLS_ERR_SSL_WANT_READ)
|
||
|
return out;
|
||
|
#endif
|
||
|
elog(res, "mbedtls_ssl_read");
|
||
|
wpabuf_free(out);
|
||
|
return NULL;
|
||
|
}
|
||
|
wpabuf_put(out, res);
|
||
|
|
||
|
return out;
|
||
|
}
|
||
|
|
||
|
|
||
|
int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
|
||
|
{
|
||
|
/* XXX: might need to detect if session resumed from TLS session ticket
|
||
|
* even if not special session ticket handling for EAP-FAST, EAP-TEAP */
|
||
|
/* (?ssl->handshake->resume during session ticket validation?) */
|
||
|
return conn && conn->resumed;
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef TLS_MBEDTLS_EAP_FAST
|
||
|
int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
|
||
|
u8 *ciphers)
|
||
|
{
|
||
|
/* ciphers is list of TLS_CIPHER_* from hostap/src/crypto/tls.h */
|
||
|
int ids[7];
|
||
|
const int idsz = (int)sizeof(ids);
|
||
|
int nids = -1, id;
|
||
|
for ( ; *ciphers != TLS_CIPHER_NONE; ++ciphers) {
|
||
|
switch (*ciphers) {
|
||
|
case TLS_CIPHER_RC4_SHA:
|
||
|
#ifdef MBEDTLS_TLS_RSA_WITH_RC4_128_SHA
|
||
|
id = MBEDTLS_TLS_RSA_WITH_RC4_128_SHA;
|
||
|
break;
|
||
|
#else
|
||
|
continue; /*(not supported in mbedtls 3.x; ignore)*/
|
||
|
#endif
|
||
|
case TLS_CIPHER_AES128_SHA:
|
||
|
id = MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA;
|
||
|
break;
|
||
|
case TLS_CIPHER_RSA_DHE_AES128_SHA:
|
||
|
id = MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
|
||
|
break;
|
||
|
case TLS_CIPHER_ANON_DH_AES128_SHA:
|
||
|
continue; /*(not supported in mbedtls; ignore)*/
|
||
|
case TLS_CIPHER_RSA_DHE_AES256_SHA:
|
||
|
id = MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
|
||
|
break;
|
||
|
case TLS_CIPHER_AES256_SHA:
|
||
|
id = MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA;
|
||
|
break;
|
||
|
default:
|
||
|
return -1; /* should not happen */
|
||
|
}
|
||
|
if (++nids == idsz)
|
||
|
return -1; /* should not happen */
|
||
|
ids[nids] = id;
|
||
|
}
|
||
|
if (nids < 0)
|
||
|
return 0; /* nothing to do */
|
||
|
if (++nids == idsz)
|
||
|
return -1; /* should not happen */
|
||
|
ids[nids] = 0; /* terminate list */
|
||
|
++nids;
|
||
|
|
||
|
return tls_mbedtls_set_ciphersuites(conn->tls_conf, ids, nids) ? 0 : -1;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
|
||
|
char *buf, size_t buflen)
|
||
|
{
|
||
|
if (conn == NULL)
|
||
|
return -1;
|
||
|
os_strlcpy(buf, mbedtls_ssl_get_version(&conn->ssl), buflen);
|
||
|
return buf[0] != 'u' ? 0 : -1; /*(-1 if "unknown")*/
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef TLS_MBEDTLS_EAP_TEAP
|
||
|
u16 tls_connection_get_cipher_suite(struct tls_connection *conn)
|
||
|
{
|
||
|
if (conn == NULL)
|
||
|
return 0;
|
||
|
return (u16)mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
|
||
|
char *buf, size_t buflen)
|
||
|
{
|
||
|
if (conn == NULL)
|
||
|
return -1;
|
||
|
const int id = mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl);
|
||
|
return tls_mbedtls_translate_ciphername(id, buf, buflen) ? 0 : -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef TLS_MBEDTLS_SESSION_TICKETS
|
||
|
|
||
|
int tls_connection_enable_workaround(void *tls_ctx,
|
||
|
struct tls_connection *conn)
|
||
|
{
|
||
|
/* (see comment in src/eap_peer/eap_fast.c:eap_fast_init()) */
|
||
|
/* XXX: is there a relevant setting for this in mbed TLS? */
|
||
|
/* (do we even care that much about older CBC ciphers?) */
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
|
||
|
int ext_type, const u8 *data,
|
||
|
size_t data_len)
|
||
|
{
|
||
|
/* (EAP-FAST and EAP-TEAP) */
|
||
|
if (ext_type == MBEDTLS_TLS_EXT_SESSION_TICKET) /*(ext_type == 35)*/
|
||
|
return tls_mbedtls_clienthello_session_ticket_prep(conn, data,
|
||
|
data_len);
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
#endif /* TLS_MBEDTLS_SESSION_TICKETS */
|
||
|
|
||
|
|
||
|
int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
|
||
|
{
|
||
|
return conn ? conn->failed : -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
|
||
|
{
|
||
|
return conn ? conn->read_alerts : -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
int tls_connection_get_write_alerts(void *tls_ctx,
|
||
|
struct tls_connection *conn)
|
||
|
{
|
||
|
return conn ? conn->write_alerts : -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef TLS_MBEDTLS_SESSION_TICKETS
|
||
|
int tls_connection_set_session_ticket_cb(
|
||
|
void *tls_ctx, struct tls_connection *conn,
|
||
|
tls_session_ticket_cb cb, void *ctx)
|
||
|
{
|
||
|
if (!(conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)) {
|
||
|
/* (EAP-FAST and EAP-TEAP) */
|
||
|
conn->session_ticket_cb = cb;
|
||
|
conn->session_ticket_cb_ctx = ctx;
|
||
|
return 0;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
int tls_get_library_version(char *buf, size_t buf_len)
|
||
|
{
|
||
|
#ifndef MBEDTLS_VERSION_C
|
||
|
const char * const ver = "n/a";
|
||
|
#else
|
||
|
char ver[9];
|
||
|
mbedtls_version_get_string(ver);
|
||
|
#endif
|
||
|
return os_snprintf(buf, buf_len,
|
||
|
"mbed TLS build=" MBEDTLS_VERSION_STRING " run=%s", ver);
|
||
|
}
|
||
|
|
||
|
|
||
|
void tls_connection_set_success_data(struct tls_connection *conn,
|
||
|
struct wpabuf *data)
|
||
|
{
|
||
|
wpabuf_free(conn->success_data);
|
||
|
conn->success_data = data;
|
||
|
}
|
||
|
|
||
|
|
||
|
void tls_connection_set_success_data_resumed(struct tls_connection *conn)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
const struct wpabuf *
|
||
|
tls_connection_get_success_data(struct tls_connection *conn)
|
||
|
{
|
||
|
return conn->success_data;
|
||
|
}
|
||
|
|
||
|
|
||
|
void tls_connection_remove_session(struct tls_connection *conn)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef TLS_MBEDTLS_EAP_TEAP
|
||
|
int tls_get_tls_unique(struct tls_connection *conn, u8 *buf, size_t max_len)
|
||
|
{
|
||
|
#if defined(MBEDTLS_SSL_RENEGOTIATION) /* XXX: renegotiation or resumption? */
|
||
|
/* data from TLS handshake Finished message */
|
||
|
size_t verify_len = conn->ssl.MBEDTLS_PRIVATE(verify_data_len);
|
||
|
char *verify_data = (conn->is_server ^ conn->resumed)
|
||
|
? conn->ssl.MBEDTLS_PRIVATE(peer_verify_data)
|
||
|
: conn->ssl.MBEDTLS_PRIVATE(own_verify_data);
|
||
|
if (verify_len && verify_len <= max_len) {
|
||
|
os_memcpy(buf, verify_data, verify_len);
|
||
|
return (int)verify_len;
|
||
|
}
|
||
|
#endif
|
||
|
return -1;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
__attribute_noinline__
|
||
|
static void tls_mbedtls_set_peer_subject(struct tls_connection *conn, const mbedtls_x509_crt *crt)
|
||
|
{
|
||
|
if (conn->peer_subject)
|
||
|
return;
|
||
|
char buf[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
|
||
|
int buflen = mbedtls_x509_dn_gets(buf, sizeof(buf), &crt->subject);
|
||
|
if (buflen >= 0 && (conn->peer_subject = os_malloc((size_t)buflen+1)))
|
||
|
os_memcpy(conn->peer_subject, buf, (size_t)buflen+1);
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef TLS_MBEDTLS_EAP_TEAP
|
||
|
const char * tls_connection_get_peer_subject(struct tls_connection *conn)
|
||
|
{
|
||
|
if (!conn)
|
||
|
return NULL;
|
||
|
if (!conn->peer_subject) { /*(if not set during cert verify)*/
|
||
|
const mbedtls_x509_crt *peer_cert =
|
||
|
mbedtls_ssl_get_peer_cert(&conn->ssl);
|
||
|
if (peer_cert)
|
||
|
tls_mbedtls_set_peer_subject(conn, peer_cert);
|
||
|
}
|
||
|
return conn->peer_subject;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#ifdef TLS_MBEDTLS_EAP_TEAP
|
||
|
bool tls_connection_get_own_cert_used(struct tls_connection *conn)
|
||
|
{
|
||
|
/* XXX: availability of cert does not necessary mean that client
|
||
|
* received certificate request from server and then sent cert.
|
||
|
* ? step handshake in tls_connection_handshake() looking for
|
||
|
* MBEDTLS_SSL_CERTIFICATE_REQUEST ? */
|
||
|
const struct tls_conf * const tls_conf = conn->tls_conf;
|
||
|
return (tls_conf->has_client_cert && tls_conf->has_private_key);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#if defined(CONFIG_FIPS)
|
||
|
#define TLS_MBEDTLS_CONFIG_FIPS
|
||
|
#endif
|
||
|
|
||
|
#if defined(CONFIG_SHA256)
|
||
|
#define TLS_MBEDTLS_TLS_PRF_SHA256
|
||
|
#endif
|
||
|
|
||
|
#if defined(CONFIG_SHA384)
|
||
|
#define TLS_MBEDTLS_TLS_PRF_SHA384
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#ifndef TLS_MBEDTLS_CONFIG_FIPS
|
||
|
#if defined(CONFIG_MODULE_TESTS)
|
||
|
/* unused with CONFIG_TLS=mbedtls except in crypto_module_tests.c */
|
||
|
#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */ \
|
||
|
&& MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
|
||
|
/* sha1-tlsprf.c */
|
||
|
#include "sha1.h"
|
||
|
int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label,
|
||
|
const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
|
||
|
{
|
||
|
return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_TLS1,
|
||
|
secret, secret_len, label,
|
||
|
seed, seed_len, out, outlen) ? -1 : 0;
|
||
|
}
|
||
|
#else
|
||
|
#include "sha1-tlsprf.c" /* pull in hostap local implementation */
|
||
|
#endif
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
#ifdef TLS_MBEDTLS_TLS_PRF_SHA256
|
||
|
/* sha256-tlsprf.c */
|
||
|
#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
|
||
|
#include "sha256.h"
|
||
|
int tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label,
|
||
|
const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
|
||
|
{
|
||
|
return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_SHA256,
|
||
|
secret, secret_len, label,
|
||
|
seed, seed_len, out, outlen) ? -1 : 0;
|
||
|
}
|
||
|
#else
|
||
|
#include "sha256-tlsprf.c" /* pull in hostap local implementation */
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
#ifdef TLS_MBEDTLS_TLS_PRF_SHA384
|
||
|
/* sha384-tlsprf.c */
|
||
|
#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
|
||
|
#include "sha384.h"
|
||
|
int tls_prf_sha384(const u8 *secret, size_t secret_len, const char *label,
|
||
|
const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
|
||
|
{
|
||
|
return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_SHA384,
|
||
|
secret, secret_len, label,
|
||
|
seed, seed_len, out, outlen) ? -1 : 0;
|
||
|
}
|
||
|
#else
|
||
|
#include "sha384-tlsprf.c" /* pull in hostap local implementation */
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
|
||
|
#define mbedtls_x509_crt_has_ext_type(crt, ext_type) \
|
||
|
((crt)->MBEDTLS_PRIVATE(ext_types) & (ext_type))
|
||
|
#endif
|
||
|
|
||
|
struct mlist { const char *p; size_t n; };
|
||
|
|
||
|
|
||
|
static int
|
||
|
tls_mbedtls_match_altsubject(mbedtls_x509_crt *crt, const char *match)
|
||
|
{
|
||
|
/* RFE: this could be pre-parsed into structured data at config time */
|
||
|
struct mlist list[256]; /*(much larger than expected)*/
|
||
|
int nlist = 0;
|
||
|
if ( os_strncmp(match, "EMAIL:", 6) != 0
|
||
|
&& os_strncmp(match, "DNS:", 4) != 0
|
||
|
&& os_strncmp(match, "URI:", 4) != 0 ) {
|
||
|
wpa_printf(MSG_INFO, "MTLS: Invalid altSubjectName match '%s'", match);
|
||
|
return 0;
|
||
|
}
|
||
|
for (const char *s = match, *tok; *s; s = tok ? tok+1 : "") {
|
||
|
do { } while ((tok = os_strchr(s, ';'))
|
||
|
&& os_strncmp(tok+1, "EMAIL:", 6) != 0
|
||
|
&& os_strncmp(tok+1, "DNS:", 4) != 0
|
||
|
&& os_strncmp(tok+1, "URI:", 4) != 0);
|
||
|
list[nlist].p = s;
|
||
|
list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
|
||
|
if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
|
||
|
wpa_printf(MSG_INFO, "MTLS: excessive altSubjectName match '%s'",
|
||
|
match);
|
||
|
break; /* truncate huge list and continue */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME))
|
||
|
return 0;
|
||
|
|
||
|
const mbedtls_x509_sequence *cur = &crt->subject_alt_names;
|
||
|
for (; cur != NULL; cur = cur->next) {
|
||
|
const unsigned char san_type = (unsigned char)cur->buf.tag
|
||
|
& MBEDTLS_ASN1_TAG_VALUE_MASK;
|
||
|
char t;
|
||
|
size_t step = 4;
|
||
|
switch (san_type) { /* "EMAIL:" or "DNS:" or "URI:" */
|
||
|
case MBEDTLS_X509_SAN_RFC822_NAME: step = 6; t = 'E'; break;
|
||
|
case MBEDTLS_X509_SAN_DNS_NAME: t = 'D'; break;
|
||
|
case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: t = 'U'; break;
|
||
|
default: continue;
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < nlist; ++i) {
|
||
|
/* step over "EMAIL:" or "DNS:" or "URI:" in list[i].p */
|
||
|
/* Note: v is not '\0'-terminated, but is a known length vlen,
|
||
|
* so okay to pass to os_strncasecmp() even though not z-string */
|
||
|
if (cur->buf.len == list[i].n - step && t == *list[i].p
|
||
|
&& 0 == os_strncasecmp((char *)cur->buf.p,
|
||
|
list[i].p+step, cur->buf.len)) {
|
||
|
return 1; /* match */
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return 0; /* no match */
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
tls_mbedtls_match_suffix(const char *v, size_t vlen,
|
||
|
const struct mlist *list, int nlist, int full)
|
||
|
{
|
||
|
/* Note: v is not '\0'-terminated, but is a known length vlen,
|
||
|
* so okay to pass to os_strncasecmp() even though not z-string */
|
||
|
for (int i = 0; i < nlist; ++i) {
|
||
|
size_t n = list[i].n;
|
||
|
if ((n == vlen || (n < vlen && v[vlen-n-1] == '.' && !full))
|
||
|
&& 0 == os_strncasecmp(v+vlen-n, list[i].p, n))
|
||
|
return 1; /* match */
|
||
|
}
|
||
|
return 0; /* no match */
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
tls_mbedtls_match_suffixes(mbedtls_x509_crt *crt, const char *match, int full)
|
||
|
{
|
||
|
/* RFE: this could be pre-parsed into structured data at config time */
|
||
|
struct mlist list[256]; /*(much larger than expected)*/
|
||
|
int nlist = 0;
|
||
|
for (const char *s = match, *tok; *s; s = tok ? tok+1 : "") {
|
||
|
tok = os_strchr(s, ';');
|
||
|
list[nlist].p = s;
|
||
|
list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
|
||
|
if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
|
||
|
wpa_printf(MSG_INFO, "MTLS: excessive suffix match '%s'", match);
|
||
|
break; /* truncate huge list and continue */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* check subjectAltNames */
|
||
|
if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME)) {
|
||
|
const mbedtls_x509_sequence *cur = &crt->subject_alt_names;
|
||
|
for (; cur != NULL; cur = cur->next) {
|
||
|
const unsigned char san_type = (unsigned char)cur->buf.tag
|
||
|
& MBEDTLS_ASN1_TAG_VALUE_MASK;
|
||
|
if (san_type == MBEDTLS_X509_SAN_DNS_NAME
|
||
|
&& tls_mbedtls_match_suffix((char *)cur->buf.p,
|
||
|
cur->buf.len,
|
||
|
list, nlist, full)) {
|
||
|
return 1; /* match */
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* check subject CN */
|
||
|
const mbedtls_x509_name *name = &crt->subject;
|
||
|
for (; name != NULL; name = name->next) {
|
||
|
if (name->oid.p && MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &name->oid) == 0)
|
||
|
break;
|
||
|
}
|
||
|
if (name && tls_mbedtls_match_suffix((char *)name->val.p, name->val.len,
|
||
|
list, nlist, full)) {
|
||
|
return 1; /* match */
|
||
|
}
|
||
|
|
||
|
return 0; /* no match */
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
tls_mbedtls_match_dn_field(mbedtls_x509_crt *crt, const char *match)
|
||
|
{
|
||
|
/* RFE: this could be pre-parsed into structured data at config time */
|
||
|
struct mlistoid { const char *p; size_t n;
|
||
|
const char *oid; size_t olen;
|
||
|
int prefix; };
|
||
|
struct mlistoid list[32]; /*(much larger than expected)*/
|
||
|
int nlist = 0;
|
||
|
for (const char *s = match, *tok, *e; *s; s = tok ? tok+1 : "") {
|
||
|
tok = os_strchr(s, '/');
|
||
|
list[nlist].oid = NULL;
|
||
|
list[nlist].olen = 0;
|
||
|
list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
|
||
|
e = memchr(s, '=', list[nlist].n);
|
||
|
if (e == NULL) {
|
||
|
if (list[nlist].n == 0)
|
||
|
continue; /* skip consecutive, repeated '/' */
|
||
|
if (list[nlist].n == 1 && *s == '*') {
|
||
|
/* special-case "*" to match any OID and value */
|
||
|
s = e = "=*";
|
||
|
list[nlist].n = 2;
|
||
|
list[nlist].oid = "";
|
||
|
}
|
||
|
else {
|
||
|
wpa_printf(MSG_INFO,
|
||
|
"MTLS: invalid check_cert_subject '%s' missing '='",
|
||
|
match);
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
switch (e - s) {
|
||
|
case 1:
|
||
|
if (*s == 'C') {
|
||
|
list[nlist].oid = MBEDTLS_OID_AT_COUNTRY;
|
||
|
list[nlist].olen = sizeof(MBEDTLS_OID_AT_COUNTRY)-1;
|
||
|
}
|
||
|
else if (*s == 'L') {
|
||
|
list[nlist].oid = MBEDTLS_OID_AT_LOCALITY;
|
||
|
list[nlist].olen = sizeof(MBEDTLS_OID_AT_LOCALITY)-1;
|
||
|
}
|
||
|
else if (*s == 'O') {
|
||
|
list[nlist].oid = MBEDTLS_OID_AT_ORGANIZATION;
|
||
|
list[nlist].olen = sizeof(MBEDTLS_OID_AT_ORGANIZATION)-1;
|
||
|
}
|
||
|
break;
|
||
|
case 2:
|
||
|
if (s[0] == 'C' && s[1] == 'N') {
|
||
|
list[nlist].oid = MBEDTLS_OID_AT_CN;
|
||
|
list[nlist].olen = sizeof(MBEDTLS_OID_AT_CN)-1;
|
||
|
}
|
||
|
else if (s[0] == 'S' && s[1] == 'T') {
|
||
|
list[nlist].oid = MBEDTLS_OID_AT_STATE;
|
||
|
list[nlist].olen = sizeof(MBEDTLS_OID_AT_STATE)-1;
|
||
|
}
|
||
|
else if (s[0] == 'O' && s[1] == 'U') {
|
||
|
list[nlist].oid = MBEDTLS_OID_AT_ORG_UNIT;
|
||
|
list[nlist].olen = sizeof(MBEDTLS_OID_AT_ORG_UNIT)-1;
|
||
|
}
|
||
|
break;
|
||
|
case 12:
|
||
|
if (os_memcmp(s, "emailAddress", 12) == 0) {
|
||
|
list[nlist].oid = MBEDTLS_OID_PKCS9_EMAIL;
|
||
|
list[nlist].olen = sizeof(MBEDTLS_OID_PKCS9_EMAIL)-1;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
if (list[nlist].oid == NULL) {
|
||
|
wpa_printf(MSG_INFO,
|
||
|
"MTLS: Unknown field in check_cert_subject '%s'",
|
||
|
match);
|
||
|
return 0;
|
||
|
}
|
||
|
list[nlist].n -= (size_t)(++e - s);
|
||
|
list[nlist].p = e;
|
||
|
if (list[nlist].n && e[list[nlist].n-1] == '*') {
|
||
|
--list[nlist].n;
|
||
|
list[nlist].prefix = 1;
|
||
|
}
|
||
|
/*(could easily add support for suffix matches if value begins with '*',
|
||
|
* but suffix match is not currently supported by other TLS modules)*/
|
||
|
|
||
|
if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
|
||
|
wpa_printf(MSG_INFO,
|
||
|
"MTLS: excessive check_cert_subject match '%s'",
|
||
|
match);
|
||
|
break; /* truncate huge list and continue */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* each component in match string must match cert Subject in order listed
|
||
|
* The behavior below preserves ordering but is slightly different than
|
||
|
* the grossly inefficient contortions implemented in tls_openssl.c */
|
||
|
const mbedtls_x509_name *name = &crt->subject;
|
||
|
for (int i = 0; i < nlist; ++i) {
|
||
|
int found = 0;
|
||
|
for (; name != NULL && !found; name = name->next) {
|
||
|
if (!name->oid.p)
|
||
|
continue;
|
||
|
/* special-case "*" to match any OID and value */
|
||
|
if (list[i].olen == 0) {
|
||
|
found = 1;
|
||
|
continue;
|
||
|
}
|
||
|
/* perform equalent of !MBEDTLS_OID_CMP() with oid ptr and len */
|
||
|
if (list[i].olen != name->oid.len
|
||
|
|| os_memcmp(list[i].oid, name->oid.p, name->oid.len) != 0)
|
||
|
continue;
|
||
|
/* Note: v is not '\0'-terminated, but is a known length vlen,
|
||
|
* so okay to pass to os_strncasecmp() even though not z-string */
|
||
|
if ((list[i].prefix
|
||
|
? list[i].n <= name->val.len /* prefix match */
|
||
|
: list[i].n == name->val.len) /* full match */
|
||
|
&& 0 == os_strncasecmp((char *)name->val.p,
|
||
|
list[i].p, list[i].n)) {
|
||
|
found = 1;
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
if (!found)
|
||
|
return 0; /* no match */
|
||
|
}
|
||
|
return 1; /* match */
|
||
|
}
|
||
|
|
||
|
|
||
|
__attribute_cold__
|
||
|
static void
|
||
|
tls_mbedtls_verify_fail_event (mbedtls_x509_crt *crt, int depth,
|
||
|
const char *errmsg, enum tls_fail_reason reason)
|
||
|
{
|
||
|
struct tls_config *init_conf = &tls_ctx_global.init_conf;
|
||
|
if (init_conf->event_cb == NULL)
|
||
|
return;
|
||
|
|
||
|
struct wpabuf *certbuf = wpabuf_alloc_copy(crt->raw.p, crt->raw.len);
|
||
|
char subject[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
|
||
|
if (mbedtls_x509_dn_gets(subject, sizeof(subject), &crt->subject) < 0)
|
||
|
subject[0] = '\0';
|
||
|
union tls_event_data ev;
|
||
|
os_memset(&ev, 0, sizeof(ev));
|
||
|
ev.cert_fail.reason = reason;
|
||
|
ev.cert_fail.depth = depth;
|
||
|
ev.cert_fail.subject = subject;
|
||
|
ev.cert_fail.reason_txt = errmsg;
|
||
|
ev.cert_fail.cert = certbuf;
|
||
|
|
||
|
init_conf->event_cb(init_conf->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
|
||
|
|
||
|
wpabuf_free(certbuf);
|
||
|
}
|
||
|
|
||
|
|
||
|
__attribute_noinline__
|
||
|
static void
|
||
|
tls_mbedtls_verify_cert_event (struct tls_connection *conn,
|
||
|
mbedtls_x509_crt *crt, int depth)
|
||
|
{
|
||
|
struct tls_config *init_conf = &tls_ctx_global.init_conf;
|
||
|
if (init_conf->event_cb == NULL)
|
||
|
return;
|
||
|
|
||
|
struct wpabuf *certbuf = NULL;
|
||
|
union tls_event_data ev;
|
||
|
os_memset(&ev, 0, sizeof(ev));
|
||
|
|
||
|
#ifdef MBEDTLS_SHA256_C
|
||
|
u8 hash[SHA256_DIGEST_LENGTH];
|
||
|
const u8 *addr[] = { (u8 *)crt->raw.p };
|
||
|
if (sha256_vector(1, addr, &crt->raw.len, hash) == 0) {
|
||
|
ev.peer_cert.hash = hash;
|
||
|
ev.peer_cert.hash_len = sizeof(hash);
|
||
|
}
|
||
|
#endif
|
||
|
ev.peer_cert.depth = depth;
|
||
|
char subject[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
|
||
|
if (depth == 0)
|
||
|
ev.peer_cert.subject = conn->peer_subject;
|
||
|
if (ev.peer_cert.subject == NULL) {
|
||
|
ev.peer_cert.subject = subject;
|
||
|
if (mbedtls_x509_dn_gets(subject, sizeof(subject), &crt->subject) < 0)
|
||
|
subject[0] = '\0';
|
||
|
}
|
||
|
|
||
|
char serial_num[128+1];
|
||
|
ev.peer_cert.serial_num =
|
||
|
tls_mbedtls_peer_serial_num(crt, serial_num, sizeof(serial_num));
|
||
|
|
||
|
const mbedtls_x509_sequence *cur;
|
||
|
|
||
|
cur = NULL;
|
||
|
if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME))
|
||
|
cur = &crt->subject_alt_names;
|
||
|
for (; cur != NULL; cur = cur->next) {
|
||
|
const unsigned char san_type = (unsigned char)cur->buf.tag
|
||
|
& MBEDTLS_ASN1_TAG_VALUE_MASK;
|
||
|
size_t prelen = 4;
|
||
|
const char *pre;
|
||
|
switch (san_type) {
|
||
|
case MBEDTLS_X509_SAN_RFC822_NAME: prelen = 6; pre = "EMAIL:";break;
|
||
|
case MBEDTLS_X509_SAN_DNS_NAME: pre = "DNS:"; break;
|
||
|
case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: pre = "URI:"; break;
|
||
|
default: continue;
|
||
|
}
|
||
|
|
||
|
char *pos = os_malloc(prelen + cur->buf.len + 1);
|
||
|
if (pos == NULL)
|
||
|
break;
|
||
|
ev.peer_cert.altsubject[ev.peer_cert.num_altsubject] = pos;
|
||
|
os_memcpy(pos, pre, prelen);
|
||
|
/* data should be properly backslash-escaped if needed,
|
||
|
* so code below does not re-escape, but does replace CTLs */
|
||
|
/*os_memcpy(pos+prelen, cur->buf.p, cur->buf.len);*/
|
||
|
/*pos[prelen+cur->buf.len] = '\0';*/
|
||
|
pos += prelen;
|
||
|
for (size_t i = 0; i < cur->buf.len; ++i) {
|
||
|
unsigned char c = cur->buf.p[i];
|
||
|
*pos++ = (c >= 32 && c != 127) ? c : '?';
|
||
|
}
|
||
|
*pos = '\0';
|
||
|
|
||
|
if (++ev.peer_cert.num_altsubject == TLS_MAX_ALT_SUBJECT)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
cur = NULL;
|
||
|
if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_CERTIFICATE_POLICIES))
|
||
|
cur = &crt->certificate_policies;
|
||
|
for (; cur != NULL; cur = cur->next) {
|
||
|
if (cur->buf.len != 11) /* len of OID_TOD_STRICT or OID_TOD_TOFU */
|
||
|
continue;
|
||
|
/* TOD-STRICT "1.3.6.1.4.1.40808.1.3.1" */
|
||
|
/* TOD-TOFU "1.3.6.1.4.1.40808.1.3.2" */
|
||
|
#define OID_TOD_STRICT "\x2b\x06\x01\x04\x01\x82\xbe\x68\x01\x03\x01"
|
||
|
#define OID_TOD_TOFU "\x2b\x06\x01\x04\x01\x82\xbe\x68\x01\x03\x02"
|
||
|
if (os_memcmp(cur->buf.p,
|
||
|
OID_TOD_STRICT, sizeof(OID_TOD_STRICT)-1) == 0) {
|
||
|
ev.peer_cert.tod = 1; /* TOD-STRICT */
|
||
|
break;
|
||
|
}
|
||
|
if (os_memcmp(cur->buf.p,
|
||
|
OID_TOD_TOFU, sizeof(OID_TOD_TOFU)-1) == 0) {
|
||
|
ev.peer_cert.tod = 2; /* TOD-TOFU */
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
struct tls_conf *tls_conf = conn->tls_conf;
|
||
|
if (tls_conf->ca_cert_probe || (tls_conf->flags & TLS_CONN_EXT_CERT_CHECK)
|
||
|
|| init_conf->cert_in_cb) {
|
||
|
certbuf = wpabuf_alloc_copy(crt->raw.p, crt->raw.len);
|
||
|
ev.peer_cert.cert = certbuf;
|
||
|
}
|
||
|
|
||
|
init_conf->event_cb(init_conf->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
|
||
|
|
||
|
wpabuf_free(certbuf);
|
||
|
char **altsubject;
|
||
|
*(const char ***)&altsubject = ev.peer_cert.altsubject;
|
||
|
for (size_t i = 0; i < ev.peer_cert.num_altsubject; ++i)
|
||
|
os_free(altsubject[i]);
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
tls_mbedtls_verify_cb (void *arg, mbedtls_x509_crt *crt, int depth, uint32_t *flags)
|
||
|
{
|
||
|
/* XXX: N.B. verify code not carefully tested besides hwsim tests
|
||
|
*
|
||
|
* RFE: mbedtls_x509_crt_verify_info() and enhance log trace messages
|
||
|
* RFE: review and add support for additional TLS_CONN_* flags
|
||
|
* not handling OCSP (not available in mbedtls)
|
||
|
* ... */
|
||
|
|
||
|
struct tls_connection *conn = (struct tls_connection *)arg;
|
||
|
struct tls_conf *tls_conf = conn->tls_conf;
|
||
|
uint32_t flags_in = *flags;
|
||
|
|
||
|
if (depth > 8) { /*(depth 8 picked as arbitrary limit)*/
|
||
|
emsg(MSG_WARNING, "client cert chain too long");
|
||
|
*flags |= MBEDTLS_X509_BADCERT_OTHER; /* cert chain too long */
|
||
|
tls_mbedtls_verify_fail_event(crt, depth,
|
||
|
"client cert chain too long",
|
||
|
TLS_FAIL_BAD_CERTIFICATE);
|
||
|
}
|
||
|
else if (tls_conf->verify_depth0_only) {
|
||
|
if (depth > 0)
|
||
|
*flags = 0;
|
||
|
else {
|
||
|
#ifdef MBEDTLS_SHA256_C
|
||
|
u8 hash[SHA256_DIGEST_LENGTH];
|
||
|
const u8 *addr[] = { (u8 *)crt->raw.p };
|
||
|
if (sha256_vector(1, addr, &crt->raw.len, hash) < 0
|
||
|
|| os_memcmp(tls_conf->ca_cert_hash, hash, sizeof(hash)) != 0) {
|
||
|
*flags |= MBEDTLS_X509_BADCERT_NOT_TRUSTED;
|
||
|
tls_mbedtls_verify_fail_event(crt, depth,
|
||
|
"cert hash mismatch",
|
||
|
TLS_FAIL_UNTRUSTED);
|
||
|
}
|
||
|
else /* hash matches; ignore other issues *except* if revoked)*/
|
||
|
*flags &= MBEDTLS_X509_BADCERT_REVOKED;
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
else if (depth == 0) {
|
||
|
if (!conn->peer_subject)
|
||
|
tls_mbedtls_set_peer_subject(conn, crt);
|
||
|
/*(use same labels to tls_mbedtls_verify_fail_event() as used in
|
||
|
* other TLS modules so that hwsim tests find exact string match)*/
|
||
|
if (!conn->peer_subject) { /* error copying subject string */
|
||
|
*flags |= MBEDTLS_X509_BADCERT_OTHER;
|
||
|
tls_mbedtls_verify_fail_event(crt, depth,
|
||
|
"internal error",
|
||
|
TLS_FAIL_UNSPECIFIED);
|
||
|
}
|
||
|
/*(use os_strstr() for subject match as is done in tls_mbedtls.c
|
||
|
* to follow the same behavior, even though a suffix match would
|
||
|
* make more sense. Also, note that strstr match does not
|
||
|
* normalize whitespace (between components) for comparison)*/
|
||
|
else if (tls_conf->subject_match
|
||
|
&& os_strstr(conn->peer_subject,
|
||
|
tls_conf->subject_match) == NULL) {
|
||
|
wpa_printf(MSG_WARNING,
|
||
|
"MTLS: Subject '%s' did not match with '%s'",
|
||
|
conn->peer_subject, tls_conf->subject_match);
|
||
|
*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
|
||
|
tls_mbedtls_verify_fail_event(crt, depth,
|
||
|
"Subject mismatch",
|
||
|
TLS_FAIL_SUBJECT_MISMATCH);
|
||
|
}
|
||
|
if (tls_conf->altsubject_match
|
||
|
&& !tls_mbedtls_match_altsubject(crt, tls_conf->altsubject_match)) {
|
||
|
wpa_printf(MSG_WARNING,
|
||
|
"MTLS: altSubjectName match '%s' not found",
|
||
|
tls_conf->altsubject_match);
|
||
|
*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
|
||
|
tls_mbedtls_verify_fail_event(crt, depth,
|
||
|
"AltSubject mismatch",
|
||
|
TLS_FAIL_ALTSUBJECT_MISMATCH);
|
||
|
}
|
||
|
if (tls_conf->suffix_match
|
||
|
&& !tls_mbedtls_match_suffixes(crt, tls_conf->suffix_match, 0)) {
|
||
|
wpa_printf(MSG_WARNING,
|
||
|
"MTLS: Domain suffix match '%s' not found",
|
||
|
tls_conf->suffix_match);
|
||
|
*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
|
||
|
tls_mbedtls_verify_fail_event(crt, depth,
|
||
|
"Domain suffix mismatch",
|
||
|
TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
|
||
|
}
|
||
|
if (tls_conf->domain_match
|
||
|
&& !tls_mbedtls_match_suffixes(crt, tls_conf->domain_match, 1)) {
|
||
|
wpa_printf(MSG_WARNING,
|
||
|
"MTLS: Domain match '%s' not found",
|
||
|
tls_conf->domain_match);
|
||
|
*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
|
||
|
tls_mbedtls_verify_fail_event(crt, depth,
|
||
|
"Domain mismatch",
|
||
|
TLS_FAIL_DOMAIN_MISMATCH);
|
||
|
}
|
||
|
if (tls_conf->check_cert_subject
|
||
|
&& !tls_mbedtls_match_dn_field(crt, tls_conf->check_cert_subject)) {
|
||
|
*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
|
||
|
tls_mbedtls_verify_fail_event(crt, depth,
|
||
|
"Distinguished Name",
|
||
|
TLS_FAIL_DN_MISMATCH);
|
||
|
}
|
||
|
if (tls_conf->flags & TLS_CONN_SUITEB) {
|
||
|
/* check RSA modulus size (public key bitlen) */
|
||
|
const mbedtls_pk_type_t pk_alg = mbedtls_pk_get_type(&crt->pk);
|
||
|
if ((pk_alg == MBEDTLS_PK_RSA || pk_alg == MBEDTLS_PK_RSASSA_PSS)
|
||
|
&& mbedtls_pk_get_bitlen(&crt->pk) < 3072) {
|
||
|
/* hwsim suite_b RSA tests expect 3072
|
||
|
* suite_b_192_rsa_ecdhe_radius_rsa2048_client
|
||
|
* suite_b_192_rsa_dhe_radius_rsa2048_client */
|
||
|
*flags |= MBEDTLS_X509_BADCERT_BAD_KEY;
|
||
|
tls_mbedtls_verify_fail_event(crt, depth,
|
||
|
"Insufficient RSA modulus size",
|
||
|
TLS_FAIL_INSUFFICIENT_KEY_LEN);
|
||
|
}
|
||
|
}
|
||
|
if (tls_conf->check_crl && tls_conf->crl == NULL) {
|
||
|
/* see tests/hwsim test_ap_eap.py ap_wpa2_eap_tls_check_crl */
|
||
|
emsg(MSG_WARNING, "check_crl set but no CRL loaded; reject all?");
|
||
|
*flags |= MBEDTLS_X509_BADCERT_OTHER;
|
||
|
tls_mbedtls_verify_fail_event(crt, depth,
|
||
|
"check_crl set but no CRL loaded; "
|
||
|
"reject all?",
|
||
|
TLS_FAIL_BAD_CERTIFICATE);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if (tls_conf->check_crl != 2) /* 2 == verify CRLs for all certs */
|
||
|
*flags &= ~MBEDTLS_X509_BADCERT_REVOKED;
|
||
|
}
|
||
|
|
||
|
if (!tls_conf->check_crl_strict) {
|
||
|
*flags &= ~MBEDTLS_X509_BADCRL_EXPIRED;
|
||
|
*flags &= ~MBEDTLS_X509_BADCRL_FUTURE;
|
||
|
}
|
||
|
|
||
|
if (tls_conf->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
|
||
|
*flags &= ~MBEDTLS_X509_BADCERT_EXPIRED;
|
||
|
*flags &= ~MBEDTLS_X509_BADCERT_FUTURE;
|
||
|
}
|
||
|
|
||
|
tls_mbedtls_verify_cert_event(conn, crt, depth);
|
||
|
|
||
|
if (*flags) {
|
||
|
if (*flags & (MBEDTLS_X509_BADCERT_NOT_TRUSTED
|
||
|
|MBEDTLS_X509_BADCERT_CN_MISMATCH
|
||
|
|MBEDTLS_X509_BADCERT_REVOKED)) {
|
||
|
emsg(MSG_WARNING, "client cert not trusted");
|
||
|
}
|
||
|
/* report event if flags set but no additional flags set above */
|
||
|
/* (could translate flags to more detailed TLS_FAIL_* if needed) */
|
||
|
if (!(*flags & ~flags_in)) {
|
||
|
enum tls_fail_reason reason = TLS_FAIL_UNSPECIFIED;
|
||
|
const char *errmsg = "cert verify fail unspecified";
|
||
|
if (*flags & MBEDTLS_X509_BADCERT_NOT_TRUSTED) {
|
||
|
reason = TLS_FAIL_UNTRUSTED;
|
||
|
errmsg = "certificate not trusted";
|
||
|
}
|
||
|
if (*flags & MBEDTLS_X509_BADCERT_REVOKED) {
|
||
|
reason = TLS_FAIL_REVOKED;
|
||
|
errmsg = "certificate has been revoked";
|
||
|
}
|
||
|
if (*flags & MBEDTLS_X509_BADCERT_FUTURE) {
|
||
|
reason = TLS_FAIL_NOT_YET_VALID;
|
||
|
errmsg = "certificate not yet valid";
|
||
|
}
|
||
|
if (*flags & MBEDTLS_X509_BADCERT_EXPIRED) {
|
||
|
reason = TLS_FAIL_EXPIRED;
|
||
|
errmsg = "certificate has expired";
|
||
|
}
|
||
|
if (*flags & MBEDTLS_X509_BADCERT_BAD_MD) {
|
||
|
reason = TLS_FAIL_BAD_CERTIFICATE;
|
||
|
errmsg = "certificate uses insecure algorithm";
|
||
|
}
|
||
|
tls_mbedtls_verify_fail_event(crt, depth, errmsg, reason);
|
||
|
}
|
||
|
#if 0
|
||
|
/* ??? send (again) cert events for all certs in chain ???
|
||
|
* (should already have been called for greater depths) */
|
||
|
/* tls_openssl.c:tls_verify_cb() sends cert events for all certs
|
||
|
* in chain if certificate validation fails, but sends all events
|
||
|
* with depth set to 0 (might be a bug) */
|
||
|
if (depth > 0) {
|
||
|
int pdepth = depth + 1;
|
||
|
for (mbedtls_x509_crt *pcrt; (pcrt = crt->next); ++pdepth) {
|
||
|
tls_mbedtls_verify_cert_event(conn, pcrt, pdepth);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
/*(do not preserve subject if verification failed but was optional)*/
|
||
|
if (depth == 0 && conn->peer_subject) {
|
||
|
os_free(conn->peer_subject);
|
||
|
conn->peer_subject = NULL;
|
||
|
}
|
||
|
}
|
||
|
else if (depth == 0) {
|
||
|
struct tls_config *init_conf = &tls_ctx_global.init_conf;
|
||
|
if (tls_conf->ca_cert_probe) {
|
||
|
/* reject server certificate on probe-only run */
|
||
|
*flags |= MBEDTLS_X509_BADCERT_OTHER;
|
||
|
tls_mbedtls_verify_fail_event(crt, depth,
|
||
|
"server chain probe",
|
||
|
TLS_FAIL_SERVER_CHAIN_PROBE);
|
||
|
}
|
||
|
else if (init_conf->event_cb) {
|
||
|
/* ??? send event as soon as depth == 0 is verified ???
|
||
|
* What about rest of chain?
|
||
|
* Follows tls_mbedtls.c behavior: */
|
||
|
init_conf->event_cb(init_conf->cb_ctx,
|
||
|
TLS_CERT_CHAIN_SUCCESS, NULL);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|