Make vinum use libedit instead of libreadline.
[dragonfly.git] / contrib / wpa_supplicant-0.4.9 / tls_gnutls.c
blob2bc9ed8e50900fd01b40c0bba1dd98f958c41402
1 /*
2 * WPA Supplicant / SSL/TLS interface functions for openssl
3 * Copyright (c) 2004-2006, Jouni Malinen <jkmaline@cc.hut.fi>
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.
9 * Alternatively, this software may be distributed under the terms of BSD
10 * license.
12 * See README and COPYING for more details.
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <errno.h>
19 #include <gnutls/gnutls.h>
20 #include <gnutls/x509.h>
22 #include "common.h"
23 #include "tls.h"
27 * It looks like gnutls does not provide access to client/server_random and
28 * master_key. This is somewhat unfortunate since these are needed for key
29 * derivation in EAP-{TLS,TTLS,PEAP,FAST}. Workaround for now is a horrible
30 * hack that copies the gnutls_session_int definition from gnutls_int.h so that
31 * we can get the needed information.
34 typedef u8 uint8;
35 #define TLS_RANDOM_SIZE 32
36 #define TLS_MASTER_SIZE 48
37 typedef unsigned char opaque;
38 typedef struct {
39 uint8 suite[2];
40 } cipher_suite_st;
42 typedef struct {
43 gnutls_connection_end_t entity;
44 gnutls_kx_algorithm_t kx_algorithm;
45 gnutls_cipher_algorithm_t read_bulk_cipher_algorithm;
46 gnutls_mac_algorithm_t read_mac_algorithm;
47 gnutls_compression_method_t read_compression_algorithm;
48 gnutls_cipher_algorithm_t write_bulk_cipher_algorithm;
49 gnutls_mac_algorithm_t write_mac_algorithm;
50 gnutls_compression_method_t write_compression_algorithm;
51 cipher_suite_st current_cipher_suite;
52 opaque master_secret[TLS_MASTER_SIZE];
53 opaque client_random[TLS_RANDOM_SIZE];
54 opaque server_random[TLS_RANDOM_SIZE];
55 /* followed by stuff we are not interested in */
56 } security_parameters_st;
58 struct gnutls_session_int {
59 security_parameters_st security_parameters;
60 /* followed by things we are not interested in */
63 static int tls_gnutls_ref_count = 0;
65 struct tls_connection {
66 gnutls_session session;
67 char *subject_match, *altsubject_match;
68 int read_alerts, write_alerts, failed;
70 u8 *pre_shared_secret;
71 size_t pre_shared_secret_len;
72 int established;
73 int verify_peer;
75 u8 *push_buf, *pull_buf, *pull_buf_offset;
76 size_t push_buf_len, pull_buf_len;
78 gnutls_certificate_credentials_t xcred;
82 static void tls_log_func(int level, const char *msg)
84 char *s, *pos;
85 if (level == 6 || level == 7) {
86 /* These levels seem to be mostly I/O debug and msg dumps */
87 return;
90 s = strdup(msg);
91 if (s == NULL)
92 return;
94 pos = s;
95 while (*pos != '\0') {
96 if (*pos == '\n') {
97 *pos = '\0';
98 break;
100 pos++;
102 wpa_printf(level > 3 ? MSG_MSGDUMP : MSG_DEBUG,
103 "gnutls<%d> %s", level, s);
104 free(s);
108 extern int wpa_debug_show_keys;
110 void * tls_init(const struct tls_config *conf)
112 /* Because of the horrible hack to get master_secret and client/server
113 * random, we need to make sure that the gnutls version is something
114 * that is expected to have same structure definition for the session
115 * data.. */
116 const char *ver;
117 const char *ok_ver[] = { "1.2.3", "1.2.4", "1.2.5", "1.2.6", NULL };
118 int i;
120 if (tls_gnutls_ref_count == 0 && gnutls_global_init() < 0)
121 return NULL;
122 tls_gnutls_ref_count++;
124 ver = gnutls_check_version(NULL);
125 if (ver == NULL)
126 return NULL;
127 wpa_printf(MSG_DEBUG, "%s - gnutls version %s", __func__, ver);
128 for (i = 0; ok_ver[i]; i++) {
129 if (strcmp(ok_ver[i], ver) == 0)
130 break;
132 if (ok_ver[i] == NULL) {
133 wpa_printf(MSG_INFO, "Untested gnutls version %s - this needs "
134 "to be tested and enabled in tls_gnutls.c", ver);
135 return NULL;
138 gnutls_global_set_log_function(tls_log_func);
139 if (wpa_debug_show_keys)
140 gnutls_global_set_log_level(11);
141 return (void *) 1;
145 void tls_deinit(void *ssl_ctx)
147 tls_gnutls_ref_count--;
148 if (tls_gnutls_ref_count == 0)
149 gnutls_global_deinit();
153 int tls_get_errors(void *ssl_ctx)
155 return 0;
159 static ssize_t tls_pull_func(gnutls_transport_ptr ptr, void *buf,
160 size_t len)
162 struct tls_connection *conn = (struct tls_connection *) ptr;
163 u8 *end;
164 if (conn->pull_buf == NULL) {
165 errno = EWOULDBLOCK;
166 return -1;
169 end = conn->pull_buf + conn->pull_buf_len;
170 if (end - conn->pull_buf_offset < len)
171 len = end - conn->pull_buf_offset;
172 memcpy(buf, conn->pull_buf_offset, len);
173 conn->pull_buf_offset += len;
174 if (conn->pull_buf_offset == end) {
175 wpa_printf(MSG_DEBUG, "%s - pull_buf consumed", __func__);
176 free(conn->pull_buf);
177 conn->pull_buf = conn->pull_buf_offset = NULL;
178 conn->pull_buf_len = 0;
179 } else {
180 wpa_printf(MSG_DEBUG, "%s - %d bytes remaining in pull_buf",
181 __func__, end - conn->pull_buf_offset);
183 return len;
187 static ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf,
188 size_t len)
190 struct tls_connection *conn = (struct tls_connection *) ptr;
191 u8 *nbuf;
193 nbuf = realloc(conn->push_buf, conn->push_buf_len + len);
194 if (nbuf == NULL) {
195 errno = ENOMEM;
196 return -1;
198 memcpy(nbuf + conn->push_buf_len, buf, len);
199 conn->push_buf = nbuf;
200 conn->push_buf_len += len;
202 return len;
206 struct tls_connection * tls_connection_init(void *ssl_ctx)
208 struct tls_connection *conn;
209 const int cert_types[2] = { GNUTLS_CRT_X509, 0 };
210 const int protos[2] = { GNUTLS_TLS1, 0 };
213 conn = malloc(sizeof(*conn));
214 if (conn == NULL)
215 return NULL;
216 memset(conn, 0, sizeof(*conn));
217 if (gnutls_init(&conn->session, GNUTLS_CLIENT) < 0) {
218 wpa_printf(MSG_INFO, "TLS: Failed to initialize new TLS "
219 "connection");
220 free(conn);
221 return NULL;
224 gnutls_set_default_priority(conn->session);
225 gnutls_certificate_type_set_priority(conn->session, cert_types);
226 gnutls_protocol_set_priority(conn->session, protos);
228 gnutls_transport_set_pull_function(conn->session, tls_pull_func);
229 gnutls_transport_set_push_function(conn->session, tls_push_func);
230 gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr) conn);
232 gnutls_certificate_allocate_credentials(&conn->xcred);
234 return conn;
238 void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn)
240 if (conn == NULL)
241 return;
242 gnutls_certificate_free_credentials(conn->xcred);
243 gnutls_deinit(conn->session);
244 free(conn->pre_shared_secret);
245 free(conn->subject_match);
246 free(conn->altsubject_match);
247 free(conn->push_buf);
248 free(conn->pull_buf);
249 free(conn);
253 int tls_connection_established(void *ssl_ctx, struct tls_connection *conn)
255 return conn ? conn->established : 0;
259 int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
261 if (conn == NULL)
262 return -1;
264 /* Shutdown previous TLS connection without notifying the peer
265 * because the connection was already terminated in practice
266 * and "close notify" shutdown alert would confuse AS. */
267 gnutls_bye(conn->session, GNUTLS_SHUT_RDWR);
268 free(conn->push_buf);
269 conn->push_buf = NULL;
270 conn->push_buf_len = 0;
271 conn->established = 0;
272 /* TODO: what to do trigger new handshake for re-auth? */
273 return 0;
277 #if 0
278 static int tls_match_altsubject(X509 *cert, const char *match)
280 GENERAL_NAME *gen;
281 char *field, *tmp;
282 void *ext;
283 int i, found = 0;
284 size_t len;
286 ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
288 for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
289 gen = sk_GENERAL_NAME_value(ext, i);
290 switch (gen->type) {
291 case GEN_EMAIL:
292 field = "EMAIL";
293 break;
294 case GEN_DNS:
295 field = "DNS";
296 break;
297 case GEN_URI:
298 field = "URI";
299 break;
300 default:
301 field = NULL;
302 wpa_printf(MSG_DEBUG, "TLS: altSubjectName: "
303 "unsupported type=%d", gen->type);
304 break;
307 if (!field)
308 continue;
310 wpa_printf(MSG_DEBUG, "TLS: altSubjectName: %s:%s",
311 field, gen->d.ia5->data);
312 len = strlen(field) + 1 + strlen((char *) gen->d.ia5->data) +
314 tmp = malloc(len);
315 if (tmp == NULL)
316 continue;
317 snprintf(tmp, len, "%s:%s", field, gen->d.ia5->data);
318 if (strstr(tmp, match))
319 found++;
320 free(tmp);
323 return found;
325 #endif
328 #if 0
329 static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
331 char buf[256];
332 X509 *err_cert;
333 int err, depth;
334 SSL *ssl;
335 struct tls_connection *conn;
336 char *match, *altmatch;
338 err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
339 err = X509_STORE_CTX_get_error(x509_ctx);
340 depth = X509_STORE_CTX_get_error_depth(x509_ctx);
341 ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
342 SSL_get_ex_data_X509_STORE_CTX_idx());
343 X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
345 conn = SSL_get_app_data(ssl);
346 match = conn ? conn->subject_match : NULL;
347 altmatch = conn ? conn->altsubject_match : NULL;
349 if (!preverify_ok) {
350 wpa_printf(MSG_WARNING, "TLS: Certificate verification failed,"
351 " error %d (%s) depth %d for '%s'", err,
352 X509_verify_cert_error_string(err), depth, buf);
353 } else {
354 wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - "
355 "preverify_ok=%d err=%d (%s) depth=%d buf='%s'",
356 preverify_ok, err,
357 X509_verify_cert_error_string(err), depth, buf);
358 if (depth == 0 && match && strstr(buf, match) == NULL) {
359 wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not "
360 "match with '%s'", buf, match);
361 preverify_ok = 0;
362 } else if (depth == 0 && altmatch &&
363 !tls_match_altsubject(err_cert, altmatch)) {
364 wpa_printf(MSG_WARNING, "TLS: altSubjectName match "
365 "'%s' not found", altmatch);
366 preverify_ok = 0;
370 return preverify_ok;
372 #endif
375 int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
376 const struct tls_connection_params *params)
378 int ret;
380 if (conn == NULL || params == NULL)
381 return -1;
383 free(conn->subject_match);
384 conn->subject_match = NULL;
385 if (params->subject_match) {
386 conn->subject_match = strdup(params->subject_match);
387 if (conn->subject_match == NULL)
388 return -1;
391 free(conn->altsubject_match);
392 conn->altsubject_match = NULL;
393 if (params->altsubject_match) {
394 conn->altsubject_match = strdup(params->altsubject_match);
395 if (conn->altsubject_match == NULL)
396 return -1;
399 /* TODO: gnutls_certificate_set_verify_flags(xcred, flags);
400 * to force peer validation(?) */
402 if (params->ca_cert) {
403 conn->verify_peer = 1;
404 ret = gnutls_certificate_set_x509_trust_file(
405 conn->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM);
406 if (ret < 0) {
407 wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' "
408 "in PEM format: %s", params->ca_cert,
409 gnutls_strerror(ret));
410 ret = gnutls_certificate_set_x509_trust_file(
411 conn->xcred, params->ca_cert,
412 GNUTLS_X509_FMT_DER);
413 if (ret < 0) {
414 wpa_printf(MSG_DEBUG, "Failed to read CA cert "
415 "'%s' in DER format: %s",
416 params->ca_cert,
417 gnutls_strerror(ret));
422 if (params->client_cert && params->private_key) {
423 /* TODO: private_key_passwd? */
424 ret = gnutls_certificate_set_x509_key_file(
425 conn->xcred, params->client_cert, params->private_key,
426 GNUTLS_X509_FMT_PEM);
427 if (ret < 0) {
428 wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
429 "in PEM format: %s", gnutls_strerror(ret));
430 ret = gnutls_certificate_set_x509_key_file(
431 conn->xcred, params->client_cert,
432 params->private_key, GNUTLS_X509_FMT_DER);
433 if (ret < 0) {
434 wpa_printf(MSG_DEBUG, "Failed to read client "
435 "cert/key in DER format: %s",
436 gnutls_strerror(ret));
437 return ret;
442 ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE,
443 conn->xcred);
444 if (ret < 0) {
445 wpa_printf(MSG_INFO, "Failed to configure credentials: %s",
446 gnutls_strerror(ret));
449 return ret;
453 int tls_global_ca_cert(void *_ssl_ctx, const char *ca_cert)
455 /* TODO */
456 return -1;
460 int tls_global_set_verify(void *ssl_ctx, int check_crl)
462 /* TODO */
463 return -1;
467 int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
468 int verify_peer)
470 if (conn == NULL)
471 return -1;
473 /* TODO */
475 return 1;
479 int tls_global_client_cert(void *_ssl_ctx, const char *client_cert)
481 /* TODO */
482 return -1;
486 int tls_global_private_key(void *_ssl_ctx, const char *private_key,
487 const char *private_key_passwd)
489 /* TODO */
490 return -1;
494 int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
495 struct tls_keys *keys)
497 security_parameters_st *sec;
499 if (conn == NULL || conn->session == NULL || keys == NULL)
500 return -1;
502 memset(keys, 0, sizeof(*keys));
503 sec = &conn->session->security_parameters;
504 keys->master_key = sec->master_secret;
505 keys->master_key_len = TLS_MASTER_SIZE;
506 keys->client_random = sec->client_random;
507 keys->client_random_len = TLS_RANDOM_SIZE;
508 keys->server_random = sec->server_random;
509 keys->server_random_len = TLS_RANDOM_SIZE;
511 return 0;
515 static int tls_connection_verify_peer(struct tls_connection *conn)
517 unsigned int status, num_certs, i;
518 time_t now;
519 const gnutls_datum_t *certs;
520 gnutls_x509_crt_t cert;
522 if (gnutls_certificate_verify_peers2(conn->session, &status) < 0) {
523 wpa_printf(MSG_INFO, "TLS: Failed to verify peer "
524 "certificate chain");
525 return -1;
528 if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) {
529 wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted");
530 return -1;
533 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
534 wpa_printf(MSG_INFO, "TLS: Peer certificate does not have a "
535 "known issuer");
536 return -1;
539 if (status & GNUTLS_CERT_REVOKED) {
540 wpa_printf(MSG_INFO, "TLS: Peer certificate has been revoked");
541 return -1;
544 now = time(NULL);
546 certs = gnutls_certificate_get_peers(conn->session, &num_certs);
547 if (certs == NULL) {
548 wpa_printf(MSG_INFO, "TLS: No peer certificate chain "
549 "received");
550 return -1;
553 for (i = 0; i < num_certs; i++) {
554 char *buf;
555 size_t len;
556 if (gnutls_x509_crt_init(&cert) < 0) {
557 wpa_printf(MSG_INFO, "TLS: Certificate initialization "
558 "failed");
559 return -1;
562 if (gnutls_x509_crt_import(cert, &certs[i],
563 GNUTLS_X509_FMT_DER) < 0) {
564 wpa_printf(MSG_INFO, "TLS: Could not parse peer "
565 "certificate %d/%d", i + 1, num_certs);
566 gnutls_x509_crt_deinit(cert);
567 return -1;
570 gnutls_x509_crt_get_dn(cert, NULL, &len);
571 len++;
572 buf = malloc(len + 1);
573 if (buf) {
574 buf[0] = buf[len] = '\0';
575 gnutls_x509_crt_get_dn(cert, buf, &len);
577 wpa_printf(MSG_DEBUG, "TLS: Peer cert chain %d/%d: %s",
578 i + 1, num_certs, buf);
580 if (i == 0) {
581 /* TODO: validate subject_match and altsubject_match */
584 free(buf);
586 if (gnutls_x509_crt_get_expiration_time(cert) < now ||
587 gnutls_x509_crt_get_activation_time(cert) > now) {
588 wpa_printf(MSG_INFO, "TLS: Peer certificate %d/%d is "
589 "not valid at this time",
590 i + 1, num_certs);
591 gnutls_x509_crt_deinit(cert);
592 return -1;
595 gnutls_x509_crt_deinit(cert);
598 return 0;
602 u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn,
603 const u8 *in_data, size_t in_len,
604 size_t *out_len)
606 u8 *out_data;
607 int ret;
609 if (in_data && in_len) {
610 if (conn->pull_buf) {
611 wpa_printf(MSG_DEBUG, "%s - %d bytes remaining in "
612 "pull_buf", __func__, conn->pull_buf_len);
613 free(conn->pull_buf);
615 conn->pull_buf = malloc(in_len);
616 if (conn->pull_buf == NULL)
617 return NULL;
618 memcpy(conn->pull_buf, in_data, in_len);
619 conn->pull_buf_offset = conn->pull_buf;
620 conn->pull_buf_len = in_len;
623 ret = gnutls_handshake(conn->session);
624 if (ret < 0) {
625 switch (ret) {
626 case GNUTLS_E_AGAIN:
627 break;
628 case GNUTLS_E_FATAL_ALERT_RECEIVED:
629 wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert",
630 __func__, gnutls_alert_get_name(
631 gnutls_alert_get(conn->session)));
632 conn->read_alerts++;
633 /* continue */
634 default:
635 wpa_printf(MSG_DEBUG, "%s - gnutls_handshake failed "
636 "-> %s", __func__, gnutls_strerror(ret));
637 conn->failed++;
639 } else {
640 wpa_printf(MSG_DEBUG, "TLS: Handshake completed successfully");
641 if (conn->verify_peer && tls_connection_verify_peer(conn)) {
642 wpa_printf(MSG_INFO, "TLS: Peer certificate chain "
643 "failed validation");
644 conn->failed++;
645 return NULL;
647 conn->established = 1;
648 if (conn->push_buf == NULL) {
649 /* Need to return something to get final TLS ACK. */
650 conn->push_buf = malloc(1);
654 out_data = conn->push_buf;
655 *out_len = conn->push_buf_len;
656 conn->push_buf = NULL;
657 conn->push_buf_len = 0;
658 return out_data;
662 u8 * tls_connection_server_handshake(void *ssl_ctx,
663 struct tls_connection *conn,
664 const u8 *in_data, size_t in_len,
665 size_t *out_len)
667 /* TODO */
668 return NULL;
672 int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn,
673 const u8 *in_data, size_t in_len,
674 u8 *out_data, size_t out_len)
676 ssize_t res;
677 res = gnutls_record_send(conn->session, in_data, in_len);
678 if (conn->push_buf == NULL)
679 return -1;
680 if (conn->push_buf_len < out_len)
681 out_len = conn->push_buf_len;
682 memcpy(out_data, conn->push_buf, out_len);
683 free(conn->push_buf);
684 conn->push_buf = NULL;
685 conn->push_buf_len = 0;
686 return out_len;
690 int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn,
691 const u8 *in_data, size_t in_len,
692 u8 *out_data, size_t out_len)
694 ssize_t res;
696 if (conn->pull_buf) {
697 wpa_printf(MSG_DEBUG, "%s - %d bytes remaining in "
698 "pull_buf", __func__, conn->pull_buf_len);
699 free(conn->pull_buf);
701 conn->pull_buf = malloc(in_len);
702 if (conn->pull_buf == NULL)
703 return -1;
704 memcpy(conn->pull_buf, in_data, in_len);
705 conn->pull_buf_offset = conn->pull_buf;
706 conn->pull_buf_len = in_len;
708 res = gnutls_record_recv(conn->session, out_data, out_len);
709 if (res < 0) {
710 wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d "
711 "(%s)", __func__, res, gnutls_strerror(res));
714 return res;
718 int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn)
720 if (conn == NULL)
721 return 0;
722 return gnutls_session_is_resumed(conn->session);
726 #ifdef EAP_FAST
727 int tls_connection_set_master_key(void *ssl_ctx, struct tls_connection *conn,
728 const u8 *key, size_t key_len)
730 /* TODO */
731 return -1;
733 #endif /* EAP_FAST */
736 int tls_connection_set_anon_dh(void *ssl_ctx, struct tls_connection *conn)
738 /* TODO: set ADH-AES128-SHA cipher */
739 return -1;
743 int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
744 char *buf, size_t buflen)
746 /* TODO */
747 buf[0] = '\0';
748 return 0;
752 int tls_connection_enable_workaround(void *ssl_ctx,
753 struct tls_connection *conn)
755 /* TODO: set SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS */
756 return 0;
760 #ifdef EAP_FAST
761 int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
762 int ext_type, const u8 *data,
763 size_t data_len)
765 /* TODO */
766 return -1;
768 #endif /* EAP_FAST */
771 int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn)
773 if (conn == NULL)
774 return -1;
775 return conn->failed;
779 int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn)
781 if (conn == NULL)
782 return -1;
783 return conn->read_alerts;
787 int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn)
789 if (conn == NULL)
790 return -1;
791 return conn->write_alerts;