blob: 20d0a31f37b70ccf8fad60eb96ac514767a8da50 [file] [log] [blame]
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001/*
2 * SSL/TLS interface functions for GnuTLS
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08003 * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004 *
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08005 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007 */
8
9#include "includes.h"
10#include <gnutls/gnutls.h>
11#include <gnutls/x509.h>
12#ifdef PKCS12_FUNCS
13#include <gnutls/pkcs12.h>
14#endif /* PKCS12_FUNCS */
15
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070016#include "common.h"
17#include "tls.h"
18
19
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -080020#define WPA_TLS_RANDOM_SIZE 32
21#define WPA_TLS_MASTER_SIZE 48
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070022
23
24#if LIBGNUTLS_VERSION_NUMBER < 0x010302
25/* GnuTLS 1.3.2 added functions for using master secret. Older versions require
26 * use of internal structures to get the master_secret and
27 * {server,client}_random.
28 */
29#define GNUTLS_INTERNAL_STRUCTURE_HACK
30#endif /* LIBGNUTLS_VERSION_NUMBER < 0x010302 */
31
32
33#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
34/*
35 * It looks like gnutls does not provide access to client/server_random and
36 * master_key. This is somewhat unfortunate since these are needed for key
37 * derivation in EAP-{TLS,TTLS,PEAP,FAST}. Workaround for now is a horrible
38 * hack that copies the gnutls_session_int definition from gnutls_int.h so that
39 * we can get the needed information.
40 */
41
42typedef u8 uint8;
43typedef unsigned char opaque;
44typedef struct {
45 uint8 suite[2];
46} cipher_suite_st;
47
48typedef struct {
49 gnutls_connection_end_t entity;
50 gnutls_kx_algorithm_t kx_algorithm;
51 gnutls_cipher_algorithm_t read_bulk_cipher_algorithm;
52 gnutls_mac_algorithm_t read_mac_algorithm;
53 gnutls_compression_method_t read_compression_algorithm;
54 gnutls_cipher_algorithm_t write_bulk_cipher_algorithm;
55 gnutls_mac_algorithm_t write_mac_algorithm;
56 gnutls_compression_method_t write_compression_algorithm;
57 cipher_suite_st current_cipher_suite;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -080058 opaque master_secret[WPA_TLS_MASTER_SIZE];
59 opaque client_random[WPA_TLS_RANDOM_SIZE];
60 opaque server_random[WPA_TLS_RANDOM_SIZE];
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070061 /* followed by stuff we are not interested in */
62} security_parameters_st;
63
64struct gnutls_session_int {
65 security_parameters_st security_parameters;
66 /* followed by things we are not interested in */
67};
68#endif /* LIBGNUTLS_VERSION_NUMBER < 0x010302 */
69
70static int tls_gnutls_ref_count = 0;
71
72struct tls_global {
73 /* Data for session resumption */
74 void *session_data;
75 size_t session_data_size;
76
77 int server;
78
79 int params_set;
80 gnutls_certificate_credentials_t xcred;
81};
82
83struct tls_connection {
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -080084 gnutls_session_t session;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070085 char *subject_match, *altsubject_match;
86 int read_alerts, write_alerts, failed;
87
88 u8 *pre_shared_secret;
89 size_t pre_shared_secret_len;
90 int established;
91 int verify_peer;
92
93 struct wpabuf *push_buf;
94 struct wpabuf *pull_buf;
95 const u8 *pull_buf_offset;
96
97 int params_set;
98 gnutls_certificate_credentials_t xcred;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070099};
100
101
102static void tls_log_func(int level, const char *msg)
103{
104 char *s, *pos;
105 if (level == 6 || level == 7) {
106 /* These levels seem to be mostly I/O debug and msg dumps */
107 return;
108 }
109
110 s = os_strdup(msg);
111 if (s == NULL)
112 return;
113
114 pos = s;
115 while (*pos != '\0') {
116 if (*pos == '\n') {
117 *pos = '\0';
118 break;
119 }
120 pos++;
121 }
122 wpa_printf(level > 3 ? MSG_MSGDUMP : MSG_DEBUG,
123 "gnutls<%d> %s", level, s);
124 os_free(s);
125}
126
127
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700128void * tls_init(const struct tls_config *conf)
129{
130 struct tls_global *global;
131
132#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
133 /* Because of the horrible hack to get master_secret and client/server
134 * random, we need to make sure that the gnutls version is something
135 * that is expected to have same structure definition for the session
136 * data.. */
137 const char *ver;
138 const char *ok_ver[] = { "1.2.3", "1.2.4", "1.2.5", "1.2.6", "1.2.9",
139 "1.3.2",
140 NULL };
141 int i;
142#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
143
144 global = os_zalloc(sizeof(*global));
145 if (global == NULL)
146 return NULL;
147
148 if (tls_gnutls_ref_count == 0 && gnutls_global_init() < 0) {
149 os_free(global);
150 return NULL;
151 }
152 tls_gnutls_ref_count++;
153
154#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
155 ver = gnutls_check_version(NULL);
156 if (ver == NULL) {
157 tls_deinit(global);
158 return NULL;
159 }
160 wpa_printf(MSG_DEBUG, "%s - gnutls version %s", __func__, ver);
161 for (i = 0; ok_ver[i]; i++) {
162 if (strcmp(ok_ver[i], ver) == 0)
163 break;
164 }
165 if (ok_ver[i] == NULL) {
166 wpa_printf(MSG_INFO, "Untested gnutls version %s - this needs "
167 "to be tested and enabled in tls_gnutls.c", ver);
168 tls_deinit(global);
169 return NULL;
170 }
171#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
172
173 gnutls_global_set_log_function(tls_log_func);
174 if (wpa_debug_show_keys)
175 gnutls_global_set_log_level(11);
176 return global;
177}
178
179
180void tls_deinit(void *ssl_ctx)
181{
182 struct tls_global *global = ssl_ctx;
183 if (global) {
184 if (global->params_set)
185 gnutls_certificate_free_credentials(global->xcred);
186 os_free(global->session_data);
187 os_free(global);
188 }
189
190 tls_gnutls_ref_count--;
191 if (tls_gnutls_ref_count == 0)
192 gnutls_global_deinit();
193}
194
195
196int tls_get_errors(void *ssl_ctx)
197{
198 return 0;
199}
200
201
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800202static ssize_t tls_pull_func(gnutls_transport_ptr_t ptr, void *buf,
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700203 size_t len)
204{
205 struct tls_connection *conn = (struct tls_connection *) ptr;
206 const u8 *end;
207 if (conn->pull_buf == NULL) {
208 errno = EWOULDBLOCK;
209 return -1;
210 }
211
212 end = wpabuf_head_u8(conn->pull_buf) + wpabuf_len(conn->pull_buf);
213 if ((size_t) (end - conn->pull_buf_offset) < len)
214 len = end - conn->pull_buf_offset;
215 os_memcpy(buf, conn->pull_buf_offset, len);
216 conn->pull_buf_offset += len;
217 if (conn->pull_buf_offset == end) {
218 wpa_printf(MSG_DEBUG, "%s - pull_buf consumed", __func__);
219 wpabuf_free(conn->pull_buf);
220 conn->pull_buf = NULL;
221 conn->pull_buf_offset = NULL;
222 } else {
223 wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in pull_buf",
224 __func__,
225 (unsigned long) (end - conn->pull_buf_offset));
226 }
227 return len;
228}
229
230
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800231static ssize_t tls_push_func(gnutls_transport_ptr_t ptr, const void *buf,
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700232 size_t len)
233{
234 struct tls_connection *conn = (struct tls_connection *) ptr;
235
236 if (wpabuf_resize(&conn->push_buf, len) < 0) {
237 errno = ENOMEM;
238 return -1;
239 }
240 wpabuf_put_data(conn->push_buf, buf, len);
241
242 return len;
243}
244
245
246static int tls_gnutls_init_session(struct tls_global *global,
247 struct tls_connection *conn)
248{
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800249#if LIBGNUTLS_VERSION_NUMBER >= 0x020200
250 const char *err;
251#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700252 const int cert_types[2] = { GNUTLS_CRT_X509, 0 };
253 const int protos[2] = { GNUTLS_TLS1, 0 };
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800254#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700255 int ret;
256
257 ret = gnutls_init(&conn->session,
258 global->server ? GNUTLS_SERVER : GNUTLS_CLIENT);
259 if (ret < 0) {
260 wpa_printf(MSG_INFO, "TLS: Failed to initialize new TLS "
261 "connection: %s", gnutls_strerror(ret));
262 return -1;
263 }
264
265 ret = gnutls_set_default_priority(conn->session);
266 if (ret < 0)
267 goto fail;
268
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800269#if LIBGNUTLS_VERSION_NUMBER >= 0x020200
270 ret = gnutls_priority_set_direct(conn->session, "NORMAL:-VERS-SSL3.0",
271 &err);
272 if (ret < 0) {
273 wpa_printf(MSG_ERROR, "GnuTLS: Priority string failure at "
274 "'%s'", err);
275 goto fail;
276 }
277#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700278 ret = gnutls_certificate_type_set_priority(conn->session, cert_types);
279 if (ret < 0)
280 goto fail;
281
282 ret = gnutls_protocol_set_priority(conn->session, protos);
283 if (ret < 0)
284 goto fail;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800285#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700286
287 gnutls_transport_set_pull_function(conn->session, tls_pull_func);
288 gnutls_transport_set_push_function(conn->session, tls_push_func);
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800289 gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr_t) conn);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700290
291 return 0;
292
293fail:
294 wpa_printf(MSG_INFO, "TLS: Failed to setup new TLS connection: %s",
295 gnutls_strerror(ret));
296 gnutls_deinit(conn->session);
297 return -1;
298}
299
300
301struct tls_connection * tls_connection_init(void *ssl_ctx)
302{
303 struct tls_global *global = ssl_ctx;
304 struct tls_connection *conn;
305 int ret;
306
307 conn = os_zalloc(sizeof(*conn));
308 if (conn == NULL)
309 return NULL;
310
311 if (tls_gnutls_init_session(global, conn)) {
312 os_free(conn);
313 return NULL;
314 }
315
316 if (global->params_set) {
317 ret = gnutls_credentials_set(conn->session,
318 GNUTLS_CRD_CERTIFICATE,
319 global->xcred);
320 if (ret < 0) {
321 wpa_printf(MSG_INFO, "Failed to configure "
322 "credentials: %s", gnutls_strerror(ret));
323 os_free(conn);
324 return NULL;
325 }
326 }
327
328 if (gnutls_certificate_allocate_credentials(&conn->xcred)) {
329 os_free(conn);
330 return NULL;
331 }
332
333 return conn;
334}
335
336
337void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn)
338{
339 if (conn == NULL)
340 return;
341
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700342 gnutls_certificate_free_credentials(conn->xcred);
343 gnutls_deinit(conn->session);
344 os_free(conn->pre_shared_secret);
345 os_free(conn->subject_match);
346 os_free(conn->altsubject_match);
347 wpabuf_free(conn->push_buf);
348 wpabuf_free(conn->pull_buf);
349 os_free(conn);
350}
351
352
353int tls_connection_established(void *ssl_ctx, struct tls_connection *conn)
354{
355 return conn ? conn->established : 0;
356}
357
358
359int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
360{
361 struct tls_global *global = ssl_ctx;
362 int ret;
363
364 if (conn == NULL)
365 return -1;
366
367 /* Shutdown previous TLS connection without notifying the peer
368 * because the connection was already terminated in practice
369 * and "close notify" shutdown alert would confuse AS. */
370 gnutls_bye(conn->session, GNUTLS_SHUT_RDWR);
371 wpabuf_free(conn->push_buf);
372 conn->push_buf = NULL;
373 conn->established = 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700374
375 gnutls_deinit(conn->session);
376 if (tls_gnutls_init_session(global, conn)) {
377 wpa_printf(MSG_INFO, "GnuTLS: Failed to preparare new session "
378 "for session resumption use");
379 return -1;
380 }
381
382 ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE,
383 conn->params_set ? conn->xcred :
384 global->xcred);
385 if (ret < 0) {
386 wpa_printf(MSG_INFO, "GnuTLS: Failed to configure credentials "
387 "for session resumption: %s", gnutls_strerror(ret));
388 return -1;
389 }
390
391 if (global->session_data) {
392 ret = gnutls_session_set_data(conn->session,
393 global->session_data,
394 global->session_data_size);
395 if (ret < 0) {
396 wpa_printf(MSG_INFO, "GnuTLS: Failed to set session "
397 "data: %s", gnutls_strerror(ret));
398 return -1;
399 }
400 }
401
402 return 0;
403}
404
405
406#if 0
407static int tls_match_altsubject(X509 *cert, const char *match)
408{
409 GENERAL_NAME *gen;
410 char *field, *tmp;
411 void *ext;
412 int i, found = 0;
413 size_t len;
414
415 ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
416
417 for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
418 gen = sk_GENERAL_NAME_value(ext, i);
419 switch (gen->type) {
420 case GEN_EMAIL:
421 field = "EMAIL";
422 break;
423 case GEN_DNS:
424 field = "DNS";
425 break;
426 case GEN_URI:
427 field = "URI";
428 break;
429 default:
430 field = NULL;
431 wpa_printf(MSG_DEBUG, "TLS: altSubjectName: "
432 "unsupported type=%d", gen->type);
433 break;
434 }
435
436 if (!field)
437 continue;
438
439 wpa_printf(MSG_DEBUG, "TLS: altSubjectName: %s:%s",
440 field, gen->d.ia5->data);
441 len = os_strlen(field) + 1 +
442 strlen((char *) gen->d.ia5->data) + 1;
443 tmp = os_malloc(len);
444 if (tmp == NULL)
445 continue;
446 snprintf(tmp, len, "%s:%s", field, gen->d.ia5->data);
447 if (strstr(tmp, match))
448 found++;
449 os_free(tmp);
450 }
451
452 return found;
453}
454#endif
455
456
457#if 0
458static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
459{
460 char buf[256];
461 X509 *err_cert;
462 int err, depth;
463 SSL *ssl;
464 struct tls_connection *conn;
465 char *match, *altmatch;
466
467 err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
468 err = X509_STORE_CTX_get_error(x509_ctx);
469 depth = X509_STORE_CTX_get_error_depth(x509_ctx);
470 ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
471 SSL_get_ex_data_X509_STORE_CTX_idx());
472 X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
473
474 conn = SSL_get_app_data(ssl);
475 match = conn ? conn->subject_match : NULL;
476 altmatch = conn ? conn->altsubject_match : NULL;
477
478 if (!preverify_ok) {
479 wpa_printf(MSG_WARNING, "TLS: Certificate verification failed,"
480 " error %d (%s) depth %d for '%s'", err,
481 X509_verify_cert_error_string(err), depth, buf);
482 } else {
483 wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - "
484 "preverify_ok=%d err=%d (%s) depth=%d buf='%s'",
485 preverify_ok, err,
486 X509_verify_cert_error_string(err), depth, buf);
487 if (depth == 0 && match && strstr(buf, match) == NULL) {
488 wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not "
489 "match with '%s'", buf, match);
490 preverify_ok = 0;
491 } else if (depth == 0 && altmatch &&
492 !tls_match_altsubject(err_cert, altmatch)) {
493 wpa_printf(MSG_WARNING, "TLS: altSubjectName match "
494 "'%s' not found", altmatch);
495 preverify_ok = 0;
496 }
497 }
498
499 return preverify_ok;
500}
501#endif
502
503
504int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
505 const struct tls_connection_params *params)
506{
507 int ret;
508
509 if (conn == NULL || params == NULL)
510 return -1;
511
512 os_free(conn->subject_match);
513 conn->subject_match = NULL;
514 if (params->subject_match) {
515 conn->subject_match = os_strdup(params->subject_match);
516 if (conn->subject_match == NULL)
517 return -1;
518 }
519
520 os_free(conn->altsubject_match);
521 conn->altsubject_match = NULL;
522 if (params->altsubject_match) {
523 conn->altsubject_match = os_strdup(params->altsubject_match);
524 if (conn->altsubject_match == NULL)
525 return -1;
526 }
527
528 /* TODO: gnutls_certificate_set_verify_flags(xcred, flags);
529 * to force peer validation(?) */
530
531 if (params->ca_cert) {
532 conn->verify_peer = 1;
533 ret = gnutls_certificate_set_x509_trust_file(
534 conn->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM);
535 if (ret < 0) {
536 wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' "
537 "in PEM format: %s", params->ca_cert,
538 gnutls_strerror(ret));
539 ret = gnutls_certificate_set_x509_trust_file(
540 conn->xcred, params->ca_cert,
541 GNUTLS_X509_FMT_DER);
542 if (ret < 0) {
543 wpa_printf(MSG_DEBUG, "Failed to read CA cert "
544 "'%s' in DER format: %s",
545 params->ca_cert,
546 gnutls_strerror(ret));
547 return -1;
548 }
549 }
550
551 if (params->flags & TLS_CONN_ALLOW_SIGN_RSA_MD5) {
552 gnutls_certificate_set_verify_flags(
553 conn->xcred, GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
554 }
555
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800556#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700557 if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
558 gnutls_certificate_set_verify_flags(
559 conn->xcred,
560 GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
561 }
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800562#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700563 }
564
565 if (params->client_cert && params->private_key) {
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800566#if GNUTLS_VERSION_NUMBER >= 0x03010b
567 ret = gnutls_certificate_set_x509_key_file2(
568 conn->xcred, params->client_cert, params->private_key,
569 GNUTLS_X509_FMT_PEM, params->private_key_passwd, 0);
570#else
571 /* private_key_passwd not (easily) supported here */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700572 ret = gnutls_certificate_set_x509_key_file(
573 conn->xcred, params->client_cert, params->private_key,
574 GNUTLS_X509_FMT_PEM);
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800575#endif
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700576 if (ret < 0) {
577 wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
578 "in PEM format: %s", gnutls_strerror(ret));
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800579#if GNUTLS_VERSION_NUMBER >= 0x03010b
580 ret = gnutls_certificate_set_x509_key_file2(
581 conn->xcred, params->client_cert,
582 params->private_key, GNUTLS_X509_FMT_DER,
583 params->private_key_passwd, 0);
584#else
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700585 ret = gnutls_certificate_set_x509_key_file(
586 conn->xcred, params->client_cert,
587 params->private_key, GNUTLS_X509_FMT_DER);
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800588#endif
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700589 if (ret < 0) {
590 wpa_printf(MSG_DEBUG, "Failed to read client "
591 "cert/key in DER format: %s",
592 gnutls_strerror(ret));
593 return ret;
594 }
595 }
596 } else if (params->private_key) {
597 int pkcs12_ok = 0;
598#ifdef PKCS12_FUNCS
599 /* Try to load in PKCS#12 format */
600#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
601 ret = gnutls_certificate_set_x509_simple_pkcs12_file(
602 conn->xcred, params->private_key, GNUTLS_X509_FMT_DER,
603 params->private_key_passwd);
604 if (ret != 0) {
605 wpa_printf(MSG_DEBUG, "Failed to load private_key in "
606 "PKCS#12 format: %s", gnutls_strerror(ret));
607 return -1;
608 } else
609 pkcs12_ok = 1;
610#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
611#endif /* PKCS12_FUNCS */
612
613 if (!pkcs12_ok) {
614 wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not "
615 "included");
616 return -1;
617 }
618 }
619
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700620 conn->params_set = 1;
621
622 ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE,
623 conn->xcred);
624 if (ret < 0) {
625 wpa_printf(MSG_INFO, "Failed to configure credentials: %s",
626 gnutls_strerror(ret));
627 }
628
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700629 return ret;
630}
631
632
633int tls_global_set_params(void *tls_ctx,
634 const struct tls_connection_params *params)
635{
636 struct tls_global *global = tls_ctx;
637 int ret;
638
639 /* Currently, global parameters are only set when running in server
640 * mode. */
641 global->server = 1;
642
643 if (global->params_set) {
644 gnutls_certificate_free_credentials(global->xcred);
645 global->params_set = 0;
646 }
647
648 ret = gnutls_certificate_allocate_credentials(&global->xcred);
649 if (ret) {
650 wpa_printf(MSG_DEBUG, "Failed to allocate global credentials "
651 "%s", gnutls_strerror(ret));
652 return -1;
653 }
654
655 if (params->ca_cert) {
656 ret = gnutls_certificate_set_x509_trust_file(
657 global->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM);
658 if (ret < 0) {
659 wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' "
660 "in PEM format: %s", params->ca_cert,
661 gnutls_strerror(ret));
662 ret = gnutls_certificate_set_x509_trust_file(
663 global->xcred, params->ca_cert,
664 GNUTLS_X509_FMT_DER);
665 if (ret < 0) {
666 wpa_printf(MSG_DEBUG, "Failed to read CA cert "
667 "'%s' in DER format: %s",
668 params->ca_cert,
669 gnutls_strerror(ret));
670 goto fail;
671 }
672 }
673
674 if (params->flags & TLS_CONN_ALLOW_SIGN_RSA_MD5) {
675 gnutls_certificate_set_verify_flags(
676 global->xcred,
677 GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
678 }
679
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800680#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700681 if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
682 gnutls_certificate_set_verify_flags(
683 global->xcred,
684 GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
685 }
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800686#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700687 }
688
689 if (params->client_cert && params->private_key) {
690 /* TODO: private_key_passwd? */
691 ret = gnutls_certificate_set_x509_key_file(
692 global->xcred, params->client_cert,
693 params->private_key, GNUTLS_X509_FMT_PEM);
694 if (ret < 0) {
695 wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
696 "in PEM format: %s", gnutls_strerror(ret));
697 ret = gnutls_certificate_set_x509_key_file(
698 global->xcred, params->client_cert,
699 params->private_key, GNUTLS_X509_FMT_DER);
700 if (ret < 0) {
701 wpa_printf(MSG_DEBUG, "Failed to read client "
702 "cert/key in DER format: %s",
703 gnutls_strerror(ret));
704 goto fail;
705 }
706 }
707 } else if (params->private_key) {
708 int pkcs12_ok = 0;
709#ifdef PKCS12_FUNCS
710 /* Try to load in PKCS#12 format */
711#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
712 ret = gnutls_certificate_set_x509_simple_pkcs12_file(
713 global->xcred, params->private_key,
714 GNUTLS_X509_FMT_DER, params->private_key_passwd);
715 if (ret != 0) {
716 wpa_printf(MSG_DEBUG, "Failed to load private_key in "
717 "PKCS#12 format: %s", gnutls_strerror(ret));
718 goto fail;
719 } else
720 pkcs12_ok = 1;
721#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
722#endif /* PKCS12_FUNCS */
723
724 if (!pkcs12_ok) {
725 wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not "
726 "included");
727 goto fail;
728 }
729 }
730
731 global->params_set = 1;
732
733 return 0;
734
735fail:
736 gnutls_certificate_free_credentials(global->xcred);
737 return -1;
738}
739
740
741int tls_global_set_verify(void *ssl_ctx, int check_crl)
742{
743 /* TODO */
744 return 0;
745}
746
747
748int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
749 int verify_peer)
750{
751 if (conn == NULL || conn->session == NULL)
752 return -1;
753
754 conn->verify_peer = verify_peer;
755 gnutls_certificate_server_set_request(conn->session,
756 verify_peer ? GNUTLS_CERT_REQUIRE
757 : GNUTLS_CERT_REQUEST);
758
759 return 0;
760}
761
762
763int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
764 struct tls_keys *keys)
765{
766#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
767 security_parameters_st *sec;
768#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
769
770 if (conn == NULL || conn->session == NULL || keys == NULL)
771 return -1;
772
773 os_memset(keys, 0, sizeof(*keys));
774
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800775#if LIBGNUTLS_VERSION_NUMBER < 0x020c00
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700776#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
777 sec = &conn->session->security_parameters;
778 keys->master_key = sec->master_secret;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800779 keys->master_key_len = WPA_TLS_MASTER_SIZE;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700780 keys->client_random = sec->client_random;
781 keys->server_random = sec->server_random;
782#else /* GNUTLS_INTERNAL_STRUCTURE_HACK */
783 keys->client_random =
784 (u8 *) gnutls_session_get_client_random(conn->session);
785 keys->server_random =
786 (u8 *) gnutls_session_get_server_random(conn->session);
787 /* No access to master_secret */
788#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800789#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700790
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800791#if LIBGNUTLS_VERSION_NUMBER < 0x020c00
792 keys->client_random_len = WPA_TLS_RANDOM_SIZE;
793 keys->server_random_len = WPA_TLS_RANDOM_SIZE;
794#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700795
796 return 0;
797}
798
799
800int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
801 const char *label, int server_random_first,
802 u8 *out, size_t out_len)
803{
804#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
805 if (conn == NULL || conn->session == NULL)
806 return -1;
807
808 return gnutls_prf(conn->session, os_strlen(label), label,
809 server_random_first, 0, NULL, out_len, (char *) out);
810#else /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
811 return -1;
812#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
813}
814
815
816static int tls_connection_verify_peer(struct tls_connection *conn,
817 gnutls_alert_description_t *err)
818{
819 unsigned int status, num_certs, i;
820 struct os_time now;
821 const gnutls_datum_t *certs;
822 gnutls_x509_crt_t cert;
823
824 if (gnutls_certificate_verify_peers2(conn->session, &status) < 0) {
825 wpa_printf(MSG_INFO, "TLS: Failed to verify peer "
826 "certificate chain");
827 *err = GNUTLS_A_INTERNAL_ERROR;
828 return -1;
829 }
830
831 if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) {
832 wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted");
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800833 *err = GNUTLS_A_INTERNAL_ERROR;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700834 if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
835 wpa_printf(MSG_INFO, "TLS: Certificate uses insecure "
836 "algorithm");
837 *err = GNUTLS_A_INSUFFICIENT_SECURITY;
838 }
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800839#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700840 if (status & GNUTLS_CERT_NOT_ACTIVATED) {
841 wpa_printf(MSG_INFO, "TLS: Certificate not yet "
842 "activated");
843 *err = GNUTLS_A_CERTIFICATE_EXPIRED;
844 }
845 if (status & GNUTLS_CERT_EXPIRED) {
846 wpa_printf(MSG_INFO, "TLS: Certificate expired");
847 *err = GNUTLS_A_CERTIFICATE_EXPIRED;
848 }
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800849#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700850 return -1;
851 }
852
853 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
854 wpa_printf(MSG_INFO, "TLS: Peer certificate does not have a "
855 "known issuer");
856 *err = GNUTLS_A_UNKNOWN_CA;
857 return -1;
858 }
859
860 if (status & GNUTLS_CERT_REVOKED) {
861 wpa_printf(MSG_INFO, "TLS: Peer certificate has been revoked");
862 *err = GNUTLS_A_CERTIFICATE_REVOKED;
863 return -1;
864 }
865
866 os_get_time(&now);
867
868 certs = gnutls_certificate_get_peers(conn->session, &num_certs);
869 if (certs == NULL) {
870 wpa_printf(MSG_INFO, "TLS: No peer certificate chain "
871 "received");
872 *err = GNUTLS_A_UNKNOWN_CA;
873 return -1;
874 }
875
876 for (i = 0; i < num_certs; i++) {
877 char *buf;
878 size_t len;
879 if (gnutls_x509_crt_init(&cert) < 0) {
880 wpa_printf(MSG_INFO, "TLS: Certificate initialization "
881 "failed");
882 *err = GNUTLS_A_BAD_CERTIFICATE;
883 return -1;
884 }
885
886 if (gnutls_x509_crt_import(cert, &certs[i],
887 GNUTLS_X509_FMT_DER) < 0) {
888 wpa_printf(MSG_INFO, "TLS: Could not parse peer "
889 "certificate %d/%d", i + 1, num_certs);
890 gnutls_x509_crt_deinit(cert);
891 *err = GNUTLS_A_BAD_CERTIFICATE;
892 return -1;
893 }
894
895 gnutls_x509_crt_get_dn(cert, NULL, &len);
896 len++;
897 buf = os_malloc(len + 1);
898 if (buf) {
899 buf[0] = buf[len] = '\0';
900 gnutls_x509_crt_get_dn(cert, buf, &len);
901 }
902 wpa_printf(MSG_DEBUG, "TLS: Peer cert chain %d/%d: %s",
903 i + 1, num_certs, buf);
904
905 if (i == 0) {
906 /* TODO: validate subject_match and altsubject_match */
907 }
908
909 os_free(buf);
910
911 if (gnutls_x509_crt_get_expiration_time(cert) < now.sec ||
912 gnutls_x509_crt_get_activation_time(cert) > now.sec) {
913 wpa_printf(MSG_INFO, "TLS: Peer certificate %d/%d is "
914 "not valid at this time",
915 i + 1, num_certs);
916 gnutls_x509_crt_deinit(cert);
917 *err = GNUTLS_A_CERTIFICATE_EXPIRED;
918 return -1;
919 }
920
921 gnutls_x509_crt_deinit(cert);
922 }
923
924 return 0;
925}
926
927
928static struct wpabuf * gnutls_get_appl_data(struct tls_connection *conn)
929{
930 int res;
931 struct wpabuf *ad;
932 wpa_printf(MSG_DEBUG, "GnuTLS: Check for possible Application Data");
933 ad = wpabuf_alloc((wpabuf_len(conn->pull_buf) + 500) * 3);
934 if (ad == NULL)
935 return NULL;
936
937 res = gnutls_record_recv(conn->session, wpabuf_mhead(ad),
938 wpabuf_size(ad));
939 wpa_printf(MSG_DEBUG, "GnuTLS: gnutls_record_recv: %d", res);
940 if (res < 0) {
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800941 wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d "
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700942 "(%s)", __func__, (int) res,
943 gnutls_strerror(res));
944 wpabuf_free(ad);
945 return NULL;
946 }
947
948 wpabuf_put(ad, res);
949 wpa_printf(MSG_DEBUG, "GnuTLS: Received %d bytes of Application Data",
950 res);
951 return ad;
952}
953
954
955struct wpabuf * tls_connection_handshake(void *tls_ctx,
956 struct tls_connection *conn,
957 const struct wpabuf *in_data,
958 struct wpabuf **appl_data)
959{
960 struct tls_global *global = tls_ctx;
961 struct wpabuf *out_data;
962 int ret;
963
964 if (appl_data)
965 *appl_data = NULL;
966
967 if (in_data && wpabuf_len(in_data) > 0) {
968 if (conn->pull_buf) {
969 wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
970 "pull_buf", __func__,
971 (unsigned long) wpabuf_len(conn->pull_buf));
972 wpabuf_free(conn->pull_buf);
973 }
974 conn->pull_buf = wpabuf_dup(in_data);
975 if (conn->pull_buf == NULL)
976 return NULL;
977 conn->pull_buf_offset = wpabuf_head(conn->pull_buf);
978 }
979
980 ret = gnutls_handshake(conn->session);
981 if (ret < 0) {
982 switch (ret) {
983 case GNUTLS_E_AGAIN:
984 if (global->server && conn->established &&
985 conn->push_buf == NULL) {
986 /* Need to return something to trigger
987 * completion of EAP-TLS. */
988 conn->push_buf = wpabuf_alloc(0);
989 }
990 break;
991 case GNUTLS_E_FATAL_ALERT_RECEIVED:
992 wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert",
993 __func__, gnutls_alert_get_name(
994 gnutls_alert_get(conn->session)));
995 conn->read_alerts++;
996 /* continue */
997 default:
998 wpa_printf(MSG_DEBUG, "%s - gnutls_handshake failed "
999 "-> %s", __func__, gnutls_strerror(ret));
1000 conn->failed++;
1001 }
1002 } else {
1003 size_t size;
1004 gnutls_alert_description_t err;
1005
1006 if (conn->verify_peer &&
1007 tls_connection_verify_peer(conn, &err)) {
1008 wpa_printf(MSG_INFO, "TLS: Peer certificate chain "
1009 "failed validation");
1010 conn->failed++;
1011 gnutls_alert_send(conn->session, GNUTLS_AL_FATAL, err);
1012 goto out;
1013 }
1014
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001015 wpa_printf(MSG_DEBUG, "TLS: Handshake completed successfully");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001016 conn->established = 1;
1017 if (conn->push_buf == NULL) {
1018 /* Need to return something to get final TLS ACK. */
1019 conn->push_buf = wpabuf_alloc(0);
1020 }
1021
1022 gnutls_session_get_data(conn->session, NULL, &size);
1023 if (global->session_data == NULL ||
1024 global->session_data_size < size) {
1025 os_free(global->session_data);
1026 global->session_data = os_malloc(size);
1027 }
1028 if (global->session_data) {
1029 global->session_data_size = size;
1030 gnutls_session_get_data(conn->session,
1031 global->session_data,
1032 &global->session_data_size);
1033 }
1034
1035 if (conn->pull_buf && appl_data)
1036 *appl_data = gnutls_get_appl_data(conn);
1037 }
1038
1039out:
1040 out_data = conn->push_buf;
1041 conn->push_buf = NULL;
1042 return out_data;
1043}
1044
1045
1046struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
1047 struct tls_connection *conn,
1048 const struct wpabuf *in_data,
1049 struct wpabuf **appl_data)
1050{
1051 return tls_connection_handshake(tls_ctx, conn, in_data, appl_data);
1052}
1053
1054
1055struct wpabuf * tls_connection_encrypt(void *tls_ctx,
1056 struct tls_connection *conn,
1057 const struct wpabuf *in_data)
1058{
1059 ssize_t res;
1060 struct wpabuf *buf;
1061
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001062 res = gnutls_record_send(conn->session, wpabuf_head(in_data),
1063 wpabuf_len(in_data));
1064 if (res < 0) {
1065 wpa_printf(MSG_INFO, "%s: Encryption failed: %s",
1066 __func__, gnutls_strerror(res));
1067 return NULL;
1068 }
1069
1070 buf = conn->push_buf;
1071 conn->push_buf = NULL;
1072 return buf;
1073}
1074
1075
1076struct wpabuf * tls_connection_decrypt(void *tls_ctx,
1077 struct tls_connection *conn,
1078 const struct wpabuf *in_data)
1079{
1080 ssize_t res;
1081 struct wpabuf *out;
1082
1083 if (conn->pull_buf) {
1084 wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
1085 "pull_buf", __func__,
1086 (unsigned long) wpabuf_len(conn->pull_buf));
1087 wpabuf_free(conn->pull_buf);
1088 }
1089 conn->pull_buf = wpabuf_dup(in_data);
1090 if (conn->pull_buf == NULL)
1091 return NULL;
1092 conn->pull_buf_offset = wpabuf_head(conn->pull_buf);
1093
1094 /*
1095 * Even though we try to disable TLS compression, it is possible that
1096 * this cannot be done with all TLS libraries. Add extra buffer space
1097 * to handle the possibility of the decrypted data being longer than
1098 * input data.
1099 */
1100 out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
1101 if (out == NULL)
1102 return NULL;
1103
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001104 res = gnutls_record_recv(conn->session, wpabuf_mhead(out),
1105 wpabuf_size(out));
1106 if (res < 0) {
1107 wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d "
1108 "(%s)", __func__, (int) res, gnutls_strerror(res));
1109 wpabuf_free(out);
1110 return NULL;
1111 }
1112 wpabuf_put(out, res);
1113
1114 return out;
1115}
1116
1117
1118int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn)
1119{
1120 if (conn == NULL)
1121 return 0;
1122 return gnutls_session_is_resumed(conn->session);
1123}
1124
1125
1126int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
1127 u8 *ciphers)
1128{
1129 /* TODO */
1130 return -1;
1131}
1132
1133
1134int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
1135 char *buf, size_t buflen)
1136{
1137 /* TODO */
1138 buf[0] = '\0';
1139 return 0;
1140}
1141
1142
1143int tls_connection_enable_workaround(void *ssl_ctx,
1144 struct tls_connection *conn)
1145{
1146 gnutls_record_disable_padding(conn->session);
1147 return 0;
1148}
1149
1150
1151int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
1152 int ext_type, const u8 *data,
1153 size_t data_len)
1154{
1155 /* TODO */
1156 return -1;
1157}
1158
1159
1160int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn)
1161{
1162 if (conn == NULL)
1163 return -1;
1164 return conn->failed;
1165}
1166
1167
1168int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn)
1169{
1170 if (conn == NULL)
1171 return -1;
1172 return conn->read_alerts;
1173}
1174
1175
1176int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn)
1177{
1178 if (conn == NULL)
1179 return -1;
1180 return conn->write_alerts;
1181}
1182
1183
1184int tls_connection_get_keyblock_size(void *tls_ctx,
1185 struct tls_connection *conn)
1186{
1187 /* TODO */
1188 return -1;
1189}
1190
1191
1192unsigned int tls_capabilities(void *tls_ctx)
1193{
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001194 return 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001195}
1196
1197
1198int tls_connection_set_session_ticket_cb(void *tls_ctx,
1199 struct tls_connection *conn,
1200 tls_session_ticket_cb cb, void *ctx)
1201{
1202 return -1;
1203}