blob: cb23eb9c5f36fd36bd37cc17c907cc8b079598ee [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 {
84 gnutls_session session;
85 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
202static ssize_t tls_pull_func(gnutls_transport_ptr ptr, void *buf,
203 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
231static ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf,
232 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);
289 gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr) conn);
290
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) {
566 /* TODO: private_key_passwd? */
567 ret = gnutls_certificate_set_x509_key_file(
568 conn->xcred, params->client_cert, params->private_key,
569 GNUTLS_X509_FMT_PEM);
570 if (ret < 0) {
571 wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
572 "in PEM format: %s", gnutls_strerror(ret));
573 ret = gnutls_certificate_set_x509_key_file(
574 conn->xcred, params->client_cert,
575 params->private_key, GNUTLS_X509_FMT_DER);
576 if (ret < 0) {
577 wpa_printf(MSG_DEBUG, "Failed to read client "
578 "cert/key in DER format: %s",
579 gnutls_strerror(ret));
580 return ret;
581 }
582 }
583 } else if (params->private_key) {
584 int pkcs12_ok = 0;
585#ifdef PKCS12_FUNCS
586 /* Try to load in PKCS#12 format */
587#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
588 ret = gnutls_certificate_set_x509_simple_pkcs12_file(
589 conn->xcred, params->private_key, GNUTLS_X509_FMT_DER,
590 params->private_key_passwd);
591 if (ret != 0) {
592 wpa_printf(MSG_DEBUG, "Failed to load private_key in "
593 "PKCS#12 format: %s", gnutls_strerror(ret));
594 return -1;
595 } else
596 pkcs12_ok = 1;
597#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
598#endif /* PKCS12_FUNCS */
599
600 if (!pkcs12_ok) {
601 wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not "
602 "included");
603 return -1;
604 }
605 }
606
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700607 conn->params_set = 1;
608
609 ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE,
610 conn->xcred);
611 if (ret < 0) {
612 wpa_printf(MSG_INFO, "Failed to configure credentials: %s",
613 gnutls_strerror(ret));
614 }
615
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700616 return ret;
617}
618
619
620int tls_global_set_params(void *tls_ctx,
621 const struct tls_connection_params *params)
622{
623 struct tls_global *global = tls_ctx;
624 int ret;
625
626 /* Currently, global parameters are only set when running in server
627 * mode. */
628 global->server = 1;
629
630 if (global->params_set) {
631 gnutls_certificate_free_credentials(global->xcred);
632 global->params_set = 0;
633 }
634
635 ret = gnutls_certificate_allocate_credentials(&global->xcred);
636 if (ret) {
637 wpa_printf(MSG_DEBUG, "Failed to allocate global credentials "
638 "%s", gnutls_strerror(ret));
639 return -1;
640 }
641
642 if (params->ca_cert) {
643 ret = gnutls_certificate_set_x509_trust_file(
644 global->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM);
645 if (ret < 0) {
646 wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' "
647 "in PEM format: %s", params->ca_cert,
648 gnutls_strerror(ret));
649 ret = gnutls_certificate_set_x509_trust_file(
650 global->xcred, params->ca_cert,
651 GNUTLS_X509_FMT_DER);
652 if (ret < 0) {
653 wpa_printf(MSG_DEBUG, "Failed to read CA cert "
654 "'%s' in DER format: %s",
655 params->ca_cert,
656 gnutls_strerror(ret));
657 goto fail;
658 }
659 }
660
661 if (params->flags & TLS_CONN_ALLOW_SIGN_RSA_MD5) {
662 gnutls_certificate_set_verify_flags(
663 global->xcred,
664 GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
665 }
666
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800667#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700668 if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
669 gnutls_certificate_set_verify_flags(
670 global->xcred,
671 GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
672 }
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800673#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700674 }
675
676 if (params->client_cert && params->private_key) {
677 /* TODO: private_key_passwd? */
678 ret = gnutls_certificate_set_x509_key_file(
679 global->xcred, params->client_cert,
680 params->private_key, GNUTLS_X509_FMT_PEM);
681 if (ret < 0) {
682 wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
683 "in PEM format: %s", gnutls_strerror(ret));
684 ret = gnutls_certificate_set_x509_key_file(
685 global->xcred, params->client_cert,
686 params->private_key, GNUTLS_X509_FMT_DER);
687 if (ret < 0) {
688 wpa_printf(MSG_DEBUG, "Failed to read client "
689 "cert/key in DER format: %s",
690 gnutls_strerror(ret));
691 goto fail;
692 }
693 }
694 } else if (params->private_key) {
695 int pkcs12_ok = 0;
696#ifdef PKCS12_FUNCS
697 /* Try to load in PKCS#12 format */
698#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
699 ret = gnutls_certificate_set_x509_simple_pkcs12_file(
700 global->xcred, params->private_key,
701 GNUTLS_X509_FMT_DER, params->private_key_passwd);
702 if (ret != 0) {
703 wpa_printf(MSG_DEBUG, "Failed to load private_key in "
704 "PKCS#12 format: %s", gnutls_strerror(ret));
705 goto fail;
706 } else
707 pkcs12_ok = 1;
708#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
709#endif /* PKCS12_FUNCS */
710
711 if (!pkcs12_ok) {
712 wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not "
713 "included");
714 goto fail;
715 }
716 }
717
718 global->params_set = 1;
719
720 return 0;
721
722fail:
723 gnutls_certificate_free_credentials(global->xcred);
724 return -1;
725}
726
727
728int tls_global_set_verify(void *ssl_ctx, int check_crl)
729{
730 /* TODO */
731 return 0;
732}
733
734
735int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
736 int verify_peer)
737{
738 if (conn == NULL || conn->session == NULL)
739 return -1;
740
741 conn->verify_peer = verify_peer;
742 gnutls_certificate_server_set_request(conn->session,
743 verify_peer ? GNUTLS_CERT_REQUIRE
744 : GNUTLS_CERT_REQUEST);
745
746 return 0;
747}
748
749
750int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
751 struct tls_keys *keys)
752{
753#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
754 security_parameters_st *sec;
755#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
756
757 if (conn == NULL || conn->session == NULL || keys == NULL)
758 return -1;
759
760 os_memset(keys, 0, sizeof(*keys));
761
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800762#if LIBGNUTLS_VERSION_NUMBER < 0x020c00
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700763#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
764 sec = &conn->session->security_parameters;
765 keys->master_key = sec->master_secret;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800766 keys->master_key_len = WPA_TLS_MASTER_SIZE;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700767 keys->client_random = sec->client_random;
768 keys->server_random = sec->server_random;
769#else /* GNUTLS_INTERNAL_STRUCTURE_HACK */
770 keys->client_random =
771 (u8 *) gnutls_session_get_client_random(conn->session);
772 keys->server_random =
773 (u8 *) gnutls_session_get_server_random(conn->session);
774 /* No access to master_secret */
775#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800776#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700777
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800778#if LIBGNUTLS_VERSION_NUMBER < 0x020c00
779 keys->client_random_len = WPA_TLS_RANDOM_SIZE;
780 keys->server_random_len = WPA_TLS_RANDOM_SIZE;
781#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700782
783 return 0;
784}
785
786
787int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
788 const char *label, int server_random_first,
789 u8 *out, size_t out_len)
790{
791#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
792 if (conn == NULL || conn->session == NULL)
793 return -1;
794
795 return gnutls_prf(conn->session, os_strlen(label), label,
796 server_random_first, 0, NULL, out_len, (char *) out);
797#else /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
798 return -1;
799#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
800}
801
802
803static int tls_connection_verify_peer(struct tls_connection *conn,
804 gnutls_alert_description_t *err)
805{
806 unsigned int status, num_certs, i;
807 struct os_time now;
808 const gnutls_datum_t *certs;
809 gnutls_x509_crt_t cert;
810
811 if (gnutls_certificate_verify_peers2(conn->session, &status) < 0) {
812 wpa_printf(MSG_INFO, "TLS: Failed to verify peer "
813 "certificate chain");
814 *err = GNUTLS_A_INTERNAL_ERROR;
815 return -1;
816 }
817
818 if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) {
819 wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted");
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800820 *err = GNUTLS_A_INTERNAL_ERROR;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700821 if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
822 wpa_printf(MSG_INFO, "TLS: Certificate uses insecure "
823 "algorithm");
824 *err = GNUTLS_A_INSUFFICIENT_SECURITY;
825 }
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800826#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700827 if (status & GNUTLS_CERT_NOT_ACTIVATED) {
828 wpa_printf(MSG_INFO, "TLS: Certificate not yet "
829 "activated");
830 *err = GNUTLS_A_CERTIFICATE_EXPIRED;
831 }
832 if (status & GNUTLS_CERT_EXPIRED) {
833 wpa_printf(MSG_INFO, "TLS: Certificate expired");
834 *err = GNUTLS_A_CERTIFICATE_EXPIRED;
835 }
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800836#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700837 return -1;
838 }
839
840 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
841 wpa_printf(MSG_INFO, "TLS: Peer certificate does not have a "
842 "known issuer");
843 *err = GNUTLS_A_UNKNOWN_CA;
844 return -1;
845 }
846
847 if (status & GNUTLS_CERT_REVOKED) {
848 wpa_printf(MSG_INFO, "TLS: Peer certificate has been revoked");
849 *err = GNUTLS_A_CERTIFICATE_REVOKED;
850 return -1;
851 }
852
853 os_get_time(&now);
854
855 certs = gnutls_certificate_get_peers(conn->session, &num_certs);
856 if (certs == NULL) {
857 wpa_printf(MSG_INFO, "TLS: No peer certificate chain "
858 "received");
859 *err = GNUTLS_A_UNKNOWN_CA;
860 return -1;
861 }
862
863 for (i = 0; i < num_certs; i++) {
864 char *buf;
865 size_t len;
866 if (gnutls_x509_crt_init(&cert) < 0) {
867 wpa_printf(MSG_INFO, "TLS: Certificate initialization "
868 "failed");
869 *err = GNUTLS_A_BAD_CERTIFICATE;
870 return -1;
871 }
872
873 if (gnutls_x509_crt_import(cert, &certs[i],
874 GNUTLS_X509_FMT_DER) < 0) {
875 wpa_printf(MSG_INFO, "TLS: Could not parse peer "
876 "certificate %d/%d", i + 1, num_certs);
877 gnutls_x509_crt_deinit(cert);
878 *err = GNUTLS_A_BAD_CERTIFICATE;
879 return -1;
880 }
881
882 gnutls_x509_crt_get_dn(cert, NULL, &len);
883 len++;
884 buf = os_malloc(len + 1);
885 if (buf) {
886 buf[0] = buf[len] = '\0';
887 gnutls_x509_crt_get_dn(cert, buf, &len);
888 }
889 wpa_printf(MSG_DEBUG, "TLS: Peer cert chain %d/%d: %s",
890 i + 1, num_certs, buf);
891
892 if (i == 0) {
893 /* TODO: validate subject_match and altsubject_match */
894 }
895
896 os_free(buf);
897
898 if (gnutls_x509_crt_get_expiration_time(cert) < now.sec ||
899 gnutls_x509_crt_get_activation_time(cert) > now.sec) {
900 wpa_printf(MSG_INFO, "TLS: Peer certificate %d/%d is "
901 "not valid at this time",
902 i + 1, num_certs);
903 gnutls_x509_crt_deinit(cert);
904 *err = GNUTLS_A_CERTIFICATE_EXPIRED;
905 return -1;
906 }
907
908 gnutls_x509_crt_deinit(cert);
909 }
910
911 return 0;
912}
913
914
915static struct wpabuf * gnutls_get_appl_data(struct tls_connection *conn)
916{
917 int res;
918 struct wpabuf *ad;
919 wpa_printf(MSG_DEBUG, "GnuTLS: Check for possible Application Data");
920 ad = wpabuf_alloc((wpabuf_len(conn->pull_buf) + 500) * 3);
921 if (ad == NULL)
922 return NULL;
923
924 res = gnutls_record_recv(conn->session, wpabuf_mhead(ad),
925 wpabuf_size(ad));
926 wpa_printf(MSG_DEBUG, "GnuTLS: gnutls_record_recv: %d", res);
927 if (res < 0) {
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800928 wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d "
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700929 "(%s)", __func__, (int) res,
930 gnutls_strerror(res));
931 wpabuf_free(ad);
932 return NULL;
933 }
934
935 wpabuf_put(ad, res);
936 wpa_printf(MSG_DEBUG, "GnuTLS: Received %d bytes of Application Data",
937 res);
938 return ad;
939}
940
941
942struct wpabuf * tls_connection_handshake(void *tls_ctx,
943 struct tls_connection *conn,
944 const struct wpabuf *in_data,
945 struct wpabuf **appl_data)
946{
947 struct tls_global *global = tls_ctx;
948 struct wpabuf *out_data;
949 int ret;
950
951 if (appl_data)
952 *appl_data = NULL;
953
954 if (in_data && wpabuf_len(in_data) > 0) {
955 if (conn->pull_buf) {
956 wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
957 "pull_buf", __func__,
958 (unsigned long) wpabuf_len(conn->pull_buf));
959 wpabuf_free(conn->pull_buf);
960 }
961 conn->pull_buf = wpabuf_dup(in_data);
962 if (conn->pull_buf == NULL)
963 return NULL;
964 conn->pull_buf_offset = wpabuf_head(conn->pull_buf);
965 }
966
967 ret = gnutls_handshake(conn->session);
968 if (ret < 0) {
969 switch (ret) {
970 case GNUTLS_E_AGAIN:
971 if (global->server && conn->established &&
972 conn->push_buf == NULL) {
973 /* Need to return something to trigger
974 * completion of EAP-TLS. */
975 conn->push_buf = wpabuf_alloc(0);
976 }
977 break;
978 case GNUTLS_E_FATAL_ALERT_RECEIVED:
979 wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert",
980 __func__, gnutls_alert_get_name(
981 gnutls_alert_get(conn->session)));
982 conn->read_alerts++;
983 /* continue */
984 default:
985 wpa_printf(MSG_DEBUG, "%s - gnutls_handshake failed "
986 "-> %s", __func__, gnutls_strerror(ret));
987 conn->failed++;
988 }
989 } else {
990 size_t size;
991 gnutls_alert_description_t err;
992
993 if (conn->verify_peer &&
994 tls_connection_verify_peer(conn, &err)) {
995 wpa_printf(MSG_INFO, "TLS: Peer certificate chain "
996 "failed validation");
997 conn->failed++;
998 gnutls_alert_send(conn->session, GNUTLS_AL_FATAL, err);
999 goto out;
1000 }
1001
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001002 wpa_printf(MSG_DEBUG, "TLS: Handshake completed successfully");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001003 conn->established = 1;
1004 if (conn->push_buf == NULL) {
1005 /* Need to return something to get final TLS ACK. */
1006 conn->push_buf = wpabuf_alloc(0);
1007 }
1008
1009 gnutls_session_get_data(conn->session, NULL, &size);
1010 if (global->session_data == NULL ||
1011 global->session_data_size < size) {
1012 os_free(global->session_data);
1013 global->session_data = os_malloc(size);
1014 }
1015 if (global->session_data) {
1016 global->session_data_size = size;
1017 gnutls_session_get_data(conn->session,
1018 global->session_data,
1019 &global->session_data_size);
1020 }
1021
1022 if (conn->pull_buf && appl_data)
1023 *appl_data = gnutls_get_appl_data(conn);
1024 }
1025
1026out:
1027 out_data = conn->push_buf;
1028 conn->push_buf = NULL;
1029 return out_data;
1030}
1031
1032
1033struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
1034 struct tls_connection *conn,
1035 const struct wpabuf *in_data,
1036 struct wpabuf **appl_data)
1037{
1038 return tls_connection_handshake(tls_ctx, conn, in_data, appl_data);
1039}
1040
1041
1042struct wpabuf * tls_connection_encrypt(void *tls_ctx,
1043 struct tls_connection *conn,
1044 const struct wpabuf *in_data)
1045{
1046 ssize_t res;
1047 struct wpabuf *buf;
1048
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001049 res = gnutls_record_send(conn->session, wpabuf_head(in_data),
1050 wpabuf_len(in_data));
1051 if (res < 0) {
1052 wpa_printf(MSG_INFO, "%s: Encryption failed: %s",
1053 __func__, gnutls_strerror(res));
1054 return NULL;
1055 }
1056
1057 buf = conn->push_buf;
1058 conn->push_buf = NULL;
1059 return buf;
1060}
1061
1062
1063struct wpabuf * tls_connection_decrypt(void *tls_ctx,
1064 struct tls_connection *conn,
1065 const struct wpabuf *in_data)
1066{
1067 ssize_t res;
1068 struct wpabuf *out;
1069
1070 if (conn->pull_buf) {
1071 wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
1072 "pull_buf", __func__,
1073 (unsigned long) wpabuf_len(conn->pull_buf));
1074 wpabuf_free(conn->pull_buf);
1075 }
1076 conn->pull_buf = wpabuf_dup(in_data);
1077 if (conn->pull_buf == NULL)
1078 return NULL;
1079 conn->pull_buf_offset = wpabuf_head(conn->pull_buf);
1080
1081 /*
1082 * Even though we try to disable TLS compression, it is possible that
1083 * this cannot be done with all TLS libraries. Add extra buffer space
1084 * to handle the possibility of the decrypted data being longer than
1085 * input data.
1086 */
1087 out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
1088 if (out == NULL)
1089 return NULL;
1090
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001091 res = gnutls_record_recv(conn->session, wpabuf_mhead(out),
1092 wpabuf_size(out));
1093 if (res < 0) {
1094 wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d "
1095 "(%s)", __func__, (int) res, gnutls_strerror(res));
1096 wpabuf_free(out);
1097 return NULL;
1098 }
1099 wpabuf_put(out, res);
1100
1101 return out;
1102}
1103
1104
1105int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn)
1106{
1107 if (conn == NULL)
1108 return 0;
1109 return gnutls_session_is_resumed(conn->session);
1110}
1111
1112
1113int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
1114 u8 *ciphers)
1115{
1116 /* TODO */
1117 return -1;
1118}
1119
1120
1121int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
1122 char *buf, size_t buflen)
1123{
1124 /* TODO */
1125 buf[0] = '\0';
1126 return 0;
1127}
1128
1129
1130int tls_connection_enable_workaround(void *ssl_ctx,
1131 struct tls_connection *conn)
1132{
1133 gnutls_record_disable_padding(conn->session);
1134 return 0;
1135}
1136
1137
1138int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
1139 int ext_type, const u8 *data,
1140 size_t data_len)
1141{
1142 /* TODO */
1143 return -1;
1144}
1145
1146
1147int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn)
1148{
1149 if (conn == NULL)
1150 return -1;
1151 return conn->failed;
1152}
1153
1154
1155int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn)
1156{
1157 if (conn == NULL)
1158 return -1;
1159 return conn->read_alerts;
1160}
1161
1162
1163int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn)
1164{
1165 if (conn == NULL)
1166 return -1;
1167 return conn->write_alerts;
1168}
1169
1170
1171int tls_connection_get_keyblock_size(void *tls_ctx,
1172 struct tls_connection *conn)
1173{
1174 /* TODO */
1175 return -1;
1176}
1177
1178
1179unsigned int tls_capabilities(void *tls_ctx)
1180{
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001181 return 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001182}
1183
1184
1185int tls_connection_set_session_ticket_cb(void *tls_ctx,
1186 struct tls_connection *conn,
1187 tls_session_ticket_cb cb, void *ctx)
1188{
1189 return -1;
1190}