X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=libeap%2Fsrc%2Fcrypto%2Ftls_gnutls.c;h=f994379b16b22a07596626ee65626711b4624d02;hb=4f319dde67a76fe0aaf33f6d2788968012584ada;hp=c3a7358c0e77b113b739f6b7feb8fc922f46c474;hpb=ccf542544c4add8d720da2e5c9e048bab695732d;p=mech_eap.git diff --git a/libeap/src/crypto/tls_gnutls.c b/libeap/src/crypto/tls_gnutls.c index c3a7358..f994379 100644 --- a/libeap/src/crypto/tls_gnutls.c +++ b/libeap/src/crypto/tls_gnutls.c @@ -1,15 +1,9 @@ /* * SSL/TLS interface functions for GnuTLS - * Copyright (c) 2004-2009, Jouni Malinen + * Copyright (c) 2004-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -18,77 +12,15 @@ #ifdef PKCS12_FUNCS #include #endif /* PKCS12_FUNCS */ - -#ifdef CONFIG_GNUTLS_EXTRA -#if LIBGNUTLS_VERSION_NUMBER >= 0x010302 -#define GNUTLS_IA -#include -#if LIBGNUTLS_VERSION_NUMBER == 0x010302 -/* This function is not included in the current gnutls/extra.h even though it - * should be, so define it here as a workaround for the time being. */ -int gnutls_ia_verify_endphase(gnutls_session_t session, char *checksum); -#endif /* LIBGNUTLS_VERSION_NUMBER == 0x010302 */ -#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ -#endif /* CONFIG_GNUTLS_EXTRA */ +#if GNUTLS_VERSION_NUMBER >= 0x030103 +#include +#endif /* 3.1.3 */ #include "common.h" +#include "crypto/crypto.h" #include "tls.h" -#ifndef TLS_RANDOM_SIZE -#define TLS_RANDOM_SIZE 32 -#endif -#ifndef TLS_MASTER_SIZE -#define TLS_MASTER_SIZE 48 -#endif - - -#if LIBGNUTLS_VERSION_NUMBER < 0x010302 -/* GnuTLS 1.3.2 added functions for using master secret. Older versions require - * use of internal structures to get the master_secret and - * {server,client}_random. - */ -#define GNUTLS_INTERNAL_STRUCTURE_HACK -#endif /* LIBGNUTLS_VERSION_NUMBER < 0x010302 */ - - -#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK -/* - * It looks like gnutls does not provide access to client/server_random and - * master_key. This is somewhat unfortunate since these are needed for key - * derivation in EAP-{TLS,TTLS,PEAP,FAST}. Workaround for now is a horrible - * hack that copies the gnutls_session_int definition from gnutls_int.h so that - * we can get the needed information. - */ - -typedef u8 uint8; -typedef unsigned char opaque; -typedef struct { - uint8 suite[2]; -} cipher_suite_st; - -typedef struct { - gnutls_connection_end_t entity; - gnutls_kx_algorithm_t kx_algorithm; - gnutls_cipher_algorithm_t read_bulk_cipher_algorithm; - gnutls_mac_algorithm_t read_mac_algorithm; - gnutls_compression_method_t read_compression_algorithm; - gnutls_cipher_algorithm_t write_bulk_cipher_algorithm; - gnutls_mac_algorithm_t write_mac_algorithm; - gnutls_compression_method_t write_compression_algorithm; - cipher_suite_st current_cipher_suite; - opaque master_secret[TLS_MASTER_SIZE]; - opaque client_random[TLS_RANDOM_SIZE]; - opaque server_random[TLS_RANDOM_SIZE]; - /* followed by stuff we are not interested in */ -} security_parameters_st; - -struct gnutls_session_int { - security_parameters_st security_parameters; - /* followed by things we are not interested in */ -}; -#endif /* LIBGNUTLS_VERSION_NUMBER < 0x010302 */ - static int tls_gnutls_ref_count = 0; struct tls_global { @@ -100,17 +32,23 @@ struct tls_global { int params_set; gnutls_certificate_credentials_t xcred; + + void (*event_cb)(void *ctx, enum tls_event ev, + union tls_event_data *data); + void *cb_ctx; + int cert_in_cb; }; struct tls_connection { - gnutls_session session; - char *subject_match, *altsubject_match; + struct tls_global *global; + gnutls_session_t session; int read_alerts, write_alerts, failed; u8 *pre_shared_secret; size_t pre_shared_secret_len; int established; int verify_peer; + unsigned int disable_time_checks:1; struct wpabuf *push_buf; struct wpabuf *pull_buf; @@ -119,21 +57,13 @@ struct tls_connection { int params_set; gnutls_certificate_credentials_t xcred; - int tls_ia; - int final_phase_finished; - -#ifdef GNUTLS_IA - gnutls_ia_server_credentials_t iacred_srv; - gnutls_ia_client_credentials_t iacred_cli; + char *suffix_match; + char *domain_match; + unsigned int flags; +}; - /* Session keys generated in the current phase for inner secret - * permutation before generating/verifying PhaseFinished. */ - u8 *session_keys; - size_t session_keys_len; - u8 inner_secret[TLS_MASTER_SIZE]; -#endif /* GNUTLS_IA */ -}; +static int tls_connection_verify_peer(gnutls_session_t session); static void tls_log_func(int level, const char *msg) @@ -162,23 +92,15 @@ static void tls_log_func(int level, const char *msg) } -extern int wpa_debug_show_keys; - void * tls_init(const struct tls_config *conf) { struct tls_global *global; -#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK - /* Because of the horrible hack to get master_secret and client/server - * random, we need to make sure that the gnutls version is something - * that is expected to have same structure definition for the session - * data.. */ - const char *ver; - const char *ok_ver[] = { "1.2.3", "1.2.4", "1.2.5", "1.2.6", "1.2.9", - "1.3.2", - NULL }; - int i; -#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ + if (tls_gnutls_ref_count == 0) { + wpa_printf(MSG_DEBUG, + "GnuTLS: Library version %s (runtime) - %s (build)", + gnutls_check_version(NULL), GNUTLS_VERSION); + } global = os_zalloc(sizeof(*global)); if (global == NULL) @@ -190,28 +112,16 @@ void * tls_init(const struct tls_config *conf) } tls_gnutls_ref_count++; -#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK - ver = gnutls_check_version(NULL); - if (ver == NULL) { - tls_deinit(global); - return NULL; - } - wpa_printf(MSG_DEBUG, "%s - gnutls version %s", __func__, ver); - for (i = 0; ok_ver[i]; i++) { - if (strcmp(ok_ver[i], ver) == 0) - break; - } - if (ok_ver[i] == NULL) { - wpa_printf(MSG_INFO, "Untested gnutls version %s - this needs " - "to be tested and enabled in tls_gnutls.c", ver); - tls_deinit(global); - return NULL; - } -#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ - gnutls_global_set_log_function(tls_log_func); if (wpa_debug_show_keys) gnutls_global_set_log_level(11); + + if (conf) { + global->event_cb = conf->event_cb; + global->cb_ctx = conf->cb_ctx; + global->cert_in_cb = conf->cert_in_cb; + } + return global; } @@ -238,7 +148,7 @@ int tls_get_errors(void *ssl_ctx) } -static ssize_t tls_pull_func(gnutls_transport_ptr ptr, void *buf, +static ssize_t tls_pull_func(gnutls_transport_ptr_t ptr, void *buf, size_t len) { struct tls_connection *conn = (struct tls_connection *) ptr; @@ -267,7 +177,7 @@ static ssize_t tls_pull_func(gnutls_transport_ptr ptr, void *buf, } -static ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf, +static ssize_t tls_push_func(gnutls_transport_ptr_t ptr, const void *buf, size_t len) { struct tls_connection *conn = (struct tls_connection *) ptr; @@ -285,8 +195,7 @@ static ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf, static int tls_gnutls_init_session(struct tls_global *global, struct tls_connection *conn) { - const int cert_types[2] = { GNUTLS_CRT_X509, 0 }; - const int protos[2] = { GNUTLS_TLS1, 0 }; + const char *err; int ret; ret = gnutls_init(&conn->session, @@ -301,17 +210,18 @@ static int tls_gnutls_init_session(struct tls_global *global, if (ret < 0) goto fail; - ret = gnutls_certificate_type_set_priority(conn->session, cert_types); - if (ret < 0) - goto fail; - - ret = gnutls_protocol_set_priority(conn->session, protos); - if (ret < 0) + ret = gnutls_priority_set_direct(conn->session, "NORMAL:-VERS-SSL3.0", + &err); + if (ret < 0) { + wpa_printf(MSG_ERROR, "GnuTLS: Priority string failure at " + "'%s'", err); goto fail; + } gnutls_transport_set_pull_function(conn->session, tls_pull_func); gnutls_transport_set_push_function(conn->session, tls_push_func); - gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr) conn); + gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr_t) conn); + gnutls_session_set_ptr(conn->session, conn); return 0; @@ -332,6 +242,7 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) conn = os_zalloc(sizeof(*conn)); if (conn == NULL) return NULL; + conn->global = global; if (tls_gnutls_init_session(global, conn)) { os_free(conn); @@ -364,24 +275,13 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) if (conn == NULL) return; -#ifdef GNUTLS_IA - if (conn->iacred_srv) - gnutls_ia_free_server_credentials(conn->iacred_srv); - if (conn->iacred_cli) - gnutls_ia_free_client_credentials(conn->iacred_cli); - if (conn->session_keys) { - os_memset(conn->session_keys, 0, conn->session_keys_len); - os_free(conn->session_keys); - } -#endif /* GNUTLS_IA */ - gnutls_certificate_free_credentials(conn->xcred); gnutls_deinit(conn->session); os_free(conn->pre_shared_secret); - os_free(conn->subject_match); - os_free(conn->altsubject_match); wpabuf_free(conn->push_buf); wpabuf_free(conn->pull_buf); + os_free(conn->suffix_match); + os_free(conn->domain_match); os_free(conn); } @@ -407,14 +307,6 @@ int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) wpabuf_free(conn->push_buf); conn->push_buf = NULL; conn->established = 0; - conn->final_phase_finished = 0; -#ifdef GNUTLS_IA - if (conn->session_keys) { - os_memset(conn->session_keys, 0, conn->session_keys_len); - os_free(conn->session_keys); - } - conn->session_keys_len = 0; -#endif /* GNUTLS_IA */ gnutls_deinit(conn->session); if (tls_gnutls_init_session(global, conn)) { @@ -447,104 +339,6 @@ int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) } -#if 0 -static int tls_match_altsubject(X509 *cert, const char *match) -{ - GENERAL_NAME *gen; - char *field, *tmp; - void *ext; - int i, found = 0; - size_t len; - - ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); - - for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) { - gen = sk_GENERAL_NAME_value(ext, i); - switch (gen->type) { - case GEN_EMAIL: - field = "EMAIL"; - break; - case GEN_DNS: - field = "DNS"; - break; - case GEN_URI: - field = "URI"; - break; - default: - field = NULL; - wpa_printf(MSG_DEBUG, "TLS: altSubjectName: " - "unsupported type=%d", gen->type); - break; - } - - if (!field) - continue; - - wpa_printf(MSG_DEBUG, "TLS: altSubjectName: %s:%s", - field, gen->d.ia5->data); - len = os_strlen(field) + 1 + - strlen((char *) gen->d.ia5->data) + 1; - tmp = os_malloc(len); - if (tmp == NULL) - continue; - snprintf(tmp, len, "%s:%s", field, gen->d.ia5->data); - if (strstr(tmp, match)) - found++; - os_free(tmp); - } - - return found; -} -#endif - - -#if 0 -static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) -{ - char buf[256]; - X509 *err_cert; - int err, depth; - SSL *ssl; - struct tls_connection *conn; - char *match, *altmatch; - - err_cert = X509_STORE_CTX_get_current_cert(x509_ctx); - err = X509_STORE_CTX_get_error(x509_ctx); - depth = X509_STORE_CTX_get_error_depth(x509_ctx); - ssl = X509_STORE_CTX_get_ex_data(x509_ctx, - SSL_get_ex_data_X509_STORE_CTX_idx()); - X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf)); - - conn = SSL_get_app_data(ssl); - match = conn ? conn->subject_match : NULL; - altmatch = conn ? conn->altsubject_match : NULL; - - if (!preverify_ok) { - wpa_printf(MSG_WARNING, "TLS: Certificate verification failed," - " error %d (%s) depth %d for '%s'", err, - X509_verify_cert_error_string(err), depth, buf); - } else { - wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - " - "preverify_ok=%d err=%d (%s) depth=%d buf='%s'", - preverify_ok, err, - X509_verify_cert_error_string(err), depth, buf); - if (depth == 0 && match && strstr(buf, match) == NULL) { - wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not " - "match with '%s'", buf, match); - preverify_ok = 0; - } else if (depth == 0 && altmatch && - !tls_match_altsubject(err_cert, altmatch)) { - wpa_printf(MSG_WARNING, "TLS: altSubjectName match " - "'%s' not found", altmatch); - preverify_ok = 0; - } - } - - return preverify_ok; -} -#endif - - int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, const struct tls_connection_params *params) { @@ -553,44 +347,101 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, if (conn == NULL || params == NULL) return -1; - os_free(conn->subject_match); - conn->subject_match = NULL; if (params->subject_match) { - conn->subject_match = os_strdup(params->subject_match); - if (conn->subject_match == NULL) - return -1; + wpa_printf(MSG_INFO, "GnuTLS: subject_match not supported"); + return -1; } - os_free(conn->altsubject_match); - conn->altsubject_match = NULL; if (params->altsubject_match) { - conn->altsubject_match = os_strdup(params->altsubject_match); - if (conn->altsubject_match == NULL) + wpa_printf(MSG_INFO, "GnuTLS: altsubject_match not supported"); + return -1; + } + + os_free(conn->suffix_match); + conn->suffix_match = NULL; + if (params->suffix_match) { + conn->suffix_match = os_strdup(params->suffix_match); + if (conn->suffix_match == NULL) return -1; } +#if GNUTLS_VERSION_NUMBER >= 0x030300 + os_free(conn->domain_match); + conn->domain_match = NULL; + if (params->domain_match) { + conn->domain_match = os_strdup(params->domain_match); + if (conn->domain_match == NULL) + return -1; + } +#else /* < 3.3.0 */ + if (params->domain_match) { + wpa_printf(MSG_INFO, "GnuTLS: domain_match not supported"); + return -1; + } +#endif /* >= 3.3.0 */ + + conn->flags = params->flags; + + if (params->openssl_ciphers) { + wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported"); + return -1; + } + /* TODO: gnutls_certificate_set_verify_flags(xcred, flags); * to force peer validation(?) */ if (params->ca_cert) { - conn->verify_peer = 1; + wpa_printf(MSG_DEBUG, "GnuTLS: Try to parse %s in DER format", + params->ca_cert); ret = gnutls_certificate_set_x509_trust_file( - conn->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM); + conn->xcred, params->ca_cert, GNUTLS_X509_FMT_DER); if (ret < 0) { - wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' " - "in PEM format: %s", params->ca_cert, + wpa_printf(MSG_DEBUG, + "GnuTLS: Failed to read CA cert '%s' in DER format (%s) - try in PEM format", + params->ca_cert, gnutls_strerror(ret)); ret = gnutls_certificate_set_x509_trust_file( conn->xcred, params->ca_cert, - GNUTLS_X509_FMT_DER); + GNUTLS_X509_FMT_PEM); if (ret < 0) { - wpa_printf(MSG_DEBUG, "Failed to read CA cert " - "'%s' in DER format: %s", + wpa_printf(MSG_DEBUG, + "Failed to read CA cert '%s' in PEM format: %s", params->ca_cert, gnutls_strerror(ret)); return -1; } } + } else if (params->ca_cert_blob) { + gnutls_datum_t ca; + + ca.data = (unsigned char *) params->ca_cert_blob; + ca.size = params->ca_cert_blob_len; + + ret = gnutls_certificate_set_x509_trust_mem( + conn->xcred, &ca, GNUTLS_X509_FMT_DER); + if (ret < 0) { + wpa_printf(MSG_DEBUG, + "Failed to parse CA cert in DER format: %s", + gnutls_strerror(ret)); + ret = gnutls_certificate_set_x509_trust_mem( + conn->xcred, &ca, GNUTLS_X509_FMT_PEM); + if (ret < 0) { + wpa_printf(MSG_DEBUG, + "Failed to parse CA cert in PEM format: %s", + gnutls_strerror(ret)); + return -1; + } + } + } else if (params->ca_path) { + wpa_printf(MSG_INFO, "GnuTLS: ca_path not supported"); + return -1; + } + + conn->disable_time_checks = 0; + if (params->ca_cert || params->ca_cert_blob) { + conn->verify_peer = 1; + gnutls_certificate_set_verify_function( + conn->xcred, tls_connection_verify_peer); if (params->flags & TLS_CONN_ALLOW_SIGN_RSA_MD5) { gnutls_certificate_set_verify_flags( @@ -598,6 +449,7 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, } if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) { + conn->disable_time_checks = 1; gnutls_certificate_set_verify_flags( conn->xcred, GNUTLS_VERIFY_DISABLE_TIME_CHECKS); @@ -605,19 +457,32 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, } if (params->client_cert && params->private_key) { - /* TODO: private_key_passwd? */ +#if GNUTLS_VERSION_NUMBER >= 0x03010b + ret = gnutls_certificate_set_x509_key_file2( + conn->xcred, params->client_cert, params->private_key, + GNUTLS_X509_FMT_DER, params->private_key_passwd, 0); +#else + /* private_key_passwd not (easily) supported here */ ret = gnutls_certificate_set_x509_key_file( conn->xcred, params->client_cert, params->private_key, - GNUTLS_X509_FMT_PEM); + GNUTLS_X509_FMT_DER); +#endif if (ret < 0) { wpa_printf(MSG_DEBUG, "Failed to read client cert/key " - "in PEM format: %s", gnutls_strerror(ret)); + "in DER format: %s", gnutls_strerror(ret)); +#if GNUTLS_VERSION_NUMBER >= 0x03010b + ret = gnutls_certificate_set_x509_key_file2( + conn->xcred, params->client_cert, + params->private_key, GNUTLS_X509_FMT_PEM, + params->private_key_passwd, 0); +#else ret = gnutls_certificate_set_x509_key_file( conn->xcred, params->client_cert, - params->private_key, GNUTLS_X509_FMT_DER); + params->private_key, GNUTLS_X509_FMT_PEM); +#endif if (ret < 0) { wpa_printf(MSG_DEBUG, "Failed to read client " - "cert/key in DER format: %s", + "cert/key in PEM format: %s", gnutls_strerror(ret)); return ret; } @@ -626,7 +491,6 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, int pkcs12_ok = 0; #ifdef PKCS12_FUNCS /* Try to load in PKCS#12 format */ -#if LIBGNUTLS_VERSION_NUMBER >= 0x010302 ret = gnutls_certificate_set_x509_simple_pkcs12_file( conn->xcred, params->private_key, GNUTLS_X509_FMT_DER, params->private_key_passwd); @@ -636,7 +500,6 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } else pkcs12_ok = 1; -#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ #endif /* PKCS12_FUNCS */ if (!pkcs12_ok) { @@ -644,9 +507,82 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, "included"); return -1; } + } else if (params->client_cert_blob && params->private_key_blob) { + gnutls_datum_t cert, key; + + cert.data = (unsigned char *) params->client_cert_blob; + cert.size = params->client_cert_blob_len; + key.data = (unsigned char *) params->private_key_blob; + key.size = params->private_key_blob_len; + +#if GNUTLS_VERSION_NUMBER >= 0x03010b + ret = gnutls_certificate_set_x509_key_mem2( + conn->xcred, &cert, &key, GNUTLS_X509_FMT_DER, + params->private_key_passwd, 0); +#else + /* private_key_passwd not (easily) supported here */ + ret = gnutls_certificate_set_x509_key_mem( + conn->xcred, &cert, &key, GNUTLS_X509_FMT_DER); +#endif + if (ret < 0) { + wpa_printf(MSG_DEBUG, "Failed to read client cert/key " + "in DER format: %s", gnutls_strerror(ret)); +#if GNUTLS_VERSION_NUMBER >= 0x03010b + ret = gnutls_certificate_set_x509_key_mem2( + conn->xcred, &cert, &key, GNUTLS_X509_FMT_PEM, + params->private_key_passwd, 0); +#else + /* private_key_passwd not (easily) supported here */ + ret = gnutls_certificate_set_x509_key_mem( + conn->xcred, &cert, &key, GNUTLS_X509_FMT_PEM); +#endif + if (ret < 0) { + wpa_printf(MSG_DEBUG, "Failed to read client " + "cert/key in PEM format: %s", + gnutls_strerror(ret)); + return ret; + } + } + } else if (params->private_key_blob) { +#ifdef PKCS12_FUNCS + gnutls_datum_t key; + + key.data = (unsigned char *) params->private_key_blob; + key.size = params->private_key_blob_len; + + /* Try to load in PKCS#12 format */ + ret = gnutls_certificate_set_x509_simple_pkcs12_mem( + conn->xcred, &key, GNUTLS_X509_FMT_DER, + params->private_key_passwd); + if (ret != 0) { + wpa_printf(MSG_DEBUG, "Failed to load private_key in " + "PKCS#12 format: %s", gnutls_strerror(ret)); + return -1; + } +#else /* PKCS12_FUNCS */ + wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not included"); + return -1; +#endif /* PKCS12_FUNCS */ } - conn->tls_ia = params->tls_ia; +#if GNUTLS_VERSION_NUMBER >= 0x030103 + if (params->flags & (TLS_CONN_REQUEST_OCSP | TLS_CONN_REQUIRE_OCSP)) { + ret = gnutls_ocsp_status_request_enable_client(conn->session, + NULL, 0, NULL); + if (ret != GNUTLS_E_SUCCESS) { + wpa_printf(MSG_INFO, + "GnuTLS: Failed to enable OCSP client"); + return -1; + } + } +#else /* 3.1.3 */ + if (params->flags & TLS_CONN_REQUIRE_OCSP) { + wpa_printf(MSG_INFO, + "GnuTLS: OCSP not supported by this version of GnuTLS"); + return -1; + } +#endif /* 3.1.3 */ + conn->params_set = 1; ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, @@ -656,28 +592,6 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, gnutls_strerror(ret)); } -#ifdef GNUTLS_IA - if (conn->iacred_cli) - gnutls_ia_free_client_credentials(conn->iacred_cli); - - ret = gnutls_ia_allocate_client_credentials(&conn->iacred_cli); - if (ret) { - wpa_printf(MSG_DEBUG, "Failed to allocate IA credentials: %s", - gnutls_strerror(ret)); - return -1; - } - - ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_IA, - conn->iacred_cli); - if (ret) { - wpa_printf(MSG_DEBUG, "Failed to configure IA credentials: %s", - gnutls_strerror(ret)); - gnutls_ia_free_client_credentials(conn->iacred_cli); - conn->iacred_cli = NULL; - return -1; - } -#endif /* GNUTLS_IE */ - return ret; } @@ -706,17 +620,17 @@ int tls_global_set_params(void *tls_ctx, if (params->ca_cert) { ret = gnutls_certificate_set_x509_trust_file( - global->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM); + global->xcred, params->ca_cert, GNUTLS_X509_FMT_DER); if (ret < 0) { wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' " - "in PEM format: %s", params->ca_cert, + "in DER format: %s", params->ca_cert, gnutls_strerror(ret)); ret = gnutls_certificate_set_x509_trust_file( global->xcred, params->ca_cert, - GNUTLS_X509_FMT_DER); + GNUTLS_X509_FMT_PEM); if (ret < 0) { wpa_printf(MSG_DEBUG, "Failed to read CA cert " - "'%s' in DER format: %s", + "'%s' in PEM format: %s", params->ca_cert, gnutls_strerror(ret)); goto fail; @@ -740,16 +654,16 @@ int tls_global_set_params(void *tls_ctx, /* TODO: private_key_passwd? */ ret = gnutls_certificate_set_x509_key_file( global->xcred, params->client_cert, - params->private_key, GNUTLS_X509_FMT_PEM); + params->private_key, GNUTLS_X509_FMT_DER); if (ret < 0) { wpa_printf(MSG_DEBUG, "Failed to read client cert/key " - "in PEM format: %s", gnutls_strerror(ret)); + "in DER format: %s", gnutls_strerror(ret)); ret = gnutls_certificate_set_x509_key_file( global->xcred, params->client_cert, - params->private_key, GNUTLS_X509_FMT_DER); + params->private_key, GNUTLS_X509_FMT_PEM); if (ret < 0) { wpa_printf(MSG_DEBUG, "Failed to read client " - "cert/key in DER format: %s", + "cert/key in PEM format: %s", gnutls_strerror(ret)); goto fail; } @@ -758,7 +672,6 @@ int tls_global_set_params(void *tls_ctx, int pkcs12_ok = 0; #ifdef PKCS12_FUNCS /* Try to load in PKCS#12 format */ -#if LIBGNUTLS_VERSION_NUMBER >= 0x010302 ret = gnutls_certificate_set_x509_simple_pkcs12_file( global->xcred, params->private_key, GNUTLS_X509_FMT_DER, params->private_key_passwd); @@ -768,7 +681,6 @@ int tls_global_set_params(void *tls_ctx, goto fail; } else pkcs12_ok = 1; -#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ #endif /* PKCS12_FUNCS */ if (!pkcs12_ok) { @@ -796,7 +708,8 @@ int tls_global_set_verify(void *ssl_ctx, int check_crl) int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, - int verify_peer) + int verify_peer, unsigned int flags, + const u8 *session_ctx, size_t session_ctx_len) { if (conn == NULL || conn->session == NULL) return -1; @@ -810,75 +723,269 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, } -int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, - struct tls_keys *keys) +int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, + struct tls_random *keys) { -#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK - security_parameters_st *sec; -#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ +#if GNUTLS_VERSION_NUMBER >= 0x030012 + gnutls_datum_t client, server; if (conn == NULL || conn->session == NULL || keys == NULL) return -1; os_memset(keys, 0, sizeof(*keys)); - -#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK - sec = &conn->session->security_parameters; - keys->master_key = sec->master_secret; - keys->master_key_len = TLS_MASTER_SIZE; - keys->client_random = sec->client_random; - keys->server_random = sec->server_random; -#else /* GNUTLS_INTERNAL_STRUCTURE_HACK */ - keys->client_random = - (u8 *) gnutls_session_get_client_random(conn->session); - keys->server_random = - (u8 *) gnutls_session_get_server_random(conn->session); - /* No access to master_secret */ -#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ - -#ifdef GNUTLS_IA - gnutls_ia_extract_inner_secret(conn->session, - (char *) conn->inner_secret); - keys->inner_secret = conn->inner_secret; - keys->inner_secret_len = TLS_MASTER_SIZE; -#endif /* GNUTLS_IA */ - - keys->client_random_len = TLS_RANDOM_SIZE; - keys->server_random_len = TLS_RANDOM_SIZE; + gnutls_session_get_random(conn->session, &client, &server); + keys->client_random = client.data; + keys->server_random = server.data; + keys->client_random_len = client.size; + keys->server_random_len = client.size; return 0; +#else /* 3.0.18 */ + return -1; +#endif /* 3.0.18 */ } int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, const char *label, int server_random_first, - u8 *out, size_t out_len) + int skip_keyblock, u8 *out, size_t out_len) { -#if LIBGNUTLS_VERSION_NUMBER >= 0x010302 - if (conn == NULL || conn->session == NULL) + if (conn == NULL || conn->session == NULL || skip_keyblock) return -1; return gnutls_prf(conn->session, os_strlen(label), label, server_random_first, 0, NULL, out_len, (char *) out); -#else /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ +} + + +static void gnutls_tls_fail_event(struct tls_connection *conn, + const gnutls_datum_t *cert, int depth, + const char *subject, const char *err_str, + enum tls_fail_reason reason) +{ + union tls_event_data ev; + struct tls_global *global = conn->global; + struct wpabuf *cert_buf = NULL; + + if (global->event_cb == NULL) + return; + + os_memset(&ev, 0, sizeof(ev)); + ev.cert_fail.depth = depth; + ev.cert_fail.subject = subject ? subject : ""; + ev.cert_fail.reason = reason; + ev.cert_fail.reason_txt = err_str; + if (cert) { + cert_buf = wpabuf_alloc_copy(cert->data, cert->size); + ev.cert_fail.cert = cert_buf; + } + global->event_cb(global->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev); + wpabuf_free(cert_buf); +} + + +#if GNUTLS_VERSION_NUMBER < 0x030300 +static int server_eku_purpose(gnutls_x509_crt_t cert) +{ + unsigned int i; + + for (i = 0; ; i++) { + char oid[128]; + size_t oid_size = sizeof(oid); + int res; + + res = gnutls_x509_crt_get_key_purpose_oid(cert, i, oid, + &oid_size, NULL); + if (res == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + if (i == 0) { + /* No EKU - assume any use allowed */ + return 1; + } + break; + } + + if (res < 0) { + wpa_printf(MSG_INFO, "GnuTLS: Failed to get EKU"); + return 0; + } + + wpa_printf(MSG_DEBUG, "GnuTLS: Certificate purpose: %s", oid); + if (os_strcmp(oid, GNUTLS_KP_TLS_WWW_SERVER) == 0 || + os_strcmp(oid, GNUTLS_KP_ANY) == 0) + return 1; + } + + return 0; +} +#endif /* < 3.3.0 */ + + +static int check_ocsp(struct tls_connection *conn, gnutls_session_t session, + gnutls_alert_description_t *err) +{ +#if GNUTLS_VERSION_NUMBER >= 0x030103 + gnutls_datum_t response, buf; + gnutls_ocsp_resp_t resp; + unsigned int cert_status; + int res; + + if (!(conn->flags & (TLS_CONN_REQUEST_OCSP | TLS_CONN_REQUIRE_OCSP))) + return 0; + + if (!gnutls_ocsp_status_request_is_checked(session, 0)) { + if (conn->flags & TLS_CONN_REQUIRE_OCSP) { + wpa_printf(MSG_INFO, + "GnuTLS: No valid OCSP response received"); + goto ocsp_error; + } + + wpa_printf(MSG_DEBUG, + "GnuTLS: Valid OCSP response was not received - continue since OCSP was not required"); + return 0; + } + + /* + * GnuTLS has already verified the OCSP response in + * check_ocsp_response() and rejected handshake if the certificate was + * found to be revoked. However, if the response indicates that the + * status is unknown, handshake continues and reaches here. We need to + * re-import the OCSP response to check for unknown certificate status, + * but we do not need to repeat gnutls_ocsp_resp_check_crt() and + * gnutls_ocsp_resp_verify_direct() calls. + */ + + res = gnutls_ocsp_status_request_get(session, &response); + if (res != GNUTLS_E_SUCCESS) { + wpa_printf(MSG_INFO, + "GnuTLS: OCSP response was received, but it was not valid"); + goto ocsp_error; + } + + if (gnutls_ocsp_resp_init(&resp) != GNUTLS_E_SUCCESS) + goto ocsp_error; + + res = gnutls_ocsp_resp_import(resp, &response); + if (res != GNUTLS_E_SUCCESS) { + wpa_printf(MSG_INFO, + "GnuTLS: Could not parse received OCSP response: %s", + gnutls_strerror(res)); + gnutls_ocsp_resp_deinit(resp); + goto ocsp_error; + } + + res = gnutls_ocsp_resp_print(resp, GNUTLS_OCSP_PRINT_FULL, &buf); + if (res == GNUTLS_E_SUCCESS) { + wpa_printf(MSG_DEBUG, "GnuTLS: %s", buf.data); + gnutls_free(buf.data); + } + + res = gnutls_ocsp_resp_get_single(resp, 0, NULL, NULL, NULL, + NULL, &cert_status, NULL, + NULL, NULL, NULL); + gnutls_ocsp_resp_deinit(resp); + if (res != GNUTLS_E_SUCCESS) { + wpa_printf(MSG_INFO, + "GnuTLS: Failed to extract OCSP information: %s", + gnutls_strerror(res)); + goto ocsp_error; + } + + if (cert_status == GNUTLS_OCSP_CERT_GOOD) { + wpa_printf(MSG_DEBUG, "GnuTLS: OCSP cert status: good"); + } else if (cert_status == GNUTLS_OCSP_CERT_REVOKED) { + wpa_printf(MSG_DEBUG, + "GnuTLS: OCSP cert status: revoked"); + goto ocsp_error; + } else { + wpa_printf(MSG_DEBUG, + "GnuTLS: OCSP cert status: unknown"); + if (conn->flags & TLS_CONN_REQUIRE_OCSP) + goto ocsp_error; + wpa_printf(MSG_DEBUG, + "GnuTLS: OCSP was not required, so allow connection to continue"); + } + + return 0; + +ocsp_error: + gnutls_tls_fail_event(conn, NULL, 0, NULL, + "bad certificate status response", + TLS_FAIL_REVOKED); + *err = GNUTLS_A_CERTIFICATE_REVOKED; return -1; -#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ +#else /* GnuTLS 3.1.3 or newer */ + return 0; +#endif /* GnuTLS 3.1.3 or newer */ } -static int tls_connection_verify_peer(struct tls_connection *conn, - gnutls_alert_description_t *err) +static int tls_connection_verify_peer(gnutls_session_t session) { + struct tls_connection *conn; unsigned int status, num_certs, i; struct os_time now; const gnutls_datum_t *certs; gnutls_x509_crt_t cert; + gnutls_alert_description_t err; + int res; + + conn = gnutls_session_get_ptr(session); + if (!conn->verify_peer) { + wpa_printf(MSG_DEBUG, + "GnuTLS: No peer certificate verification enabled"); + return 0; + } - if (gnutls_certificate_verify_peers2(conn->session, &status) < 0) { + wpa_printf(MSG_DEBUG, "GnuTSL: Verifying peer certificate"); + +#if GNUTLS_VERSION_NUMBER >= 0x030300 + { + gnutls_typed_vdata_st data[1]; + unsigned int elements = 0; + + os_memset(data, 0, sizeof(data)); + if (!conn->global->server) { + data[elements].type = GNUTLS_DT_KEY_PURPOSE_OID; + data[elements].data = (void *) GNUTLS_KP_TLS_WWW_SERVER; + elements++; + } + res = gnutls_certificate_verify_peers(session, data, 1, + &status); + } +#else /* < 3.3.0 */ + res = gnutls_certificate_verify_peers2(session, &status); +#endif + if (res < 0) { wpa_printf(MSG_INFO, "TLS: Failed to verify peer " "certificate chain"); - *err = GNUTLS_A_INTERNAL_ERROR; - return -1; + err = GNUTLS_A_INTERNAL_ERROR; + goto out; + } + +#if GNUTLS_VERSION_NUMBER >= 0x030104 + { + gnutls_datum_t info; + int ret, type; + + type = gnutls_certificate_type_get(session); + ret = gnutls_certificate_verification_status_print(status, type, + &info, 0); + if (ret < 0) { + wpa_printf(MSG_DEBUG, + "GnuTLS: Failed to print verification status"); + err = GNUTLS_A_INTERNAL_ERROR; + goto out; + } + wpa_printf(MSG_DEBUG, "GnuTLS: %s", info.data); + gnutls_free(info.data); + } +#endif /* GnuTLS 3.1.4 or newer */ + + certs = gnutls_certificate_get_peers(session, &num_certs); + if (certs == NULL || num_certs == 0) { + wpa_printf(MSG_INFO, "TLS: No peer certificate chain received"); + err = GNUTLS_A_UNKNOWN_CA; + goto out; } if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) { @@ -886,51 +993,74 @@ static int tls_connection_verify_peer(struct tls_connection *conn, if (status & GNUTLS_CERT_INSECURE_ALGORITHM) { wpa_printf(MSG_INFO, "TLS: Certificate uses insecure " "algorithm"); - *err = GNUTLS_A_INSUFFICIENT_SECURITY; + gnutls_tls_fail_event(conn, NULL, 0, NULL, + "certificate uses insecure algorithm", + TLS_FAIL_BAD_CERTIFICATE); + err = GNUTLS_A_INSUFFICIENT_SECURITY; + goto out; } if (status & GNUTLS_CERT_NOT_ACTIVATED) { wpa_printf(MSG_INFO, "TLS: Certificate not yet " "activated"); - *err = GNUTLS_A_CERTIFICATE_EXPIRED; + gnutls_tls_fail_event(conn, NULL, 0, NULL, + "certificate not yet valid", + TLS_FAIL_NOT_YET_VALID); + err = GNUTLS_A_CERTIFICATE_EXPIRED; + goto out; } if (status & GNUTLS_CERT_EXPIRED) { wpa_printf(MSG_INFO, "TLS: Certificate expired"); - *err = GNUTLS_A_CERTIFICATE_EXPIRED; + gnutls_tls_fail_event(conn, NULL, 0, NULL, + "certificate has expired", + TLS_FAIL_EXPIRED); + err = GNUTLS_A_CERTIFICATE_EXPIRED; + goto out; } - return -1; + gnutls_tls_fail_event(conn, NULL, 0, NULL, + "untrusted certificate", + TLS_FAIL_UNTRUSTED); + err = GNUTLS_A_INTERNAL_ERROR; + goto out; } if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { wpa_printf(MSG_INFO, "TLS: Peer certificate does not have a " "known issuer"); - *err = GNUTLS_A_UNKNOWN_CA; - return -1; + gnutls_tls_fail_event(conn, NULL, 0, NULL, "signed not found", + TLS_FAIL_UNTRUSTED); + err = GNUTLS_A_UNKNOWN_CA; + goto out; } if (status & GNUTLS_CERT_REVOKED) { wpa_printf(MSG_INFO, "TLS: Peer certificate has been revoked"); - *err = GNUTLS_A_CERTIFICATE_REVOKED; - return -1; + gnutls_tls_fail_event(conn, NULL, 0, NULL, + "certificate revoked", + TLS_FAIL_REVOKED); + err = GNUTLS_A_CERTIFICATE_REVOKED; + goto out; } - os_get_time(&now); - - certs = gnutls_certificate_get_peers(conn->session, &num_certs); - if (certs == NULL) { - wpa_printf(MSG_INFO, "TLS: No peer certificate chain " - "received"); - *err = GNUTLS_A_UNKNOWN_CA; - return -1; + if (status != 0) { + wpa_printf(MSG_INFO, "TLS: Unknown verification status: %d", + status); + err = GNUTLS_A_INTERNAL_ERROR; + goto out; } + if (check_ocsp(conn, session, &err)) + goto out; + + os_get_time(&now); + for (i = 0; i < num_certs; i++) { char *buf; size_t len; if (gnutls_x509_crt_init(&cert) < 0) { wpa_printf(MSG_INFO, "TLS: Certificate initialization " "failed"); - *err = GNUTLS_A_BAD_CERTIFICATE; - return -1; + err = GNUTLS_A_BAD_CERTIFICATE; + goto out; } if (gnutls_x509_crt_import(cert, &certs[i], @@ -938,8 +1068,8 @@ static int tls_connection_verify_peer(struct tls_connection *conn, wpa_printf(MSG_INFO, "TLS: Could not parse peer " "certificate %d/%d", i + 1, num_certs); gnutls_x509_crt_deinit(cert); - *err = GNUTLS_A_BAD_CERTIFICATE; - return -1; + err = GNUTLS_A_BAD_CERTIFICATE; + goto out; } gnutls_x509_crt_get_dn(cert, NULL, &len); @@ -952,26 +1082,128 @@ static int tls_connection_verify_peer(struct tls_connection *conn, wpa_printf(MSG_DEBUG, "TLS: Peer cert chain %d/%d: %s", i + 1, num_certs, buf); - if (i == 0) { - /* TODO: validate subject_match and altsubject_match */ + if (conn->global->event_cb) { + struct wpabuf *cert_buf = NULL; + union tls_event_data ev; +#ifdef CONFIG_SHA256 + u8 hash[32]; + const u8 *_addr[1]; + size_t _len[1]; +#endif /* CONFIG_SHA256 */ + + os_memset(&ev, 0, sizeof(ev)); + if (conn->global->cert_in_cb) { + cert_buf = wpabuf_alloc_copy(certs[i].data, + certs[i].size); + ev.peer_cert.cert = cert_buf; + } +#ifdef CONFIG_SHA256 + _addr[0] = certs[i].data; + _len[0] = certs[i].size; + if (sha256_vector(1, _addr, _len, hash) == 0) { + ev.peer_cert.hash = hash; + ev.peer_cert.hash_len = sizeof(hash); + } +#endif /* CONFIG_SHA256 */ + ev.peer_cert.depth = i; + ev.peer_cert.subject = buf; + conn->global->event_cb(conn->global->cb_ctx, + TLS_PEER_CERTIFICATE, &ev); + wpabuf_free(cert_buf); } - os_free(buf); + if (i == 0) { + if (conn->suffix_match && + !gnutls_x509_crt_check_hostname( + cert, conn->suffix_match)) { + wpa_printf(MSG_WARNING, + "TLS: Domain suffix match '%s' not found", + conn->suffix_match); + gnutls_tls_fail_event( + conn, &certs[i], i, buf, + "Domain suffix mismatch", + TLS_FAIL_DOMAIN_SUFFIX_MISMATCH); + err = GNUTLS_A_BAD_CERTIFICATE; + gnutls_x509_crt_deinit(cert); + os_free(buf); + goto out; + } - if (gnutls_x509_crt_get_expiration_time(cert) < now.sec || - gnutls_x509_crt_get_activation_time(cert) > now.sec) { +#if GNUTLS_VERSION_NUMBER >= 0x030300 + if (conn->domain_match && + !gnutls_x509_crt_check_hostname2( + cert, conn->domain_match, + GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS)) { + wpa_printf(MSG_WARNING, + "TLS: Domain match '%s' not found", + conn->domain_match); + gnutls_tls_fail_event( + conn, &certs[i], i, buf, + "Domain mismatch", + TLS_FAIL_DOMAIN_MISMATCH); + err = GNUTLS_A_BAD_CERTIFICATE; + gnutls_x509_crt_deinit(cert); + os_free(buf); + goto out; + } +#endif /* >= 3.3.0 */ + + /* TODO: validate altsubject_match. + * For now, any such configuration is rejected in + * tls_connection_set_params() */ + +#if GNUTLS_VERSION_NUMBER < 0x030300 + /* + * gnutls_certificate_verify_peers() not available, so + * need to check EKU separately. + */ + if (!conn->global->server && + !server_eku_purpose(cert)) { + wpa_printf(MSG_WARNING, + "GnuTLS: No server EKU"); + gnutls_tls_fail_event( + conn, &certs[i], i, buf, + "No server EKU", + TLS_FAIL_BAD_CERTIFICATE); + err = GNUTLS_A_BAD_CERTIFICATE; + gnutls_x509_crt_deinit(cert); + os_free(buf); + goto out; + } +#endif /* < 3.3.0 */ + } + + if (!conn->disable_time_checks && + (gnutls_x509_crt_get_expiration_time(cert) < now.sec || + gnutls_x509_crt_get_activation_time(cert) > now.sec)) { wpa_printf(MSG_INFO, "TLS: Peer certificate %d/%d is " "not valid at this time", i + 1, num_certs); + gnutls_tls_fail_event( + conn, &certs[i], i, buf, + "Certificate is not valid at this time", + TLS_FAIL_EXPIRED); gnutls_x509_crt_deinit(cert); - *err = GNUTLS_A_CERTIFICATE_EXPIRED; - return -1; + os_free(buf); + err = GNUTLS_A_CERTIFICATE_EXPIRED; + goto out; } + os_free(buf); + gnutls_x509_crt_deinit(cert); } + if (conn->global->event_cb != NULL) + conn->global->event_cb(conn->global->cb_ctx, + TLS_CERT_CHAIN_SUCCESS, NULL); + return 0; + +out: + conn->failed++; + gnutls_alert_send(session, GNUTLS_AL_FATAL, err); + return GNUTLS_E_CERTIFICATE_ERROR; } @@ -988,7 +1220,7 @@ static struct wpabuf * gnutls_get_appl_data(struct tls_connection *conn) wpabuf_size(ad)); wpa_printf(MSG_DEBUG, "GnuTLS: gnutls_record_recv: %d", res); if (res < 0) { - wpa_printf(MSG_DEBUG, "%s - gnutls_ia_recv failed: %d " + wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d " "(%s)", __func__, (int) res, gnutls_strerror(res)); wpabuf_free(ad); @@ -1029,6 +1261,8 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx, ret = gnutls_handshake(conn->session); if (ret < 0) { + gnutls_alert_description_t alert; + switch (ret) { case GNUTLS_E_AGAIN: if (global->server && conn->established && @@ -1039,10 +1273,20 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx, } break; case GNUTLS_E_FATAL_ALERT_RECEIVED: + alert = gnutls_alert_get(conn->session); wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert", - __func__, gnutls_alert_get_name( - gnutls_alert_get(conn->session))); + __func__, gnutls_alert_get_name(alert)); conn->read_alerts++; + if (conn->global->event_cb != NULL) { + union tls_event_data ev; + + os_memset(&ev, 0, sizeof(ev)); + ev.alert.is_local = 0; + ev.alert.type = gnutls_alert_get_name(alert); + ev.alert.description = ev.alert.type; + conn->global->event_cb(conn->global->cb_ctx, + TLS_ALERT, &ev); + } /* continue */ default: wpa_printf(MSG_DEBUG, "%s - gnutls_handshake failed " @@ -1051,31 +1295,21 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx, } } else { size_t size; - gnutls_alert_description_t err; - if (conn->verify_peer && - tls_connection_verify_peer(conn, &err)) { - wpa_printf(MSG_INFO, "TLS: Peer certificate chain " - "failed validation"); - conn->failed++; - gnutls_alert_send(conn->session, GNUTLS_AL_FATAL, err); - goto out; - } + wpa_printf(MSG_DEBUG, "TLS: Handshake completed successfully"); -#ifdef CONFIG_GNUTLS_EXTRA - if (conn->tls_ia && !gnutls_ia_handshake_p(conn->session)) { - wpa_printf(MSG_INFO, "TLS: No TLS/IA negotiation"); - conn->failed++; - return NULL; - } -#endif /* CONFIG_GNUTLS_EXTRA */ +#if GNUTLS_VERSION_NUMBER >= 0x03010a + { + char *desc; - if (conn->tls_ia) - wpa_printf(MSG_DEBUG, "TLS: Start TLS/IA handshake"); - else { - wpa_printf(MSG_DEBUG, "TLS: Handshake completed " - "successfully"); + desc = gnutls_session_get_desc(conn->session); + if (desc) { + wpa_printf(MSG_DEBUG, "GnuTLS: %s", desc); + gnutls_free(desc); + } } +#endif /* GnuTLS 3.1.10 or newer */ + conn->established = 1; if (conn->push_buf == NULL) { /* Need to return something to get final TLS ACK. */ @@ -1099,7 +1333,6 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx, *appl_data = gnutls_get_appl_data(conn); } -out: out_data = conn->push_buf; conn->push_buf = NULL; return out_data; @@ -1122,12 +1355,6 @@ struct wpabuf * tls_connection_encrypt(void *tls_ctx, ssize_t res; struct wpabuf *buf; -#ifdef GNUTLS_IA - if (conn->tls_ia) - res = gnutls_ia_send(conn->session, wpabuf_head(in_data), - wpabuf_len(in_data)); - else -#endif /* GNUTLS_IA */ res = gnutls_record_send(conn->session, wpabuf_head(in_data), wpabuf_len(in_data)); if (res < 0) { @@ -1170,65 +1397,6 @@ struct wpabuf * tls_connection_decrypt(void *tls_ctx, if (out == NULL) return NULL; -#ifdef GNUTLS_IA - if (conn->tls_ia) { - res = gnutls_ia_recv(conn->session, wpabuf_mhead(out), - wpabuf_size(out)); - if (res == GNUTLS_E_WARNING_IA_IPHF_RECEIVED || - res == GNUTLS_E_WARNING_IA_FPHF_RECEIVED) { - int final = res == GNUTLS_E_WARNING_IA_FPHF_RECEIVED; - wpa_printf(MSG_DEBUG, "%s: Received %sPhaseFinished", - __func__, final ? "Final" : "Intermediate"); - - res = gnutls_ia_permute_inner_secret( - conn->session, conn->session_keys_len, - (char *) conn->session_keys); - if (conn->session_keys) { - os_memset(conn->session_keys, 0, - conn->session_keys_len); - os_free(conn->session_keys); - } - conn->session_keys = NULL; - conn->session_keys_len = 0; - if (res) { - wpa_printf(MSG_DEBUG, "%s: Failed to permute " - "inner secret: %s", - __func__, gnutls_strerror(res)); - wpabuf_free(out); - return NULL; - } - - res = gnutls_ia_verify_endphase(conn->session, - wpabuf_head(out)); - if (res == 0) { - wpa_printf(MSG_DEBUG, "%s: Correct endphase " - "checksum", __func__); - } else { - wpa_printf(MSG_INFO, "%s: Endphase " - "verification failed: %s", - __func__, gnutls_strerror(res)); - wpabuf_free(out); - return NULL; - } - - if (final) - conn->final_phase_finished = 1; - - return out; - } - - if (res < 0) { - wpa_printf(MSG_DEBUG, "%s - gnutls_ia_recv failed: %d " - "(%s)", __func__, (int) res, - gnutls_strerror(res)); - wpabuf_free(out); - return NULL; - } - wpabuf_put(out, res); - return out; - } -#endif /* GNUTLS_IA */ - res = gnutls_record_recv(conn->session, wpabuf_mhead(out), wpabuf_size(out)); if (res < 0) { @@ -1259,6 +1427,14 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, } +int tls_get_version(void *ssl_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + /* TODO */ + return -1; +} + + int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, char *buf, size_t buflen) { @@ -1309,149 +1485,39 @@ int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) } -int tls_connection_get_keyblock_size(void *tls_ctx, - struct tls_connection *conn) +int tls_connection_set_session_ticket_cb(void *tls_ctx, + struct tls_connection *conn, + tls_session_ticket_cb cb, void *ctx) { - /* TODO */ return -1; } -unsigned int tls_capabilities(void *tls_ctx) +int tls_get_library_version(char *buf, size_t buf_len) { - unsigned int capa = 0; - -#ifdef GNUTLS_IA - capa |= TLS_CAPABILITY_IA; -#endif /* GNUTLS_IA */ - - return capa; -} - - -int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn, - int tls_ia) -{ -#ifdef GNUTLS_IA - int ret; - - if (conn == NULL) - return -1; - - conn->tls_ia = tls_ia; - if (!tls_ia) - return 0; - - ret = gnutls_ia_allocate_server_credentials(&conn->iacred_srv); - if (ret) { - wpa_printf(MSG_DEBUG, "Failed to allocate IA credentials: %s", - gnutls_strerror(ret)); - return -1; - } - - ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_IA, - conn->iacred_srv); - if (ret) { - wpa_printf(MSG_DEBUG, "Failed to configure IA credentials: %s", - gnutls_strerror(ret)); - gnutls_ia_free_server_credentials(conn->iacred_srv); - conn->iacred_srv = NULL; - return -1; - } - - return 0; -#else /* GNUTLS_IA */ - return -1; -#endif /* GNUTLS_IA */ + return os_snprintf(buf, buf_len, "GnuTLS build=%s run=%s", + GNUTLS_VERSION, gnutls_check_version(NULL)); } -struct wpabuf * tls_connection_ia_send_phase_finished( - void *tls_ctx, struct tls_connection *conn, int final) +void tls_connection_set_success_data(struct tls_connection *conn, + struct wpabuf *data) { -#ifdef GNUTLS_IA - int ret; - struct wpabuf *buf; - - if (conn == NULL || conn->session == NULL || !conn->tls_ia) - return NULL; - - ret = gnutls_ia_permute_inner_secret(conn->session, - conn->session_keys_len, - (char *) conn->session_keys); - if (conn->session_keys) { - os_memset(conn->session_keys, 0, conn->session_keys_len); - os_free(conn->session_keys); - } - conn->session_keys = NULL; - conn->session_keys_len = 0; - if (ret) { - wpa_printf(MSG_DEBUG, "%s: Failed to permute inner secret: %s", - __func__, gnutls_strerror(ret)); - return NULL; - } - - ret = gnutls_ia_endphase_send(conn->session, final); - if (ret) { - wpa_printf(MSG_DEBUG, "%s: Failed to send endphase: %s", - __func__, gnutls_strerror(ret)); - return NULL; - } - - buf = conn->push_buf; - conn->push_buf = NULL; - return buf; -#else /* GNUTLS_IA */ - return NULL; -#endif /* GNUTLS_IA */ } -int tls_connection_ia_final_phase_finished(void *tls_ctx, - struct tls_connection *conn) +void tls_connection_set_success_data_resumed(struct tls_connection *conn) { - if (conn == NULL) - return -1; - - return conn->final_phase_finished; } -int tls_connection_ia_permute_inner_secret(void *tls_ctx, - struct tls_connection *conn, - const u8 *key, size_t key_len) +const struct wpabuf * +tls_connection_get_success_data(struct tls_connection *conn) { -#ifdef GNUTLS_IA - if (conn == NULL || !conn->tls_ia) - return -1; - - if (conn->session_keys) { - os_memset(conn->session_keys, 0, conn->session_keys_len); - os_free(conn->session_keys); - } - conn->session_keys_len = 0; - - if (key) { - conn->session_keys = os_malloc(key_len); - if (conn->session_keys == NULL) - return -1; - os_memcpy(conn->session_keys, key, key_len); - conn->session_keys_len = key_len; - } else { - conn->session_keys = NULL; - conn->session_keys_len = 0; - } - - return 0; -#else /* GNUTLS_IA */ - return -1; -#endif /* GNUTLS_IA */ + return NULL; } -int tls_connection_set_session_ticket_cb(void *tls_ctx, - struct tls_connection *conn, - tls_session_ticket_cb cb, void *ctx) +void tls_connection_remove_session(struct tls_connection *conn) { - return -1; }