diff options
Diffstat (limited to 'src/crypto/tls_openssl.c')
| -rw-r--r-- | src/crypto/tls_openssl.c | 320 |
1 files changed, 300 insertions, 20 deletions
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index b0c23ae6c9b1f..07d38e47b917d 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -44,6 +44,13 @@ #define OPENSSL_NEED_EAP_FAST_PRF #endif +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || \ + defined(EAP_SERVER_FAST) || defined(EAP_TEAP) || \ + defined(EAP_SERVER_TEAP) +#define EAP_FAST_OR_TEAP +#endif + + #if defined(OPENSSL_IS_BORINGSSL) /* stack_index_t is the return type of OpenSSL's sk_XXX_num() functions. */ typedef size_t stack_index_t; @@ -1071,11 +1078,8 @@ void * tls_init(const struct tls_config *conf) } #ifndef OPENSSL_NO_ENGINE - wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine"); -#if OPENSSL_VERSION_NUMBER < 0x10100000L - ERR_load_ENGINE_strings(); - ENGINE_load_dynamic(); -#endif /* OPENSSL_VERSION_NUMBER */ + wpa_printf(MSG_DEBUG, "ENGINE: Loading builtin engines"); + ENGINE_load_builtin_engines(); if (conf && (conf->opensc_engine_path || conf->pkcs11_engine_path || @@ -1331,6 +1335,8 @@ static const char * openssl_content_type(int content_type) return "heartbeat"; case 256: return "TLS header info"; /* pseudo content type */ + case 257: + return "inner content type"; /* pseudo content type */ default: return "?"; } @@ -1340,6 +1346,8 @@ static const char * openssl_content_type(int content_type) static const char * openssl_handshake_type(int content_type, const u8 *buf, size_t len) { + if (content_type == 257 && buf && len == 1) + return openssl_content_type(buf[0]); if (content_type != 22 || !buf || len == 0) return ""; switch (buf[0]) { @@ -1570,6 +1578,11 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) options |= SSL_OP_NO_COMPRESSION; #endif /* SSL_OP_NO_COMPRESSION */ SSL_set_options(conn->ssl, options); +#ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT + /* Hopefully there is no need for middlebox compatibility mechanisms + * when going through EAP authentication. */ + SSL_clear_options(conn->ssl, SSL_OP_ENABLE_MIDDLEBOX_COMPAT); +#endif conn->ssl_in = BIO_new(BIO_s_mem()); if (!conn->ssl_in) { @@ -2152,6 +2165,34 @@ static void openssl_tls_fail_event(struct tls_connection *conn, } +static int openssl_cert_tod(X509 *cert) +{ + CERTIFICATEPOLICIES *ext; + stack_index_t i; + char buf[100]; + int res; + int tod = 0; + + ext = X509_get_ext_d2i(cert, NID_certificate_policies, NULL, NULL); + if (!ext) + return 0; + + for (i = 0; i < sk_POLICYINFO_num(ext); i++) { + POLICYINFO *policy; + + policy = sk_POLICYINFO_value(ext, i); + res = OBJ_obj2txt(buf, sizeof(buf), policy->policyid, 0); + if (res < 0 || (size_t) res >= sizeof(buf)) + continue; + wpa_printf(MSG_DEBUG, "OpenSSL: Certificate Policy %s", buf); + if (os_strcmp(buf, "1.3.6.1.4.1.40808.1.3.1") == 0) + tod = 1; + } + + return tod; +} + + static void openssl_tls_cert_event(struct tls_connection *conn, X509 *err_cert, int depth, const char *subject) @@ -2244,6 +2285,8 @@ static void openssl_tls_cert_event(struct tls_connection *conn, ev.peer_cert.altsubject[alt] = altsubject[alt]; ev.peer_cert.num_altsubject = num_altsubject; + ev.peer_cert.tod = openssl_cert_tod(err_cert); + context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev); wpabuf_free(cert); for (alt = 0; alt < num_altsubject; alt++) @@ -2348,7 +2391,30 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) } #endif /* CONFIG_SHA256 */ + openssl_tls_cert_event(conn, err_cert, depth, buf); + if (!preverify_ok) { + if (depth > 0) { + /* Send cert event for the peer certificate so that + * the upper layers get information about it even if + * validation of a CA certificate fails. */ + STACK_OF(X509) *chain; + + chain = X509_STORE_CTX_get1_chain(x509_ctx); + if (chain && sk_X509_num(chain) > 0) { + char buf2[256]; + X509 *cert; + + cert = sk_X509_value(chain, 0); + X509_NAME_oneline(X509_get_subject_name(cert), + buf2, sizeof(buf2)); + + openssl_tls_cert_event(conn, cert, 0, buf2); + } + if (chain) + sk_X509_pop_free(chain, X509_free); + } + wpa_printf(MSG_WARNING, "TLS: Certificate verification failed," " error %d (%s) depth %d for '%s'", err, err_str, depth, buf); @@ -2404,8 +2470,7 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) openssl_tls_fail_event(conn, err_cert, err, depth, buf, "Domain mismatch", TLS_FAIL_DOMAIN_MISMATCH); - } else - openssl_tls_cert_event(conn, err_cert, depth, buf); + } if (conn->cert_probe && preverify_ok && depth == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: Reject server certificate " @@ -2580,9 +2645,23 @@ static int tls_connection_ca_cert(struct tls_data *data, (const unsigned char **) &ca_cert_blob, ca_cert_blob_len); if (cert == NULL) { - tls_show_errors(MSG_WARNING, __func__, - "Failed to parse ca_cert_blob"); - return -1; + BIO *bio = BIO_new_mem_buf(ca_cert_blob, + ca_cert_blob_len); + + if (bio) { + cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); + BIO_free(bio); + } + + if (!cert) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to parse ca_cert_blob"); + return -1; + } + + while (ERR_get_error()) { + /* Ignore errors from DER conversion. */ + } } if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx), @@ -3016,6 +3095,40 @@ static int tls_set_conn_flags(struct tls_connection *conn, unsigned int flags, } #endif /* CONFIG_SUITEB */ + if (flags & TLS_CONN_TEAP_ANON_DH) { +#ifndef TEAP_DH_ANON_CS +#define TEAP_DH_ANON_CS \ + "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:" \ + "ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:" \ + "ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:" \ + "DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:" \ + "DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:" \ + "DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:" \ + "ADH-AES256-GCM-SHA384:ADH-AES128-GCM-SHA256:" \ + "ADH-AES256-SHA256:ADH-AES128-SHA256:ADH-AES256-SHA:ADH-AES128-SHA" +#endif + static const char *cs = TEAP_DH_ANON_CS; + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !defined(LIBRESSL_VERSION_NUMBER) && \ + !defined(OPENSSL_IS_BORINGSSL) + /* + * Need to drop to security level 0 to allow anonymous + * cipher suites for EAP-TEAP. + */ + SSL_set_security_level(conn->ssl, 0); +#endif + + wpa_printf(MSG_DEBUG, + "OpenSSL: Enable cipher suites for anonymous EAP-TEAP provisioning: %s", + cs); + if (SSL_set_cipher_list(conn->ssl, cs) != 1) { + tls_show_errors(MSG_INFO, __func__, + "Cipher suite configuration failed"); + return -1; + } + } + return 0; } @@ -4002,7 +4115,7 @@ int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn, _out, skip + out_len) == 0) { ret = 0; } - os_memset(master_key, 0, sizeof(master_key)); + forced_memzero(master_key, sizeof(master_key)); os_free(rnd); if (ret == 0) os_memcpy(out, _out + skip, out_len); @@ -4192,6 +4305,22 @@ openssl_connection_handshake(struct tls_connection *conn, wpa_printf(MSG_DEBUG, "OpenSSL: Handshake finished - resumed=%d", tls_connection_resumed(conn->ssl_ctx, conn)); + if (conn->server) { + char *buf; + size_t buflen = 2000; + + buf = os_malloc(buflen); + if (buf) { + if (SSL_get_shared_ciphers(conn->ssl, buf, + buflen)) { + buf[buflen - 1] = '\0'; + wpa_printf(MSG_DEBUG, + "OpenSSL: Shared ciphers: %s", + buf); + } + os_free(buf); + } + } if (appl_data && in_data) *appl_data = openssl_get_appl_data(conn, wpabuf_len(in_data)); @@ -4374,11 +4503,15 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, c++; } + if (!buf[0]) { + wpa_printf(MSG_DEBUG, "OpenSSL: No ciphers listed"); + return -1; + } wpa_printf(MSG_DEBUG, "OpenSSL: cipher suites: %s", buf + 1); #if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) -#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) +#ifdef EAP_FAST_OR_TEAP if (os_strstr(buf, ":ADH-")) { /* * Need to drop to security level 0 to allow anonymous @@ -4389,7 +4522,7 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, /* Force at least security level 1 */ SSL_set_security_level(conn->ssl, 1); } -#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ +#endif /* EAP_FAST_OR_TEAP */ #endif if (SSL_set_cipher_list(conn->ssl, buf + 1) != 1) { @@ -4443,7 +4576,7 @@ int tls_connection_enable_workaround(void *ssl_ctx, } -#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) +#ifdef EAP_FAST_OR_TEAP /* ClientHello TLS extensions require a patch to openssl, so this function is * commented out unless explicitly needed for EAP-FAST in order to be able to * build this file with unmodified openssl. */ @@ -4460,7 +4593,7 @@ int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, return 0; } -#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ +#endif /* EAP_FAST_OR_TEAP */ int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) @@ -4669,6 +4802,7 @@ static int ocsp_resp_cb(SSL *s, void *arg) res = OCSP_resp_find_status(basic, id, &status, &reason, &produced_at, &this_update, &next_update); if (!res) { + OCSP_CERTID_free(id); id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer); if (!id) { wpa_printf(MSG_DEBUG, @@ -4979,6 +5113,114 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, } +static void openssl_debug_dump_cipher_list(SSL_CTX *ssl_ctx) +{ + SSL *ssl; + int i; + + ssl = SSL_new(ssl_ctx); + if (!ssl) + return; + + wpa_printf(MSG_DEBUG, + "OpenSSL: Enabled cipher suites in priority order"); + for (i = 0; ; i++) { + const char *cipher; + + cipher = SSL_get_cipher_list(ssl, i); + if (!cipher) + break; + wpa_printf(MSG_DEBUG, "Cipher %d: %s", i, cipher); + } + + SSL_free(ssl); +} + + +#if !defined(LIBRESSL_VERSION_NUMBER) && !defined(BORINGSSL_API_VERSION) + +static const char * openssl_pkey_type_str(const EVP_PKEY *pkey) +{ + if (!pkey) + return "NULL"; + switch (EVP_PKEY_type(EVP_PKEY_id(pkey))) { + case EVP_PKEY_RSA: + return "RSA"; + case EVP_PKEY_DSA: + return "DSA"; + case EVP_PKEY_DH: + return "DH"; + case EVP_PKEY_EC: + return "EC"; + } + return "?"; +} + + +static void openssl_debug_dump_certificate(int i, X509 *cert) +{ + char buf[256]; + EVP_PKEY *pkey; + ASN1_INTEGER *ser; + char serial_num[128]; + + X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)); + + ser = X509_get_serialNumber(cert); + if (ser) + wpa_snprintf_hex_uppercase(serial_num, sizeof(serial_num), + ASN1_STRING_get0_data(ser), + ASN1_STRING_length(ser)); + else + serial_num[0] = '\0'; + + pkey = X509_get_pubkey(cert); + wpa_printf(MSG_DEBUG, "%d: %s (%s) %s", i, buf, + openssl_pkey_type_str(pkey), serial_num); + EVP_PKEY_free(pkey); +} + + +static void openssl_debug_dump_certificates(SSL_CTX *ssl_ctx) +{ + STACK_OF(X509) *certs; + + wpa_printf(MSG_DEBUG, "OpenSSL: Configured certificate chain"); + if (SSL_CTX_get0_chain_certs(ssl_ctx, &certs) == 1) { + int i; + + for (i = sk_X509_num(certs); i > 0; i--) + openssl_debug_dump_certificate(i, sk_X509_value(certs, + i - 1)); + } + openssl_debug_dump_certificate(0, SSL_CTX_get0_certificate(ssl_ctx)); +} + +#endif + + +static void openssl_debug_dump_certificate_chains(SSL_CTX *ssl_ctx) +{ +#if !defined(LIBRESSL_VERSION_NUMBER) && !defined(BORINGSSL_API_VERSION) + int res; + + for (res = SSL_CTX_set_current_cert(ssl_ctx, SSL_CERT_SET_FIRST); + res == 1; + res = SSL_CTX_set_current_cert(ssl_ctx, SSL_CERT_SET_NEXT)) + openssl_debug_dump_certificates(ssl_ctx); + + SSL_CTX_set_current_cert(ssl_ctx, SSL_CERT_SET_FIRST); +#endif +} + + +static void openssl_debug_dump_ctx(SSL_CTX *ssl_ctx) +{ + openssl_debug_dump_cipher_list(ssl_ctx); + openssl_debug_dump_certificate_chains(ssl_ctx); +} + + int tls_global_set_params(void *tls_ctx, const struct tls_connection_params *params) { @@ -5004,6 +5246,9 @@ int tls_global_set_params(void *tls_ctx, tls_global_client_cert(data, params->client_cert) || tls_global_private_key(data, params->private_key, params->private_key_passwd) || + tls_global_client_cert(data, params->client_cert2) || + tls_global_private_key(data, params->private_key2, + params->private_key_passwd2) || tls_global_dh(data, params->dh_file)) { wpa_printf(MSG_INFO, "TLS: Failed to set global parameters"); return -1; @@ -5073,11 +5318,13 @@ int tls_global_set_params(void *tls_ctx, tls_global->ocsp_stapling_response = NULL; #endif /* HAVE_OCSP */ + openssl_debug_dump_ctx(ssl_ctx); + return 0; } -#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) +#ifdef EAP_FAST_OR_TEAP /* Pre-shared secred requires a patch to openssl, so this function is * commented out unless explicitly needed for EAP-FAST in order to be able to * build this file with unmodified openssl. */ @@ -5158,7 +5405,7 @@ static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data, return 1; } -#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ +#endif /* EAP_FAST_OR_TEAP */ int tls_connection_set_session_ticket_cb(void *tls_ctx, @@ -5166,7 +5413,7 @@ int tls_connection_set_session_ticket_cb(void *tls_ctx, tls_session_ticket_cb cb, void *ctx) { -#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) +#ifdef EAP_FAST_OR_TEAP conn->session_ticket_cb = cb; conn->session_ticket_cb_ctx = ctx; @@ -5183,9 +5430,9 @@ int tls_connection_set_session_ticket_cb(void *tls_ctx, } return 0; -#else /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ +#else /* EAP_FAST_OR_TEAP */ return -1; -#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ +#endif /* EAP_FAST_OR_TEAP */ } @@ -5268,3 +5515,36 @@ void tls_connection_remove_session(struct tls_connection *conn) wpa_printf(MSG_DEBUG, "OpenSSL: Removed cached session to disable session resumption"); } + + +int tls_get_tls_unique(struct tls_connection *conn, u8 *buf, size_t max_len) +{ + size_t len; + int reused; + + reused = SSL_session_reused(conn->ssl); + if ((conn->server && !reused) || (!conn->server && reused)) + len = SSL_get_peer_finished(conn->ssl, buf, max_len); + else + len = SSL_get_finished(conn->ssl, buf, max_len); + + if (len == 0 || len > max_len) + return -1; + + return len; +} + + +u16 tls_connection_get_cipher_suite(struct tls_connection *conn) +{ + const SSL_CIPHER *cipher; + + cipher = SSL_get_current_cipher(conn->ssl); + if (!cipher) + return 0; +#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER) + return SSL_CIPHER_get_protocol_id(cipher); +#else + return SSL_CIPHER_get_id(cipher) & 0xFFFF; +#endif +} |
