summaryrefslogtreecommitdiffstats
path: root/contrib/wpa_supplicant/tls_gnutls.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/wpa_supplicant/tls_gnutls.c')
-rw-r--r--contrib/wpa_supplicant/tls_gnutls.c772
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 */
+}
OpenPOWER on IntegriCloud