1189251Ssam/*
2214734Srpaulo * SSL/TLS interface functions for GnuTLS
3252726Srpaulo * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
4189251Ssam *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7189251Ssam */
8189251Ssam
9189251Ssam#include "includes.h"
10189251Ssam#include <gnutls/gnutls.h>
11189251Ssam#include <gnutls/x509.h>
12189251Ssam#ifdef PKCS12_FUNCS
13189251Ssam#include <gnutls/pkcs12.h>
14189251Ssam#endif /* PKCS12_FUNCS */
15189251Ssam
16189251Ssam#include "common.h"
17189251Ssam#include "tls.h"
18189251Ssam
19189251Ssam
20252726Srpaulo#define WPA_TLS_RANDOM_SIZE 32
21252726Srpaulo#define WPA_TLS_MASTER_SIZE 48
22189251Ssam
23189251Ssam
24189251Ssam#if LIBGNUTLS_VERSION_NUMBER < 0x010302
25189251Ssam/* GnuTLS 1.3.2 added functions for using master secret. Older versions require
26189251Ssam * use of internal structures to get the master_secret and
27189251Ssam * {server,client}_random.
28189251Ssam */
29189251Ssam#define GNUTLS_INTERNAL_STRUCTURE_HACK
30189251Ssam#endif /* LIBGNUTLS_VERSION_NUMBER < 0x010302 */
31189251Ssam
32189251Ssam
33189251Ssam#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
34189251Ssam/*
35189251Ssam * It looks like gnutls does not provide access to client/server_random and
36189251Ssam * master_key. This is somewhat unfortunate since these are needed for key
37189251Ssam * derivation in EAP-{TLS,TTLS,PEAP,FAST}. Workaround for now is a horrible
38189251Ssam * hack that copies the gnutls_session_int definition from gnutls_int.h so that
39189251Ssam * we can get the needed information.
40189251Ssam */
41189251Ssam
42189251Ssamtypedef u8 uint8;
43189251Ssamtypedef unsigned char opaque;
44189251Ssamtypedef struct {
45189251Ssam    uint8 suite[2];
46189251Ssam} cipher_suite_st;
47189251Ssam
48189251Ssamtypedef struct {
49189251Ssam	gnutls_connection_end_t entity;
50189251Ssam	gnutls_kx_algorithm_t kx_algorithm;
51189251Ssam	gnutls_cipher_algorithm_t read_bulk_cipher_algorithm;
52189251Ssam	gnutls_mac_algorithm_t read_mac_algorithm;
53189251Ssam	gnutls_compression_method_t read_compression_algorithm;
54189251Ssam	gnutls_cipher_algorithm_t write_bulk_cipher_algorithm;
55189251Ssam	gnutls_mac_algorithm_t write_mac_algorithm;
56189251Ssam	gnutls_compression_method_t write_compression_algorithm;
57189251Ssam	cipher_suite_st current_cipher_suite;
58252726Srpaulo	opaque master_secret[WPA_TLS_MASTER_SIZE];
59252726Srpaulo	opaque client_random[WPA_TLS_RANDOM_SIZE];
60252726Srpaulo	opaque server_random[WPA_TLS_RANDOM_SIZE];
61189251Ssam	/* followed by stuff we are not interested in */
62189251Ssam} security_parameters_st;
63189251Ssam
64189251Ssamstruct gnutls_session_int {
65189251Ssam	security_parameters_st security_parameters;
66189251Ssam	/* followed by things we are not interested in */
67189251Ssam};
68189251Ssam#endif /* LIBGNUTLS_VERSION_NUMBER < 0x010302 */
69189251Ssam
70189251Ssamstatic int tls_gnutls_ref_count = 0;
71189251Ssam
72189251Ssamstruct tls_global {
73189251Ssam	/* Data for session resumption */
74189251Ssam	void *session_data;
75189251Ssam	size_t session_data_size;
76189251Ssam
77189251Ssam	int server;
78189251Ssam
79189251Ssam	int params_set;
80189251Ssam	gnutls_certificate_credentials_t xcred;
81189251Ssam};
82189251Ssam
83189251Ssamstruct tls_connection {
84189251Ssam	gnutls_session session;
85189251Ssam	char *subject_match, *altsubject_match;
86189251Ssam	int read_alerts, write_alerts, failed;
87189251Ssam
88189251Ssam	u8 *pre_shared_secret;
89189251Ssam	size_t pre_shared_secret_len;
90189251Ssam	int established;
91189251Ssam	int verify_peer;
92189251Ssam
93214734Srpaulo	struct wpabuf *push_buf;
94214734Srpaulo	struct wpabuf *pull_buf;
95214734Srpaulo	const u8 *pull_buf_offset;
96189251Ssam
97189251Ssam	int params_set;
98189251Ssam	gnutls_certificate_credentials_t xcred;
99189251Ssam};
100189251Ssam
101189251Ssam
102189251Ssamstatic void tls_log_func(int level, const char *msg)
103189251Ssam{
104189251Ssam	char *s, *pos;
105189251Ssam	if (level == 6 || level == 7) {
106189251Ssam		/* These levels seem to be mostly I/O debug and msg dumps */
107189251Ssam		return;
108189251Ssam	}
109189251Ssam
110189251Ssam	s = os_strdup(msg);
111189251Ssam	if (s == NULL)
112189251Ssam		return;
113189251Ssam
114189251Ssam	pos = s;
115189251Ssam	while (*pos != '\0') {
116189251Ssam		if (*pos == '\n') {
117189251Ssam			*pos = '\0';
118189251Ssam			break;
119189251Ssam		}
120189251Ssam		pos++;
121189251Ssam	}
122189251Ssam	wpa_printf(level > 3 ? MSG_MSGDUMP : MSG_DEBUG,
123189251Ssam		   "gnutls<%d> %s", level, s);
124189251Ssam	os_free(s);
125189251Ssam}
126189251Ssam
127189251Ssam
128189251Ssamextern int wpa_debug_show_keys;
129189251Ssam
130189251Ssamvoid * tls_init(const struct tls_config *conf)
131189251Ssam{
132189251Ssam	struct tls_global *global;
133189251Ssam
134189251Ssam#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
135189251Ssam	/* Because of the horrible hack to get master_secret and client/server
136189251Ssam	 * random, we need to make sure that the gnutls version is something
137189251Ssam	 * that is expected to have same structure definition for the session
138189251Ssam	 * data.. */
139189251Ssam	const char *ver;
140189251Ssam	const char *ok_ver[] = { "1.2.3", "1.2.4", "1.2.5", "1.2.6", "1.2.9",
141189251Ssam				 "1.3.2",
142189251Ssam				 NULL };
143189251Ssam	int i;
144189251Ssam#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
145189251Ssam
146189251Ssam	global = os_zalloc(sizeof(*global));
147189251Ssam	if (global == NULL)
148189251Ssam		return NULL;
149189251Ssam
150189251Ssam	if (tls_gnutls_ref_count == 0 && gnutls_global_init() < 0) {
151189251Ssam		os_free(global);
152189251Ssam		return NULL;
153189251Ssam	}
154189251Ssam	tls_gnutls_ref_count++;
155189251Ssam
156189251Ssam#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
157189251Ssam	ver = gnutls_check_version(NULL);
158189251Ssam	if (ver == NULL) {
159189251Ssam		tls_deinit(global);
160189251Ssam		return NULL;
161189251Ssam	}
162189251Ssam	wpa_printf(MSG_DEBUG, "%s - gnutls version %s", __func__, ver);
163189251Ssam	for (i = 0; ok_ver[i]; i++) {
164189251Ssam		if (strcmp(ok_ver[i], ver) == 0)
165189251Ssam			break;
166189251Ssam	}
167189251Ssam	if (ok_ver[i] == NULL) {
168189251Ssam		wpa_printf(MSG_INFO, "Untested gnutls version %s - this needs "
169189251Ssam			   "to be tested and enabled in tls_gnutls.c", ver);
170189251Ssam		tls_deinit(global);
171189251Ssam		return NULL;
172189251Ssam	}
173189251Ssam#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
174189251Ssam
175189251Ssam	gnutls_global_set_log_function(tls_log_func);
176189251Ssam	if (wpa_debug_show_keys)
177189251Ssam		gnutls_global_set_log_level(11);
178189251Ssam	return global;
179189251Ssam}
180189251Ssam
181189251Ssam
182189251Ssamvoid tls_deinit(void *ssl_ctx)
183189251Ssam{
184189251Ssam	struct tls_global *global = ssl_ctx;
185189251Ssam	if (global) {
186189251Ssam		if (global->params_set)
187189251Ssam			gnutls_certificate_free_credentials(global->xcred);
188189251Ssam		os_free(global->session_data);
189189251Ssam		os_free(global);
190189251Ssam	}
191189251Ssam
192189251Ssam	tls_gnutls_ref_count--;
193189251Ssam	if (tls_gnutls_ref_count == 0)
194189251Ssam		gnutls_global_deinit();
195189251Ssam}
196189251Ssam
197189251Ssam
198189251Ssamint tls_get_errors(void *ssl_ctx)
199189251Ssam{
200189251Ssam	return 0;
201189251Ssam}
202189251Ssam
203189251Ssam
204189251Ssamstatic ssize_t tls_pull_func(gnutls_transport_ptr ptr, void *buf,
205189251Ssam			     size_t len)
206189251Ssam{
207189251Ssam	struct tls_connection *conn = (struct tls_connection *) ptr;
208214734Srpaulo	const u8 *end;
209189251Ssam	if (conn->pull_buf == NULL) {
210189251Ssam		errno = EWOULDBLOCK;
211189251Ssam		return -1;
212189251Ssam	}
213189251Ssam
214214734Srpaulo	end = wpabuf_head_u8(conn->pull_buf) + wpabuf_len(conn->pull_buf);
215189251Ssam	if ((size_t) (end - conn->pull_buf_offset) < len)
216189251Ssam		len = end - conn->pull_buf_offset;
217189251Ssam	os_memcpy(buf, conn->pull_buf_offset, len);
218189251Ssam	conn->pull_buf_offset += len;
219189251Ssam	if (conn->pull_buf_offset == end) {
220189251Ssam		wpa_printf(MSG_DEBUG, "%s - pull_buf consumed", __func__);
221214734Srpaulo		wpabuf_free(conn->pull_buf);
222214734Srpaulo		conn->pull_buf = NULL;
223214734Srpaulo		conn->pull_buf_offset = NULL;
224189251Ssam	} else {
225189251Ssam		wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in pull_buf",
226189251Ssam			   __func__,
227189251Ssam			   (unsigned long) (end - conn->pull_buf_offset));
228189251Ssam	}
229189251Ssam	return len;
230189251Ssam}
231189251Ssam
232189251Ssam
233189251Ssamstatic ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf,
234189251Ssam			     size_t len)
235189251Ssam{
236189251Ssam	struct tls_connection *conn = (struct tls_connection *) ptr;
237189251Ssam
238214734Srpaulo	if (wpabuf_resize(&conn->push_buf, len) < 0) {
239189251Ssam		errno = ENOMEM;
240189251Ssam		return -1;
241189251Ssam	}
242214734Srpaulo	wpabuf_put_data(conn->push_buf, buf, len);
243189251Ssam
244189251Ssam	return len;
245189251Ssam}
246189251Ssam
247189251Ssam
248189251Ssamstatic int tls_gnutls_init_session(struct tls_global *global,
249189251Ssam				   struct tls_connection *conn)
250189251Ssam{
251252726Srpaulo#if LIBGNUTLS_VERSION_NUMBER >= 0x020200
252252726Srpaulo	const char *err;
253252726Srpaulo#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */
254189251Ssam	const int cert_types[2] = { GNUTLS_CRT_X509, 0 };
255189251Ssam	const int protos[2] = { GNUTLS_TLS1, 0 };
256252726Srpaulo#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */
257189251Ssam	int ret;
258189251Ssam
259189251Ssam	ret = gnutls_init(&conn->session,
260189251Ssam			  global->server ? GNUTLS_SERVER : GNUTLS_CLIENT);
261189251Ssam	if (ret < 0) {
262189251Ssam		wpa_printf(MSG_INFO, "TLS: Failed to initialize new TLS "
263189251Ssam			   "connection: %s", gnutls_strerror(ret));
264189251Ssam		return -1;
265189251Ssam	}
266189251Ssam
267189251Ssam	ret = gnutls_set_default_priority(conn->session);
268189251Ssam	if (ret < 0)
269189251Ssam		goto fail;
270189251Ssam
271252726Srpaulo#if LIBGNUTLS_VERSION_NUMBER >= 0x020200
272252726Srpaulo	ret = gnutls_priority_set_direct(conn->session, "NORMAL:-VERS-SSL3.0",
273252726Srpaulo					 &err);
274252726Srpaulo	if (ret < 0) {
275252726Srpaulo		wpa_printf(MSG_ERROR, "GnuTLS: Priority string failure at "
276252726Srpaulo			   "'%s'", err);
277252726Srpaulo		goto fail;
278252726Srpaulo	}
279252726Srpaulo#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */
280189251Ssam	ret = gnutls_certificate_type_set_priority(conn->session, cert_types);
281189251Ssam	if (ret < 0)
282189251Ssam		goto fail;
283189251Ssam
284189251Ssam	ret = gnutls_protocol_set_priority(conn->session, protos);
285189251Ssam	if (ret < 0)
286189251Ssam		goto fail;
287252726Srpaulo#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */
288189251Ssam
289189251Ssam	gnutls_transport_set_pull_function(conn->session, tls_pull_func);
290189251Ssam	gnutls_transport_set_push_function(conn->session, tls_push_func);
291189251Ssam	gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr) conn);
292189251Ssam
293189251Ssam	return 0;
294189251Ssam
295189251Ssamfail:
296189251Ssam	wpa_printf(MSG_INFO, "TLS: Failed to setup new TLS connection: %s",
297189251Ssam		   gnutls_strerror(ret));
298189251Ssam	gnutls_deinit(conn->session);
299189251Ssam	return -1;
300189251Ssam}
301189251Ssam
302189251Ssam
303189251Ssamstruct tls_connection * tls_connection_init(void *ssl_ctx)
304189251Ssam{
305189251Ssam	struct tls_global *global = ssl_ctx;
306189251Ssam	struct tls_connection *conn;
307189251Ssam	int ret;
308189251Ssam
309189251Ssam	conn = os_zalloc(sizeof(*conn));
310189251Ssam	if (conn == NULL)
311189251Ssam		return NULL;
312189251Ssam
313189251Ssam	if (tls_gnutls_init_session(global, conn)) {
314189251Ssam		os_free(conn);
315189251Ssam		return NULL;
316189251Ssam	}
317189251Ssam
318189251Ssam	if (global->params_set) {
319189251Ssam		ret = gnutls_credentials_set(conn->session,
320189251Ssam					     GNUTLS_CRD_CERTIFICATE,
321189251Ssam					     global->xcred);
322189251Ssam		if (ret < 0) {
323189251Ssam			wpa_printf(MSG_INFO, "Failed to configure "
324189251Ssam				   "credentials: %s", gnutls_strerror(ret));
325189251Ssam			os_free(conn);
326189251Ssam			return NULL;
327189251Ssam		}
328189251Ssam	}
329189251Ssam
330189251Ssam	if (gnutls_certificate_allocate_credentials(&conn->xcred)) {
331189251Ssam		os_free(conn);
332189251Ssam		return NULL;
333189251Ssam	}
334189251Ssam
335189251Ssam	return conn;
336189251Ssam}
337189251Ssam
338189251Ssam
339189251Ssamvoid tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn)
340189251Ssam{
341189251Ssam	if (conn == NULL)
342189251Ssam		return;
343189251Ssam
344189251Ssam	gnutls_certificate_free_credentials(conn->xcred);
345189251Ssam	gnutls_deinit(conn->session);
346189251Ssam	os_free(conn->pre_shared_secret);
347189251Ssam	os_free(conn->subject_match);
348189251Ssam	os_free(conn->altsubject_match);
349214734Srpaulo	wpabuf_free(conn->push_buf);
350214734Srpaulo	wpabuf_free(conn->pull_buf);
351189251Ssam	os_free(conn);
352189251Ssam}
353189251Ssam
354189251Ssam
355189251Ssamint tls_connection_established(void *ssl_ctx, struct tls_connection *conn)
356189251Ssam{
357189251Ssam	return conn ? conn->established : 0;
358189251Ssam}
359189251Ssam
360189251Ssam
361189251Ssamint tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
362189251Ssam{
363189251Ssam	struct tls_global *global = ssl_ctx;
364189251Ssam	int ret;
365189251Ssam
366189251Ssam	if (conn == NULL)
367189251Ssam		return -1;
368189251Ssam
369189251Ssam	/* Shutdown previous TLS connection without notifying the peer
370189251Ssam	 * because the connection was already terminated in practice
371189251Ssam	 * and "close notify" shutdown alert would confuse AS. */
372189251Ssam	gnutls_bye(conn->session, GNUTLS_SHUT_RDWR);
373214734Srpaulo	wpabuf_free(conn->push_buf);
374189251Ssam	conn->push_buf = NULL;
375189251Ssam	conn->established = 0;
376189251Ssam
377189251Ssam	gnutls_deinit(conn->session);
378189251Ssam	if (tls_gnutls_init_session(global, conn)) {
379189251Ssam		wpa_printf(MSG_INFO, "GnuTLS: Failed to preparare new session "
380189251Ssam			   "for session resumption use");
381189251Ssam		return -1;
382189251Ssam	}
383189251Ssam
384189251Ssam	ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE,
385189251Ssam				     conn->params_set ? conn->xcred :
386189251Ssam				     global->xcred);
387189251Ssam	if (ret < 0) {
388189251Ssam		wpa_printf(MSG_INFO, "GnuTLS: Failed to configure credentials "
389189251Ssam			   "for session resumption: %s", gnutls_strerror(ret));
390189251Ssam		return -1;
391189251Ssam	}
392189251Ssam
393189251Ssam	if (global->session_data) {
394189251Ssam		ret = gnutls_session_set_data(conn->session,
395189251Ssam					      global->session_data,
396189251Ssam					      global->session_data_size);
397189251Ssam		if (ret < 0) {
398189251Ssam			wpa_printf(MSG_INFO, "GnuTLS: Failed to set session "
399189251Ssam				   "data: %s", gnutls_strerror(ret));
400189251Ssam			return -1;
401189251Ssam		}
402189251Ssam	}
403189251Ssam
404189251Ssam	return 0;
405189251Ssam}
406189251Ssam
407189251Ssam
408189251Ssam#if 0
409189251Ssamstatic int tls_match_altsubject(X509 *cert, const char *match)
410189251Ssam{
411189251Ssam	GENERAL_NAME *gen;
412189251Ssam	char *field, *tmp;
413189251Ssam	void *ext;
414189251Ssam	int i, found = 0;
415189251Ssam	size_t len;
416189251Ssam
417189251Ssam	ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
418189251Ssam
419189251Ssam	for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
420189251Ssam		gen = sk_GENERAL_NAME_value(ext, i);
421189251Ssam		switch (gen->type) {
422189251Ssam		case GEN_EMAIL:
423189251Ssam			field = "EMAIL";
424189251Ssam			break;
425189251Ssam		case GEN_DNS:
426189251Ssam			field = "DNS";
427189251Ssam			break;
428189251Ssam		case GEN_URI:
429189251Ssam			field = "URI";
430189251Ssam			break;
431189251Ssam		default:
432189251Ssam			field = NULL;
433189251Ssam			wpa_printf(MSG_DEBUG, "TLS: altSubjectName: "
434189251Ssam				   "unsupported type=%d", gen->type);
435189251Ssam			break;
436189251Ssam		}
437189251Ssam
438189251Ssam		if (!field)
439189251Ssam			continue;
440189251Ssam
441189251Ssam		wpa_printf(MSG_DEBUG, "TLS: altSubjectName: %s:%s",
442189251Ssam			   field, gen->d.ia5->data);
443189251Ssam		len = os_strlen(field) + 1 +
444189251Ssam			strlen((char *) gen->d.ia5->data) + 1;
445189251Ssam		tmp = os_malloc(len);
446189251Ssam		if (tmp == NULL)
447189251Ssam			continue;
448189251Ssam		snprintf(tmp, len, "%s:%s", field, gen->d.ia5->data);
449189251Ssam		if (strstr(tmp, match))
450189251Ssam			found++;
451189251Ssam		os_free(tmp);
452189251Ssam	}
453189251Ssam
454189251Ssam	return found;
455189251Ssam}
456189251Ssam#endif
457189251Ssam
458189251Ssam
459189251Ssam#if 0
460189251Ssamstatic int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
461189251Ssam{
462189251Ssam	char buf[256];
463189251Ssam	X509 *err_cert;
464189251Ssam	int err, depth;
465189251Ssam	SSL *ssl;
466189251Ssam	struct tls_connection *conn;
467189251Ssam	char *match, *altmatch;
468189251Ssam
469189251Ssam	err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
470189251Ssam	err = X509_STORE_CTX_get_error(x509_ctx);
471189251Ssam	depth = X509_STORE_CTX_get_error_depth(x509_ctx);
472189251Ssam	ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
473189251Ssam					 SSL_get_ex_data_X509_STORE_CTX_idx());
474189251Ssam	X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
475189251Ssam
476189251Ssam	conn = SSL_get_app_data(ssl);
477189251Ssam	match = conn ? conn->subject_match : NULL;
478189251Ssam	altmatch = conn ? conn->altsubject_match : NULL;
479189251Ssam
480189251Ssam	if (!preverify_ok) {
481189251Ssam		wpa_printf(MSG_WARNING, "TLS: Certificate verification failed,"
482189251Ssam			   " error %d (%s) depth %d for '%s'", err,
483189251Ssam			   X509_verify_cert_error_string(err), depth, buf);
484189251Ssam	} else {
485189251Ssam		wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - "
486189251Ssam			   "preverify_ok=%d err=%d (%s) depth=%d buf='%s'",
487189251Ssam			   preverify_ok, err,
488189251Ssam			   X509_verify_cert_error_string(err), depth, buf);
489189251Ssam		if (depth == 0 && match && strstr(buf, match) == NULL) {
490189251Ssam			wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not "
491189251Ssam				   "match with '%s'", buf, match);
492189251Ssam			preverify_ok = 0;
493189251Ssam		} else if (depth == 0 && altmatch &&
494189251Ssam			   !tls_match_altsubject(err_cert, altmatch)) {
495189251Ssam			wpa_printf(MSG_WARNING, "TLS: altSubjectName match "
496189251Ssam				   "'%s' not found", altmatch);
497189251Ssam			preverify_ok = 0;
498189251Ssam		}
499189251Ssam	}
500189251Ssam
501189251Ssam	return preverify_ok;
502189251Ssam}
503189251Ssam#endif
504189251Ssam
505189251Ssam
506189251Ssamint tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
507189251Ssam			      const struct tls_connection_params *params)
508189251Ssam{
509189251Ssam	int ret;
510189251Ssam
511189251Ssam	if (conn == NULL || params == NULL)
512189251Ssam		return -1;
513189251Ssam
514189251Ssam	os_free(conn->subject_match);
515189251Ssam	conn->subject_match = NULL;
516189251Ssam	if (params->subject_match) {
517189251Ssam		conn->subject_match = os_strdup(params->subject_match);
518189251Ssam		if (conn->subject_match == NULL)
519189251Ssam			return -1;
520189251Ssam	}
521189251Ssam
522189251Ssam	os_free(conn->altsubject_match);
523189251Ssam	conn->altsubject_match = NULL;
524189251Ssam	if (params->altsubject_match) {
525189251Ssam		conn->altsubject_match = os_strdup(params->altsubject_match);
526189251Ssam		if (conn->altsubject_match == NULL)
527189251Ssam			return -1;
528189251Ssam	}
529189251Ssam
530189251Ssam	/* TODO: gnutls_certificate_set_verify_flags(xcred, flags);
531189251Ssam	 * to force peer validation(?) */
532189251Ssam
533189251Ssam	if (params->ca_cert) {
534189251Ssam		conn->verify_peer = 1;
535189251Ssam		ret = gnutls_certificate_set_x509_trust_file(
536189251Ssam			conn->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM);
537189251Ssam		if (ret < 0) {
538189251Ssam			wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' "
539189251Ssam				   "in PEM format: %s", params->ca_cert,
540189251Ssam				   gnutls_strerror(ret));
541189251Ssam			ret = gnutls_certificate_set_x509_trust_file(
542189251Ssam				conn->xcred, params->ca_cert,
543189251Ssam				GNUTLS_X509_FMT_DER);
544189251Ssam			if (ret < 0) {
545189251Ssam				wpa_printf(MSG_DEBUG, "Failed to read CA cert "
546189251Ssam					   "'%s' in DER format: %s",
547189251Ssam					   params->ca_cert,
548189251Ssam					   gnutls_strerror(ret));
549189251Ssam				return -1;
550189251Ssam			}
551189251Ssam		}
552209158Srpaulo
553209158Srpaulo		if (params->flags & TLS_CONN_ALLOW_SIGN_RSA_MD5) {
554209158Srpaulo			gnutls_certificate_set_verify_flags(
555209158Srpaulo				conn->xcred, GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
556209158Srpaulo		}
557209158Srpaulo
558252726Srpaulo#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
559209158Srpaulo		if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
560209158Srpaulo			gnutls_certificate_set_verify_flags(
561209158Srpaulo				conn->xcred,
562209158Srpaulo				GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
563209158Srpaulo		}
564252726Srpaulo#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
565189251Ssam	}
566189251Ssam
567189251Ssam	if (params->client_cert && params->private_key) {
568189251Ssam		/* TODO: private_key_passwd? */
569189251Ssam		ret = gnutls_certificate_set_x509_key_file(
570189251Ssam			conn->xcred, params->client_cert, params->private_key,
571189251Ssam			GNUTLS_X509_FMT_PEM);
572189251Ssam		if (ret < 0) {
573189251Ssam			wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
574189251Ssam				   "in PEM format: %s", gnutls_strerror(ret));
575189251Ssam			ret = gnutls_certificate_set_x509_key_file(
576189251Ssam				conn->xcred, params->client_cert,
577189251Ssam				params->private_key, GNUTLS_X509_FMT_DER);
578189251Ssam			if (ret < 0) {
579189251Ssam				wpa_printf(MSG_DEBUG, "Failed to read client "
580189251Ssam					   "cert/key in DER format: %s",
581189251Ssam					   gnutls_strerror(ret));
582189251Ssam				return ret;
583189251Ssam			}
584189251Ssam		}
585189251Ssam	} else if (params->private_key) {
586189251Ssam		int pkcs12_ok = 0;
587189251Ssam#ifdef PKCS12_FUNCS
588189251Ssam		/* Try to load in PKCS#12 format */
589189251Ssam#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
590189251Ssam		ret = gnutls_certificate_set_x509_simple_pkcs12_file(
591189251Ssam			conn->xcred, params->private_key, GNUTLS_X509_FMT_DER,
592189251Ssam			params->private_key_passwd);
593189251Ssam		if (ret != 0) {
594189251Ssam			wpa_printf(MSG_DEBUG, "Failed to load private_key in "
595189251Ssam				   "PKCS#12 format: %s", gnutls_strerror(ret));
596189251Ssam			return -1;
597189251Ssam		} else
598189251Ssam			pkcs12_ok = 1;
599189251Ssam#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
600189251Ssam#endif /* PKCS12_FUNCS */
601189251Ssam
602189251Ssam		if (!pkcs12_ok) {
603189251Ssam			wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not "
604189251Ssam				   "included");
605189251Ssam			return -1;
606189251Ssam		}
607189251Ssam	}
608189251Ssam
609189251Ssam	conn->params_set = 1;
610189251Ssam
611189251Ssam	ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE,
612189251Ssam				     conn->xcred);
613189251Ssam	if (ret < 0) {
614189251Ssam		wpa_printf(MSG_INFO, "Failed to configure credentials: %s",
615189251Ssam			   gnutls_strerror(ret));
616189251Ssam	}
617189251Ssam
618189251Ssam	return ret;
619189251Ssam}
620189251Ssam
621189251Ssam
622189251Ssamint tls_global_set_params(void *tls_ctx,
623189251Ssam			  const struct tls_connection_params *params)
624189251Ssam{
625189251Ssam	struct tls_global *global = tls_ctx;
626189251Ssam	int ret;
627189251Ssam
628189251Ssam	/* Currently, global parameters are only set when running in server
629189251Ssam	 * mode. */
630189251Ssam	global->server = 1;
631189251Ssam
632189251Ssam	if (global->params_set) {
633189251Ssam		gnutls_certificate_free_credentials(global->xcred);
634189251Ssam		global->params_set = 0;
635189251Ssam	}
636189251Ssam
637189251Ssam	ret = gnutls_certificate_allocate_credentials(&global->xcred);
638189251Ssam	if (ret) {
639189251Ssam		wpa_printf(MSG_DEBUG, "Failed to allocate global credentials "
640189251Ssam			   "%s", gnutls_strerror(ret));
641189251Ssam		return -1;
642189251Ssam	}
643189251Ssam
644189251Ssam	if (params->ca_cert) {
645189251Ssam		ret = gnutls_certificate_set_x509_trust_file(
646189251Ssam			global->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM);
647189251Ssam		if (ret < 0) {
648189251Ssam			wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' "
649189251Ssam				   "in PEM format: %s", params->ca_cert,
650189251Ssam				   gnutls_strerror(ret));
651189251Ssam			ret = gnutls_certificate_set_x509_trust_file(
652189251Ssam				global->xcred, params->ca_cert,
653189251Ssam				GNUTLS_X509_FMT_DER);
654189251Ssam			if (ret < 0) {
655189251Ssam				wpa_printf(MSG_DEBUG, "Failed to read CA cert "
656189251Ssam					   "'%s' in DER format: %s",
657189251Ssam					   params->ca_cert,
658189251Ssam					   gnutls_strerror(ret));
659189251Ssam				goto fail;
660189251Ssam			}
661189251Ssam		}
662209158Srpaulo
663209158Srpaulo		if (params->flags & TLS_CONN_ALLOW_SIGN_RSA_MD5) {
664209158Srpaulo			gnutls_certificate_set_verify_flags(
665209158Srpaulo				global->xcred,
666209158Srpaulo				GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
667209158Srpaulo		}
668209158Srpaulo
669252726Srpaulo#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
670209158Srpaulo		if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
671209158Srpaulo			gnutls_certificate_set_verify_flags(
672209158Srpaulo				global->xcred,
673209158Srpaulo				GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
674209158Srpaulo		}
675252726Srpaulo#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
676189251Ssam	}
677189251Ssam
678189251Ssam	if (params->client_cert && params->private_key) {
679189251Ssam		/* TODO: private_key_passwd? */
680189251Ssam		ret = gnutls_certificate_set_x509_key_file(
681189251Ssam			global->xcred, params->client_cert,
682189251Ssam			params->private_key, GNUTLS_X509_FMT_PEM);
683189251Ssam		if (ret < 0) {
684189251Ssam			wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
685189251Ssam				   "in PEM format: %s", gnutls_strerror(ret));
686189251Ssam			ret = gnutls_certificate_set_x509_key_file(
687189251Ssam				global->xcred, params->client_cert,
688189251Ssam				params->private_key, GNUTLS_X509_FMT_DER);
689189251Ssam			if (ret < 0) {
690189251Ssam				wpa_printf(MSG_DEBUG, "Failed to read client "
691189251Ssam					   "cert/key in DER format: %s",
692189251Ssam					   gnutls_strerror(ret));
693189251Ssam				goto fail;
694189251Ssam			}
695189251Ssam		}
696189251Ssam	} else if (params->private_key) {
697189251Ssam		int pkcs12_ok = 0;
698189251Ssam#ifdef PKCS12_FUNCS
699189251Ssam		/* Try to load in PKCS#12 format */
700189251Ssam#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
701189251Ssam		ret = gnutls_certificate_set_x509_simple_pkcs12_file(
702189251Ssam			global->xcred, params->private_key,
703189251Ssam			GNUTLS_X509_FMT_DER, params->private_key_passwd);
704189251Ssam		if (ret != 0) {
705189251Ssam			wpa_printf(MSG_DEBUG, "Failed to load private_key in "
706189251Ssam				   "PKCS#12 format: %s", gnutls_strerror(ret));
707189251Ssam			goto fail;
708189251Ssam		} else
709189251Ssam			pkcs12_ok = 1;
710189251Ssam#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
711189251Ssam#endif /* PKCS12_FUNCS */
712189251Ssam
713189251Ssam		if (!pkcs12_ok) {
714189251Ssam			wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not "
715189251Ssam				   "included");
716189251Ssam			goto fail;
717189251Ssam		}
718189251Ssam	}
719189251Ssam
720189251Ssam	global->params_set = 1;
721189251Ssam
722189251Ssam	return 0;
723189251Ssam
724189251Ssamfail:
725189251Ssam	gnutls_certificate_free_credentials(global->xcred);
726189251Ssam	return -1;
727189251Ssam}
728189251Ssam
729189251Ssam
730189251Ssamint tls_global_set_verify(void *ssl_ctx, int check_crl)
731189251Ssam{
732189251Ssam	/* TODO */
733189251Ssam	return 0;
734189251Ssam}
735189251Ssam
736189251Ssam
737189251Ssamint tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
738189251Ssam			      int verify_peer)
739189251Ssam{
740189251Ssam	if (conn == NULL || conn->session == NULL)
741189251Ssam		return -1;
742189251Ssam
743189251Ssam	conn->verify_peer = verify_peer;
744189251Ssam	gnutls_certificate_server_set_request(conn->session,
745189251Ssam					      verify_peer ? GNUTLS_CERT_REQUIRE
746189251Ssam					      : GNUTLS_CERT_REQUEST);
747189251Ssam
748189251Ssam	return 0;
749189251Ssam}
750189251Ssam
751189251Ssam
752189251Ssamint tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
753189251Ssam			    struct tls_keys *keys)
754189251Ssam{
755189251Ssam#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
756189251Ssam	security_parameters_st *sec;
757189251Ssam#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
758189251Ssam
759189251Ssam	if (conn == NULL || conn->session == NULL || keys == NULL)
760189251Ssam		return -1;
761189251Ssam
762189251Ssam	os_memset(keys, 0, sizeof(*keys));
763189251Ssam
764252726Srpaulo#if LIBGNUTLS_VERSION_NUMBER < 0x020c00
765189251Ssam#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
766189251Ssam	sec = &conn->session->security_parameters;
767189251Ssam	keys->master_key = sec->master_secret;
768252726Srpaulo	keys->master_key_len = WPA_TLS_MASTER_SIZE;
769189251Ssam	keys->client_random = sec->client_random;
770189251Ssam	keys->server_random = sec->server_random;
771189251Ssam#else /* GNUTLS_INTERNAL_STRUCTURE_HACK */
772189251Ssam	keys->client_random =
773189251Ssam		(u8 *) gnutls_session_get_client_random(conn->session);
774189251Ssam	keys->server_random =
775189251Ssam		(u8 *) gnutls_session_get_server_random(conn->session);
776189251Ssam	/* No access to master_secret */
777189251Ssam#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
778252726Srpaulo#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */
779189251Ssam
780252726Srpaulo#if LIBGNUTLS_VERSION_NUMBER < 0x020c00
781252726Srpaulo	keys->client_random_len = WPA_TLS_RANDOM_SIZE;
782252726Srpaulo	keys->server_random_len = WPA_TLS_RANDOM_SIZE;
783252726Srpaulo#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */
784189251Ssam
785189251Ssam	return 0;
786189251Ssam}
787189251Ssam
788189251Ssam
789189251Ssamint tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
790189251Ssam		       const char *label, int server_random_first,
791189251Ssam		       u8 *out, size_t out_len)
792189251Ssam{
793189251Ssam#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
794189251Ssam	if (conn == NULL || conn->session == NULL)
795189251Ssam		return -1;
796189251Ssam
797189251Ssam	return gnutls_prf(conn->session, os_strlen(label), label,
798189251Ssam			  server_random_first, 0, NULL, out_len, (char *) out);
799189251Ssam#else /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
800189251Ssam	return -1;
801189251Ssam#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
802189251Ssam}
803189251Ssam
804189251Ssam
805209158Srpaulostatic int tls_connection_verify_peer(struct tls_connection *conn,
806209158Srpaulo				      gnutls_alert_description_t *err)
807189251Ssam{
808189251Ssam	unsigned int status, num_certs, i;
809189251Ssam	struct os_time now;
810189251Ssam	const gnutls_datum_t *certs;
811189251Ssam	gnutls_x509_crt_t cert;
812189251Ssam
813189251Ssam	if (gnutls_certificate_verify_peers2(conn->session, &status) < 0) {
814189251Ssam		wpa_printf(MSG_INFO, "TLS: Failed to verify peer "
815189251Ssam			   "certificate chain");
816209158Srpaulo		*err = GNUTLS_A_INTERNAL_ERROR;
817189251Ssam		return -1;
818189251Ssam	}
819189251Ssam
820189251Ssam	if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) {
821189251Ssam		wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted");
822252726Srpaulo		*err = GNUTLS_A_INTERNAL_ERROR;
823209158Srpaulo		if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
824209158Srpaulo			wpa_printf(MSG_INFO, "TLS: Certificate uses insecure "
825209158Srpaulo				   "algorithm");
826209158Srpaulo			*err = GNUTLS_A_INSUFFICIENT_SECURITY;
827209158Srpaulo		}
828252726Srpaulo#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
829209158Srpaulo		if (status & GNUTLS_CERT_NOT_ACTIVATED) {
830209158Srpaulo			wpa_printf(MSG_INFO, "TLS: Certificate not yet "
831209158Srpaulo				   "activated");
832209158Srpaulo			*err = GNUTLS_A_CERTIFICATE_EXPIRED;
833209158Srpaulo		}
834209158Srpaulo		if (status & GNUTLS_CERT_EXPIRED) {
835209158Srpaulo			wpa_printf(MSG_INFO, "TLS: Certificate expired");
836209158Srpaulo			*err = GNUTLS_A_CERTIFICATE_EXPIRED;
837209158Srpaulo		}
838252726Srpaulo#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
839189251Ssam		return -1;
840189251Ssam	}
841189251Ssam
842189251Ssam	if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
843189251Ssam		wpa_printf(MSG_INFO, "TLS: Peer certificate does not have a "
844189251Ssam			   "known issuer");
845209158Srpaulo		*err = GNUTLS_A_UNKNOWN_CA;
846189251Ssam		return -1;
847189251Ssam	}
848189251Ssam
849189251Ssam	if (status & GNUTLS_CERT_REVOKED) {
850189251Ssam		wpa_printf(MSG_INFO, "TLS: Peer certificate has been revoked");
851209158Srpaulo		*err = GNUTLS_A_CERTIFICATE_REVOKED;
852189251Ssam		return -1;
853189251Ssam	}
854189251Ssam
855189251Ssam	os_get_time(&now);
856189251Ssam
857189251Ssam	certs = gnutls_certificate_get_peers(conn->session, &num_certs);
858189251Ssam	if (certs == NULL) {
859189251Ssam		wpa_printf(MSG_INFO, "TLS: No peer certificate chain "
860189251Ssam			   "received");
861209158Srpaulo		*err = GNUTLS_A_UNKNOWN_CA;
862189251Ssam		return -1;
863189251Ssam	}
864189251Ssam
865189251Ssam	for (i = 0; i < num_certs; i++) {
866189251Ssam		char *buf;
867189251Ssam		size_t len;
868189251Ssam		if (gnutls_x509_crt_init(&cert) < 0) {
869189251Ssam			wpa_printf(MSG_INFO, "TLS: Certificate initialization "
870189251Ssam				   "failed");
871209158Srpaulo			*err = GNUTLS_A_BAD_CERTIFICATE;
872189251Ssam			return -1;
873189251Ssam		}
874189251Ssam
875189251Ssam		if (gnutls_x509_crt_import(cert, &certs[i],
876189251Ssam					   GNUTLS_X509_FMT_DER) < 0) {
877189251Ssam			wpa_printf(MSG_INFO, "TLS: Could not parse peer "
878189251Ssam				   "certificate %d/%d", i + 1, num_certs);
879189251Ssam			gnutls_x509_crt_deinit(cert);
880209158Srpaulo			*err = GNUTLS_A_BAD_CERTIFICATE;
881189251Ssam			return -1;
882189251Ssam		}
883189251Ssam
884189251Ssam		gnutls_x509_crt_get_dn(cert, NULL, &len);
885189251Ssam		len++;
886189251Ssam		buf = os_malloc(len + 1);
887189251Ssam		if (buf) {
888189251Ssam			buf[0] = buf[len] = '\0';
889189251Ssam			gnutls_x509_crt_get_dn(cert, buf, &len);
890189251Ssam		}
891189251Ssam		wpa_printf(MSG_DEBUG, "TLS: Peer cert chain %d/%d: %s",
892189251Ssam			   i + 1, num_certs, buf);
893189251Ssam
894189251Ssam		if (i == 0) {
895189251Ssam			/* TODO: validate subject_match and altsubject_match */
896189251Ssam		}
897189251Ssam
898189251Ssam		os_free(buf);
899189251Ssam
900189251Ssam		if (gnutls_x509_crt_get_expiration_time(cert) < now.sec ||
901189251Ssam		    gnutls_x509_crt_get_activation_time(cert) > now.sec) {
902189251Ssam			wpa_printf(MSG_INFO, "TLS: Peer certificate %d/%d is "
903189251Ssam				   "not valid at this time",
904189251Ssam				   i + 1, num_certs);
905189251Ssam			gnutls_x509_crt_deinit(cert);
906209158Srpaulo			*err = GNUTLS_A_CERTIFICATE_EXPIRED;
907189251Ssam			return -1;
908189251Ssam		}
909189251Ssam
910189251Ssam		gnutls_x509_crt_deinit(cert);
911189251Ssam	}
912189251Ssam
913189251Ssam	return 0;
914189251Ssam}
915189251Ssam
916189251Ssam
917214734Srpaulostatic struct wpabuf * gnutls_get_appl_data(struct tls_connection *conn)
918189251Ssam{
919214734Srpaulo	int res;
920214734Srpaulo	struct wpabuf *ad;
921214734Srpaulo	wpa_printf(MSG_DEBUG, "GnuTLS: Check for possible Application Data");
922214734Srpaulo	ad = wpabuf_alloc((wpabuf_len(conn->pull_buf) + 500) * 3);
923214734Srpaulo	if (ad == NULL)
924214734Srpaulo		return NULL;
925214734Srpaulo
926214734Srpaulo	res = gnutls_record_recv(conn->session, wpabuf_mhead(ad),
927214734Srpaulo				 wpabuf_size(ad));
928214734Srpaulo	wpa_printf(MSG_DEBUG, "GnuTLS: gnutls_record_recv: %d", res);
929214734Srpaulo	if (res < 0) {
930252726Srpaulo		wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d "
931214734Srpaulo			   "(%s)", __func__, (int) res,
932214734Srpaulo			   gnutls_strerror(res));
933214734Srpaulo		wpabuf_free(ad);
934214734Srpaulo		return NULL;
935214734Srpaulo	}
936214734Srpaulo
937214734Srpaulo	wpabuf_put(ad, res);
938214734Srpaulo	wpa_printf(MSG_DEBUG, "GnuTLS: Received %d bytes of Application Data",
939214734Srpaulo		   res);
940214734Srpaulo	return ad;
941214734Srpaulo}
942214734Srpaulo
943214734Srpaulo
944214734Srpaulostruct wpabuf * tls_connection_handshake(void *tls_ctx,
945214734Srpaulo					 struct tls_connection *conn,
946214734Srpaulo					 const struct wpabuf *in_data,
947214734Srpaulo					 struct wpabuf **appl_data)
948214734Srpaulo{
949214734Srpaulo	struct tls_global *global = tls_ctx;
950214734Srpaulo	struct wpabuf *out_data;
951189251Ssam	int ret;
952189251Ssam
953189251Ssam	if (appl_data)
954189251Ssam		*appl_data = NULL;
955189251Ssam
956214734Srpaulo	if (in_data && wpabuf_len(in_data) > 0) {
957189251Ssam		if (conn->pull_buf) {
958189251Ssam			wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
959189251Ssam				   "pull_buf", __func__,
960214734Srpaulo				   (unsigned long) wpabuf_len(conn->pull_buf));
961214734Srpaulo			wpabuf_free(conn->pull_buf);
962189251Ssam		}
963214734Srpaulo		conn->pull_buf = wpabuf_dup(in_data);
964189251Ssam		if (conn->pull_buf == NULL)
965189251Ssam			return NULL;
966214734Srpaulo		conn->pull_buf_offset = wpabuf_head(conn->pull_buf);
967189251Ssam	}
968189251Ssam
969189251Ssam	ret = gnutls_handshake(conn->session);
970189251Ssam	if (ret < 0) {
971189251Ssam		switch (ret) {
972189251Ssam		case GNUTLS_E_AGAIN:
973189251Ssam			if (global->server && conn->established &&
974189251Ssam			    conn->push_buf == NULL) {
975189251Ssam				/* Need to return something to trigger
976189251Ssam				 * completion of EAP-TLS. */
977214734Srpaulo				conn->push_buf = wpabuf_alloc(0);
978189251Ssam			}
979189251Ssam			break;
980189251Ssam		case GNUTLS_E_FATAL_ALERT_RECEIVED:
981189251Ssam			wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert",
982189251Ssam				   __func__, gnutls_alert_get_name(
983189251Ssam					   gnutls_alert_get(conn->session)));
984189251Ssam			conn->read_alerts++;
985189251Ssam			/* continue */
986189251Ssam		default:
987189251Ssam			wpa_printf(MSG_DEBUG, "%s - gnutls_handshake failed "
988189251Ssam				   "-> %s", __func__, gnutls_strerror(ret));
989189251Ssam			conn->failed++;
990189251Ssam		}
991189251Ssam	} else {
992189251Ssam		size_t size;
993209158Srpaulo		gnutls_alert_description_t err;
994189251Ssam
995209158Srpaulo		if (conn->verify_peer &&
996209158Srpaulo		    tls_connection_verify_peer(conn, &err)) {
997189251Ssam			wpa_printf(MSG_INFO, "TLS: Peer certificate chain "
998189251Ssam				   "failed validation");
999189251Ssam			conn->failed++;
1000209158Srpaulo			gnutls_alert_send(conn->session, GNUTLS_AL_FATAL, err);
1001209158Srpaulo			goto out;
1002189251Ssam		}
1003189251Ssam
1004252726Srpaulo		wpa_printf(MSG_DEBUG, "TLS: Handshake completed successfully");
1005189251Ssam		conn->established = 1;
1006189251Ssam		if (conn->push_buf == NULL) {
1007189251Ssam			/* Need to return something to get final TLS ACK. */
1008214734Srpaulo			conn->push_buf = wpabuf_alloc(0);
1009189251Ssam		}
1010189251Ssam
1011189251Ssam		gnutls_session_get_data(conn->session, NULL, &size);
1012189251Ssam		if (global->session_data == NULL ||
1013189251Ssam		    global->session_data_size < size) {
1014189251Ssam			os_free(global->session_data);
1015189251Ssam			global->session_data = os_malloc(size);
1016189251Ssam		}
1017189251Ssam		if (global->session_data) {
1018189251Ssam			global->session_data_size = size;
1019189251Ssam			gnutls_session_get_data(conn->session,
1020189251Ssam						global->session_data,
1021189251Ssam						&global->session_data_size);
1022189251Ssam		}
1023214734Srpaulo
1024214734Srpaulo		if (conn->pull_buf && appl_data)
1025214734Srpaulo			*appl_data = gnutls_get_appl_data(conn);
1026189251Ssam	}
1027189251Ssam
1028209158Srpauloout:
1029189251Ssam	out_data = conn->push_buf;
1030189251Ssam	conn->push_buf = NULL;
1031189251Ssam	return out_data;
1032189251Ssam}
1033189251Ssam
1034189251Ssam
1035214734Srpaulostruct wpabuf * tls_connection_server_handshake(void *tls_ctx,
1036214734Srpaulo						struct tls_connection *conn,
1037214734Srpaulo						const struct wpabuf *in_data,
1038214734Srpaulo						struct wpabuf **appl_data)
1039189251Ssam{
1040214734Srpaulo	return tls_connection_handshake(tls_ctx, conn, in_data, appl_data);
1041189251Ssam}
1042189251Ssam
1043189251Ssam
1044214734Srpaulostruct wpabuf * tls_connection_encrypt(void *tls_ctx,
1045214734Srpaulo				       struct tls_connection *conn,
1046214734Srpaulo				       const struct wpabuf *in_data)
1047189251Ssam{
1048189251Ssam	ssize_t res;
1049214734Srpaulo	struct wpabuf *buf;
1050189251Ssam
1051214734Srpaulo	res = gnutls_record_send(conn->session, wpabuf_head(in_data),
1052214734Srpaulo				 wpabuf_len(in_data));
1053189251Ssam	if (res < 0) {
1054189251Ssam		wpa_printf(MSG_INFO, "%s: Encryption failed: %s",
1055189251Ssam			   __func__, gnutls_strerror(res));
1056214734Srpaulo		return NULL;
1057189251Ssam	}
1058214734Srpaulo
1059214734Srpaulo	buf = conn->push_buf;
1060189251Ssam	conn->push_buf = NULL;
1061214734Srpaulo	return buf;
1062189251Ssam}
1063189251Ssam
1064189251Ssam
1065214734Srpaulostruct wpabuf * tls_connection_decrypt(void *tls_ctx,
1066214734Srpaulo				       struct tls_connection *conn,
1067214734Srpaulo				       const struct wpabuf *in_data)
1068189251Ssam{
1069189251Ssam	ssize_t res;
1070214734Srpaulo	struct wpabuf *out;
1071189251Ssam
1072189251Ssam	if (conn->pull_buf) {
1073189251Ssam		wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
1074189251Ssam			   "pull_buf", __func__,
1075214734Srpaulo			   (unsigned long) wpabuf_len(conn->pull_buf));
1076214734Srpaulo		wpabuf_free(conn->pull_buf);
1077189251Ssam	}
1078214734Srpaulo	conn->pull_buf = wpabuf_dup(in_data);
1079189251Ssam	if (conn->pull_buf == NULL)
1080214734Srpaulo		return NULL;
1081214734Srpaulo	conn->pull_buf_offset = wpabuf_head(conn->pull_buf);
1082189251Ssam
1083214734Srpaulo	/*
1084214734Srpaulo	 * Even though we try to disable TLS compression, it is possible that
1085214734Srpaulo	 * this cannot be done with all TLS libraries. Add extra buffer space
1086214734Srpaulo	 * to handle the possibility of the decrypted data being longer than
1087214734Srpaulo	 * input data.
1088214734Srpaulo	 */
1089214734Srpaulo	out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
1090214734Srpaulo	if (out == NULL)
1091214734Srpaulo		return NULL;
1092214734Srpaulo
1093214734Srpaulo	res = gnutls_record_recv(conn->session, wpabuf_mhead(out),
1094214734Srpaulo				 wpabuf_size(out));
1095189251Ssam	if (res < 0) {
1096189251Ssam		wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d "
1097189251Ssam			   "(%s)", __func__, (int) res, gnutls_strerror(res));
1098214734Srpaulo		wpabuf_free(out);
1099214734Srpaulo		return NULL;
1100189251Ssam	}
1101214734Srpaulo	wpabuf_put(out, res);
1102189251Ssam
1103214734Srpaulo	return out;
1104189251Ssam}
1105189251Ssam
1106189251Ssam
1107189251Ssamint tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn)
1108189251Ssam{
1109189251Ssam	if (conn == NULL)
1110189251Ssam		return 0;
1111189251Ssam	return gnutls_session_is_resumed(conn->session);
1112189251Ssam}
1113189251Ssam
1114189251Ssam
1115189251Ssamint tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
1116189251Ssam				   u8 *ciphers)
1117189251Ssam{
1118189251Ssam	/* TODO */
1119189251Ssam	return -1;
1120189251Ssam}
1121189251Ssam
1122189251Ssam
1123189251Ssamint tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
1124189251Ssam		   char *buf, size_t buflen)
1125189251Ssam{
1126189251Ssam	/* TODO */
1127189251Ssam	buf[0] = '\0';
1128189251Ssam	return 0;
1129189251Ssam}
1130189251Ssam
1131189251Ssam
1132189251Ssamint tls_connection_enable_workaround(void *ssl_ctx,
1133189251Ssam				     struct tls_connection *conn)
1134189251Ssam{
1135214734Srpaulo	gnutls_record_disable_padding(conn->session);
1136189251Ssam	return 0;
1137189251Ssam}
1138189251Ssam
1139189251Ssam
1140189251Ssamint tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
1141189251Ssam				    int ext_type, const u8 *data,
1142189251Ssam				    size_t data_len)
1143189251Ssam{
1144189251Ssam	/* TODO */
1145189251Ssam	return -1;
1146189251Ssam}
1147189251Ssam
1148189251Ssam
1149189251Ssamint tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn)
1150189251Ssam{
1151189251Ssam	if (conn == NULL)
1152189251Ssam		return -1;
1153189251Ssam	return conn->failed;
1154189251Ssam}
1155189251Ssam
1156189251Ssam
1157189251Ssamint tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn)
1158189251Ssam{
1159189251Ssam	if (conn == NULL)
1160189251Ssam		return -1;
1161189251Ssam	return conn->read_alerts;
1162189251Ssam}
1163189251Ssam
1164189251Ssam
1165189251Ssamint tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn)
1166189251Ssam{
1167189251Ssam	if (conn == NULL)
1168189251Ssam		return -1;
1169189251Ssam	return conn->write_alerts;
1170189251Ssam}
1171189251Ssam
1172189251Ssam
1173189251Ssamint tls_connection_get_keyblock_size(void *tls_ctx,
1174189251Ssam				     struct tls_connection *conn)
1175189251Ssam{
1176189251Ssam	/* TODO */
1177189251Ssam	return -1;
1178189251Ssam}
1179189251Ssam
1180189251Ssam
1181189251Ssamunsigned int tls_capabilities(void *tls_ctx)
1182189251Ssam{
1183189251Ssam	return 0;
1184189251Ssam}
1185189251Ssam
1186189251Ssam
1187214734Srpauloint tls_connection_set_session_ticket_cb(void *tls_ctx,
1188214734Srpaulo					 struct tls_connection *conn,
1189214734Srpaulo					 tls_session_ticket_cb cb, void *ctx)
1190214734Srpaulo{
1191214734Srpaulo	return -1;
1192214734Srpaulo}
1193