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