1189251Ssam/* 2214734Srpaulo * SSL/TLS interface functions for GnuTLS 3252726Srpaulo * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi> 4189251Ssam * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7189251Ssam */ 8189251Ssam 9189251Ssam#include "includes.h" 10189251Ssam#include <gnutls/gnutls.h> 11189251Ssam#include <gnutls/x509.h> 12189251Ssam#ifdef PKCS12_FUNCS 13189251Ssam#include <gnutls/pkcs12.h> 14189251Ssam#endif /* PKCS12_FUNCS */ 15189251Ssam 16189251Ssam#include "common.h" 17189251Ssam#include "tls.h" 18189251Ssam 19189251Ssam 20252726Srpaulo#define WPA_TLS_RANDOM_SIZE 32 21252726Srpaulo#define WPA_TLS_MASTER_SIZE 48 22189251Ssam 23189251Ssam 24189251Ssam#if LIBGNUTLS_VERSION_NUMBER < 0x010302 25189251Ssam/* GnuTLS 1.3.2 added functions for using master secret. Older versions require 26189251Ssam * use of internal structures to get the master_secret and 27189251Ssam * {server,client}_random. 28189251Ssam */ 29189251Ssam#define GNUTLS_INTERNAL_STRUCTURE_HACK 30189251Ssam#endif /* LIBGNUTLS_VERSION_NUMBER < 0x010302 */ 31189251Ssam 32189251Ssam 33189251Ssam#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK 34189251Ssam/* 35189251Ssam * It looks like gnutls does not provide access to client/server_random and 36189251Ssam * master_key. This is somewhat unfortunate since these are needed for key 37189251Ssam * derivation in EAP-{TLS,TTLS,PEAP,FAST}. Workaround for now is a horrible 38189251Ssam * hack that copies the gnutls_session_int definition from gnutls_int.h so that 39189251Ssam * we can get the needed information. 40189251Ssam */ 41189251Ssam 42189251Ssamtypedef u8 uint8; 43189251Ssamtypedef unsigned char opaque; 44189251Ssamtypedef struct { 45189251Ssam uint8 suite[2]; 46189251Ssam} cipher_suite_st; 47189251Ssam 48189251Ssamtypedef struct { 49189251Ssam gnutls_connection_end_t entity; 50189251Ssam gnutls_kx_algorithm_t kx_algorithm; 51189251Ssam gnutls_cipher_algorithm_t read_bulk_cipher_algorithm; 52189251Ssam gnutls_mac_algorithm_t read_mac_algorithm; 53189251Ssam gnutls_compression_method_t read_compression_algorithm; 54189251Ssam gnutls_cipher_algorithm_t write_bulk_cipher_algorithm; 55189251Ssam gnutls_mac_algorithm_t write_mac_algorithm; 56189251Ssam gnutls_compression_method_t write_compression_algorithm; 57189251Ssam cipher_suite_st current_cipher_suite; 58252726Srpaulo opaque master_secret[WPA_TLS_MASTER_SIZE]; 59252726Srpaulo opaque client_random[WPA_TLS_RANDOM_SIZE]; 60252726Srpaulo opaque server_random[WPA_TLS_RANDOM_SIZE]; 61189251Ssam /* followed by stuff we are not interested in */ 62189251Ssam} security_parameters_st; 63189251Ssam 64189251Ssamstruct gnutls_session_int { 65189251Ssam security_parameters_st security_parameters; 66189251Ssam /* followed by things we are not interested in */ 67189251Ssam}; 68189251Ssam#endif /* LIBGNUTLS_VERSION_NUMBER < 0x010302 */ 69189251Ssam 70189251Ssamstatic int tls_gnutls_ref_count = 0; 71189251Ssam 72189251Ssamstruct tls_global { 73189251Ssam /* Data for session resumption */ 74189251Ssam void *session_data; 75189251Ssam size_t session_data_size; 76189251Ssam 77189251Ssam int server; 78189251Ssam 79189251Ssam int params_set; 80189251Ssam gnutls_certificate_credentials_t xcred; 81189251Ssam}; 82189251Ssam 83189251Ssamstruct tls_connection { 84189251Ssam gnutls_session session; 85189251Ssam char *subject_match, *altsubject_match; 86189251Ssam int read_alerts, write_alerts, failed; 87189251Ssam 88189251Ssam u8 *pre_shared_secret; 89189251Ssam size_t pre_shared_secret_len; 90189251Ssam int established; 91189251Ssam int verify_peer; 92189251Ssam 93214734Srpaulo struct wpabuf *push_buf; 94214734Srpaulo struct wpabuf *pull_buf; 95214734Srpaulo const u8 *pull_buf_offset; 96189251Ssam 97189251Ssam int params_set; 98189251Ssam gnutls_certificate_credentials_t xcred; 99189251Ssam}; 100189251Ssam 101189251Ssam 102189251Ssamstatic void tls_log_func(int level, const char *msg) 103189251Ssam{ 104189251Ssam char *s, *pos; 105189251Ssam if (level == 6 || level == 7) { 106189251Ssam /* These levels seem to be mostly I/O debug and msg dumps */ 107189251Ssam return; 108189251Ssam } 109189251Ssam 110189251Ssam s = os_strdup(msg); 111189251Ssam if (s == NULL) 112189251Ssam return; 113189251Ssam 114189251Ssam pos = s; 115189251Ssam while (*pos != '\0') { 116189251Ssam if (*pos == '\n') { 117189251Ssam *pos = '\0'; 118189251Ssam break; 119189251Ssam } 120189251Ssam pos++; 121189251Ssam } 122189251Ssam wpa_printf(level > 3 ? MSG_MSGDUMP : MSG_DEBUG, 123189251Ssam "gnutls<%d> %s", level, s); 124189251Ssam os_free(s); 125189251Ssam} 126189251Ssam 127189251Ssam 128189251Ssamextern int wpa_debug_show_keys; 129189251Ssam 130189251Ssamvoid * tls_init(const struct tls_config *conf) 131189251Ssam{ 132189251Ssam struct tls_global *global; 133189251Ssam 134189251Ssam#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK 135189251Ssam /* Because of the horrible hack to get master_secret and client/server 136189251Ssam * random, we need to make sure that the gnutls version is something 137189251Ssam * that is expected to have same structure definition for the session 138189251Ssam * data.. */ 139189251Ssam const char *ver; 140189251Ssam const char *ok_ver[] = { "1.2.3", "1.2.4", "1.2.5", "1.2.6", "1.2.9", 141189251Ssam "1.3.2", 142189251Ssam NULL }; 143189251Ssam int i; 144189251Ssam#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ 145189251Ssam 146189251Ssam global = os_zalloc(sizeof(*global)); 147189251Ssam if (global == NULL) 148189251Ssam return NULL; 149189251Ssam 150189251Ssam if (tls_gnutls_ref_count == 0 && gnutls_global_init() < 0) { 151189251Ssam os_free(global); 152189251Ssam return NULL; 153189251Ssam } 154189251Ssam tls_gnutls_ref_count++; 155189251Ssam 156189251Ssam#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK 157189251Ssam ver = gnutls_check_version(NULL); 158189251Ssam if (ver == NULL) { 159189251Ssam tls_deinit(global); 160189251Ssam return NULL; 161189251Ssam } 162189251Ssam wpa_printf(MSG_DEBUG, "%s - gnutls version %s", __func__, ver); 163189251Ssam for (i = 0; ok_ver[i]; i++) { 164189251Ssam if (strcmp(ok_ver[i], ver) == 0) 165189251Ssam break; 166189251Ssam } 167189251Ssam if (ok_ver[i] == NULL) { 168189251Ssam wpa_printf(MSG_INFO, "Untested gnutls version %s - this needs " 169189251Ssam "to be tested and enabled in tls_gnutls.c", ver); 170189251Ssam tls_deinit(global); 171189251Ssam return NULL; 172189251Ssam } 173189251Ssam#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ 174189251Ssam 175189251Ssam gnutls_global_set_log_function(tls_log_func); 176189251Ssam if (wpa_debug_show_keys) 177189251Ssam gnutls_global_set_log_level(11); 178189251Ssam return global; 179189251Ssam} 180189251Ssam 181189251Ssam 182189251Ssamvoid tls_deinit(void *ssl_ctx) 183189251Ssam{ 184189251Ssam struct tls_global *global = ssl_ctx; 185189251Ssam if (global) { 186189251Ssam if (global->params_set) 187189251Ssam gnutls_certificate_free_credentials(global->xcred); 188189251Ssam os_free(global->session_data); 189189251Ssam os_free(global); 190189251Ssam } 191189251Ssam 192189251Ssam tls_gnutls_ref_count--; 193189251Ssam if (tls_gnutls_ref_count == 0) 194189251Ssam gnutls_global_deinit(); 195189251Ssam} 196189251Ssam 197189251Ssam 198189251Ssamint tls_get_errors(void *ssl_ctx) 199189251Ssam{ 200189251Ssam return 0; 201189251Ssam} 202189251Ssam 203189251Ssam 204189251Ssamstatic ssize_t tls_pull_func(gnutls_transport_ptr ptr, void *buf, 205189251Ssam size_t len) 206189251Ssam{ 207189251Ssam struct tls_connection *conn = (struct tls_connection *) ptr; 208214734Srpaulo const u8 *end; 209189251Ssam if (conn->pull_buf == NULL) { 210189251Ssam errno = EWOULDBLOCK; 211189251Ssam return -1; 212189251Ssam } 213189251Ssam 214214734Srpaulo end = wpabuf_head_u8(conn->pull_buf) + wpabuf_len(conn->pull_buf); 215189251Ssam if ((size_t) (end - conn->pull_buf_offset) < len) 216189251Ssam len = end - conn->pull_buf_offset; 217189251Ssam os_memcpy(buf, conn->pull_buf_offset, len); 218189251Ssam conn->pull_buf_offset += len; 219189251Ssam if (conn->pull_buf_offset == end) { 220189251Ssam wpa_printf(MSG_DEBUG, "%s - pull_buf consumed", __func__); 221214734Srpaulo wpabuf_free(conn->pull_buf); 222214734Srpaulo conn->pull_buf = NULL; 223214734Srpaulo conn->pull_buf_offset = NULL; 224189251Ssam } else { 225189251Ssam wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in pull_buf", 226189251Ssam __func__, 227189251Ssam (unsigned long) (end - conn->pull_buf_offset)); 228189251Ssam } 229189251Ssam return len; 230189251Ssam} 231189251Ssam 232189251Ssam 233189251Ssamstatic ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf, 234189251Ssam size_t len) 235189251Ssam{ 236189251Ssam struct tls_connection *conn = (struct tls_connection *) ptr; 237189251Ssam 238214734Srpaulo if (wpabuf_resize(&conn->push_buf, len) < 0) { 239189251Ssam errno = ENOMEM; 240189251Ssam return -1; 241189251Ssam } 242214734Srpaulo wpabuf_put_data(conn->push_buf, buf, len); 243189251Ssam 244189251Ssam return len; 245189251Ssam} 246189251Ssam 247189251Ssam 248189251Ssamstatic int tls_gnutls_init_session(struct tls_global *global, 249189251Ssam struct tls_connection *conn) 250189251Ssam{ 251252726Srpaulo#if LIBGNUTLS_VERSION_NUMBER >= 0x020200 252252726Srpaulo const char *err; 253252726Srpaulo#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */ 254189251Ssam const int cert_types[2] = { GNUTLS_CRT_X509, 0 }; 255189251Ssam const int protos[2] = { GNUTLS_TLS1, 0 }; 256252726Srpaulo#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */ 257189251Ssam int ret; 258189251Ssam 259189251Ssam ret = gnutls_init(&conn->session, 260189251Ssam global->server ? GNUTLS_SERVER : GNUTLS_CLIENT); 261189251Ssam if (ret < 0) { 262189251Ssam wpa_printf(MSG_INFO, "TLS: Failed to initialize new TLS " 263189251Ssam "connection: %s", gnutls_strerror(ret)); 264189251Ssam return -1; 265189251Ssam } 266189251Ssam 267189251Ssam ret = gnutls_set_default_priority(conn->session); 268189251Ssam if (ret < 0) 269189251Ssam goto fail; 270189251Ssam 271252726Srpaulo#if LIBGNUTLS_VERSION_NUMBER >= 0x020200 272252726Srpaulo ret = gnutls_priority_set_direct(conn->session, "NORMAL:-VERS-SSL3.0", 273252726Srpaulo &err); 274252726Srpaulo if (ret < 0) { 275252726Srpaulo wpa_printf(MSG_ERROR, "GnuTLS: Priority string failure at " 276252726Srpaulo "'%s'", err); 277252726Srpaulo goto fail; 278252726Srpaulo } 279252726Srpaulo#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */ 280189251Ssam ret = gnutls_certificate_type_set_priority(conn->session, cert_types); 281189251Ssam if (ret < 0) 282189251Ssam goto fail; 283189251Ssam 284189251Ssam ret = gnutls_protocol_set_priority(conn->session, protos); 285189251Ssam if (ret < 0) 286189251Ssam goto fail; 287252726Srpaulo#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */ 288189251Ssam 289189251Ssam gnutls_transport_set_pull_function(conn->session, tls_pull_func); 290189251Ssam gnutls_transport_set_push_function(conn->session, tls_push_func); 291189251Ssam gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr) conn); 292189251Ssam 293189251Ssam return 0; 294189251Ssam 295189251Ssamfail: 296189251Ssam wpa_printf(MSG_INFO, "TLS: Failed to setup new TLS connection: %s", 297189251Ssam gnutls_strerror(ret)); 298189251Ssam gnutls_deinit(conn->session); 299189251Ssam return -1; 300189251Ssam} 301189251Ssam 302189251Ssam 303189251Ssamstruct tls_connection * tls_connection_init(void *ssl_ctx) 304189251Ssam{ 305189251Ssam struct tls_global *global = ssl_ctx; 306189251Ssam struct tls_connection *conn; 307189251Ssam int ret; 308189251Ssam 309189251Ssam conn = os_zalloc(sizeof(*conn)); 310189251Ssam if (conn == NULL) 311189251Ssam return NULL; 312189251Ssam 313189251Ssam if (tls_gnutls_init_session(global, conn)) { 314189251Ssam os_free(conn); 315189251Ssam return NULL; 316189251Ssam } 317189251Ssam 318189251Ssam if (global->params_set) { 319189251Ssam ret = gnutls_credentials_set(conn->session, 320189251Ssam GNUTLS_CRD_CERTIFICATE, 321189251Ssam global->xcred); 322189251Ssam if (ret < 0) { 323189251Ssam wpa_printf(MSG_INFO, "Failed to configure " 324189251Ssam "credentials: %s", gnutls_strerror(ret)); 325189251Ssam os_free(conn); 326189251Ssam return NULL; 327189251Ssam } 328189251Ssam } 329189251Ssam 330189251Ssam if (gnutls_certificate_allocate_credentials(&conn->xcred)) { 331189251Ssam os_free(conn); 332189251Ssam return NULL; 333189251Ssam } 334189251Ssam 335189251Ssam return conn; 336189251Ssam} 337189251Ssam 338189251Ssam 339189251Ssamvoid tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) 340189251Ssam{ 341189251Ssam if (conn == NULL) 342189251Ssam return; 343189251Ssam 344189251Ssam gnutls_certificate_free_credentials(conn->xcred); 345189251Ssam gnutls_deinit(conn->session); 346189251Ssam os_free(conn->pre_shared_secret); 347189251Ssam os_free(conn->subject_match); 348189251Ssam os_free(conn->altsubject_match); 349214734Srpaulo wpabuf_free(conn->push_buf); 350214734Srpaulo wpabuf_free(conn->pull_buf); 351189251Ssam os_free(conn); 352189251Ssam} 353189251Ssam 354189251Ssam 355189251Ssamint tls_connection_established(void *ssl_ctx, struct tls_connection *conn) 356189251Ssam{ 357189251Ssam return conn ? conn->established : 0; 358189251Ssam} 359189251Ssam 360189251Ssam 361189251Ssamint tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) 362189251Ssam{ 363189251Ssam struct tls_global *global = ssl_ctx; 364189251Ssam int ret; 365189251Ssam 366189251Ssam if (conn == NULL) 367189251Ssam return -1; 368189251Ssam 369189251Ssam /* Shutdown previous TLS connection without notifying the peer 370189251Ssam * because the connection was already terminated in practice 371189251Ssam * and "close notify" shutdown alert would confuse AS. */ 372189251Ssam gnutls_bye(conn->session, GNUTLS_SHUT_RDWR); 373214734Srpaulo wpabuf_free(conn->push_buf); 374189251Ssam conn->push_buf = NULL; 375189251Ssam conn->established = 0; 376189251Ssam 377189251Ssam gnutls_deinit(conn->session); 378189251Ssam if (tls_gnutls_init_session(global, conn)) { 379189251Ssam wpa_printf(MSG_INFO, "GnuTLS: Failed to preparare new session " 380189251Ssam "for session resumption use"); 381189251Ssam return -1; 382189251Ssam } 383189251Ssam 384189251Ssam ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, 385189251Ssam conn->params_set ? conn->xcred : 386189251Ssam global->xcred); 387189251Ssam if (ret < 0) { 388189251Ssam wpa_printf(MSG_INFO, "GnuTLS: Failed to configure credentials " 389189251Ssam "for session resumption: %s", gnutls_strerror(ret)); 390189251Ssam return -1; 391189251Ssam } 392189251Ssam 393189251Ssam if (global->session_data) { 394189251Ssam ret = gnutls_session_set_data(conn->session, 395189251Ssam global->session_data, 396189251Ssam global->session_data_size); 397189251Ssam if (ret < 0) { 398189251Ssam wpa_printf(MSG_INFO, "GnuTLS: Failed to set session " 399189251Ssam "data: %s", gnutls_strerror(ret)); 400189251Ssam return -1; 401189251Ssam } 402189251Ssam } 403189251Ssam 404189251Ssam return 0; 405189251Ssam} 406189251Ssam 407189251Ssam 408189251Ssam#if 0 409189251Ssamstatic int tls_match_altsubject(X509 *cert, const char *match) 410189251Ssam{ 411189251Ssam GENERAL_NAME *gen; 412189251Ssam char *field, *tmp; 413189251Ssam void *ext; 414189251Ssam int i, found = 0; 415189251Ssam size_t len; 416189251Ssam 417189251Ssam ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); 418189251Ssam 419189251Ssam for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) { 420189251Ssam gen = sk_GENERAL_NAME_value(ext, i); 421189251Ssam switch (gen->type) { 422189251Ssam case GEN_EMAIL: 423189251Ssam field = "EMAIL"; 424189251Ssam break; 425189251Ssam case GEN_DNS: 426189251Ssam field = "DNS"; 427189251Ssam break; 428189251Ssam case GEN_URI: 429189251Ssam field = "URI"; 430189251Ssam break; 431189251Ssam default: 432189251Ssam field = NULL; 433189251Ssam wpa_printf(MSG_DEBUG, "TLS: altSubjectName: " 434189251Ssam "unsupported type=%d", gen->type); 435189251Ssam break; 436189251Ssam } 437189251Ssam 438189251Ssam if (!field) 439189251Ssam continue; 440189251Ssam 441189251Ssam wpa_printf(MSG_DEBUG, "TLS: altSubjectName: %s:%s", 442189251Ssam field, gen->d.ia5->data); 443189251Ssam len = os_strlen(field) + 1 + 444189251Ssam strlen((char *) gen->d.ia5->data) + 1; 445189251Ssam tmp = os_malloc(len); 446189251Ssam if (tmp == NULL) 447189251Ssam continue; 448189251Ssam snprintf(tmp, len, "%s:%s", field, gen->d.ia5->data); 449189251Ssam if (strstr(tmp, match)) 450189251Ssam found++; 451189251Ssam os_free(tmp); 452189251Ssam } 453189251Ssam 454189251Ssam return found; 455189251Ssam} 456189251Ssam#endif 457189251Ssam 458189251Ssam 459189251Ssam#if 0 460189251Ssamstatic int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) 461189251Ssam{ 462189251Ssam char buf[256]; 463189251Ssam X509 *err_cert; 464189251Ssam int err, depth; 465189251Ssam SSL *ssl; 466189251Ssam struct tls_connection *conn; 467189251Ssam char *match, *altmatch; 468189251Ssam 469189251Ssam err_cert = X509_STORE_CTX_get_current_cert(x509_ctx); 470189251Ssam err = X509_STORE_CTX_get_error(x509_ctx); 471189251Ssam depth = X509_STORE_CTX_get_error_depth(x509_ctx); 472189251Ssam ssl = X509_STORE_CTX_get_ex_data(x509_ctx, 473189251Ssam SSL_get_ex_data_X509_STORE_CTX_idx()); 474189251Ssam X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf)); 475189251Ssam 476189251Ssam conn = SSL_get_app_data(ssl); 477189251Ssam match = conn ? conn->subject_match : NULL; 478189251Ssam altmatch = conn ? conn->altsubject_match : NULL; 479189251Ssam 480189251Ssam if (!preverify_ok) { 481189251Ssam wpa_printf(MSG_WARNING, "TLS: Certificate verification failed," 482189251Ssam " error %d (%s) depth %d for '%s'", err, 483189251Ssam X509_verify_cert_error_string(err), depth, buf); 484189251Ssam } else { 485189251Ssam wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - " 486189251Ssam "preverify_ok=%d err=%d (%s) depth=%d buf='%s'", 487189251Ssam preverify_ok, err, 488189251Ssam X509_verify_cert_error_string(err), depth, buf); 489189251Ssam if (depth == 0 && match && strstr(buf, match) == NULL) { 490189251Ssam wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not " 491189251Ssam "match with '%s'", buf, match); 492189251Ssam preverify_ok = 0; 493189251Ssam } else if (depth == 0 && altmatch && 494189251Ssam !tls_match_altsubject(err_cert, altmatch)) { 495189251Ssam wpa_printf(MSG_WARNING, "TLS: altSubjectName match " 496189251Ssam "'%s' not found", altmatch); 497189251Ssam preverify_ok = 0; 498189251Ssam } 499189251Ssam } 500189251Ssam 501189251Ssam return preverify_ok; 502189251Ssam} 503189251Ssam#endif 504189251Ssam 505189251Ssam 506189251Ssamint tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, 507189251Ssam const struct tls_connection_params *params) 508189251Ssam{ 509189251Ssam int ret; 510189251Ssam 511189251Ssam if (conn == NULL || params == NULL) 512189251Ssam return -1; 513189251Ssam 514189251Ssam os_free(conn->subject_match); 515189251Ssam conn->subject_match = NULL; 516189251Ssam if (params->subject_match) { 517189251Ssam conn->subject_match = os_strdup(params->subject_match); 518189251Ssam if (conn->subject_match == NULL) 519189251Ssam return -1; 520189251Ssam } 521189251Ssam 522189251Ssam os_free(conn->altsubject_match); 523189251Ssam conn->altsubject_match = NULL; 524189251Ssam if (params->altsubject_match) { 525189251Ssam conn->altsubject_match = os_strdup(params->altsubject_match); 526189251Ssam if (conn->altsubject_match == NULL) 527189251Ssam return -1; 528189251Ssam } 529189251Ssam 530189251Ssam /* TODO: gnutls_certificate_set_verify_flags(xcred, flags); 531189251Ssam * to force peer validation(?) */ 532189251Ssam 533189251Ssam if (params->ca_cert) { 534189251Ssam conn->verify_peer = 1; 535189251Ssam ret = gnutls_certificate_set_x509_trust_file( 536189251Ssam conn->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM); 537189251Ssam if (ret < 0) { 538189251Ssam wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' " 539189251Ssam "in PEM format: %s", params->ca_cert, 540189251Ssam gnutls_strerror(ret)); 541189251Ssam ret = gnutls_certificate_set_x509_trust_file( 542189251Ssam conn->xcred, params->ca_cert, 543189251Ssam GNUTLS_X509_FMT_DER); 544189251Ssam if (ret < 0) { 545189251Ssam wpa_printf(MSG_DEBUG, "Failed to read CA cert " 546189251Ssam "'%s' in DER format: %s", 547189251Ssam params->ca_cert, 548189251Ssam gnutls_strerror(ret)); 549189251Ssam return -1; 550189251Ssam } 551189251Ssam } 552209158Srpaulo 553209158Srpaulo if (params->flags & TLS_CONN_ALLOW_SIGN_RSA_MD5) { 554209158Srpaulo gnutls_certificate_set_verify_flags( 555209158Srpaulo conn->xcred, GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5); 556209158Srpaulo } 557209158Srpaulo 558252726Srpaulo#if LIBGNUTLS_VERSION_NUMBER >= 0x020800 559209158Srpaulo if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) { 560209158Srpaulo gnutls_certificate_set_verify_flags( 561209158Srpaulo conn->xcred, 562209158Srpaulo GNUTLS_VERIFY_DISABLE_TIME_CHECKS); 563209158Srpaulo } 564252726Srpaulo#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */ 565189251Ssam } 566189251Ssam 567189251Ssam if (params->client_cert && params->private_key) { 568189251Ssam /* TODO: private_key_passwd? */ 569189251Ssam ret = gnutls_certificate_set_x509_key_file( 570189251Ssam conn->xcred, params->client_cert, params->private_key, 571189251Ssam GNUTLS_X509_FMT_PEM); 572189251Ssam if (ret < 0) { 573189251Ssam wpa_printf(MSG_DEBUG, "Failed to read client cert/key " 574189251Ssam "in PEM format: %s", gnutls_strerror(ret)); 575189251Ssam ret = gnutls_certificate_set_x509_key_file( 576189251Ssam conn->xcred, params->client_cert, 577189251Ssam params->private_key, GNUTLS_X509_FMT_DER); 578189251Ssam if (ret < 0) { 579189251Ssam wpa_printf(MSG_DEBUG, "Failed to read client " 580189251Ssam "cert/key in DER format: %s", 581189251Ssam gnutls_strerror(ret)); 582189251Ssam return ret; 583189251Ssam } 584189251Ssam } 585189251Ssam } else if (params->private_key) { 586189251Ssam int pkcs12_ok = 0; 587189251Ssam#ifdef PKCS12_FUNCS 588189251Ssam /* Try to load in PKCS#12 format */ 589189251Ssam#if LIBGNUTLS_VERSION_NUMBER >= 0x010302 590189251Ssam ret = gnutls_certificate_set_x509_simple_pkcs12_file( 591189251Ssam conn->xcred, params->private_key, GNUTLS_X509_FMT_DER, 592189251Ssam params->private_key_passwd); 593189251Ssam if (ret != 0) { 594189251Ssam wpa_printf(MSG_DEBUG, "Failed to load private_key in " 595189251Ssam "PKCS#12 format: %s", gnutls_strerror(ret)); 596189251Ssam return -1; 597189251Ssam } else 598189251Ssam pkcs12_ok = 1; 599189251Ssam#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ 600189251Ssam#endif /* PKCS12_FUNCS */ 601189251Ssam 602189251Ssam if (!pkcs12_ok) { 603189251Ssam wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not " 604189251Ssam "included"); 605189251Ssam return -1; 606189251Ssam } 607189251Ssam } 608189251Ssam 609189251Ssam conn->params_set = 1; 610189251Ssam 611189251Ssam ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, 612189251Ssam conn->xcred); 613189251Ssam if (ret < 0) { 614189251Ssam wpa_printf(MSG_INFO, "Failed to configure credentials: %s", 615189251Ssam gnutls_strerror(ret)); 616189251Ssam } 617189251Ssam 618189251Ssam return ret; 619189251Ssam} 620189251Ssam 621189251Ssam 622189251Ssamint tls_global_set_params(void *tls_ctx, 623189251Ssam const struct tls_connection_params *params) 624189251Ssam{ 625189251Ssam struct tls_global *global = tls_ctx; 626189251Ssam int ret; 627189251Ssam 628189251Ssam /* Currently, global parameters are only set when running in server 629189251Ssam * mode. */ 630189251Ssam global->server = 1; 631189251Ssam 632189251Ssam if (global->params_set) { 633189251Ssam gnutls_certificate_free_credentials(global->xcred); 634189251Ssam global->params_set = 0; 635189251Ssam } 636189251Ssam 637189251Ssam ret = gnutls_certificate_allocate_credentials(&global->xcred); 638189251Ssam if (ret) { 639189251Ssam wpa_printf(MSG_DEBUG, "Failed to allocate global credentials " 640189251Ssam "%s", gnutls_strerror(ret)); 641189251Ssam return -1; 642189251Ssam } 643189251Ssam 644189251Ssam if (params->ca_cert) { 645189251Ssam ret = gnutls_certificate_set_x509_trust_file( 646189251Ssam global->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM); 647189251Ssam if (ret < 0) { 648189251Ssam wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' " 649189251Ssam "in PEM format: %s", params->ca_cert, 650189251Ssam gnutls_strerror(ret)); 651189251Ssam ret = gnutls_certificate_set_x509_trust_file( 652189251Ssam global->xcred, params->ca_cert, 653189251Ssam GNUTLS_X509_FMT_DER); 654189251Ssam if (ret < 0) { 655189251Ssam wpa_printf(MSG_DEBUG, "Failed to read CA cert " 656189251Ssam "'%s' in DER format: %s", 657189251Ssam params->ca_cert, 658189251Ssam gnutls_strerror(ret)); 659189251Ssam goto fail; 660189251Ssam } 661189251Ssam } 662209158Srpaulo 663209158Srpaulo if (params->flags & TLS_CONN_ALLOW_SIGN_RSA_MD5) { 664209158Srpaulo gnutls_certificate_set_verify_flags( 665209158Srpaulo global->xcred, 666209158Srpaulo GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5); 667209158Srpaulo } 668209158Srpaulo 669252726Srpaulo#if LIBGNUTLS_VERSION_NUMBER >= 0x020800 670209158Srpaulo if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) { 671209158Srpaulo gnutls_certificate_set_verify_flags( 672209158Srpaulo global->xcred, 673209158Srpaulo GNUTLS_VERIFY_DISABLE_TIME_CHECKS); 674209158Srpaulo } 675252726Srpaulo#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */ 676189251Ssam } 677189251Ssam 678189251Ssam if (params->client_cert && params->private_key) { 679189251Ssam /* TODO: private_key_passwd? */ 680189251Ssam ret = gnutls_certificate_set_x509_key_file( 681189251Ssam global->xcred, params->client_cert, 682189251Ssam params->private_key, GNUTLS_X509_FMT_PEM); 683189251Ssam if (ret < 0) { 684189251Ssam wpa_printf(MSG_DEBUG, "Failed to read client cert/key " 685189251Ssam "in PEM format: %s", gnutls_strerror(ret)); 686189251Ssam ret = gnutls_certificate_set_x509_key_file( 687189251Ssam global->xcred, params->client_cert, 688189251Ssam params->private_key, GNUTLS_X509_FMT_DER); 689189251Ssam if (ret < 0) { 690189251Ssam wpa_printf(MSG_DEBUG, "Failed to read client " 691189251Ssam "cert/key in DER format: %s", 692189251Ssam gnutls_strerror(ret)); 693189251Ssam goto fail; 694189251Ssam } 695189251Ssam } 696189251Ssam } else if (params->private_key) { 697189251Ssam int pkcs12_ok = 0; 698189251Ssam#ifdef PKCS12_FUNCS 699189251Ssam /* Try to load in PKCS#12 format */ 700189251Ssam#if LIBGNUTLS_VERSION_NUMBER >= 0x010302 701189251Ssam ret = gnutls_certificate_set_x509_simple_pkcs12_file( 702189251Ssam global->xcred, params->private_key, 703189251Ssam GNUTLS_X509_FMT_DER, params->private_key_passwd); 704189251Ssam if (ret != 0) { 705189251Ssam wpa_printf(MSG_DEBUG, "Failed to load private_key in " 706189251Ssam "PKCS#12 format: %s", gnutls_strerror(ret)); 707189251Ssam goto fail; 708189251Ssam } else 709189251Ssam pkcs12_ok = 1; 710189251Ssam#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ 711189251Ssam#endif /* PKCS12_FUNCS */ 712189251Ssam 713189251Ssam if (!pkcs12_ok) { 714189251Ssam wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not " 715189251Ssam "included"); 716189251Ssam goto fail; 717189251Ssam } 718189251Ssam } 719189251Ssam 720189251Ssam global->params_set = 1; 721189251Ssam 722189251Ssam return 0; 723189251Ssam 724189251Ssamfail: 725189251Ssam gnutls_certificate_free_credentials(global->xcred); 726189251Ssam return -1; 727189251Ssam} 728189251Ssam 729189251Ssam 730189251Ssamint tls_global_set_verify(void *ssl_ctx, int check_crl) 731189251Ssam{ 732189251Ssam /* TODO */ 733189251Ssam return 0; 734189251Ssam} 735189251Ssam 736189251Ssam 737189251Ssamint tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, 738189251Ssam int verify_peer) 739189251Ssam{ 740189251Ssam if (conn == NULL || conn->session == NULL) 741189251Ssam return -1; 742189251Ssam 743189251Ssam conn->verify_peer = verify_peer; 744189251Ssam gnutls_certificate_server_set_request(conn->session, 745189251Ssam verify_peer ? GNUTLS_CERT_REQUIRE 746189251Ssam : GNUTLS_CERT_REQUEST); 747189251Ssam 748189251Ssam return 0; 749189251Ssam} 750189251Ssam 751189251Ssam 752189251Ssamint tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, 753189251Ssam struct tls_keys *keys) 754189251Ssam{ 755189251Ssam#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK 756189251Ssam security_parameters_st *sec; 757189251Ssam#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ 758189251Ssam 759189251Ssam if (conn == NULL || conn->session == NULL || keys == NULL) 760189251Ssam return -1; 761189251Ssam 762189251Ssam os_memset(keys, 0, sizeof(*keys)); 763189251Ssam 764252726Srpaulo#if LIBGNUTLS_VERSION_NUMBER < 0x020c00 765189251Ssam#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK 766189251Ssam sec = &conn->session->security_parameters; 767189251Ssam keys->master_key = sec->master_secret; 768252726Srpaulo keys->master_key_len = WPA_TLS_MASTER_SIZE; 769189251Ssam keys->client_random = sec->client_random; 770189251Ssam keys->server_random = sec->server_random; 771189251Ssam#else /* GNUTLS_INTERNAL_STRUCTURE_HACK */ 772189251Ssam keys->client_random = 773189251Ssam (u8 *) gnutls_session_get_client_random(conn->session); 774189251Ssam keys->server_random = 775189251Ssam (u8 *) gnutls_session_get_server_random(conn->session); 776189251Ssam /* No access to master_secret */ 777189251Ssam#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ 778252726Srpaulo#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */ 779189251Ssam 780252726Srpaulo#if LIBGNUTLS_VERSION_NUMBER < 0x020c00 781252726Srpaulo keys->client_random_len = WPA_TLS_RANDOM_SIZE; 782252726Srpaulo keys->server_random_len = WPA_TLS_RANDOM_SIZE; 783252726Srpaulo#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */ 784189251Ssam 785189251Ssam return 0; 786189251Ssam} 787189251Ssam 788189251Ssam 789189251Ssamint tls_connection_prf(void *tls_ctx, struct tls_connection *conn, 790189251Ssam const char *label, int server_random_first, 791189251Ssam u8 *out, size_t out_len) 792189251Ssam{ 793189251Ssam#if LIBGNUTLS_VERSION_NUMBER >= 0x010302 794189251Ssam if (conn == NULL || conn->session == NULL) 795189251Ssam return -1; 796189251Ssam 797189251Ssam return gnutls_prf(conn->session, os_strlen(label), label, 798189251Ssam server_random_first, 0, NULL, out_len, (char *) out); 799189251Ssam#else /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ 800189251Ssam return -1; 801189251Ssam#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ 802189251Ssam} 803189251Ssam 804189251Ssam 805209158Srpaulostatic int tls_connection_verify_peer(struct tls_connection *conn, 806209158Srpaulo gnutls_alert_description_t *err) 807189251Ssam{ 808189251Ssam unsigned int status, num_certs, i; 809189251Ssam struct os_time now; 810189251Ssam const gnutls_datum_t *certs; 811189251Ssam gnutls_x509_crt_t cert; 812189251Ssam 813189251Ssam if (gnutls_certificate_verify_peers2(conn->session, &status) < 0) { 814189251Ssam wpa_printf(MSG_INFO, "TLS: Failed to verify peer " 815189251Ssam "certificate chain"); 816209158Srpaulo *err = GNUTLS_A_INTERNAL_ERROR; 817189251Ssam return -1; 818189251Ssam } 819189251Ssam 820189251Ssam if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) { 821189251Ssam wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted"); 822252726Srpaulo *err = GNUTLS_A_INTERNAL_ERROR; 823209158Srpaulo if (status & GNUTLS_CERT_INSECURE_ALGORITHM) { 824209158Srpaulo wpa_printf(MSG_INFO, "TLS: Certificate uses insecure " 825209158Srpaulo "algorithm"); 826209158Srpaulo *err = GNUTLS_A_INSUFFICIENT_SECURITY; 827209158Srpaulo } 828252726Srpaulo#if LIBGNUTLS_VERSION_NUMBER >= 0x020800 829209158Srpaulo if (status & GNUTLS_CERT_NOT_ACTIVATED) { 830209158Srpaulo wpa_printf(MSG_INFO, "TLS: Certificate not yet " 831209158Srpaulo "activated"); 832209158Srpaulo *err = GNUTLS_A_CERTIFICATE_EXPIRED; 833209158Srpaulo } 834209158Srpaulo if (status & GNUTLS_CERT_EXPIRED) { 835209158Srpaulo wpa_printf(MSG_INFO, "TLS: Certificate expired"); 836209158Srpaulo *err = GNUTLS_A_CERTIFICATE_EXPIRED; 837209158Srpaulo } 838252726Srpaulo#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */ 839189251Ssam return -1; 840189251Ssam } 841189251Ssam 842189251Ssam if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { 843189251Ssam wpa_printf(MSG_INFO, "TLS: Peer certificate does not have a " 844189251Ssam "known issuer"); 845209158Srpaulo *err = GNUTLS_A_UNKNOWN_CA; 846189251Ssam return -1; 847189251Ssam } 848189251Ssam 849189251Ssam if (status & GNUTLS_CERT_REVOKED) { 850189251Ssam wpa_printf(MSG_INFO, "TLS: Peer certificate has been revoked"); 851209158Srpaulo *err = GNUTLS_A_CERTIFICATE_REVOKED; 852189251Ssam return -1; 853189251Ssam } 854189251Ssam 855189251Ssam os_get_time(&now); 856189251Ssam 857189251Ssam certs = gnutls_certificate_get_peers(conn->session, &num_certs); 858189251Ssam if (certs == NULL) { 859189251Ssam wpa_printf(MSG_INFO, "TLS: No peer certificate chain " 860189251Ssam "received"); 861209158Srpaulo *err = GNUTLS_A_UNKNOWN_CA; 862189251Ssam return -1; 863189251Ssam } 864189251Ssam 865189251Ssam for (i = 0; i < num_certs; i++) { 866189251Ssam char *buf; 867189251Ssam size_t len; 868189251Ssam if (gnutls_x509_crt_init(&cert) < 0) { 869189251Ssam wpa_printf(MSG_INFO, "TLS: Certificate initialization " 870189251Ssam "failed"); 871209158Srpaulo *err = GNUTLS_A_BAD_CERTIFICATE; 872189251Ssam return -1; 873189251Ssam } 874189251Ssam 875189251Ssam if (gnutls_x509_crt_import(cert, &certs[i], 876189251Ssam GNUTLS_X509_FMT_DER) < 0) { 877189251Ssam wpa_printf(MSG_INFO, "TLS: Could not parse peer " 878189251Ssam "certificate %d/%d", i + 1, num_certs); 879189251Ssam gnutls_x509_crt_deinit(cert); 880209158Srpaulo *err = GNUTLS_A_BAD_CERTIFICATE; 881189251Ssam return -1; 882189251Ssam } 883189251Ssam 884189251Ssam gnutls_x509_crt_get_dn(cert, NULL, &len); 885189251Ssam len++; 886189251Ssam buf = os_malloc(len + 1); 887189251Ssam if (buf) { 888189251Ssam buf[0] = buf[len] = '\0'; 889189251Ssam gnutls_x509_crt_get_dn(cert, buf, &len); 890189251Ssam } 891189251Ssam wpa_printf(MSG_DEBUG, "TLS: Peer cert chain %d/%d: %s", 892189251Ssam i + 1, num_certs, buf); 893189251Ssam 894189251Ssam if (i == 0) { 895189251Ssam /* TODO: validate subject_match and altsubject_match */ 896189251Ssam } 897189251Ssam 898189251Ssam os_free(buf); 899189251Ssam 900189251Ssam if (gnutls_x509_crt_get_expiration_time(cert) < now.sec || 901189251Ssam gnutls_x509_crt_get_activation_time(cert) > now.sec) { 902189251Ssam wpa_printf(MSG_INFO, "TLS: Peer certificate %d/%d is " 903189251Ssam "not valid at this time", 904189251Ssam i + 1, num_certs); 905189251Ssam gnutls_x509_crt_deinit(cert); 906209158Srpaulo *err = GNUTLS_A_CERTIFICATE_EXPIRED; 907189251Ssam return -1; 908189251Ssam } 909189251Ssam 910189251Ssam gnutls_x509_crt_deinit(cert); 911189251Ssam } 912189251Ssam 913189251Ssam return 0; 914189251Ssam} 915189251Ssam 916189251Ssam 917214734Srpaulostatic struct wpabuf * gnutls_get_appl_data(struct tls_connection *conn) 918189251Ssam{ 919214734Srpaulo int res; 920214734Srpaulo struct wpabuf *ad; 921214734Srpaulo wpa_printf(MSG_DEBUG, "GnuTLS: Check for possible Application Data"); 922214734Srpaulo ad = wpabuf_alloc((wpabuf_len(conn->pull_buf) + 500) * 3); 923214734Srpaulo if (ad == NULL) 924214734Srpaulo return NULL; 925214734Srpaulo 926214734Srpaulo res = gnutls_record_recv(conn->session, wpabuf_mhead(ad), 927214734Srpaulo wpabuf_size(ad)); 928214734Srpaulo wpa_printf(MSG_DEBUG, "GnuTLS: gnutls_record_recv: %d", res); 929214734Srpaulo if (res < 0) { 930252726Srpaulo wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d " 931214734Srpaulo "(%s)", __func__, (int) res, 932214734Srpaulo gnutls_strerror(res)); 933214734Srpaulo wpabuf_free(ad); 934214734Srpaulo return NULL; 935214734Srpaulo } 936214734Srpaulo 937214734Srpaulo wpabuf_put(ad, res); 938214734Srpaulo wpa_printf(MSG_DEBUG, "GnuTLS: Received %d bytes of Application Data", 939214734Srpaulo res); 940214734Srpaulo return ad; 941214734Srpaulo} 942214734Srpaulo 943214734Srpaulo 944214734Srpaulostruct wpabuf * tls_connection_handshake(void *tls_ctx, 945214734Srpaulo struct tls_connection *conn, 946214734Srpaulo const struct wpabuf *in_data, 947214734Srpaulo struct wpabuf **appl_data) 948214734Srpaulo{ 949214734Srpaulo struct tls_global *global = tls_ctx; 950214734Srpaulo struct wpabuf *out_data; 951189251Ssam int ret; 952189251Ssam 953189251Ssam if (appl_data) 954189251Ssam *appl_data = NULL; 955189251Ssam 956214734Srpaulo if (in_data && wpabuf_len(in_data) > 0) { 957189251Ssam if (conn->pull_buf) { 958189251Ssam wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in " 959189251Ssam "pull_buf", __func__, 960214734Srpaulo (unsigned long) wpabuf_len(conn->pull_buf)); 961214734Srpaulo wpabuf_free(conn->pull_buf); 962189251Ssam } 963214734Srpaulo conn->pull_buf = wpabuf_dup(in_data); 964189251Ssam if (conn->pull_buf == NULL) 965189251Ssam return NULL; 966214734Srpaulo conn->pull_buf_offset = wpabuf_head(conn->pull_buf); 967189251Ssam } 968189251Ssam 969189251Ssam ret = gnutls_handshake(conn->session); 970189251Ssam if (ret < 0) { 971189251Ssam switch (ret) { 972189251Ssam case GNUTLS_E_AGAIN: 973189251Ssam if (global->server && conn->established && 974189251Ssam conn->push_buf == NULL) { 975189251Ssam /* Need to return something to trigger 976189251Ssam * completion of EAP-TLS. */ 977214734Srpaulo conn->push_buf = wpabuf_alloc(0); 978189251Ssam } 979189251Ssam break; 980189251Ssam case GNUTLS_E_FATAL_ALERT_RECEIVED: 981189251Ssam wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert", 982189251Ssam __func__, gnutls_alert_get_name( 983189251Ssam gnutls_alert_get(conn->session))); 984189251Ssam conn->read_alerts++; 985189251Ssam /* continue */ 986189251Ssam default: 987189251Ssam wpa_printf(MSG_DEBUG, "%s - gnutls_handshake failed " 988189251Ssam "-> %s", __func__, gnutls_strerror(ret)); 989189251Ssam conn->failed++; 990189251Ssam } 991189251Ssam } else { 992189251Ssam size_t size; 993209158Srpaulo gnutls_alert_description_t err; 994189251Ssam 995209158Srpaulo if (conn->verify_peer && 996209158Srpaulo tls_connection_verify_peer(conn, &err)) { 997189251Ssam wpa_printf(MSG_INFO, "TLS: Peer certificate chain " 998189251Ssam "failed validation"); 999189251Ssam conn->failed++; 1000209158Srpaulo gnutls_alert_send(conn->session, GNUTLS_AL_FATAL, err); 1001209158Srpaulo goto out; 1002189251Ssam } 1003189251Ssam 1004252726Srpaulo wpa_printf(MSG_DEBUG, "TLS: Handshake completed successfully"); 1005189251Ssam conn->established = 1; 1006189251Ssam if (conn->push_buf == NULL) { 1007189251Ssam /* Need to return something to get final TLS ACK. */ 1008214734Srpaulo conn->push_buf = wpabuf_alloc(0); 1009189251Ssam } 1010189251Ssam 1011189251Ssam gnutls_session_get_data(conn->session, NULL, &size); 1012189251Ssam if (global->session_data == NULL || 1013189251Ssam global->session_data_size < size) { 1014189251Ssam os_free(global->session_data); 1015189251Ssam global->session_data = os_malloc(size); 1016189251Ssam } 1017189251Ssam if (global->session_data) { 1018189251Ssam global->session_data_size = size; 1019189251Ssam gnutls_session_get_data(conn->session, 1020189251Ssam global->session_data, 1021189251Ssam &global->session_data_size); 1022189251Ssam } 1023214734Srpaulo 1024214734Srpaulo if (conn->pull_buf && appl_data) 1025214734Srpaulo *appl_data = gnutls_get_appl_data(conn); 1026189251Ssam } 1027189251Ssam 1028209158Srpauloout: 1029189251Ssam out_data = conn->push_buf; 1030189251Ssam conn->push_buf = NULL; 1031189251Ssam return out_data; 1032189251Ssam} 1033189251Ssam 1034189251Ssam 1035214734Srpaulostruct wpabuf * tls_connection_server_handshake(void *tls_ctx, 1036214734Srpaulo struct tls_connection *conn, 1037214734Srpaulo const struct wpabuf *in_data, 1038214734Srpaulo struct wpabuf **appl_data) 1039189251Ssam{ 1040214734Srpaulo return tls_connection_handshake(tls_ctx, conn, in_data, appl_data); 1041189251Ssam} 1042189251Ssam 1043189251Ssam 1044214734Srpaulostruct wpabuf * tls_connection_encrypt(void *tls_ctx, 1045214734Srpaulo struct tls_connection *conn, 1046214734Srpaulo const struct wpabuf *in_data) 1047189251Ssam{ 1048189251Ssam ssize_t res; 1049214734Srpaulo struct wpabuf *buf; 1050189251Ssam 1051214734Srpaulo res = gnutls_record_send(conn->session, wpabuf_head(in_data), 1052214734Srpaulo wpabuf_len(in_data)); 1053189251Ssam if (res < 0) { 1054189251Ssam wpa_printf(MSG_INFO, "%s: Encryption failed: %s", 1055189251Ssam __func__, gnutls_strerror(res)); 1056214734Srpaulo return NULL; 1057189251Ssam } 1058214734Srpaulo 1059214734Srpaulo buf = conn->push_buf; 1060189251Ssam conn->push_buf = NULL; 1061214734Srpaulo return buf; 1062189251Ssam} 1063189251Ssam 1064189251Ssam 1065214734Srpaulostruct wpabuf * tls_connection_decrypt(void *tls_ctx, 1066214734Srpaulo struct tls_connection *conn, 1067214734Srpaulo const struct wpabuf *in_data) 1068189251Ssam{ 1069189251Ssam ssize_t res; 1070214734Srpaulo struct wpabuf *out; 1071189251Ssam 1072189251Ssam if (conn->pull_buf) { 1073189251Ssam wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in " 1074189251Ssam "pull_buf", __func__, 1075214734Srpaulo (unsigned long) wpabuf_len(conn->pull_buf)); 1076214734Srpaulo wpabuf_free(conn->pull_buf); 1077189251Ssam } 1078214734Srpaulo conn->pull_buf = wpabuf_dup(in_data); 1079189251Ssam if (conn->pull_buf == NULL) 1080214734Srpaulo return NULL; 1081214734Srpaulo conn->pull_buf_offset = wpabuf_head(conn->pull_buf); 1082189251Ssam 1083214734Srpaulo /* 1084214734Srpaulo * Even though we try to disable TLS compression, it is possible that 1085214734Srpaulo * this cannot be done with all TLS libraries. Add extra buffer space 1086214734Srpaulo * to handle the possibility of the decrypted data being longer than 1087214734Srpaulo * input data. 1088214734Srpaulo */ 1089214734Srpaulo out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3); 1090214734Srpaulo if (out == NULL) 1091214734Srpaulo return NULL; 1092214734Srpaulo 1093214734Srpaulo res = gnutls_record_recv(conn->session, wpabuf_mhead(out), 1094214734Srpaulo wpabuf_size(out)); 1095189251Ssam if (res < 0) { 1096189251Ssam wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d " 1097189251Ssam "(%s)", __func__, (int) res, gnutls_strerror(res)); 1098214734Srpaulo wpabuf_free(out); 1099214734Srpaulo return NULL; 1100189251Ssam } 1101214734Srpaulo wpabuf_put(out, res); 1102189251Ssam 1103214734Srpaulo return out; 1104189251Ssam} 1105189251Ssam 1106189251Ssam 1107189251Ssamint tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) 1108189251Ssam{ 1109189251Ssam if (conn == NULL) 1110189251Ssam return 0; 1111189251Ssam return gnutls_session_is_resumed(conn->session); 1112189251Ssam} 1113189251Ssam 1114189251Ssam 1115189251Ssamint tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, 1116189251Ssam u8 *ciphers) 1117189251Ssam{ 1118189251Ssam /* TODO */ 1119189251Ssam return -1; 1120189251Ssam} 1121189251Ssam 1122189251Ssam 1123189251Ssamint tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, 1124189251Ssam char *buf, size_t buflen) 1125189251Ssam{ 1126189251Ssam /* TODO */ 1127189251Ssam buf[0] = '\0'; 1128189251Ssam return 0; 1129189251Ssam} 1130189251Ssam 1131189251Ssam 1132189251Ssamint tls_connection_enable_workaround(void *ssl_ctx, 1133189251Ssam struct tls_connection *conn) 1134189251Ssam{ 1135214734Srpaulo gnutls_record_disable_padding(conn->session); 1136189251Ssam return 0; 1137189251Ssam} 1138189251Ssam 1139189251Ssam 1140189251Ssamint tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, 1141189251Ssam int ext_type, const u8 *data, 1142189251Ssam size_t data_len) 1143189251Ssam{ 1144189251Ssam /* TODO */ 1145189251Ssam return -1; 1146189251Ssam} 1147189251Ssam 1148189251Ssam 1149189251Ssamint tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) 1150189251Ssam{ 1151189251Ssam if (conn == NULL) 1152189251Ssam return -1; 1153189251Ssam return conn->failed; 1154189251Ssam} 1155189251Ssam 1156189251Ssam 1157189251Ssamint tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn) 1158189251Ssam{ 1159189251Ssam if (conn == NULL) 1160189251Ssam return -1; 1161189251Ssam return conn->read_alerts; 1162189251Ssam} 1163189251Ssam 1164189251Ssam 1165189251Ssamint tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) 1166189251Ssam{ 1167189251Ssam if (conn == NULL) 1168189251Ssam return -1; 1169189251Ssam return conn->write_alerts; 1170189251Ssam} 1171189251Ssam 1172189251Ssam 1173189251Ssamint tls_connection_get_keyblock_size(void *tls_ctx, 1174189251Ssam struct tls_connection *conn) 1175189251Ssam{ 1176189251Ssam /* TODO */ 1177189251Ssam return -1; 1178189251Ssam} 1179189251Ssam 1180189251Ssam 1181189251Ssamunsigned int tls_capabilities(void *tls_ctx) 1182189251Ssam{ 1183189251Ssam return 0; 1184189251Ssam} 1185189251Ssam 1186189251Ssam 1187214734Srpauloint tls_connection_set_session_ticket_cb(void *tls_ctx, 1188214734Srpaulo struct tls_connection *conn, 1189214734Srpaulo tls_session_ticket_cb cb, void *ctx) 1190214734Srpaulo{ 1191214734Srpaulo return -1; 1192214734Srpaulo} 1193