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