diff options
Diffstat (limited to 'contrib/hostapd/tls_gnutls.c')
-rw-r--r-- | contrib/hostapd/tls_gnutls.c | 1370 |
1 files changed, 0 insertions, 1370 deletions
diff --git a/contrib/hostapd/tls_gnutls.c b/contrib/hostapd/tls_gnutls.c deleted file mode 100644 index 0789398..0000000 --- a/contrib/hostapd/tls_gnutls.c +++ /dev/null @@ -1,1370 +0,0 @@ -/* - * WPA Supplicant / SSL/TLS interface functions for openssl - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> - * - * 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. - */ - -#include "includes.h" -#include <gnutls/gnutls.h> -#include <gnutls/x509.h> -#ifdef PKCS12_FUNCS -#include <gnutls/pkcs12.h> -#endif /* PKCS12_FUNCS */ - -#ifdef CONFIG_GNUTLS_EXTRA -#if LIBGNUTLS_VERSION_NUMBER >= 0x010302 -#define GNUTLS_IA -#include <gnutls/extra.h> -#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 */ - -#include "common.h" -#include "tls.h" - - -#define TLS_RANDOM_SIZE 32 -#define TLS_MASTER_SIZE 48 - - -#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 { - /* Data for session resumption */ - void *session_data; - size_t session_data_size; - - int server; - - int params_set; - gnutls_certificate_credentials_t xcred; -}; - -struct tls_connection { - gnutls_session session; - char *subject_match, *altsubject_match; - int read_alerts, write_alerts, failed; - - u8 *pre_shared_secret; - size_t pre_shared_secret_len; - int established; - int verify_peer; - - u8 *push_buf, *pull_buf, *pull_buf_offset; - size_t push_buf_len, pull_buf_len; - - 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; - - /* 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 void tls_log_func(int level, const char *msg) -{ - char *s, *pos; - if (level == 6 || level == 7) { - /* These levels seem to be mostly I/O debug and msg dumps */ - return; - } - - s = os_strdup(msg); - if (s == NULL) - return; - - pos = s; - while (*pos != '\0') { - if (*pos == '\n') { - *pos = '\0'; - break; - } - pos++; - } - wpa_printf(level > 3 ? MSG_MSGDUMP : MSG_DEBUG, - "gnutls<%d> %s", level, s); - os_free(s); -} - - -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 */ - - global = os_zalloc(sizeof(*global)); - if (global == NULL) - return NULL; - - if (tls_gnutls_ref_count == 0 && gnutls_global_init() < 0) { - os_free(global); - return NULL; - } - 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); - return global; -} - - -void tls_deinit(void *ssl_ctx) -{ - struct tls_global *global = ssl_ctx; - if (global) { - if (global->params_set) - gnutls_certificate_free_credentials(global->xcred); - os_free(global->session_data); - os_free(global); - } - - tls_gnutls_ref_count--; - if (tls_gnutls_ref_count == 0) - gnutls_global_deinit(); -} - - -int tls_get_errors(void *ssl_ctx) -{ - return 0; -} - - -static ssize_t tls_pull_func(gnutls_transport_ptr ptr, void *buf, - size_t len) -{ - struct tls_connection *conn = (struct tls_connection *) ptr; - u8 *end; - if (conn->pull_buf == NULL) { - errno = EWOULDBLOCK; - return -1; - } - - end = conn->pull_buf + conn->pull_buf_len; - if ((size_t) (end - conn->pull_buf_offset) < len) - len = end - conn->pull_buf_offset; - os_memcpy(buf, conn->pull_buf_offset, len); - conn->pull_buf_offset += len; - if (conn->pull_buf_offset == end) { - wpa_printf(MSG_DEBUG, "%s - pull_buf consumed", __func__); - os_free(conn->pull_buf); - conn->pull_buf = conn->pull_buf_offset = NULL; - conn->pull_buf_len = 0; - } else { - wpa_printf(MSG_DEBUG, "%s - %d bytes remaining in pull_buf", - __func__, end - conn->pull_buf_offset); - } - return len; -} - - -static ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf, - size_t len) -{ - struct tls_connection *conn = (struct tls_connection *) ptr; - u8 *nbuf; - - nbuf = os_realloc(conn->push_buf, conn->push_buf_len + len); - if (nbuf == NULL) { - errno = ENOMEM; - return -1; - } - os_memcpy(nbuf + conn->push_buf_len, buf, len); - conn->push_buf = nbuf; - conn->push_buf_len += len; - - return len; -} - - -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 }; - int ret; - - ret = gnutls_init(&conn->session, - global->server ? GNUTLS_SERVER : GNUTLS_CLIENT); - if (ret < 0) { - wpa_printf(MSG_INFO, "TLS: Failed to initialize new TLS " - "connection: %s", gnutls_strerror(ret)); - return -1; - } - - ret = gnutls_set_default_priority(conn->session); - 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) - 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); - - return 0; - -fail: - wpa_printf(MSG_INFO, "TLS: Failed to setup new TLS connection: %s", - gnutls_strerror(ret)); - gnutls_deinit(conn->session); - return -1; -} - - -struct tls_connection * tls_connection_init(void *ssl_ctx) -{ - struct tls_global *global = ssl_ctx; - struct tls_connection *conn; - int ret; - - conn = os_zalloc(sizeof(*conn)); - if (conn == NULL) - return NULL; - - if (tls_gnutls_init_session(global, conn)) { - os_free(conn); - return NULL; - } - - if (global->params_set) { - ret = gnutls_credentials_set(conn->session, - GNUTLS_CRD_CERTIFICATE, - global->xcred); - if (ret < 0) { - wpa_printf(MSG_INFO, "Failed to configure " - "credentials: %s", gnutls_strerror(ret)); - os_free(conn); - return NULL; - } - } - - if (gnutls_certificate_allocate_credentials(&conn->xcred)) { - os_free(conn); - return NULL; - } - - return conn; -} - - -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); - os_free(conn->push_buf); - os_free(conn->pull_buf); - os_free(conn); -} - - -int tls_connection_established(void *ssl_ctx, struct tls_connection *conn) -{ - return conn ? conn->established : 0; -} - - -int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) -{ - struct tls_global *global = ssl_ctx; - int ret; - - if (conn == NULL) - return -1; - - /* Shutdown previous TLS connection without notifying the peer - * because the connection was already terminated in practice - * and "close notify" shutdown alert would confuse AS. */ - gnutls_bye(conn->session, GNUTLS_SHUT_RDWR); - os_free(conn->push_buf); - conn->push_buf = NULL; - conn->push_buf_len = 0; - 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)) { - wpa_printf(MSG_INFO, "GnuTLS: Failed to preparare new session " - "for session resumption use"); - return -1; - } - - ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, - conn->params_set ? conn->xcred : - global->xcred); - if (ret < 0) { - wpa_printf(MSG_INFO, "GnuTLS: Failed to configure credentials " - "for session resumption: %s", gnutls_strerror(ret)); - return -1; - } - - if (global->session_data) { - ret = gnutls_session_set_data(conn->session, - global->session_data, - global->session_data_size); - if (ret < 0) { - wpa_printf(MSG_INFO, "GnuTLS: Failed to set session " - "data: %s", gnutls_strerror(ret)); - return -1; - } - } - - return 0; -} - - -#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) -{ - int ret; - - 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; - } - - 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) - return -1; - } - - /* TODO: gnutls_certificate_set_verify_flags(xcred, flags); - * to force peer validation(?) */ - - if (params->ca_cert) { - conn->verify_peer = 1; - ret = gnutls_certificate_set_x509_trust_file( - conn->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' " - "in PEM format: %s", params->ca_cert, - gnutls_strerror(ret)); - ret = gnutls_certificate_set_x509_trust_file( - conn->xcred, params->ca_cert, - GNUTLS_X509_FMT_DER); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "Failed to read CA cert " - "'%s' in DER format: %s", - params->ca_cert, - gnutls_strerror(ret)); - return -1; - } - } - } - - if (params->client_cert && params->private_key) { - /* TODO: private_key_passwd? */ - ret = gnutls_certificate_set_x509_key_file( - conn->xcred, params->client_cert, params->private_key, - GNUTLS_X509_FMT_PEM); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "Failed to read client cert/key " - "in PEM format: %s", gnutls_strerror(ret)); - ret = gnutls_certificate_set_x509_key_file( - conn->xcred, params->client_cert, - params->private_key, GNUTLS_X509_FMT_DER); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "Failed to read client " - "cert/key in DER format: %s", - gnutls_strerror(ret)); - return ret; - } - } - } else if (params->private_key) { - 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); - if (ret != 0) { - wpa_printf(MSG_DEBUG, "Failed to load private_key in " - "PKCS#12 format: %s", gnutls_strerror(ret)); - return -1; - } else - pkcs12_ok = 1; -#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ -#endif /* PKCS12_FUNCS */ - - if (!pkcs12_ok) { - wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not " - "included"); - return -1; - } - } - - conn->tls_ia = params->tls_ia; - conn->params_set = 1; - - ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, - conn->xcred); - if (ret < 0) { - wpa_printf(MSG_INFO, "Failed to configure credentials: %s", - 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; -} - - -int tls_global_set_params(void *tls_ctx, - const struct tls_connection_params *params) -{ - struct tls_global *global = tls_ctx; - int ret; - - /* Currently, global parameters are only set when running in server - * mode. */ - global->server = 1; - - if (global->params_set) { - gnutls_certificate_free_credentials(global->xcred); - global->params_set = 0; - } - - ret = gnutls_certificate_allocate_credentials(&global->xcred); - if (ret) { - wpa_printf(MSG_DEBUG, "Failed to allocate global credentials " - "%s", gnutls_strerror(ret)); - return -1; - } - - if (params->ca_cert) { - ret = gnutls_certificate_set_x509_trust_file( - global->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' " - "in PEM format: %s", params->ca_cert, - gnutls_strerror(ret)); - ret = gnutls_certificate_set_x509_trust_file( - global->xcred, params->ca_cert, - GNUTLS_X509_FMT_DER); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "Failed to read CA cert " - "'%s' in DER format: %s", - params->ca_cert, - gnutls_strerror(ret)); - goto fail; - } - } - } - - if (params->client_cert && params->private_key) { - /* TODO: private_key_passwd? */ - ret = gnutls_certificate_set_x509_key_file( - global->xcred, params->client_cert, - params->private_key, GNUTLS_X509_FMT_PEM); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "Failed to read client cert/key " - "in PEM format: %s", gnutls_strerror(ret)); - ret = gnutls_certificate_set_x509_key_file( - global->xcred, params->client_cert, - params->private_key, GNUTLS_X509_FMT_DER); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "Failed to read client " - "cert/key in DER format: %s", - gnutls_strerror(ret)); - goto fail; - } - } - } else if (params->private_key) { - 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); - if (ret != 0) { - wpa_printf(MSG_DEBUG, "Failed to load private_key in " - "PKCS#12 format: %s", gnutls_strerror(ret)); - goto fail; - } else - pkcs12_ok = 1; -#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ -#endif /* PKCS12_FUNCS */ - - if (!pkcs12_ok) { - wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not " - "included"); - goto fail; - } - } - - global->params_set = 1; - - return 0; - -fail: - gnutls_certificate_free_credentials(global->xcred); - return -1; -} - - -int tls_global_set_verify(void *ssl_ctx, int check_crl) -{ - /* TODO */ - return 0; -} - - -int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, - int verify_peer) -{ - if (conn == NULL || conn->session == NULL) - return -1; - - conn->verify_peer = verify_peer; - gnutls_certificate_server_set_request(conn->session, - verify_peer ? GNUTLS_CERT_REQUIRE - : GNUTLS_CERT_REQUEST); - - return 0; -} - - -int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, - struct tls_keys *keys) -{ -#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK - security_parameters_st *sec; -#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ - - 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; - - return 0; -} - - -int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, - const char *label, int server_random_first, - u8 *out, size_t out_len) -{ -#if LIBGNUTLS_VERSION_NUMBER >= 0x010302 - if (conn == NULL || conn->session == NULL) - 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 */ - return -1; -#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ -} - - -static int tls_connection_verify_peer(struct tls_connection *conn) -{ - unsigned int status, num_certs, i; - struct os_time now; - const gnutls_datum_t *certs; - gnutls_x509_crt_t cert; - - if (gnutls_certificate_verify_peers2(conn->session, &status) < 0) { - wpa_printf(MSG_INFO, "TLS: Failed to verify peer " - "certificate chain"); - return -1; - } - - if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) { - wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted"); - return -1; - } - - if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { - wpa_printf(MSG_INFO, "TLS: Peer certificate does not have a " - "known issuer"); - return -1; - } - - if (status & GNUTLS_CERT_REVOKED) { - wpa_printf(MSG_INFO, "TLS: Peer certificate has been revoked"); - return -1; - } - - 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"); - return -1; - } - - 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"); - return -1; - } - - if (gnutls_x509_crt_import(cert, &certs[i], - GNUTLS_X509_FMT_DER) < 0) { - wpa_printf(MSG_INFO, "TLS: Could not parse peer " - "certificate %d/%d", i + 1, num_certs); - gnutls_x509_crt_deinit(cert); - return -1; - } - - gnutls_x509_crt_get_dn(cert, NULL, &len); - len++; - buf = os_malloc(len + 1); - if (buf) { - buf[0] = buf[len] = '\0'; - gnutls_x509_crt_get_dn(cert, buf, &len); - } - 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 */ - } - - os_free(buf); - - if (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_x509_crt_deinit(cert); - return -1; - } - - gnutls_x509_crt_deinit(cert); - } - - return 0; -} - - -u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len, u8 **appl_data, - size_t *appl_data_len) -{ - struct tls_global *global = ssl_ctx; - u8 *out_data; - int ret; - - if (appl_data) - *appl_data = NULL; - - if (in_data && in_len) { - if (conn->pull_buf) { - wpa_printf(MSG_DEBUG, "%s - %d bytes remaining in " - "pull_buf", __func__, conn->pull_buf_len); - os_free(conn->pull_buf); - } - conn->pull_buf = os_malloc(in_len); - if (conn->pull_buf == NULL) - return NULL; - os_memcpy(conn->pull_buf, in_data, in_len); - conn->pull_buf_offset = conn->pull_buf; - conn->pull_buf_len = in_len; - } - - ret = gnutls_handshake(conn->session); - if (ret < 0) { - switch (ret) { - case GNUTLS_E_AGAIN: - if (global->server && conn->established && - conn->push_buf == NULL) { - /* Need to return something to trigger - * completion of EAP-TLS. */ - conn->push_buf = os_malloc(1); - } - break; - case GNUTLS_E_FATAL_ALERT_RECEIVED: - wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert", - __func__, gnutls_alert_get_name( - gnutls_alert_get(conn->session))); - conn->read_alerts++; - /* continue */ - default: - wpa_printf(MSG_DEBUG, "%s - gnutls_handshake failed " - "-> %s", __func__, gnutls_strerror(ret)); - conn->failed++; - } - } else { - size_t size; - - if (conn->verify_peer && tls_connection_verify_peer(conn)) { - wpa_printf(MSG_INFO, "TLS: Peer certificate chain " - "failed validation"); - conn->failed++; - return NULL; - } - - if (conn->tls_ia && !gnutls_ia_handshake_p(conn->session)) { - wpa_printf(MSG_INFO, "TLS: No TLS/IA negotiation"); - conn->failed++; - return NULL; - } - - if (conn->tls_ia) - wpa_printf(MSG_DEBUG, "TLS: Start TLS/IA handshake"); - else { - wpa_printf(MSG_DEBUG, "TLS: Handshake completed " - "successfully"); - } - conn->established = 1; - if (conn->push_buf == NULL) { - /* Need to return something to get final TLS ACK. */ - conn->push_buf = os_malloc(1); - } - - gnutls_session_get_data(conn->session, NULL, &size); - if (global->session_data == NULL || - global->session_data_size < size) { - os_free(global->session_data); - global->session_data = os_malloc(size); - } - if (global->session_data) { - global->session_data_size = size; - gnutls_session_get_data(conn->session, - global->session_data, - &global->session_data_size); - } - } - - out_data = conn->push_buf; - *out_len = conn->push_buf_len; - conn->push_buf = NULL; - conn->push_buf_len = 0; - return out_data; -} - - -u8 * tls_connection_server_handshake(void *ssl_ctx, - struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len) -{ - return tls_connection_handshake(ssl_ctx, conn, in_data, in_len, - out_len, NULL, NULL); -} - - -int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len) -{ - ssize_t res; - -#ifdef GNUTLS_IA - if (conn->tls_ia) - res = gnutls_ia_send(conn->session, (char *) in_data, in_len); - else -#endif /* GNUTLS_IA */ - res = gnutls_record_send(conn->session, in_data, in_len); - if (res < 0) { - wpa_printf(MSG_INFO, "%s: Encryption failed: %s", - __func__, gnutls_strerror(res)); - return -1; - } - if (conn->push_buf == NULL) - return -1; - if (conn->push_buf_len < out_len) - out_len = conn->push_buf_len; - os_memcpy(out_data, conn->push_buf, out_len); - os_free(conn->push_buf); - conn->push_buf = NULL; - conn->push_buf_len = 0; - return out_len; -} - - -int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len) -{ - ssize_t res; - - if (conn->pull_buf) { - wpa_printf(MSG_DEBUG, "%s - %d bytes remaining in " - "pull_buf", __func__, conn->pull_buf_len); - os_free(conn->pull_buf); - } - conn->pull_buf = os_malloc(in_len); - if (conn->pull_buf == NULL) - return -1; - os_memcpy(conn->pull_buf, in_data, in_len); - conn->pull_buf_offset = conn->pull_buf; - conn->pull_buf_len = in_len; - -#ifdef GNUTLS_IA - if (conn->tls_ia) { - res = gnutls_ia_recv(conn->session, (char *) out_data, - out_len); - if (out_len >= 12 && - (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)); - return -1; - } - - res = gnutls_ia_verify_endphase(conn->session, - (char *) out_data); - 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)); - return -1; - } - - if (final) - conn->final_phase_finished = 1; - - return 0; - } - - if (res < 0) { - wpa_printf(MSG_DEBUG, "%s - gnutls_ia_recv failed: %d " - "(%s)", __func__, res, - gnutls_strerror(res)); - } - return res; - } -#endif /* GNUTLS_IA */ - - res = gnutls_record_recv(conn->session, out_data, out_len); - if (res < 0) { - wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d " - "(%s)", __func__, res, gnutls_strerror(res)); - } - - return res; -} - - -int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) -{ - if (conn == NULL) - return 0; - return gnutls_session_is_resumed(conn->session); -} - - -int tls_connection_set_master_key(void *ssl_ctx, struct tls_connection *conn, - const u8 *key, size_t key_len) -{ - /* TODO */ - return -1; -} - - -int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, - u8 *ciphers) -{ - /* TODO */ - return -1; -} - - -int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, - char *buf, size_t buflen) -{ - /* TODO */ - buf[0] = '\0'; - return 0; -} - - -int tls_connection_enable_workaround(void *ssl_ctx, - struct tls_connection *conn) -{ - /* TODO: set SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS */ - return 0; -} - - -int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, - int ext_type, const u8 *data, - size_t data_len) -{ - /* TODO */ - return -1; -} - - -int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) -{ - if (conn == NULL) - return -1; - return conn->failed; -} - - -int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn) -{ - if (conn == NULL) - return -1; - return conn->read_alerts; -} - - -int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) -{ - if (conn == NULL) - return -1; - return conn->write_alerts; -} - - -int tls_connection_get_keyblock_size(void *tls_ctx, - struct tls_connection *conn) -{ - /* TODO */ - return -1; -} - - -unsigned int tls_capabilities(void *tls_ctx) -{ - 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 */ -} - - -int tls_connection_ia_send_phase_finished(void *tls_ctx, - struct tls_connection *conn, - int final, - u8 *out_data, size_t out_len) -{ -#ifdef GNUTLS_IA - int ret; - - if (conn == NULL || conn->session == NULL || !conn->tls_ia) - return -1; - - 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 -1; - } - - 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 -1; - } - - if (conn->push_buf == NULL) - return -1; - if (conn->push_buf_len < out_len) - out_len = conn->push_buf_len; - os_memcpy(out_data, conn->push_buf, out_len); - os_free(conn->push_buf); - conn->push_buf = NULL; - conn->push_buf_len = 0; - return out_len; -#else /* GNUTLS_IA */ - return -1; -#endif /* GNUTLS_IA */ -} - - -int tls_connection_ia_final_phase_finished(void *tls_ctx, - 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) -{ -#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 */ -} |