diff options
Diffstat (limited to 'contrib/wpa_supplicant/tls_gnutls.c')
-rw-r--r-- | contrib/wpa_supplicant/tls_gnutls.c | 772 |
1 files changed, 675 insertions, 97 deletions
diff --git a/contrib/wpa_supplicant/tls_gnutls.c b/contrib/wpa_supplicant/tls_gnutls.c index 2bc9ed8..0789398 100644 --- a/contrib/wpa_supplicant/tls_gnutls.c +++ b/contrib/wpa_supplicant/tls_gnutls.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / SSL/TLS interface functions for openssl - * Copyright (c) 2004-2006, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -12,17 +12,43 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <errno.h> +#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 @@ -32,8 +58,6 @@ */ typedef u8 uint8; -#define TLS_RANDOM_SIZE 32 -#define TLS_MASTER_SIZE 48 typedef unsigned char opaque; typedef struct { uint8 suite[2]; @@ -59,9 +83,21 @@ 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; @@ -75,7 +111,23 @@ struct tls_connection { 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 */ }; @@ -87,7 +139,7 @@ static void tls_log_func(int level, const char *msg) return; } - s = strdup(msg); + s = os_strdup(msg); if (s == NULL) return; @@ -101,7 +153,7 @@ static void tls_log_func(int level, const char *msg) } wpa_printf(level > 3 ? MSG_MSGDUMP : MSG_DEBUG, "gnutls<%d> %s", level, s); - free(s); + os_free(s); } @@ -109,21 +161,36 @@ 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", NULL }; + 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) + 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) + 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) @@ -132,18 +199,28 @@ void * tls_init(const struct tls_config *conf) 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 (void *) 1; + 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(); @@ -167,13 +244,13 @@ static ssize_t tls_pull_func(gnutls_transport_ptr ptr, void *buf, } end = conn->pull_buf + conn->pull_buf_len; - if (end - conn->pull_buf_offset < len) + if ((size_t) (end - conn->pull_buf_offset) < len) len = end - conn->pull_buf_offset; - memcpy(buf, conn->pull_buf_offset, len); + 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__); - free(conn->pull_buf); + os_free(conn->pull_buf); conn->pull_buf = conn->pull_buf_offset = NULL; conn->pull_buf_len = 0; } else { @@ -190,12 +267,12 @@ static ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf, struct tls_connection *conn = (struct tls_connection *) ptr; u8 *nbuf; - nbuf = realloc(conn->push_buf, conn->push_buf_len + len); + nbuf = os_realloc(conn->push_buf, conn->push_buf_len + len); if (nbuf == NULL) { errno = ENOMEM; return -1; } - memcpy(nbuf + conn->push_buf_len, buf, len); + os_memcpy(nbuf + conn->push_buf_len, buf, len); conn->push_buf = nbuf; conn->push_buf_len += len; @@ -203,33 +280,78 @@ static ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf, } -struct tls_connection * tls_connection_init(void *ssl_ctx) +static int tls_gnutls_init_session(struct tls_global *global, + struct tls_connection *conn) { - struct tls_connection *conn; const int cert_types[2] = { GNUTLS_CRT_X509, 0 }; const int protos[2] = { GNUTLS_TLS1, 0 }; + int ret; - - conn = malloc(sizeof(*conn)); - if (conn == NULL) - return NULL; - memset(conn, 0, sizeof(*conn)); - if (gnutls_init(&conn->session, GNUTLS_CLIENT) < 0) { + 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"); - free(conn); - return NULL; + "connection: %s", gnutls_strerror(ret)); + return -1; } - gnutls_set_default_priority(conn->session); - gnutls_certificate_type_set_priority(conn->session, cert_types); - gnutls_protocol_set_priority(conn->session, protos); + 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); - gnutls_certificate_allocate_credentials(&conn->xcred); + 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; } @@ -239,14 +361,26 @@ 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); - free(conn->pre_shared_secret); - free(conn->subject_match); - free(conn->altsubject_match); - free(conn->push_buf); - free(conn->pull_buf); - free(conn); + 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); } @@ -258,6 +392,9 @@ int tls_connection_established(void *ssl_ctx, struct tls_connection *conn) int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) { + struct tls_global *global = ssl_ctx; + int ret; + if (conn == NULL) return -1; @@ -265,11 +402,46 @@ int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) * because the connection was already terminated in practice * and "close notify" shutdown alert would confuse AS. */ gnutls_bye(conn->session, GNUTLS_SHUT_RDWR); - free(conn->push_buf); + os_free(conn->push_buf); conn->push_buf = NULL; conn->push_buf_len = 0; conn->established = 0; - /* TODO: what to do trigger new handshake for re-auth? */ + 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; } @@ -309,15 +481,15 @@ static int tls_match_altsubject(X509 *cert, const char *match) wpa_printf(MSG_DEBUG, "TLS: altSubjectName: %s:%s", field, gen->d.ia5->data); - len = strlen(field) + 1 + strlen((char *) gen->d.ia5->data) + - 1; - tmp = malloc(len); + 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++; - free(tmp); + os_free(tmp); } return found; @@ -380,18 +552,18 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, if (conn == NULL || params == NULL) return -1; - free(conn->subject_match); + os_free(conn->subject_match); conn->subject_match = NULL; if (params->subject_match) { - conn->subject_match = strdup(params->subject_match); + conn->subject_match = os_strdup(params->subject_match); if (conn->subject_match == NULL) return -1; } - free(conn->altsubject_match); + os_free(conn->altsubject_match); conn->altsubject_match = NULL; if (params->altsubject_match) { - conn->altsubject_match = strdup(params->altsubject_match); + conn->altsubject_match = os_strdup(params->altsubject_match); if (conn->altsubject_match == NULL) return -1; } @@ -415,6 +587,7 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, "'%s' in DER format: %s", params->ca_cert, gnutls_strerror(ret)); + return -1; } } } @@ -437,8 +610,33 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, 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) { @@ -446,13 +644,122 @@ 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; } -int tls_global_ca_cert(void *_ssl_ctx, const char *ca_cert) +int tls_global_set_params(void *tls_ctx, + const struct tls_connection_params *params) { - /* TODO */ + 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; } @@ -460,62 +767,85 @@ int tls_global_ca_cert(void *_ssl_ctx, const char *ca_cert) int tls_global_set_verify(void *ssl_ctx, int check_crl) { /* TODO */ - return -1; + return 0; } int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, int verify_peer) { - if (conn == NULL) + if (conn == NULL || conn->session == NULL) return -1; - /* TODO */ - - return 1; -} - + conn->verify_peer = verify_peer; + gnutls_certificate_server_set_request(conn->session, + verify_peer ? GNUTLS_CERT_REQUIRE + : GNUTLS_CERT_REQUEST); -int tls_global_client_cert(void *_ssl_ctx, const char *client_cert) -{ - /* TODO */ - return -1; -} - - -int tls_global_private_key(void *_ssl_ctx, const char *private_key, - const char *private_key_passwd) -{ - /* TODO */ - return -1; + 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; - memset(keys, 0, sizeof(*keys)); + 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->client_random_len = TLS_RANDOM_SIZE; 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; - time_t now; + struct os_time now; const gnutls_datum_t *certs; gnutls_x509_crt_t cert; @@ -541,7 +871,7 @@ static int tls_connection_verify_peer(struct tls_connection *conn) return -1; } - now = time(NULL); + os_get_time(&now); certs = gnutls_certificate_get_peers(conn->session, &num_certs); if (certs == NULL) { @@ -569,7 +899,7 @@ static int tls_connection_verify_peer(struct tls_connection *conn) gnutls_x509_crt_get_dn(cert, NULL, &len); len++; - buf = malloc(len + 1); + buf = os_malloc(len + 1); if (buf) { buf[0] = buf[len] = '\0'; gnutls_x509_crt_get_dn(cert, buf, &len); @@ -581,10 +911,10 @@ static int tls_connection_verify_peer(struct tls_connection *conn) /* TODO: validate subject_match and altsubject_match */ } - free(buf); + os_free(buf); - if (gnutls_x509_crt_get_expiration_time(cert) < now || - gnutls_x509_crt_get_activation_time(cert) > now) { + 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); @@ -601,21 +931,26 @@ static int tls_connection_verify_peer(struct tls_connection *conn) u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, const u8 *in_data, size_t in_len, - size_t *out_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); - free(conn->pull_buf); + os_free(conn->pull_buf); } - conn->pull_buf = malloc(in_len); + conn->pull_buf = os_malloc(in_len); if (conn->pull_buf == NULL) return NULL; - memcpy(conn->pull_buf, in_data, in_len); + os_memcpy(conn->pull_buf, in_data, in_len); conn->pull_buf_offset = conn->pull_buf; conn->pull_buf_len = in_len; } @@ -624,6 +959,12 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, 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", @@ -637,17 +978,44 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, conn->failed++; } } else { - wpa_printf(MSG_DEBUG, "TLS: Handshake completed successfully"); + 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 = malloc(1); + 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); } } @@ -664,8 +1032,8 @@ u8 * tls_connection_server_handshake(void *ssl_ctx, const u8 *in_data, size_t in_len, size_t *out_len) { - /* TODO */ - return NULL; + return tls_connection_handshake(ssl_ctx, conn, in_data, in_len, + out_len, NULL, NULL); } @@ -674,13 +1042,24 @@ int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, 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; - memcpy(out_data, conn->push_buf, out_len); - free(conn->push_buf); + 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; @@ -696,15 +1075,70 @@ int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn, if (conn->pull_buf) { wpa_printf(MSG_DEBUG, "%s - %d bytes remaining in " "pull_buf", __func__, conn->pull_buf_len); - free(conn->pull_buf); + os_free(conn->pull_buf); } - conn->pull_buf = malloc(in_len); + conn->pull_buf = os_malloc(in_len); if (conn->pull_buf == NULL) return -1; - memcpy(conn->pull_buf, in_data, in_len); + 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 " @@ -723,19 +1157,18 @@ int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) } -#ifdef EAP_FAST int tls_connection_set_master_key(void *ssl_ctx, struct tls_connection *conn, const u8 *key, size_t key_len) { /* TODO */ return -1; } -#endif /* EAP_FAST */ -int tls_connection_set_anon_dh(void *ssl_ctx, struct tls_connection *conn) +int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, + u8 *ciphers) { - /* TODO: set ADH-AES128-SHA cipher */ + /* TODO */ return -1; } @@ -757,7 +1190,6 @@ int tls_connection_enable_workaround(void *ssl_ctx, } -#ifdef EAP_FAST int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, int ext_type, const u8 *data, size_t data_len) @@ -765,7 +1197,6 @@ int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, /* TODO */ return -1; } -#endif /* EAP_FAST */ int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) @@ -790,3 +1221,150 @@ int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) 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 */ +} |