+Makefile.in
+aclocal.m4
+autom4te.cache
+configure
+config.*
+ltmain.sh
+libtool
+depcomp
+m4
+!m4/minuso.m4
+build-aux
+!build-aux/compile
+mech_eap.spec
+mech_eap*tar*
+*.lo
+ *.o
+ *.d
+ *.gcno
+ *.gcda
+ *.gcov
+ *.pyc
+*#
+ *~
+ .config
+ tests/hwsim/logs
+ wpaspy/build
+ wpa_supplicant/eapol_test
+ wpa_supplicant/nfc_pw_token
+ wpa_supplicant/preauth_test
+ wpa_supplicant/wpa_cli
+ wpa_supplicant/wpa_passphrase
+ wpa_supplicant/wpa_supplicant
+ wpa_supplicant/wpa_priv
+ wpa_supplicant/wpa_gui/Makefile
+ wpa_supplicant/wpa_gui/wpa_gui
+ wpa_supplicant/wpa_gui-qt4/Makefile
+ wpa_supplicant/wpa_gui-qt4/wpa_gui
+ hostapd/hostapd
+ hostapd/hostapd_cli
+ hostapd/hlr_auc_gw
+ hostapd/nt_password_hash
+ mac80211_hwsim/tools/hwsim_test
+ wlantest/libwlantest.a
+ wlantest/test_vectors
+ wlantest/wlantest
+ wlantest/wlantest_cli
--- /dev/null
- src/crypto/md5-non-fips.c \
+AUTOMAKE_OPTIONS = foreign
+
+AM_CPPFLAGS = -I$(srcdir)/src -I$(srcdir)/eap_example -I$(srcdir)/src/utils @OPENSSL_CFLAGS@
+noinst_HEADERS = \
+ src/common/defs.h \
+ src/common/eapol_common.h \
+ src/common/ieee802_11_common.h \
+ src/common/ieee802_11_defs.h \
+ src/common/privsep_commands.h \
+ src/common/version.h \
+ src/common/wpa_common.h \
+ src/common/wpa_ctrl.h
+
+EXTRA_DIST = src/tls/libtommath.c \
+ wpa_supplicant/README
+
+SOURCES_BOTH = src/eap_common/eap_peap_common.c
+SOURCES_BOTH += src/eap_common/eap_psk_common.c
+SOURCES_BOTH += src/eap_common/eap_pax_common.c
+SOURCES_BOTH += src/eap_common/eap_sake_common.c
+SOURCES_BOTH += src/eap_common/eap_gpsk_common.c
+SOURCES_BOTH += src/eap_common/chap.c \
+ src/eap_common/chap.h \
+ src/eap_common/eap_common.h \
+ src/eap_common/eap_defs.h \
+ src/eap_common/eap_fast_common.h \
+ src/eap_common/eap_gpsk_common.h \
+ src/eap_common/eap_ikev2_common.h \
+ src/eap_common/eap_pax_common.h \
+ src/eap_common/eap_peap_common.h \
+ src/eap_common/eap_psk_common.h \
+ src/eap_common/eap_pwd_common.h \
+ src/eap_common/eap_sake_common.h \
+ src/eap_common/eap_sim_common.h \
+ src/eap_common/eap_tlv_common.h \
+ src/eap_common/eap_ttls.h \
+ src/eap_common/eap_wsc_common.h \
+ src/eap_common/ikev2_common.h
+
+
+SOURCES_peer = src/eap_peer/eap_tls.c
+SOURCES_peer += src/eap_peer/eap_peap.c
+SOURCES_peer += src/eap_peer/eap_ttls.c
+SOURCES_peer += src/eap_peer/eap_md5.c
+SOURCES_peer += src/eap_peer/eap_mschapv2.c
+SOURCES_peer += src/eap_peer/mschapv2.c
+SOURCES_peer += src/eap_peer/eap_otp.c
+SOURCES_peer += src/eap_peer/eap_gtc.c
+SOURCES_peer += src/eap_peer/eap_leap.c
+SOURCES_peer += src/eap_peer/eap_psk.c
+SOURCES_peer += src/eap_peer/eap_pax.c
+SOURCES_peer += src/eap_peer/eap_sake.c
+SOURCES_peer += src/eap_peer/eap_gpsk.c
+SOURCES_peer += src/eap_peer/eap.c
+SOURCES_peer += src/eap_common/eap_common.c
+SOURCES_peer += src/eap_peer/eap_methods.c
+SOURCES_peer += src/eap_peer/eap_tls_common.c \
+ src/eap_peer/eap_config.h \
+ src/eap_peer/eap_fast_pac.h \
+ src/eap_peer/eap.h \
+ src/eap_peer/eap_i.h \
+ src/eap_peer/eap_methods.h \
+ src/eap_peer/eap_tls_common.h \
+ src/eap_peer/ikev2.h \
+ src/eap_peer/mschapv2.h \
+ src/eap_peer/tncc.h \
+ src/radius/radius.h
+
+
+AM_CFLAGS = -DEAP_TLS
+AM_CFLAGS += -DEAP_PEAP
+AM_CFLAGS += -DEAP_TTLS
+AM_CFLAGS += -DEAP_MD5
+AM_CFLAGS += -DEAP_MSCHAPv2
+AM_CFLAGS += -DEAP_GTC
+AM_CFLAGS += -DEAP_OTP
+AM_CFLAGS += -DEAP_LEAP
+AM_CFLAGS += -DEAP_PSK
+AM_CFLAGS += -DEAP_PAX
+AM_CFLAGS += -DEAP_SAKE
+AM_CFLAGS += -DEAP_GPSK -DEAP_GPSK_SHA256
+
+AM_CFLAGS += -DEAP_SERVER_IDENTITY
+AM_CFLAGS += -DEAP_SERVER_TLS
+AM_CFLAGS += -DEAP_SERVER_PEAP
+AM_CFLAGS += -DEAP_SERVER_TTLS
+AM_CFLAGS += -DEAP_SERVER_MD5
+AM_CFLAGS += -DEAP_SERVER_MSCHAPV2
+AM_CFLAGS += -DEAP_SERVER_GTC
+AM_CFLAGS += -DEAP_SERVER_PSK
+AM_CFLAGS += -DEAP_SERVER_PAX
+AM_CFLAGS += -DEAP_SERVER_SAKE
+AM_CFLAGS += -DEAP_SERVER_GPSK -DEAP_SERVER_GPSK_SHA256
+
+AM_CFLAGS += -DIEEE8021X_EAPOL
+AM_CFLAGS += -DCONFIG_IPV6
+AM_CFLAGS += -DCONFIG_DEBUG_FILE
+AM_CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
+AM_CFLAGS += -DCONFIG_INTERNAL_SHA1
+AM_CFLAGS += -DEAP_TLS_OPENSSL
+AM_CFLAGS += -DPKCS12_FUNCS
+AM_CFLAGS += -DCONFIG_SHA256
+
+UTILS_SRCS = src/utils/base64.c \
+ src/utils/common.c \
+ src/utils/ip_addr.c \
+ src/utils/radiotap.c \
+ src/utils/trace.c \
+ src/utils/uuid.c \
+ src/utils/wpa_debug.c \
+ src/utils/wpabuf.c \
+ src/utils/os_unix.c \
+ src/utils/radius_utils.c \
+src/utils/radius_utils.h \
+ src/utils/base64.h \
+ src/utils/build_config.h \
+ src/utils/common.h \
+ src/utils/eloop.h \
+ src/utils/includes.h \
+ src/utils/ip_addr.h \
+ src/utils/list.h \
+ src/utils/os.h \
+ src/utils/pcsc_funcs.h \
+ src/utils/radiotap.h \
+ src/utils/radiotap_iter.h \
+ src/utils/state_machine.h \
+ src/utils/trace.h \
+ src/utils/uuid.h \
+ src/utils/wpabuf.h \
+ src/utils/wpa_debug.h
+
+
+CRYPTO_SRCS = \
+ src/crypto/aes-cbc.c \
++ src/crypto/aes-ccm.c \
+ src/crypto/aes-ctr.c \
+ src/crypto/aes-eax.c \
+ src/crypto/aes-encblock.c \
++ src/crypto/aes-gcm.c \
++ src/crypto/aes-internal.c \
++ src/crypto/aes-internal-dec.c \
++ src/crypto/aes-internal-enc.c \
+ src/crypto/aes-omac1.c \
++ src/crypto/aes-siv.c \
++ src/crypto/aes-siv.h \
+ src/crypto/aes-unwrap.c \
+ src/crypto/aes-wrap.c \
++ src/crypto/aes.h \
++ src/crypto/aes_i.h \
++ src/crypto/aes_wrap.h \
++ src/crypto/crypto.h \
++ src/crypto/des-internal.c \
++ src/crypto/dh_group5.h \
++ src/crypto/dh_groups.h \
++ src/crypto/md4-internal.c \
+ src/crypto/md5.c \
- src/crypto/crypto_openssl.c \
- src/crypto/tls_openssl.c \
- src/crypto/aes.h \
- src/crypto/aes_i.h \
- src/crypto/aes_wrap.h \
- src/crypto/crypto.h \
- src/crypto/dh_group5.h \
- src/crypto/dh_groups.h \
- src/crypto/md5.h \
- src/crypto/milenage.h \
- src/crypto/ms_funcs.h \
- src/crypto/sha1.h \
++ src/crypto/md5-internal.c \
++ src/crypto/md5.h \
+ src/crypto/milenage.c \
++ src/crypto/milenage.h \
+ src/crypto/ms_funcs.c \
++ src/crypto/ms_funcs.h \
++ src/crypto/rc4.c \
+ src/crypto/sha1.c \
++ src/crypto/sha1-internal.c \
+ src/crypto/sha1-pbkdf2.c \
++ src/crypto/sha1-prf.c \
+ src/crypto/sha1-tlsprf.c \
+ src/crypto/sha1-tprf.c \
++ src/crypto/sha256-prf.c \
++ src/crypto/sha256-tlsprf.c \
+ src/crypto/sha256.c \
+ src/crypto/sha256.h \
++ src/crypto/sha256_i.h \
+ src/crypto/tls.h
+
++
+TLS_SRCS = \
+ src/tls/asn1.c \
+ src/tls/bignum.c \
+ src/tls/pkcs1.c \
+ src/tls/pkcs5.c \
+ src/tls/pkcs8.c \
+ src/tls/rsa.c \
+ src/tls/tlsv1_client.c \
+ src/tls/tlsv1_client_read.c \
+ src/tls/tlsv1_client_write.c \
+ src/tls/tlsv1_common.c \
+ src/tls/tlsv1_cred.c \
+ src/tls/tlsv1_record.c \
+ src/tls/tlsv1_server.c \
+ src/tls/tlsv1_server_read.c \
+ src/tls/tlsv1_server_write.c \
+ src/tls/x509v3.c \
+ src/tls/asn1.h \
+ src/tls/bignum.h \
+ src/tls/pkcs1.h \
+ src/tls/pkcs5.h \
+ src/tls/pkcs8.h \
+ src/tls/rsa.h \
+ src/tls/tlsv1_client.h \
+ src/tls/tlsv1_client_i.h \
+ src/tls/tlsv1_common.h \
+ src/tls/tlsv1_cred.h \
+ src/tls/tlsv1_record.h \
+ src/tls/tlsv1_server.h \
+ src/tls/tlsv1_server_i.h \
+ src/tls/x509v3.h
+
+libeap_la_SOURCES = $(SOURCES_BOTH) $(SOURCES_peer) $(UTILS_SRCS) $(CRYPTO_SRCS)
+
+noinst_LTLIBRARIES = libeap.la
--- /dev/null
- * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
+/*
+ * SSL/TLS interface functions for OpenSSL
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
++ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ *
- #if OPENSSL_VERSION_NUMBER >= 0x0090800fL
- #define OPENSSL_d2i_TYPE const unsigned char **
- #else
- #define OPENSSL_d2i_TYPE unsigned char **
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
+ */
+
+#include "includes.h"
+
+#ifndef CONFIG_SMARTCARD
+#ifndef OPENSSL_NO_ENGINE
++#ifndef ANDROID
+#define OPENSSL_NO_ENGINE
+#endif
+#endif
++#endif
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/pkcs12.h>
+#include <openssl/x509v3.h>
+#ifndef OPENSSL_NO_ENGINE
+#include <openssl/engine.h>
+#endif /* OPENSSL_NO_ENGINE */
++#ifndef OPENSSL_NO_DSA
++#include <openssl/dsa.h>
++#endif
++#ifndef OPENSSL_NO_DH
++#include <openssl/dh.h>
++#endif
+
+#include "common.h"
+#include "crypto.h"
++#include "sha1.h"
++#include "sha256.h"
+#include "tls.h"
+
- #ifdef SSL_F_SSL_SET_SESSION_TICKET_EXT
- #ifdef SSL_OP_NO_TICKET
- /*
- * Session ticket override patch was merged into OpenSSL 0.9.9 tree on
- * 2008-11-15. This version uses a bit different API compared to the old patch.
- */
- #define CONFIG_OPENSSL_TICKET_OVERRIDE
- #endif
++#if OPENSSL_VERSION_NUMBER < 0x10000000L
++/* ERR_remove_thread_state replaces ERR_remove_state and the latter is
++ * deprecated. However, OpenSSL 0.9.8 doesn't include
++ * ERR_remove_thread_state. */
++#define ERR_remove_thread_state(tid) ERR_remove_state(0)
+#endif
+
- struct tls_global {
++#if defined(OPENSSL_IS_BORINGSSL)
++/* stack_index_t is the return type of OpenSSL's sk_XXX_num() functions. */
++typedef size_t stack_index_t;
++#else
++typedef int stack_index_t;
+#endif
+
++#ifdef SSL_set_tlsext_status_type
++#ifndef OPENSSL_NO_TLSEXT
++#define HAVE_OCSP
++#include <openssl/ocsp.h>
++#endif /* OPENSSL_NO_TLSEXT */
++#endif /* SSL_set_tlsext_status_type */
++
++#ifdef ANDROID
++#include <openssl/pem.h>
++#include <keystore/keystore_get.h>
++
++static BIO * BIO_from_keystore(const char *key)
++{
++ BIO *bio = NULL;
++ uint8_t *value = NULL;
++ int length = keystore_get(key, strlen(key), &value);
++ if (length != -1 && (bio = BIO_new(BIO_s_mem())) != NULL)
++ BIO_write(bio, value, length);
++ free(value);
++ return bio;
++}
++#endif /* ANDROID */
++
+static int tls_openssl_ref_count = 0;
++static int tls_ex_idx_session = -1;
+
- static struct tls_global *tls_global = NULL;
++struct tls_context {
+ void (*event_cb)(void *ctx, enum tls_event ev,
+ union tls_event_data *data);
+ void *cb_ctx;
++ int cert_in_cb;
++ char *ocsp_stapling_response;
+};
+
- char *subject_match, *altsubject_match;
++static struct tls_context *tls_global = NULL;
++
+
++struct tls_data {
++ SSL_CTX *ssl;
++ unsigned int tls_session_lifetime;
++};
+
+struct tls_connection {
++ struct tls_context *context;
++ SSL_CTX *ssl_ctx;
+ SSL *ssl;
+ BIO *ssl_in, *ssl_out;
+#ifndef OPENSSL_NO_ENGINE
+ ENGINE *engine; /* functional reference to the engine */
+ EVP_PKEY *private_key; /* the private key if using engine */
+#endif /* OPENSSL_NO_ENGINE */
- cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &priv->cert->pbCertEncoded,
++ char *subject_match, *altsubject_match, *suffix_match, *domain_match;
+ int read_alerts, write_alerts, failed;
+
+ tls_session_ticket_cb session_ticket_cb;
+ void *session_ticket_cb_ctx;
+
+ /* SessionTicket received from OpenSSL hello_extension_cb (server) */
+ u8 *session_ticket;
+ size_t session_ticket_len;
+
+ unsigned int ca_cert_verify:1;
+ unsigned int cert_probe:1;
+ unsigned int server_cert_only:1;
++ unsigned int invalid_hb_used:1;
++ unsigned int success_data:1;
+
+ u8 srv_cert_hash[32];
++
++ unsigned int flags;
++
++ X509 *peer_cert;
++ X509 *peer_issuer;
++ X509 *peer_issuer_issuer;
++
++#if OPENSSL_VERSION_NUMBER >= 0x10100000L
++ unsigned char client_random[SSL3_RANDOM_SIZE];
++ unsigned char server_random[SSL3_RANDOM_SIZE];
++#endif
+};
+
+
++static struct tls_context * tls_context_new(const struct tls_config *conf)
++{
++ struct tls_context *context = os_zalloc(sizeof(*context));
++ if (context == NULL)
++ return NULL;
++ if (conf) {
++ context->event_cb = conf->event_cb;
++ context->cb_ctx = conf->cb_ctx;
++ context->cert_in_cb = conf->cert_in_cb;
++ }
++ return context;
++}
++
++
+#ifdef CONFIG_NO_STDOUT_DEBUG
+
+static void _tls_show_errors(void)
+{
+ unsigned long err;
+
+ while ((err = ERR_get_error())) {
+ /* Just ignore the errors, since stdout is disabled */
+ }
+}
+#define tls_show_errors(l, f, t) _tls_show_errors()
+
+#else /* CONFIG_NO_STDOUT_DEBUG */
+
+static void tls_show_errors(int level, const char *func, const char *txt)
+{
+ unsigned long err;
+
+ wpa_printf(level, "OpenSSL: %s - %s %s",
+ func, txt, ERR_error_string(ERR_get_error(), NULL));
+
+ while ((err = ERR_get_error())) {
+ wpa_printf(MSG_INFO, "OpenSSL: pending error: %s",
+ ERR_error_string(err, NULL));
+ }
+}
+
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+#ifdef CONFIG_NATIVE_WINDOWS
+
+/* Windows CryptoAPI and access to certificate stores */
+#include <wincrypt.h>
+
+#ifdef __MINGW32_VERSION
+/*
+ * MinGW does not yet include all the needed definitions for CryptoAPI, so
+ * define here whatever extra is needed.
+ */
+#define CERT_SYSTEM_STORE_CURRENT_USER (1 << 16)
+#define CERT_STORE_READONLY_FLAG 0x00008000
+#define CERT_STORE_OPEN_EXISTING_FLAG 0x00004000
+
+#endif /* __MINGW32_VERSION */
+
+
+struct cryptoapi_rsa_data {
+ const CERT_CONTEXT *cert;
+ HCRYPTPROV crypt_prov;
+ DWORD key_spec;
+ BOOL free_crypt_prov;
+};
+
+
+static void cryptoapi_error(const char *msg)
+{
+ wpa_printf(MSG_INFO, "CryptoAPI: %s; err=%u",
+ msg, (unsigned int) GetLastError());
+}
+
+
+static int cryptoapi_rsa_pub_enc(int flen, const unsigned char *from,
+ unsigned char *to, RSA *rsa, int padding)
+{
+ wpa_printf(MSG_DEBUG, "%s - not implemented", __func__);
+ return 0;
+}
+
+
+static int cryptoapi_rsa_pub_dec(int flen, const unsigned char *from,
+ unsigned char *to, RSA *rsa, int padding)
+{
+ wpa_printf(MSG_DEBUG, "%s - not implemented", __func__);
+ return 0;
+}
+
+
+static int cryptoapi_rsa_priv_enc(int flen, const unsigned char *from,
+ unsigned char *to, RSA *rsa, int padding)
+{
+ struct cryptoapi_rsa_data *priv =
+ (struct cryptoapi_rsa_data *) rsa->meth->app_data;
+ HCRYPTHASH hash;
+ DWORD hash_size, len, i;
+ unsigned char *buf = NULL;
+ int ret = 0;
+
+ if (priv == NULL) {
+ RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
+ ERR_R_PASSED_NULL_PARAMETER);
+ return 0;
+ }
+
+ if (padding != RSA_PKCS1_PADDING) {
+ RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
+ RSA_R_UNKNOWN_PADDING_TYPE);
+ return 0;
+ }
+
+ if (flen != 16 /* MD5 */ + 20 /* SHA-1 */) {
+ wpa_printf(MSG_INFO, "%s - only MD5-SHA1 hash supported",
+ __func__);
+ RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
+ RSA_R_INVALID_MESSAGE_LENGTH);
+ return 0;
+ }
+
+ if (!CryptCreateHash(priv->crypt_prov, CALG_SSL3_SHAMD5, 0, 0, &hash))
+ {
+ cryptoapi_error("CryptCreateHash failed");
+ return 0;
+ }
+
+ len = sizeof(hash_size);
+ if (!CryptGetHashParam(hash, HP_HASHSIZE, (BYTE *) &hash_size, &len,
+ 0)) {
+ cryptoapi_error("CryptGetHashParam failed");
+ goto err;
+ }
+
+ if ((int) hash_size != flen) {
+ wpa_printf(MSG_INFO, "CryptoAPI: Invalid hash size (%u != %d)",
+ (unsigned) hash_size, flen);
+ RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
+ RSA_R_INVALID_MESSAGE_LENGTH);
+ goto err;
+ }
+ if (!CryptSetHashParam(hash, HP_HASHVAL, (BYTE * ) from, 0)) {
+ cryptoapi_error("CryptSetHashParam failed");
+ goto err;
+ }
+
+ len = RSA_size(rsa);
+ buf = os_malloc(len);
+ if (buf == NULL) {
+ RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ if (!CryptSignHash(hash, priv->key_spec, NULL, 0, buf, &len)) {
+ cryptoapi_error("CryptSignHash failed");
+ goto err;
+ }
+
+ for (i = 0; i < len; i++)
+ to[i] = buf[len - i - 1];
+ ret = len;
+
+err:
+ os_free(buf);
+ CryptDestroyHash(hash);
+
+ return ret;
+}
+
+
+static int cryptoapi_rsa_priv_dec(int flen, const unsigned char *from,
+ unsigned char *to, RSA *rsa, int padding)
+{
+ wpa_printf(MSG_DEBUG, "%s - not implemented", __func__);
+ return 0;
+}
+
+
+static void cryptoapi_free_data(struct cryptoapi_rsa_data *priv)
+{
+ if (priv == NULL)
+ return;
+ if (priv->crypt_prov && priv->free_crypt_prov)
+ CryptReleaseContext(priv->crypt_prov, 0);
+ if (priv->cert)
+ CertFreeCertificateContext(priv->cert);
+ os_free(priv);
+}
+
+
+static int cryptoapi_finish(RSA *rsa)
+{
+ cryptoapi_free_data((struct cryptoapi_rsa_data *) rsa->meth->app_data);
+ os_free((void *) rsa->meth);
+ rsa->meth = NULL;
+ return 1;
+}
+
+
+static const CERT_CONTEXT * cryptoapi_find_cert(const char *name, DWORD store)
+{
+ HCERTSTORE cs;
+ const CERT_CONTEXT *ret = NULL;
+
+ cs = CertOpenStore((LPCSTR) CERT_STORE_PROV_SYSTEM, 0, 0,
+ store | CERT_STORE_OPEN_EXISTING_FLAG |
+ CERT_STORE_READONLY_FLAG, L"MY");
+ if (cs == NULL) {
+ cryptoapi_error("Failed to open 'My system store'");
+ return NULL;
+ }
+
+ if (strncmp(name, "cert://", 7) == 0) {
+ unsigned short wbuf[255];
+ MultiByteToWideChar(CP_ACP, 0, name + 7, -1, wbuf, 255);
+ ret = CertFindCertificateInStore(cs, X509_ASN_ENCODING |
+ PKCS_7_ASN_ENCODING,
+ 0, CERT_FIND_SUBJECT_STR,
+ wbuf, NULL);
+ } else if (strncmp(name, "hash://", 7) == 0) {
+ CRYPT_HASH_BLOB blob;
+ int len;
+ const char *hash = name + 7;
+ unsigned char *buf;
+
+ len = os_strlen(hash) / 2;
+ buf = os_malloc(len);
+ if (buf && hexstr2bin(hash, buf, len) == 0) {
+ blob.cbData = len;
+ blob.pbData = buf;
+ ret = CertFindCertificateInStore(cs,
+ X509_ASN_ENCODING |
+ PKCS_7_ASN_ENCODING,
+ 0, CERT_FIND_HASH,
+ &blob, NULL);
+ }
+ os_free(buf);
+ }
+
+ CertCloseStore(cs, 0);
+
+ return ret;
+}
+
+
+static int tls_cryptoapi_cert(SSL *ssl, const char *name)
+{
+ X509 *cert = NULL;
+ RSA *rsa = NULL, *pub_rsa;
+ struct cryptoapi_rsa_data *priv;
+ RSA_METHOD *rsa_meth;
+
+ if (name == NULL ||
+ (strncmp(name, "cert://", 7) != 0 &&
+ strncmp(name, "hash://", 7) != 0))
+ return -1;
+
+ priv = os_zalloc(sizeof(*priv));
+ rsa_meth = os_zalloc(sizeof(*rsa_meth));
+ if (priv == NULL || rsa_meth == NULL) {
+ wpa_printf(MSG_WARNING, "CryptoAPI: Failed to allocate memory "
+ "for CryptoAPI RSA method");
+ os_free(priv);
+ os_free(rsa_meth);
+ return -1;
+ }
+
+ priv->cert = cryptoapi_find_cert(name, CERT_SYSTEM_STORE_CURRENT_USER);
+ if (priv->cert == NULL) {
+ priv->cert = cryptoapi_find_cert(
+ name, CERT_SYSTEM_STORE_LOCAL_MACHINE);
+ }
+ if (priv->cert == NULL) {
+ wpa_printf(MSG_INFO, "CryptoAPI: Could not find certificate "
+ "'%s'", name);
+ goto err;
+ }
+
- cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ctx->pbCertEncoded,
++ cert = d2i_X509(NULL,
++ (const unsigned char **) &priv->cert->pbCertEncoded,
+ priv->cert->cbCertEncoded);
+ if (cert == NULL) {
+ wpa_printf(MSG_INFO, "CryptoAPI: Could not process X509 DER "
+ "encoding");
+ goto err;
+ }
+
+ if (!CryptAcquireCertificatePrivateKey(priv->cert,
+ CRYPT_ACQUIRE_COMPARE_KEY_FLAG,
+ NULL, &priv->crypt_prov,
+ &priv->key_spec,
+ &priv->free_crypt_prov)) {
+ cryptoapi_error("Failed to acquire a private key for the "
+ "certificate");
+ goto err;
+ }
+
+ rsa_meth->name = "Microsoft CryptoAPI RSA Method";
+ rsa_meth->rsa_pub_enc = cryptoapi_rsa_pub_enc;
+ rsa_meth->rsa_pub_dec = cryptoapi_rsa_pub_dec;
+ rsa_meth->rsa_priv_enc = cryptoapi_rsa_priv_enc;
+ rsa_meth->rsa_priv_dec = cryptoapi_rsa_priv_dec;
+ rsa_meth->finish = cryptoapi_finish;
+ rsa_meth->flags = RSA_METHOD_FLAG_NO_CHECK;
+ rsa_meth->app_data = (char *) priv;
+
+ rsa = RSA_new();
+ if (rsa == NULL) {
+ SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE,
+ ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ if (!SSL_use_certificate(ssl, cert)) {
+ RSA_free(rsa);
+ rsa = NULL;
+ goto err;
+ }
+ pub_rsa = cert->cert_info->key->pkey->pkey.rsa;
+ X509_free(cert);
+ cert = NULL;
+
+ rsa->n = BN_dup(pub_rsa->n);
+ rsa->e = BN_dup(pub_rsa->e);
+ if (!RSA_set_method(rsa, rsa_meth))
+ goto err;
+
+ if (!SSL_use_RSAPrivateKey(ssl, rsa))
+ goto err;
+ RSA_free(rsa);
+
+ return 0;
+
+err:
+ if (cert)
+ X509_free(cert);
+ if (rsa)
+ RSA_free(rsa);
+ else {
+ os_free(rsa_meth);
+ cryptoapi_free_data(priv);
+ }
+ return -1;
+}
+
+
+static int tls_cryptoapi_ca_cert(SSL_CTX *ssl_ctx, SSL *ssl, const char *name)
+{
+ HCERTSTORE cs;
+ PCCERT_CONTEXT ctx = NULL;
+ X509 *cert;
+ char buf[128];
+ const char *store;
+#ifdef UNICODE
+ WCHAR *wstore;
+#endif /* UNICODE */
+
+ if (name == NULL || strncmp(name, "cert_store://", 13) != 0)
+ return -1;
+
+ store = name + 13;
+#ifdef UNICODE
+ wstore = os_malloc((os_strlen(store) + 1) * sizeof(WCHAR));
+ if (wstore == NULL)
+ return -1;
+ wsprintf(wstore, L"%S", store);
+ cs = CertOpenSystemStore(0, wstore);
+ os_free(wstore);
+#else /* UNICODE */
+ cs = CertOpenSystemStore(0, store);
+#endif /* UNICODE */
+ if (cs == NULL) {
+ wpa_printf(MSG_DEBUG, "%s: failed to open system cert store "
+ "'%s': error=%d", __func__, store,
+ (int) GetLastError());
+ return -1;
+ }
+
+ while ((ctx = CertEnumCertificatesInStore(cs, ctx))) {
- struct tls_connection *conn =
- SSL_get_app_data((SSL *) ssl);
++ cert = d2i_X509(NULL,
++ (const unsigned char **) &ctx->pbCertEncoded,
+ ctx->cbCertEncoded);
+ if (cert == NULL) {
+ wpa_printf(MSG_INFO, "CryptoAPI: Could not process "
+ "X509 DER encoding for CA cert");
+ continue;
+ }
+
+ X509_NAME_oneline(X509_get_subject_name(cert), buf,
+ sizeof(buf));
+ wpa_printf(MSG_DEBUG, "OpenSSL: Loaded CA certificate for "
+ "system certificate store: subject='%s'", buf);
+
+ if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) {
+ tls_show_errors(MSG_WARNING, __func__,
+ "Failed to add ca_cert to OpenSSL "
+ "certificate store");
+ }
+
+ X509_free(cert);
+ }
+
+ if (!CertCloseStore(cs, 0)) {
+ wpa_printf(MSG_DEBUG, "%s: failed to close system cert store "
+ "'%s': error=%d", __func__, name + 13,
+ (int) GetLastError());
+ }
+
+ return 0;
+}
+
+
+#else /* CONFIG_NATIVE_WINDOWS */
+
+static int tls_cryptoapi_cert(SSL *ssl, const char *name)
+{
+ return -1;
+}
+
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+static void ssl_info_cb(const SSL *ssl, int where, int ret)
+{
+ const char *str;
+ int w;
+
+ wpa_printf(MSG_DEBUG, "SSL: (where=0x%x ret=0x%x)", where, ret);
+ w = where & ~SSL_ST_MASK;
+ if (w & SSL_ST_CONNECT)
+ str = "SSL_connect";
+ else if (w & SSL_ST_ACCEPT)
+ str = "SSL_accept";
+ else
+ str = "undefined";
+
+ if (where & SSL_CB_LOOP) {
+ wpa_printf(MSG_DEBUG, "SSL: %s:%s",
+ str, SSL_state_string_long(ssl));
+ } else if (where & SSL_CB_ALERT) {
++ struct tls_connection *conn = SSL_get_app_data((SSL *) ssl);
+ wpa_printf(MSG_INFO, "SSL: SSL3 alert: %s:%s:%s",
+ where & SSL_CB_READ ?
+ "read (remote end reported an error)" :
+ "write (local SSL3 detected an error)",
+ SSL_alert_type_string_long(ret),
+ SSL_alert_desc_string_long(ret));
+ if ((ret >> 8) == SSL3_AL_FATAL) {
- if (!pkcs11_so_path || !pkcs11_module_path)
+ if (where & SSL_CB_READ)
+ conn->read_alerts++;
+ else
+ conn->write_alerts++;
+ }
++ if (conn->context->event_cb != NULL) {
++ union tls_event_data ev;
++ struct tls_context *context = conn->context;
++ os_memset(&ev, 0, sizeof(ev));
++ ev.alert.is_local = !(where & SSL_CB_READ);
++ ev.alert.type = SSL_alert_type_string_long(ret);
++ ev.alert.description = SSL_alert_desc_string_long(ret);
++ context->event_cb(context->cb_ctx, TLS_ALERT, &ev);
++ }
+ } else if (where & SSL_CB_EXIT && ret <= 0) {
+ wpa_printf(MSG_DEBUG, "SSL: %s:%s in %s",
+ str, ret == 0 ? "failed" : "error",
+ SSL_state_string_long(ssl));
+ }
+}
+
+
+#ifndef OPENSSL_NO_ENGINE
+/**
+ * tls_engine_load_dynamic_generic - load any openssl engine
+ * @pre: an array of commands and values that load an engine initialized
+ * in the engine specific function
+ * @post: an array of commands and values that initialize an already loaded
+ * engine (or %NULL if not required)
+ * @id: the engine id of the engine to load (only required if post is not %NULL
+ *
+ * This function is a generic function that loads any openssl engine.
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+static int tls_engine_load_dynamic_generic(const char *pre[],
+ const char *post[], const char *id)
+{
+ ENGINE *engine;
+ const char *dynamic_id = "dynamic";
+
+ engine = ENGINE_by_id(id);
+ if (engine) {
+ ENGINE_free(engine);
+ wpa_printf(MSG_DEBUG, "ENGINE: engine '%s' is already "
+ "available", id);
+ return 0;
+ }
+ ERR_clear_error();
+
+ engine = ENGINE_by_id(dynamic_id);
+ if (engine == NULL) {
+ wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]",
+ dynamic_id,
+ ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+
+ /* Perform the pre commands. This will load the engine. */
+ while (pre && pre[0]) {
+ wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", pre[0], pre[1]);
+ if (ENGINE_ctrl_cmd_string(engine, pre[0], pre[1], 0) == 0) {
+ wpa_printf(MSG_INFO, "ENGINE: ctrl cmd_string failed: "
+ "%s %s [%s]", pre[0], pre[1],
+ ERR_error_string(ERR_get_error(), NULL));
+ ENGINE_free(engine);
+ return -1;
+ }
+ pre += 2;
+ }
+
+ /*
+ * Free the reference to the "dynamic" engine. The loaded engine can
+ * now be looked up using ENGINE_by_id().
+ */
+ ENGINE_free(engine);
+
+ engine = ENGINE_by_id(id);
+ if (engine == NULL) {
+ wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]",
+ id, ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+
+ while (post && post[0]) {
+ wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", post[0], post[1]);
+ if (ENGINE_ctrl_cmd_string(engine, post[0], post[1], 0) == 0) {
+ wpa_printf(MSG_DEBUG, "ENGINE: ctrl cmd_string failed:"
+ " %s %s [%s]", post[0], post[1],
+ ERR_error_string(ERR_get_error(), NULL));
+ ENGINE_remove(engine);
+ ENGINE_free(engine);
+ return -1;
+ }
+ post += 2;
+ }
+ ENGINE_free(engine);
+
+ return 0;
+}
+
+
+/**
+ * tls_engine_load_dynamic_pkcs11 - load the pkcs11 engine provided by opensc
+ * @pkcs11_so_path: pksc11_so_path from the configuration
+ * @pcks11_module_path: pkcs11_module_path from the configuration
+ */
+static int tls_engine_load_dynamic_pkcs11(const char *pkcs11_so_path,
+ const char *pkcs11_module_path)
+{
+ char *engine_id = "pkcs11";
+ const char *pre_cmd[] = {
+ "SO_PATH", NULL /* pkcs11_so_path */,
+ "ID", NULL /* engine_id */,
+ "LIST_ADD", "1",
+ /* "NO_VCHECK", "1", */
+ "LOAD", NULL,
+ NULL, NULL
+ };
+ const char *post_cmd[] = {
+ "MODULE_PATH", NULL /* pkcs11_module_path */,
+ NULL, NULL
+ };
+
- post_cmd[1] = pkcs11_module_path;
++ if (!pkcs11_so_path)
+ return 0;
+
+ pre_cmd[1] = pkcs11_so_path;
+ pre_cmd[3] = engine_id;
- tls_global = os_zalloc(sizeof(*tls_global));
- if (tls_global == NULL)
++ if (pkcs11_module_path)
++ post_cmd[1] = pkcs11_module_path;
++ else
++ post_cmd[0] = NULL;
+
+ wpa_printf(MSG_DEBUG, "ENGINE: Loading pkcs11 Engine from %s",
+ pkcs11_so_path);
+
+ return tls_engine_load_dynamic_generic(pre_cmd, post_cmd, engine_id);
+}
+
+
+/**
+ * tls_engine_load_dynamic_opensc - load the opensc engine provided by opensc
+ * @opensc_so_path: opensc_so_path from the configuration
+ */
+static int tls_engine_load_dynamic_opensc(const char *opensc_so_path)
+{
+ char *engine_id = "opensc";
+ const char *pre_cmd[] = {
+ "SO_PATH", NULL /* opensc_so_path */,
+ "ID", NULL /* engine_id */,
+ "LIST_ADD", "1",
+ "LOAD", NULL,
+ NULL, NULL
+ };
+
+ if (!opensc_so_path)
+ return 0;
+
+ pre_cmd[1] = opensc_so_path;
+ pre_cmd[3] = engine_id;
+
+ wpa_printf(MSG_DEBUG, "ENGINE: Loading OpenSC Engine from %s",
+ opensc_so_path);
+
+ return tls_engine_load_dynamic_generic(pre_cmd, NULL, engine_id);
+}
+#endif /* OPENSSL_NO_ENGINE */
+
+
++static void remove_session_cb(SSL_CTX *ctx, SSL_SESSION *sess)
++{
++ struct wpabuf *buf;
++
++ if (tls_ex_idx_session < 0)
++ return;
++ buf = SSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
++ if (!buf)
++ return;
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Free application session data %p (sess %p)",
++ buf, sess);
++ wpabuf_free(buf);
++
++ SSL_SESSION_set_ex_data(sess, tls_ex_idx_session, NULL);
++}
++
++
+void * tls_init(const struct tls_config *conf)
+{
++ struct tls_data *data;
+ SSL_CTX *ssl;
++ struct tls_context *context;
++ const char *ciphers;
+
+ if (tls_openssl_ref_count == 0) {
- if (conf) {
- tls_global->event_cb = conf->event_cb;
- tls_global->cb_ctx = conf->cb_ctx;
- }
-
++ tls_global = context = tls_context_new(conf);
++ if (context == NULL)
+ return NULL;
- if (!FIPS_mode_set(1)) {
+#ifdef CONFIG_FIPS
+#ifdef OPENSSL_FIPS
+ if (conf && conf->fips_mode) {
- } else
++ static int fips_enabled = 0;
++
++ if (!fips_enabled && !FIPS_mode_set(1)) {
+ wpa_printf(MSG_ERROR, "Failed to enable FIPS "
+ "mode");
+ ERR_load_crypto_strings();
+ ERR_print_errors_fp(stderr);
++ os_free(tls_global);
++ tls_global = NULL;
+ return NULL;
- ssl = SSL_CTX_new(TLSv1_method());
- if (ssl == NULL)
++ } else {
+ wpa_printf(MSG_INFO, "Running in FIPS mode");
++ fips_enabled = 1;
++ }
+ }
+#else /* OPENSSL_FIPS */
+ if (conf && conf->fips_mode) {
+ wpa_printf(MSG_ERROR, "FIPS mode requested, but not "
+ "supported");
++ os_free(tls_global);
++ tls_global = NULL;
+ return NULL;
+ }
+#endif /* OPENSSL_FIPS */
+#endif /* CONFIG_FIPS */
+ SSL_load_error_strings();
+ SSL_library_init();
+#ifndef OPENSSL_NO_SHA256
+ EVP_add_digest(EVP_sha256());
+#endif /* OPENSSL_NO_SHA256 */
+ /* TODO: if /dev/urandom is available, PRNG is seeded
+ * automatically. If this is not the case, random data should
+ * be added here. */
+
+#ifdef PKCS12_FUNCS
+#ifndef OPENSSL_NO_RC2
+ /*
+ * 40-bit RC2 is commonly used in PKCS#12 files, so enable it.
+ * This is enabled by PKCS12_PBE_add() in OpenSSL 0.9.8
+ * versions, but it looks like OpenSSL 1.0.0 does not do that
+ * anymore.
+ */
+ EVP_add_cipher(EVP_rc2_40_cbc());
+#endif /* OPENSSL_NO_RC2 */
+ PKCS12_PBE_add();
+#endif /* PKCS12_FUNCS */
++ } else {
++ context = tls_context_new(conf);
++ if (context == NULL)
++ return NULL;
+ }
+ tls_openssl_ref_count++;
+
- wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine");
- ERR_load_ENGINE_strings();
- ENGINE_load_dynamic();
-
++ data = os_zalloc(sizeof(*data));
++ if (data)
++ ssl = SSL_CTX_new(SSLv23_method());
++ else
++ ssl = NULL;
++ if (ssl == NULL) {
++ tls_openssl_ref_count--;
++ if (context != tls_global)
++ os_free(context);
++ if (tls_openssl_ref_count == 0) {
++ os_free(tls_global);
++ tls_global = NULL;
++ }
+ return NULL;
++ }
++ data->ssl = ssl;
++ if (conf)
++ data->tls_session_lifetime = conf->tls_session_lifetime;
++
++ SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv2);
++ SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv3);
+
+ SSL_CTX_set_info_callback(ssl, ssl_info_cb);
++ SSL_CTX_set_app_data(ssl, context);
++ if (data->tls_session_lifetime > 0) {
++ SSL_CTX_set_quiet_shutdown(ssl, 1);
++ /*
++ * Set default context here. In practice, this will be replaced
++ * by the per-EAP method context in tls_connection_set_verify().
++ */
++ SSL_CTX_set_session_id_context(ssl, (u8 *) "hostapd", 7);
++ SSL_CTX_set_session_cache_mode(ssl, SSL_SESS_CACHE_SERVER);
++ SSL_CTX_set_timeout(ssl, data->tls_session_lifetime);
++ SSL_CTX_sess_set_remove_cb(ssl, remove_session_cb);
++ } else {
++ SSL_CTX_set_session_cache_mode(ssl, SSL_SESS_CACHE_OFF);
++ }
++
++ if (tls_ex_idx_session < 0) {
++ tls_ex_idx_session = SSL_SESSION_get_ex_new_index(
++ 0, NULL, NULL, NULL, NULL);
++ if (tls_ex_idx_session < 0) {
++ tls_deinit(data);
++ return NULL;
++ }
++ }
+
+#ifndef OPENSSL_NO_ENGINE
++ wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine");
++ ERR_load_ENGINE_strings();
++ ENGINE_load_dynamic();
++
+ if (conf &&
+ (conf->opensc_engine_path || conf->pkcs11_engine_path ||
+ conf->pkcs11_module_path)) {
- tls_deinit(ssl);
+ if (tls_engine_load_dynamic_opensc(conf->opensc_engine_path) ||
+ tls_engine_load_dynamic_pkcs11(conf->pkcs11_engine_path,
+ conf->pkcs11_module_path)) {
- return ssl;
++ tls_deinit(data);
+ return NULL;
+ }
+ }
+#endif /* OPENSSL_NO_ENGINE */
+
- SSL_CTX *ssl = ssl_ctx;
++ if (conf && conf->openssl_ciphers)
++ ciphers = conf->openssl_ciphers;
++ else
++ ciphers = "DEFAULT:!EXP:!LOW";
++ if (SSL_CTX_set_cipher_list(ssl, ciphers) != 1) {
++ wpa_printf(MSG_ERROR,
++ "OpenSSL: Failed to set cipher string '%s'",
++ ciphers);
++ tls_deinit(data);
++ return NULL;
++ }
++
++ return data;
+}
+
+
+void tls_deinit(void *ssl_ctx)
+{
- ERR_remove_state(0);
++ struct tls_data *data = ssl_ctx;
++ SSL_CTX *ssl = data->ssl;
++ struct tls_context *context = SSL_CTX_get_app_data(ssl);
++ if (context != tls_global)
++ os_free(context);
++ if (data->tls_session_lifetime > 0)
++ SSL_CTX_flush_sessions(ssl, 0);
+ SSL_CTX_free(ssl);
+
+ tls_openssl_ref_count--;
+ if (tls_openssl_ref_count == 0) {
- if (pin == NULL) {
- wpa_printf(MSG_ERROR, "ENGINE: Smartcard PIN not set");
- return -1;
- }
- if (key_id == NULL) {
- wpa_printf(MSG_ERROR, "ENGINE: Key Id not set");
- return -1;
- }
++// The next four lines, and two more just below, deal with de-initializing
++// global state in the OpenSSL engine. We (Moonshot) don't want that, since
++// we use OpenSSL elsewhere in our apps (i.e., not only via hostap / libeap.)
++//// #ifndef OPENSSL_NO_ENGINE
++//// ENGINE_cleanup();
++//// #endif /* OPENSSL_NO_ENGINE */
++//// CRYPTO_cleanup_all_ex_data();
++ ERR_remove_thread_state(NULL);
++//// ERR_free_strings();
++//// EVP_cleanup();
++ os_free(tls_global->ocsp_stapling_response);
++ tls_global->ocsp_stapling_response = NULL;
+ os_free(tls_global);
+ tls_global = NULL;
+ }
++
++ os_free(data);
++}
++
++
++#ifndef OPENSSL_NO_ENGINE
++
++/* Cryptoki return values */
++#define CKR_PIN_INCORRECT 0x000000a0
++#define CKR_PIN_INVALID 0x000000a1
++#define CKR_PIN_LEN_RANGE 0x000000a2
++
++/* libp11 */
++#define ERR_LIB_PKCS11 ERR_LIB_USER
++
++static int tls_is_pin_error(unsigned int err)
++{
++ return ERR_GET_LIB(err) == ERR_LIB_PKCS11 &&
++ (ERR_GET_REASON(err) == CKR_PIN_INCORRECT ||
++ ERR_GET_REASON(err) == CKR_PIN_INVALID ||
++ ERR_GET_REASON(err) == CKR_PIN_LEN_RANGE);
+}
+
++#endif /* OPENSSL_NO_ENGINE */
++
+
+static int tls_engine_init(struct tls_connection *conn, const char *engine_id,
+ const char *pin, const char *key_id,
+ const char *cert_id, const char *ca_cert_id)
+{
+#ifndef OPENSSL_NO_ENGINE
+ int ret = -1;
+ if (engine_id == NULL) {
+ wpa_printf(MSG_ERROR, "ENGINE: Engine ID not set");
+ return -1;
+ }
- if (ENGINE_ctrl_cmd_string(conn->engine, "PIN", pin, 0) == 0) {
+
+ ERR_clear_error();
++#ifdef ANDROID
++ ENGINE_load_dynamic();
++#endif
+ conn->engine = ENGINE_by_id(engine_id);
+ if (!conn->engine) {
+ wpa_printf(MSG_ERROR, "ENGINE: engine %s not available [%s]",
+ engine_id, ERR_error_string(ERR_get_error(), NULL));
+ goto err;
+ }
+ if (ENGINE_init(conn->engine) != 1) {
+ wpa_printf(MSG_ERROR, "ENGINE: engine init failed "
+ "(engine: %s) [%s]", engine_id,
+ ERR_error_string(ERR_get_error(), NULL));
+ goto err;
+ }
+ wpa_printf(MSG_DEBUG, "ENGINE: engine initialized");
+
- /* load private key first in-case PIN is required for cert */
- conn->private_key = ENGINE_load_private_key(conn->engine,
- key_id, NULL, NULL);
- if (!conn->private_key) {
- wpa_printf(MSG_ERROR, "ENGINE: cannot load private key with id"
- " '%s' [%s]", key_id,
- ERR_error_string(ERR_get_error(), NULL));
- ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
- goto err;
++#ifndef ANDROID
++ if (pin && ENGINE_ctrl_cmd_string(conn->engine, "PIN", pin, 0) == 0) {
+ wpa_printf(MSG_ERROR, "ENGINE: cannot set pin [%s]",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto err;
+ }
- SSL_CTX *ssl = ssl_ctx;
++#endif
++ if (key_id) {
++ /*
++ * Ensure that the ENGINE does not attempt to use the OpenSSL
++ * UI system to obtain a PIN, if we didn't provide one.
++ */
++ struct {
++ const void *password;
++ const char *prompt_info;
++ } key_cb = { "", NULL };
++
++ /* load private key first in-case PIN is required for cert */
++ conn->private_key = ENGINE_load_private_key(conn->engine,
++ key_id, NULL,
++ &key_cb);
++ if (!conn->private_key) {
++ unsigned long err = ERR_get_error();
++
++ wpa_printf(MSG_ERROR,
++ "ENGINE: cannot load private key with id '%s' [%s]",
++ key_id,
++ ERR_error_string(err, NULL));
++ if (tls_is_pin_error(err))
++ ret = TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN;
++ else
++ ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
++ goto err;
++ }
+ }
+
+ /* handle a certificate and/or CA certificate */
+ if (cert_id || ca_cert_id) {
+ const char *cmd_name = "LOAD_CERT_CTRL";
+
+ /* test if the engine supports a LOAD_CERT_CTRL */
+ if (!ENGINE_ctrl(conn->engine, ENGINE_CTRL_GET_CMD_FROM_NAME,
+ 0, (void *)cmd_name, NULL)) {
+ wpa_printf(MSG_ERROR, "ENGINE: engine does not support"
+ " loading certificates");
+ ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ if (conn->engine) {
+ ENGINE_free(conn->engine);
+ conn->engine = NULL;
+ }
+
+ if (conn->private_key) {
+ EVP_PKEY_free(conn->private_key);
+ conn->private_key = NULL;
+ }
+
+ return ret;
+#else /* OPENSSL_NO_ENGINE */
+ return 0;
+#endif /* OPENSSL_NO_ENGINE */
+}
+
+
+static void tls_engine_deinit(struct tls_connection *conn)
+{
+#ifndef OPENSSL_NO_ENGINE
+ wpa_printf(MSG_DEBUG, "ENGINE: engine deinit");
+ if (conn->private_key) {
+ EVP_PKEY_free(conn->private_key);
+ conn->private_key = NULL;
+ }
+ if (conn->engine) {
+ ENGINE_finish(conn->engine);
+ conn->engine = NULL;
+ }
+#endif /* OPENSSL_NO_ENGINE */
+}
+
+
+int tls_get_errors(void *ssl_ctx)
+{
+ int count = 0;
+ unsigned long err;
+
+ while ((err = ERR_get_error())) {
+ wpa_printf(MSG_INFO, "TLS - SSL error: %s",
+ ERR_error_string(err, NULL));
+ count++;
+ }
+
+ return count;
+}
+
++
++static void tls_msg_cb(int write_p, int version, int content_type,
++ const void *buf, size_t len, SSL *ssl, void *arg)
++{
++ struct tls_connection *conn = arg;
++ const u8 *pos = buf;
++
++ wpa_printf(MSG_DEBUG, "OpenSSL: %s ver=0x%x content_type=%d",
++ write_p ? "TX" : "RX", version, content_type);
++ wpa_hexdump_key(MSG_MSGDUMP, "OpenSSL: Message", buf, len);
++ if (content_type == 24 && len >= 3 && pos[0] == 1) {
++ size_t payload_len = WPA_GET_BE16(pos + 1);
++ if (payload_len + 3 > len) {
++ wpa_printf(MSG_ERROR, "OpenSSL: Heartbeat attack detected");
++ conn->invalid_hb_used = 1;
++ }
++ }
++}
++
++
+struct tls_connection * tls_connection_init(void *ssl_ctx)
+{
- return 0;
++ struct tls_data *data = ssl_ctx;
++ SSL_CTX *ssl = data->ssl;
+ struct tls_connection *conn;
+ long options;
++ struct tls_context *context = SSL_CTX_get_app_data(ssl);
+
+ conn = os_zalloc(sizeof(*conn));
+ if (conn == NULL)
+ return NULL;
++ conn->ssl_ctx = ssl;
+ conn->ssl = SSL_new(ssl);
+ if (conn->ssl == NULL) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Failed to initialize new SSL connection");
+ os_free(conn);
+ return NULL;
+ }
+
++ conn->context = context;
+ SSL_set_app_data(conn->ssl, conn);
++ SSL_set_msg_callback(conn->ssl, tls_msg_cb);
++ SSL_set_msg_callback_arg(conn->ssl, conn);
+ options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
+ SSL_OP_SINGLE_DH_USE;
+#ifdef SSL_OP_NO_COMPRESSION
+ options |= SSL_OP_NO_COMPRESSION;
+#endif /* SSL_OP_NO_COMPRESSION */
+ SSL_set_options(conn->ssl, options);
+
+ conn->ssl_in = BIO_new(BIO_s_mem());
+ if (!conn->ssl_in) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Failed to create a new BIO for ssl_in");
+ SSL_free(conn->ssl);
+ os_free(conn);
+ return NULL;
+ }
+
+ conn->ssl_out = BIO_new(BIO_s_mem());
+ if (!conn->ssl_out) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Failed to create a new BIO for ssl_out");
+ SSL_free(conn->ssl);
+ BIO_free(conn->ssl_in);
+ os_free(conn);
+ return NULL;
+ }
+
+ SSL_set_bio(conn->ssl, conn->ssl_in, conn->ssl_out);
+
+ return conn;
+}
+
+
+void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn)
+{
+ if (conn == NULL)
+ return;
++ if (conn->success_data) {
++ /*
++ * Make sure ssl_clear_bad_session() does not remove this
++ * session.
++ */
++ SSL_set_quiet_shutdown(conn->ssl, 1);
++ SSL_shutdown(conn->ssl);
++ }
+ SSL_free(conn->ssl);
+ tls_engine_deinit(conn);
+ os_free(conn->subject_match);
+ os_free(conn->altsubject_match);
++ os_free(conn->suffix_match);
++ os_free(conn->domain_match);
+ os_free(conn->session_ticket);
+ os_free(conn);
+}
+
+
+int tls_connection_established(void *ssl_ctx, struct tls_connection *conn)
+{
+ return conn ? SSL_is_init_finished(conn->ssl) : 0;
+}
+
+
+int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
+{
+ if (conn == NULL)
+ return -1;
+
+ /* Shutdown previous TLS connection without notifying the peer
+ * because the connection was already terminated in practice
+ * and "close notify" shutdown alert would confuse AS. */
+ SSL_set_quiet_shutdown(conn->ssl, 1);
+ SSL_shutdown(conn->ssl);
- int i, found = 0;
++ return SSL_clear(conn->ssl) == 1 ? 0 : -1;
+}
+
+
+static int tls_match_altsubject_component(X509 *cert, int type,
+ const char *value, size_t len)
+{
+ GENERAL_NAME *gen;
+ void *ext;
- if (tls_global->event_cb == NULL)
++ int found = 0;
++ stack_index_t i;
+
+ ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+
+ for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
+ gen = sk_GENERAL_NAME_value(ext, i);
+ if (gen->type != type)
+ continue;
+ if (os_strlen((char *) gen->d.ia5->data) == len &&
+ os_memcmp(value, gen->d.ia5->data, len) == 0)
+ found++;
+ }
+
+ return found;
+}
+
+
+static int tls_match_altsubject(X509 *cert, const char *match)
+{
+ int type;
+ const char *pos, *end;
+ size_t len;
+
+ pos = match;
+ do {
+ if (os_strncmp(pos, "EMAIL:", 6) == 0) {
+ type = GEN_EMAIL;
+ pos += 6;
+ } else if (os_strncmp(pos, "DNS:", 4) == 0) {
+ type = GEN_DNS;
+ pos += 4;
+ } else if (os_strncmp(pos, "URI:", 4) == 0) {
+ type = GEN_URI;
+ pos += 4;
+ } else {
+ wpa_printf(MSG_INFO, "TLS: Invalid altSubjectName "
+ "match '%s'", pos);
+ return 0;
+ }
+ end = os_strchr(pos, ';');
+ while (end) {
+ if (os_strncmp(end + 1, "EMAIL:", 6) == 0 ||
+ os_strncmp(end + 1, "DNS:", 4) == 0 ||
+ os_strncmp(end + 1, "URI:", 4) == 0)
+ break;
+ end = os_strchr(end + 1, ';');
+ }
+ if (end)
+ len = end - pos;
+ else
+ len = os_strlen(pos);
+ if (tls_match_altsubject_component(cert, type, pos, len) > 0)
+ return 1;
+ pos = end + 1;
+ } while (end);
+
+ return 0;
+}
+
+
++#ifndef CONFIG_NATIVE_WINDOWS
++static int domain_suffix_match(const u8 *val, size_t len, const char *match,
++ int full)
++{
++ size_t i, match_len;
++
++ /* Check for embedded nuls that could mess up suffix matching */
++ for (i = 0; i < len; i++) {
++ if (val[i] == '\0') {
++ wpa_printf(MSG_DEBUG, "TLS: Embedded null in a string - reject");
++ return 0;
++ }
++ }
++
++ match_len = os_strlen(match);
++ if (match_len > len || (full && match_len != len))
++ return 0;
++
++ if (os_strncasecmp((const char *) val + len - match_len, match,
++ match_len) != 0)
++ return 0; /* no match */
++
++ if (match_len == len)
++ return 1; /* exact match */
++
++ if (val[len - match_len - 1] == '.')
++ return 1; /* full label match completes suffix match */
++
++ wpa_printf(MSG_DEBUG, "TLS: Reject due to incomplete label match");
++ return 0;
++}
++#endif /* CONFIG_NATIVE_WINDOWS */
++
++
++static int tls_match_suffix(X509 *cert, const char *match, int full)
++{
++#ifdef CONFIG_NATIVE_WINDOWS
++ /* wincrypt.h has conflicting X509_NAME definition */
++ return -1;
++#else /* CONFIG_NATIVE_WINDOWS */
++ GENERAL_NAME *gen;
++ void *ext;
++ int i;
++ stack_index_t j;
++ int dns_name = 0;
++ X509_NAME *name;
++
++ wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s",
++ full ? "": "suffix ", match);
++
++ ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
++
++ for (j = 0; ext && j < sk_GENERAL_NAME_num(ext); j++) {
++ gen = sk_GENERAL_NAME_value(ext, j);
++ if (gen->type != GEN_DNS)
++ continue;
++ dns_name++;
++ wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName",
++ gen->d.dNSName->data,
++ gen->d.dNSName->length);
++ if (domain_suffix_match(gen->d.dNSName->data,
++ gen->d.dNSName->length, match, full) ==
++ 1) {
++ wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found",
++ full ? "Match" : "Suffix match");
++ return 1;
++ }
++ }
++
++ if (dns_name) {
++ wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched");
++ return 0;
++ }
++
++ name = X509_get_subject_name(cert);
++ i = -1;
++ for (;;) {
++ X509_NAME_ENTRY *e;
++ ASN1_STRING *cn;
++
++ i = X509_NAME_get_index_by_NID(name, NID_commonName, i);
++ if (i == -1)
++ break;
++ e = X509_NAME_get_entry(name, i);
++ if (e == NULL)
++ continue;
++ cn = X509_NAME_ENTRY_get_data(e);
++ if (cn == NULL)
++ continue;
++ wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName",
++ cn->data, cn->length);
++ if (domain_suffix_match(cn->data, cn->length, match, full) == 1)
++ {
++ wpa_printf(MSG_DEBUG, "TLS: %s in commonName found",
++ full ? "Match" : "Suffix match");
++ return 1;
++ }
++ }
++
++ wpa_printf(MSG_DEBUG, "TLS: No CommonName %smatch found",
++ full ? "": "suffix ");
++ return 0;
++#endif /* CONFIG_NATIVE_WINDOWS */
++}
++
++
+static enum tls_fail_reason openssl_tls_fail_reason(int err)
+{
+ switch (err) {
+ case X509_V_ERR_CERT_REVOKED:
+ return TLS_FAIL_REVOKED;
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ case X509_V_ERR_CRL_NOT_YET_VALID:
+ return TLS_FAIL_NOT_YET_VALID;
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ case X509_V_ERR_CRL_HAS_EXPIRED:
+ return TLS_FAIL_EXPIRED;
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ case X509_V_ERR_UNABLE_TO_GET_CRL:
+ case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER:
+ case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+ case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+ case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+ case X509_V_ERR_CERT_CHAIN_TOO_LONG:
+ case X509_V_ERR_PATH_LENGTH_EXCEEDED:
+ case X509_V_ERR_INVALID_CA:
+ return TLS_FAIL_UNTRUSTED;
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
+ case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+ case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
+ case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
+ case X509_V_ERR_CERT_UNTRUSTED:
+ case X509_V_ERR_CERT_REJECTED:
+ return TLS_FAIL_BAD_CERTIFICATE;
+ default:
+ return TLS_FAIL_UNSPECIFIED;
+ }
+}
+
+
+static struct wpabuf * get_x509_cert(X509 *cert)
+{
+ struct wpabuf *buf;
+ u8 *tmp;
+
+ int cert_len = i2d_X509(cert, NULL);
+ if (cert_len <= 0)
+ return NULL;
+
+ buf = wpabuf_alloc(cert_len);
+ if (buf == NULL)
+ return NULL;
+
+ tmp = wpabuf_put(buf, cert_len);
+ i2d_X509(cert, &tmp);
+ return buf;
+}
+
+
+static void openssl_tls_fail_event(struct tls_connection *conn,
+ X509 *err_cert, int err, int depth,
+ const char *subject, const char *err_str,
+ enum tls_fail_reason reason)
+{
+ union tls_event_data ev;
+ struct wpabuf *cert = NULL;
++ struct tls_context *context = conn->context;
+
- tls_global->event_cb(tls_global->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
++ if (context->event_cb == NULL)
+ return;
+
+ cert = get_x509_cert(err_cert);
+ os_memset(&ev, 0, sizeof(ev));
+ ev.cert_fail.reason = reason != TLS_FAIL_UNSPECIFIED ?
+ reason : openssl_tls_fail_reason(err);
+ ev.cert_fail.depth = depth;
+ ev.cert_fail.subject = subject;
+ ev.cert_fail.reason_txt = err_str;
+ ev.cert_fail.cert = cert;
- if (tls_global->event_cb == NULL)
++ context->event_cb(context->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
+ wpabuf_free(cert);
+}
+
+
+static void openssl_tls_cert_event(struct tls_connection *conn,
+ X509 *err_cert, int depth,
+ const char *subject)
+{
+ struct wpabuf *cert = NULL;
+ union tls_event_data ev;
++ struct tls_context *context = conn->context;
++ char *altsubject[TLS_MAX_ALT_SUBJECT];
++ int alt, num_altsubject = 0;
++ GENERAL_NAME *gen;
++ void *ext;
++ stack_index_t i;
+#ifdef CONFIG_SHA256
+ u8 hash[32];
+#endif /* CONFIG_SHA256 */
+
- if (conn->cert_probe) {
++ if (context->event_cb == NULL)
+ return;
+
+ os_memset(&ev, 0, sizeof(ev));
- tls_global->event_cb(tls_global->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
++ if (conn->cert_probe || context->cert_in_cb) {
+ cert = get_x509_cert(err_cert);
+ ev.peer_cert.cert = cert;
+ }
+#ifdef CONFIG_SHA256
+ if (cert) {
+ const u8 *addr[1];
+ size_t len[1];
+ addr[0] = wpabuf_head(cert);
+ len[0] = wpabuf_len(cert);
+ if (sha256_vector(1, addr, len, hash) == 0) {
+ ev.peer_cert.hash = hash;
+ ev.peer_cert.hash_len = sizeof(hash);
+ }
+ }
+#endif /* CONFIG_SHA256 */
+ ev.peer_cert.depth = depth;
+ ev.peer_cert.subject = subject;
- char *match, *altmatch;
++
++ ext = X509_get_ext_d2i(err_cert, NID_subject_alt_name, NULL, NULL);
++ for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
++ char *pos;
++
++ if (num_altsubject == TLS_MAX_ALT_SUBJECT)
++ break;
++ gen = sk_GENERAL_NAME_value(ext, i);
++ if (gen->type != GEN_EMAIL &&
++ gen->type != GEN_DNS &&
++ gen->type != GEN_URI)
++ continue;
++
++ pos = os_malloc(10 + gen->d.ia5->length + 1);
++ if (pos == NULL)
++ break;
++ altsubject[num_altsubject++] = pos;
++
++ switch (gen->type) {
++ case GEN_EMAIL:
++ os_memcpy(pos, "EMAIL:", 6);
++ pos += 6;
++ break;
++ case GEN_DNS:
++ os_memcpy(pos, "DNS:", 4);
++ pos += 4;
++ break;
++ case GEN_URI:
++ os_memcpy(pos, "URI:", 4);
++ pos += 4;
++ break;
++ }
++
++ os_memcpy(pos, gen->d.ia5->data, gen->d.ia5->length);
++ pos += gen->d.ia5->length;
++ *pos = '\0';
++ }
++
++ for (alt = 0; alt < num_altsubject; alt++)
++ ev.peer_cert.altsubject[alt] = altsubject[alt];
++ ev.peer_cert.num_altsubject = num_altsubject;
++
++ context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
+ wpabuf_free(cert);
++ for (alt = 0; alt < num_altsubject; alt++)
++ os_free(altsubject[alt]);
+}
+
+
+static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
+{
+ char buf[256];
+ X509 *err_cert;
+ int err, depth;
+ SSL *ssl;
+ struct tls_connection *conn;
- match = conn ? conn->subject_match : NULL;
- altmatch = conn ? conn->altsubject_match : NULL;
++ struct tls_context *context;
++ char *match, *altmatch, *suffix_match, *domain_match;
+ const char *err_str;
+
+ err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
++ if (!err_cert)
++ return 0;
++
+ err = X509_STORE_CTX_get_error(x509_ctx);
+ depth = X509_STORE_CTX_get_error_depth(x509_ctx);
+ ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
+ SSL_get_ex_data_X509_STORE_CTX_idx());
+ X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
+
+ conn = SSL_get_app_data(ssl);
- if (preverify_ok && depth == 0 && conn->server_cert_only) {
++ if (conn == NULL)
++ return 0;
++
++ if (depth == 0)
++ conn->peer_cert = err_cert;
++ else if (depth == 1)
++ conn->peer_issuer = err_cert;
++ else if (depth == 2)
++ conn->peer_issuer_issuer = err_cert;
++
++ context = conn->context;
++ match = conn->subject_match;
++ altmatch = conn->altsubject_match;
++ suffix_match = conn->suffix_match;
++ domain_match = conn->domain_match;
+
+ if (!preverify_ok && !conn->ca_cert_verify)
+ preverify_ok = 1;
+ if (!preverify_ok && depth > 0 && conn->server_cert_only)
+ preverify_ok = 1;
++ if (!preverify_ok && (conn->flags & TLS_CONN_DISABLE_TIME_CHECKS) &&
++ (err == X509_V_ERR_CERT_HAS_EXPIRED ||
++ err == X509_V_ERR_CERT_NOT_YET_VALID)) {
++ wpa_printf(MSG_DEBUG, "OpenSSL: Ignore certificate validity "
++ "time mismatch");
++ preverify_ok = 1;
++ }
+
+ err_str = X509_verify_cert_error_string(err);
+
+#ifdef CONFIG_SHA256
- static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert)
++ /*
++ * Do not require preverify_ok so we can explicity allow otherwise
++ * invalid pinned server certificates.
++ */
++ if (depth == 0 && conn->server_cert_only) {
+ struct wpabuf *cert;
+ cert = get_x509_cert(err_cert);
+ if (!cert) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Could not fetch "
+ "server certificate data");
+ preverify_ok = 0;
+ } else {
+ u8 hash[32];
+ const u8 *addr[1];
+ size_t len[1];
+ addr[0] = wpabuf_head(cert);
+ len[0] = wpabuf_len(cert);
+ if (sha256_vector(1, addr, len, hash) < 0 ||
+ os_memcmp(conn->srv_cert_hash, hash, 32) != 0) {
+ err_str = "Server certificate mismatch";
+ err = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
+ preverify_ok = 0;
++ } else if (!preverify_ok) {
++ /*
++ * Certificate matches pinned certificate, allow
++ * regardless of other problems.
++ */
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Ignore validation issues for a pinned server certificate");
++ preverify_ok = 1;
+ }
+ wpabuf_free(cert);
+ }
+ }
+#endif /* CONFIG_SHA256 */
+
+ if (!preverify_ok) {
+ wpa_printf(MSG_WARNING, "TLS: Certificate verification failed,"
+ " error %d (%s) depth %d for '%s'", err, err_str,
+ depth, buf);
+ openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ err_str, TLS_FAIL_UNSPECIFIED);
+ return preverify_ok;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - preverify_ok=%d "
+ "err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'",
+ preverify_ok, err, err_str,
+ conn->ca_cert_verify, depth, buf);
+ if (depth == 0 && match && os_strstr(buf, match) == NULL) {
+ wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not "
+ "match with '%s'", buf, match);
+ preverify_ok = 0;
+ openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ "Subject mismatch",
+ TLS_FAIL_SUBJECT_MISMATCH);
+ } else if (depth == 0 && altmatch &&
+ !tls_match_altsubject(err_cert, altmatch)) {
+ wpa_printf(MSG_WARNING, "TLS: altSubjectName match "
+ "'%s' not found", altmatch);
+ preverify_ok = 0;
+ openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ "AltSubject mismatch",
+ TLS_FAIL_ALTSUBJECT_MISMATCH);
++ } else if (depth == 0 && suffix_match &&
++ !tls_match_suffix(err_cert, suffix_match, 0)) {
++ wpa_printf(MSG_WARNING, "TLS: Domain suffix match '%s' not found",
++ suffix_match);
++ preverify_ok = 0;
++ openssl_tls_fail_event(conn, err_cert, err, depth, buf,
++ "Domain suffix mismatch",
++ TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
++ } else if (depth == 0 && domain_match &&
++ !tls_match_suffix(err_cert, domain_match, 1)) {
++ wpa_printf(MSG_WARNING, "TLS: Domain match '%s' not found",
++ domain_match);
++ preverify_ok = 0;
++ openssl_tls_fail_event(conn, err_cert, err, depth, buf,
++ "Domain mismatch",
++ TLS_FAIL_DOMAIN_MISMATCH);
+ } else
+ openssl_tls_cert_event(conn, err_cert, depth, buf);
+
+ if (conn->cert_probe && preverify_ok && depth == 0) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Reject server certificate "
+ "on probe-only run");
+ preverify_ok = 0;
+ openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ "Server certificate chain probe",
+ TLS_FAIL_SERVER_CHAIN_PROBE);
+ }
+
++ if (preverify_ok && context->event_cb != NULL)
++ context->event_cb(context->cb_ctx,
++ TLS_CERT_CHAIN_SUCCESS, NULL);
++
+ return preverify_ok;
+}
+
+
+#ifndef OPENSSL_NO_STDIO
- SSL_CTX *ssl_ctx = _ssl_ctx;
++static int tls_load_ca_der(struct tls_data *data, const char *ca_cert)
+{
- lookup = X509_STORE_add_lookup(ssl_ctx->cert_store,
++ SSL_CTX *ssl_ctx = data->ssl;
+ X509_LOOKUP *lookup;
+ int ret = 0;
+
- static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn,
++ lookup = X509_STORE_add_lookup(SSL_CTX_get_cert_store(ssl_ctx),
+ X509_LOOKUP_file());
+ if (lookup == NULL) {
+ tls_show_errors(MSG_WARNING, __func__,
+ "Failed add lookup for X509 store");
+ return -1;
+ }
+
+ if (!X509_LOOKUP_load_file(lookup, ca_cert, X509_FILETYPE_ASN1)) {
+ unsigned long err = ERR_peek_error();
+ tls_show_errors(MSG_WARNING, __func__,
+ "Failed load CA in DER format");
+ if (ERR_GET_LIB(err) == ERR_LIB_X509 &&
+ ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring "
+ "cert already in hash table error",
+ __func__);
+ } else
+ ret = -1;
+ }
+
+ return ret;
+}
+#endif /* OPENSSL_NO_STDIO */
+
+
- SSL_CTX *ssl_ctx = _ssl_ctx;
++static int tls_connection_ca_cert(struct tls_data *data,
++ struct tls_connection *conn,
+ const char *ca_cert, const u8 *ca_cert_blob,
+ size_t ca_cert_blob_len, const char *ca_path)
+{
- X509_STORE_free(ssl_ctx->cert_store);
- ssl_ctx->cert_store = X509_STORE_new();
- if (ssl_ctx->cert_store == NULL) {
++ SSL_CTX *ssl_ctx = data->ssl;
++ X509_STORE *store;
+
+ /*
+ * Remove previously configured trusted CA certificates before adding
+ * new ones.
+ */
- X509 *cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ca_cert_blob,
++ store = X509_STORE_new();
++ if (store == NULL) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new "
+ "certificate store", __func__);
+ return -1;
+ }
++ SSL_CTX_set_cert_store(ssl_ctx, store);
+
+ SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
+ conn->ca_cert_verify = 1;
+
+ if (ca_cert && os_strncmp(ca_cert, "probe://", 8) == 0) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Probe for server certificate "
+ "chain");
+ conn->cert_probe = 1;
+ conn->ca_cert_verify = 0;
+ return 0;
+ }
+
+ if (ca_cert && os_strncmp(ca_cert, "hash://", 7) == 0) {
+#ifdef CONFIG_SHA256
+ const char *pos = ca_cert + 7;
+ if (os_strncmp(pos, "server/sha256/", 14) != 0) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Unsupported ca_cert "
+ "hash value '%s'", ca_cert);
+ return -1;
+ }
+ pos += 14;
+ if (os_strlen(pos) != 32 * 2) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Unexpected SHA256 "
+ "hash length in ca_cert '%s'", ca_cert);
+ return -1;
+ }
+ if (hexstr2bin(pos, conn->srv_cert_hash, 32) < 0) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Invalid SHA256 hash "
+ "value in ca_cert '%s'", ca_cert);
+ return -1;
+ }
+ conn->server_cert_only = 1;
+ wpa_printf(MSG_DEBUG, "OpenSSL: Checking only server "
+ "certificate match");
+ return 0;
+#else /* CONFIG_SHA256 */
+ wpa_printf(MSG_INFO, "No SHA256 included in the build - "
+ "cannot validate server certificate hash");
+ return -1;
+#endif /* CONFIG_SHA256 */
+ }
+
+ if (ca_cert_blob) {
- if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) {
++ X509 *cert = d2i_X509(NULL,
++ (const unsigned char **) &ca_cert_blob,
+ ca_cert_blob_len);
+ if (cert == NULL) {
+ tls_show_errors(MSG_WARNING, __func__,
+ "Failed to parse ca_cert_blob");
+ return -1;
+ }
+
- tls_load_ca_der(ssl_ctx, ca_cert) == 0) {
++ if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx),
++ cert)) {
+ unsigned long err = ERR_peek_error();
+ tls_show_errors(MSG_WARNING, __func__,
+ "Failed to add ca_cert_blob to "
+ "certificate store");
+ if (ERR_GET_LIB(err) == ERR_LIB_X509 &&
+ ERR_GET_REASON(err) ==
+ X509_R_CERT_ALREADY_IN_HASH_TABLE) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring "
+ "cert already in hash table error",
+ __func__);
+ } else {
+ X509_free(cert);
+ return -1;
+ }
+ }
+ X509_free(cert);
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s - added ca_cert_blob "
+ "to certificate store", __func__);
+ return 0;
+ }
+
++#ifdef ANDROID
++ if (ca_cert && os_strncmp("keystore://", ca_cert, 11) == 0) {
++ BIO *bio = BIO_from_keystore(&ca_cert[11]);
++ STACK_OF(X509_INFO) *stack = NULL;
++ stack_index_t i;
++
++ if (bio) {
++ stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
++ BIO_free(bio);
++ }
++ if (!stack)
++ return -1;
++
++ for (i = 0; i < sk_X509_INFO_num(stack); ++i) {
++ X509_INFO *info = sk_X509_INFO_value(stack, i);
++ if (info->x509) {
++ X509_STORE_add_cert(ssl_ctx->cert_store,
++ info->x509);
++ }
++ if (info->crl) {
++ X509_STORE_add_crl(ssl_ctx->cert_store,
++ info->crl);
++ }
++ }
++ sk_X509_INFO_pop_free(stack, X509_INFO_free);
++ SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
++ return 0;
++ }
++#endif /* ANDROID */
++
+#ifdef CONFIG_NATIVE_WINDOWS
+ if (ca_cert && tls_cryptoapi_ca_cert(ssl_ctx, conn->ssl, ca_cert) ==
+ 0) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Added CA certificates from "
+ "system certificate store");
+ return 0;
+ }
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+ if (ca_cert || ca_path) {
+#ifndef OPENSSL_NO_STDIO
+ if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, ca_path) !=
+ 1) {
+ tls_show_errors(MSG_WARNING, __func__,
+ "Failed to load root certificates");
+ if (ca_cert &&
- tls_get_errors(ssl_ctx);
++ tls_load_ca_der(data, ca_cert) == 0) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s - loaded "
+ "DER format CA certificate",
+ __func__);
+ } else
+ return -1;
+ } else {
+ wpa_printf(MSG_DEBUG, "TLS: Trusted root "
+ "certificate(s) loaded");
- static int tls_global_ca_cert(SSL_CTX *ssl_ctx, const char *ca_cert)
++ tls_get_errors(data);
+ }
+#else /* OPENSSL_NO_STDIO */
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO",
+ __func__);
+ return -1;
+#endif /* OPENSSL_NO_STDIO */
+ } else {
+ /* No ca_cert configured - do not try to verify server
+ * certificate */
+ conn->ca_cert_verify = 0;
+ }
+
+ return 0;
+}
+
+
- X509_STORE *cs = SSL_CTX_get_cert_store(ssl_ctx);
++static int tls_global_ca_cert(struct tls_data *data, const char *ca_cert)
+{
++ SSL_CTX *ssl_ctx = data->ssl;
++
+ if (ca_cert) {
+ if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1)
+ {
+ tls_show_errors(MSG_WARNING, __func__,
+ "Failed to load root certificates");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLS: Trusted root "
+ "certificate(s) loaded");
+
+#ifndef OPENSSL_NO_STDIO
+ /* Add the same CAs to the client certificate requests */
+ SSL_CTX_set_client_CA_list(ssl_ctx,
+ SSL_load_client_CA_file(ca_cert));
+#endif /* OPENSSL_NO_STDIO */
+ }
+
+ return 0;
+}
+
+
+int tls_global_set_verify(void *ssl_ctx, int check_crl)
+{
+ int flags;
+
+ if (check_crl) {
- const char *altsubject_match)
++ struct tls_data *data = ssl_ctx;
++ X509_STORE *cs = SSL_CTX_get_cert_store(data->ssl);
+ if (cs == NULL) {
+ tls_show_errors(MSG_INFO, __func__, "Failed to get "
+ "certificate store when enabling "
+ "check_crl");
+ return -1;
+ }
+ flags = X509_V_FLAG_CRL_CHECK;
+ if (check_crl == 2)
+ flags |= X509_V_FLAG_CRL_CHECK_ALL;
+ X509_STORE_set_flags(cs, flags);
+ }
+ return 0;
+}
+
+
+static int tls_connection_set_subject_match(struct tls_connection *conn,
+ const char *subject_match,
- int verify_peer)
++ const char *altsubject_match,
++ const char *suffix_match,
++ const char *domain_match)
+{
+ os_free(conn->subject_match);
+ conn->subject_match = NULL;
+ if (subject_match) {
+ conn->subject_match = os_strdup(subject_match);
+ if (conn->subject_match == NULL)
+ return -1;
+ }
+
+ os_free(conn->altsubject_match);
+ conn->altsubject_match = NULL;
+ if (altsubject_match) {
+ conn->altsubject_match = os_strdup(altsubject_match);
+ if (conn->altsubject_match == NULL)
+ return -1;
+ }
+
++ os_free(conn->suffix_match);
++ conn->suffix_match = NULL;
++ if (suffix_match) {
++ conn->suffix_match = os_strdup(suffix_match);
++ if (conn->suffix_match == NULL)
++ return -1;
++ }
++
++ os_free(conn->domain_match);
++ conn->domain_match = NULL;
++ if (domain_match) {
++ conn->domain_match = os_strdup(domain_match);
++ if (conn->domain_match == NULL)
++ return -1;
++ }
++
+ return 0;
+}
+
+
++static void tls_set_conn_flags(SSL *ssl, unsigned int flags)
++{
++#ifdef SSL_OP_NO_TICKET
++ if (flags & TLS_CONN_DISABLE_SESSION_TICKET)
++ SSL_set_options(ssl, SSL_OP_NO_TICKET);
++#ifdef SSL_clear_options
++ else
++ SSL_clear_options(ssl, SSL_OP_NO_TICKET);
++#endif /* SSL_clear_options */
++#endif /* SSL_OP_NO_TICKET */
++
++#ifdef SSL_OP_NO_TLSv1
++ if (flags & TLS_CONN_DISABLE_TLSv1_0)
++ SSL_set_options(ssl, SSL_OP_NO_TLSv1);
++ else
++ SSL_clear_options(ssl, SSL_OP_NO_TLSv1);
++#endif /* SSL_OP_NO_TLSv1 */
++#ifdef SSL_OP_NO_TLSv1_1
++ if (flags & TLS_CONN_DISABLE_TLSv1_1)
++ SSL_set_options(ssl, SSL_OP_NO_TLSv1_1);
++ else
++ SSL_clear_options(ssl, SSL_OP_NO_TLSv1_1);
++#endif /* SSL_OP_NO_TLSv1_1 */
++#ifdef SSL_OP_NO_TLSv1_2
++ if (flags & TLS_CONN_DISABLE_TLSv1_2)
++ SSL_set_options(ssl, SSL_OP_NO_TLSv1_2);
++ else
++ SSL_clear_options(ssl, SSL_OP_NO_TLSv1_2);
++#endif /* SSL_OP_NO_TLSv1_2 */
++}
++
++
+int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
- /*
- * Set session id context in order to avoid fatal errors when client
- * tries to resume a session. However, set the context to a unique
- * value in order to effectively disable session resumption for now
- * since not all areas of the server code are ready for it (e.g.,
- * EAP-TTLS needs special handling for Phase 2 after abbreviated TLS
- * handshake).
- */
- counter++;
- SSL_set_session_id_context(conn->ssl,
- (const unsigned char *) &counter,
- sizeof(counter));
++ int verify_peer, unsigned int flags,
++ const u8 *session_ctx, size_t session_ctx_len)
+{
+ static int counter = 0;
++ struct tls_data *data = ssl_ctx;
+
+ if (conn == NULL)
+ return -1;
+
+ if (verify_peer) {
+ conn->ca_cert_verify = 1;
+ SSL_set_verify(conn->ssl, SSL_VERIFY_PEER |
+ SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
+ SSL_VERIFY_CLIENT_ONCE, tls_verify_cb);
+ } else {
+ conn->ca_cert_verify = 0;
+ SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
+ }
+
++ tls_set_conn_flags(conn->ssl, flags);
++ conn->flags = flags;
++
+ SSL_set_accept_state(conn->ssl);
+
- } else {
- tls_show_errors(MSG_DEBUG, __func__,
- "SSL_use_certificate_file (DER) failed");
++ if (data->tls_session_lifetime == 0) {
++ /*
++ * Set session id context to a unique value to make sure
++ * session resumption cannot be used either through session
++ * caching or TLS ticket extension.
++ */
++ counter++;
++ SSL_set_session_id_context(conn->ssl,
++ (const unsigned char *) &counter,
++ sizeof(counter));
++ } else if (session_ctx) {
++ SSL_set_session_id_context(conn->ssl, session_ctx,
++ session_ctx_len);
++ }
+
+ return 0;
+}
+
+
+static int tls_connection_client_cert(struct tls_connection *conn,
+ const char *client_cert,
+ const u8 *client_cert_blob,
+ size_t client_cert_blob_len)
+{
+ if (client_cert == NULL && client_cert_blob == NULL)
+ return 0;
+
+ if (client_cert_blob &&
+ SSL_use_certificate_ASN1(conn->ssl, (u8 *) client_cert_blob,
+ client_cert_blob_len) == 1) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_ASN1 --> "
+ "OK");
+ return 0;
+ } else if (client_cert_blob) {
+ tls_show_errors(MSG_DEBUG, __func__,
+ "SSL_use_certificate_ASN1 failed");
+ }
+
+ if (client_cert == NULL)
+ return -1;
+
++#ifdef ANDROID
++ if (os_strncmp("keystore://", client_cert, 11) == 0) {
++ BIO *bio = BIO_from_keystore(&client_cert[11]);
++ X509 *x509 = NULL;
++ int ret = -1;
++ if (bio) {
++ x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
++ BIO_free(bio);
++ }
++ if (x509) {
++ if (SSL_use_certificate(conn->ssl, x509) == 1)
++ ret = 0;
++ X509_free(x509);
++ }
++ return ret;
++ }
++#endif /* ANDROID */
++
+#ifndef OPENSSL_NO_STDIO
+ if (SSL_use_certificate_file(conn->ssl, client_cert,
+ SSL_FILETYPE_ASN1) == 1) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (DER)"
+ " --> OK");
+ return 0;
- } else {
- tls_show_errors(MSG_DEBUG, __func__,
- "SSL_use_certificate_file (PEM) failed");
+ }
+
+ if (SSL_use_certificate_file(conn->ssl, client_cert,
+ SSL_FILETYPE_PEM) == 1) {
++ ERR_clear_error();
+ wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (PEM)"
+ " --> OK");
+ return 0;
- static int tls_global_client_cert(SSL_CTX *ssl_ctx, const char *client_cert)
+ }
++
++ tls_show_errors(MSG_DEBUG, __func__,
++ "SSL_use_certificate_file failed");
+#else /* OPENSSL_NO_STDIO */
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__);
+#endif /* OPENSSL_NO_STDIO */
+
+ return -1;
+}
+
+
- static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12,
++static int tls_global_client_cert(struct tls_data *data,
++ const char *client_cert)
+{
+#ifndef OPENSSL_NO_STDIO
++ SSL_CTX *ssl_ctx = data->ssl;
++
+ if (client_cert == NULL)
+ return 0;
+
+ if (SSL_CTX_use_certificate_file(ssl_ctx, client_cert,
+ SSL_FILETYPE_ASN1) != 1 &&
++ SSL_CTX_use_certificate_chain_file(ssl_ctx, client_cert) != 1 &&
+ SSL_CTX_use_certificate_file(ssl_ctx, client_cert,
+ SSL_FILETYPE_PEM) != 1) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Failed to load client certificate");
+ return -1;
+ }
+ return 0;
+#else /* OPENSSL_NO_STDIO */
+ if (client_cert == NULL)
+ return 0;
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__);
+ return -1;
+#endif /* OPENSSL_NO_STDIO */
+}
+
+
+static int tls_passwd_cb(char *buf, int size, int rwflag, void *password)
+{
+ if (password == NULL) {
+ return 0;
+ }
+ os_strlcpy(buf, (char *) password, size);
+ return os_strlen(buf);
+}
+
+
+#ifdef PKCS12_FUNCS
- if (SSL_CTX_use_certificate(ssl_ctx, cert) != 1)
++static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12,
+ const char *passwd)
+{
+ EVP_PKEY *pkey;
+ X509 *cert;
+ STACK_OF(X509) *certs;
+ int res = 0;
+ char buf[256];
+
+ pkey = NULL;
+ cert = NULL;
+ certs = NULL;
++ if (!passwd)
++ passwd = "";
+ if (!PKCS12_parse(p12, passwd, &pkey, &cert, &certs)) {
+ tls_show_errors(MSG_DEBUG, __func__,
+ "Failed to parse PKCS12 file");
+ PKCS12_free(p12);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "TLS: Successfully parsed PKCS12 data");
+
+ if (cert) {
+ X509_NAME_oneline(X509_get_subject_name(cert), buf,
+ sizeof(buf));
+ wpa_printf(MSG_DEBUG, "TLS: Got certificate from PKCS12: "
+ "subject='%s'", buf);
+ if (ssl) {
+ if (SSL_use_certificate(ssl, cert) != 1)
+ res = -1;
+ } else {
- if (SSL_CTX_use_PrivateKey(ssl_ctx, pkey) != 1)
++ if (SSL_CTX_use_certificate(data->ssl, cert) != 1)
+ res = -1;
+ }
+ X509_free(cert);
+ }
+
+ if (pkey) {
+ wpa_printf(MSG_DEBUG, "TLS: Got private key from PKCS12");
+ if (ssl) {
+ if (SSL_use_PrivateKey(ssl, pkey) != 1)
+ res = -1;
+ } else {
- if (SSL_CTX_add_extra_chain_cert(ssl_ctx, cert) != 1) {
++ if (SSL_CTX_use_PrivateKey(data->ssl, pkey) != 1)
+ res = -1;
+ }
+ EVP_PKEY_free(pkey);
+ }
+
+ if (certs) {
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++ SSL_clear_chain_certs(ssl);
++ while ((cert = sk_X509_pop(certs)) != NULL) {
++ X509_NAME_oneline(X509_get_subject_name(cert), buf,
++ sizeof(buf));
++ wpa_printf(MSG_DEBUG, "TLS: additional certificate"
++ " from PKCS12: subject='%s'", buf);
++ if (SSL_add1_chain_cert(ssl, cert) != 1) {
++ tls_show_errors(MSG_DEBUG, __func__,
++ "Failed to add additional certificate");
++ res = -1;
++ break;
++ }
++ }
++ if (!res) {
++ /* Try to continue anyway */
++ }
++ sk_X509_free(certs);
++#ifndef OPENSSL_IS_BORINGSSL
++ res = SSL_build_cert_chain(ssl,
++ SSL_BUILD_CHAIN_FLAG_CHECK |
++ SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR);
++ if (!res) {
++ tls_show_errors(MSG_DEBUG, __func__,
++ "Failed to build certificate chain");
++ } else if (res == 2) {
++ wpa_printf(MSG_DEBUG,
++ "TLS: Ignore certificate chain verification error when building chain with PKCS#12 extra certificates");
++ }
++#endif /* OPENSSL_IS_BORINGSSL */
++ /*
++ * Try to continue regardless of result since it is possible for
++ * the extra certificates not to be required.
++ */
++ res = 0;
++#else /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
++#if OPENSSL_VERSION_NUMBER >= 0x10001000L
++ SSL_CTX_clear_extra_chain_certs(data->ssl);
++#endif /* OPENSSL_VERSION_NUMBER >= 0x10001000L */
+ while ((cert = sk_X509_pop(certs)) != NULL) {
+ X509_NAME_oneline(X509_get_subject_name(cert), buf,
+ sizeof(buf));
+ wpa_printf(MSG_DEBUG, "TLS: additional certificate"
+ " from PKCS12: subject='%s'", buf);
+ /*
+ * There is no SSL equivalent for the chain cert - so
+ * always add it to the context...
+ */
- tls_get_errors(ssl_ctx);
++ if (SSL_CTX_add_extra_chain_cert(data->ssl, cert) != 1)
++ {
+ res = -1;
+ break;
+ }
+ }
+ sk_X509_free(certs);
++#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
+ }
+
+ PKCS12_free(p12);
+
+ if (res < 0)
- static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key,
- const char *passwd)
++ tls_get_errors(data);
+
+ return res;
+}
+#endif /* PKCS12_FUNCS */
+
+
- return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd);
++static int tls_read_pkcs12(struct tls_data *data, SSL *ssl,
++ const char *private_key, const char *passwd)
+{
+#ifdef PKCS12_FUNCS
+ FILE *f;
+ PKCS12 *p12;
+
+ f = fopen(private_key, "rb");
+ if (f == NULL)
+ return -1;
+
+ p12 = d2i_PKCS12_fp(f, NULL);
+ fclose(f);
+
+ if (p12 == NULL) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Failed to use PKCS#12 file");
+ return -1;
+ }
+
- static int tls_read_pkcs12_blob(SSL_CTX *ssl_ctx, SSL *ssl,
++ return tls_parse_pkcs12(data, ssl, p12, passwd);
+
+#else /* PKCS12_FUNCS */
+ wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot read "
+ "p12/pfx files");
+ return -1;
+#endif /* PKCS12_FUNCS */
+}
+
+
- p12 = d2i_PKCS12(NULL, (OPENSSL_d2i_TYPE) &blob, len);
++static int tls_read_pkcs12_blob(struct tls_data *data, SSL *ssl,
+ const u8 *blob, size_t len, const char *passwd)
+{
+#ifdef PKCS12_FUNCS
+ PKCS12 *p12;
+
- return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd);
++ p12 = d2i_PKCS12(NULL, (const unsigned char **) &blob, len);
+ if (p12 == NULL) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Failed to use PKCS#12 blob");
+ return -1;
+ }
+
- ERR_error_string(ERR_get_error(), NULL));
++ return tls_parse_pkcs12(data, ssl, p12, passwd);
+
+#else /* PKCS12_FUNCS */
+ wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot parse "
+ "p12/pfx blobs");
+ return -1;
+#endif /* PKCS12_FUNCS */
+}
+
+
+#ifndef OPENSSL_NO_ENGINE
+static int tls_engine_get_cert(struct tls_connection *conn,
+ const char *cert_id,
+ X509 **cert)
+{
+ /* this runs after the private key is loaded so no PIN is required */
+ struct {
+ const char *cert_id;
+ X509 *cert;
+ } params;
+ params.cert_id = cert_id;
+ params.cert = NULL;
+
+ if (!ENGINE_ctrl_cmd(conn->engine, "LOAD_CERT_CTRL",
+ 0, ¶ms, NULL, 1)) {
++ unsigned long err = ERR_get_error();
++
+ wpa_printf(MSG_ERROR, "ENGINE: cannot load client cert with id"
+ " '%s' [%s]", cert_id,
- static int tls_connection_engine_ca_cert(void *_ssl_ctx,
++ ERR_error_string(err, NULL));
++ if (tls_is_pin_error(err))
++ return TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN;
+ return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+ }
+ if (!params.cert) {
+ wpa_printf(MSG_ERROR, "ENGINE: did not properly cert with id"
+ " '%s'", cert_id);
+ return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+ }
+ *cert = params.cert;
+ return 0;
+}
+#endif /* OPENSSL_NO_ENGINE */
+
+
+static int tls_connection_engine_client_cert(struct tls_connection *conn,
+ const char *cert_id)
+{
+#ifndef OPENSSL_NO_ENGINE
+ X509 *cert;
+
+ if (tls_engine_get_cert(conn, cert_id, &cert))
+ return -1;
+
+ if (!SSL_use_certificate(conn->ssl, cert)) {
+ tls_show_errors(MSG_ERROR, __func__,
+ "SSL_use_certificate failed");
+ X509_free(cert);
+ return -1;
+ }
+ X509_free(cert);
+ wpa_printf(MSG_DEBUG, "ENGINE: SSL_use_certificate --> "
+ "OK");
+ return 0;
+
+#else /* OPENSSL_NO_ENGINE */
+ return -1;
+#endif /* OPENSSL_NO_ENGINE */
+}
+
+
- SSL_CTX *ssl_ctx = _ssl_ctx;
++static int tls_connection_engine_ca_cert(struct tls_data *data,
+ struct tls_connection *conn,
+ const char *ca_cert_id)
+{
+#ifndef OPENSSL_NO_ENGINE
+ X509 *cert;
- X509_STORE_free(ssl_ctx->cert_store);
- ssl_ctx->cert_store = X509_STORE_new();
- if (ssl_ctx->cert_store == NULL) {
++ SSL_CTX *ssl_ctx = data->ssl;
++ X509_STORE *store;
+
+ if (tls_engine_get_cert(conn, ca_cert_id, &cert))
+ return -1;
+
+ /* start off the same as tls_connection_ca_cert */
- if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) {
++ store = X509_STORE_new();
++ if (store == NULL) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new "
+ "certificate store", __func__);
+ X509_free(cert);
+ return -1;
+ }
- static int tls_connection_private_key(void *_ssl_ctx,
++ SSL_CTX_set_cert_store(ssl_ctx, store);
++ if (!X509_STORE_add_cert(store, cert)) {
+ unsigned long err = ERR_peek_error();
+ tls_show_errors(MSG_WARNING, __func__,
+ "Failed to add CA certificate from engine "
+ "to certificate store");
+ if (ERR_GET_LIB(err) == ERR_LIB_X509 &&
+ ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring cert"
+ " already in hash table error",
+ __func__);
+ } else {
+ X509_free(cert);
+ return -1;
+ }
+ }
+ X509_free(cert);
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s - added CA certificate from engine "
+ "to certificate store", __func__);
+ SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
++ conn->ca_cert_verify = 1;
++
+ return 0;
+
+#else /* OPENSSL_NO_ENGINE */
+ return -1;
+#endif /* OPENSSL_NO_ENGINE */
+}
+
+
+static int tls_connection_engine_private_key(struct tls_connection *conn)
+{
+#ifndef OPENSSL_NO_ENGINE
+ if (SSL_use_PrivateKey(conn->ssl, conn->private_key) != 1) {
+ tls_show_errors(MSG_ERROR, __func__,
+ "ENGINE: cannot use private key for TLS");
+ return -1;
+ }
+ if (!SSL_check_private_key(conn->ssl)) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Private key failed verification");
+ return -1;
+ }
+ return 0;
+#else /* OPENSSL_NO_ENGINE */
+ wpa_printf(MSG_ERROR, "SSL: Configuration uses engine, but "
+ "engine support was not compiled in");
+ return -1;
+#endif /* OPENSSL_NO_ENGINE */
+}
+
+
- SSL_CTX *ssl_ctx = _ssl_ctx;
++static int tls_connection_private_key(struct tls_data *data,
+ struct tls_connection *conn,
+ const char *private_key,
+ const char *private_key_passwd,
+ const u8 *private_key_blob,
+ size_t private_key_blob_len)
+{
- } else {
- tls_show_errors(MSG_DEBUG, __func__,
- "SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA)"
- " failed");
++ SSL_CTX *ssl_ctx = data->ssl;
+ char *passwd;
+ int ok;
+
+ if (private_key == NULL && private_key_blob == NULL)
+ return 0;
+
+ if (private_key_passwd) {
+ passwd = os_strdup(private_key_passwd);
+ if (passwd == NULL)
+ return -1;
+ } else
+ passwd = NULL;
+
+ SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb);
+ SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd);
+
+ ok = 0;
+ while (private_key_blob) {
+ if (SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA, conn->ssl,
+ (u8 *) private_key_blob,
+ private_key_blob_len) == 1) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_PrivateKey_"
+ "ASN1(EVP_PKEY_RSA) --> OK");
+ ok = 1;
+ break;
- } else {
- tls_show_errors(MSG_DEBUG, __func__,
- "SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA)"
- " failed");
+ }
+
+ if (SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA, conn->ssl,
+ (u8 *) private_key_blob,
+ private_key_blob_len) == 1) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_PrivateKey_"
+ "ASN1(EVP_PKEY_DSA) --> OK");
+ ok = 1;
+ break;
- } else {
- tls_show_errors(MSG_DEBUG, __func__,
- "SSL_use_RSAPrivateKey_ASN1 failed");
+ }
+
+ if (SSL_use_RSAPrivateKey_ASN1(conn->ssl,
+ (u8 *) private_key_blob,
+ private_key_blob_len) == 1) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: "
+ "SSL_use_RSAPrivateKey_ASN1 --> OK");
+ ok = 1;
+ break;
- if (tls_read_pkcs12_blob(ssl_ctx, conn->ssl, private_key_blob,
+ }
+
- } else {
- tls_show_errors(MSG_DEBUG, __func__,
- "SSL_use_PrivateKey_File (DER) "
- "failed");
++ if (tls_read_pkcs12_blob(data, conn->ssl, private_key_blob,
+ private_key_blob_len, passwd) == 0) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: PKCS#12 as blob --> "
+ "OK");
+ ok = 1;
+ break;
+ }
+
+ break;
+ }
+
+ while (!ok && private_key) {
+#ifndef OPENSSL_NO_STDIO
+ if (SSL_use_PrivateKey_file(conn->ssl, private_key,
+ SSL_FILETYPE_ASN1) == 1) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: "
+ "SSL_use_PrivateKey_File (DER) --> OK");
+ ok = 1;
+ break;
- } else {
- tls_show_errors(MSG_DEBUG, __func__,
- "SSL_use_PrivateKey_File (PEM) "
- "failed");
+ }
+
+ if (SSL_use_PrivateKey_file(conn->ssl, private_key,
+ SSL_FILETYPE_PEM) == 1) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: "
+ "SSL_use_PrivateKey_File (PEM) --> OK");
+ ok = 1;
+ break;
- if (tls_read_pkcs12(ssl_ctx, conn->ssl, private_key, passwd)
+ }
+#else /* OPENSSL_NO_STDIO */
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO",
+ __func__);
+#endif /* OPENSSL_NO_STDIO */
+
- wpa_printf(MSG_INFO, "OpenSSL: Failed to load private key");
++ if (tls_read_pkcs12(data, conn->ssl, private_key, passwd)
+ == 0) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Reading PKCS#12 file "
+ "--> OK");
+ ok = 1;
+ break;
+ }
+
+ if (tls_cryptoapi_cert(conn->ssl, private_key) == 0) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Using CryptoAPI to "
+ "access certificate store --> OK");
+ ok = 1;
+ break;
+ }
+
+ break;
+ }
+
+ if (!ok) {
- ERR_clear_error();
++ tls_show_errors(MSG_INFO, __func__,
++ "Failed to load private key");
+ os_free(passwd);
-
+ return -1;
+ }
+ ERR_clear_error();
+ SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
+ os_free(passwd);
- static int tls_global_private_key(SSL_CTX *ssl_ctx, const char *private_key,
++
+ if (!SSL_check_private_key(conn->ssl)) {
+ tls_show_errors(MSG_INFO, __func__, "Private key failed "
+ "verification");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "SSL: Private key loaded successfully");
+ return 0;
+}
+
+
- tls_read_pkcs12(ssl_ctx, NULL, private_key, passwd)) {
++static int tls_global_private_key(struct tls_data *data,
++ const char *private_key,
+ const char *private_key_passwd)
+{
++ SSL_CTX *ssl_ctx = data->ssl;
+ char *passwd;
+
+ if (private_key == NULL)
+ return 0;
+
+ if (private_key_passwd) {
+ passwd = os_strdup(private_key_passwd);
+ if (passwd == NULL)
+ return -1;
+ } else
+ passwd = NULL;
+
+ SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb);
+ SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd);
+ if (
+#ifndef OPENSSL_NO_STDIO
+ SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key,
+ SSL_FILETYPE_ASN1) != 1 &&
+ SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key,
+ SSL_FILETYPE_PEM) != 1 &&
+#endif /* OPENSSL_NO_STDIO */
-
++ tls_read_pkcs12(data, NULL, private_key, passwd)) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Failed to load private key");
+ os_free(passwd);
+ ERR_clear_error();
+ return -1;
+ }
+ os_free(passwd);
+ ERR_clear_error();
+ SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
- static int tls_global_dh(SSL_CTX *ssl_ctx, const char *dh_file)
++
+ if (!SSL_CTX_check_private_key(ssl_ctx)) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Private key failed verification");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int tls_connection_dh(struct tls_connection *conn, const char *dh_file)
+{
+#ifdef OPENSSL_NO_DH
+ if (dh_file == NULL)
+ return 0;
+ wpa_printf(MSG_ERROR, "TLS: openssl does not include DH support, but "
+ "dh_file specified");
+ return -1;
+#else /* OPENSSL_NO_DH */
+ DH *dh;
+ BIO *bio;
+
+ /* TODO: add support for dh_blob */
+ if (dh_file == NULL)
+ return 0;
+ if (conn == NULL)
+ return -1;
+
+ bio = BIO_new_file(dh_file, "r");
+ if (bio == NULL) {
+ wpa_printf(MSG_INFO, "TLS: Failed to open DH file '%s': %s",
+ dh_file, ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+ dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+ BIO_free(bio);
+#ifndef OPENSSL_NO_DSA
+ while (dh == NULL) {
+ DSA *dsa;
+ wpa_printf(MSG_DEBUG, "TLS: Failed to parse DH file '%s': %s -"
+ " trying to parse as DSA params", dh_file,
+ ERR_error_string(ERR_get_error(), NULL));
+ bio = BIO_new_file(dh_file, "r");
+ if (bio == NULL)
+ break;
+ dsa = PEM_read_bio_DSAparams(bio, NULL, NULL, NULL);
+ BIO_free(bio);
+ if (!dsa) {
+ wpa_printf(MSG_DEBUG, "TLS: Failed to parse DSA file "
+ "'%s': %s", dh_file,
+ ERR_error_string(ERR_get_error(), NULL));
+ break;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLS: DH file in DSA param format");
+ dh = DSA_dup_DH(dsa);
+ DSA_free(dsa);
+ if (dh == NULL) {
+ wpa_printf(MSG_INFO, "TLS: Failed to convert DSA "
+ "params into DH params");
+ break;
+ }
+ break;
+ }
+#endif /* !OPENSSL_NO_DSA */
+ if (dh == NULL) {
+ wpa_printf(MSG_INFO, "TLS: Failed to read/parse DH/DSA file "
+ "'%s'", dh_file);
+ return -1;
+ }
+
+ if (SSL_set_tmp_dh(conn->ssl, dh) != 1) {
+ wpa_printf(MSG_INFO, "TLS: Failed to set DH params from '%s': "
+ "%s", dh_file,
+ ERR_error_string(ERR_get_error(), NULL));
+ DH_free(dh);
+ return -1;
+ }
+ DH_free(dh);
+ return 0;
+#endif /* OPENSSL_NO_DH */
+}
+
+
- int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
- struct tls_keys *keys)
++static int tls_global_dh(struct tls_data *data, const char *dh_file)
+{
+#ifdef OPENSSL_NO_DH
+ if (dh_file == NULL)
+ return 0;
+ wpa_printf(MSG_ERROR, "TLS: openssl does not include DH support, but "
+ "dh_file specified");
+ return -1;
+#else /* OPENSSL_NO_DH */
++ SSL_CTX *ssl_ctx = data->ssl;
+ DH *dh;
+ BIO *bio;
+
+ /* TODO: add support for dh_blob */
+ if (dh_file == NULL)
+ return 0;
+ if (ssl_ctx == NULL)
+ return -1;
+
+ bio = BIO_new_file(dh_file, "r");
+ if (bio == NULL) {
+ wpa_printf(MSG_INFO, "TLS: Failed to open DH file '%s': %s",
+ dh_file, ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+ dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+ BIO_free(bio);
+#ifndef OPENSSL_NO_DSA
+ while (dh == NULL) {
+ DSA *dsa;
+ wpa_printf(MSG_DEBUG, "TLS: Failed to parse DH file '%s': %s -"
+ " trying to parse as DSA params", dh_file,
+ ERR_error_string(ERR_get_error(), NULL));
+ bio = BIO_new_file(dh_file, "r");
+ if (bio == NULL)
+ break;
+ dsa = PEM_read_bio_DSAparams(bio, NULL, NULL, NULL);
+ BIO_free(bio);
+ if (!dsa) {
+ wpa_printf(MSG_DEBUG, "TLS: Failed to parse DSA file "
+ "'%s': %s", dh_file,
+ ERR_error_string(ERR_get_error(), NULL));
+ break;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLS: DH file in DSA param format");
+ dh = DSA_dup_DH(dsa);
+ DSA_free(dsa);
+ if (dh == NULL) {
+ wpa_printf(MSG_INFO, "TLS: Failed to convert DSA "
+ "params into DH params");
+ break;
+ }
+ break;
+ }
+#endif /* !OPENSSL_NO_DSA */
+ if (dh == NULL) {
+ wpa_printf(MSG_INFO, "TLS: Failed to read/parse DH/DSA file "
+ "'%s'", dh_file);
+ return -1;
+ }
+
+ if (SSL_CTX_set_tmp_dh(ssl_ctx, dh) != 1) {
+ wpa_printf(MSG_INFO, "TLS: Failed to set DH params from '%s': "
+ "%s", dh_file,
+ ERR_error_string(ERR_get_error(), NULL));
+ DH_free(dh);
+ return -1;
+ }
+ DH_free(dh);
+ return 0;
+#endif /* OPENSSL_NO_DH */
+}
+
+
- keys->master_key = ssl->session->master_key;
- keys->master_key_len = ssl->session->master_key_length;
++int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn,
++ struct tls_random *keys)
+{
+ SSL *ssl;
+
+ if (conn == NULL || keys == NULL)
+ return -1;
+ ssl = conn->ssl;
++#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL)
+ return -1;
+
+ os_memset(keys, 0, sizeof(*keys));
- u8 *out, size_t out_len)
+ keys->client_random = ssl->s3->client_random;
+ keys->client_random_len = SSL3_RANDOM_SIZE;
+ keys->server_random = ssl->s3->server_random;
+ keys->server_random_len = SSL3_RANDOM_SIZE;
++#else
++ if (ssl == NULL)
++ return -1;
++
++ os_memset(keys, 0, sizeof(*keys));
++ keys->client_random = conn->client_random;
++ keys->client_random_len = SSL_get_client_random(
++ ssl, conn->client_random, sizeof(conn->client_random));
++ keys->server_random = conn->server_random;
++ keys->server_random_len = SSL_get_server_random(
++ ssl, conn->server_random, sizeof(conn->server_random));
++#endif
+
+ return 0;
+}
+
+
++#ifndef CONFIG_FIPS
++static int openssl_get_keyblock_size(SSL *ssl)
++{
++#if OPENSSL_VERSION_NUMBER < 0x10100000L
++ const EVP_CIPHER *c;
++ const EVP_MD *h;
++ int md_size;
++
++ if (ssl->enc_read_ctx == NULL || ssl->enc_read_ctx->cipher == NULL ||
++ ssl->read_hash == NULL)
++ return -1;
++
++ c = ssl->enc_read_ctx->cipher;
++#if OPENSSL_VERSION_NUMBER >= 0x00909000L
++ h = EVP_MD_CTX_md(ssl->read_hash);
++#else
++ h = ssl->read_hash;
++#endif
++ if (h)
++ md_size = EVP_MD_size(h);
++#if OPENSSL_VERSION_NUMBER >= 0x10000000L
++ else if (ssl->s3)
++ md_size = ssl->s3->tmp.new_mac_secret_size;
++#endif
++ else
++ return -1;
++
++ wpa_printf(MSG_DEBUG, "OpenSSL: keyblock size: key_len=%d MD_size=%d "
++ "IV_len=%d", EVP_CIPHER_key_length(c), md_size,
++ EVP_CIPHER_iv_length(c));
++ return 2 * (EVP_CIPHER_key_length(c) +
++ md_size +
++ EVP_CIPHER_iv_length(c));
++#else
++ const SSL_CIPHER *ssl_cipher;
++ int cipher, digest;
++ const EVP_CIPHER *c;
++ const EVP_MD *h;
++
++ ssl_cipher = SSL_get_current_cipher(ssl);
++ if (!ssl_cipher)
++ return -1;
++ cipher = SSL_CIPHER_get_cipher_nid(ssl_cipher);
++ digest = SSL_CIPHER_get_digest_nid(ssl_cipher);
++ wpa_printf(MSG_DEBUG, "OpenSSL: cipher nid %d digest nid %d",
++ cipher, digest);
++ if (cipher < 0 || digest < 0)
++ return -1;
++ c = EVP_get_cipherbynid(cipher);
++ h = EVP_get_digestbynid(digest);
++ if (!c || !h)
++ return -1;
++
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: keyblock size: key_len=%d MD_size=%d IV_len=%d",
++ EVP_CIPHER_key_length(c), EVP_MD_size(h),
++ EVP_CIPHER_iv_length(c));
++ return 2 * (EVP_CIPHER_key_length(c) + EVP_MD_size(h) +
++ EVP_CIPHER_iv_length(c));
++#endif
++}
++#endif /* CONFIG_FIPS */
++
++
++static int openssl_tls_prf(struct tls_connection *conn,
++ const char *label, int server_random_first,
++ int skip_keyblock, u8 *out, size_t out_len)
++{
++#ifdef CONFIG_FIPS
++ wpa_printf(MSG_ERROR, "OpenSSL: TLS keys cannot be exported in FIPS "
++ "mode");
++ return -1;
++#else /* CONFIG_FIPS */
++#if OPENSSL_VERSION_NUMBER < 0x10100000L
++ SSL *ssl;
++ u8 *rnd;
++ int ret = -1;
++ int skip = 0;
++ u8 *tmp_out = NULL;
++ u8 *_out = out;
++ const char *ver;
++
++ /*
++ * TLS library did not support key generation, so get the needed TLS
++ * session parameters and use an internal implementation of TLS PRF to
++ * derive the key.
++ */
++
++ if (conn == NULL)
++ return -1;
++ ssl = conn->ssl;
++ if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL ||
++ ssl->session->master_key_length <= 0)
++ return -1;
++ ver = SSL_get_version(ssl);
++
++ if (skip_keyblock) {
++ skip = openssl_get_keyblock_size(ssl);
++ if (skip < 0)
++ return -1;
++ tmp_out = os_malloc(skip + out_len);
++ if (!tmp_out)
++ return -1;
++ _out = tmp_out;
++ }
++
++ rnd = os_malloc(2 * SSL3_RANDOM_SIZE);
++ if (!rnd) {
++ os_free(tmp_out);
++ return -1;
++ }
++
++ if (server_random_first) {
++ os_memcpy(rnd, ssl->s3->server_random, SSL3_RANDOM_SIZE);
++ os_memcpy(rnd + SSL3_RANDOM_SIZE, ssl->s3->client_random,
++ SSL3_RANDOM_SIZE);
++ } else {
++ os_memcpy(rnd, ssl->s3->client_random, SSL3_RANDOM_SIZE);
++ os_memcpy(rnd + SSL3_RANDOM_SIZE, ssl->s3->server_random,
++ SSL3_RANDOM_SIZE);
++ }
++
++ if (os_strcmp(ver, "TLSv1.2") == 0) {
++ tls_prf_sha256(ssl->session->master_key,
++ ssl->session->master_key_length,
++ label, rnd, 2 * SSL3_RANDOM_SIZE,
++ _out, skip + out_len);
++ ret = 0;
++ } else if (tls_prf_sha1_md5(ssl->session->master_key,
++ ssl->session->master_key_length,
++ label, rnd, 2 * SSL3_RANDOM_SIZE,
++ _out, skip + out_len) == 0) {
++ ret = 0;
++ }
++ os_free(rnd);
++ if (ret == 0 && skip_keyblock)
++ os_memcpy(out, _out + skip, out_len);
++ bin_clear_free(tmp_out, skip);
++
++ return ret;
++#else
++ SSL *ssl;
++ SSL_SESSION *sess;
++ u8 *rnd;
++ int ret = -1;
++ int skip = 0;
++ u8 *tmp_out = NULL;
++ u8 *_out = out;
++ unsigned char client_random[SSL3_RANDOM_SIZE];
++ unsigned char server_random[SSL3_RANDOM_SIZE];
++ unsigned char master_key[64];
++ size_t master_key_len;
++ const char *ver;
++
++ /*
++ * TLS library did not support key generation, so get the needed TLS
++ * session parameters and use an internal implementation of TLS PRF to
++ * derive the key.
++ */
++
++ if (conn == NULL)
++ return -1;
++ ssl = conn->ssl;
++ if (ssl == NULL)
++ return -1;
++ ver = SSL_get_version(ssl);
++ sess = SSL_get_session(ssl);
++ if (!ver || !sess)
++ return -1;
++
++ if (skip_keyblock) {
++ skip = openssl_get_keyblock_size(ssl);
++ if (skip < 0)
++ return -1;
++ tmp_out = os_malloc(skip + out_len);
++ if (!tmp_out)
++ return -1;
++ _out = tmp_out;
++ }
++
++ rnd = os_malloc(2 * SSL3_RANDOM_SIZE);
++ if (!rnd) {
++ os_free(tmp_out);
++ return -1;
++ }
++
++ SSL_get_client_random(ssl, client_random, sizeof(client_random));
++ SSL_get_server_random(ssl, server_random, sizeof(server_random));
++ master_key_len = SSL_SESSION_get_master_key(sess, master_key,
++ sizeof(master_key));
++
++ if (server_random_first) {
++ os_memcpy(rnd, server_random, SSL3_RANDOM_SIZE);
++ os_memcpy(rnd + SSL3_RANDOM_SIZE, client_random,
++ SSL3_RANDOM_SIZE);
++ } else {
++ os_memcpy(rnd, client_random, SSL3_RANDOM_SIZE);
++ os_memcpy(rnd + SSL3_RANDOM_SIZE, server_random,
++ SSL3_RANDOM_SIZE);
++ }
++
++ if (os_strcmp(ver, "TLSv1.2") == 0) {
++ tls_prf_sha256(master_key, master_key_len,
++ label, rnd, 2 * SSL3_RANDOM_SIZE,
++ _out, skip + out_len);
++ ret = 0;
++ } else if (tls_prf_sha1_md5(master_key, master_key_len,
++ label, rnd, 2 * SSL3_RANDOM_SIZE,
++ _out, skip + out_len) == 0) {
++ ret = 0;
++ }
++ os_memset(master_key, 0, sizeof(master_key));
++ os_free(rnd);
++ if (ret == 0 && skip_keyblock)
++ os_memcpy(out, _out + skip, out_len);
++ bin_clear_free(tmp_out, skip);
++
++ return ret;
++#endif
++#endif /* CONFIG_FIPS */
++}
++
++
+int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+ const char *label, int server_random_first,
- return -1;
++ int skip_keyblock, u8 *out, size_t out_len)
+{
- if (in_data &&
++#if OPENSSL_VERSION_NUMBER >= 0x10001000L
++ SSL *ssl;
++ if (conn == NULL)
++ return -1;
++ if (server_random_first || skip_keyblock)
++ return openssl_tls_prf(conn, label,
++ server_random_first, skip_keyblock,
++ out, out_len);
++ ssl = conn->ssl;
++ if (SSL_export_keying_material(ssl, out, out_len, label,
++ os_strlen(label), NULL, 0, 0) == 1) {
++ wpa_printf(MSG_DEBUG, "OpenSSL: Using internal PRF");
++ return 0;
++ }
++#endif
++ return openssl_tls_prf(conn, label, server_random_first,
++ skip_keyblock, out, out_len);
+}
+
+
+static struct wpabuf *
+openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data,
+ int server)
+{
+ int res;
+ struct wpabuf *out_data;
+
+ /*
+ * Give TLS handshake data from the server (if available) to OpenSSL
+ * for processing.
+ */
- if (SSL_is_init_finished(conn->ssl) && appl_data && in_data)
- *appl_data = openssl_get_appl_data(conn, wpabuf_len(in_data));
++ if (in_data && wpabuf_len(in_data) > 0 &&
+ BIO_write(conn->ssl_in, wpabuf_head(in_data), wpabuf_len(in_data))
+ < 0) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Handshake failed - BIO_write");
+ return NULL;
+ }
+
+ /* Initiate TLS handshake or continue the existing handshake */
+ if (server)
+ res = SSL_accept(conn->ssl);
+ else
+ res = SSL_connect(conn->ssl);
+ if (res != 1) {
+ int err = SSL_get_error(conn->ssl, res);
+ if (err == SSL_ERROR_WANT_READ)
+ wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want "
+ "more data");
+ else if (err == SSL_ERROR_WANT_WRITE)
+ wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want to "
+ "write");
+ else {
+ tls_show_errors(MSG_INFO, __func__, "SSL_connect");
+ conn->failed++;
+ }
+ }
+
+ /* Get the TLS handshake data to be sent to the server */
+ res = BIO_ctrl_pending(conn->ssl_out);
+ wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res);
+ out_data = wpabuf_alloc(res);
+ if (out_data == NULL) {
+ wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for "
+ "handshake output (%d bytes)", res);
+ if (BIO_reset(conn->ssl_out) < 0) {
+ tls_show_errors(MSG_INFO, __func__,
+ "BIO_reset failed");
+ }
+ return NULL;
+ }
+ res = res == 0 ? 0 : BIO_read(conn->ssl_out, wpabuf_mhead(out_data),
+ res);
+ if (res < 0) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Handshake failed - BIO_read");
+ if (BIO_reset(conn->ssl_out) < 0) {
+ tls_show_errors(MSG_INFO, __func__,
+ "BIO_reset failed");
+ }
+ wpabuf_free(out_data);
+ return NULL;
+ }
+ wpabuf_put(out_data, res);
+
+ return out_data;
+}
+
+
+static struct wpabuf *
+openssl_get_appl_data(struct tls_connection *conn, size_t max_len)
+{
+ struct wpabuf *appl_data;
+ int res;
+
+ appl_data = wpabuf_alloc(max_len + 100);
+ if (appl_data == NULL)
+ return NULL;
+
+ res = SSL_read(conn->ssl, wpabuf_mhead(appl_data),
+ wpabuf_size(appl_data));
+ if (res < 0) {
+ int err = SSL_get_error(conn->ssl, res);
+ if (err == SSL_ERROR_WANT_READ ||
+ err == SSL_ERROR_WANT_WRITE) {
+ wpa_printf(MSG_DEBUG, "SSL: No Application Data "
+ "included");
+ } else {
+ tls_show_errors(MSG_INFO, __func__,
+ "Failed to read possible "
+ "Application Data");
+ }
+ wpabuf_free(appl_data);
+ return NULL;
+ }
+
+ wpabuf_put(appl_data, res);
+ wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application Data in Finished "
+ "message", appl_data);
+
+ return appl_data;
+}
+
+
+static struct wpabuf *
+openssl_connection_handshake(struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data, int server)
+{
+ struct wpabuf *out_data;
+
+ if (appl_data)
+ *appl_data = NULL;
+
+ out_data = openssl_handshake(conn, in_data, server);
+ if (out_data == NULL)
+ return NULL;
++ if (conn->invalid_hb_used) {
++ wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response");
++ wpabuf_free(out_data);
++ return NULL;
++ }
+
- if (ret < 0 || ret >= end - pos)
++ if (SSL_is_init_finished(conn->ssl)) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Handshake finished - resumed=%d",
++ tls_connection_resumed(conn->ssl_ctx, conn));
++ if (appl_data && in_data)
++ *appl_data = openssl_get_appl_data(conn,
++ wpabuf_len(in_data));
++ }
++
++ if (conn->invalid_hb_used) {
++ wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response");
++ if (appl_data) {
++ wpabuf_free(*appl_data);
++ *appl_data = NULL;
++ }
++ wpabuf_free(out_data);
++ return NULL;
++ }
+
+ return out_data;
+}
+
+
+struct wpabuf *
+tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data)
+{
+ return openssl_connection_handshake(conn, in_data, appl_data, 0);
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data)
+{
+ return openssl_connection_handshake(conn, in_data, appl_data, 1);
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data)
+{
+ int res;
+ struct wpabuf *buf;
+
+ if (conn == NULL)
+ return NULL;
+
+ /* Give plaintext data for OpenSSL to encrypt into the TLS tunnel. */
+ if ((res = BIO_reset(conn->ssl_in)) < 0 ||
+ (res = BIO_reset(conn->ssl_out)) < 0) {
+ tls_show_errors(MSG_INFO, __func__, "BIO_reset failed");
+ return NULL;
+ }
+ res = SSL_write(conn->ssl, wpabuf_head(in_data), wpabuf_len(in_data));
+ if (res < 0) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Encryption failed - SSL_write");
+ return NULL;
+ }
+
+ /* Read encrypted data to be sent to the server */
+ buf = wpabuf_alloc(wpabuf_len(in_data) + 300);
+ if (buf == NULL)
+ return NULL;
+ res = BIO_read(conn->ssl_out, wpabuf_mhead(buf), wpabuf_size(buf));
+ if (res < 0) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Encryption failed - BIO_read");
+ wpabuf_free(buf);
+ return NULL;
+ }
+ wpabuf_put(buf, res);
+
+ return buf;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data)
+{
+ int res;
+ struct wpabuf *buf;
+
+ /* Give encrypted data from TLS tunnel for OpenSSL to decrypt. */
+ res = BIO_write(conn->ssl_in, wpabuf_head(in_data),
+ wpabuf_len(in_data));
+ if (res < 0) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Decryption failed - BIO_write");
+ return NULL;
+ }
+ if (BIO_reset(conn->ssl_out) < 0) {
+ tls_show_errors(MSG_INFO, __func__, "BIO_reset failed");
+ return NULL;
+ }
+
+ /* Read decrypted data for further processing */
+ /*
+ * Even though we try to disable TLS compression, it is possible that
+ * this cannot be done with all TLS libraries. Add extra buffer space
+ * to handle the possibility of the decrypted data being longer than
+ * input data.
+ */
+ buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
+ if (buf == NULL)
+ return NULL;
+ res = SSL_read(conn->ssl, wpabuf_mhead(buf), wpabuf_size(buf));
+ if (res < 0) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Decryption failed - SSL_read");
+ wpabuf_free(buf);
+ return NULL;
+ }
+ wpabuf_put(buf, res);
+
++ if (conn->invalid_hb_used) {
++ wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response");
++ wpabuf_free(buf);
++ return NULL;
++ }
++
+ return buf;
+}
+
+
+int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn)
+{
++#if OPENSSL_VERSION_NUMBER >= 0x10001000L
++ return conn ? SSL_cache_hit(conn->ssl) : 0;
++#else
+ return conn ? conn->ssl->hit : 0;
++#endif
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+ u8 *ciphers)
+{
+ char buf[100], *pos, *end;
+ u8 *c;
+ int ret;
+
+ if (conn == NULL || conn->ssl == NULL || ciphers == NULL)
+ return -1;
+
+ buf[0] = '\0';
+ pos = buf;
+ end = pos + sizeof(buf);
+
+ c = ciphers;
+ while (*c != TLS_CIPHER_NONE) {
+ const char *suite;
+
+ switch (*c) {
+ case TLS_CIPHER_RC4_SHA:
+ suite = "RC4-SHA";
+ break;
+ case TLS_CIPHER_AES128_SHA:
+ suite = "AES128-SHA";
+ break;
+ case TLS_CIPHER_RSA_DHE_AES128_SHA:
+ suite = "DHE-RSA-AES128-SHA";
+ break;
+ case TLS_CIPHER_ANON_DH_AES128_SHA:
+ suite = "ADH-AES128-SHA";
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "TLS: Unsupported "
+ "cipher selection: %d", *c);
+ return -1;
+ }
+ ret = os_snprintf(pos, end - pos, ":%s", suite);
- #ifdef CONFIG_OPENSSL_TICKET_OVERRIDE
++ if (os_snprintf_error(end - pos, ret))
+ break;
+ pos += ret;
+
+ c++;
+ }
+
+ wpa_printf(MSG_DEBUG, "OpenSSL: cipher suites: %s", buf + 1);
+
++#if OPENSSL_VERSION_NUMBER >= 0x10100000L
++#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
++ if (os_strstr(buf, ":ADH-")) {
++ /*
++ * Need to drop to security level 0 to allow anonymous
++ * cipher suites for EAP-FAST.
++ */
++ SSL_set_security_level(conn->ssl, 0);
++ } else if (SSL_get_security_level(conn->ssl) == 0) {
++ /* Force at least security level 1 */
++ SSL_set_security_level(conn->ssl, 1);
++ }
++#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
++#endif
++
+ if (SSL_set_cipher_list(conn->ssl, buf + 1) != 1) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Cipher suite configuration failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+
++int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
++ char *buf, size_t buflen)
++{
++ const char *name;
++ if (conn == NULL || conn->ssl == NULL)
++ return -1;
++
++ name = SSL_get_version(conn->ssl);
++ if (name == NULL)
++ return -1;
++
++ os_strlcpy(buf, name, buflen);
++ return 0;
++}
++
++
+int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen)
+{
+ const char *name;
+ if (conn == NULL || conn->ssl == NULL)
+ return -1;
+
+ name = SSL_get_cipher(conn->ssl);
+ if (name == NULL)
+ return -1;
+
+ os_strlcpy(buf, name, buflen);
+ return 0;
+}
+
+
+int tls_connection_enable_workaround(void *ssl_ctx,
+ struct tls_connection *conn)
+{
+ SSL_set_options(conn->ssl, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
+
+ return 0;
+}
+
+
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+/* ClientHello TLS extensions require a patch to openssl, so this function is
+ * commented out unless explicitly needed for EAP-FAST in order to be able to
+ * build this file with unmodified openssl. */
+int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
+ int ext_type, const u8 *data,
+ size_t data_len)
+{
+ if (conn == NULL || conn->ssl == NULL || ext_type != 35)
+ return -1;
+
- #else /* CONFIG_OPENSSL_TICKET_OVERRIDE */
- if (SSL_set_hello_extension(conn->ssl, ext_type, (void *) data,
- data_len) != 1)
- return -1;
- #endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */
+ if (SSL_set_session_ticket_ext(conn->ssl, (void *) data,
+ data_len) != 1)
+ return -1;
- if (params->engine) {
+
+ return 0;
+}
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+
+
+int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn)
+{
+ if (conn == NULL)
+ return -1;
+ return conn->failed;
+}
+
+
+int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn)
+{
+ if (conn == NULL)
+ return -1;
+ return conn->read_alerts;
+}
+
+
+int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn)
+{
+ if (conn == NULL)
+ return -1;
+ return conn->write_alerts;
+}
+
+
++#ifdef HAVE_OCSP
++
++static void ocsp_debug_print_resp(OCSP_RESPONSE *rsp)
++{
++#ifndef CONFIG_NO_STDOUT_DEBUG
++ BIO *out;
++ size_t rlen;
++ char *txt;
++ int res;
++
++ if (wpa_debug_level > MSG_DEBUG)
++ return;
++
++ out = BIO_new(BIO_s_mem());
++ if (!out)
++ return;
++
++ OCSP_RESPONSE_print(out, rsp, 0);
++ rlen = BIO_ctrl_pending(out);
++ txt = os_malloc(rlen + 1);
++ if (!txt) {
++ BIO_free(out);
++ return;
++ }
++
++ res = BIO_read(out, txt, rlen);
++ if (res > 0) {
++ txt[res] = '\0';
++ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP Response\n%s", txt);
++ }
++ os_free(txt);
++ BIO_free(out);
++#endif /* CONFIG_NO_STDOUT_DEBUG */
++}
++
++
++static void debug_print_cert(X509 *cert, const char *title)
++{
++#ifndef CONFIG_NO_STDOUT_DEBUG
++ BIO *out;
++ size_t rlen;
++ char *txt;
++ int res;
++
++ if (wpa_debug_level > MSG_DEBUG)
++ return;
++
++ out = BIO_new(BIO_s_mem());
++ if (!out)
++ return;
++
++ X509_print(out, cert);
++ rlen = BIO_ctrl_pending(out);
++ txt = os_malloc(rlen + 1);
++ if (!txt) {
++ BIO_free(out);
++ return;
++ }
++
++ res = BIO_read(out, txt, rlen);
++ if (res > 0) {
++ txt[res] = '\0';
++ wpa_printf(MSG_DEBUG, "OpenSSL: %s\n%s", title, txt);
++ }
++ os_free(txt);
++
++ BIO_free(out);
++#endif /* CONFIG_NO_STDOUT_DEBUG */
++}
++
++
++static int ocsp_resp_cb(SSL *s, void *arg)
++{
++ struct tls_connection *conn = arg;
++ const unsigned char *p;
++ int len, status, reason;
++ OCSP_RESPONSE *rsp;
++ OCSP_BASICRESP *basic;
++ OCSP_CERTID *id;
++ ASN1_GENERALIZEDTIME *produced_at, *this_update, *next_update;
++ X509_STORE *store;
++ STACK_OF(X509) *certs = NULL;
++
++ len = SSL_get_tlsext_status_ocsp_resp(s, &p);
++ if (!p) {
++ wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP response received");
++ return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1;
++ }
++
++ wpa_hexdump(MSG_DEBUG, "OpenSSL: OCSP response", p, len);
++
++ rsp = d2i_OCSP_RESPONSE(NULL, &p, len);
++ if (!rsp) {
++ wpa_printf(MSG_INFO, "OpenSSL: Failed to parse OCSP response");
++ return 0;
++ }
++
++ ocsp_debug_print_resp(rsp);
++
++ status = OCSP_response_status(rsp);
++ if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
++ wpa_printf(MSG_INFO, "OpenSSL: OCSP responder error %d (%s)",
++ status, OCSP_response_status_str(status));
++ return 0;
++ }
++
++ basic = OCSP_response_get1_basic(rsp);
++ if (!basic) {
++ wpa_printf(MSG_INFO, "OpenSSL: Could not find BasicOCSPResponse");
++ return 0;
++ }
++
++ store = SSL_CTX_get_cert_store(conn->ssl_ctx);
++ if (conn->peer_issuer) {
++ debug_print_cert(conn->peer_issuer, "Add OCSP issuer");
++
++ if (X509_STORE_add_cert(store, conn->peer_issuer) != 1) {
++ tls_show_errors(MSG_INFO, __func__,
++ "OpenSSL: Could not add issuer to certificate store");
++ }
++ certs = sk_X509_new_null();
++ if (certs) {
++ X509 *cert;
++ cert = X509_dup(conn->peer_issuer);
++ if (cert && !sk_X509_push(certs, cert)) {
++ tls_show_errors(
++ MSG_INFO, __func__,
++ "OpenSSL: Could not add issuer to OCSP responder trust store");
++ X509_free(cert);
++ sk_X509_free(certs);
++ certs = NULL;
++ }
++ if (certs && conn->peer_issuer_issuer) {
++ cert = X509_dup(conn->peer_issuer_issuer);
++ if (cert && !sk_X509_push(certs, cert)) {
++ tls_show_errors(
++ MSG_INFO, __func__,
++ "OpenSSL: Could not add issuer's issuer to OCSP responder trust store");
++ X509_free(cert);
++ }
++ }
++ }
++ }
++
++ status = OCSP_basic_verify(basic, certs, store, OCSP_TRUSTOTHER);
++ sk_X509_pop_free(certs, X509_free);
++ if (status <= 0) {
++ tls_show_errors(MSG_INFO, __func__,
++ "OpenSSL: OCSP response failed verification");
++ OCSP_BASICRESP_free(basic);
++ OCSP_RESPONSE_free(rsp);
++ return 0;
++ }
++
++ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response verification succeeded");
++
++ if (!conn->peer_cert) {
++ wpa_printf(MSG_DEBUG, "OpenSSL: Peer certificate not available for OCSP status check");
++ OCSP_BASICRESP_free(basic);
++ OCSP_RESPONSE_free(rsp);
++ return 0;
++ }
++
++ if (!conn->peer_issuer) {
++ wpa_printf(MSG_DEBUG, "OpenSSL: Peer issuer certificate not available for OCSP status check");
++ OCSP_BASICRESP_free(basic);
++ OCSP_RESPONSE_free(rsp);
++ return 0;
++ }
++
++ id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer);
++ if (!id) {
++ wpa_printf(MSG_DEBUG, "OpenSSL: Could not create OCSP certificate identifier");
++ OCSP_BASICRESP_free(basic);
++ OCSP_RESPONSE_free(rsp);
++ return 0;
++ }
++
++ if (!OCSP_resp_find_status(basic, id, &status, &reason, &produced_at,
++ &this_update, &next_update)) {
++ wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s",
++ (conn->flags & TLS_CONN_REQUIRE_OCSP) ? "" :
++ " (OCSP not required)");
++ OCSP_BASICRESP_free(basic);
++ OCSP_RESPONSE_free(rsp);
++ return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1;
++ }
++
++ if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) {
++ tls_show_errors(MSG_INFO, __func__,
++ "OpenSSL: OCSP status times invalid");
++ OCSP_BASICRESP_free(basic);
++ OCSP_RESPONSE_free(rsp);
++ return 0;
++ }
++
++ OCSP_BASICRESP_free(basic);
++ OCSP_RESPONSE_free(rsp);
++
++ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status for server certificate: %s",
++ OCSP_cert_status_str(status));
++
++ if (status == V_OCSP_CERTSTATUS_GOOD)
++ return 1;
++ if (status == V_OCSP_CERTSTATUS_REVOKED)
++ return 0;
++ if (conn->flags & TLS_CONN_REQUIRE_OCSP) {
++ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP required");
++ return 0;
++ }
++ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP was not required, so allow connection to continue");
++ return 1;
++}
++
++
++static int ocsp_status_cb(SSL *s, void *arg)
++{
++ char *tmp;
++ char *resp;
++ size_t len;
++
++ if (tls_global->ocsp_stapling_response == NULL) {
++ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - no response configured");
++ return SSL_TLSEXT_ERR_OK;
++ }
++
++ resp = os_readfile(tls_global->ocsp_stapling_response, &len);
++ if (resp == NULL) {
++ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - could not read response file");
++ /* TODO: Build OCSPResponse with responseStatus = internalError
++ */
++ return SSL_TLSEXT_ERR_OK;
++ }
++ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - send cached response");
++ tmp = OPENSSL_malloc(len);
++ if (tmp == NULL) {
++ os_free(resp);
++ return SSL_TLSEXT_ERR_ALERT_FATAL;
++ }
++
++ os_memcpy(tmp, resp, len);
++ os_free(resp);
++ SSL_set_tlsext_status_ocsp_resp(s, tmp, len);
++
++ return SSL_TLSEXT_ERR_OK;
++}
++
++#endif /* HAVE_OCSP */
++
++
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+ const struct tls_connection_params *params)
+{
++ struct tls_data *data = tls_ctx;
+ int ret;
+ unsigned long err;
++ int can_pkcs11 = 0;
++ const char *key_id = params->key_id;
++ const char *cert_id = params->cert_id;
++ const char *ca_cert_id = params->ca_cert_id;
++ const char *engine_id = params->engine ? params->engine_id : NULL;
+
+ if (conn == NULL)
+ return -1;
+
++ /*
++ * If the engine isn't explicitly configured, and any of the
++ * cert/key fields are actually PKCS#11 URIs, then automatically
++ * use the PKCS#11 ENGINE.
++ */
++ if (!engine_id || os_strcmp(engine_id, "pkcs11") == 0)
++ can_pkcs11 = 1;
++
++ if (!key_id && params->private_key && can_pkcs11 &&
++ os_strncmp(params->private_key, "pkcs11:", 7) == 0) {
++ can_pkcs11 = 2;
++ key_id = params->private_key;
++ }
++
++ if (!cert_id && params->client_cert && can_pkcs11 &&
++ os_strncmp(params->client_cert, "pkcs11:", 7) == 0) {
++ can_pkcs11 = 2;
++ cert_id = params->client_cert;
++ }
++
++ if (!ca_cert_id && params->ca_cert && can_pkcs11 &&
++ os_strncmp(params->ca_cert, "pkcs11:", 7) == 0) {
++ can_pkcs11 = 2;
++ ca_cert_id = params->ca_cert;
++ }
++
++ /* If we need to automatically enable the PKCS#11 ENGINE, do so. */
++ if (can_pkcs11 == 2 && !engine_id)
++ engine_id = "pkcs11";
++
++#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
++#if OPENSSL_VERSION_NUMBER < 0x10100000L
++ if (params->flags & TLS_CONN_EAP_FAST) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Use TLSv1_method() for EAP-FAST");
++ if (SSL_set_ssl_method(conn->ssl, TLSv1_method()) != 1) {
++ tls_show_errors(MSG_INFO, __func__,
++ "Failed to set TLSv1_method() for EAP-FAST");
++ return -1;
++ }
++ }
++#endif
++#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
++
+ while ((err = ERR_get_error())) {
+ wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s",
+ __func__, ERR_error_string(err, NULL));
+ }
+
- ret = tls_engine_init(conn, params->engine_id, params->pin,
- params->key_id, params->cert_id,
- params->ca_cert_id);
++ if (engine_id) {
+ wpa_printf(MSG_DEBUG, "SSL: Initializing TLS engine");
- params->altsubject_match))
++ ret = tls_engine_init(conn, engine_id, params->pin,
++ key_id, cert_id, ca_cert_id);
+ if (ret)
+ return ret;
+ }
+ if (tls_connection_set_subject_match(conn,
+ params->subject_match,
- if (params->engine && params->ca_cert_id) {
- if (tls_connection_engine_ca_cert(tls_ctx, conn,
- params->ca_cert_id))
++ params->altsubject_match,
++ params->suffix_match,
++ params->domain_match))
+ return -1;
+
- } else if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert,
++ if (engine_id && ca_cert_id) {
++ if (tls_connection_engine_ca_cert(data, conn, ca_cert_id))
+ return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
- if (params->engine && params->cert_id) {
- if (tls_connection_engine_client_cert(conn, params->cert_id))
++ } else if (tls_connection_ca_cert(data, conn, params->ca_cert,
+ params->ca_cert_blob,
+ params->ca_cert_blob_len,
+ params->ca_path))
+ return -1;
+
- if (params->engine && params->key_id) {
++ if (engine_id && cert_id) {
++ if (tls_connection_engine_client_cert(conn, cert_id))
+ return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
+ } else if (tls_connection_client_cert(conn, params->client_cert,
+ params->client_cert_blob,
+ params->client_cert_blob_len))
+ return -1;
+
- } else if (tls_connection_private_key(tls_ctx, conn,
++ if (engine_id && key_id) {
+ wpa_printf(MSG_DEBUG, "TLS: Using private key from engine");
+ if (tls_connection_engine_private_key(conn))
+ return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
- tls_get_errors(tls_ctx);
++ } else if (tls_connection_private_key(data, conn,
+ params->private_key,
+ params->private_key_passwd,
+ params->private_key_blob,
+ params->private_key_blob_len)) {
+ wpa_printf(MSG_INFO, "TLS: Failed to load private key '%s'",
+ params->private_key);
+ return -1;
+ }
+
+ if (tls_connection_dh(conn, params->dh_file)) {
+ wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'",
+ params->dh_file);
+ return -1;
+ }
+
- SSL_CTX *ssl_ctx = tls_ctx;
++ if (params->openssl_ciphers &&
++ SSL_set_cipher_list(conn->ssl, params->openssl_ciphers) != 1) {
++ wpa_printf(MSG_INFO,
++ "OpenSSL: Failed to set cipher string '%s'",
++ params->openssl_ciphers);
++ return -1;
++ }
++
++ tls_set_conn_flags(conn->ssl, params->flags);
++
++#ifdef HAVE_OCSP
++ if (params->flags & TLS_CONN_REQUEST_OCSP) {
++ SSL_CTX *ssl_ctx = data->ssl;
++ SSL_set_tlsext_status_type(conn->ssl, TLSEXT_STATUSTYPE_ocsp);
++ SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_resp_cb);
++ SSL_CTX_set_tlsext_status_arg(ssl_ctx, conn);
++ }
++#else /* HAVE_OCSP */
++ if (params->flags & TLS_CONN_REQUIRE_OCSP) {
++ wpa_printf(MSG_INFO,
++ "OpenSSL: No OCSP support included - reject configuration");
++ return -1;
++ }
++ if (params->flags & TLS_CONN_REQUEST_OCSP) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: No OCSP support included - allow optional OCSP case to continue");
++ }
++#endif /* HAVE_OCSP */
++
++ conn->flags = params->flags;
++
++ tls_get_errors(data);
+
+ return 0;
+}
+
+
+int tls_global_set_params(void *tls_ctx,
+ const struct tls_connection_params *params)
+{
- if (tls_global_ca_cert(ssl_ctx, params->ca_cert))
- return -1;
-
- if (tls_global_client_cert(ssl_ctx, params->client_cert))
- return -1;
-
- if (tls_global_private_key(ssl_ctx, params->private_key,
- params->private_key_passwd))
- return -1;
-
- if (tls_global_dh(ssl_ctx, params->dh_file)) {
- wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'",
- params->dh_file);
++ struct tls_data *data = tls_ctx;
++ SSL_CTX *ssl_ctx = data->ssl;
+ unsigned long err;
+
+ while ((err = ERR_get_error())) {
+ wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s",
+ __func__, ERR_error_string(err, NULL));
+ }
+
- return 0;
- }
-
-
- int tls_connection_get_keyblock_size(void *tls_ctx,
- struct tls_connection *conn)
- {
- const EVP_CIPHER *c;
- const EVP_MD *h;
-
- if (conn == NULL || conn->ssl == NULL ||
- conn->ssl->enc_read_ctx == NULL ||
- conn->ssl->enc_read_ctx->cipher == NULL ||
- conn->ssl->read_hash == NULL)
++ if (tls_global_ca_cert(data, params->ca_cert) ||
++ tls_global_client_cert(data, params->client_cert) ||
++ tls_global_private_key(data, params->private_key,
++ params->private_key_passwd) ||
++ tls_global_dh(data, params->dh_file)) {
++ wpa_printf(MSG_INFO, "TLS: Failed to set global parameters");
+ return -1;
+ }
+
- c = conn->ssl->enc_read_ctx->cipher;
- #if OPENSSL_VERSION_NUMBER >= 0x00909000L
- h = EVP_MD_CTX_md(conn->ssl->read_hash);
- #else
- h = conn->ssl->read_hash;
- #endif
-
- return 2 * (EVP_CIPHER_key_length(c) +
- EVP_MD_size(h) +
- EVP_CIPHER_iv_length(c));
- }
-
++ if (params->openssl_ciphers &&
++ SSL_CTX_set_cipher_list(ssl_ctx, params->openssl_ciphers) != 1) {
++ wpa_printf(MSG_INFO,
++ "OpenSSL: Failed to set cipher string '%s'",
++ params->openssl_ciphers);
+ return -1;
++ }
+
- unsigned int tls_capabilities(void *tls_ctx)
- {
++#ifdef SSL_OP_NO_TICKET
++ if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET)
++ SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET);
++#ifdef SSL_CTX_clear_options
++ else
++ SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TICKET);
++#endif /* SSL_clear_options */
++#endif /* SSL_OP_NO_TICKET */
++
++#ifdef HAVE_OCSP
++ SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_status_cb);
++ SSL_CTX_set_tlsext_status_arg(ssl_ctx, ssl_ctx);
++ os_free(tls_global->ocsp_stapling_response);
++ if (params->ocsp_stapling_response)
++ tls_global->ocsp_stapling_response =
++ os_strdup(params->ocsp_stapling_response);
++ else
++ tls_global->ocsp_stapling_response = NULL;
++#endif /* HAVE_OCSP */
+
- int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
- int tls_ia)
- {
- return -1;
- }
-
-
- struct wpabuf * tls_connection_ia_send_phase_finished(
- void *tls_ctx, struct tls_connection *conn, int final)
- {
- return NULL;
- }
-
-
- int tls_connection_ia_final_phase_finished(void *tls_ctx,
- struct tls_connection *conn)
- {
- return -1;
- }
-
-
- int tls_connection_ia_permute_inner_secret(void *tls_ctx,
- struct tls_connection *conn,
- const u8 *key, size_t key_len)
- {
- return -1;
- }
-
-
+ return 0;
+}
+
+
- #ifdef CONFIG_OPENSSL_TICKET_OVERRIDE
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+/* Pre-shared secred requires a patch to openssl, so this function is
+ * commented out unless explicitly needed for EAP-FAST in order to be able to
+ * build this file with unmodified openssl. */
+
++#ifdef OPENSSL_IS_BORINGSSL
++static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len,
++ STACK_OF(SSL_CIPHER) *peer_ciphers,
++ const SSL_CIPHER **cipher, void *arg)
++#else /* OPENSSL_IS_BORINGSSL */
+static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len,
+ STACK_OF(SSL_CIPHER) *peer_ciphers,
+ SSL_CIPHER **cipher, void *arg)
++#endif /* OPENSSL_IS_BORINGSSL */
+{
+ struct tls_connection *conn = arg;
+ int ret;
+
++#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ if (conn == NULL || conn->session_ticket_cb == NULL)
+ return 0;
+
+ ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx,
+ conn->session_ticket,
+ conn->session_ticket_len,
+ s->s3->client_random,
+ s->s3->server_random, secret);
++#else
++ unsigned char client_random[SSL3_RANDOM_SIZE];
++ unsigned char server_random[SSL3_RANDOM_SIZE];
++
++ if (conn == NULL || conn->session_ticket_cb == NULL)
++ return 0;
++
++ SSL_get_client_random(s, client_random, sizeof(client_random));
++ SSL_get_server_random(s, server_random, sizeof(server_random));
++
++ ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx,
++ conn->session_ticket,
++ conn->session_ticket_len,
++ client_random,
++ server_random, secret);
++#endif
++
+ os_free(conn->session_ticket);
+ conn->session_ticket = NULL;
+
+ if (ret <= 0)
+ return 0;
+
+ *secret_len = SSL_MAX_MASTER_KEY_LENGTH;
+ return 1;
+}
+
+
- #else /* CONFIG_OPENSSL_TICKET_OVERRIDE */
- #ifdef SSL_OP_NO_TICKET
- static void tls_hello_ext_cb(SSL *s, int client_server, int type,
- unsigned char *data, int len, void *arg)
- {
- struct tls_connection *conn = arg;
-
- if (conn == NULL || conn->session_ticket_cb == NULL)
- return;
-
- wpa_printf(MSG_DEBUG, "OpenSSL: %s: type=%d length=%d", __func__,
- type, len);
-
- if (type == TLSEXT_TYPE_session_ticket && !client_server) {
- os_free(conn->session_ticket);
- conn->session_ticket = NULL;
-
- wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket "
- "extension", data, len);
- conn->session_ticket = os_malloc(len);
- if (conn->session_ticket == NULL)
- return;
-
- os_memcpy(conn->session_ticket, data, len);
- conn->session_ticket_len = len;
- }
- }
- #else /* SSL_OP_NO_TICKET */
- static int tls_hello_ext_cb(SSL *s, TLS_EXTENSION *ext, void *arg)
- {
- struct tls_connection *conn = arg;
-
- if (conn == NULL || conn->session_ticket_cb == NULL)
- return 0;
-
- wpa_printf(MSG_DEBUG, "OpenSSL: %s: type=%d length=%d", __func__,
- ext->type, ext->length);
-
- os_free(conn->session_ticket);
- conn->session_ticket = NULL;
-
- if (ext->type == 35) {
- wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket "
- "extension", ext->data, ext->length);
- conn->session_ticket = os_malloc(ext->length);
- if (conn->session_ticket == NULL)
- return SSL_AD_INTERNAL_ERROR;
-
- os_memcpy(conn->session_ticket, ext->data, ext->length);
- conn->session_ticket_len = ext->length;
- }
-
- return 0;
- }
- #endif /* SSL_OP_NO_TICKET */
- #endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */
+static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data,
+ int len, void *arg)
+{
+ struct tls_connection *conn = arg;
+
+ if (conn == NULL || conn->session_ticket_cb == NULL)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s: length=%d", __func__, len);
+
+ os_free(conn->session_ticket);
+ conn->session_ticket = NULL;
+
+ wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket "
+ "extension", data, len);
+
+ conn->session_ticket = os_malloc(len);
+ if (conn->session_ticket == NULL)
+ return 0;
+
+ os_memcpy(conn->session_ticket, data, len);
+ conn->session_ticket_len = len;
+
+ return 1;
+}
- #ifdef CONFIG_OPENSSL_TICKET_OVERRIDE
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+
+
+int tls_connection_set_session_ticket_cb(void *tls_ctx,
+ struct tls_connection *conn,
+ tls_session_ticket_cb cb,
+ void *ctx)
+{
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+ conn->session_ticket_cb = cb;
+ conn->session_ticket_cb_ctx = ctx;
+
+ if (cb) {
+ if (SSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb,
+ conn) != 1)
+ return -1;
- #else /* CONFIG_OPENSSL_TICKET_OVERRIDE */
- #ifdef SSL_OP_NO_TICKET
- SSL_set_tlsext_debug_callback(conn->ssl, tls_hello_ext_cb);
- SSL_set_tlsext_debug_arg(conn->ssl, conn);
- #else /* SSL_OP_NO_TICKET */
- if (SSL_set_hello_extension_cb(conn->ssl, tls_hello_ext_cb,
- conn) != 1)
- return -1;
- #endif /* SSL_OP_NO_TICKET */
- #endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */
+ SSL_set_session_ticket_ext_cb(conn->ssl,
+ tls_session_ticket_ext_cb, conn);
- #ifdef CONFIG_OPENSSL_TICKET_OVERRIDE
+ } else {
+ if (SSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1)
+ return -1;
- #else /* CONFIG_OPENSSL_TICKET_OVERRIDE */
- #ifdef SSL_OP_NO_TICKET
- SSL_set_tlsext_debug_callback(conn->ssl, NULL);
- SSL_set_tlsext_debug_arg(conn->ssl, conn);
- #else /* SSL_OP_NO_TICKET */
- if (SSL_set_hello_extension_cb(conn->ssl, NULL, NULL) != 1)
- return -1;
- #endif /* SSL_OP_NO_TICKET */
- #endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */
+ SSL_set_session_ticket_ext_cb(conn->ssl, NULL, NULL);
+ }
+
+ return 0;
+#else /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+ return -1;
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+}
++
++
++int tls_get_library_version(char *buf, size_t buf_len)
++{
++ return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s",
++ OPENSSL_VERSION_TEXT,
++ SSLeay_version(SSLEAY_VERSION));
++}
++
++
++void tls_connection_set_success_data(struct tls_connection *conn,
++ struct wpabuf *data)
++{
++ SSL_SESSION *sess;
++ struct wpabuf *old;
++
++ if (tls_ex_idx_session < 0)
++ goto fail;
++ sess = SSL_get_session(conn->ssl);
++ if (!sess)
++ goto fail;
++ old = SSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
++ if (old) {
++ wpa_printf(MSG_DEBUG, "OpenSSL: Replacing old success data %p",
++ old);
++ wpabuf_free(old);
++ }
++ if (SSL_SESSION_set_ex_data(sess, tls_ex_idx_session, data) != 1)
++ goto fail;
++
++ wpa_printf(MSG_DEBUG, "OpenSSL: Stored success data %p", data);
++ conn->success_data = 1;
++ return;
++
++fail:
++ wpa_printf(MSG_INFO, "OpenSSL: Failed to store success data");
++ wpabuf_free(data);
++}
++
++
++void tls_connection_set_success_data_resumed(struct tls_connection *conn)
++{
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Success data accepted for resumed session");
++ conn->success_data = 1;
++}
++
++
++const struct wpabuf *
++tls_connection_get_success_data(struct tls_connection *conn)
++{
++ SSL_SESSION *sess;
++
++ if (tls_ex_idx_session < 0 ||
++ !(sess = SSL_get_session(conn->ssl)))
++ return NULL;
++ return SSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
++}
++
++
++void tls_connection_remove_session(struct tls_connection *conn)
++{
++ SSL_SESSION *sess;
++
++ sess = SSL_get_session(conn->ssl);
++ if (!sess)
++ return;
++
++ if (SSL_CTX_remove_session(conn->ssl_ctx, sess) != 1)
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Session was not cached");
++ else
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Removed cached session to disable session resumption");
++}
--- /dev/null
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+/*
+ * EAP common peer/server definitions
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
++ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
+ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
+ */
+
+#ifndef EAP_COMMON_H
+#define EAP_COMMON_H
+
+#include "wpabuf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
++
++struct erp_tlvs {
++ const u8 *keyname;
++ const u8 *domain;
++ u8 keyname_len;
++ u8 domain_len;
++};
++
++int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload);
+const u8 * eap_hdr_validate(int vendor, EapType eap_type,
+ const struct wpabuf *msg, size_t *plen);
+struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len,
+ u8 code, u8 identifier);
+void eap_update_len(struct wpabuf *msg);
+u8 eap_get_id(const struct wpabuf *msg);
+EapType eap_get_type(const struct wpabuf *msg);
++int erp_parse_tlvs(const u8 *pos, const u8 *end, struct erp_tlvs *tlvs,
++ int stop_at_keyname);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EAP_COMMON_H */
--- /dev/null
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+/*
+ * EAP server/peer: Shared EAP definitions
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
++ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
+ *
- EAP_CODE_FAILURE = 4 };
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
+ */
+
+#ifndef EAP_DEFS_H
+#define EAP_DEFS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* RFC 3748 - Extensible Authentication Protocol (EAP) */
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_hdr {
+ u8 code;
+ u8 identifier;
+ be16 length; /* including code and identifier; network byte order */
+ /* followed by length-4 octets of data */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+enum { EAP_CODE_REQUEST = 1, EAP_CODE_RESPONSE = 2, EAP_CODE_SUCCESS = 3,
- EAP_TYPE_AKA_PRIME = 50 /* draft-arkko-eap-aka-kdf-10.txt */,
++ EAP_CODE_FAILURE = 4, EAP_CODE_INITIATE = 5, EAP_CODE_FINISH = 6 };
+
+/* EAP Request and Response data begins with one octet Type. Success and
+ * Failure do not have additional data. */
+
++/* Type field in EAP-Initiate and EAP-Finish messages */
++enum eap_erp_type {
++ EAP_ERP_TYPE_REAUTH_START = 1,
++ EAP_ERP_TYPE_REAUTH = 2,
++};
++
++/* ERP TV/TLV types */
++enum eap_erp_tlv_type {
++ EAP_ERP_TLV_KEYNAME_NAI = 1,
++ EAP_ERP_TV_RRK_LIFETIME = 2,
++ EAP_ERP_TV_RMSK_LIFETIME = 3,
++ EAP_ERP_TLV_DOMAIN_NAME = 4,
++ EAP_ERP_TLV_CRYPTOSUITES = 5,
++ EAP_ERP_TLV_AUTHORIZATION_INDICATION = 6,
++ EAP_ERP_TLV_CALLED_STATION_ID = 128,
++ EAP_ERP_TLV_CALLING_STATION_ID = 129,
++ EAP_ERP_TLV_NAS_IDENTIFIER = 130,
++ EAP_ERP_TLV_NAS_IP_ADDRESS = 131,
++ EAP_ERP_TLV_NAS_IPV6_ADDRESS = 132,
++};
++
++/* ERP Cryptosuite */
++enum eap_erp_cryptosuite {
++ EAP_ERP_CS_HMAC_SHA256_64 = 1,
++ EAP_ERP_CS_HMAC_SHA256_128 = 2,
++ EAP_ERP_CS_HMAC_SHA256_256 = 3,
++};
++
+/*
+ * EAP Method Types as allocated by IANA:
+ * http://www.iana.org/assignments/eap-numbers
+ */
+typedef enum {
+ EAP_TYPE_NONE = 0,
+ EAP_TYPE_IDENTITY = 1 /* RFC 3748 */,
+ EAP_TYPE_NOTIFICATION = 2 /* RFC 3748 */,
+ EAP_TYPE_NAK = 3 /* Response only, RFC 3748 */,
+ EAP_TYPE_MD5 = 4, /* RFC 3748 */
+ EAP_TYPE_OTP = 5 /* RFC 3748 */,
+ EAP_TYPE_GTC = 6, /* RFC 3748 */
+ EAP_TYPE_TLS = 13 /* RFC 2716 */,
+ EAP_TYPE_LEAP = 17 /* Cisco proprietary */,
+ EAP_TYPE_SIM = 18 /* RFC 4186 */,
+ EAP_TYPE_TTLS = 21 /* RFC 5281 */,
+ EAP_TYPE_AKA = 23 /* RFC 4187 */,
+ EAP_TYPE_PEAP = 25 /* draft-josefsson-pppext-eap-tls-eap-06.txt */,
+ EAP_TYPE_MSCHAPV2 = 26 /* draft-kamath-pppext-eap-mschapv2-00.txt */,
+ EAP_TYPE_TLV = 33 /* draft-josefsson-pppext-eap-tls-eap-07.txt */,
+ EAP_TYPE_TNC = 38 /* TNC IF-T v1.0-r3; note: tentative assignment;
+ * type 38 has previously been allocated for
+ * EAP-HTTP Digest, (funk.com) */,
+ EAP_TYPE_FAST = 43 /* RFC 4851 */,
+ EAP_TYPE_PAX = 46 /* RFC 4746 */,
+ EAP_TYPE_PSK = 47 /* RFC 4764 */,
+ EAP_TYPE_SAKE = 48 /* RFC 4763 */,
+ EAP_TYPE_IKEV2 = 49 /* RFC 5106 */,
- EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance */
++ EAP_TYPE_AKA_PRIME = 50 /* RFC 5448 */,
+ EAP_TYPE_GPSK = 51 /* RFC 5433 */,
+ EAP_TYPE_PWD = 52 /* RFC 5931 */,
++ EAP_TYPE_EKE = 53 /* RFC 6124 */,
+ EAP_TYPE_EXPANDED = 254 /* RFC 3748 */
+} EapType;
+
+
+/* SMI Network Management Private Enterprise Code for vendor specific types */
+enum {
+ EAP_VENDOR_IETF = 0,
+ EAP_VENDOR_MICROSOFT = 0x000137 /* Microsoft */,
++ EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance (moved to WBA) */,
++ EAP_VENDOR_HOSTAP = 39068 /* hostapd/wpa_supplicant project */,
++ EAP_VENDOR_WFA_NEW = 40808 /* Wi-Fi Alliance */
+};
+
++#define EAP_VENDOR_UNAUTH_TLS EAP_VENDOR_HOSTAP
++#define EAP_VENDOR_TYPE_UNAUTH_TLS 1
++
++#define EAP_VENDOR_WFA_UNAUTH_TLS 13
++
+#define EAP_MSK_LEN 64
+#define EAP_EMSK_LEN 64
++#define EAP_EMSK_NAME_LEN 8
++#define ERP_MAX_KEY_LEN 64
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EAP_DEFS_H */
--- /dev/null
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+/*
+ * EAP-FAST definitions (RFC 4851)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
- int tlv_type, u8 *pos, int len);
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
+ */
+
+#ifndef EAP_FAST_H
+#define EAP_FAST_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EAP_FAST_VERSION 1
+#define EAP_FAST_KEY_LEN 64
+#define EAP_FAST_SIMCK_LEN 40
+#define EAP_FAST_SKS_LEN 40
+#define EAP_FAST_CMK_LEN 20
+
+#define TLS_EXT_PAC_OPAQUE 35
+
+/*
+ * RFC 5422: Section 4.2.1 - Formats for PAC TLV Attributes / Type Field
+ * Note: bit 0x8000 (Mandatory) and bit 0x4000 (Reserved) are also defined
+ * in the general PAC TLV format (Section 4.2).
+ */
+#define PAC_TYPE_PAC_KEY 1
+#define PAC_TYPE_PAC_OPAQUE 2
+#define PAC_TYPE_CRED_LIFETIME 3
+#define PAC_TYPE_A_ID 4
+#define PAC_TYPE_I_ID 5
+/*
+ * 6 was previous assigned for SERVER_PROTECTED_DATA, but
+ * draft-cam-winget-eap-fast-provisioning-02.txt changed this to Reserved.
+ */
+#define PAC_TYPE_A_ID_INFO 7
+#define PAC_TYPE_PAC_ACKNOWLEDGEMENT 8
+#define PAC_TYPE_PAC_INFO 9
+#define PAC_TYPE_PAC_TYPE 10
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct pac_tlv_hdr {
+ be16 type;
+ be16 len;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+#define EAP_FAST_PAC_KEY_LEN 32
+
+/* RFC 5422: 4.2.6 PAC-Type TLV */
+#define PAC_TYPE_TUNNEL_PAC 1
+/* Application Specific Short Lived PACs (only in volatile storage) */
+/* User Authorization PAC */
+#define PAC_TYPE_USER_AUTHORIZATION 3
+/* Application Specific Long Lived PACs */
+/* Machine Authentication PAC */
+#define PAC_TYPE_MACHINE_AUTHENTICATION 2
+
+
+/*
+ * RFC 5422:
+ * Section 3.3 - Key Derivations Used in the EAP-FAST Provisioning Exchange
+ */
+struct eap_fast_key_block_provisioning {
+ /* Extra key material after TLS key_block */
+ u8 session_key_seed[EAP_FAST_SKS_LEN];
+ u8 server_challenge[16]; /* MSCHAPv2 ServerChallenge */
+ u8 client_challenge[16]; /* MSCHAPv2 ClientChallenge */
+};
+
+
+struct wpabuf;
+struct tls_connection;
+
+struct eap_fast_tlv_parse {
+ u8 *eap_payload_tlv;
+ size_t eap_payload_tlv_len;
+ struct eap_tlv_crypto_binding_tlv *crypto_binding;
+ size_t crypto_binding_len;
+ int iresult;
+ int result;
+ int request_action;
+ u8 *pac;
+ size_t pac_len;
+};
+
+void eap_fast_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len);
+void eap_fast_put_tlv(struct wpabuf *buf, u16 type, const void *data,
+ u16 len);
+void eap_fast_put_tlv_buf(struct wpabuf *buf, u16 type,
+ const struct wpabuf *data);
+struct wpabuf * eap_fast_tlv_eap_payload(struct wpabuf *buf);
+void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random,
+ const u8 *client_random, u8 *master_secret);
+u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn,
+ const char *label, size_t len);
+void eap_fast_derive_eap_msk(const u8 *simck, u8 *msk);
+void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk);
+int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv,
++ int tlv_type, u8 *pos, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EAP_FAST_H */
--- /dev/null
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+/*
+ * EAP server/peer: EAP-GPSK shared routines
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
+ */
+
+#ifndef EAP_GPSK_COMMON_H
+#define EAP_GPSK_COMMON_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EAP_GPSK_OPCODE_GPSK_1 1
+#define EAP_GPSK_OPCODE_GPSK_2 2
+#define EAP_GPSK_OPCODE_GPSK_3 3
+#define EAP_GPSK_OPCODE_GPSK_4 4
+#define EAP_GPSK_OPCODE_FAIL 5
+#define EAP_GPSK_OPCODE_PROTECTED_FAIL 6
+
+/* Failure-Code in GPSK-Fail and GPSK-Protected-Fail */
+#define EAP_GPSK_FAIL_PSK_NOT_FOUND 0x00000001
+#define EAP_GPSK_FAIL_AUTHENTICATION_FAILURE 0x00000002
+#define EAP_GPSK_FAIL_AUTHORIZATION_FAILURE 0x00000003
+
+#define EAP_GPSK_RAND_LEN 32
+#define EAP_GPSK_MAX_SK_LEN 32
+#define EAP_GPSK_MAX_PK_LEN 32
+#define EAP_GPSK_MAX_MIC_LEN 32
+
+#define EAP_GPSK_VENDOR_IETF 0x00000000
+#define EAP_GPSK_CIPHER_RESERVED 0x000000
+#define EAP_GPSK_CIPHER_AES 0x000001
+#define EAP_GPSK_CIPHER_SHA256 0x000002
+
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_gpsk_csuite {
+ u8 vendor[4];
+ u8 specifier[2];
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+int eap_gpsk_supported_ciphersuite(int vendor, int specifier);
+int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor,
+ int specifier,
+ const u8 *rand_client, const u8 *rand_server,
+ const u8 *id_client, size_t id_client_len,
+ const u8 *id_server, size_t id_server_len,
+ u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
+ u8 *pk, size_t *pk_len);
++int eap_gpsk_derive_session_id(const u8 *psk, size_t psk_len, int vendor,
++ int specifier,
++ const u8 *rand_peer, const u8 *rand_server,
++ const u8 *id_peer, size_t id_peer_len,
++ const u8 *id_server, size_t id_server_len,
++ u8 method_type, u8 *sid, size_t *sid_len);
+size_t eap_gpsk_mic_len(int vendor, int specifier);
+int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor,
+ int specifier, const u8 *data, size_t len, u8 *mic);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EAP_GPSK_COMMON_H */
--- /dev/null
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+/*
+ * EAP-IKEv2 definitions
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
- #ifdef CCNS_PL
- /* incorrect bit order */
- #define IKEV2_FLAGS_LENGTH_INCLUDED 0x01
- #define IKEV2_FLAGS_MORE_FRAGMENTS 0x02
- #define IKEV2_FLAGS_ICV_INCLUDED 0x04
- #else /* CCNS_PL */
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
+ */
+
+#ifndef EAP_IKEV2_COMMON_H
+#define EAP_IKEV2_COMMON_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
- #endif /* CCNS_PL */
+#define IKEV2_FLAGS_LENGTH_INCLUDED 0x80
+#define IKEV2_FLAGS_MORE_FRAGMENTS 0x40
+#define IKEV2_FLAGS_ICV_INCLUDED 0x20
+
+#define IKEV2_FRAGMENT_SIZE 1400
+
+struct ikev2_keys;
+
+int eap_ikev2_derive_keymat(int prf, struct ikev2_keys *keys,
+ const u8 *i_nonce, size_t i_nonce_len,
+ const u8 *r_nonce, size_t r_nonce_len,
+ u8 *keymat);
+struct wpabuf * eap_ikev2_build_frag_ack(u8 id, u8 code);
+int eap_ikev2_validate_icv(int integ_alg, struct ikev2_keys *keys,
+ int initiator, const struct wpabuf *msg,
+ const u8 *pos, const u8 *end);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EAP_IKEV2_COMMON_H */
--- /dev/null
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+/*
+ * EAP server/peer: EAP-PAX shared routines
+ * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
+ *
- u8 *mk, u8 *ck, u8 *ick);
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
+ */
+
+#ifndef EAP_PAX_COMMON_H
+#define EAP_PAX_COMMON_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_pax_hdr {
+ u8 op_code;
+ u8 flags;
+ u8 mac_id;
+ u8 dh_group_id;
+ u8 public_key_id;
+ /* Followed by variable length payload and ICV */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+/* op_code: */
+enum {
+ EAP_PAX_OP_STD_1 = 0x01,
+ EAP_PAX_OP_STD_2 = 0x02,
+ EAP_PAX_OP_STD_3 = 0x03,
+ EAP_PAX_OP_SEC_1 = 0x11,
+ EAP_PAX_OP_SEC_2 = 0x12,
+ EAP_PAX_OP_SEC_3 = 0x13,
+ EAP_PAX_OP_SEC_4 = 0x14,
+ EAP_PAX_OP_SEC_5 = 0x15,
+ EAP_PAX_OP_ACK = 0x21
+};
+
+/* flags: */
+#define EAP_PAX_FLAGS_MF 0x01
+#define EAP_PAX_FLAGS_CE 0x02
+#define EAP_PAX_FLAGS_AI 0x04
+
+/* mac_id: */
+#define EAP_PAX_MAC_HMAC_SHA1_128 0x01
+#define EAP_PAX_HMAC_SHA256_128 0x02
+
+/* dh_group_id: */
+#define EAP_PAX_DH_GROUP_NONE 0x00
+#define EAP_PAX_DH_GROUP_2048_MODP 0x01
+#define EAP_PAX_DH_GROUP_3072_MODP 0x02
+#define EAP_PAX_DH_GROUP_NIST_ECC_P_256 0x03
+
+/* public_key_id: */
+#define EAP_PAX_PUBLIC_KEY_NONE 0x00
+#define EAP_PAX_PUBLIC_KEY_RSAES_OAEP 0x01
+#define EAP_PAX_PUBLIC_KEY_RSA_PKCS1_V1_5 0x02
+#define EAP_PAX_PUBLIC_KEY_EL_GAMAL_NIST_ECC 0x03
+
+/* ADE type: */
+#define EAP_PAX_ADE_VENDOR_SPECIFIC 0x01
+#define EAP_PAX_ADE_CLIENT_CHANNEL_BINDING 0x02
+#define EAP_PAX_ADE_SERVER_CHANNEL_BINDING 0x03
+
+
+#define EAP_PAX_RAND_LEN 32
+#define EAP_PAX_MAC_LEN 16
+#define EAP_PAX_ICV_LEN 16
+#define EAP_PAX_AK_LEN 16
+#define EAP_PAX_MK_LEN 16
+#define EAP_PAX_CK_LEN 16
+#define EAP_PAX_ICK_LEN 16
++#define EAP_PAX_MID_LEN 16
+
+
+int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len,
+ const char *identifier,
+ const u8 *entropy, size_t entropy_len,
+ size_t output_len, u8 *output);
+int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len,
+ const u8 *data1, size_t data1_len,
+ const u8 *data2, size_t data2_len,
+ const u8 *data3, size_t data3_len,
+ u8 *mac);
+int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e,
++ u8 *mk, u8 *ck, u8 *ick, u8 *mid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EAP_PAX_COMMON_H */
--- /dev/null
- * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+/*
+ * EAP-PEAP common routines
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
++ * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>
+ *
- void peap_prfplus(int version, const u8 *key, size_t key_len,
- const char *label, const u8 *seed, size_t seed_len,
- u8 *buf, size_t buf_len);
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
+ */
+
+#ifndef EAP_PEAP_COMMON_H
+#define EAP_PEAP_COMMON_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
++int peap_prfplus(int version, const u8 *key, size_t key_len,
++ const char *label, const u8 *seed, size_t seed_len,
++ u8 *buf, size_t buf_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EAP_PEAP_COMMON_H */
--- /dev/null
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+/*
+ * EAP server/peer: EAP-PSK shared routines
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
+ */
+
+#ifndef EAP_PSK_COMMON_H
+#define EAP_PSK_COMMON_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EAP_PSK_RAND_LEN 16
+#define EAP_PSK_MAC_LEN 16
+#define EAP_PSK_TEK_LEN 16
+#define EAP_PSK_PSK_LEN 16
+#define EAP_PSK_AK_LEN 16
+#define EAP_PSK_KDK_LEN 16
+
+#define EAP_PSK_R_FLAG_CONT 1
+#define EAP_PSK_R_FLAG_DONE_SUCCESS 2
+#define EAP_PSK_R_FLAG_DONE_FAILURE 3
+#define EAP_PSK_E_FLAG 0x20
+
+#define EAP_PSK_FLAGS_GET_T(flags) (((flags) & 0xc0) >> 6)
+#define EAP_PSK_FLAGS_SET_T(t) ((u8) (t) << 6)
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+/* EAP-PSK First Message (AS -> Supplicant) */
+struct eap_psk_hdr_1 {
+ u8 flags;
+ u8 rand_s[EAP_PSK_RAND_LEN];
+ /* Followed by variable length ID_S */
+} STRUCT_PACKED;
+
+/* EAP-PSK Second Message (Supplicant -> AS) */
+struct eap_psk_hdr_2 {
+ u8 flags;
+ u8 rand_s[EAP_PSK_RAND_LEN];
+ u8 rand_p[EAP_PSK_RAND_LEN];
+ u8 mac_p[EAP_PSK_MAC_LEN];
+ /* Followed by variable length ID_P */
+} STRUCT_PACKED;
+
+/* EAP-PSK Third Message (AS -> Supplicant) */
+struct eap_psk_hdr_3 {
+ u8 flags;
+ u8 rand_s[EAP_PSK_RAND_LEN];
+ u8 mac_s[EAP_PSK_MAC_LEN];
+ /* Followed by variable length PCHANNEL */
+} STRUCT_PACKED;
+
+/* EAP-PSK Fourth Message (Supplicant -> AS) */
+struct eap_psk_hdr_4 {
+ u8 flags;
+ u8 rand_s[EAP_PSK_RAND_LEN];
+ /* Followed by variable length PCHANNEL */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+int __must_check eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk);
+int __must_check eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek,
+ u8 *msk, u8 *emsk);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EAP_PSK_COMMON_H */
--- /dev/null
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the BSD license.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License version 2 as published by the Free Software
- * Foundation.
- *
- * See README and COPYING for more details.
+/*
+ * EAP server/peer: EAP-pwd shared definitions
+ * Copyright (c) 2009, Dan Harkins <dharkins@lounge.org>
+ *
- #include <openssl/sha.h>
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
+ */
+
+#ifndef EAP_PWD_COMMON_H
+#define EAP_PWD_COMMON_H
+
+#include <openssl/bn.h>
- #include <openssl/hmac.h>
+#include <openssl/ec.h>
+#include <openssl/evp.h>
- struct eap_pwd_hdr {
- u8 l_bit:1;
- u8 m_bit:1;
- u8 exch:6;
- u8 total_length[0]; /* included when l_bit is set */
- } STRUCT_PACKED;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * definition of a finite cyclic group
+ * TODO: support one based on a prime field
+ */
+typedef struct group_definition_ {
+ u16 group_num;
+ EC_GROUP *group;
+ EC_POINT *pwe;
+ BIGNUM *order;
+ BIGNUM *prime;
+} EAP_PWD_group;
+
+/*
+ * EAP-pwd header, included on all payloads
++ * L(1 bit) | M(1 bit) | exch(6 bits) | total_length(if L is set)
+ */
- #define EAP_PWD_GET_LENGTH_BIT(x) ((x)->lm_exch & 0x80)
- #define EAP_PWD_SET_LENGTH_BIT(x) ((x)->lm_exch |= 0x80)
- #define EAP_PWD_GET_MORE_BIT(x) ((x)->lm_exch & 0x40)
- #define EAP_PWD_SET_MORE_BIT(x) ((x)->lm_exch |= 0x40)
- #define EAP_PWD_GET_EXCHANGE(x) ((x)->lm_exch & 0x3f)
- #define EAP_PWD_SET_EXCHANGE(x,y) ((x)->lm_exch |= (y))
++#define EAP_PWD_HDR_SIZE 1
+
+#define EAP_PWD_OPCODE_ID_EXCH 1
+#define EAP_PWD_OPCODE_COMMIT_EXCH 2
+#define EAP_PWD_OPCODE_CONFIRM_EXCH 3
- int compute_password_element(EAP_PWD_group *, u16, u8 *, int, u8 *, int, u8 *,
- int, u8 *);
- int compute_keys(EAP_PWD_group *, BN_CTX *, BIGNUM *, EC_POINT *, EC_POINT *,
- BIGNUM *, BIGNUM *, u32 *, u8 *, u8 *);
- void H_Init(HMAC_CTX *);
- void H_Update(HMAC_CTX *, const u8 *, int);
- void H_Final(HMAC_CTX *, u8 *);
++#define EAP_PWD_GET_LENGTH_BIT(x) ((x) & 0x80)
++#define EAP_PWD_SET_LENGTH_BIT(x) ((x) |= 0x80)
++#define EAP_PWD_GET_MORE_BIT(x) ((x) & 0x40)
++#define EAP_PWD_SET_MORE_BIT(x) ((x) |= 0x40)
++#define EAP_PWD_GET_EXCHANGE(x) ((x) & 0x3f)
++#define EAP_PWD_SET_EXCHANGE(x,y) ((x) |= (y))
+
+/* EAP-pwd-ID payload */
+struct eap_pwd_id {
+ be16 group_num;
+ u8 random_function;
+#define EAP_PWD_DEFAULT_RAND_FUNC 1
+ u8 prf;
+#define EAP_PWD_DEFAULT_PRF 1
+ u8 token[4];
+ u8 prep;
+#define EAP_PWD_PREP_NONE 0
+#define EAP_PWD_PREP_MS 1
+ u8 identity[0]; /* length inferred from payload */
+} STRUCT_PACKED;
+
+/* common routines */
++int compute_password_element(EAP_PWD_group *grp, u16 num,
++ const u8 *password, size_t password_len,
++ const u8 *id_server, size_t id_server_len,
++ const u8 *id_peer, size_t id_peer_len,
++ const u8 *token);
++int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k,
++ const BIGNUM *peer_scalar, const BIGNUM *server_scalar,
++ const u8 *confirm_peer, const u8 *confirm_server,
++ const u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id);
++struct crypto_hash * eap_pwd_h_init(void);
++void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len);
++void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EAP_PWD_COMMON_H */
--- /dev/null
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+/*
+ * EAP server/peer: EAP-SAKE shared routines
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
+ */
+
+#ifndef EAP_SAKE_COMMON_H
+#define EAP_SAKE_COMMON_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EAP_SAKE_VERSION 2
+
+#define EAP_SAKE_SUBTYPE_CHALLENGE 1
+#define EAP_SAKE_SUBTYPE_CONFIRM 2
+#define EAP_SAKE_SUBTYPE_AUTH_REJECT 3
+#define EAP_SAKE_SUBTYPE_IDENTITY 4
+
+#define EAP_SAKE_AT_RAND_S 1
+#define EAP_SAKE_AT_RAND_P 2
+#define EAP_SAKE_AT_MIC_S 3
+#define EAP_SAKE_AT_MIC_P 4
+#define EAP_SAKE_AT_SERVERID 5
+#define EAP_SAKE_AT_PEERID 6
+#define EAP_SAKE_AT_SPI_S 7
+#define EAP_SAKE_AT_SPI_P 8
+#define EAP_SAKE_AT_ANY_ID_REQ 9
+#define EAP_SAKE_AT_PERM_ID_REQ 10
+#define EAP_SAKE_AT_ENCR_DATA 128
+#define EAP_SAKE_AT_IV 129
+#define EAP_SAKE_AT_PADDING 130
+#define EAP_SAKE_AT_NEXT_TMPID 131
+#define EAP_SAKE_AT_MSK_LIFE 132
+
+#define EAP_SAKE_RAND_LEN 16
+#define EAP_SAKE_MIC_LEN 16
+#define EAP_SAKE_ROOT_SECRET_LEN 16
+#define EAP_SAKE_SMS_LEN 16
+#define EAP_SAKE_TEK_AUTH_LEN 16
+#define EAP_SAKE_TEK_CIPHER_LEN 16
+#define EAP_SAKE_TEK_LEN (EAP_SAKE_TEK_AUTH_LEN + EAP_SAKE_TEK_CIPHER_LEN)
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_sake_hdr {
+ u8 version; /* EAP_SAKE_VERSION */
+ u8 session_id;
+ u8 subtype;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+struct eap_sake_parse_attr {
+ const u8 *rand_s;
+ const u8 *rand_p;
+ const u8 *mic_s;
+ const u8 *mic_p;
+ const u8 *serverid;
+ size_t serverid_len;
+ const u8 *peerid;
+ size_t peerid_len;
+ const u8 *spi_s;
+ size_t spi_s_len;
+ const u8 *spi_p;
+ size_t spi_p_len;
+ const u8 *any_id_req;
+ const u8 *perm_id_req;
+ const u8 *encr_data;
+ size_t encr_data_len;
+ const u8 *iv;
+ size_t iv_len;
+ const u8 *next_tmpid;
+ size_t next_tmpid_len;
+ const u8 *msk_life;
+};
+
+int eap_sake_parse_attributes(const u8 *buf, size_t len,
+ struct eap_sake_parse_attr *attr);
+void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b,
+ const u8 *rand_s, const u8 *rand_p,
+ u8 *tek, u8 *msk, u8 *emsk);
+int eap_sake_compute_mic(const u8 *tek_auth,
+ const u8 *rand_s, const u8 *rand_p,
+ const u8 *serverid, size_t serverid_len,
+ const u8 *peerid, size_t peerid_len,
+ int peer, const u8 *eap, size_t eap_len,
+ const u8 *mic_pos, u8 *mic);
+void eap_sake_add_attr(struct wpabuf *buf, u8 type, const u8 *data,
+ size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EAP_SAKE_COMMON_H */
--- /dev/null
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+/*
+ * EAP peer/server: EAP-SIM/AKA/AKA' shared routines
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
- struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, const u8 *k_aut,
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
+ */
+
+#ifndef EAP_SIM_COMMON_H
+#define EAP_SIM_COMMON_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EAP_SIM_NONCE_S_LEN 16
+#define EAP_SIM_NONCE_MT_LEN 16
+#define EAP_SIM_MAC_LEN 16
+#define EAP_SIM_MK_LEN 20
+#define EAP_SIM_K_AUT_LEN 16
+#define EAP_SIM_K_ENCR_LEN 16
+#define EAP_SIM_KEYING_DATA_LEN 64
+#define EAP_SIM_IV_LEN 16
+#define EAP_SIM_KC_LEN 8
+#define EAP_SIM_SRES_LEN 4
+
+#define GSM_RAND_LEN 16
+
+#define EAP_SIM_VERSION 1
+
+/* EAP-SIM Subtypes */
+#define EAP_SIM_SUBTYPE_START 10
+#define EAP_SIM_SUBTYPE_CHALLENGE 11
+#define EAP_SIM_SUBTYPE_NOTIFICATION 12
+#define EAP_SIM_SUBTYPE_REAUTHENTICATION 13
+#define EAP_SIM_SUBTYPE_CLIENT_ERROR 14
+
+/* AT_CLIENT_ERROR_CODE error codes */
+#define EAP_SIM_UNABLE_TO_PROCESS_PACKET 0
+#define EAP_SIM_UNSUPPORTED_VERSION 1
+#define EAP_SIM_INSUFFICIENT_NUM_OF_CHAL 2
+#define EAP_SIM_RAND_NOT_FRESH 3
+
+#define EAP_SIM_MAX_FAST_REAUTHS 1000
+
+#define EAP_SIM_MAX_CHAL 3
+
+
+/* EAP-AKA Subtypes */
+#define EAP_AKA_SUBTYPE_CHALLENGE 1
+#define EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT 2
+#define EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE 4
+#define EAP_AKA_SUBTYPE_IDENTITY 5
+#define EAP_AKA_SUBTYPE_NOTIFICATION 12
+#define EAP_AKA_SUBTYPE_REAUTHENTICATION 13
+#define EAP_AKA_SUBTYPE_CLIENT_ERROR 14
+
+/* AT_CLIENT_ERROR_CODE error codes */
+#define EAP_AKA_UNABLE_TO_PROCESS_PACKET 0
+
+#define EAP_AKA_RAND_LEN 16
+#define EAP_AKA_AUTN_LEN 16
+#define EAP_AKA_AUTS_LEN 14
+#define EAP_AKA_RES_MAX_LEN 16
+#define EAP_AKA_IK_LEN 16
+#define EAP_AKA_CK_LEN 16
+#define EAP_AKA_MAX_FAST_REAUTHS 1000
+#define EAP_AKA_MIN_RES_LEN 4
+#define EAP_AKA_MAX_RES_LEN 16
+#define EAP_AKA_CHECKCODE_LEN 20
+
+#define EAP_AKA_PRIME_K_AUT_LEN 32
+#define EAP_AKA_PRIME_CHECKCODE_LEN 32
+#define EAP_AKA_PRIME_K_RE_LEN 32
+
+struct wpabuf;
+
+void eap_sim_derive_mk(const u8 *identity, size_t identity_len,
+ const u8 *nonce_mt, u16 selected_version,
+ const u8 *ver_list, size_t ver_list_len,
+ int num_chal, const u8 *kc, u8 *mk);
+void eap_aka_derive_mk(const u8 *identity, size_t identity_len,
+ const u8 *ik, const u8 *ck, u8 *mk);
+int eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk,
+ u8 *emsk);
+int eap_sim_derive_keys_reauth(u16 _counter,
+ const u8 *identity, size_t identity_len,
+ const u8 *nonce_s, const u8 *mk, u8 *msk,
+ u8 *emsk);
+int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req,
+ const u8 *mac, const u8 *extra, size_t extra_len);
+void eap_sim_add_mac(const u8 *k_aut, const u8 *msg, size_t msg_len, u8 *mac,
+ const u8 *extra, size_t extra_len);
+
+#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME)
+void eap_aka_prime_derive_keys(const u8 *identity, size_t identity_len,
+ const u8 *ik, const u8 *ck, u8 *k_encr,
+ u8 *k_aut, u8 *k_re, u8 *msk, u8 *emsk);
+int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter,
+ const u8 *identity, size_t identity_len,
+ const u8 *nonce_s, u8 *msk, u8 *emsk);
+int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req,
+ const u8 *mac, const u8 *extra,
+ size_t extra_len);
+void eap_sim_add_mac_sha256(const u8 *k_aut, const u8 *msg, size_t msg_len,
+ u8 *mac, const u8 *extra, size_t extra_len);
+
+void eap_aka_prime_derive_ck_ik_prime(u8 *ck, u8 *ik, const u8 *sqn_ak,
+ const u8 *network_name,
+ size_t network_name_len);
+#else /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
+static inline void eap_aka_prime_derive_keys(const u8 *identity,
+ size_t identity_len,
+ const u8 *ik, const u8 *ck,
+ u8 *k_encr, u8 *k_aut, u8 *k_re,
+ u8 *msk, u8 *emsk)
+{
+}
+
+static inline int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter,
+ const u8 *identity,
+ size_t identity_len,
+ const u8 *nonce_s, u8 *msk,
+ u8 *emsk)
+{
+ return -1;
+}
+
+static inline int eap_sim_verify_mac_sha256(const u8 *k_aut,
+ const struct wpabuf *req,
+ const u8 *mac, const u8 *extra,
+ size_t extra_len)
+{
+ return -1;
+}
+#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
+
+
+/* EAP-SIM/AKA Attributes (0..127 non-skippable) */
+#define EAP_SIM_AT_RAND 1
+#define EAP_SIM_AT_AUTN 2 /* only AKA */
+#define EAP_SIM_AT_RES 3 /* only AKA, only peer->server */
+#define EAP_SIM_AT_AUTS 4 /* only AKA, only peer->server */
+#define EAP_SIM_AT_PADDING 6 /* only encrypted */
+#define EAP_SIM_AT_NONCE_MT 7 /* only SIM, only send */
+#define EAP_SIM_AT_PERMANENT_ID_REQ 10
+#define EAP_SIM_AT_MAC 11
+#define EAP_SIM_AT_NOTIFICATION 12
+#define EAP_SIM_AT_ANY_ID_REQ 13
+#define EAP_SIM_AT_IDENTITY 14 /* only send */
+#define EAP_SIM_AT_VERSION_LIST 15 /* only SIM */
+#define EAP_SIM_AT_SELECTED_VERSION 16 /* only SIM */
+#define EAP_SIM_AT_FULLAUTH_ID_REQ 17
+#define EAP_SIM_AT_COUNTER 19 /* only encrypted */
+#define EAP_SIM_AT_COUNTER_TOO_SMALL 20 /* only encrypted */
+#define EAP_SIM_AT_NONCE_S 21 /* only encrypted */
+#define EAP_SIM_AT_CLIENT_ERROR_CODE 22 /* only send */
+#define EAP_SIM_AT_KDF_INPUT 23 /* only AKA' */
+#define EAP_SIM_AT_KDF 24 /* only AKA' */
+#define EAP_SIM_AT_IV 129
+#define EAP_SIM_AT_ENCR_DATA 130
+#define EAP_SIM_AT_NEXT_PSEUDONYM 132 /* only encrypted */
+#define EAP_SIM_AT_NEXT_REAUTH_ID 133 /* only encrypted */
+#define EAP_SIM_AT_CHECKCODE 134 /* only AKA */
+#define EAP_SIM_AT_RESULT_IND 135
+#define EAP_SIM_AT_BIDDING 136
+
+/* AT_NOTIFICATION notification code values */
+#define EAP_SIM_GENERAL_FAILURE_AFTER_AUTH 0
+#define EAP_SIM_TEMPORARILY_DENIED 1026
+#define EAP_SIM_NOT_SUBSCRIBED 1031
+#define EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH 16384
+#define EAP_SIM_SUCCESS 32768
+
+/* EAP-AKA' AT_KDF Key Derivation Function values */
+#define EAP_AKA_PRIME_KDF 1
+
+/* AT_BIDDING flags */
+#define EAP_AKA_BIDDING_FLAG_D 0x8000
+
+
+enum eap_sim_id_req {
+ NO_ID_REQ, ANY_ID, FULLAUTH_ID, PERMANENT_ID
+};
+
+
+struct eap_sim_attrs {
+ const u8 *rand, *autn, *mac, *iv, *encr_data, *version_list, *nonce_s;
+ const u8 *next_pseudonym, *next_reauth_id;
+ const u8 *nonce_mt, *identity, *res, *auts;
+ const u8 *checkcode;
+ const u8 *kdf_input;
+ const u8 *bidding;
+ size_t num_chal, version_list_len, encr_data_len;
+ size_t next_pseudonym_len, next_reauth_id_len, identity_len, res_len;
+ size_t res_len_bits;
+ size_t checkcode_len;
+ size_t kdf_input_len;
+ enum eap_sim_id_req id_req;
+ int notification, counter, selected_version, client_error_code;
+ int counter_too_small;
+ int result_ind;
+#define EAP_AKA_PRIME_KDF_MAX 10
+ u16 kdf[EAP_AKA_PRIME_KDF_MAX];
+ size_t kdf_count;
+};
+
+int eap_sim_parse_attr(const u8 *start, const u8 *end,
+ struct eap_sim_attrs *attr, int aka, int encr);
+u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data,
+ size_t encr_data_len, const u8 *iv,
+ struct eap_sim_attrs *attr, int aka);
+
+
+struct eap_sim_msg;
+
+struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype);
++struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, int type,
++ const u8 *k_aut,
+ const u8 *extra, size_t extra_len);
+void eap_sim_msg_free(struct eap_sim_msg *msg);
+u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr,
+ const u8 *data, size_t len);
+u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr,
+ u16 value, const u8 *data, size_t len);
+u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr);
+int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv,
+ u8 attr_encr);
+int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr,
+ int attr_pad);
+
+void eap_sim_report_notification(void *msg_ctx, int notification, int aka);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EAP_SIM_COMMON_H */
--- /dev/null
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+/*
+ * EAP-TLV definitions (draft-josefsson-pppext-eap-tls-eap-10.txt)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
+ */
+
+#ifndef EAP_TLV_COMMON_H
+#define EAP_TLV_COMMON_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* EAP-TLV TLVs (draft-josefsson-ppext-eap-tls-eap-10.txt) */
+#define EAP_TLV_RESULT_TLV 3 /* Acknowledged Result */
+#define EAP_TLV_NAK_TLV 4
+#define EAP_TLV_ERROR_CODE_TLV 5
+#define EAP_TLV_CONNECTION_BINDING_TLV 6
+#define EAP_TLV_VENDOR_SPECIFIC_TLV 7
+#define EAP_TLV_URI_TLV 8
+#define EAP_TLV_EAP_PAYLOAD_TLV 9
+#define EAP_TLV_INTERMEDIATE_RESULT_TLV 10
+#define EAP_TLV_PAC_TLV 11 /* RFC 5422, Section 4.2 */
+#define EAP_TLV_CRYPTO_BINDING_TLV 12
+#define EAP_TLV_CALLING_STATION_ID_TLV 13
+#define EAP_TLV_CALLED_STATION_ID_TLV 14
+#define EAP_TLV_NAS_PORT_TYPE_TLV 15
+#define EAP_TLV_SERVER_IDENTIFIER_TLV 16
+#define EAP_TLV_IDENTITY_TYPE_TLV 17
+#define EAP_TLV_SERVER_TRUSTED_ROOT_TLV 18
+#define EAP_TLV_REQUEST_ACTION_TLV 19
+#define EAP_TLV_PKCS7_TLV 20
+
+#define EAP_TLV_RESULT_SUCCESS 1
+#define EAP_TLV_RESULT_FAILURE 2
+
+#define EAP_TLV_TYPE_MANDATORY 0x8000
+#define EAP_TLV_TYPE_MASK 0x3fff
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_tlv_hdr {
+ be16 tlv_type;
+ be16 length;
+} STRUCT_PACKED;
+
+struct eap_tlv_nak_tlv {
+ be16 tlv_type;
+ be16 length;
+ be32 vendor_id;
+ be16 nak_type;
+} STRUCT_PACKED;
+
+struct eap_tlv_result_tlv {
+ be16 tlv_type;
+ be16 length;
+ be16 status;
+} STRUCT_PACKED;
+
+/* RFC 4851, Section 4.2.7 - Intermediate-Result TLV */
+struct eap_tlv_intermediate_result_tlv {
+ be16 tlv_type;
+ be16 length;
+ be16 status;
+ /* Followed by optional TLVs */
+} STRUCT_PACKED;
+
+/* RFC 4851, Section 4.2.8 - Crypto-Binding TLV */
+struct eap_tlv_crypto_binding_tlv {
+ be16 tlv_type;
+ be16 length;
+ u8 reserved;
+ u8 version;
+ u8 received_version;
+ u8 subtype;
+ u8 nonce[32];
+ u8 compound_mac[20];
+} STRUCT_PACKED;
+
+struct eap_tlv_pac_ack_tlv {
+ be16 tlv_type;
+ be16 length;
+ be16 pac_type;
+ be16 pac_len;
+ be16 result;
+} STRUCT_PACKED;
+
+/* RFC 4851, Section 4.2.9 - Request-Action TLV */
+struct eap_tlv_request_action_tlv {
+ be16 tlv_type;
+ be16 length;
+ be16 action;
+} STRUCT_PACKED;
+
+/* RFC 5422, Section 4.2.6 - PAC-Type TLV */
+struct eap_tlv_pac_type_tlv {
+ be16 tlv_type; /* PAC_TYPE_PAC_TYPE */
+ be16 length;
+ be16 pac_type;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST 0
+#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE 1
+
+#define EAP_TLV_ACTION_PROCESS_TLV 1
+#define EAP_TLV_ACTION_NEGOTIATE_EAP 2
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EAP_TLV_COMMON_H */
--- /dev/null
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+/*
+ * EAP server/peer: EAP-TTLS (RFC 5281)
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
+ */
+
+#ifndef EAP_TTLS_H
+#define EAP_TTLS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ttls_avp {
+ be32 avp_code;
+ be32 avp_length; /* 8-bit flags, 24-bit length;
+ * length includes AVP header */
+ /* optional 32-bit Vendor-ID */
+ /* Data */
+};
+
+struct ttls_avp_vendor {
+ be32 avp_code;
+ be32 avp_length; /* 8-bit flags, 24-bit length;
+ * length includes AVP header */
+ be32 vendor_id;
+ /* Data */
+};
+
+
+#define AVP_FLAGS_VENDOR 0x80
+#define AVP_FLAGS_MANDATORY 0x40
+
+#define AVP_PAD(start, pos) \
+do { \
+ int __pad; \
+ __pad = (4 - (((pos) - (start)) & 3)) & 3; \
+ os_memset((pos), 0, __pad); \
+ pos += __pad; \
+} while (0)
+
+
+/* RFC 2865 */
+#define RADIUS_ATTR_USER_NAME 1
+#define RADIUS_ATTR_USER_PASSWORD 2
+#define RADIUS_ATTR_CHAP_PASSWORD 3
+#define RADIUS_ATTR_REPLY_MESSAGE 18
+#define RADIUS_ATTR_VENDOR_SPECIFIC 26
+#define RADIUS_ATTR_CHAP_CHALLENGE 60
+#define RADIUS_ATTR_EAP_MESSAGE 79
+
+/* RFC 2548 */
+#define RADIUS_VENDOR_ID_MICROSOFT 311
+#define RADIUS_ATTR_MS_CHAP_RESPONSE 1
+#define RADIUS_ATTR_MS_CHAP_ERROR 2
+#define RADIUS_ATTR_MS_CHAP_NT_ENC_PW 6
+#define RADIUS_ATTR_MS_CHAP_CHALLENGE 11
+#define RADIUS_ATTR_MS_CHAP2_RESPONSE 25
+#define RADIUS_ATTR_MS_CHAP2_SUCCESS 26
+#define RADIUS_ATTR_MS_CHAP2_CPW 27
+
+#define EAP_TTLS_MSCHAPV2_CHALLENGE_LEN 16
+#define EAP_TTLS_MSCHAPV2_RESPONSE_LEN 50
+#define EAP_TTLS_MSCHAP_CHALLENGE_LEN 8
+#define EAP_TTLS_MSCHAP_RESPONSE_LEN 50
+#define EAP_TTLS_CHAP_CHALLENGE_LEN 16
+#define EAP_TTLS_CHAP_PASSWORD_LEN 16
+
+#define RADIUS_VENDOR_ID_UKERNA 25622
+#define RADIUS_ATTR_UKERNA_CHBIND 135
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EAP_TTLS_H */
--- /dev/null
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+/*
+ * EAP-WSC definitions for Wi-Fi Protected Setup
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
+ */
+
+#ifndef EAP_WSC_COMMON_H
+#define EAP_WSC_COMMON_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EAP_VENDOR_TYPE_WSC 1
+
+#define WSC_FLAGS_MF 0x01
+#define WSC_FLAGS_LF 0x02
+
+#define WSC_ID_REGISTRAR "WFA-SimpleConfig-Registrar-1-0"
+#define WSC_ID_REGISTRAR_LEN 30
+#define WSC_ID_ENROLLEE "WFA-SimpleConfig-Enrollee-1-0"
+#define WSC_ID_ENROLLEE_LEN 29
+
+#define WSC_FRAGMENT_SIZE 1400
+
+
+struct wpabuf * eap_wsc_build_frag_ack(u8 id, u8 code);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EAP_WSC_COMMON_H */
--- /dev/null
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+/*
+ * EAP peer state machine functions (RFC 4137)
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
++ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ *
- EAPOL_altReject
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
+ */
+
+#ifndef EAP_H
+#define EAP_H
+
+#include "common/defs.h"
+#include "eap_common/eap_defs.h"
+#include "eap_peer/eap_methods.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct eap_sm;
+struct wpa_config_blob;
+struct wpabuf;
+
+struct eap_method_type {
+ int vendor;
+ u32 method;
+};
+
+#ifdef IEEE8021X_EAPOL
+
+/**
+ * enum eapol_bool_var - EAPOL boolean state variables for EAP state machine
+ *
+ * These variables are used in the interface between EAP peer state machine and
+ * lower layer. These are defined in RFC 4137, Sect. 4.1. Lower layer code is
+ * expected to maintain these variables and register a callback functions for
+ * EAP state machine to get and set the variables.
+ */
+enum eapol_bool_var {
+ /**
+ * EAPOL_eapSuccess - EAP SUCCESS state reached
+ *
+ * EAP state machine reads and writes this value.
+ */
+ EAPOL_eapSuccess,
+
+ /**
+ * EAPOL_eapRestart - Lower layer request to restart authentication
+ *
+ * Set to TRUE in lower layer, FALSE in EAP state machine.
+ */
+ EAPOL_eapRestart,
+
+ /**
+ * EAPOL_eapFail - EAP FAILURE state reached
+ *
+ * EAP state machine writes this value.
+ */
+ EAPOL_eapFail,
+
+ /**
+ * EAPOL_eapResp - Response to send
+ *
+ * Set to TRUE in EAP state machine, FALSE in lower layer.
+ */
+ EAPOL_eapResp,
+
+ /**
+ * EAPOL_eapNoResp - Request has been process; no response to send
+ *
+ * Set to TRUE in EAP state machine, FALSE in lower layer.
+ */
+ EAPOL_eapNoResp,
+
+ /**
+ * EAPOL_eapReq - EAP request available from lower layer
+ *
+ * Set to TRUE in lower layer, FALSE in EAP state machine.
+ */
+ EAPOL_eapReq,
+
+ /**
+ * EAPOL_portEnabled - Lower layer is ready for communication
+ *
+ * EAP state machines reads this value.
+ */
+ EAPOL_portEnabled,
+
+ /**
+ * EAPOL_altAccept - Alternate indication of success (RFC3748)
+ *
+ * EAP state machines reads this value.
+ */
+ EAPOL_altAccept,
+
+ /**
+ * EAPOL_altReject - Alternate indication of failure (RFC3748)
+ *
+ * EAP state machines reads this value.
+ */
- * @field: Field name (e.g., "IDENTITY")
++ EAPOL_altReject,
++
++ /**
++ * EAPOL_eapTriggerStart - EAP-based trigger to send EAPOL-Start
++ *
++ * EAP state machine writes this value.
++ */
++ EAPOL_eapTriggerStart
+};
+
+/**
+ * enum eapol_int_var - EAPOL integer state variables for EAP state machine
+ *
+ * These variables are used in the interface between EAP peer state machine and
+ * lower layer. These are defined in RFC 4137, Sect. 4.1. Lower layer code is
+ * expected to maintain these variables and register a callback functions for
+ * EAP state machine to get and set the variables.
+ */
+enum eapol_int_var {
+ /**
+ * EAPOL_idleWhile - Outside time for EAP peer timeout
+ *
+ * This integer variable is used to provide an outside timer that the
+ * external (to EAP state machine) code must decrement by one every
+ * second until the value reaches zero. This is used in the same way as
+ * EAPOL state machine timers. EAP state machine reads and writes this
+ * value.
+ */
+ EAPOL_idleWhile
+};
+
+/**
+ * struct eapol_callbacks - Callback functions from EAP to lower layer
+ *
+ * This structure defines the callback functions that EAP state machine
+ * requires from the lower layer (usually EAPOL state machine) for updating
+ * state variables and requesting information. eapol_ctx from
+ * eap_peer_sm_init() call will be used as the ctx parameter for these
+ * callback functions.
+ */
+struct eapol_callbacks {
+ /**
+ * get_config - Get pointer to the current network configuration
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ */
+ struct eap_peer_config * (*get_config)(void *ctx);
+
+ /**
+ * get_bool - Get a boolean EAPOL state variable
+ * @variable: EAPOL boolean variable to get
+ * Returns: Value of the EAPOL variable
+ */
+ Boolean (*get_bool)(void *ctx, enum eapol_bool_var variable);
+
+ /**
+ * set_bool - Set a boolean EAPOL state variable
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @variable: EAPOL boolean variable to set
+ * @value: Value for the EAPOL variable
+ */
+ void (*set_bool)(void *ctx, enum eapol_bool_var variable,
+ Boolean value);
+
+ /**
+ * get_int - Get an integer EAPOL state variable
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @variable: EAPOL integer variable to get
+ * Returns: Value of the EAPOL variable
+ */
+ unsigned int (*get_int)(void *ctx, enum eapol_int_var variable);
+
+ /**
+ * set_int - Set an integer EAPOL state variable
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @variable: EAPOL integer variable to set
+ * @value: Value for the EAPOL variable
+ */
+ void (*set_int)(void *ctx, enum eapol_int_var variable,
+ unsigned int value);
+
+ /**
+ * get_eapReqData - Get EAP-Request data
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @len: Pointer to variable that will be set to eapReqDataLen
+ * Returns: Reference to eapReqData (EAP state machine will not free
+ * this) or %NULL if eapReqData not available.
+ */
+ struct wpabuf * (*get_eapReqData)(void *ctx);
+
+ /**
+ * set_config_blob - Set named configuration blob
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @blob: New value for the blob
+ *
+ * Adds a new configuration blob or replaces the current value of an
+ * existing blob.
+ */
+ void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob);
+
+ /**
+ * get_config_blob - Get a named configuration blob
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @name: Name of the blob
+ * Returns: Pointer to blob data or %NULL if not found
+ */
+ const struct wpa_config_blob * (*get_config_blob)(void *ctx,
+ const char *name);
+
+ /**
+ * notify_pending - Notify that a pending request can be retried
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ *
+ * An EAP method can perform a pending operation (e.g., to get a
+ * response from an external process). Once the response is available,
+ * this callback function can be used to request EAPOL state machine to
+ * retry delivering the previously received (and still unanswered) EAP
+ * request to EAP state machine.
+ */
+ void (*notify_pending)(void *ctx);
+
+ /**
+ * eap_param_needed - Notify that EAP parameter is needed
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
- void (*eap_param_needed)(void *ctx, const char *field,
++ * @field: Field indicator (e.g., WPA_CTRL_REQ_EAP_IDENTITY)
+ * @txt: User readable text describing the required parameter
+ */
- struct eapol_callbacks *eapol_cb,
++ void (*eap_param_needed)(void *ctx, enum wpa_ctrl_req_type field,
+ const char *txt);
++
++ /**
++ * notify_cert - Notification of a peer certificate
++ * @ctx: eapol_ctx from eap_peer_sm_init() call
++ * @depth: Depth in certificate chain (0 = server)
++ * @subject: Subject of the peer certificate
++ * @altsubject: Select fields from AltSubject of the peer certificate
++ * @num_altsubject: Number of altsubject values
++ * @cert_hash: SHA-256 hash of the certificate
++ * @cert: Peer certificate
++ */
++ void (*notify_cert)(void *ctx, int depth, const char *subject,
++ const char *altsubject[], int num_altsubject,
++ const char *cert_hash, const struct wpabuf *cert);
++
++ /**
++ * notify_status - Notification of the current EAP state
++ * @ctx: eapol_ctx from eap_peer_sm_init() call
++ * @status: Step in the process of EAP authentication
++ * @parameter: Step-specific parameter, e.g., EAP method name
++ */
++ void (*notify_status)(void *ctx, const char *status,
++ const char *parameter);
++
++#ifdef CONFIG_EAP_PROXY
++ /**
++ * eap_proxy_cb - Callback signifying any updates from eap_proxy
++ * @ctx: eapol_ctx from eap_peer_sm_init() call
++ */
++ void (*eap_proxy_cb)(void *ctx);
++#endif /* CONFIG_EAP_PROXY */
++
++ /**
++ * set_anon_id - Set or add anonymous identity
++ * @ctx: eapol_ctx from eap_peer_sm_init() call
++ * @id: Anonymous identity (e.g., EAP-SIM pseudonym) or %NULL to clear
++ * @len: Length of anonymous identity in octets
++ */
++ void (*set_anon_id)(void *ctx, const u8 *id, size_t len);
+};
+
+/**
+ * struct eap_config - Configuration for EAP state machine
+ */
+struct eap_config {
+ /**
+ * opensc_engine_path - OpenSC engine for OpenSSL engine support
+ *
+ * Usually, path to engine_opensc.so.
+ */
+ const char *opensc_engine_path;
+ /**
+ * pkcs11_engine_path - PKCS#11 engine for OpenSSL engine support
+ *
+ * Usually, path to engine_pkcs11.so.
+ */
+ const char *pkcs11_engine_path;
+ /**
+ * pkcs11_module_path - OpenSC PKCS#11 module for OpenSSL engine
+ *
+ * Usually, path to opensc-pkcs11.so.
+ */
+ const char *pkcs11_module_path;
+ /**
++ * openssl_ciphers - OpenSSL cipher string
++ *
++ * This is an OpenSSL specific configuration option for configuring the
++ * default ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the
++ * default.
++ */
++ const char *openssl_ciphers;
++ /**
+ * wps - WPS context data
+ *
+ * This is only used by EAP-WSC and can be left %NULL if not available.
+ */
+ struct wps_context *wps;
++
++ /**
++ * cert_in_cb - Include server certificates in callback
++ */
++ int cert_in_cb;
+};
+
+struct eap_sm * eap_peer_sm_init(void *eapol_ctx,
++ const struct eapol_callbacks *eapol_cb,
+ void *msg_ctx, struct eap_config *conf);
+void eap_peer_sm_deinit(struct eap_sm *sm);
+int eap_peer_sm_step(struct eap_sm *sm);
+void eap_sm_abort(struct eap_sm *sm);
+int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen,
+ int verbose);
++const char * eap_sm_get_method_name(struct eap_sm *sm);
+struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted);
+void eap_sm_request_identity(struct eap_sm *sm);
+void eap_sm_request_password(struct eap_sm *sm);
+void eap_sm_request_new_password(struct eap_sm *sm);
+void eap_sm_request_pin(struct eap_sm *sm);
+void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len);
+void eap_sm_request_passphrase(struct eap_sm *sm);
++void eap_sm_request_sim(struct eap_sm *sm, const char *req);
+void eap_sm_notify_ctrl_attached(struct eap_sm *sm);
+u32 eap_get_phase2_type(const char *name, int *vendor);
+struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config,
+ size_t *count);
+void eap_set_fast_reauth(struct eap_sm *sm, int enabled);
+void eap_set_workaround(struct eap_sm *sm, unsigned int workaround);
+void eap_set_force_disabled(struct eap_sm *sm, int disabled);
++void eap_set_external_sim(struct eap_sm *sm, int external_sim);
+int eap_key_available(struct eap_sm *sm);
+void eap_notify_success(struct eap_sm *sm);
+void eap_notify_lower_layer_success(struct eap_sm *sm);
++const u8 * eap_get_eapSessionId(struct eap_sm *sm, size_t *len);
+const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len);
+struct wpabuf * eap_get_eapRespData(struct eap_sm *sm);
+void eap_register_scard_ctx(struct eap_sm *sm, void *ctx);
+void eap_invalidate_cached_session(struct eap_sm *sm);
+
+int eap_is_wps_pbc_enrollee(struct eap_peer_config *conf);
+int eap_is_wps_pin_enrollee(struct eap_peer_config *conf);
+
++struct ext_password_data;
++void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext);
++void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len);
++int eap_peer_was_failure_expected(struct eap_sm *sm);
++void eap_peer_erp_free_keys(struct eap_sm *sm);
++
+#endif /* IEEE8021X_EAPOL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EAP_H */
--- /dev/null
- * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
+/*
+ * EAP peer configuration data
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
++ * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
+ *
- u8 *private_key_passwd;
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
+ */
+
+#ifndef EAP_CONFIG_H
+#define EAP_CONFIG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* http://tools.ietf.org/html/draft-ietf-emu-chbind-13#section-5.3.1 */
+#define CHBIND_CODE_REQUEST 1
+#define CHBIND_CODE_SUCCESS 2
+#define CHBIND_CODE_FAILURE 3
+/* http://tools.ietf.org/html/draft-ietf-emu-chbind-13#section-5.3. */
+#define CHBIND_NSID_RADIUS 1
+
+struct eap_peer_chbind_config
+{
+ /* namespace id for this channel binding info */
+ int nsid;
+
+ /* data to be sent in channel binding request */
+ u8 *req_data;
+
+ size_t req_data_len;
+
+ /* lower level callback invoked when response is received */
+ void (*response_cb)(void *ctx, int code, int nsid, u8 *resp_data, size_t resp_data_len);
+
+ /* context for response callback */
+ void *ctx;
+};
+
+/**
+ * struct eap_peer_config - EAP peer configuration/credentials
+ */
+struct eap_peer_config {
+ /**
+ * identity - EAP Identity
+ *
+ * This field is used to set the real user identity or NAI (for
+ * EAP-PSK/PAX/SAKE/GPSK).
+ */
+ u8 *identity;
+
+ /**
+ * identity_len - EAP Identity length
+ */
+ size_t identity_len;
+
+ /**
+ * anonymous_identity - Anonymous EAP Identity
+ *
+ * This field is used for unencrypted use with EAP types that support
+ * different tunnelled identity, e.g., EAP-TTLS, in order to reveal the
+ * real identity (identity field) only to the authentication server.
+ *
+ * If not set, the identity field will be used for both unencrypted and
+ * protected fields.
++ *
++ * This field can also be used with EAP-SIM/AKA/AKA' to store the
++ * pseudonym identity.
+ */
+ u8 *anonymous_identity;
+
+ /**
+ * anonymous_identity_len - Length of anonymous_identity
+ */
+ size_t anonymous_identity_len;
+
+ /**
+ * password - Password string for EAP
+ *
+ * This field can include either the plaintext password (default
+ * option) or a NtPasswordHash (16-byte MD4 hash of the unicode
+ * presentation of the password) if flags field has
+ * EAP_CONFIG_FLAGS_PASSWORD_NTHASH bit set to 1. NtPasswordHash can
+ * only be used with authentication mechanism that use this hash as the
+ * starting point for operation: MSCHAP and MSCHAPv2 (EAP-MSCHAPv2,
+ * EAP-TTLS/MSCHAPv2, EAP-TTLS/MSCHAP, LEAP).
+ *
+ * In addition, this field is used to configure a pre-shared key for
+ * EAP-PSK/PAX/SAKE/GPSK. The length of the PSK must be 16 for EAP-PSK
+ * and EAP-PAX and 32 for EAP-SAKE. EAP-GPSK can use a variable length
+ * PSK.
+ */
+ u8 *password;
+
+ /**
+ * password_len - Length of password field
+ */
+ size_t password_len;
+
+ /**
+ * ca_cert - File path to CA certificate file (PEM/DER)
+ *
+ * This file can have one or more trusted CA certificates. If ca_cert
+ * and ca_path are not included, server certificate will not be
+ * verified. This is insecure and a trusted CA certificate should
+ * always be configured when using EAP-TLS/TTLS/PEAP. Full path to the
+ * file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ *
+ * Alternatively, this can be used to only perform matching of the
+ * server certificate (SHA-256 hash of the DER encoded X.509
+ * certificate). In this case, the possible CA certificates in the
+ * server certificate chain are ignored and only the server certificate
+ * is verified. This is configured with the following format:
+ * hash:://server/sha256/cert_hash_in_hex
+ * For example: "hash://server/sha256/
+ * 5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca6a"
+ *
+ * On Windows, trusted CA certificates can be loaded from the system
+ * certificate store by setting this to cert_store://name, e.g.,
+ * ca_cert="cert_store://CA" or ca_cert="cert_store://ROOT".
+ * Note that when running wpa_supplicant as an application, the user
+ * certificate store (My user account) is used, whereas computer store
+ * (Computer account) is used when running wpasvc as a service.
+ */
+ u8 *ca_cert;
+
+ /**
+ * ca_path - Directory path for CA certificate files (PEM)
+ *
+ * This path may contain multiple CA certificates in OpenSSL format.
+ * Common use for this is to point to system trusted CA list which is
+ * often installed into directory like /etc/ssl/certs. If configured,
+ * these certificates are added to the list of trusted CAs. ca_cert
+ * may also be included in that case, but it is not required.
+ */
+ u8 *ca_path;
+
+ /**
+ * client_cert - File path to client certificate file (PEM/DER)
+ *
+ * This field is used with EAP method that use TLS authentication.
+ * Usually, this is only configured for EAP-TLS, even though this could
+ * in theory be used with EAP-TTLS and EAP-PEAP, too. Full path to the
+ * file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ u8 *client_cert;
+
+ /**
+ * private_key - File path to client private key file (PEM/DER/PFX)
+ *
+ * When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be
+ * commented out. Both the private key and certificate will be read
+ * from the PKCS#12 file in this case. Full path to the file should be
+ * used since working directory may change when wpa_supplicant is run
+ * in the background.
+ *
+ * Windows certificate store can be used by leaving client_cert out and
+ * configuring private_key in one of the following formats:
+ *
+ * cert://substring_to_match
+ *
+ * hash://certificate_thumbprint_in_hex
+ *
+ * For example: private_key="hash://63093aa9c47f56ae88334c7b65a4"
+ *
+ * Note that when running wpa_supplicant as an application, the user
+ * certificate store (My user account) is used, whereas computer store
+ * (Computer account) is used when running wpasvc as a service.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ u8 *private_key;
+
+ /**
+ * private_key_passwd - Password for private key file
+ *
+ * If left out, this will be asked through control interface.
+ */
- u8 *private_key2_passwd;
++ char *private_key_passwd;
+
+ /**
+ * dh_file - File path to DH/DSA parameters file (in PEM format)
+ *
+ * This is an optional configuration file for setting parameters for an
+ * ephemeral DH key exchange. In most cases, the default RSA
+ * authentication does not use this configuration. However, it is
+ * possible setup RSA to use ephemeral DH key exchange. In addition,
+ * ciphers with DSA keys always use ephemeral DH keys. This can be used
+ * to achieve forward secrecy. If the file is in DSA parameters format,
+ * it will be automatically converted into DH params. Full path to the
+ * file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ u8 *dh_file;
+
+ /**
+ * subject_match - Constraint for server certificate subject
+ *
+ * This substring is matched against the subject of the authentication
+ * server certificate. If this string is set, the server sertificate is
+ * only accepted if it contains this string in the subject. The subject
+ * string is in following format:
+ *
+ * /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@n.example.com
++ *
++ * Note: Since this is a substring match, this cannot be used securily
++ * to do a suffix match against a possible domain name in the CN entry.
++ * For such a use case, domain_suffix_match should be used instead.
+ */
+ u8 *subject_match;
+
+ /**
+ * altsubject_match - Constraint for server certificate alt. subject
+ *
+ * Semicolon separated string of entries to be matched against the
+ * alternative subject name of the authentication server certificate.
+ * If this string is set, the server sertificate is only accepted if it
+ * contains one of the entries in an alternative subject name
+ * extension.
+ *
+ * altSubjectName string is in following format: TYPE:VALUE
+ *
+ * Example: EMAIL:server@example.com
+ * Example: DNS:server.example.com;DNS:server2.example.com
+ *
+ * Following types are supported: EMAIL, DNS, URI
+ */
+ u8 *altsubject_match;
+
+ /**
++ * domain_suffix_match - Constraint for server domain name
++ *
++ * If set, this FQDN is used as a suffix match requirement for the
++ * server certificate in SubjectAltName dNSName element(s). If a
++ * matching dNSName is found, this constraint is met. If no dNSName
++ * values are present, this constraint is matched against SubjectName CN
++ * using same suffix match comparison. Suffix match here means that the
++ * host/domain name is compared one label at a time starting from the
++ * top-level domain and all the labels in domain_suffix_match shall be
++ * included in the certificate. The certificate may include additional
++ * sub-level labels in addition to the required labels.
++ *
++ * For example, domain_suffix_match=example.com would match
++ * test.example.com but would not match test-example.com.
++ */
++ char *domain_suffix_match;
++
++ /**
++ * domain_match - Constraint for server domain name
++ *
++ * If set, this FQDN is used as a full match requirement for the
++ * server certificate in SubjectAltName dNSName element(s). If a
++ * matching dNSName is found, this constraint is met. If no dNSName
++ * values are present, this constraint is matched against SubjectName CN
++ * using same full match comparison. This behavior is similar to
++ * domain_suffix_match, but has the requirement of a full match, i.e.,
++ * no subdomains or wildcard matches are allowed. Case-insensitive
++ * comparison is used, so "Example.com" matches "example.com", but would
++ * not match "test.Example.com".
++ */
++ char *domain_match;
++
++ /**
+ * ca_cert2 - File path to CA certificate file (PEM/DER) (Phase 2)
+ *
+ * This file can have one or more trusted CA certificates. If ca_cert2
+ * and ca_path2 are not included, server certificate will not be
+ * verified. This is insecure and a trusted CA certificate should
+ * always be configured. Full path to the file should be used since
+ * working directory may change when wpa_supplicant is run in the
+ * background.
+ *
+ * This field is like ca_cert, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ u8 *ca_cert2;
+
+ /**
+ * ca_path2 - Directory path for CA certificate files (PEM) (Phase 2)
+ *
+ * This path may contain multiple CA certificates in OpenSSL format.
+ * Common use for this is to point to system trusted CA list which is
+ * often installed into directory like /etc/ssl/certs. If configured,
+ * these certificates are added to the list of trusted CAs. ca_cert
+ * may also be included in that case, but it is not required.
+ *
+ * This field is like ca_path, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ */
+ u8 *ca_path2;
+
+ /**
+ * client_cert2 - File path to client certificate file
+ *
+ * This field is like client_cert, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the
+ * file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ u8 *client_cert2;
+
+ /**
+ * private_key2 - File path to client private key file
+ *
+ * This field is like private_key, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the
+ * file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ u8 *private_key2;
+
+ /**
+ * private_key2_passwd - Password for private key file
+ *
+ * This field is like private_key_passwd, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ */
- * "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS.
++ char *private_key2_passwd;
+
+ /**
+ * dh_file2 - File path to DH/DSA parameters file (in PEM format)
+ *
+ * This field is like dh_file, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the
+ * file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ u8 *dh_file2;
+
+ /**
+ * subject_match2 - Constraint for server certificate subject
+ *
+ * This field is like subject_match, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ */
+ u8 *subject_match2;
+
+ /**
+ * altsubject_match2 - Constraint for server certificate alt. subject
+ *
+ * This field is like altsubject_match, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ */
+ u8 *altsubject_match2;
+
+ /**
++ * domain_suffix_match2 - Constraint for server domain name
++ *
++ * This field is like domain_suffix_match, but used for phase 2 (inside
++ * EAP-TTLS/PEAP/FAST tunnel) authentication.
++ */
++ char *domain_suffix_match2;
++
++ /**
++ * domain_match2 - Constraint for server domain name
++ *
++ * This field is like domain_match, but used for phase 2 (inside
++ * EAP-TTLS/PEAP/FAST tunnel) authentication.
++ */
++ char *domain_match2;
++
++ /**
+ * eap_methods - Allowed EAP methods
+ *
+ * (vendor=EAP_VENDOR_IETF,method=EAP_TYPE_NONE) terminated list of
+ * allowed EAP methods or %NULL if all methods are accepted.
+ */
+ struct eap_method_type *eap_methods;
+
+ /**
+ * phase1 - Phase 1 (outer authentication) parameters
+ *
+ * String with field-value pairs, e.g., "peapver=0" or
+ * "peapver=1 peaplabel=1".
+ *
+ * 'peapver' can be used to force which PEAP version (0 or 1) is used.
+ *
+ * 'peaplabel=1' can be used to force new label, "client PEAP
+ * encryption", to be used during key derivation when PEAPv1 or newer.
+ *
+ * Most existing PEAPv1 implementation seem to be using the old label,
+ * "client EAP encryption", and wpa_supplicant is now using that as the
+ * default value.
+ *
+ * Some servers, e.g., Radiator, may require peaplabel=1 configuration
+ * to interoperate with PEAPv1; see eap_testing.txt for more details.
+ *
+ * 'peap_outer_success=0' can be used to terminate PEAP authentication
+ * on tunneled EAP-Success. This is required with some RADIUS servers
+ * that implement draft-josefsson-pppext-eap-tls-eap-05.txt (e.g.,
+ * Lucent NavisRadius v4.4.0 with PEAP in "IETF Draft 5" mode).
+ *
+ * include_tls_length=1 can be used to force wpa_supplicant to include
+ * TLS Message Length field in all TLS messages even if they are not
+ * fragmented.
+ *
+ * sim_min_num_chal=3 can be used to configure EAP-SIM to require three
+ * challenges (by default, it accepts 2 or 3).
+ *
+ * result_ind=1 can be used to enable EAP-SIM and EAP-AKA to use
+ * protected result indication.
+ *
+ * fast_provisioning option can be used to enable in-line provisioning
+ * of EAP-FAST credentials (PAC):
+ * 0 = disabled,
+ * 1 = allow unauthenticated provisioning,
+ * 2 = allow authenticated provisioning,
+ * 3 = allow both unauthenticated and authenticated provisioning
+ *
+ * fast_max_pac_list_len=num option can be used to set the maximum
+ * number of PAC entries to store in a PAC list (default: 10).
+ *
+ * fast_pac_format=binary option can be used to select binary format
+ * for storing PAC entries in order to save some space (the default
+ * text format uses about 2.5 times the size of minimal binary format).
+ *
+ * crypto_binding option can be used to control PEAPv0 cryptobinding
+ * behavior:
+ * 0 = do not use cryptobinding (default)
+ * 1 = use cryptobinding if server supports it
+ * 2 = require cryptobinding
+ *
+ * EAP-WSC (WPS) uses following options: pin=Device_Password and
+ * uuid=Device_UUID
++ *
++ * For wired IEEE 802.1X authentication, "allow_canned_success=1" can be
++ * used to configure a mode that allows EAP-Success (and EAP-Failure)
++ * without going through authentication step. Some switches use such
++ * sequence when forcing the port to be authorized/unauthorized or as a
++ * fallback option if the authentication server is unreachable. By
++ * default, wpa_supplicant discards such frames to protect against
++ * potential attacks by rogue devices, but this option can be used to
++ * disable that protection for cases where the server/authenticator does
++ * not need to be authenticated.
+ */
+ char *phase1;
+
+ /**
+ * phase2 - Phase2 (inner authentication with TLS tunnel) parameters
+ *
+ * String with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or
++ * "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS. "mschapv2_retry=0" can
++ * be used to disable MSCHAPv2 password retry in authentication failure
++ * cases.
+ */
+ char *phase2;
+
+ /**
+ * pcsc - Parameters for PC/SC smartcard interface for USIM and GSM SIM
+ *
+ * This field is used to configure PC/SC smartcard interface.
+ * Currently, the only configuration is whether this field is %NULL (do
+ * not use PC/SC) or non-NULL (e.g., "") to enable PC/SC.
+ *
+ * This field is used for EAP-SIM and EAP-AKA.
+ */
+ char *pcsc;
+
+ /**
+ * pin - PIN for USIM, GSM SIM, and smartcards
+ *
+ * This field is used to configure PIN for SIM and smartcards for
+ * EAP-SIM and EAP-AKA. In addition, this is used with EAP-TLS if a
+ * smartcard is used for private key operations.
+ *
+ * If left out, this will be asked through control interface.
+ */
+ char *pin;
+
+ /**
+ * engine - Enable OpenSSL engine (e.g., for smartcard access)
+ *
+ * This is used if private key operations for EAP-TLS are performed
+ * using a smartcard.
+ */
+ int engine;
+
+ /**
+ * engine_id - Engine ID for OpenSSL engine
+ *
+ * "opensc" to select OpenSC engine or "pkcs11" to select PKCS#11
+ * engine.
+ *
+ * This is used if private key operations for EAP-TLS are performed
+ * using a smartcard.
+ */
+ char *engine_id;
+
+ /**
+ * engine2 - Enable OpenSSL engine (e.g., for smartcard) (Phase 2)
+ *
+ * This is used if private key operations for EAP-TLS are performed
+ * using a smartcard.
+ *
+ * This field is like engine, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ */
+ int engine2;
+
+
+ /**
+ * pin2 - PIN for USIM, GSM SIM, and smartcards (Phase 2)
+ *
+ * This field is used to configure PIN for SIM and smartcards for
+ * EAP-SIM and EAP-AKA. In addition, this is used with EAP-TLS if a
+ * smartcard is used for private key operations.
+ *
+ * This field is like pin2, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ *
+ * If left out, this will be asked through control interface.
+ */
+ char *pin2;
+
+ /**
+ * engine2_id - Engine ID for OpenSSL engine (Phase 2)
+ *
+ * "opensc" to select OpenSC engine or "pkcs11" to select PKCS#11
+ * engine.
+ *
+ * This is used if private key operations for EAP-TLS are performed
+ * using a smartcard.
+ *
+ * This field is like engine_id, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ */
+ char *engine2_id;
+
+
+ /**
+ * key_id - Key ID for OpenSSL engine
+ *
+ * This is used if private key operations for EAP-TLS are performed
+ * using a smartcard.
+ */
+ char *key_id;
+
+ /**
+ * cert_id - Cert ID for OpenSSL engine
+ *
+ * This is used if the certificate operations for EAP-TLS are performed
+ * using a smartcard.
+ */
+ char *cert_id;
+
+ /**
+ * ca_cert_id - CA Cert ID for OpenSSL engine
+ *
+ * This is used if the CA certificate for EAP-TLS is on a smartcard.
+ */
+ char *ca_cert_id;
+
+ /**
+ * key2_id - Key ID for OpenSSL engine (phase2)
+ *
+ * This is used if private key operations for EAP-TLS are performed
+ * using a smartcard.
+ */
+ char *key2_id;
+
+ /**
+ * cert2_id - Cert ID for OpenSSL engine (phase2)
+ *
+ * This is used if the certificate operations for EAP-TLS are performed
+ * using a smartcard.
+ */
+ char *cert2_id;
+
+ /**
+ * ca_cert2_id - CA Cert ID for OpenSSL engine (phase2)
+ *
+ * This is used if the CA certificate for EAP-TLS is on a smartcard.
+ */
+ char *ca_cert2_id;
+
+ /**
+ * otp - One-time-password
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when OTP is entered through the control interface.
+ */
+ u8 *otp;
+
+ /**
+ * otp_len - Length of the otp field
+ */
+ size_t otp_len;
+
+ /**
+ * pending_req_identity - Whether there is a pending identity request
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when control interface is used to request needed
+ * information.
+ */
+ int pending_req_identity;
+
+ /**
+ * pending_req_password - Whether there is a pending password request
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when control interface is used to request needed
+ * information.
+ */
+ int pending_req_password;
+
+ /**
+ * pending_req_pin - Whether there is a pending PIN request
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when control interface is used to request needed
+ * information.
+ */
+ int pending_req_pin;
+
+ /**
+ * pending_req_new_password - Pending password update request
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when control interface is used to request needed
+ * information.
+ */
+ int pending_req_new_password;
+
+ /**
+ * pending_req_passphrase - Pending passphrase request
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when control interface is used to request needed
+ * information.
+ */
+ int pending_req_passphrase;
+
+ /**
+ * pending_req_otp - Whether there is a pending OTP request
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when control interface is used to request needed
+ * information.
+ */
+ char *pending_req_otp;
+
+ /**
+ * pending_req_otp_len - Length of the pending OTP request
+ */
+ size_t pending_req_otp_len;
+
+ /**
+ * pac_file - File path or blob name for the PAC entries (EAP-FAST)
+ *
+ * wpa_supplicant will need to be able to create this file and write
+ * updates to it when PAC is being provisioned or refreshed. Full path
+ * to the file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ char *pac_file;
+
+ /**
+ * mschapv2_retry - MSCHAPv2 retry in progress
+ *
+ * This field is used internally by EAP-MSCHAPv2 and should not be set
+ * as part of configuration.
+ */
+ int mschapv2_retry;
+
+ /**
+ * new_password - New password for password update
+ *
+ * This field is used during MSCHAPv2 password update. This is normally
+ * requested from the user through the control interface and not set
+ * from configuration.
+ */
+ u8 *new_password;
+
+ /**
+ * new_password_len - Length of new_password field
+ */
+ size_t new_password_len;
+
+ /**
+ * fragment_size - Maximum EAP fragment size in bytes (default 1398)
+ *
+ * This value limits the fragment size for EAP methods that support
+ * fragmentation (e.g., EAP-TLS and EAP-PEAP). This value should be set
+ * small enough to make the EAP messages fit in MTU of the network
+ * interface used for EAPOL. The default value is suitable for most
+ * cases.
+ */
+ int fragment_size;
+
+ /**
+ * chbind_config - eap channel binding config data
+ */
+ struct eap_peer_chbind_config *chbind_config;
+
+ /**
+ * chbind_config_len - channel binding config data count
+ */
+ size_t chbind_config_len;
+
+#define EAP_CONFIG_FLAGS_PASSWORD_NTHASH BIT(0)
++#define EAP_CONFIG_FLAGS_EXT_PASSWORD BIT(1)
+ /**
+ * flags - Network configuration flags (bitfield)
+ *
+ * This variable is used for internal flags to describe further details
+ * for the network parameters.
+ * bit 0 = password is represented as a 16-byte NtPasswordHash value
+ * instead of plaintext password
++ * bit 1 = password is stored in external storage; the value in the
++ * password field is the name of that external entry
+ */
+ u32 flags;
++
++ /**
++ * ocsp - Whether to use/require OCSP to check server certificate
++ *
++ * 0 = do not use OCSP stapling (TLS certificate status extension)
++ * 1 = try to use OCSP stapling, but not require response
++ * 2 = require valid OCSP stapling response
++ */
++ int ocsp;
++
++ /**
++ * external_sim_resp - Response from external SIM processing
++ *
++ * This field should not be set in configuration step. It is only used
++ * internally when control interface is used to request external
++ * SIM/USIM processing.
++ */
++ char *external_sim_resp;
++
++ /**
++ * sim_num - User selected SIM identifier
++ *
++ * This variable is used for identifying which SIM is used if the system
++ * has more than one.
++ */
++ int sim_num;
++
++ /**
++ * openssl_ciphers - OpenSSL cipher string
++ *
++ * This is an OpenSSL specific configuration option for configuring the
++ * ciphers for this connection. If not set, the default cipher suite
++ * list is used.
++ */
++ char *openssl_ciphers;
++
++ /**
++ * erp - Whether EAP Re-authentication Protocol (ERP) is enabled
++ */
++ int erp;
+};
+
+
+/**
+ * struct wpa_config_blob - Named configuration blob
+ *
+ * This data structure is used to provide storage for binary objects to store
+ * abstract information like certificates and private keys inlined with the
+ * configuration data.
+ */
+struct wpa_config_blob {
+ /**
+ * name - Blob name
+ */
+ char *name;
+
+ /**
+ * data - Pointer to binary data
+ */
+ u8 *data;
+
+ /**
+ * len - Length of binary data
+ */
+ size_t len;
+
+ /**
+ * next - Pointer to next blob in the configuration
+ */
+ struct wpa_config_blob *next;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EAP_CONFIG_H */
--- /dev/null
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+/*
+ * EAP peer state machines internal structures (RFC 4137)
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
++ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
+ *
- struct eapol_callbacks *eapol_cb;
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
+ */
+
+#ifndef EAP_I_H
+#define EAP_I_H
+
+#include "wpabuf.h"
++#include "utils/list.h"
+#include "eap_peer/eap.h"
+#include "eap_common/eap_common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* RFC 4137 - EAP Peer state machine */
+
+typedef enum {
+ DECISION_FAIL, DECISION_COND_SUCC, DECISION_UNCOND_SUCC
+} EapDecision;
+
+typedef enum {
+ METHOD_NONE, METHOD_INIT, METHOD_CONT, METHOD_MAY_CONT, METHOD_DONE
+} EapMethodState;
+
+/**
+ * struct eap_method_ret - EAP return values from struct eap_method::process()
+ *
+ * These structure contains OUT variables for the interface between peer state
+ * machine and methods (RFC 4137, Sect. 4.2). eapRespData will be returned as
+ * the return value of struct eap_method::process() so it is not included in
+ * this structure.
+ */
+struct eap_method_ret {
+ /**
+ * ignore - Whether method decided to drop the current packed (OUT)
+ */
+ Boolean ignore;
+
+ /**
+ * methodState - Method-specific state (IN/OUT)
+ */
+ EapMethodState methodState;
+
+ /**
+ * decision - Authentication decision (OUT)
+ */
+ EapDecision decision;
+
+ /**
+ * allowNotifications - Whether method allows notifications (OUT)
+ */
+ Boolean allowNotifications;
+};
+
+
+/**
+ * struct eap_method - EAP method interface
+ * This structure defines the EAP method interface. Each method will need to
+ * register its own EAP type, EAP name, and set of function pointers for method
+ * specific operations. This interface is based on section 4.4 of RFC 4137.
+ */
+struct eap_method {
+ /**
+ * vendor - EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF)
+ */
+ int vendor;
+
+ /**
+ * method - EAP type number (EAP_TYPE_*)
+ */
+ EapType method;
+
+ /**
+ * name - Name of the method (e.g., "TLS")
+ */
+ const char *name;
+
+ /**
+ * init - Initialize an EAP method
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: Pointer to allocated private data, or %NULL on failure
+ *
+ * This function is used to initialize the EAP method explicitly
+ * instead of using METHOD_INIT state as specific in RFC 4137. The
+ * method is expected to initialize it method-specific state and return
+ * a pointer that will be used as the priv argument to other calls.
+ */
+ void * (*init)(struct eap_sm *sm);
+
+ /**
+ * deinit - Deinitialize an EAP method
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ *
+ * Deinitialize the EAP method and free any allocated private data.
+ */
+ void (*deinit)(struct eap_sm *sm, void *priv);
+
+ /**
+ * process - Process an EAP request
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * @ret: Return values from EAP request validation and processing
+ * @reqData: EAP request to be processed (eapReqData)
+ * Returns: Pointer to allocated EAP response packet (eapRespData)
+ *
+ * This function is a combination of m.check(), m.process(), and
+ * m.buildResp() procedures defined in section 4.4 of RFC 4137 In other
+ * words, this function validates the incoming request, processes it,
+ * and build a response packet. m.check() and m.process() return values
+ * are returned through struct eap_method_ret *ret variable. Caller is
+ * responsible for freeing the returned EAP response packet.
+ */
+ struct wpabuf * (*process)(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData);
+
+ /**
+ * isKeyAvailable - Find out whether EAP method has keying material
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * Returns: %TRUE if key material (eapKeyData) is available
+ */
+ Boolean (*isKeyAvailable)(struct eap_sm *sm, void *priv);
+
+ /**
+ * getKey - Get EAP method specific keying material (eapKeyData)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * @len: Pointer to variable to store key length (eapKeyDataLen)
+ * Returns: Keying material (eapKeyData) or %NULL if not available
+ *
+ * This function can be used to get the keying material from the EAP
+ * method. The key may already be stored in the method-specific private
+ * data or this function may derive the key.
+ */
+ u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len);
+
+ /**
+ * get_status - Get EAP method status
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf
+ *
+ * Query EAP method for status information. This function fills in a
+ * text area with current status information from the EAP method. If
+ * the buffer (buf) is not large enough, status information will be
+ * truncated to fit the buffer.
+ */
+ int (*get_status)(struct eap_sm *sm, void *priv, char *buf,
+ size_t buflen, int verbose);
+
+ /**
+ * has_reauth_data - Whether method is ready for fast reauthentication
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * Returns: %TRUE or %FALSE based on whether fast reauthentication is
+ * possible
+ *
+ * This function is an optional handler that only EAP methods
+ * supporting fast re-authentication need to implement.
+ */
+ Boolean (*has_reauth_data)(struct eap_sm *sm, void *priv);
+
+ /**
+ * deinit_for_reauth - Release data that is not needed for fast re-auth
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ *
+ * This function is an optional handler that only EAP methods
+ * supporting fast re-authentication need to implement. This is called
+ * when authentication has been completed and EAP state machine is
+ * requesting that enough state information is maintained for fast
+ * re-authentication
+ */
+ void (*deinit_for_reauth)(struct eap_sm *sm, void *priv);
+
+ /**
+ * init_for_reauth - Prepare for start of fast re-authentication
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ *
+ * This function is an optional handler that only EAP methods
+ * supporting fast re-authentication need to implement. This is called
+ * when EAP authentication is started and EAP state machine is
+ * requesting fast re-authentication to be used.
+ */
+ void * (*init_for_reauth)(struct eap_sm *sm, void *priv);
+
+ /**
+ * get_identity - Get method specific identity for re-authentication
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * @len: Length of the returned identity
+ * Returns: Pointer to the method specific identity or %NULL if default
+ * identity is to be used
+ *
+ * This function is an optional handler that only EAP methods
+ * that use method specific identity need to implement.
+ */
+ const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len);
+
+ /**
+ * free - Free EAP method data
+ * @method: Pointer to the method data registered with
+ * eap_peer_method_register().
+ *
+ * This function will be called when the EAP method is being
+ * unregistered. If the EAP method allocated resources during
+ * registration (e.g., allocated struct eap_method), they should be
+ * freed in this function. No other method functions will be called
+ * after this call. If this function is not defined (i.e., function
+ * pointer is %NULL), a default handler is used to release the method
+ * data with free(method). This is suitable for most cases.
+ */
+ void (*free)(struct eap_method *method);
+
+#define EAP_PEER_METHOD_INTERFACE_VERSION 1
+ /**
+ * version - Version of the EAP peer method interface
+ *
+ * The EAP peer method implementation should set this variable to
+ * EAP_PEER_METHOD_INTERFACE_VERSION. This is used to verify that the
+ * EAP method is using supported API version when using dynamically
+ * loadable EAP methods.
+ */
+ int version;
+
+ /**
+ * next - Pointer to the next EAP method
+ *
+ * This variable is used internally in the EAP method registration code
+ * to create a linked list of registered EAP methods.
+ */
+ struct eap_method *next;
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+ /**
+ * dl_handle - Handle for the dynamic library
+ *
+ * This variable is used internally in the EAP method registration code
+ * to store a handle for the dynamic library. If the method is linked
+ * in statically, this is %NULL.
+ */
+ void *dl_handle;
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+ /**
+ * get_emsk - Get EAP method specific keying extended material (EMSK)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * @len: Pointer to a variable to store EMSK length
+ * Returns: EMSK or %NULL if not available
+ *
+ * This function can be used to get the extended keying material from
+ * the EAP method. The key may already be stored in the method-specific
+ * private data or this function may derive the key.
+ */
+ u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len);
++
++ /**
++ * getSessionId - Get EAP method specific Session-Id
++ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
++ * @priv: Pointer to private EAP method data from eap_method::init()
++ * @len: Pointer to a variable to store Session-Id length
++ * Returns: Session-Id or %NULL if not available
++ *
++ * This function can be used to get the Session-Id from the EAP method.
++ * The Session-Id may already be stored in the method-specific private
++ * data or this function may derive the Session-Id.
++ */
++ u8 * (*getSessionId)(struct eap_sm *sm, void *priv, size_t *len);
+};
+
+
++struct eap_erp_key {
++ struct dl_list list;
++ size_t rRK_len;
++ size_t rIK_len;
++ u8 rRK[ERP_MAX_KEY_LEN];
++ u8 rIK[ERP_MAX_KEY_LEN];
++ u32 next_seq;
++ char keyname_nai[];
++};
++
+/**
+ * struct eap_sm - EAP state machine data
+ */
+struct eap_sm {
+ enum {
+ EAP_INITIALIZE, EAP_DISABLED, EAP_IDLE, EAP_RECEIVED,
+ EAP_GET_METHOD, EAP_METHOD, EAP_SEND_RESPONSE, EAP_DISCARD,
+ EAP_IDENTITY, EAP_NOTIFICATION, EAP_RETRANSMIT, EAP_SUCCESS,
+ EAP_FAILURE
+ } EAP_state;
+ /* Long-term local variables */
+ EapType selectedMethod;
+ EapMethodState methodState;
+ int lastId;
+ struct wpabuf *lastRespData;
+ EapDecision decision;
+ /* Short-term local variables */
+ Boolean rxReq;
+ Boolean rxSuccess;
+ Boolean rxFailure;
+ int reqId;
+ EapType reqMethod;
+ int reqVendor;
+ u32 reqVendorMethod;
+ Boolean ignore;
+ /* Constants */
+ int ClientTimeout;
+
+ /* Miscellaneous variables */
+ Boolean allowNotifications; /* peer state machine <-> methods */
+ struct wpabuf *eapRespData; /* peer to lower layer */
+ Boolean eapKeyAvailable; /* peer to lower layer */
+ u8 *eapKeyData; /* peer to lower layer */
+ size_t eapKeyDataLen; /* peer to lower layer */
++ u8 *eapSessionId; /* peer to lower layer */
++ size_t eapSessionIdLen; /* peer to lower layer */
+ const struct eap_method *m; /* selected EAP method */
+ /* not defined in RFC 4137 */
+ Boolean changed;
+ void *eapol_ctx;
- u8 req_md5[16]; /* MD5() of the current EAP packet */
- u8 last_md5[16]; /* MD5() of the previously received EAP packet; used
- * in duplicate request detection. */
++ const struct eapol_callbacks *eapol_cb;
+ void *eap_method_priv;
+ int init_phase2;
+ int fast_reauth;
++ Boolean reauthInit; /* send EAP-Identity/Re-auth */
++ u32 erp_seq;
+
+ Boolean rxResp /* LEAP only */;
+ Boolean leap_done;
+ Boolean peap_done;
++ u8 req_sha1[20]; /* SHA1() of the current EAP packet */
++ u8 last_sha1[20]; /* SHA1() of the previously received EAP packet; used
++ * in duplicate request detection. */
+
+ void *msg_ctx;
+ void *scard_ctx;
+ void *ssl_ctx;
++ void *ssl_ctx2;
+
+ unsigned int workaround;
+
+ /* Optional challenges generated in Phase 1 (EAP-FAST) */
+ u8 *peer_challenge, *auth_challenge;
+
+ int num_rounds;
+ int force_disabled;
+
+ struct wps_context *wps;
+
+ int prev_failure;
++ struct eap_peer_config *last_config;
++
++ struct ext_password_data *ext_pw;
++ struct wpabuf *ext_pw_buf;
++
++ int external_sim;
++
++ unsigned int expected_failure:1;
++
++ struct dl_list erp_keys; /* struct eap_erp_key */
+};
+
+const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len);
+const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len);
+const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash);
+const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len);
+const u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len);
+void eap_clear_config_otp(struct eap_sm *sm);
+const char * eap_get_config_phase1(struct eap_sm *sm);
+const char * eap_get_config_phase2(struct eap_sm *sm);
+int eap_get_config_fragment_size(struct eap_sm *sm);
+struct eap_peer_config * eap_get_config(struct eap_sm *sm);
+void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob);
+const struct wpa_config_blob *
+eap_get_config_blob(struct eap_sm *sm, const char *name);
+void eap_notify_pending(struct eap_sm *sm);
+int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EAP_I_H */
--- /dev/null
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+/*
+ * EAP peer: Method registration
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
+ */
+
+#ifndef EAP_METHODS_H
+#define EAP_METHODS_H
+
+#include "eap_common/eap_defs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+const struct eap_method * eap_peer_get_eap_method(int vendor, EapType method);
+const struct eap_method * eap_peer_get_methods(size_t *count);
+
+struct eap_method * eap_peer_method_alloc(int version, int vendor,
+ EapType method, const char *name);
+void eap_peer_method_free(struct eap_method *method);
+int eap_peer_method_register(struct eap_method *method);
+
+
+#ifdef IEEE8021X_EAPOL
+
+EapType eap_peer_get_type(const char *name, int *vendor);
+const char * eap_get_name(int vendor, EapType type);
+size_t eap_get_names(char *buf, size_t buflen);
+char ** eap_get_names_as_string_array(size_t *num);
+void eap_peer_unregister_methods(void);
+
+#else /* IEEE8021X_EAPOL */
+
+static inline EapType eap_peer_get_type(const char *name, int *vendor)
+{
+ *vendor = EAP_VENDOR_IETF;
+ return EAP_TYPE_NONE;
+}
+
+static inline const char * eap_get_name(int vendor, EapType type)
+{
+ return NULL;
+}
+
+static inline size_t eap_get_names(char *buf, size_t buflen)
+{
+ return 0;
+}
+
+static inline int eap_peer_register_methods(void)
+{
+ return 0;
+}
+
+static inline void eap_peer_unregister_methods(void)
+{
+}
+
+static inline char ** eap_get_names_as_string_array(size_t *num)
+{
+ return NULL;
+}
+
+#endif /* IEEE8021X_EAPOL */
+
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+
+int eap_peer_method_load(const char *so);
+int eap_peer_method_unload(struct eap_method *method);
+
+#else /* CONFIG_DYNAMIC_EAP_METHODS */
+
+static inline int eap_peer_method_load(const char *so UNUSED)
+{
+ return 0;
+}
+
+static inline int eap_peer_method_unload(struct eap_method *method UNUSED)
+{
+ return 0;
+}
+
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+/* EAP peer method registration calls for statically linked in methods */
+int eap_peer_md5_register(void);
+int eap_peer_tls_register(void);
++int eap_peer_unauth_tls_register(void);
++int eap_peer_wfa_unauth_tls_register(void);
+int eap_peer_mschapv2_register(void);
+int eap_peer_peap_register(void);
+int eap_peer_ttls_register(void);
+int eap_peer_gtc_register(void);
+int eap_peer_otp_register(void);
+int eap_peer_sim_register(void);
+int eap_peer_leap_register(void);
+int eap_peer_psk_register(void);
+int eap_peer_aka_register(void);
+int eap_peer_aka_prime_register(void);
+int eap_peer_fast_register(void);
+int eap_peer_pax_register(void);
+int eap_peer_sake_register(void);
+int eap_peer_gpsk_register(void);
+int eap_peer_wsc_register(void);
+int eap_peer_ikev2_register(void);
+int eap_peer_vendor_test_register(void);
+int eap_peer_tnc_register(void);
+int eap_peer_pwd_register(void);
++int eap_peer_eke_register(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EAP_METHODS_H */
--- /dev/null
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+/*
+ * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
++ * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
+ *
- params->tls_ia = data->tls_ia;
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_config.h"
+
+
++static struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len,
++ u8 code, u8 identifier)
++{
++ if (type == EAP_UNAUTH_TLS_TYPE)
++ return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS,
++ EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len,
++ code, identifier);
++ if (type == EAP_WFA_UNAUTH_TLS_TYPE)
++ return eap_msg_alloc(EAP_VENDOR_WFA_NEW,
++ EAP_VENDOR_WFA_UNAUTH_TLS, payload_len,
++ code, identifier);
++ return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code,
++ identifier);
++}
++
++
+static int eap_tls_check_blob(struct eap_sm *sm, const char **name,
+ const u8 **data, size_t *data_len)
+{
+ const struct wpa_config_blob *blob;
+
+ if (*name == NULL || os_strncmp(*name, "blob://", 7) != 0)
+ return 0;
+
+ blob = eap_get_config_blob(sm, *name + 7);
+ if (blob == NULL) {
+ wpa_printf(MSG_ERROR, "%s: Named configuration blob '%s' not "
+ "found", __func__, *name + 7);
+ return -1;
+ }
+
+ *name = NULL;
+ *data = blob->data;
+ *data_len = blob->len;
+
+ return 0;
+}
+
+
+static void eap_tls_params_flags(struct tls_connection_params *params,
+ const char *txt)
+{
+ if (txt == NULL)
+ return;
+ if (os_strstr(txt, "tls_allow_md5=1"))
+ params->flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5;
+ if (os_strstr(txt, "tls_disable_time_checks=1"))
+ params->flags |= TLS_CONN_DISABLE_TIME_CHECKS;
++ if (os_strstr(txt, "tls_disable_session_ticket=1"))
++ params->flags |= TLS_CONN_DISABLE_SESSION_TICKET;
++ if (os_strstr(txt, "tls_disable_session_ticket=0"))
++ params->flags &= ~TLS_CONN_DISABLE_SESSION_TICKET;
++ if (os_strstr(txt, "tls_disable_tlsv1_0=1"))
++ params->flags |= TLS_CONN_DISABLE_TLSv1_0;
++ if (os_strstr(txt, "tls_disable_tlsv1_0=0"))
++ params->flags &= ~TLS_CONN_DISABLE_TLSv1_0;
++ if (os_strstr(txt, "tls_disable_tlsv1_1=1"))
++ params->flags |= TLS_CONN_DISABLE_TLSv1_1;
++ if (os_strstr(txt, "tls_disable_tlsv1_1=0"))
++ params->flags &= ~TLS_CONN_DISABLE_TLSv1_1;
++ if (os_strstr(txt, "tls_disable_tlsv1_2=1"))
++ params->flags |= TLS_CONN_DISABLE_TLSv1_2;
++ if (os_strstr(txt, "tls_disable_tlsv1_2=0"))
++ params->flags &= ~TLS_CONN_DISABLE_TLSv1_2;
+}
+
+
+static void eap_tls_params_from_conf1(struct tls_connection_params *params,
+ struct eap_peer_config *config)
+{
+ params->ca_cert = (char *) config->ca_cert;
+ params->ca_path = (char *) config->ca_path;
+ params->client_cert = (char *) config->client_cert;
+ params->private_key = (char *) config->private_key;
+ params->private_key_passwd = (char *) config->private_key_passwd;
+ params->dh_file = (char *) config->dh_file;
+ params->subject_match = (char *) config->subject_match;
+ params->altsubject_match = (char *) config->altsubject_match;
++ params->suffix_match = config->domain_suffix_match;
++ params->domain_match = config->domain_match;
+ params->engine = config->engine;
+ params->engine_id = config->engine_id;
+ params->pin = config->pin;
+ params->key_id = config->key_id;
+ params->cert_id = config->cert_id;
+ params->ca_cert_id = config->ca_cert_id;
+ eap_tls_params_flags(params, config->phase1);
+}
+
+
+static void eap_tls_params_from_conf2(struct tls_connection_params *params,
+ struct eap_peer_config *config)
+{
+ params->ca_cert = (char *) config->ca_cert2;
+ params->ca_path = (char *) config->ca_path2;
+ params->client_cert = (char *) config->client_cert2;
+ params->private_key = (char *) config->private_key2;
+ params->private_key_passwd = (char *) config->private_key2_passwd;
+ params->dh_file = (char *) config->dh_file2;
+ params->subject_match = (char *) config->subject_match2;
+ params->altsubject_match = (char *) config->altsubject_match2;
++ params->suffix_match = config->domain_suffix_match2;
++ params->domain_match = config->domain_match2;
+ params->engine = config->engine2;
+ params->engine_id = config->engine2_id;
+ params->pin = config->pin2;
+ params->key_id = config->key2_id;
+ params->cert_id = config->cert2_id;
+ params->ca_cert_id = config->ca_cert2_id;
+ eap_tls_params_flags(params, config->phase2);
+}
+
+
+static int eap_tls_params_from_conf(struct eap_sm *sm,
+ struct eap_ssl_data *data,
+ struct tls_connection_params *params,
+ struct eap_peer_config *config, int phase2)
+{
+ os_memset(params, 0, sizeof(*params));
++ if (sm->workaround && data->eap_type != EAP_TYPE_FAST) {
++ /*
++ * Some deployed authentication servers seem to be unable to
++ * handle the TLS Session Ticket extension (they are supposed
++ * to ignore unrecognized TLS extensions, but end up rejecting
++ * the ClientHello instead). As a workaround, disable use of
++ * TLS Sesson Ticket extension for EAP-TLS, EAP-PEAP, and
++ * EAP-TTLS (EAP-FAST uses session ticket, so any server that
++ * supports EAP-FAST does not need this workaround).
++ */
++ params->flags |= TLS_CONN_DISABLE_SESSION_TICKET;
++ }
+ if (phase2) {
+ wpa_printf(MSG_DEBUG, "TLS: using phase2 config options");
+ eap_tls_params_from_conf2(params, config);
+ } else {
+ wpa_printf(MSG_DEBUG, "TLS: using phase1 config options");
+ eap_tls_params_from_conf1(params, config);
++ if (data->eap_type == EAP_TYPE_FAST)
++ params->flags |= TLS_CONN_EAP_FAST;
+ }
- data->conn = tls_connection_init(sm->ssl_ctx);
+
+ /*
+ * Use blob data, if available. Otherwise, leave reference to external
+ * file as-is.
+ */
+ if (eap_tls_check_blob(sm, ¶ms->ca_cert, ¶ms->ca_cert_blob,
+ ¶ms->ca_cert_blob_len) ||
+ eap_tls_check_blob(sm, ¶ms->client_cert,
+ ¶ms->client_cert_blob,
+ ¶ms->client_cert_blob_len) ||
+ eap_tls_check_blob(sm, ¶ms->private_key,
+ ¶ms->private_key_blob,
+ ¶ms->private_key_blob_len) ||
+ eap_tls_check_blob(sm, ¶ms->dh_file, ¶ms->dh_blob,
+ ¶ms->dh_blob_len)) {
+ wpa_printf(MSG_INFO, "SSL: Failed to get configuration blobs");
+ return -1;
+ }
+
++ params->openssl_ciphers = config->openssl_ciphers;
++
+ return 0;
+}
+
+
+static int eap_tls_init_connection(struct eap_sm *sm,
+ struct eap_ssl_data *data,
+ struct eap_peer_config *config,
+ struct tls_connection_params *params)
+{
+ int res;
+
- res = tls_connection_set_params(sm->ssl_ctx, data->conn, params);
- if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) {
++ if (config->ocsp)
++ params->flags |= TLS_CONN_REQUEST_OCSP;
++ if (config->ocsp == 2)
++ params->flags |= TLS_CONN_REQUIRE_OCSP;
++ data->conn = tls_connection_init(data->ssl_ctx);
+ if (data->conn == NULL) {
+ wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS "
+ "connection");
+ return -1;
+ }
+
- * At this point with the pkcs11 engine the PIN might be wrong.
- * We reset the PIN in the configuration to be sure to not use
- * it again and the calling function must request a new one.
++ res = tls_connection_set_params(data->ssl_ctx, data->conn, params);
++ if (res == TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN) {
+ /*
- /*
- * We do not know exactly but maybe the PIN was wrong,
- * so ask for a new one.
- */
- os_free(config->pin);
- config->pin = NULL;
- eap_sm_request_pin(sm);
++ * At this point with the pkcs11 engine the PIN is wrong. We
++ * reset the PIN in the configuration to be sure to not use it
++ * again and the calling function must request a new one.
+ */
++ wpa_printf(MSG_INFO,
++ "TLS: Bad PIN provided, requesting a new one");
+ os_free(config->pin);
+ config->pin = NULL;
++ eap_sm_request_pin(sm);
++ sm->ignore = TRUE;
++ } else if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) {
++ wpa_printf(MSG_INFO, "TLS: Failed to initialize engine");
+ } else if (res == TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED) {
+ wpa_printf(MSG_INFO, "TLS: Failed to load private key");
- tls_connection_deinit(sm->ssl_ctx, data->conn);
- data->conn = NULL;
- return -1;
- } else if (res) {
+ sm->ignore = TRUE;
- tls_connection_deinit(sm->ssl_ctx, data->conn);
++ }
++ if (res) {
+ wpa_printf(MSG_INFO, "TLS: Failed to set TLS connection "
+ "parameters");
- struct eap_peer_config *config)
++ tls_connection_deinit(data->ssl_ctx, data->conn);
+ data->conn = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * eap_peer_tls_ssl_init - Initialize shared TLS functionality
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @config: Pointer to the network configuration
++ * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to initialize shared TLS functionality for EAP-TLS,
+ * EAP-PEAP, EAP-TTLS, and EAP-FAST.
+ */
+int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
- tls_connection_deinit(sm->ssl_ctx, data->conn);
++ struct eap_peer_config *config, u8 eap_type)
+{
+ struct tls_connection_params params;
+
+ if (config == NULL)
+ return -1;
+
+ data->eap = sm;
++ data->eap_type = eap_type;
+ data->phase2 = sm->init_phase2;
++ data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 :
++ sm->ssl_ctx;
+ if (eap_tls_params_from_conf(sm, data, ¶ms, config, data->phase2) <
+ 0)
+ return -1;
+
+ if (eap_tls_init_connection(sm, data, config, ¶ms) < 0)
+ return -1;
+
+ data->tls_out_limit = config->fragment_size;
+ if (data->phase2) {
+ /* Limit the fragment size in the inner TLS authentication
+ * since the outer authentication with EAP-PEAP does not yet
+ * support fragmentation */
+ if (data->tls_out_limit > 100)
+ data->tls_out_limit -= 100;
+ }
+
+ if (config->phase1 &&
+ os_strstr(config->phase1, "include_tls_length=1")) {
+ wpa_printf(MSG_DEBUG, "TLS: Include TLS Message Length in "
+ "unfragmented packets");
+ data->include_tls_length = 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * eap_peer_tls_ssl_deinit - Deinitialize shared TLS functionality
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ *
+ * This function deinitializes shared TLS functionality that was initialized
+ * with eap_peer_tls_ssl_init().
+ */
+void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
+{
- struct tls_keys keys;
- u8 *rnd = NULL, *out;
++ tls_connection_deinit(data->ssl_ctx, data->conn);
+ eap_peer_tls_reset_input(data);
+ eap_peer_tls_reset_output(data);
+}
+
+
+/**
+ * eap_peer_tls_derive_key - Derive a key based on TLS session data
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @label: Label string for deriving the keys, e.g., "client EAP encryption"
+ * @len: Length of the key material to generate (usually 64 for MSK)
+ * Returns: Pointer to allocated key on success or %NULL on failure
+ *
+ * This function uses TLS-PRF to generate pseudo-random data based on the TLS
+ * session data (client/server random and master key). Each key type may use a
+ * different label to bind the key usage into the generated material.
+ *
+ * The caller is responsible for freeing the returned buffer.
+ */
+u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
+ const char *label, size_t len)
+{
- /* First, try to use TLS library function for PRF, if available. */
- if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, out, len) ==
- 0)
- return out;
++ u8 *out;
+
+ out = os_malloc(len);
+ if (out == NULL)
+ return NULL;
+
- /*
- * TLS library did not support key generation, so get the needed TLS
- * session parameters and use an internal implementation of TLS PRF to
- * derive the key.
- */
- if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
- goto fail;
-
- if (keys.client_random == NULL || keys.server_random == NULL ||
- keys.master_key == NULL)
- goto fail;
-
- rnd = os_malloc(keys.client_random_len + keys.server_random_len);
- if (rnd == NULL)
- goto fail;
- os_memcpy(rnd, keys.client_random, keys.client_random_len);
- os_memcpy(rnd + keys.client_random_len, keys.server_random,
- keys.server_random_len);
++ if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, 0,
++ out, len)) {
++ os_free(out);
++ return NULL;
++ }
+
- if (tls_prf(keys.master_key, keys.master_key_len,
- label, rnd, keys.client_random_len +
- keys.server_random_len, out, len))
- goto fail;
++ return out;
++}
+
- os_free(rnd);
- return out;
+
- fail:
- os_free(out);
- os_free(rnd);
- return NULL;
++/**
++ * eap_peer_tls_derive_session_id - Derive a Session-Id based on TLS data
++ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
++ * @data: Data for TLS processing
++ * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
++ * @len: Pointer to length of the session ID generated
++ * Returns: Pointer to allocated Session-Id on success or %NULL on failure
++ *
++ * This function derive the Session-Id based on the TLS session data
++ * (client/server random and method type).
++ *
++ * The caller is responsible for freeing the returned buffer.
++ */
++u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm,
++ struct eap_ssl_data *data, u8 eap_type,
++ size_t *len)
++{
++ struct tls_random keys;
++ u8 *out;
++
++ if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys))
++ return NULL;
++
++ if (keys.client_random == NULL || keys.server_random == NULL)
++ return NULL;
++
++ *len = 1 + keys.client_random_len + keys.server_random_len;
++ out = os_malloc(*len);
++ if (out == NULL)
++ return NULL;
++
++ /* Session-Id = EAP type || client.random || server.random */
++ out[0] = eap_type;
++ os_memcpy(out + 1, keys.client_random, keys.client_random_len);
++ os_memcpy(out + 1 + keys.client_random_len, keys.server_random,
++ keys.server_random_len);
+
- wpabuf_put_buf(data->tls_in, in_data);
++ return out;
+}
+
+
+/**
+ * eap_peer_tls_reassemble_fragment - Reassemble a received fragment
+ * @data: Data for TLS processing
+ * @in_data: Next incoming TLS segment
+ * Returns: 0 on success, 1 if more data is needed for the full message, or
+ * -1 on error
+ */
+static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data,
+ const struct wpabuf *in_data)
+{
+ size_t tls_in_len, in_len;
+
+ tls_in_len = data->tls_in ? wpabuf_len(data->tls_in) : 0;
+ in_len = in_data ? wpabuf_len(in_data) : 0;
+
+ if (tls_in_len + in_len == 0) {
+ /* No message data received?! */
+ wpa_printf(MSG_WARNING, "SSL: Invalid reassembly state: "
+ "tls_in_left=%lu tls_in_len=%lu in_len=%lu",
+ (unsigned long) data->tls_in_left,
+ (unsigned long) tls_in_len,
+ (unsigned long) in_len);
+ eap_peer_tls_reset_input(data);
+ return -1;
+ }
+
+ if (tls_in_len + in_len > 65536) {
+ /*
+ * Limit length to avoid rogue servers from causing large
+ * memory allocations.
+ */
+ wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size over "
+ "64 kB)");
+ eap_peer_tls_reset_input(data);
+ return -1;
+ }
+
+ if (in_len > data->tls_in_left) {
+ /* Sender is doing something odd - reject message */
+ wpa_printf(MSG_INFO, "SSL: more data than TLS message length "
+ "indicated");
+ eap_peer_tls_reset_input(data);
+ return -1;
+ }
+
+ if (wpabuf_resize(&data->tls_in, in_len) < 0) {
+ wpa_printf(MSG_INFO, "SSL: Could not allocate memory for TLS "
+ "data");
+ eap_peer_tls_reset_input(data);
+ return -1;
+ }
- * @in_len: Length of in_data
++ if (in_data)
++ wpabuf_put_buf(data->tls_in, in_data);
+ data->tls_in_left -= in_len;
+
+ if (data->tls_in_left > 0) {
+ wpa_printf(MSG_DEBUG, "SSL: Need %lu bytes more input "
+ "data", (unsigned long) data->tls_in_left);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * eap_peer_tls_data_reassemble - Reassemble TLS data
+ * @data: Data for TLS processing
+ * @in_data: Next incoming TLS segment
+ * @need_more_input: Variable for returning whether more input data is needed
+ * to reassemble this TLS packet
+ * Returns: Pointer to output data, %NULL on error or when more data is needed
+ * for the full message (in which case, *need_more_input is also set to 1).
+ *
+ * This function reassembles TLS fragments. Caller must not free the returned
+ * data buffer since an internal pointer to it is maintained.
+ */
+static const struct wpabuf * eap_peer_tls_data_reassemble(
+ struct eap_ssl_data *data, const struct wpabuf *in_data,
+ int *need_more_input)
+{
+ *need_more_input = 0;
+
+ if (data->tls_in_left > wpabuf_len(in_data) || data->tls_in) {
+ /* Message has fragments */
+ int res = eap_peer_tls_reassemble_fragment(data, in_data);
+ if (res) {
+ if (res == 1)
+ *need_more_input = 1;
+ return NULL;
+ }
+
+ /* Message is now fully reassembled. */
+ } else {
+ /* No fragments in this message, so just make a copy of it. */
+ data->tls_in_left = 0;
+ data->tls_in = wpabuf_dup(in_data);
+ if (data->tls_in == NULL)
+ return NULL;
+ }
+
+ return data->tls_in;
+}
+
+
+/**
+ * eap_tls_process_input - Process incoming TLS message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @in_data: Message received from the server
- const u8 *in_data, size_t in_len,
+ * @out_data: Buffer for returning a pointer to application data (if available)
+ * Returns: 0 on success, 1 if more input data is needed, 2 if application data
+ * is available, -1 on failure
+ */
+static int eap_tls_process_input(struct eap_sm *sm, struct eap_ssl_data *data,
- struct wpabuf buf;
++ const struct wpabuf *in_data,
+ struct wpabuf **out_data)
+{
+ const struct wpabuf *msg;
+ int need_more_input;
+ struct wpabuf *appl_data;
- wpabuf_set(&buf, in_data, in_len);
- msg = eap_peer_tls_data_reassemble(data, &buf, &need_more_input);
+
- data->tls_out = tls_connection_handshake(sm->ssl_ctx, data->conn,
++ msg = eap_peer_tls_data_reassemble(data, in_data, &need_more_input);
+ if (msg == NULL)
+ return need_more_input ? 1 : -1;
+
+ /* Full TLS message reassembled - continue handshake processing */
+ if (data->tls_out) {
+ /* This should not happen.. */
+ wpa_printf(MSG_INFO, "SSL: eap_tls_process_input - pending "
+ "tls_out data even though tls_out_len = 0");
+ wpabuf_free(data->tls_out);
+ WPA_ASSERT(data->tls_out == NULL);
+ }
+ appl_data = NULL;
- tls_connection_established(sm->ssl_ctx, data->conn) &&
- !tls_connection_get_failed(sm->ssl_ctx, data->conn)) {
++ data->tls_out = tls_connection_handshake(data->ssl_ctx, data->conn,
+ msg, &appl_data);
+
+ eap_peer_tls_reset_input(data);
+
+ if (appl_data &&
- *out_data = eap_msg_alloc(EAP_VENDOR_IETF, eap_type,
- 1 + length_included * 4 + len,
- EAP_CODE_RESPONSE, id);
++ tls_connection_established(data->ssl_ctx, data->conn) &&
++ !tls_connection_get_failed(data->ssl_ctx, data->conn)) {
+ wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application data",
+ appl_data);
+ *out_data = appl_data;
+ return 2;
+ }
+
+ wpabuf_free(appl_data);
+
+ return 0;
+}
+
+
+/**
+ * eap_tls_process_output - Process outgoing TLS message
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * @id: EAP identifier for the response
+ * @ret: Return value to use on success
+ * @out_data: Buffer for returning the allocated output buffer
+ * Returns: ret (0 or 1) on success, -1 on failure
+ */
+static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type,
+ int peap_version, u8 id, int ret,
+ struct wpabuf **out_data)
+{
+ size_t len;
+ u8 *flags;
+ int more_fragments, length_included;
+
+ if (data->tls_out == NULL)
+ return -1;
+ len = wpabuf_len(data->tls_out) - data->tls_out_pos;
+ wpa_printf(MSG_DEBUG, "SSL: %lu bytes left to be sent out (of total "
+ "%lu bytes)",
+ (unsigned long) len,
+ (unsigned long) wpabuf_len(data->tls_out));
+
+ /*
+ * Limit outgoing message to the configured maximum size. Fragment
+ * message if needed.
+ */
+ if (len > data->tls_out_limit) {
+ more_fragments = 1;
+ len = data->tls_out_limit;
+ wpa_printf(MSG_DEBUG, "SSL: sending %lu bytes, more fragments "
+ "will follow", (unsigned long) len);
+ } else
+ more_fragments = 0;
+
+ length_included = data->tls_out_pos == 0 &&
+ (wpabuf_len(data->tls_out) > data->tls_out_limit ||
+ data->include_tls_length);
+ if (!length_included &&
+ eap_type == EAP_TYPE_PEAP && peap_version == 0 &&
+ !tls_connection_established(data->eap->ssl_ctx, data->conn)) {
+ /*
+ * Windows Server 2008 NPS really wants to have the TLS Message
+ * length included in phase 0 even for unfragmented frames or
+ * it will get very confused with Compound MAC calculation and
+ * Outer TLVs.
+ */
+ length_included = 1;
+ }
+
- * @in_len: Length of in_data
++ *out_data = eap_tls_msg_alloc(eap_type, 1 + length_included * 4 + len,
++ EAP_CODE_RESPONSE, id);
+ if (*out_data == NULL)
+ return -1;
+
+ flags = wpabuf_put(*out_data, 1);
+ *flags = peap_version;
+ if (more_fragments)
+ *flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS;
+ if (length_included) {
+ *flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED;
+ wpabuf_put_be32(*out_data, wpabuf_len(data->tls_out));
+ }
+
+ wpabuf_put_data(*out_data,
+ wpabuf_head_u8(data->tls_out) + data->tls_out_pos,
+ len);
+ data->tls_out_pos += len;
+
+ if (!more_fragments)
+ eap_peer_tls_reset_output(data);
+
+ return ret;
+}
+
+
+/**
+ * eap_peer_tls_process_helper - Process TLS handshake message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * @id: EAP identifier for the response
+ * @in_data: Message received from the server
- u8 id, const u8 *in_data, size_t in_len,
+ * @out_data: Buffer for returning a pointer to the response message
+ * Returns: 0 on success, 1 if more input data is needed, 2 if application data
+ * is available, or -1 on failure
+ *
+ * This function can be used to process TLS handshake messages. It reassembles
+ * the received fragments and uses a TLS library to process the messages. The
+ * response data from the TLS library is fragmented to suitable output messages
+ * that the caller can send out.
+ *
+ * out_data is used to return the response message if the return value of this
+ * function is 0, 2, or -1. In case of failure, the message is likely a TLS
+ * alarm message. The caller is responsible for freeing the allocated buffer if
+ * *out_data is not %NULL.
+ *
+ * This function is called for each received TLS message during the TLS
+ * handshake after eap_peer_tls_process_init() call and possible processing of
+ * TLS Flags field. Once the handshake has been completed, i.e., when
+ * tls_connection_established() returns 1, EAP method specific decrypting of
+ * the tunneled data is used.
+ */
+int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
+ EapType eap_type, int peap_version,
- if (data->tls_out && wpabuf_len(data->tls_out) > 0 && in_len > 0) {
++ u8 id, const struct wpabuf *in_data,
+ struct wpabuf **out_data)
+{
+ int ret = 0;
+
+ *out_data = NULL;
+
- int res = eap_tls_process_input(sm, data, in_data, in_len,
- out_data);
++ if (data->tls_out && wpabuf_len(data->tls_out) > 0 &&
++ wpabuf_len(in_data) > 0) {
+ wpa_printf(MSG_DEBUG, "SSL: Received non-ACK when output "
+ "fragments are waiting to be sent out");
+ return -1;
+ }
+
+ if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) {
+ /*
+ * No more data to send out - expect to receive more data from
+ * the AS.
+ */
- if (tls_connection_get_failed(sm->ssl_ctx, data->conn)) {
++ int res = eap_tls_process_input(sm, data, in_data, out_data);
+ if (res) {
+ /*
+ * Input processing failed (res = -1) or more data is
+ * needed (res = 1).
+ */
+ return res;
+ }
+
+ /*
+ * The incoming message has been reassembled and processed. The
+ * response was allocated into data->tls_out buffer.
+ */
+ }
+
+ if (data->tls_out == NULL) {
+ /*
+ * No outgoing fragments remaining from the previous message
+ * and no new message generated. This indicates an error in TLS
+ * processing.
+ */
+ eap_peer_tls_reset_output(data);
+ return -1;
+ }
+
- "report error");
++ if (tls_connection_get_failed(data->ssl_ctx, data->conn)) {
+ /* TLS processing has failed - return error */
+ wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to "
- if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) {
++ "report error (len=%u)",
++ (unsigned int) wpabuf_len(data->tls_out));
+ ret = -1;
+ /* TODO: clean pin if engine used? */
++ if (wpabuf_len(data->tls_out) == 0) {
++ wpabuf_free(data->tls_out);
++ data->tls_out = NULL;
++ return -1;
++ }
+ }
+
- resp = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, 1, EAP_CODE_RESPONSE,
- id);
++ if (wpabuf_len(data->tls_out) == 0) {
+ /*
+ * TLS negotiation should now be complete since all other cases
+ * needing more data should have been caught above based on
+ * the TLS Message Length field.
+ */
+ wpa_printf(MSG_DEBUG, "SSL: No data to be sent out");
+ wpabuf_free(data->tls_out);
+ data->tls_out = NULL;
+ return 1;
+ }
+
+ /* Send the pending message (in fragments, if needed). */
+ return eap_tls_process_output(data, eap_type, peap_version, id, ret,
+ out_data);
+}
+
+
+/**
+ * eap_peer_tls_build_ack - Build a TLS ACK frame
+ * @id: EAP identifier for the response
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * Returns: Pointer to the allocated ACK frame or %NULL on failure
+ */
+struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type,
+ int peap_version)
+{
+ struct wpabuf *resp;
+
- return tls_connection_shutdown(sm->ssl_ctx, data->conn);
++ resp = eap_tls_msg_alloc(eap_type, 1, EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+ wpa_printf(MSG_DEBUG, "SSL: Building ACK (type=%d id=%d ver=%d)",
+ (int) eap_type, id, peap_version);
+ wpabuf_put_u8(resp, peap_version); /* Flags */
+ return resp;
+}
+
+
+/**
+ * eap_peer_tls_reauth_init - Re-initialize shared TLS for session resumption
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data)
+{
+ eap_peer_tls_reset_input(data);
+ eap_peer_tls_reset_output(data);
- char name[128];
++ return tls_connection_shutdown(data->ssl_ctx, data->conn);
+}
+
+
+/**
+ * eap_peer_tls_status - Get TLS status
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf.
+ */
+int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data,
+ char *buf, size_t buflen, int verbose)
+{
- if (tls_get_cipher(sm->ssl_ctx, data->conn, name, sizeof(name)) == 0) {
- ret = os_snprintf(buf + len, buflen - len,
- "EAP TLS cipher=%s\n", name);
- if (ret < 0 || (size_t) ret >= buflen - len)
- return len;
- len += ret;
- }
++ char version[20], name[128];
+ int len = 0, ret;
+
- pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData, &left);
++ if (tls_get_version(data->ssl_ctx, data->conn, version,
++ sizeof(version)) < 0)
++ version[0] = '\0';
++ if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) < 0)
++ name[0] = '\0';
++
++ ret = os_snprintf(buf + len, buflen - len,
++ "eap_tls_version=%s\n"
++ "EAP TLS cipher=%s\n"
++ "tls_session_reused=%d\n",
++ version, name,
++ tls_connection_resumed(data->ssl_ctx, data->conn));
++ if (os_snprintf_error(buflen - len, ret))
++ return len;
++ len += ret;
+
+ return len;
+}
+
+
+/**
+ * eap_peer_tls_process_init - Initial validation/processing of EAP requests
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @ret: Return values from EAP request validation and processing
+ * @reqData: EAP request to be processed (eapReqData)
+ * @len: Buffer for returning length of the remaining payload
+ * @flags: Buffer for returning TLS flags
+ * Returns: Pointer to payload after TLS flags and length or %NULL on failure
+ *
+ * This function validates the EAP header and processes the optional TLS
+ * Message Length field. If this is the first fragment of a TLS message, the
+ * TLS reassembly code is initialized to receive the indicated number of bytes.
+ *
+ * EAP-TLS, EAP-PEAP, EAP-TTLS, and EAP-FAST methods are expected to use this
+ * function as the first step in processing received messages. They will need
+ * to process the flags (apart from Message Length Included) that are returned
+ * through the flags pointer and the message payload that will be returned (and
+ * the length is returned through the len pointer). Return values (ret) are set
+ * for continuation of EAP method processing. The caller is responsible for
+ * setting these to indicate completion (either success or failure) based on
+ * the authentication result.
+ */
+const u8 * eap_peer_tls_process_init(struct eap_sm *sm,
+ struct eap_ssl_data *data,
+ EapType eap_type,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData,
+ size_t *len, u8 *flags)
+{
+ const u8 *pos;
+ size_t left;
+ unsigned int tls_msg_len;
+
+ /* Ignore errors before we do anything*/
+ (void) tls_get_errors(sm->ssl_ctx);
- *in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->conn, msg);
++
++ //// if (tls_get_errors(data->ssl_ctx)) {
++ //// wpa_printf(MSG_INFO, "SSL: TLS errors detected");
++ //// ret->ignore = TRUE;
++ //// return NULL;
++ //// }
++
++ if (eap_type == EAP_UNAUTH_TLS_TYPE)
++ pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
++ EAP_VENDOR_TYPE_UNAUTH_TLS, reqData,
++ &left);
++ else if (eap_type == EAP_WFA_UNAUTH_TLS_TYPE)
++ pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW,
++ EAP_VENDOR_WFA_UNAUTH_TLS, reqData,
++ &left);
++ else
++ pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData,
++ &left);
+ if (pos == NULL) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ if (left == 0) {
+ wpa_printf(MSG_DEBUG, "SSL: Invalid TLS message: no Flags "
+ "octet included");
+ if (!sm->workaround) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "SSL: Workaround - assume no Flags "
+ "indicates ACK frame");
+ *flags = 0;
+ } else {
+ *flags = *pos++;
+ left--;
+ }
+ wpa_printf(MSG_DEBUG, "SSL: Received packet(len=%lu) - "
+ "Flags 0x%02x", (unsigned long) wpabuf_len(reqData),
+ *flags);
+ if (*flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
+ if (left < 4) {
+ wpa_printf(MSG_INFO, "SSL: Short frame with TLS "
+ "length");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ tls_msg_len = WPA_GET_BE32(pos);
+ wpa_printf(MSG_DEBUG, "SSL: TLS Message Length: %d",
+ tls_msg_len);
+ if (data->tls_in_left == 0) {
+ data->tls_in_total = tls_msg_len;
+ data->tls_in_left = tls_msg_len;
+ wpabuf_free(data->tls_in);
+ data->tls_in = NULL;
+ }
+ pos += 4;
+ left -= 4;
++
++ if (left > tls_msg_len) {
++ wpa_printf(MSG_INFO, "SSL: TLS Message Length (%d "
++ "bytes) smaller than this fragment (%d "
++ "bytes)", (int) tls_msg_len, (int) left);
++ ret->ignore = TRUE;
++ return NULL;
++ }
+ }
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = TRUE;
+
+ *len = left;
+ return pos;
+}
+
+
+/**
+ * eap_peer_tls_reset_input - Reset input buffers
+ * @data: Data for TLS processing
+ *
+ * This function frees any allocated memory for input buffers and resets input
+ * state.
+ */
+void eap_peer_tls_reset_input(struct eap_ssl_data *data)
+{
+ data->tls_in_left = data->tls_in_total = 0;
+ wpabuf_free(data->tls_in);
+ data->tls_in = NULL;
+}
+
+
+/**
+ * eap_peer_tls_reset_output - Reset output buffers
+ * @data: Data for TLS processing
+ *
+ * This function frees any allocated memory for output buffers and resets
+ * output state.
+ */
+void eap_peer_tls_reset_output(struct eap_ssl_data *data)
+{
+ data->tls_out_pos = 0;
+ wpabuf_free(data->tls_out);
+ data->tls_out = NULL;
+}
+
+
+/**
+ * eap_peer_tls_decrypt - Decrypt received phase 2 TLS message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @in_data: Message received from the server
+ * @in_decrypted: Buffer for returning a pointer to the decrypted message
+ * Returns: 0 on success, 1 if more input data is needed, or -1 on failure
+ */
+int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data,
+ const struct wpabuf *in_data,
+ struct wpabuf **in_decrypted)
+{
+ const struct wpabuf *msg;
+ int need_more_input;
+
+ msg = eap_peer_tls_data_reassemble(data, in_data, &need_more_input);
+ if (msg == NULL)
+ return need_more_input ? 1 : -1;
+
- data->tls_out = tls_connection_encrypt(sm->ssl_ctx, data->conn,
- in_data);
++ *in_decrypted = tls_connection_decrypt(data->ssl_ctx, data->conn, msg);
+ eap_peer_tls_reset_input(data);
+ if (*in_decrypted == NULL) {
+ wpa_printf(MSG_INFO, "SSL: Failed to decrypt Phase 2 data");
+ return -1;
+ }
+ return 0;
+}
+
+
+/**
+ * eap_peer_tls_encrypt - Encrypt phase 2 TLS message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * @id: EAP identifier for the response
+ * @in_data: Plaintext phase 2 data to encrypt or %NULL to continue fragments
+ * @out_data: Buffer for returning a pointer to the encrypted response message
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data,
+ EapType eap_type, int peap_version, u8 id,
+ const struct wpabuf *in_data,
+ struct wpabuf **out_data)
+{
+ if (in_data) {
+ eap_peer_tls_reset_output(data);
- u8 method;
++ data->tls_out = tls_connection_encrypt(data->ssl_ctx,
++ data->conn, in_data);
+ if (data->tls_out == NULL) {
+ wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 "
+ "data (in_len=%lu)",
+ (unsigned long) wpabuf_len(in_data));
+ eap_peer_tls_reset_output(data);
+ return -1;
+ }
+ }
+
+ return eap_tls_process_output(data, eap_type, peap_version, id, 0,
+ out_data);
+}
+
+
+/**
+ * eap_peer_select_phase2_methods - Select phase 2 EAP method
+ * @config: Pointer to the network configuration
+ * @prefix: 'phase2' configuration prefix, e.g., "auth="
+ * @types: Buffer for returning allocated list of allowed EAP methods
+ * @num_types: Buffer for returning number of allocated EAP methods
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to parse EAP method list and select allowed methods
+ * for Phase2 authentication.
+ */
+int eap_peer_select_phase2_methods(struct eap_peer_config *config,
+ const char *prefix,
+ struct eap_method_type **types,
+ size_t *num_types)
+{
+ char *start, *pos, *buf;
+ struct eap_method_type *methods = NULL, *_methods;
- _methods = os_realloc(methods,
- num_methods * sizeof(*methods));
++ u32 method;
+ size_t num_methods = 0, prefix_len;
+
+ if (config == NULL || config->phase2 == NULL)
+ goto get_defaults;
+
+ start = buf = os_strdup(config->phase2);
+ if (buf == NULL)
+ return -1;
+
+ prefix_len = os_strlen(prefix);
+
+ while (start && *start != '\0') {
+ int vendor;
+ pos = os_strstr(start, prefix);
+ if (pos == NULL)
+ break;
+ if (start != pos && *(pos - 1) != ' ') {
+ start = pos + prefix_len;
+ continue;
+ }
+
+ start = pos + prefix_len;
+ pos = os_strchr(start, ' ');
+ if (pos)
+ *pos++ = '\0';
+ method = eap_get_phase2_type(start, &vendor);
+ if (vendor == EAP_VENDOR_IETF && method == EAP_TYPE_NONE) {
+ wpa_printf(MSG_ERROR, "TLS: Unsupported Phase2 EAP "
+ "method '%s'", start);
+ } else {
+ num_methods++;
++ _methods = os_realloc_array(methods, num_methods,
++ sizeof(*methods));
+ if (_methods == NULL) {
+ os_free(methods);
+ os_free(buf);
+ return -1;
+ }
+ methods = _methods;
+ methods[num_methods - 1].vendor = vendor;
+ methods[num_methods - 1].method = method;
+ }
+
+ start = pos;
+ }
+
+ os_free(buf);
+
+get_defaults:
+ if (methods == NULL)
+ methods = eap_get_phase2_types(config, &num_methods);
+
+ if (methods == NULL) {
+ wpa_printf(MSG_ERROR, "TLS: No Phase2 EAP methods available");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "TLS: Phase2 EAP types",
+ (u8 *) methods,
+ num_methods * sizeof(struct eap_method_type));
+
+ *types = methods;
+ *num_types = num_methods;
+
+ return 0;
+}
+
+
+/**
+ * eap_peer_tls_phase2_nak - Generate EAP-Nak for Phase 2
+ * @types: Buffer for returning allocated list of allowed EAP methods
+ * @num_types: Buffer for returning number of allocated EAP methods
+ * @hdr: EAP-Request header (and the following EAP type octet)
+ * @resp: Buffer for returning the EAP-Nak message
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types,
+ struct eap_hdr *hdr, struct wpabuf **resp)
+{
+ u8 *pos = (u8 *) (hdr + 1);
+ size_t i;
+
+ /* TODO: add support for expanded Nak */
+ wpa_printf(MSG_DEBUG, "TLS: Phase 2 Request: Nak type=%d", *pos);
+ wpa_hexdump(MSG_DEBUG, "TLS: Allowed Phase2 EAP types",
+ (u8 *) types, num_types * sizeof(struct eap_method_type));
+ *resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK, num_types,
+ EAP_CODE_RESPONSE, hdr->identifier);
+ if (*resp == NULL)
+ return -1;
+
+ for (i = 0; i < num_types; i++) {
+ if (types[i].vendor == EAP_VENDOR_IETF &&
+ types[i].method < 256)
+ wpabuf_put_u8(*resp, types[i].method);
+ }
+
+ eap_update_len(*resp);
+
+ return 0;
+}
--- /dev/null
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+/*
+ * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
++ * Copyright (c) 2004-2009, 2012, Jouni Malinen <j@w1.fi>
+ *
- * tls_ia - Whether TLS/IA is enabled for this TLS connection
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
+ */
+
+#ifndef EAP_TLS_COMMON_H
+#define EAP_TLS_COMMON_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * struct eap_ssl_data - TLS data for EAP methods
+ */
+struct eap_ssl_data {
+ /**
+ * conn - TLS connection context data from tls_connection_init()
+ */
+ struct tls_connection *conn;
+
+ /**
+ * tls_out - TLS message to be sent out in fragments
+ */
+ struct wpabuf *tls_out;
+
+ /**
+ * tls_out_pos - The current position in the outgoing TLS message
+ */
+ size_t tls_out_pos;
+
+ /**
+ * tls_out_limit - Maximum fragment size for outgoing TLS messages
+ */
+ size_t tls_out_limit;
+
+ /**
+ * tls_in - Received TLS message buffer for re-assembly
+ */
+ struct wpabuf *tls_in;
+
+ /**
+ * tls_in_left - Number of remaining bytes in the incoming TLS message
+ */
+ size_t tls_in_left;
+
+ /**
+ * tls_in_total - Total number of bytes in the incoming TLS message
+ */
+ size_t tls_in_total;
+
+ /**
+ * phase2 - Whether this TLS connection is used in EAP phase 2 (tunnel)
+ */
+ int phase2;
+
+ /**
+ * include_tls_length - Whether the TLS length field is included even
+ * if the TLS data is not fragmented
+ */
+ int include_tls_length;
+
+ /**
- int tls_ia;
++ * eap - EAP state machine allocated with eap_peer_sm_init()
+ */
- * eap - EAP state machine allocated with eap_peer_sm_init()
++ struct eap_sm *eap;
+
+ /**
- struct eap_sm *eap;
++ * ssl_ctx - TLS library context to use for the connection
+ */
- struct eap_peer_config *config);
++ void *ssl_ctx;
++
++ /**
++ * eap_type - EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
++ */
++ u8 eap_type;
+};
+
+
+/* EAP TLS Flags */
+#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80
+#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40
+#define EAP_TLS_FLAGS_START 0x20
+#define EAP_TLS_VERSION_MASK 0x07
+
+ /* could be up to 128 bytes, but only the first 64 bytes are used */
+#define EAP_TLS_KEY_LEN 64
+
++/* dummy type used as a flag for UNAUTH-TLS */
++#define EAP_UNAUTH_TLS_TYPE 255
++#define EAP_WFA_UNAUTH_TLS_TYPE 254
++
+
+int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
- u8 id, const u8 *in_data, size_t in_len,
++ struct eap_peer_config *config, u8 eap_type);
+void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
+u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
+ const char *label, size_t len);
++u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm,
++ struct eap_ssl_data *data, u8 eap_type,
++ size_t *len);
+int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
+ EapType eap_type, int peap_version,
++ u8 id, const struct wpabuf *in_data,
+ struct wpabuf **out_data);
+struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type,
+ int peap_version);
+int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data);
+int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data,
+ char *buf, size_t buflen, int verbose);
+const u8 * eap_peer_tls_process_init(struct eap_sm *sm,
+ struct eap_ssl_data *data,
+ EapType eap_type,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData,
+ size_t *len, u8 *flags);
+void eap_peer_tls_reset_input(struct eap_ssl_data *data);
+void eap_peer_tls_reset_output(struct eap_ssl_data *data);
+int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data,
+ const struct wpabuf *in_data,
+ struct wpabuf **in_decrypted);
+int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data,
+ EapType eap_type, int peap_version, u8 id,
+ const struct wpabuf *in_data,
+ struct wpabuf **out_data);
+int eap_peer_select_phase2_methods(struct eap_peer_config *config,
+ const char *prefix,
+ struct eap_method_type **types,
+ size_t *num_types);
+int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types,
+ struct eap_hdr *hdr, struct wpabuf **resp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EAP_TLS_COMMON_H */
--- /dev/null
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+/*
+ * EAP peer method: EAP-TTLS (RFC 5281)
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
++ * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
+ *
- /* Maximum supported TTLS version
- * 0 = RFC 5281
- * 1 = draft-funk-eap-ttls-v1-00.txt
- */
- #ifndef EAP_TTLS_VERSION
- #define EAP_TTLS_VERSION 0 /* TTLSv1 implementation is not yet complete */
- #endif /* EAP_TTLS_VERSION */
-
-
- #define MSCHAPV2_KEY_LEN 16
- #define MSCHAPV2_NT_RESPONSE_LEN 24
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "radius/radius.h"
+#include "crypto/ms_funcs.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "eap_common/chap.h"
+#include "eap_common/eap_ttls.h"
+#include "mschapv2.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_config.h"
+
+
- int ssl_initialized;
++#define EAP_TTLS_VERSION 0
+
+
+static void eap_ttls_deinit(struct eap_sm *sm, void *priv);
+
+
+struct eap_ttls_data {
+ struct eap_ssl_data ssl;
- int ttls_version, force_ttls_version;
+
- data->force_ttls_version = -1;
++ int ttls_version;
+
+ const struct eap_method *phase2_method;
+ void *phase2_priv;
+ int phase2_success;
+ int phase2_start;
+
+ enum phase2_types {
+ EAP_TTLS_PHASE2_EAP,
+ EAP_TTLS_PHASE2_MSCHAPV2,
+ EAP_TTLS_PHASE2_MSCHAP,
+ EAP_TTLS_PHASE2_PAP,
+ EAP_TTLS_PHASE2_CHAP
+ } phase2_type;
+ struct eap_method_type phase2_eap_type;
+ struct eap_method_type *phase2_eap_types;
+ size_t num_phase2_eap_types;
+
+ u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN];
+ int auth_response_valid;
+ u8 master_key[MSCHAPV2_MASTER_KEY_LEN]; /* MSCHAPv2 master key */
+ u8 ident;
+ int resuming; /* starting a resumed session */
+ int reauth; /* reauthentication */
+ u8 *key_data;
++ u8 *session_id;
++ size_t id_len;
+
+ struct wpabuf *pending_phase2_req;
+ int chbind_req_sent; /* channel binding request was sent */
+ int done_butfor_cb; /*we turned METHOD_DONE into METHOD_MAY_CONT to receive cb*/
+ EapDecision cbDecision;
+#ifdef EAP_TNC
+ int ready_for_tnc;
+ int tnc_started;
+#endif /* EAP_TNC */
+};
+
+
+/* draft-ietf-emu-chbind-13 section 5.3 */
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct chbind_hdr {
+ u16 len;
+ u8 nsid;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+
+static void * eap_ttls_init(struct eap_sm *sm)
+{
+ struct eap_ttls_data *data;
+ struct eap_peer_config *config = eap_get_config(sm);
+ char *selected;
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->ttls_version = EAP_TTLS_VERSION;
- #if EAP_TTLS_VERSION > 0
- if (config && config->phase1) {
- const char *pos = os_strstr(config->phase1, "ttlsver=");
- if (pos) {
- data->force_ttls_version = atoi(pos + 8);
- data->ttls_version = data->force_ttls_version;
- wpa_printf(MSG_DEBUG, "EAP-TTLS: Forced TTLS version "
- "%d", data->force_ttls_version);
- }
- }
- #endif /* EAP_TTLS_VERSION */
-
+ selected = "EAP";
+ data->phase2_type = EAP_TTLS_PHASE2_EAP;
+
- #if EAP_TTLS_VERSION > 0
- if (!(tls_capabilities(sm->ssl_ctx) & TLS_CAPABILITY_IA) &&
- data->ttls_version > 0) {
- if (data->force_ttls_version > 0) {
- wpa_printf(MSG_INFO, "EAP-TTLS: Forced TTLSv%d and "
- "TLS library does not support TLS/IA.",
- data->force_ttls_version);
- eap_ttls_deinit(sm, data);
- return NULL;
- }
- data->ttls_version = 0;
+ if (config && config->phase2) {
+ if (os_strstr(config->phase2, "autheap=")) {
+ selected = "EAP";
+ data->phase2_type = EAP_TTLS_PHASE2_EAP;
+ } else if (os_strstr(config->phase2, "auth=MSCHAPV2")) {
+ selected = "MSCHAPV2";
+ data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2;
+ } else if (os_strstr(config->phase2, "auth=MSCHAP")) {
+ selected = "MSCHAP";
+ data->phase2_type = EAP_TTLS_PHASE2_MSCHAP;
+ } else if (os_strstr(config->phase2, "auth=PAP")) {
+ selected = "PAP";
+ data->phase2_type = EAP_TTLS_PHASE2_PAP;
+ } else if (os_strstr(config->phase2, "auth=CHAP")) {
+ selected = "CHAP";
+ data->phase2_type = EAP_TTLS_PHASE2_CHAP;
+ }
+ }
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 type: %s", selected);
+
+ if (data->phase2_type == EAP_TTLS_PHASE2_EAP) {
+ if (eap_peer_select_phase2_methods(config, "autheap=",
+ &data->phase2_eap_types,
+ &data->num_phase2_eap_types)
+ < 0) {
+ eap_ttls_deinit(sm, data);
+ return NULL;
+ }
+
+ data->phase2_eap_type.vendor = EAP_VENDOR_IETF;
+ data->phase2_eap_type.method = EAP_TYPE_NONE;
+ }
+
- #endif /* EAP_TTLS_VERSION */
++ if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TTLS)) {
++ wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
++ eap_ttls_deinit(sm, data);
++ return NULL;
+ }
- if (data->ssl_initialized)
- eap_peer_tls_ssl_deinit(sm, &data->ssl);
- os_free(data->key_data);
+
+ return data;
+}
+
+
+static void eap_ttls_phase2_eap_deinit(struct eap_sm *sm,
+ struct eap_ttls_data *data)
+{
+ if (data->phase2_priv && data->phase2_method) {
+ data->phase2_method->deinit(sm, data->phase2_priv);
+ data->phase2_method = NULL;
+ data->phase2_priv = NULL;
+ }
+}
+
+
++static void eap_ttls_free_key(struct eap_ttls_data *data)
++{
++ if (data->key_data) {
++ bin_clear_free(data->key_data, EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
++ data->key_data = NULL;
++ }
++}
++
++
+static void eap_ttls_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ if (data == NULL)
+ return;
+ eap_ttls_phase2_eap_deinit(sm, data);
+ os_free(data->phase2_eap_types);
- avp->avp_length = host_to_be32((flags << 24) | (hdrlen + len));
++ eap_peer_tls_ssl_deinit(sm, &data->ssl);
++ eap_ttls_free_key(data);
++ os_free(data->session_id);
+ wpabuf_free(data->pending_phase2_req);
+ os_free(data);
+}
+
+
+static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id,
+ int mandatory, size_t len)
+{
+ struct ttls_avp_vendor *avp;
+ u8 flags;
+ size_t hdrlen;
+
+ avp = (struct ttls_avp_vendor *) avphdr;
+ flags = mandatory ? AVP_FLAGS_MANDATORY : 0;
+ if (vendor_id) {
+ flags |= AVP_FLAGS_VENDOR;
+ hdrlen = sizeof(*avp);
+ avp->vendor_id = host_to_be32(vendor_id);
+ } else {
+ hdrlen = sizeof(struct ttls_avp);
+ }
+
+ avp->avp_code = host_to_be32(avp_code);
- #if EAP_TTLS_VERSION > 0
- static int eap_ttls_ia_permute_inner_secret(struct eap_sm *sm,
- struct eap_ttls_data *data,
- const u8 *key, size_t key_len)
- {
- u8 *buf;
- size_t buf_len;
- int ret;
-
- if (key) {
- buf_len = 2 + key_len;
- buf = os_malloc(buf_len);
- if (buf == NULL)
- return -1;
- WPA_PUT_BE16(buf, key_len);
- os_memcpy(buf + 2, key, key_len);
- } else {
- buf = NULL;
- buf_len = 0;
- }
-
- wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Session keys for TLS/IA inner "
- "secret permutation", buf, buf_len);
- ret = tls_connection_ia_permute_inner_secret(sm->ssl_ctx,
- data->ssl.conn,
- buf, buf_len);
- os_free(buf);
-
- return ret;
- }
- #endif /* EAP_TTLS_VERSION */
-
-
++ avp->avp_length = host_to_be32(((u32) flags << 24) |
++ (u32) (hdrlen + len));
+
+ return avphdr + hdrlen;
+}
+
+
+static u8 * eap_ttls_avp_add(u8 *start, u8 *avphdr, u32 avp_code,
+ u32 vendor_id, int mandatory,
+ const u8 *data, size_t len)
+{
+ u8 *pos;
+ pos = eap_ttls_avp_hdr(avphdr, avp_code, vendor_id, mandatory, len);
+ os_memcpy(pos, data, len);
+ pos += len;
+ AVP_PAD(start, pos);
+ return pos;
+}
+
+
+static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code,
+ int mandatory)
+{
+ struct wpabuf *msg;
+ u8 *avp, *pos;
+
+ msg = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(*resp) + 4);
+ if (msg == NULL) {
+ wpabuf_free(*resp);
+ *resp = NULL;
+ return -1;
+ }
+
+ avp = wpabuf_mhead(msg);
+ pos = eap_ttls_avp_hdr(avp, avp_code, 0, mandatory, wpabuf_len(*resp));
+ os_memcpy(pos, wpabuf_head(*resp), wpabuf_len(*resp));
+ pos += wpabuf_len(*resp);
+ AVP_PAD(avp, pos);
+ wpabuf_free(*resp);
+ wpabuf_put(msg, pos - avp);
+ *resp = msg;
+ return 0;
+}
+
+/* chop up resp into multiple vsa's as necessary*/
+static int eap_ttls_avp_radius_vsa_encapsulate(struct wpabuf **resp, u32 vendor,
+ u8 attr, int mandatory)
+{
+ struct wpabuf *msg;
+ u8 *avp, *pos, *src, *final;
+ size_t size = wpabuf_len(*resp);
+ size_t num_msgs = 1 + (size / 248);
+ size_t msg_wrapper_size = sizeof(struct ttls_avp_vendor) + 6;
+ size_t allocated_total = num_msgs * (4 + msg_wrapper_size) + size;
+
+ msg = wpabuf_alloc(allocated_total);
+ if (msg == NULL) {
+ wpabuf_free(*resp);
+ *resp = NULL;
+ return -1;
+ }
+ src = wpabuf_mhead(*resp);
+ avp = wpabuf_mhead(msg);
+ while (size > 0) {
+ int avp_size = size > 248 ? 248 : size;
+ size -= avp_size;
+ pos = eap_ttls_avp_hdr(avp, RADIUS_ATTR_VENDOR_SPECIFIC, 0, mandatory,
+ avp_size+6);
+ wpabuf_put(msg, pos-avp);
+ wpabuf_put_be32(msg, vendor);
+ wpabuf_put_u8(msg, (u8) attr);
+ wpabuf_put_u8(msg, (u8) avp_size+2);
+ wpabuf_put_data(msg, src, avp_size);
+ src += avp_size;
+ pos = wpabuf_mhead_u8(msg) + wpabuf_len(msg);
+ final = pos; /*keep pos so we know how much padding is added*/
+ AVP_PAD(avp, final); /*final modified*/
+ if (final > pos)
+ wpabuf_put(msg, final-pos);
+ avp = final;
+ }
+ /* check avp-wpabuf_mhead(msg) < allocated_total */
+ wpabuf_free(*resp);
+ *resp = msg;
+ return 0;
+}
+
- os_free(data->key_data);
+static int eap_ttls_v0_derive_key(struct eap_sm *sm,
+ struct eap_ttls_data *data)
+{
- EAP_TLS_KEY_LEN);
++ eap_ttls_free_key(data);
+ data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
+ "ttls keying material",
-
- return 0;
- }
-
-
- #if EAP_TTLS_VERSION > 0
- static int eap_ttls_v1_derive_key(struct eap_sm *sm,
- struct eap_ttls_data *data)
- {
- struct tls_keys keys;
- u8 *rnd;
-
- os_free(data->key_data);
- data->key_data = NULL;
-
- os_memset(&keys, 0, sizeof(keys));
- if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) ||
- keys.client_random == NULL || keys.server_random == NULL ||
- keys.inner_secret == NULL) {
- wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, "
- "client random, or server random to derive keying "
- "material");
- return -1;
- }
-
- rnd = os_malloc(keys.client_random_len + keys.server_random_len);
- data->key_data = os_malloc(EAP_TLS_KEY_LEN);
- if (rnd == NULL || data->key_data == NULL) {
- wpa_printf(MSG_INFO, "EAP-TTLS: No memory for key derivation");
- os_free(rnd);
- os_free(data->key_data);
- data->key_data = NULL;
- return -1;
- }
- os_memcpy(rnd, keys.client_random, keys.client_random_len);
- os_memcpy(rnd + keys.client_random_len, keys.server_random,
- keys.server_random_len);
-
- if (tls_prf(keys.inner_secret, keys.inner_secret_len,
- "ttls v1 keying material", rnd, keys.client_random_len +
- keys.server_random_len, data->key_data, EAP_TLS_KEY_LEN)) {
- wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key");
- os_free(rnd);
- os_free(data->key_data);
- data->key_data = NULL;
- return -1;
++ EAP_TLS_KEY_LEN +
++ EAP_EMSK_LEN);
+ if (!data->key_data) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Failed to derive key");
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key",
+ data->key_data, EAP_TLS_KEY_LEN);
- wpa_hexdump(MSG_DEBUG, "EAP-TTLS: client/server random",
- rnd, keys.client_random_len + keys.server_random_len);
- wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: TLS/IA inner secret",
- keys.inner_secret, keys.inner_secret_len);
-
- os_free(rnd);
-
- wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key",
- data->key_data, EAP_TLS_KEY_LEN);
-
++ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived EMSK",
++ data->key_data + EAP_TLS_KEY_LEN,
++ EAP_EMSK_LEN);
++
++ os_free(data->session_id);
++ data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl,
++ EAP_TYPE_TTLS,
++ &data->id_len);
++ if (data->session_id) {
++ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Derived Session-Id",
++ data->session_id, data->id_len);
++ } else {
++ wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to derive Session-Id");
+ }
+
- #endif /* EAP_TTLS_VERSION */
+ return 0;
+}
- #if EAP_TTLS_VERSION > 0
- struct tls_keys keys;
- u8 *challenge, *rnd;
- #endif /* EAP_TTLS_VERSION */
-
- if (data->ttls_version == 0) {
- return eap_peer_tls_derive_key(sm, &data->ssl,
- "ttls challenge", len);
- }
-
- #if EAP_TTLS_VERSION > 0
-
- os_memset(&keys, 0, sizeof(keys));
- if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) ||
- keys.client_random == NULL || keys.server_random == NULL ||
- keys.inner_secret == NULL) {
- wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, "
- "client random, or server random to derive "
- "implicit challenge");
- return NULL;
- }
-
- rnd = os_malloc(keys.client_random_len + keys.server_random_len);
- challenge = os_malloc(len);
- if (rnd == NULL || challenge == NULL) {
- wpa_printf(MSG_INFO, "EAP-TTLS: No memory for implicit "
- "challenge derivation");
- os_free(rnd);
- os_free(challenge);
- return NULL;
- }
- os_memcpy(rnd, keys.server_random, keys.server_random_len);
- os_memcpy(rnd + keys.server_random_len, keys.client_random,
- keys.client_random_len);
-
- if (tls_prf(keys.inner_secret, keys.inner_secret_len,
- "inner application challenge", rnd,
- keys.client_random_len + keys.server_random_len,
- challenge, len)) {
- wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive implicit "
- "challenge");
- os_free(rnd);
- os_free(challenge);
- return NULL;
- }
-
- os_free(rnd);
-
- wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived implicit challenge",
- challenge, len);
-
- return challenge;
-
- #else /* EAP_TTLS_VERSION */
-
- return NULL;
-
- #endif /* EAP_TTLS_VERSION */
- }
-
-
- static void eap_ttlsv1_phase2_eap_finish(struct eap_sm *sm,
- struct eap_ttls_data *data,
- struct eap_method_ret *ret)
- {
- #if EAP_TTLS_VERSION > 0
- if (data->ttls_version > 0) {
- const struct eap_method *m = data->phase2_method;
- void *priv = data->phase2_priv;
-
- /* TTLSv1 requires TLS/IA FinalPhaseFinished */
- if (ret->decision == DECISION_UNCOND_SUCC)
- ret->decision = DECISION_COND_SUCC;
- ret->methodState = METHOD_CONT;
-
- if (ret->decision == DECISION_COND_SUCC &&
- m->isKeyAvailable && m->getKey &&
- m->isKeyAvailable(sm, priv)) {
- u8 *key;
- size_t key_len;
- key = m->getKey(sm, priv, &key_len);
- if (key) {
- eap_ttls_ia_permute_inner_secret(
- sm, data, key, key_len);
- os_free(key);
- }
- }
- }
- #endif /* EAP_TTLS_VERSION */
+
+
++#ifndef CONFIG_FIPS
+static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm,
+ struct eap_ttls_data *data, size_t len)
+{
- eap_ttlsv1_phase2_eap_finish(sm, data, ret);
++ return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len);
+}
++#endif /* CONFIG_FIPS */
+
+
+static void eap_ttls_phase2_select_eap_method(struct eap_ttls_data *data,
+ u8 method)
+{
+ size_t i;
+ for (i = 0; i < data->num_phase2_eap_types; i++) {
+ if (data->phase2_eap_types[i].vendor != EAP_VENDOR_IETF ||
+ data->phase2_eap_types[i].method != method)
+ continue;
+
+ data->phase2_eap_type.vendor =
+ data->phase2_eap_types[i].vendor;
+ data->phase2_eap_type.method =
+ data->phase2_eap_types[i].method;
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected "
+ "Phase 2 EAP vendor %d method %d",
+ data->phase2_eap_type.vendor,
+ data->phase2_eap_type.method);
+ break;
+ }
+}
+
+
+static int eap_ttls_phase2_eap_process(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *hdr, size_t len,
+ struct wpabuf **resp)
+{
+ struct wpabuf msg;
+ struct eap_method_ret iret;
+
+ os_memset(&iret, 0, sizeof(iret));
+ wpabuf_set(&msg, hdr, len);
+ *resp = data->phase2_method->process(sm, data->phase2_priv, &iret,
+ &msg);
+ if ((iret.methodState == METHOD_DONE ||
+ iret.methodState == METHOD_MAY_CONT) &&
+ (iret.decision == DECISION_UNCOND_SUCC ||
+ iret.decision == DECISION_COND_SUCC ||
+ iret.decision == DECISION_FAIL)) {
+ ret->methodState = iret.methodState;
+ ret->decision = iret.decision;
+ }
- static void eap_ttlsv1_permute_inner(struct eap_sm *sm,
- struct eap_ttls_data *data)
- {
- #if EAP_TTLS_VERSION > 0
- u8 session_key[2 * MSCHAPV2_KEY_LEN];
-
- if (data->ttls_version == 0)
- return;
-
- get_asymetric_start_key(data->master_key, session_key,
- MSCHAPV2_KEY_LEN, 0, 0);
- get_asymetric_start_key(data->master_key,
- session_key + MSCHAPV2_KEY_LEN,
- MSCHAPV2_KEY_LEN, 1, 0);
- eap_ttls_ia_permute_inner_secret(sm, data, session_key,
- sizeof(session_key));
- #endif /* EAP_TTLS_VERSION */
- }
-
-
+
+ return 0;
+}
+
+
+static int eap_ttls_phase2_request_eap_method(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *hdr, size_t len,
+ u8 method, struct wpabuf **resp)
+{
+#ifdef EAP_TNC
+ if (data->tnc_started && data->phase2_method &&
+ data->phase2_priv && method == EAP_TYPE_TNC &&
+ data->phase2_eap_type.method == EAP_TYPE_TNC)
+ return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len,
+ resp);
+
+ if (data->ready_for_tnc && !data->tnc_started &&
+ method == EAP_TYPE_TNC) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed "
+ "EAP method");
+ data->tnc_started = 1;
+ }
+
+ if (data->tnc_started) {
+ if (data->phase2_eap_type.vendor != EAP_VENDOR_IETF ||
+ data->phase2_eap_type.method == EAP_TYPE_TNC) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Unexpected EAP "
+ "type %d for TNC", method);
+ return -1;
+ }
+
+ data->phase2_eap_type.vendor = EAP_VENDOR_IETF;
+ data->phase2_eap_type.method = method;
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected "
+ "Phase 2 EAP vendor %d method %d (TNC)",
+ data->phase2_eap_type.vendor,
+ data->phase2_eap_type.method);
+
+ if (data->phase2_type == EAP_TTLS_PHASE2_EAP)
+ eap_ttls_phase2_eap_deinit(sm, data);
+ }
+#endif /* EAP_TNC */
+
+ if (data->phase2_eap_type.vendor == EAP_VENDOR_IETF &&
+ data->phase2_eap_type.method == EAP_TYPE_NONE)
+ eap_ttls_phase2_select_eap_method(data, method);
+
+ if (method != data->phase2_eap_type.method || method == EAP_TYPE_NONE)
+ {
+ if (eap_peer_tls_phase2_nak(data->phase2_eap_types,
+ data->num_phase2_eap_types,
+ hdr, resp))
+ return -1;
+ return 0;
+ }
+
+ if (data->phase2_priv == NULL) {
+ data->phase2_method = eap_peer_get_eap_method(
+ EAP_VENDOR_IETF, method);
+ if (data->phase2_method) {
+ sm->init_phase2 = 1;
+ data->phase2_priv = data->phase2_method->init(sm);
+ sm->init_phase2 = 0;
+ }
+ }
+ if (data->phase2_priv == NULL || data->phase2_method == NULL) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: failed to initialize "
+ "Phase 2 EAP method %d", method);
+ return -1;
+ }
+
+ return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len, resp);
+}
+
+
+static int eap_ttls_phase2_request_eap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *hdr,
+ struct wpabuf **resp)
+{
+ size_t len = be_to_host16(hdr->length);
+ u8 *pos;
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ if (len <= sizeof(struct eap_hdr)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: too short "
+ "Phase 2 request (len=%lu)", (unsigned long) len);
+ return -1;
+ }
+ pos = (u8 *) (hdr + 1);
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP Request: type=%d", *pos);
+ switch (*pos) {
+ case EAP_TYPE_IDENTITY:
+ *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1);
+ break;
+ default:
+ if (eap_ttls_phase2_request_eap_method(sm, data, ret, hdr, len,
+ *pos, resp) < 0)
+ return -1;
+ break;
+ }
+
+ if (*resp == NULL &&
+ (config->pending_req_identity || config->pending_req_password ||
+ config->pending_req_otp)) {
+ return 0;
+ }
+
+ if (*resp == NULL)
+ return -1;
+
+ wpa_hexdump_buf(MSG_DEBUG, "EAP-TTLS: AVP encapsulate EAP Response",
+ *resp);
+ return eap_ttls_avp_encapsulate(resp, RADIUS_ATTR_EAP_MESSAGE, 1);
+}
+
+
- peer_challenge = challenge + 1 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN;
+static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct wpabuf **resp)
+{
++#ifdef CONFIG_FIPS
++ wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPV2 not supported in FIPS build");
++ return -1;
++#else /* CONFIG_FIPS */
++#ifdef EAP_MSCHAPv2
+ struct wpabuf *msg;
+ u8 *buf, *pos, *challenge, *peer_challenge;
+ const u8 *identity, *password;
+ size_t identity_len, password_len;
+ int pwhash;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAPV2 Request");
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ password = eap_get_config_password2(sm, &password_len, &pwhash);
+ if (identity == NULL || password == NULL)
+ return -1;
+
+ msg = wpabuf_alloc(identity_len + 1000);
+ if (msg == NULL) {
+ wpa_printf(MSG_ERROR,
+ "EAP-TTLS/MSCHAPV2: Failed to allocate memory");
+ return -1;
+ }
+ pos = buf = wpabuf_mhead(msg);
+
+ /* User-Name */
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+ identity, identity_len);
+
+ /* MS-CHAP-Challenge */
+ challenge = eap_ttls_implicit_challenge(
+ sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1);
+ if (challenge == NULL) {
+ wpabuf_free(msg);
+ wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive "
+ "implicit challenge");
+ return -1;
+ }
- os_memcpy(pos, peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE,
+ RADIUS_VENDOR_ID_MICROSOFT, 1,
+ challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+
+ /* MS-CHAP2-Response */
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_RESPONSE,
+ RADIUS_VENDOR_ID_MICROSOFT, 1,
+ EAP_TTLS_MSCHAPV2_RESPONSE_LEN);
+ data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN];
+ *pos++ = data->ident;
+ *pos++ = 0; /* Flags */
- eap_ttlsv1_permute_inner(sm, data);
-
++ if (os_get_random(pos, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) < 0) {
++ os_free(challenge);
++ wpabuf_free(msg);
++ wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to get "
++ "random data for peer challenge");
++ return -1;
++ }
++ peer_challenge = pos;
+ pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN;
+ os_memset(pos, 0, 8); /* Reserved, must be zero */
+ pos += 8;
+ if (mschapv2_derive_response(identity, identity_len, password,
+ password_len, pwhash, challenge,
+ peer_challenge, pos, data->auth_response,
+ data->master_key)) {
++ os_free(challenge);
+ wpabuf_free(msg);
+ wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive "
+ "response");
+ return -1;
+ }
+ data->auth_response_valid = 1;
+
- if (sm->workaround && data->ttls_version == 0) {
- /* At least FreeRADIUS seems to be terminating
- * EAP-TTLS/MSHCAPV2 without the expected MS-CHAP-v2 Success
- * packet. */
- wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: EAP workaround - "
- "allow success without tunneled response");
- ret->methodState = METHOD_MAY_CONT;
- ret->decision = DECISION_COND_SUCC;
- }
-
+ pos += 24;
+ os_free(challenge);
+ AVP_PAD(buf, pos);
+
+ wpabuf_put(msg, pos - buf);
+ *resp = msg;
+
- if (data->ttls_version > 0) {
- /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success,
- * so do not allow connection to be terminated yet. */
- ret->methodState = METHOD_CONT;
- ret->decision = DECISION_COND_SUCC;
- } else {
- /* EAP-TTLS/MSCHAP does not provide tunneled success
- * notification, so assume that Phase2 succeeds. */
- ret->methodState = METHOD_DONE;
- ret->decision = DECISION_COND_SUCC;
- }
+ return 0;
++#else /* EAP_MSCHAPv2 */
++ wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build");
++ return -1;
++#endif /* EAP_MSCHAPv2 */
++#endif /* CONFIG_FIPS */
+}
+
+
+static int eap_ttls_phase2_request_mschap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct wpabuf **resp)
+{
++#ifdef CONFIG_FIPS
++ wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAP not supported in FIPS build");
++ return -1;
++#else /* CONFIG_FIPS */
+ struct wpabuf *msg;
+ u8 *buf, *pos, *challenge;
+ const u8 *identity, *password;
+ size_t identity_len, password_len;
+ int pwhash;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAP Request");
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ password = eap_get_config_password2(sm, &password_len, &pwhash);
+ if (identity == NULL || password == NULL)
+ return -1;
+
+ msg = wpabuf_alloc(identity_len + 1000);
+ if (msg == NULL) {
+ wpa_printf(MSG_ERROR,
+ "EAP-TTLS/MSCHAP: Failed to allocate memory");
+ return -1;
+ }
+ pos = buf = wpabuf_mhead(msg);
+
+ /* User-Name */
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+ identity, identity_len);
+
+ /* MS-CHAP-Challenge */
+ challenge = eap_ttls_implicit_challenge(
+ sm, data, EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1);
+ if (challenge == NULL) {
+ wpabuf_free(msg);
+ wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to derive "
+ "implicit challenge");
+ return -1;
+ }
+
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE,
+ RADIUS_VENDOR_ID_MICROSOFT, 1,
+ challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN);
+
+ /* MS-CHAP-Response */
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_RESPONSE,
+ RADIUS_VENDOR_ID_MICROSOFT, 1,
+ EAP_TTLS_MSCHAP_RESPONSE_LEN);
+ data->ident = challenge[EAP_TTLS_MSCHAP_CHALLENGE_LEN];
+ *pos++ = data->ident;
+ *pos++ = 1; /* Flags: Use NT style passwords */
+ os_memset(pos, 0, 24); /* LM-Response */
+ pos += 24;
+ if (pwhash) {
+ challenge_response(challenge, password, pos); /* NT-Response */
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password hash",
+ password, 16);
+ } else {
+ nt_challenge_response(challenge, password, password_len,
+ pos); /* NT-Response */
+ wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password",
+ password, password_len);
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP implicit challenge",
+ challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP response", pos, 24);
+ pos += 24;
+ os_free(challenge);
+ AVP_PAD(buf, pos);
+
+ wpabuf_put(msg, pos - buf);
+ *resp = msg;
+
- if (data->ttls_version > 0) {
- /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success,
- * so do not allow connection to be terminated yet. */
- ret->methodState = METHOD_CONT;
- ret->decision = DECISION_COND_SUCC;
- } else {
- /* EAP-TTLS/PAP does not provide tunneled success notification,
- * so assume that Phase2 succeeds. */
- ret->methodState = METHOD_DONE;
- ret->decision = DECISION_COND_SUCC;
- }
++ /* EAP-TTLS/MSCHAP does not provide tunneled success
++ * notification, so assume that Phase2 succeeds. */
++ ret->methodState = METHOD_DONE;
++ ret->decision = DECISION_COND_SUCC;
+
+ return 0;
++#endif /* CONFIG_FIPS */
+}
+
+
+static int eap_ttls_phase2_request_pap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct wpabuf **resp)
+{
+ struct wpabuf *msg;
+ u8 *buf, *pos;
+ size_t pad;
+ const u8 *identity, *password;
+ size_t identity_len, password_len;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 PAP Request");
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ password = eap_get_config_password(sm, &password_len);
+ if (identity == NULL || password == NULL)
+ return -1;
+
+ msg = wpabuf_alloc(identity_len + password_len + 100);
+ if (msg == NULL) {
+ wpa_printf(MSG_ERROR,
+ "EAP-TTLS/PAP: Failed to allocate memory");
+ return -1;
+ }
+ pos = buf = wpabuf_mhead(msg);
+
+ /* User-Name */
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+ identity, identity_len);
+
+ /* User-Password; in RADIUS, this is encrypted, but EAP-TTLS encrypts
+ * the data, so no separate encryption is used in the AVP itself.
+ * However, the password is padded to obfuscate its length. */
+ pad = password_len == 0 ? 16 : (16 - (password_len & 15)) & 15;
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_USER_PASSWORD, 0, 1,
+ password_len + pad);
+ os_memcpy(pos, password, password_len);
+ pos += password_len;
+ os_memset(pos, 0, pad);
+ pos += pad;
+ AVP_PAD(buf, pos);
+
+ wpabuf_put(msg, pos - buf);
+ *resp = msg;
+
- if (data->ttls_version > 0) {
- /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success,
- * so do not allow connection to be terminated yet. */
- ret->methodState = METHOD_CONT;
- ret->decision = DECISION_COND_SUCC;
- } else {
- /* EAP-TTLS/CHAP does not provide tunneled success
- * notification, so assume that Phase2 succeeds. */
- ret->methodState = METHOD_DONE;
- ret->decision = DECISION_COND_SUCC;
- }
++ /* EAP-TTLS/PAP does not provide tunneled success notification,
++ * so assume that Phase2 succeeds. */
++ ret->methodState = METHOD_DONE;
++ ret->decision = DECISION_COND_SUCC;
+
+ return 0;
+}
+
+
+static int eap_ttls_phase2_request_chap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct wpabuf **resp)
+{
++#ifdef CONFIG_FIPS
++ wpa_printf(MSG_ERROR, "EAP-TTLS: CHAP not supported in FIPS build");
++ return -1;
++#else /* CONFIG_FIPS */
+ struct wpabuf *msg;
+ u8 *buf, *pos, *challenge;
+ const u8 *identity, *password;
+ size_t identity_len, password_len;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 CHAP Request");
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ password = eap_get_config_password(sm, &password_len);
+ if (identity == NULL || password == NULL)
+ return -1;
+
+ msg = wpabuf_alloc(identity_len + 1000);
+ if (msg == NULL) {
+ wpa_printf(MSG_ERROR,
+ "EAP-TTLS/CHAP: Failed to allocate memory");
+ return -1;
+ }
+ pos = buf = wpabuf_mhead(msg);
+
+ /* User-Name */
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+ identity, identity_len);
+
+ /* CHAP-Challenge */
+ challenge = eap_ttls_implicit_challenge(
+ sm, data, EAP_TTLS_CHAP_CHALLENGE_LEN + 1);
+ if (challenge == NULL) {
+ wpabuf_free(msg);
+ wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to derive "
+ "implicit challenge");
+ return -1;
+ }
+
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_CHAP_CHALLENGE, 0, 1,
+ challenge, EAP_TTLS_CHAP_CHALLENGE_LEN);
+
+ /* CHAP-Password */
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_CHAP_PASSWORD, 0, 1,
+ 1 + EAP_TTLS_CHAP_PASSWORD_LEN);
+ data->ident = challenge[EAP_TTLS_CHAP_CHALLENGE_LEN];
+ *pos++ = data->ident;
+
+ /* MD5(Ident + Password + Challenge) */
+ chap_md5(data->ident, password, password_len, challenge,
+ EAP_TTLS_CHAP_CHALLENGE_LEN, pos);
+
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: CHAP username",
+ identity, identity_len);
+ wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: CHAP password",
+ password, password_len);
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP implicit challenge",
+ challenge, EAP_TTLS_CHAP_CHALLENGE_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP password",
+ pos, EAP_TTLS_CHAP_PASSWORD_LEN);
+ pos += EAP_TTLS_CHAP_PASSWORD_LEN;
+ os_free(challenge);
+ AVP_PAD(buf, pos);
+
+ wpabuf_put(msg, pos - buf);
+ *resp = msg;
+
- #if EAP_TTLS_VERSION > 0
- static struct wpabuf * eap_ttls_build_phase_finished(
- struct eap_sm *sm, struct eap_ttls_data *data, int id, int final)
- {
- struct wpabuf *req, *buf;
-
- buf = tls_connection_ia_send_phase_finished(sm->ssl_ctx,
- data->ssl.conn,
- final);
- if (buf == NULL)
- return NULL;
-
- req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS,
- 1 + wpabuf_len(buf),
- EAP_CODE_RESPONSE, id);
- if (req == NULL) {
- wpabuf_free(buf);
- return NULL;
- }
-
- wpabuf_put_u8(req, data->ttls_version);
- wpabuf_put_buf(req, buf);
- wpabuf_free(buf);
- eap_update_len(req);
-
- return req;
- }
- #endif /* EAP_TTLS_VERSION */
-
-
++ /* EAP-TTLS/CHAP does not provide tunneled success
++ * notification, so assume that Phase2 succeeds. */
++ ret->methodState = METHOD_DONE;
++ ret->decision = DECISION_COND_SUCC;
+
+ return 0;
++#endif /* CONFIG_FIPS */
+}
+
+
+static int eap_ttls_phase2_request(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *hdr,
+ struct wpabuf **resp)
+{
+ int res = 0;
+ size_t len;
+ enum phase2_types phase2_type = data->phase2_type;
+
+#ifdef EAP_TNC
+ if (data->tnc_started) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Processing TNC");
+ phase2_type = EAP_TTLS_PHASE2_EAP;
+ }
+#endif /* EAP_TNC */
+
+ if (phase2_type == EAP_TTLS_PHASE2_MSCHAPV2 ||
+ phase2_type == EAP_TTLS_PHASE2_MSCHAP ||
+ phase2_type == EAP_TTLS_PHASE2_PAP ||
+ phase2_type == EAP_TTLS_PHASE2_CHAP) {
+ if (eap_get_config_identity(sm, &len) == NULL) {
+ wpa_printf(MSG_INFO,
+ "EAP-TTLS: Identity not configured");
+ eap_sm_request_identity(sm);
+ if (eap_get_config_password(sm, &len) == NULL)
+ eap_sm_request_password(sm);
+ return 0;
+ }
+
+ if (eap_get_config_password(sm, &len) == NULL) {
+ wpa_printf(MSG_INFO,
+ "EAP-TTLS: Password not configured");
+ eap_sm_request_password(sm);
+ return 0;
+ }
+ }
+
+ switch (phase2_type) {
+ case EAP_TTLS_PHASE2_EAP:
+ res = eap_ttls_phase2_request_eap(sm, data, ret, hdr, resp);
+ break;
+ case EAP_TTLS_PHASE2_MSCHAPV2:
+ res = eap_ttls_phase2_request_mschapv2(sm, data, ret, resp);
+ break;
+ case EAP_TTLS_PHASE2_MSCHAP:
+ res = eap_ttls_phase2_request_mschap(sm, data, ret, resp);
+ break;
+ case EAP_TTLS_PHASE2_PAP:
+ res = eap_ttls_phase2_request_pap(sm, data, ret, resp);
+ break;
+ case EAP_TTLS_PHASE2_CHAP:
+ res = eap_ttls_phase2_request_chap(sm, data, ret, resp);
+ break;
+ default:
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 - Unknown");
+ res = -1;
+ break;
+ }
+
+ if (res < 0) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ }
+
+ return res;
+}
+
+
- if (data->ttls_version > 0) {
- /*
- * EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report
- * success, so do not allow connection to be terminated
- * yet.
- */
- ret->methodState = METHOD_CONT;
- ret->decision = DECISION_COND_SUCC;
- } else {
- ret->methodState = METHOD_DONE;
- ret->decision = DECISION_UNCOND_SUCC;
- data->phase2_success = 1;
- }
+struct ttls_parse_avp {
+ u8 *mschapv2;
+ u8 *eapdata;
+ size_t eap_len;
+ u8 *chbind_data;
+ size_t chbind_len;
+ int mschapv2_error;
+};
+
+
+static int eap_ttls_parse_attr_eap(const u8 *dpos, size_t dlen,
+ struct ttls_parse_avp *parse)
+{
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message");
+ if (parse->eapdata == NULL) {
+ parse->eapdata = os_malloc(dlen);
+ if (parse->eapdata == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate "
+ "memory for Phase 2 EAP data");
+ return -1;
+ }
+ os_memcpy(parse->eapdata, dpos, dlen);
+ parse->eap_len = dlen;
+ } else {
+ u8 *neweap = os_realloc(parse->eapdata, parse->eap_len + dlen);
+ if (neweap == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate "
+ "memory for Phase 2 EAP data");
+ return -1;
+ }
+ os_memcpy(neweap + parse->eap_len, dpos, dlen);
+ parse->eapdata = neweap;
+ parse->eap_len += dlen;
+ }
+
+ return 0;
+}
+
+
+static int eap_ttls_parse_attr_chbind(const u8 *dpos, size_t dlen,
+ struct ttls_parse_avp *parse)
+{
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - Channel Binding Message");
+
+ if (parse->chbind_data == NULL) {
+ parse->chbind_data = os_malloc(dlen);
+ if (parse->chbind_data == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate "
+ "memory for Phase 2 channel binding data");
+ return -1;
+ }
+ os_memcpy(parse->chbind_data, dpos, dlen);
+ parse->chbind_len = dlen;
+ } else {
+ /* TODO: can this really happen? maybe just make this an error? */
+ u8 *newchbind = os_realloc(parse->chbind_data,
+ parse->chbind_len + dlen);
+ if (newchbind == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate "
+ "memory for Phase 2 channel binding data");
+ return -1;
+ }
+ os_memcpy(newchbind + parse->chbind_len, dpos, dlen);
+ parse->chbind_data = newchbind;
+ parse->chbind_len += dlen;
+ }
+
+ return 0;
+}
+
+
+static int eap_ttls_parse_avp(u8 *pos, size_t left,
+ struct ttls_parse_avp *parse)
+{
+ struct ttls_avp *avp;
+ u32 avp_code, avp_length, vendor_id = 0;
+ u8 avp_flags, *dpos;
+ size_t dlen;
+
+ avp = (struct ttls_avp *) pos;
+ avp_code = be_to_host32(avp->avp_code);
+ avp_length = be_to_host32(avp->avp_length);
+ avp_flags = (avp_length >> 24) & 0xff;
+ avp_length &= 0xffffff;
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x "
+ "length=%d", (int) avp_code, avp_flags,
+ (int) avp_length);
+
+ if (avp_length > left) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow "
+ "(len=%d, left=%lu) - dropped",
+ (int) avp_length, (unsigned long) left);
+ return -1;
+ }
+
+ if (avp_length < sizeof(*avp)) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid AVP length %d",
+ avp_length);
+ return -1;
+ }
+
+ dpos = (u8 *) (avp + 1);
+ dlen = avp_length - sizeof(*avp);
+ if (avp_flags & AVP_FLAGS_VENDOR) {
+ if (dlen < 4) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Vendor AVP "
+ "underflow");
+ return -1;
+ }
+ vendor_id = WPA_GET_BE32(dpos);
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d",
+ (int) vendor_id);
+ dpos += 4;
+ dlen -= 4;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP data", dpos, dlen);
+
+ if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) {
+ if (eap_ttls_parse_attr_eap(dpos, dlen, parse) < 0)
+ return -1;
+ } else if (vendor_id == RADIUS_VENDOR_ID_UKERNA &&
+ avp_code == RADIUS_ATTR_UKERNA_CHBIND) {
+ /* message containing channel binding data */
+ if (eap_ttls_parse_attr_chbind(dpos, dlen, parse) < 0)
+ return -1;
+ } else if (vendor_id == 0 && avp_code == RADIUS_ATTR_REPLY_MESSAGE) {
+ /* This is an optional message that can be displayed to
+ * the user. */
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: AVP - Reply-Message",
+ dpos, dlen);
+ } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
+ avp_code == RADIUS_ATTR_MS_CHAP2_SUCCESS) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP2-Success",
+ dpos, dlen);
+ if (dlen != 43) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Unexpected "
+ "MS-CHAP2-Success length "
+ "(len=%lu, expected 43)",
+ (unsigned long) dlen);
+ return -1;
+ }
+ parse->mschapv2 = dpos;
+ } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
+ avp_code == RADIUS_ATTR_MS_CHAP_ERROR) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP-Error",
+ dpos, dlen);
+ parse->mschapv2_error = 1;
+ } else if (avp_flags & AVP_FLAGS_MANDATORY) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported mandatory AVP "
+ "code %d vendor_id %d - dropped",
+ (int) avp_code, (int) vendor_id);
+ return -1;
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported AVP "
+ "code %d vendor_id %d",
+ (int) avp_code, (int) vendor_id);
+ }
+
+ return avp_length;
+}
+
+
+static int eap_ttls_parse_avps(struct wpabuf *in_decrypted,
+ struct ttls_parse_avp *parse)
+{
+ u8 *pos;
+ size_t left, pad;
+ int avp_length;
+
+ pos = wpabuf_mhead(in_decrypted);
+ left = wpabuf_len(in_decrypted);
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 AVPs", pos, left);
+ if (left < sizeof(struct ttls_avp)) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 AVP frame"
+ " len=%lu expected %lu or more - dropped",
+ (unsigned long) left,
+ (unsigned long) sizeof(struct ttls_avp));
+ return -1;
+ }
+
+ /* Parse AVPs */
+ os_memset(parse, 0, sizeof(*parse));
+
+ while (left > 0) {
+ avp_length = eap_ttls_parse_avp(pos, left, parse);
+ if (avp_length < 0)
+ return -1;
+
+ pad = (4 - (avp_length & 3)) & 3;
+ pos += avp_length + pad;
+ if (left < avp_length + pad)
+ left = 0;
+ else
+ left -= avp_length + pad;
+ }
+
+ return 0;
+}
+
+
+static u8 * eap_ttls_fake_identity_request(void)
+{
+ struct eap_hdr *hdr;
+ u8 *buf;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: empty data in beginning of "
+ "Phase 2 - use fake EAP-Request Identity");
+ buf = os_malloc(sizeof(*hdr) + 1);
+ if (buf == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: failed to allocate "
+ "memory for fake EAP-Identity Request");
+ return NULL;
+ }
+
+ hdr = (struct eap_hdr *) buf;
+ hdr->code = EAP_CODE_REQUEST;
+ hdr->identifier = 0;
+ hdr->length = host_to_be16(sizeof(*hdr) + 1);
+ buf[sizeof(*hdr)] = EAP_TYPE_IDENTITY;
+
+ return buf;
+}
+
+
+static int eap_ttls_encrypt_response(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct wpabuf *resp, u8 identifier,
+ struct wpabuf **out_data)
+{
+ if (resp == NULL)
+ return 0;
+
+ wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Encrypting Phase 2 data",
+ resp);
+ if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS,
+ data->ttls_version, identifier,
+ resp, out_data)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt a Phase 2 "
+ "frame");
++ wpabuf_free(resp);
+ return -1;
+ }
+ wpabuf_free(resp);
+
+ return 0;
+}
+
+static int eap_ttls_add_chbind_request(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct wpabuf **resp)
+{
+ struct wpabuf *chbind_req, *res;
+ int length = 1, i;
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ if (!config->chbind_config || config->chbind_config_len <= 0)
+ return -1;
+
+ for (i=0; i<config->chbind_config_len; i++) {
+ length += 3 + config->chbind_config[i].req_data_len;
+ }
+
+ chbind_req = wpabuf_alloc(length);
+ if (!chbind_req)
+ return -1;
+
+ wpabuf_put_u8(chbind_req, CHBIND_CODE_REQUEST);
+ for (i=0; i<config->chbind_config_len; i++) {
+ struct eap_peer_chbind_config *chbind_config =
+ &config->chbind_config[i];
+ wpabuf_put_be16(chbind_req, chbind_config->req_data_len);
+ wpabuf_put_u8(chbind_req, chbind_config->nsid);
+ wpabuf_put_data(chbind_req, chbind_config->req_data,
+ chbind_config->req_data_len);
+ }
+ if (eap_ttls_avp_radius_vsa_encapsulate(&chbind_req,
+ RADIUS_VENDOR_ID_UKERNA,
+ RADIUS_ATTR_UKERNA_CHBIND, 0) < 0)
+ return -1;
+
+ /* bleh. This will free *resp regardless of whether combined buffer
+ alloc succeeds, which is not consistent with the other error
+ condition behavior in this function */
+ *resp = wpabuf_concat(chbind_req, *resp);
+
+ return (*resp) ? 0 : -1;
+}
+
+
+static int eap_ttls_process_chbind(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct ttls_parse_avp *parse,
+ struct wpabuf **resp)
+{
+ size_t pos=0;
+ u8 code;
+ u16 len;
+ struct chbind_hdr *hdr;
+ struct eap_peer_config *config = eap_get_config(sm);
+ int i, found;
+
+
+ if (parse->chbind_data == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: No channel binding message "
+ "in the packet - dropped");
+ return -1;
+ }
+ if (parse->chbind_len < 1 ) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: bad channel binding response "
+ "frame (len=%lu, expected %lu or more) - dropped",
+ (unsigned long) parse->chbind_len,
+ (unsigned long) 1);
+ return -1;
+ }
+ code = parse->chbind_data[pos++];
+ for (i=0; i<config->chbind_config_len; i++) {
+ struct eap_peer_chbind_config *chbind_config =
+ &config->chbind_config[i];
+ pos = 1;
+ found = 0;
+ while (pos+sizeof(*hdr) < parse->chbind_len) {
+ hdr = (struct chbind_hdr *)(&parse->chbind_data[pos]);
+ pos += sizeof(*hdr);
+ len = be_to_host16(hdr->len);
+ if (pos + len <= parse->chbind_len) {
+ if (chbind_config->nsid == hdr->nsid)
+ chbind_config->response_cb(
+ chbind_config->ctx,
+ code, hdr->nsid,
+ &parse->chbind_data[pos], len);
+ found = 1;
+ }
+ pos += len;
+ }
+ if (pos != parse->chbind_len) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: bad channel binding response "
+ "frame (parsed len=%lu, expected %lu) - dropped",
+ (unsigned long) pos,
+ (unsigned long) parse->chbind_len);
+ return -1;
+ }
+ if (!found) {
+ chbind_config->response_cb(
+ chbind_config->ctx,
+ code, chbind_config->nsid,
+ NULL, 0);
+ }
+
+ }
+ return 0;
+}
+
+static int eap_ttls_process_phase2_eap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct ttls_parse_avp *parse,
+ struct wpabuf **resp)
+{
+ struct eap_hdr *hdr;
+ size_t len;
+
+ if (parse->eapdata == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: No EAP Message in the "
+ "packet - dropped");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP",
+ parse->eapdata, parse->eap_len);
+ hdr = (struct eap_hdr *) parse->eapdata;
+
+ if (parse->eap_len < sizeof(*hdr)) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 EAP "
+ "frame (len=%lu, expected %lu or more) - dropped",
+ (unsigned long) parse->eap_len,
+ (unsigned long) sizeof(*hdr));
+ return -1;
+ }
+ len = be_to_host16(hdr->length);
+ if (len > parse->eap_len) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Length mismatch in Phase 2 "
+ "EAP frame (EAP hdr len=%lu, EAP data len in "
+ "AVP=%lu)",
+ (unsigned long) len,
+ (unsigned long) parse->eap_len);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: received Phase 2: code=%d "
+ "identifier=%d length=%lu",
+ hdr->code, hdr->identifier, (unsigned long) len);
+ switch (hdr->code) {
+ case EAP_CODE_REQUEST:
+ if (eap_ttls_phase2_request(sm, data, ret, hdr, resp)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request "
+ "processing failed");
+ return -1;
+ }
+ break;
+ default:
+ wpa_printf(MSG_INFO, "EAP-TTLS: Unexpected code=%d in "
+ "Phase 2 EAP header", hdr->code);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct ttls_parse_avp *parse)
+{
++#ifdef EAP_MSCHAPv2
+ if (parse->mschapv2_error) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Received "
+ "MS-CHAP-Error - failed");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ /* Reply with empty data to ACK error */
+ return 1;
+ }
+
+ if (parse->mschapv2 == NULL) {
+#ifdef EAP_TNC
+ if (data->phase2_success && parse->eapdata) {
+ /*
+ * Allow EAP-TNC to be started after successfully
+ * completed MSCHAPV2.
+ */
+ return 1;
+ }
+#endif /* EAP_TNC */
+ wpa_printf(MSG_WARNING, "EAP-TTLS: no MS-CHAP2-Success AVP "
+ "received for Phase2 MSCHAPV2");
+ return -1;
+ }
+ if (parse->mschapv2[0] != data->ident) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Ident mismatch for Phase 2 "
+ "MSCHAPV2 (received Ident 0x%02x, expected 0x%02x)",
+ parse->mschapv2[0], data->ident);
+ return -1;
+ }
+ if (!data->auth_response_valid ||
+ mschapv2_verify_auth_response(data->auth_response,
+ parse->mschapv2 + 1, 42)) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid authenticator "
+ "response in Phase 2 MSCHAPV2 success request");
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 MSCHAPV2 "
+ "authentication succeeded");
- #if EAP_TTLS_VERSION > 0
- static void eap_ttls_final_phase_finished(struct eap_sm *sm,
- struct eap_ttls_data *data,
- struct eap_method_ret *ret,
- u8 identifier,
- struct wpabuf **out_data)
- {
- wpa_printf(MSG_DEBUG, "EAP-TTLS: FinalPhaseFinished received");
- wpa_printf(MSG_INFO, "EAP-TTLS: TLS/IA authentication succeeded");
- ret->methodState = METHOD_DONE;
- ret->decision = DECISION_UNCOND_SUCC;
- data->phase2_success = 1;
- *out_data = eap_ttls_build_phase_finished(sm, data, identifier, 1);
- eap_ttls_v1_derive_key(sm, data);
- }
- #endif /* EAP_TTLS_VERSION */
-
-
++ ret->methodState = METHOD_DONE;
++ ret->decision = DECISION_UNCOND_SUCC;
++ data->phase2_success = 1;
+
+ /*
+ * Reply with empty data; authentication server will reply
+ * with EAP-Success after this.
+ */
+ return 1;
++#else /* EAP_MSCHAPv2 */
++ wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build");
++ return -1;
++#endif /* EAP_MSCHAPv2 */
+}
+
+
+#ifdef EAP_TNC
+static int eap_ttls_process_tnc_start(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct ttls_parse_avp *parse,
+ struct wpabuf **resp)
+{
+ /* TNC uses inner EAP method after non-EAP TTLS phase 2. */
+ if (parse->eapdata == NULL) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received "
+ "unexpected tunneled data (no EAP)");
+ return -1;
+ }
+
+ if (!data->ready_for_tnc) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received "
+ "EAP after non-EAP, but not ready for TNC");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed "
+ "non-EAP method");
+ data->tnc_started = 1;
+
+ if (eap_ttls_process_phase2_eap(sm, data, ret, parse, resp) < 0)
+ return -1;
+
+ return 0;
+}
+#endif /* EAP_TNC */
+
+
+static int eap_ttls_process_decrypted(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ u8 identifier,
+ struct ttls_parse_avp *parse,
+ struct wpabuf *in_decrypted,
+ struct wpabuf **out_data)
+{
+ struct wpabuf *resp = NULL;
+ struct eap_peer_config *config = eap_get_config(sm);
+ int res;
+ enum phase2_types phase2_type = data->phase2_type;
+
+#ifdef EAP_TNC
+ if (data->tnc_started)
+ phase2_type = EAP_TTLS_PHASE2_EAP;
+#endif /* EAP_TNC */
+
+ /* handle channel binding response here */
+ if (parse->chbind_data) {
+ /* received channel binding repsonse */
+ if (eap_ttls_process_chbind(sm, data, ret, parse, &resp) < 0)
+ return -1;
+ if (data->done_butfor_cb) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = data->cbDecision;
+ data->phase2_success = 1;
+ return 1; /*request ack*/
+ }
+ }
+
+ switch (phase2_type) {
+ case EAP_TTLS_PHASE2_EAP:
+ if (eap_ttls_process_phase2_eap(sm, data, ret, parse, &resp) <
+ 0)
+ return -1;
+ break;
+ case EAP_TTLS_PHASE2_MSCHAPV2:
+ res = eap_ttls_process_phase2_mschapv2(sm, data, ret, parse);
+#ifdef EAP_TNC
+ if (res == 1 && parse->eapdata && data->phase2_success) {
+ /*
+ * TNC may be required as the next
+ * authentication method within the tunnel.
+ */
+ ret->methodState = METHOD_MAY_CONT;
+ data->ready_for_tnc = 1;
+ if (eap_ttls_process_tnc_start(sm, data, ret, parse,
+ &resp) == 0)
+ break;
+ }
+#endif /* EAP_TNC */
+ return res;
+ case EAP_TTLS_PHASE2_MSCHAP:
+ case EAP_TTLS_PHASE2_PAP:
+ case EAP_TTLS_PHASE2_CHAP:
+#ifdef EAP_TNC
+ if (eap_ttls_process_tnc_start(sm, data, ret, parse, &resp) <
+ 0)
+ return -1;
+ break;
+#else /* EAP_TNC */
+ /* EAP-TTLS/{MSCHAP,PAP,CHAP} should not send any TLS tunneled
+ * requests to the supplicant */
+ wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received unexpected "
+ "tunneled data");
+ return -1;
+#endif /* EAP_TNC */
+ }
+
+ if (!resp && (config->pending_req_identity ||
+ config->pending_req_password ||
+ config->pending_req_otp ||
+ config->pending_req_new_password)) {
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = wpabuf_dup(in_decrypted);
+ return 0;
+ }
+
+ /* issue channel binding request when appropriate */
+ if (config->chbind_config && config->chbind_config_len > 0 &&
+ !data->chbind_req_sent) {
+ if (eap_ttls_add_chbind_request(sm, data, &resp) < 0)
+ return -1;
+ data->chbind_req_sent = 1;
+ if (ret->methodState == METHOD_DONE) {
+ data->done_butfor_cb = 1;
+ data->cbDecision = ret->decision;
+ ret->methodState = METHOD_MAY_CONT;
+ }
+ }
+
+ if (resp) {
+ if (eap_ttls_encrypt_response(sm, data, resp, identifier,
+ out_data) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
- #if EAP_TTLS_VERSION > 0
- if (data->ttls_version > 0 &&
- (in_decrypted == NULL || wpabuf_len(in_decrypted) == 0) &&
- tls_connection_ia_final_phase_finished(sm->ssl_ctx,
- data->ssl.conn)) {
- eap_ttls_final_phase_finished(sm, data, ret, identifier,
- out_data);
- goto done;
- }
- #endif /* EAP_TTLS_VERSION */
-
+static int eap_ttls_implicit_identity_request(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ u8 identifier,
+ struct wpabuf **out_data)
+{
+ int retval = 0;
+ struct eap_hdr *hdr;
+ struct wpabuf *resp;
+
+ hdr = (struct eap_hdr *) eap_ttls_fake_identity_request();
+ if (hdr == NULL) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return -1;
+ }
+
+ resp = NULL;
+ if (eap_ttls_phase2_request(sm, data, ret, hdr, &resp)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request "
+ "processing failed");
+ retval = -1;
+ } else {
++ struct eap_peer_config *config = eap_get_config(sm);
++ if (resp == NULL &&
++ (config->pending_req_identity ||
++ config->pending_req_password ||
++ config->pending_req_otp ||
++ config->pending_req_new_password)) {
++ /*
++ * Use empty buffer to force implicit request
++ * processing when EAP request is re-processed after
++ * user input.
++ */
++ wpabuf_free(data->pending_phase2_req);
++ data->pending_phase2_req = wpabuf_alloc(0);
++ }
++
+ retval = eap_ttls_encrypt_response(sm, data, resp, identifier,
+ out_data);
+ }
+
+ os_free(hdr);
+
+ if (retval < 0) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ }
+
+ return retval;
+}
+
+
+static int eap_ttls_phase2_start(struct eap_sm *sm, struct eap_ttls_data *data,
+ struct eap_method_ret *ret, u8 identifier,
+ struct wpabuf **out_data)
+{
+ data->phase2_start = 0;
+
+ /*
+ * EAP-TTLS does not use Phase2 on fast re-auth; this must be done only
+ * if TLS part was indeed resuming a previous session. Most
+ * Authentication Servers terminate EAP-TTLS before reaching this
+ * point, but some do not. Make wpa_supplicant stop phase 2 here, if
+ * needed.
+ */
+ if (data->reauth &&
+ tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Session resumption - "
+ "skip phase 2");
+ *out_data = eap_peer_tls_build_ack(identifier, EAP_TYPE_TTLS,
+ data->ttls_version);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ data->phase2_success = 1;
+ return 0;
+ }
+
+ return eap_ttls_implicit_identity_request(sm, data, ret, identifier,
+ out_data);
+}
+
+
+static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data,
+ struct eap_method_ret *ret, u8 identifier,
+ const struct wpabuf *in_data,
+ struct wpabuf **out_data)
+{
+ struct wpabuf *in_decrypted = NULL;
+ int retval = 0;
+ struct ttls_parse_avp parse;
+
+ os_memset(&parse, 0, sizeof(parse));
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for"
+ " Phase 2",
+ in_data ? (unsigned long) wpabuf_len(in_data) : 0);
+
+ if (data->pending_phase2_req) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 request - "
+ "skip decryption and use old data");
+ /* Clear TLS reassembly state. */
+ eap_peer_tls_reset_input(&data->ssl);
+
+ in_decrypted = data->pending_phase2_req;
+ data->pending_phase2_req = NULL;
+ if (wpabuf_len(in_decrypted) == 0) {
+ wpabuf_free(in_decrypted);
+ return eap_ttls_implicit_identity_request(
+ sm, data, ret, identifier, out_data);
+ }
+ goto continue_req;
+ }
+
+ if ((in_data == NULL || wpabuf_len(in_data) == 0) &&
+ data->phase2_start) {
+ return eap_ttls_phase2_start(sm, data, ret, identifier,
+ out_data);
+ }
+
+ if (in_data == NULL || wpabuf_len(in_data) == 0) {
+ /* Received TLS ACK - requesting more fragments */
+ return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS,
+ data->ttls_version,
+ identifier, NULL, out_data);
+ }
+
+ retval = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);
+ if (retval)
+ goto done;
+
- static int eap_ttls_process_start(struct eap_sm *sm,
- struct eap_ttls_data *data, u8 flags,
- struct eap_method_ret *ret)
- {
- struct eap_peer_config *config = eap_get_config(sm);
-
- wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own ver=%d)",
- flags & EAP_TLS_VERSION_MASK, data->ttls_version);
- #if EAP_TTLS_VERSION > 0
- if ((flags & EAP_TLS_VERSION_MASK) < data->ttls_version)
- data->ttls_version = flags & EAP_TLS_VERSION_MASK;
- if (data->force_ttls_version >= 0 &&
- data->force_ttls_version != data->ttls_version) {
- wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to select "
- "forced TTLS version %d",
- data->force_ttls_version);
- ret->methodState = METHOD_DONE;
- ret->decision = DECISION_FAIL;
- ret->allowNotifications = FALSE;
- return -1;
- }
- wpa_printf(MSG_DEBUG, "EAP-TTLS: Using TTLS version %d",
- data->ttls_version);
-
- if (data->ttls_version > 0)
- data->ssl.tls_ia = 1;
- #endif /* EAP_TTLS_VERSION */
- if (!data->ssl_initialized &&
- eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
- wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
- return -1;
- }
- data->ssl_initialized = 1;
-
- wpa_printf(MSG_DEBUG, "EAP-TTLS: Start");
-
- return 0;
- }
-
-
+continue_req:
+ data->phase2_start = 0;
+
+ if (eap_ttls_parse_avps(in_decrypted, &parse) < 0) {
+ retval = -1;
+ goto done;
+ }
+
+ retval = eap_ttls_process_decrypted(sm, data, ret, identifier,
+ &parse, in_decrypted, out_data);
+
+done:
+ wpabuf_free(in_decrypted);
+ os_free(parse.eapdata);
+
+ if (retval < 0) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ }
+
+ return retval;
+}
+
+
- const u8 *in_data, size_t in_len,
+static int eap_ttls_process_handshake(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ u8 identifier,
- in_data, in_len, out_data);
++ const struct wpabuf *in_data,
+ struct wpabuf **out_data)
+{
+ int res;
+
+ res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS,
+ data->ttls_version, identifier,
- if (data->ttls_version == 0)
- eap_ttls_v0_derive_key(sm, data);
++ in_data, out_data);
++ if (res < 0) {
++ wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS processing failed");
++ ret->methodState = METHOD_DONE;
++ ret->decision = DECISION_FAIL;
++ return -1;
++ }
+
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to "
+ "Phase 2");
+ if (data->resuming) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: fast reauth - may "
+ "skip Phase 2");
+ ret->decision = DECISION_COND_SUCC;
+ ret->methodState = METHOD_MAY_CONT;
+ }
+ data->phase2_start = 1;
- struct wpabuf msg;
++ eap_ttls_v0_derive_key(sm, data);
+
+ if (*out_data == NULL || wpabuf_len(*out_data) == 0) {
+ if (eap_ttls_decrypt(sm, data, ret, identifier,
+ NULL, out_data)) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: "
+ "failed to process early "
+ "start for Phase 2");
+ }
+ res = 0;
+ }
+ data->resuming = 0;
+ }
+
+ if (res == 2) {
- wpabuf_set(&msg, in_data, in_len);
- res = eap_ttls_decrypt(sm, data, ret, identifier, &msg,
+ /*
+ * Application data included in the handshake message.
+ */
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = *out_data;
+ *out_data = NULL;
- if (data->ttls_version == 0 && ret->methodState == METHOD_DONE) {
++ res = eap_ttls_decrypt(sm, data, ret, identifier, in_data,
+ out_data);
+ }
+
+ return res;
+}
+
+
+static void eap_ttls_check_auth_status(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret)
+{
- } else if (data->ttls_version == 0 &&
- ret->methodState == METHOD_MAY_CONT &&
++ if (ret->methodState == METHOD_DONE) {
+ ret->allowNotifications = FALSE;
+ if (ret->decision == DECISION_UNCOND_SUCC ||
+ ret->decision == DECISION_COND_SUCC) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication "
+ "completed successfully");
+ data->phase2_success = 1;
+#ifdef EAP_TNC
+ if (!data->ready_for_tnc && !data->tnc_started) {
+ /*
+ * TNC may be required as the next
+ * authentication method within the tunnel.
+ */
+ ret->methodState = METHOD_MAY_CONT;
+ data->ready_for_tnc = 1;
+ }
+#endif /* EAP_TNC */
+ }
- if (eap_ttls_process_start(sm, data, flags, ret) < 0)
- return NULL;
++ } else if (ret->methodState == METHOD_MAY_CONT &&
+ (ret->decision == DECISION_UNCOND_SUCC ||
+ ret->decision == DECISION_COND_SUCC)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication "
+ "completed successfully (MAY_CONT)");
+ data->phase2_success = 1;
+ }
+}
+
+
+static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ size_t left;
+ int res;
+ u8 flags, id;
+ struct wpabuf *resp;
+ const u8 *pos;
+ struct eap_ttls_data *data = priv;
++ struct wpabuf msg;
+
+ pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TTLS, ret,
+ reqData, &left, &flags);
+ if (pos == NULL)
+ return NULL;
+ id = eap_get_id(reqData);
+
+ if (flags & EAP_TLS_FLAGS_START) {
- } else if (!data->ssl_initialized) {
- wpa_printf(MSG_DEBUG, "EAP-TTLS: First message did not "
- "include Start flag");
- ret->methodState = METHOD_DONE;
- ret->decision = DECISION_FAIL;
- ret->allowNotifications = FALSE;
- return NULL;
++ wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own "
++ "ver=%d)", flags & EAP_TLS_VERSION_MASK,
++ data->ttls_version);
+
+ /* RFC 5281, Ch. 9.2:
+ * "This packet MAY contain additional information in the form
+ * of AVPs, which may provide useful hints to the client"
+ * For now, ignore any potential extra data.
+ */
+ left = 0;
- struct wpabuf msg;
- wpabuf_set(&msg, pos, left);
+ }
+
++ wpabuf_set(&msg, pos, left);
++
+ resp = NULL;
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+ !data->resuming) {
- pos, left, &resp);
+ res = eap_ttls_decrypt(sm, data, ret, id, &msg, &resp);
+ } else {
+ res = eap_ttls_process_handshake(sm, data, ret, id,
- os_free(data->key_data);
- data->key_data = NULL;
++ &msg, &resp);
+ }
+
+ eap_ttls_check_auth_status(sm, data, ret);
+
+ /* FIX: what about res == -1? Could just move all error processing into
+ * the other functions and get rid of this res==1 case here. */
+ if (res == 1) {
+ wpabuf_free(resp);
+ return eap_peer_tls_build_ack(id, EAP_TYPE_TTLS,
+ data->ttls_version);
+ }
+ return resp;
+}
+
+
+static Boolean eap_ttls_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ return tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+ data->phase2_success;
+}
+
+
+static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = NULL;
+#ifdef EAP_TNC
+ data->ready_for_tnc = 0;
+ data->tnc_started = 0;
+#endif /* EAP_TNC */
+}
+
+
+static void * eap_ttls_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
- if (ret < 0 || (size_t) ret >= buflen - len)
++ eap_ttls_free_key(data);
++ os_free(data->session_id);
++ data->session_id = NULL;
+ if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
+ os_free(data);
+ return NULL;
+ }
+ if (data->phase2_priv && data->phase2_method &&
+ data->phase2_method->init_for_reauth)
+ data->phase2_method->init_for_reauth(sm, data->phase2_priv);
+ data->phase2_start = 0;
+ data->phase2_success = 0;
+ data->resuming = 1;
+ data->reauth = 1;
+ return priv;
+}
+
+
+static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf,
+ size_t buflen, int verbose)
+{
+ struct eap_ttls_data *data = priv;
+ int len, ret;
+
+ len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
+ ret = os_snprintf(buf + len, buflen - len,
+ "EAP-TTLSv%d Phase2 method=",
+ data->ttls_version);
- if (ret < 0 || (size_t) ret >= buflen - len)
++ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ switch (data->phase2_type) {
+ case EAP_TTLS_PHASE2_EAP:
+ ret = os_snprintf(buf + len, buflen - len, "EAP-%s\n",
+ data->phase2_method ?
+ data->phase2_method->name : "?");
+ break;
+ case EAP_TTLS_PHASE2_MSCHAPV2:
+ ret = os_snprintf(buf + len, buflen - len, "MSCHAPV2\n");
+ break;
+ case EAP_TTLS_PHASE2_MSCHAP:
+ ret = os_snprintf(buf + len, buflen - len, "MSCHAP\n");
+ break;
+ case EAP_TTLS_PHASE2_PAP:
+ ret = os_snprintf(buf + len, buflen - len, "PAP\n");
+ break;
+ case EAP_TTLS_PHASE2_CHAP:
+ ret = os_snprintf(buf + len, buflen - len, "CHAP\n");
+ break;
+ default:
+ ret = 0;
+ break;
+ }
++ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ return len;
+}
+
+
+static Boolean eap_ttls_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ return data->key_data != NULL && data->phase2_success;
+}
+
+
+static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_ttls_data *data = priv;
+ u8 *key;
+
+ if (data->key_data == NULL || !data->phase2_success)
+ return NULL;
+
+ key = os_malloc(EAP_TLS_KEY_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_TLS_KEY_LEN;
+ os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
+
+ return key;
+}
+
+
++static u8 * eap_ttls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
++{
++ struct eap_ttls_data *data = priv;
++ u8 *id;
++
++ if (data->session_id == NULL || !data->phase2_success)
++ return NULL;
++
++ id = os_malloc(data->id_len);
++ if (id == NULL)
++ return NULL;
++
++ *len = data->id_len;
++ os_memcpy(id, data->session_id, data->id_len);
++
++ return id;
++}
++
++
++static u8 * eap_ttls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
++{
++ struct eap_ttls_data *data = priv;
++ u8 *key;
++
++ if (data->key_data == NULL)
++ return NULL;
++
++ key = os_malloc(EAP_EMSK_LEN);
++ if (key == NULL)
++ return NULL;
++
++ *len = EAP_EMSK_LEN;
++ os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
++
++ return key;
++}
++
++
+int eap_peer_ttls_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_ttls_init;
+ eap->deinit = eap_ttls_deinit;
+ eap->process = eap_ttls_process;
+ eap->isKeyAvailable = eap_ttls_isKeyAvailable;
+ eap->getKey = eap_ttls_getKey;
++ eap->getSessionId = eap_ttls_get_session_id;
+ eap->get_status = eap_ttls_get_status;
+ eap->has_reauth_data = eap_ttls_has_reauth_data;
+ eap->deinit_for_reauth = eap_ttls_deinit_for_reauth;
+ eap->init_for_reauth = eap_ttls_init_for_reauth;
++ eap->get_emsk = eap_ttls_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
--- /dev/null
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+/*
+ * wpa_supplicant/hostapd / common helper functions, etc.
+ * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
+ *
- #ifdef CONFIG_TI_COMPILER
- #define __BIG_ENDIAN 4321
- #define __LITTLE_ENDIAN 1234
- #ifdef __big_endian__
- #define __BYTE_ORDER __BIG_ENDIAN
- #else
- #define __BYTE_ORDER __LITTLE_ENDIAN
- #endif
- #endif /* CONFIG_TI_COMPILER */
-
- #ifdef __SYMBIAN32__
- #define __BIG_ENDIAN 4321
- #define __LITTLE_ENDIAN 1234
- #define __BYTE_ORDER __LITTLE_ENDIAN
- #endif /* __SYMBIAN32__ */
-
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
+ */
+
+#ifndef COMMON_H
+#define COMMON_H
+
+#include "os.h"
+
+#if defined(__linux__) || defined(__GLIBC__)
+#include <endian.h>
+#include <byteswap.h>
+#endif /* __linux__ */
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || \
+ defined(__OpenBSD__)
+#include <sys/types.h>
+#include <sys/endian.h>
+#define __BYTE_ORDER _BYTE_ORDER
+#define __LITTLE_ENDIAN _LITTLE_ENDIAN
+#define __BIG_ENDIAN _BIG_ENDIAN
+#ifdef __OpenBSD__
+#define bswap_16 swap16
+#define bswap_32 swap32
+#define bswap_64 swap64
+#else /* __OpenBSD__ */
+#define bswap_16 bswap16
+#define bswap_32 bswap32
+#define bswap_64 bswap64
+#endif /* __OpenBSD__ */
+#endif /* defined(__FreeBSD__) || defined(__NetBSD__) ||
+ * defined(__DragonFly__) || defined(__OpenBSD__) */
+
+#ifdef __APPLE__
+#include <sys/types.h>
+#include <machine/endian.h>
+#define __BYTE_ORDER _BYTE_ORDER
+#define __LITTLE_ENDIAN _LITTLE_ENDIAN
+#define __BIG_ENDIAN _BIG_ENDIAN
+static inline unsigned short bswap_16(unsigned short v)
+{
+ return ((v & 0xff) << 8) | (v >> 8);
+}
+
+static inline unsigned int bswap_32(unsigned int v)
+{
+ return ((v & 0xff) << 24) | ((v & 0xff00) << 8) |
+ ((v & 0xff0000) >> 8) | (v >> 24);
+}
+#endif /* __APPLE__ */
+
- #ifdef CONFIG_TI_COMPILER
- #ifdef _LLONG_AVAILABLE
- typedef unsigned long long u64;
- #else
- /*
- * TODO: 64-bit variable not available. Using long as a workaround to test the
- * build, but this will likely not work for all operations.
- */
- typedef unsigned long u64;
- #endif
- typedef unsigned int u32;
- typedef unsigned short u16;
- typedef unsigned char u8;
- #define WPA_TYPES_DEFINED
- #endif /* CONFIG_TI_COMPILER */
-
- #ifdef __SYMBIAN32__
- #define __REMOVE_PLATSEC_DIAGNOSTICS__
- #include <e32def.h>
- typedef TUint64 u64;
- typedef TUint32 u32;
- typedef TUint16 u16;
- typedef TUint8 u8;
- #define WPA_TYPES_DEFINED
- #endif /* __SYMBIAN32__ */
-
+#ifdef CONFIG_NATIVE_WINDOWS
+#ifdef CONFIG_IPV6
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <winsock.h>
+#endif
+
+typedef int socklen_t;
+
+#ifndef MSG_DONTWAIT
+#define MSG_DONTWAIT 0 /* not supported */
+#endif
+
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#ifdef _MSC_VER
+#ifndef __cplusplus
+#define inline __inline
+#endif
+
+#undef vsnprintf
+#define vsnprintf _vsnprintf
+#undef close
+#define close closesocket
+#endif /* _MSC_VER */
+
+
+/* Define platform specific integer types */
+
+#ifdef _MSC_VER
+typedef UINT64 u64;
+typedef UINT32 u32;
+typedef UINT16 u16;
+typedef UINT8 u8;
+typedef INT64 s64;
+typedef INT32 s32;
+typedef INT16 s16;
+typedef INT8 s8;
+#define WPA_TYPES_DEFINED
+#endif /* _MSC_VER */
+
+#ifdef __vxworks
+typedef unsigned long long u64;
+typedef UINT32 u32;
+typedef UINT16 u16;
+typedef UINT8 u8;
+typedef long long s64;
+typedef INT32 s32;
+typedef INT16 s16;
+typedef INT8 s8;
+#define WPA_TYPES_DEFINED
+#endif /* __vxworks */
+
- #define WPA_GET_BE16(a) ((u16) (((a)[0] << 8) | (a)[1]))
- #define WPA_PUT_BE16(a, val) \
- do { \
- (a)[0] = ((u16) (val)) >> 8; \
- (a)[1] = ((u16) (val)) & 0xff; \
- } while (0)
-
- #define WPA_GET_LE16(a) ((u16) (((a)[1] << 8) | (a)[0]))
- #define WPA_PUT_LE16(a, val) \
- do { \
- (a)[1] = ((u16) (val)) >> 8; \
- (a)[0] = ((u16) (val)) & 0xff; \
- } while (0)
-
- #define WPA_GET_BE24(a) ((((u32) (a)[0]) << 16) | (((u32) (a)[1]) << 8) | \
- ((u32) (a)[2]))
- #define WPA_PUT_BE24(a, val) \
- do { \
- (a)[0] = (u8) ((((u32) (val)) >> 16) & 0xff); \
- (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff); \
- (a)[2] = (u8) (((u32) (val)) & 0xff); \
- } while (0)
-
- #define WPA_GET_BE32(a) ((((u32) (a)[0]) << 24) | (((u32) (a)[1]) << 16) | \
- (((u32) (a)[2]) << 8) | ((u32) (a)[3]))
- #define WPA_PUT_BE32(a, val) \
- do { \
- (a)[0] = (u8) ((((u32) (val)) >> 24) & 0xff); \
- (a)[1] = (u8) ((((u32) (val)) >> 16) & 0xff); \
- (a)[2] = (u8) ((((u32) (val)) >> 8) & 0xff); \
- (a)[3] = (u8) (((u32) (val)) & 0xff); \
- } while (0)
-
- #define WPA_GET_LE32(a) ((((u32) (a)[3]) << 24) | (((u32) (a)[2]) << 16) | \
- (((u32) (a)[1]) << 8) | ((u32) (a)[0]))
- #define WPA_PUT_LE32(a, val) \
- do { \
- (a)[3] = (u8) ((((u32) (val)) >> 24) & 0xff); \
- (a)[2] = (u8) ((((u32) (val)) >> 16) & 0xff); \
- (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff); \
- (a)[0] = (u8) (((u32) (val)) & 0xff); \
- } while (0)
-
- #define WPA_GET_BE64(a) ((((u64) (a)[0]) << 56) | (((u64) (a)[1]) << 48) | \
- (((u64) (a)[2]) << 40) | (((u64) (a)[3]) << 32) | \
- (((u64) (a)[4]) << 24) | (((u64) (a)[5]) << 16) | \
- (((u64) (a)[6]) << 8) | ((u64) (a)[7]))
- #define WPA_PUT_BE64(a, val) \
- do { \
- (a)[0] = (u8) (((u64) (val)) >> 56); \
- (a)[1] = (u8) (((u64) (val)) >> 48); \
- (a)[2] = (u8) (((u64) (val)) >> 40); \
- (a)[3] = (u8) (((u64) (val)) >> 32); \
- (a)[4] = (u8) (((u64) (val)) >> 24); \
- (a)[5] = (u8) (((u64) (val)) >> 16); \
- (a)[6] = (u8) (((u64) (val)) >> 8); \
- (a)[7] = (u8) (((u64) (val)) & 0xff); \
- } while (0)
-
- #define WPA_GET_LE64(a) ((((u64) (a)[7]) << 56) | (((u64) (a)[6]) << 48) | \
- (((u64) (a)[5]) << 40) | (((u64) (a)[4]) << 32) | \
- (((u64) (a)[3]) << 24) | (((u64) (a)[2]) << 16) | \
- (((u64) (a)[1]) << 8) | ((u64) (a)[0]))
+#ifndef WPA_TYPES_DEFINED
+#ifdef CONFIG_USE_INTTYPES_H
+#include <inttypes.h>
+#else
+#include <stdint.h>
+#endif
+typedef uint64_t u64;
+typedef uint32_t u32;
+typedef uint16_t u16;
+typedef uint8_t u8;
+typedef int64_t s64;
+typedef int32_t s32;
+typedef int16_t s16;
+typedef int8_t s8;
+#define WPA_TYPES_DEFINED
+#endif /* !WPA_TYPES_DEFINED */
+
+
+/* Define platform specific byte swapping macros */
+
+#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS)
+
+static inline unsigned short wpa_swap_16(unsigned short v)
+{
+ return ((v & 0xff) << 8) | (v >> 8);
+}
+
+static inline unsigned int wpa_swap_32(unsigned int v)
+{
+ return ((v & 0xff) << 24) | ((v & 0xff00) << 8) |
+ ((v & 0xff0000) >> 8) | (v >> 24);
+}
+
+#define le_to_host16(n) (n)
+#define host_to_le16(n) (n)
+#define be_to_host16(n) wpa_swap_16(n)
+#define host_to_be16(n) wpa_swap_16(n)
+#define le_to_host32(n) (n)
++#define host_to_le32(n) (n)
+#define be_to_host32(n) wpa_swap_32(n)
+#define host_to_be32(n) wpa_swap_32(n)
+
+#define WPA_BYTE_SWAP_DEFINED
+
+#endif /* __CYGWIN__ || CONFIG_NATIVE_WINDOWS */
+
+
+#ifndef WPA_BYTE_SWAP_DEFINED
+
+#ifndef __BYTE_ORDER
+#ifndef __LITTLE_ENDIAN
+#ifndef __BIG_ENDIAN
+#define __LITTLE_ENDIAN 1234
+#define __BIG_ENDIAN 4321
+#if defined(sparc)
+#define __BYTE_ORDER __BIG_ENDIAN
+#endif
+#endif /* __BIG_ENDIAN */
+#endif /* __LITTLE_ENDIAN */
+#endif /* __BYTE_ORDER */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define le_to_host16(n) ((__force u16) (le16) (n))
+#define host_to_le16(n) ((__force le16) (u16) (n))
+#define be_to_host16(n) bswap_16((__force u16) (be16) (n))
+#define host_to_be16(n) ((__force be16) bswap_16((n)))
+#define le_to_host32(n) ((__force u32) (le32) (n))
+#define host_to_le32(n) ((__force le32) (u32) (n))
+#define be_to_host32(n) bswap_32((__force u32) (be32) (n))
+#define host_to_be32(n) ((__force be32) bswap_32((n)))
+#define le_to_host64(n) ((__force u64) (le64) (n))
+#define host_to_le64(n) ((__force le64) (u64) (n))
+#define be_to_host64(n) bswap_64((__force u64) (be64) (n))
+#define host_to_be64(n) ((__force be64) bswap_64((n)))
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define le_to_host16(n) bswap_16(n)
+#define host_to_le16(n) bswap_16(n)
+#define be_to_host16(n) (n)
+#define host_to_be16(n) (n)
+#define le_to_host32(n) bswap_32(n)
++#define host_to_le32(n) bswap_32(n)
+#define be_to_host32(n) (n)
+#define host_to_be32(n) (n)
+#define le_to_host64(n) bswap_64(n)
+#define host_to_le64(n) bswap_64(n)
+#define be_to_host64(n) (n)
+#define host_to_be64(n) (n)
+#ifndef WORDS_BIGENDIAN
+#define WORDS_BIGENDIAN
+#endif
+#else
+#error Could not determine CPU byte order
+#endif
+
+#define WPA_BYTE_SWAP_DEFINED
+#endif /* !WPA_BYTE_SWAP_DEFINED */
+
+
+/* Macros for handling unaligned memory accesses */
+
- #define BIT(x) (1 << (x))
++static inline u16 WPA_GET_BE16(const u8 *a)
++{
++ return (a[0] << 8) | a[1];
++}
++
++static inline void WPA_PUT_BE16(u8 *a, u16 val)
++{
++ a[0] = val >> 8;
++ a[1] = val & 0xff;
++}
++
++static inline u16 WPA_GET_LE16(const u8 *a)
++{
++ return (a[1] << 8) | a[0];
++}
++
++static inline void WPA_PUT_LE16(u8 *a, u16 val)
++{
++ a[1] = val >> 8;
++ a[0] = val & 0xff;
++}
++
++static inline u32 WPA_GET_BE24(const u8 *a)
++{
++ return (a[0] << 16) | (a[1] << 8) | a[2];
++}
++
++static inline void WPA_PUT_BE24(u8 *a, u32 val)
++{
++ a[0] = (val >> 16) & 0xff;
++ a[1] = (val >> 8) & 0xff;
++ a[2] = val & 0xff;
++}
++
++static inline u32 WPA_GET_BE32(const u8 *a)
++{
++ return ((u32) a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3];
++}
++
++static inline void WPA_PUT_BE32(u8 *a, u32 val)
++{
++ a[0] = (val >> 24) & 0xff;
++ a[1] = (val >> 16) & 0xff;
++ a[2] = (val >> 8) & 0xff;
++ a[3] = val & 0xff;
++}
++
++static inline u32 WPA_GET_LE32(const u8 *a)
++{
++ return ((u32) a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0];
++}
++
++static inline void WPA_PUT_LE32(u8 *a, u32 val)
++{
++ a[3] = (val >> 24) & 0xff;
++ a[2] = (val >> 16) & 0xff;
++ a[1] = (val >> 8) & 0xff;
++ a[0] = val & 0xff;
++}
++
++static inline u64 WPA_GET_BE64(const u8 *a)
++{
++ return (((u64) a[0]) << 56) | (((u64) a[1]) << 48) |
++ (((u64) a[2]) << 40) | (((u64) a[3]) << 32) |
++ (((u64) a[4]) << 24) | (((u64) a[5]) << 16) |
++ (((u64) a[6]) << 8) | ((u64) a[7]);
++}
++
++static inline void WPA_PUT_BE64(u8 *a, u64 val)
++{
++ a[0] = val >> 56;
++ a[1] = val >> 48;
++ a[2] = val >> 40;
++ a[3] = val >> 32;
++ a[4] = val >> 24;
++ a[5] = val >> 16;
++ a[6] = val >> 8;
++ a[7] = val & 0xff;
++}
++
++static inline u64 WPA_GET_LE64(const u8 *a)
++{
++ return (((u64) a[7]) << 56) | (((u64) a[6]) << 48) |
++ (((u64) a[5]) << 40) | (((u64) a[4]) << 32) |
++ (((u64) a[3]) << 24) | (((u64) a[2]) << 16) |
++ (((u64) a[1]) << 8) | ((u64) a[0]);
++}
++
++static inline void WPA_PUT_LE64(u8 *a, u64 val)
++{
++ a[7] = val >> 56;
++ a[6] = val >> 48;
++ a[5] = val >> 40;
++ a[4] = val >> 32;
++ a[3] = val >> 24;
++ a[2] = val >> 16;
++ a[1] = val >> 8;
++ a[0] = val & 0xff;
++}
+
+
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif
++#ifndef ETH_HLEN
++#define ETH_HLEN 14
++#endif
+#ifndef IFNAMSIZ
+#define IFNAMSIZ 16
+#endif
+#ifndef ETH_P_ALL
+#define ETH_P_ALL 0x0003
+#endif
++#ifndef ETH_P_80211_ENCAP
++#define ETH_P_80211_ENCAP 0x890d /* TDLS comes under this category */
++#endif
+#ifndef ETH_P_PAE
+#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */
+#endif /* ETH_P_PAE */
+#ifndef ETH_P_EAPOL
+#define ETH_P_EAPOL ETH_P_PAE
+#endif /* ETH_P_EAPOL */
+#ifndef ETH_P_RSN_PREAUTH
+#define ETH_P_RSN_PREAUTH 0x88c7
+#endif /* ETH_P_RSN_PREAUTH */
+#ifndef ETH_P_RRB
+#define ETH_P_RRB 0x890D
+#endif /* ETH_P_RRB */
+
+
+#ifdef __GNUC__
+#define PRINTF_FORMAT(a,b) __attribute__ ((format (printf, (a), (b))))
+#define STRUCT_PACKED __attribute__ ((packed))
+#define UNUSED __attribute__ ((unused))
+#else
+#define PRINTF_FORMAT(a,b)
+#define STRUCT_PACKED
+#define UNUSED
+#endif
+
+
+#ifdef CONFIG_ANSI_C_EXTRA
+
+#if !defined(_MSC_VER) || _MSC_VER < 1400
+/* snprintf - used in number of places; sprintf() is _not_ a good replacement
+ * due to possible buffer overflow; see, e.g.,
+ * http://www.ijs.si/software/snprintf/ for portable implementation of
+ * snprintf. */
+int snprintf(char *str, size_t size, const char *format, ...);
+
+/* vsnprintf - only used for wpa_msg() in wpa_supplicant.c */
+int vsnprintf(char *str, size_t size, const char *format, va_list ap);
+#endif /* !defined(_MSC_VER) || _MSC_VER < 1400 */
+
+/* getopt - only used in main.c */
+int getopt(int argc, char *const argv[], const char *optstring);
+extern char *optarg;
+extern int optind;
+
+#ifndef CONFIG_NO_SOCKLEN_T_TYPEDEF
+#ifndef __socklen_t_defined
+typedef int socklen_t;
+#endif
+#endif
+
+/* inline - define as __inline or just define it to be empty, if needed */
+#ifdef CONFIG_NO_INLINE
+#define inline
+#else
+#define inline __inline
+#endif
+
+#ifndef __func__
+#define __func__ "__func__ not defined"
+#endif
+
+#ifndef bswap_16
+#define bswap_16(a) ((((u16) (a) << 8) & 0xff00) | (((u16) (a) >> 8) & 0xff))
+#endif
+
+#ifndef bswap_32
+#define bswap_32(a) ((((u32) (a) << 24) & 0xff000000) | \
+ (((u32) (a) << 8) & 0xff0000) | \
+ (((u32) (a) >> 8) & 0xff00) | \
+ (((u32) (a) >> 24) & 0xff))
+#endif
+
+#ifndef MSG_DONTWAIT
+#define MSG_DONTWAIT 0
+#endif
+
+#ifdef _WIN32_WCE
+void perror(const char *s);
+#endif /* _WIN32_WCE */
+
+#endif /* CONFIG_ANSI_C_EXTRA */
+
+#ifndef MAC2STR
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
++
++/*
++ * Compact form for string representation of MAC address
++ * To be used, e.g., for constructing dbus paths for P2P Devices
++ */
++#define COMPACT_MACSTR "%02x%02x%02x%02x%02x%02x"
+#endif
+
+#ifndef BIT
++#define BIT(x) (1U << (x))
+#endif
+
+/*
+ * Definitions for sparse validation
+ * (http://kernel.org/pub/linux/kernel/people/josh/sparse/)
+ */
+#ifdef __CHECKER__
+#define __force __attribute__((force))
+#define __bitwise __attribute__((bitwise))
+#else
+#define __force
+#define __bitwise
+#endif
+
+typedef u16 __bitwise be16;
+typedef u16 __bitwise le16;
+typedef u32 __bitwise be32;
+typedef u32 __bitwise le32;
+typedef u64 __bitwise be64;
+typedef u64 __bitwise le64;
+
+#ifndef __must_check
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+#define __must_check __attribute__((__warn_unused_result__))
+#else
+#define __must_check
+#endif /* __GNUC__ */
+#endif /* __must_check */
+
++#ifndef __maybe_unused
++#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
++#define __maybe_unused __attribute__((unused))
++#else
++#define __maybe_unused
++#endif /* __GNUC__ */
++#endif /* __must_check */
++
+int hwaddr_aton(const char *txt, u8 *addr);
++int hwaddr_masked_aton(const char *txt, u8 *addr, u8 *mask, u8 maskable);
++int hwaddr_compact_aton(const char *txt, u8 *addr);
+int hwaddr_aton2(const char *txt, u8 *addr);
++int hex2byte(const char *hex);
+int hexstr2bin(const char *hex, u8 *buf, size_t len);
+void inc_byte_array(u8 *counter, size_t len);
+void wpa_get_ntp_timestamp(u8 *buf);
++int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...);
++int wpa_snprintf_hex_sep(char *buf, size_t buf_size, const u8 *data, size_t len,
++ char sep);
+int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len);
+int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data,
+ size_t len);
+
++int hwaddr_mask_txt(char *buf, size_t len, const u8 *addr, const u8 *mask);
++
+#ifdef CONFIG_NATIVE_WINDOWS
+void wpa_unicode2ascii_inplace(TCHAR *str);
+TCHAR * wpa_strdup_tchar(const char *str);
+#else /* CONFIG_NATIVE_WINDOWS */
+#define wpa_unicode2ascii_inplace(s) do { } while (0)
+#define wpa_strdup_tchar(s) strdup((s))
+#endif /* CONFIG_NATIVE_WINDOWS */
+
++void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len);
++size_t printf_decode(u8 *buf, size_t maxlen, const char *str);
++
+const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len);
+
++char * wpa_config_parse_string(const char *value, size_t *len);
++int is_hex(const u8 *data, size_t len);
++size_t merge_byte_arrays(u8 *res, size_t res_len,
++ const u8 *src1, size_t src1_len,
++ const u8 *src2, size_t src2_len);
++char * dup_binstr(const void *src, size_t len);
++
+static inline int is_zero_ether_addr(const u8 *a)
+{
+ return !(a[0] | a[1] | a[2] | a[3] | a[4] | a[5]);
+}
+
++static inline int is_broadcast_ether_addr(const u8 *a)
++{
++ return (a[0] & a[1] & a[2] & a[3] & a[4] & a[5]) == 0xff;
++}
++
++static inline int is_multicast_ether_addr(const u8 *a)
++{
++ return a[0] & 0x01;
++}
++
++#define broadcast_ether_addr (const u8 *) "\xff\xff\xff\xff\xff\xff"
++
+#include "wpa_debug.h"
+
+
++struct wpa_freq_range_list {
++ struct wpa_freq_range {
++ unsigned int min;
++ unsigned int max;
++ } *range;
++ unsigned int num;
++};
++
++int freq_range_list_parse(struct wpa_freq_range_list *res, const char *value);
++int freq_range_list_includes(const struct wpa_freq_range_list *list,
++ unsigned int freq);
++char * freq_range_list_str(const struct wpa_freq_range_list *list);
++
++int int_array_len(const int *a);
++void int_array_concat(int **res, const int *a);
++void int_array_sort_unique(int *a);
++void int_array_add_unique(int **res, int a);
++
++#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
++
++void str_clear_free(char *str);
++void bin_clear_free(void *bin, size_t len);
++
++int random_mac_addr(u8 *addr);
++int random_mac_addr_keep_oui(u8 *addr);
++
++const char * cstr_token(const char *str, const char *delim, const char **last);
++char * str_token(char *str, const char *delim, char **context);
++size_t utf8_escape(const char *inp, size_t in_size,
++ char *outp, size_t out_size);
++size_t utf8_unescape(const char *inp, size_t in_size,
++ char *outp, size_t out_size);
++int is_ctrl_char(char c);
++
++
+/*
+ * gcc 4.4 ends up generating strict-aliasing warnings about some very common
+ * networking socket uses that do not really result in a real problem and
+ * cannot be easily avoided with union-based type-punning due to struct
+ * definitions including another struct in system header files. To avoid having
+ * to fully disable strict-aliasing warnings, provide a mechanism to hide the
+ * typecast from aliasing for now. A cleaner solution will hopefully be found
+ * in the future to handle these cases.
+ */
+void * __hide_aliasing_typecast(void *foo);
+#define aliasing_hide_typecast(a,t) (t *) __hide_aliasing_typecast((a))
+
++#ifdef CONFIG_VALGRIND
++#include <valgrind/memcheck.h>
++#define WPA_MEM_DEFINED(ptr, len) VALGRIND_MAKE_MEM_DEFINED((ptr), (len))
++#else /* CONFIG_VALGRIND */
++#define WPA_MEM_DEFINED(ptr, len) do { } while (0)
++#endif /* CONFIG_VALGRIND */
++
+#endif /* COMMON_H */
--- /dev/null
- * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
+/*
+ * wpa_supplicant/hostapd / Debug prints
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
++ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ *
- void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len);
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
+ */
+
+#ifndef WPA_DEBUG_H
+#define WPA_DEBUG_H
+
+#include "wpabuf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
++extern int wpa_debug_level;
++extern int wpa_debug_show_keys;
++extern int wpa_debug_timestamp;
++
++
+/* Debugging function - conditional printf and hex dump. Driver wrappers can
+ * use these for debugging purposes. */
+
+enum {
+ MSG_EXCESSIVE, MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR
+};
+
+#ifdef CONFIG_NO_STDOUT_DEBUG
+
+#define wpa_debug_print_timestamp() do { } while (0)
+#define wpa_printf(args...) do { } while (0)
+#define wpa_hexdump(l,t,b,le) do { } while (0)
+#define wpa_hexdump_buf(l,t,b) do { } while (0)
+#define wpa_hexdump_key(l,t,b,le) do { } while (0)
+#define wpa_hexdump_buf_key(l,t,b) do { } while (0)
+#define wpa_hexdump_ascii(l,t,b,le) do { } while (0)
+#define wpa_hexdump_ascii_key(l,t,b,le) do { } while (0)
+#define wpa_debug_open_file(p) do { } while (0)
+#define wpa_debug_close_file() do { } while (0)
++#define wpa_debug_setup_stdout() do { } while (0)
++#define wpa_dbg(args...) do { } while (0)
++
++static inline int wpa_debug_reopen_file(void)
++{
++ return 0;
++}
+
+#else /* CONFIG_NO_STDOUT_DEBUG */
+
+int wpa_debug_open_file(const char *path);
++int wpa_debug_reopen_file(void);
+void wpa_debug_close_file(void);
++void wpa_debug_setup_stdout(void);
+
+/**
+ * wpa_debug_printf_timestamp - Print timestamp for debug output
+ *
+ * This function prints a timestamp in seconds_from_1970.microsoconds
+ * format if debug output has been configured to include timestamps in debug
+ * messages.
+ */
+void wpa_debug_print_timestamp(void);
+
+/**
+ * wpa_printf - conditional printf
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration.
+ *
+ * Note: New line '\n' is added to the end of the text when printing to stdout.
+ */
+void wpa_printf(int level, const char *fmt, ...)
+PRINTF_FORMAT(2, 3);
+
+/**
+ * wpa_hexdump - conditional hex dump
+ * @level: priority level (MSG_*) of the message
+ * @title: title of for the message
+ * @buf: data buffer to be dumped
+ * @len: length of the buf
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. The contents of buf is printed out has hex dump.
+ */
- wpa_hexdump(level, title, (const u8 *)wpabuf_head(buf), wpabuf_len(buf));
++void wpa_hexdump(int level, const char *title, const void *buf, size_t len);
+
+static inline void wpa_hexdump_buf(int level, const char *title,
+ const struct wpabuf *buf)
+{
- void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len);
++//!!LOCAL wpa_hexdump(level, title, (const u8 *)wpabuf_head(buf), wpabuf_len(buf));
++
++ wpa_hexdump(level, title, buf ? wpabuf_head(buf) : NULL,
++ buf ? wpabuf_len(buf) : 0);
+}
+
+/**
+ * wpa_hexdump_key - conditional hex dump, hide keys
+ * @level: priority level (MSG_*) of the message
+ * @title: title of for the message
+ * @buf: data buffer to be dumped
+ * @len: length of the buf
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. The contents of buf is printed out has hex dump. This works
+ * like wpa_hexdump(), but by default, does not include secret keys (passwords,
+ * etc.) in debug output.
+ */
- wpa_hexdump_key(level, title, (const u8 *)wpabuf_head(buf), wpabuf_len(buf));
++void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len);
+
+static inline void wpa_hexdump_buf_key(int level, const char *title,
+ const struct wpabuf *buf)
+{
- void wpa_hexdump_ascii(int level, const char *title, const u8 *buf,
++//!! LOCAL wpa_hexdump_key(level, title, (const u8 *)wpabuf_head(buf), wpabuf_len(buf));
++
++ wpa_hexdump_key(level, title, buf ? wpabuf_head(buf) : NULL,
++ buf ? wpabuf_len(buf) : 0);
+}
+
+/**
+ * wpa_hexdump_ascii - conditional hex dump
+ * @level: priority level (MSG_*) of the message
+ * @title: title of for the message
+ * @buf: data buffer to be dumped
+ * @len: length of the buf
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. The contents of buf is printed out has hex dump with both
+ * the hex numbers and ASCII characters (for printable range) are shown. 16
+ * bytes per line will be shown.
+ */
- void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf,
++void wpa_hexdump_ascii(int level, const char *title, const void *buf,
+ size_t len);
+
+/**
+ * wpa_hexdump_ascii_key - conditional hex dump, hide keys
+ * @level: priority level (MSG_*) of the message
+ * @title: title of for the message
+ * @buf: data buffer to be dumped
+ * @len: length of the buf
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. The contents of buf is printed out has hex dump with both
+ * the hex numbers and ASCII characters (for printable range) are shown. 16
+ * bytes per line will be shown. This works like wpa_hexdump_ascii(), but by
+ * default, does not include secret keys (passwords, etc.) in debug output.
+ */
- typedef void (*wpa_msg_cb_func)(void *ctx, int level, const char *txt,
- size_t len);
++void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
+ size_t len);
+
++/*
++ * wpa_dbg() behaves like wpa_msg(), but it can be removed from build to reduce
++ * binary size. As such, it should be used with debugging messages that are not
++ * needed in the control interface while wpa_msg() has to be used for anything
++ * that needs to shown to control interface monitors.
++ */
++#define wpa_dbg(args...) wpa_msg(args)
++
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+#ifdef CONFIG_NO_WPA_MSG
+#define wpa_msg(args...) do { } while (0)
+#define wpa_msg_ctrl(args...) do { } while (0)
++#define wpa_msg_global(args...) do { } while (0)
++#define wpa_msg_global_ctrl(args...) do { } while (0)
++#define wpa_msg_no_global(args...) do { } while (0)
++#define wpa_msg_global_only(args...) do { } while (0)
+#define wpa_msg_register_cb(f) do { } while (0)
++#define wpa_msg_register_ifname_cb(f) do { } while (0)
+#else /* CONFIG_NO_WPA_MSG */
+/**
+ * wpa_msg - Conditional printf for default target and ctrl_iface monitors
+ * @ctx: Pointer to context data; this is the ctx variable registered
+ * with struct wpa_driver_ops::init()
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. This function is like wpa_printf(), but it also sends the
+ * same message to all attached ctrl_iface monitors.
+ *
+ * Note: New line '\n' is added to the end of the text when printing to stdout.
+ */
+void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
+
+/**
+ * wpa_msg_ctrl - Conditional printf for ctrl_iface monitors
+ * @ctx: Pointer to context data; this is the ctx variable registered
+ * with struct wpa_driver_ops::init()
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages.
+ * This function is like wpa_msg(), but it sends the output only to the
+ * attached ctrl_iface monitors. In other words, it can be used for frequent
+ * events that do not need to be sent to syslog.
+ */
+void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
+PRINTF_FORMAT(3, 4);
+
- #endif /* CONFIG_NO_WPA_MSG */
++/**
++ * wpa_msg_global - Global printf for ctrl_iface monitors
++ * @ctx: Pointer to context data; this is the ctx variable registered
++ * with struct wpa_driver_ops::init()
++ * @level: priority level (MSG_*) of the message
++ * @fmt: printf format string, followed by optional arguments
++ *
++ * This function is used to print conditional debugging and error messages.
++ * This function is like wpa_msg(), but it sends the output as a global event,
++ * i.e., without being specific to an interface. For backwards compatibility,
++ * an old style event is also delivered on one of the interfaces (the one
++ * specified by the context data).
++ */
++void wpa_msg_global(void *ctx, int level, const char *fmt, ...)
++PRINTF_FORMAT(3, 4);
++
++/**
++ * wpa_msg_global_ctrl - Conditional global printf for ctrl_iface monitors
++ * @ctx: Pointer to context data; this is the ctx variable registered
++ * with struct wpa_driver_ops::init()
++ * @level: priority level (MSG_*) of the message
++ * @fmt: printf format string, followed by optional arguments
++ *
++ * This function is used to print conditional debugging and error messages.
++ * This function is like wpa_msg_global(), but it sends the output only to the
++ * attached global ctrl_iface monitors. In other words, it can be used for
++ * frequent events that do not need to be sent to syslog.
++ */
++void wpa_msg_global_ctrl(void *ctx, int level, const char *fmt, ...)
++PRINTF_FORMAT(3, 4);
++
++/**
++ * wpa_msg_no_global - Conditional printf for ctrl_iface monitors
++ * @ctx: Pointer to context data; this is the ctx variable registered
++ * with struct wpa_driver_ops::init()
++ * @level: priority level (MSG_*) of the message
++ * @fmt: printf format string, followed by optional arguments
++ *
++ * This function is used to print conditional debugging and error messages.
++ * This function is like wpa_msg(), but it does not send the output as a global
++ * event.
++ */
++void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...)
++PRINTF_FORMAT(3, 4);
++
++/**
++ * wpa_msg_global_only - Conditional printf for ctrl_iface monitors
++ * @ctx: Pointer to context data; this is the ctx variable registered
++ * with struct wpa_driver_ops::init()
++ * @level: priority level (MSG_*) of the message
++ * @fmt: printf format string, followed by optional arguments
++ *
++ * This function is used to print conditional debugging and error messages.
++ * This function is like wpa_msg_global(), but it sends the output only as a
++ * global event.
++ */
++void wpa_msg_global_only(void *ctx, int level, const char *fmt, ...)
++PRINTF_FORMAT(3, 4);
++
++enum wpa_msg_type {
++ WPA_MSG_PER_INTERFACE,
++ WPA_MSG_GLOBAL,
++ WPA_MSG_NO_GLOBAL,
++ WPA_MSG_ONLY_GLOBAL,
++};
++
++typedef void (*wpa_msg_cb_func)(void *ctx, int level, enum wpa_msg_type type,
++ const char *txt, size_t len);
+
+/**
+ * wpa_msg_register_cb - Register callback function for wpa_msg() messages
+ * @func: Callback function (%NULL to unregister)
+ */
+void wpa_msg_register_cb(wpa_msg_cb_func func);
+
++typedef const char * (*wpa_msg_get_ifname_func)(void *ctx);
++void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func);
++
++#endif /* CONFIG_NO_WPA_MSG */
+
+#ifdef CONFIG_NO_HOSTAPD_LOGGER
+#define hostapd_logger(args...) do { } while (0)
+#define hostapd_logger_register_cb(f) do { } while (0)
+#else /* CONFIG_NO_HOSTAPD_LOGGER */
+void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level,
+ const char *fmt, ...) PRINTF_FORMAT(5, 6);
+
+typedef void (*hostapd_logger_cb_func)(void *ctx, const u8 *addr,
+ unsigned int module, int level,
+ const char *txt, size_t len);
+
+/**
+ * hostapd_logger_register_cb - Register callback function for hostapd_logger()
+ * @func: Callback function (%NULL to unregister)
+ */
+void hostapd_logger_register_cb(hostapd_logger_cb_func func);
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+
+#define HOSTAPD_MODULE_IEEE80211 0x00000001
+#define HOSTAPD_MODULE_IEEE8021X 0x00000002
+#define HOSTAPD_MODULE_RADIUS 0x00000004
+#define HOSTAPD_MODULE_WPA 0x00000008
+#define HOSTAPD_MODULE_DRIVER 0x00000010
+#define HOSTAPD_MODULE_IAPP 0x00000020
+#define HOSTAPD_MODULE_MLME 0x00000040
+
+enum hostapd_logger_level {
+ HOSTAPD_LEVEL_DEBUG_VERBOSE = 0,
+ HOSTAPD_LEVEL_DEBUG = 1,
+ HOSTAPD_LEVEL_INFO = 2,
+ HOSTAPD_LEVEL_NOTICE = 3,
+ HOSTAPD_LEVEL_WARNING = 4
+};
+
+
+#ifdef CONFIG_DEBUG_SYSLOG
+
+void wpa_debug_open_syslog(void);
+void wpa_debug_close_syslog(void);
+
+#else /* CONFIG_DEBUG_SYSLOG */
+
+static inline void wpa_debug_open_syslog(void)
+{
+}
+
+static inline void wpa_debug_close_syslog(void)
+{
+}
+
+#endif /* CONFIG_DEBUG_SYSLOG */
+
++#ifdef CONFIG_DEBUG_LINUX_TRACING
++
++int wpa_debug_open_linux_tracing(void);
++void wpa_debug_close_linux_tracing(void);
++
++#else /* CONFIG_DEBUG_LINUX_TRACING */
++
++static inline int wpa_debug_open_linux_tracing(void)
++{
++ return 0;
++}
++
++static inline void wpa_debug_close_linux_tracing(void)
++{
++}
++
++#endif /* CONFIG_DEBUG_LINUX_TRACING */
++
+
+#ifdef EAPOL_TEST
+#define WPA_ASSERT(a) \
+ do { \
+ if (!(a)) { \
+ printf("WPA_ASSERT FAILED '" #a "' " \
+ "%s %s:%d\n", \
+ __FUNCTION__, __FILE__, __LINE__); \
+ exit(1); \
+ } \
+ } while (0)
+#else
+#define WPA_ASSERT(a) do { } while (0)
+#endif
+
++const char * debug_level_str(int level);
++int str_to_debug_level(const char *s);
++
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WPA_DEBUG_H */
--- /dev/null
- * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+/*
+ * Dynamic data buffer
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
++ * Copyright (c) 2007-2012, Jouni Malinen <j@w1.fi>
+ *
- u8 *ext_data; /* pointer to external data; NULL if data follows
- * struct wpabuf */
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
+ */
+
+#ifndef WPABUF_H
+#define WPABUF_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
++/* wpabuf::buf is a pointer to external data */
++#define WPABUF_FLAG_EXT_DATA BIT(0)
++
+/*
+ * Internal data structure for wpabuf. Please do not touch this directly from
+ * elsewhere. This is only defined in header file to allow inline functions
+ * from this file to access data.
+ */
+struct wpabuf {
+ size_t size; /* total size of the allocated buffer */
+ size_t used; /* length of data in the buffer */
- if (buf->ext_data)
- return buf->ext_data;
- return buf + 1;
++ u8 *buf; /* pointer to the head of the buffer */
++ unsigned int flags;
+ /* optionally followed by the allocated buffer */
+};
+
+
+int wpabuf_resize(struct wpabuf **buf, size_t add_len);
+struct wpabuf * wpabuf_alloc(size_t len);
+struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len);
+struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len);
+struct wpabuf * wpabuf_dup(const struct wpabuf *src);
+void wpabuf_free(struct wpabuf *buf);
++void wpabuf_clear_free(struct wpabuf *buf);
+void * wpabuf_put(struct wpabuf *buf, size_t len);
+struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b);
+struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len);
+void wpabuf_printf(struct wpabuf *buf, char *fmt, ...) PRINTF_FORMAT(2, 3);
+
+
+/**
+ * wpabuf_size - Get the currently allocated size of a wpabuf buffer
+ * @buf: wpabuf buffer
+ * Returns: Currently allocated size of the buffer
+ */
+static inline size_t wpabuf_size(const struct wpabuf *buf)
+{
+ return buf->size;
+}
+
+/**
+ * wpabuf_len - Get the current length of a wpabuf buffer data
+ * @buf: wpabuf buffer
+ * Returns: Currently used length of the buffer
+ */
+static inline size_t wpabuf_len(const struct wpabuf *buf)
+{
+ return buf->used;
+}
+
+/**
+ * wpabuf_tailroom - Get size of available tail room in the end of the buffer
+ * @buf: wpabuf buffer
+ * Returns: Tail room (in bytes) of available space in the end of the buffer
+ */
+static inline size_t wpabuf_tailroom(const struct wpabuf *buf)
+{
+ return buf->size - buf->used;
+}
+
+/**
+ * wpabuf_head - Get pointer to the head of the buffer data
+ * @buf: wpabuf buffer
+ * Returns: Pointer to the head of the buffer data
+ */
+static inline const void * wpabuf_head(const struct wpabuf *buf)
+{
- if (buf->ext_data)
- return buf->ext_data;
- return buf + 1;
++ return buf->buf;
+}
+
+static inline const u8 * wpabuf_head_u8(const struct wpabuf *buf)
+{
+ return (const u8 *)wpabuf_head(buf);
+}
+
+/**
+ * wpabuf_mhead - Get modifiable pointer to the head of the buffer data
+ * @buf: wpabuf buffer
+ * Returns: Pointer to the head of the buffer data
+ */
+static inline void * wpabuf_mhead(struct wpabuf *buf)
+{
- buf->ext_data = (u8 *) data;
++ return buf->buf;
+}
+
+static inline u8 * wpabuf_mhead_u8(struct wpabuf *buf)
+{
+ return (u8 *)wpabuf_mhead(buf);
+}
+
+static inline void wpabuf_put_u8(struct wpabuf *buf, u8 data)
+{
+ u8 *pos = (u8 *)wpabuf_put(buf, 1);
+ *pos = data;
+}
+
+static inline void wpabuf_put_le16(struct wpabuf *buf, u16 data)
+{
+ u8 *pos = (u8 *)wpabuf_put(buf, 2);
+ WPA_PUT_LE16(pos, data);
+}
+
+static inline void wpabuf_put_le32(struct wpabuf *buf, u32 data)
+{
+ u8 *pos = (u8 *)wpabuf_put(buf, 4);
+ WPA_PUT_LE32(pos, data);
+}
+
+static inline void wpabuf_put_be16(struct wpabuf *buf, u16 data)
+{
+ u8 *pos = (u8 *)wpabuf_put(buf, 2);
+ WPA_PUT_BE16(pos, data);
+}
+
+static inline void wpabuf_put_be24(struct wpabuf *buf, u32 data)
+{
+ u8 *pos = (u8 *)wpabuf_put(buf, 3);
+ WPA_PUT_BE24(pos, data);
+}
+
+static inline void wpabuf_put_be32(struct wpabuf *buf, u32 data)
+{
+ u8 *pos = (u8 *)wpabuf_put(buf, 4);
+ WPA_PUT_BE32(pos, data);
+}
+
+static inline void wpabuf_put_data(struct wpabuf *buf, const void *data,
+ size_t len)
+{
+ if (data)
+ os_memcpy(wpabuf_put(buf, len), data, len);
+}
+
+static inline void wpabuf_put_buf(struct wpabuf *dst,
+ const struct wpabuf *src)
+{
+ wpabuf_put_data(dst, wpabuf_head(src), wpabuf_len(src));
+}
+
+static inline void wpabuf_set(struct wpabuf *buf, const void *data, size_t len)
+{
++ buf->buf = (u8 *) data;
++ buf->flags = WPABUF_FLAG_EXT_DATA;
+ buf->size = buf->used = len;
+}
+
+static inline void wpabuf_put_str(struct wpabuf *dst, const char *str)
+{
+ wpabuf_put_data(dst, str, os_strlen(str));
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WPABUF_H */
--- /dev/null
+/*
+ * Copyright (c) 2011, JANET(UK)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of JANET(UK) nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _GSSAPIP_EAP_H_
+#define _GSSAPIP_EAP_H_ 1
+
+#include "config.h"
+
+#ifdef HAVE_HEIMDAL_VERSION
+#define KRB5_DEPRECATED /* so we can use krb5_free_unparsed_name() */
+#endif
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+#include <time.h>
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef WIN32
+#ifndef MAXHOSTNAMELEN
+# include <WinSock2.h>
+# define MAXHOSTNAMELEN NI_MAXHOST
+#endif
+#endif
+
+/* GSS headers */
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_krb5.h>
+#ifdef HAVE_HEIMDAL_VERSION
+typedef struct gss_any *gss_any_t;
+#else
+#include <gssapi/gssapi_ext.h>
+#endif
+#include "gssapi_eap.h"
+
+#ifndef HAVE_GSS_INQUIRE_ATTRS_FOR_MECH
+typedef const gss_OID_desc *gss_const_OID;
+#endif
+
+/* Kerberos headers */
+#include <krb5.h>
+
+/* EAP headers */
+#include <includes.h>
+#include <common.h>
+#include <eap_peer/eap.h>
+#include <eap_peer/eap_config.h>
+#include <eap_peer/eap_methods.h>
+#include <eap_common/eap_common.h>
+#include <wpabuf.h>
+
+#ifdef GSSEAP_ENABLE_ACCEPTOR
+/* libradsec headers */
+#include <radsec/radsec.h>
+#include <radsec/request.h>
+#include <radsec/radius.h>
+#endif
+
+#include "gsseap_err.h"
+#include "radsec_err.h"
+#include "util.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* These name flags are informative and not actually used by anything yet */
+#define NAME_FLAG_NAI 0x00000001
+#define NAME_FLAG_SERVICE 0x00000002
+#define NAME_FLAG_COMPOSITE 0x00000004
+
+struct gss_eap_saml_attr_ctx;
+struct gss_eap_attr_ctx;
+
+#ifdef HAVE_HEIMDAL_VERSION
+struct gss_name_t_desc_struct
+#else
+struct gss_name_struct
+#endif
+{
+ GSSEAP_MUTEX mutex; /* mutex protects attrCtx */
+ OM_uint32 flags;
+ gss_OID mechanismUsed; /* this is immutable */
+ krb5_principal krbPrincipal; /* this is immutable */
+#ifdef GSSEAP_ENABLE_ACCEPTOR
+ struct gss_eap_attr_ctx *attrCtx;
+#endif
+};
+
+#define CRED_FLAG_INITIATE 0x00010000
+#define CRED_FLAG_ACCEPT 0x00020000
+#define CRED_FLAG_PASSWORD 0x00040000
+#define CRED_FLAG_DEFAULT_CCACHE 0x00080000
+#define CRED_FLAG_RESOLVED 0x00100000
+#define CRED_FLAG_TARGET 0x00200000
+#define CRED_FLAG_CERTIFICATE 0x00400000
+#define CRED_FLAG_CONFIG_BLOB 0x00800000
+#define CRED_FLAG_PUBLIC_MASK 0x0000FFFF
+
+#ifdef HAVE_HEIMDAL_VERSION
+struct gss_cred_id_t_desc_struct
+#else
+struct gss_cred_id_struct
+#endif
+{
+ GSSEAP_MUTEX mutex;
+ OM_uint32 flags;
+ gss_name_t name;
+ gss_name_t target; /* for initiator */
+ gss_buffer_desc password;
+ gss_OID_set mechanisms;
+ time_t expiryTime;
+ gss_buffer_desc radiusConfigFile;
+ gss_buffer_desc radiusConfigStanza;
+ gss_buffer_desc caCertificate;
+ gss_buffer_desc subjectNameConstraint;
+ gss_buffer_desc subjectAltNameConstraint;
+ gss_buffer_desc clientCertificate;
+ gss_buffer_desc privateKey;
+ gss_buffer_desc caCertificateBlob;
+#ifdef GSSEAP_ENABLE_REAUTH
+ krb5_ccache krbCredCache;
+ gss_cred_id_t reauthCred;
+#endif
+};
+
+#define CTX_FLAG_INITIATOR 0x00000001
+#define CTX_FLAG_KRB_REAUTH 0x00000002
+#define CTX_FLAG_CHANNEL_BINDINGS_VERIFIED 0x00000004
+
+#define CTX_IS_INITIATOR(ctx) (((ctx)->flags & CTX_FLAG_INITIATOR) != 0)
+
+#define CTX_IS_ESTABLISHED(ctx) ((ctx)->state == GSSEAP_STATE_ESTABLISHED)
+
+/* Initiator context flags */
+#define CTX_FLAG_EAP_SUCCESS 0x00010000
+#define CTX_FLAG_EAP_RESTART 0x00020000
+#define CTX_FLAG_EAP_FAIL 0x00040000
+#define CTX_FLAG_EAP_RESP 0x00080000
+#define CTX_FLAG_EAP_NO_RESP 0x00100000
+#define CTX_FLAG_EAP_REQ 0x00200000
+#define CTX_FLAG_EAP_PORT_ENABLED 0x00400000
+#define CTX_FLAG_EAP_ALT_ACCEPT 0x00800000
+#define CTX_FLAG_EAP_ALT_REJECT 0x01000000
+#define CTX_FLAG_EAP_CHBIND_ACCEPT 0x02000000
++#define CTX_FLAG_EAP_TRIGGER_START 0x04000000
+#define CTX_FLAG_EAP_MASK 0xFFFF0000
+
+#define CONFIG_BLOB_CLIENT_CERT 0
+#define CONFIG_BLOB_PRIVATE_KEY 1
+#define CONFIG_BLOB_CA_CERT 2
+#define CONFIG_BLOB_MAX 3
+
+struct gss_eap_initiator_ctx {
+ unsigned int idleWhile;
+ struct eap_peer_config eapPeerConfig;
+ struct eap_sm *eap;
+ struct wpabuf reqData;
+ struct wpabuf *chbindData;
+ unsigned int chbindReqFlags;
+ struct wpa_config_blob configBlobs[CONFIG_BLOB_MAX];
+};
+
+#ifdef GSSEAP_ENABLE_ACCEPTOR
+struct gss_eap_acceptor_ctx {
+ struct rs_context *radContext;
+ struct rs_connection *radConn;
+ char *radServer;
+ gss_buffer_desc state;
+ rs_avp *vps;
+};
+#endif
+
+#ifdef HAVE_HEIMDAL_VERSION
+struct gss_ctx_id_t_desc_struct
+#else
+struct gss_ctx_id_struct
+#endif
+{
+ GSSEAP_MUTEX mutex;
+ enum gss_eap_state state;
+ OM_uint32 flags;
+ OM_uint32 gssFlags;
+ gss_OID mechanismUsed;
+ krb5_cksumtype checksumType;
+ krb5_enctype encryptionType;
+ krb5_keyblock rfc3961Key;
+ gss_name_t initiatorName;
+ gss_name_t acceptorName;
+ time_t expiryTime;
+ uint64_t sendSeq, recvSeq;
+ void *seqState;
+ gss_cred_id_t cred;
+ union {
+ struct gss_eap_initiator_ctx initiator;
+ #define initiatorCtx ctxU.initiator
+#ifdef GSSEAP_ENABLE_ACCEPTOR
+ struct gss_eap_acceptor_ctx acceptor;
+ #define acceptorCtx ctxU.acceptor
+#endif
+#ifdef GSSEAP_ENABLE_REAUTH
+ gss_ctx_id_t reauth;
+ #define reauthCtx ctxU.reauth
+#endif
+ } ctxU;
+ const struct gss_eap_token_buffer_set *inputTokens;
+ const struct gss_eap_token_buffer_set *outputTokens;
+};
+
+#define TOK_FLAG_SENDER_IS_ACCEPTOR 0x01
+#define TOK_FLAG_WRAP_CONFIDENTIAL 0x02
+#define TOK_FLAG_ACCEPTOR_SUBKEY 0x04
+
+#define KEY_USAGE_ACCEPTOR_SEAL 22
+#define KEY_USAGE_ACCEPTOR_SIGN 23
+#define KEY_USAGE_INITIATOR_SEAL 24
+#define KEY_USAGE_INITIATOR_SIGN 25
+
+#define KEY_USAGE_GSSEAP_CHBIND_MIC 60
+#define KEY_USAGE_GSSEAP_ACCTOKEN_MIC 61
+#define KEY_USAGE_GSSEAP_INITOKEN_MIC 62
+
+/* accept_sec_context.c */
+OM_uint32
+gssEapAcceptSecContext(OM_uint32 *minor,
+ gss_ctx_id_t ctx,
+ gss_cred_id_t cred,
+ gss_buffer_t input_token,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_name_t *src_name,
+ gss_OID *mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 *ret_flags,
+ OM_uint32 *time_rec,
+ gss_cred_id_t *delegated_cred_handle);
+
+/* init_sec_context.c */
+OM_uint32
+gssEapInitSecContext(OM_uint32 *minor,
+ gss_cred_id_t cred,
+ gss_ctx_id_t ctx,
+ gss_name_t target_name,
+ gss_OID mech_type,
+ OM_uint32 req_flags,
+ OM_uint32 time_req,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_buffer_t input_token,
+ gss_OID *actual_mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 *ret_flags,
+ OM_uint32 *time_rec);
+
+/* wrap_iov.c */
+OM_uint32
+gssEapWrapOrGetMIC(OM_uint32 *minor,
+ gss_ctx_id_t ctx,
+ int conf_req_flag,
+ int *conf_state,
+ gss_iov_buffer_desc *iov,
+ int iov_count,
+ enum gss_eap_token_type toktype);
+
+OM_uint32
+gssEapUnwrapOrVerifyMIC(OM_uint32 *minor_status,
+ gss_ctx_id_t ctx,
+ int *conf_state,
+ gss_qop_t *qop_state,
+ gss_iov_buffer_desc *iov,
+ int iov_count,
+ enum gss_eap_token_type toktype);
+
+OM_uint32
+gssEapWrapIovLength(OM_uint32 *minor,
+ gss_ctx_id_t ctx,
+ int conf_req_flag,
+ gss_qop_t qop_req,
+ int *conf_state,
+ gss_iov_buffer_desc *iov,
+ int iov_count);
+OM_uint32
+gssEapWrap(OM_uint32 *minor,
+ gss_ctx_id_t ctx,
+ int conf_req_flag,
+ gss_qop_t qop_req,
+ gss_buffer_t input_message_buffer,
+ int *conf_state,
+ gss_buffer_t output_message_buffer);
+
+unsigned char
+rfc4121Flags(gss_ctx_id_t ctx, int receiving);
+
+/* display_status.c */
+void
+gssEapSaveStatusInfo(OM_uint32 minor, const char *format, ...);
+
+OM_uint32
+gssEapDisplayStatus(OM_uint32 *minor,
+ OM_uint32 status_value,
+ gss_buffer_t status_string);
+
+#define IS_WIRE_ERROR(err) ((err) >= GSSEAP_RESERVED && \
+ (err) <= GSSEAP_RADIUS_PROT_FAILURE)
+
+#ifdef GSSEAP_ENABLE_ACCEPTOR
+#define IS_RADIUS_ERROR(err) ((err) >= ERROR_TABLE_BASE_rse && \
+ (err) <= ERROR_TABLE_BASE_rse + RSE_MAX)
+#else
+#define IS_RADIUS_ERROR(err) (0)
+#endif
+
+/* exchange_meta_data.c */
+OM_uint32 GSSAPI_CALLCONV
+gssEapExchangeMetaData(OM_uint32 *minor,
+ gss_const_OID mech,
+ gss_cred_id_t cred,
+ gss_ctx_id_t *ctx,
+ const gss_name_t name,
+ OM_uint32 req_flags,
+ gss_const_buffer_t meta_data);
+
+/* export_sec_context.c */
+OM_uint32
+gssEapExportSecContext(OM_uint32 *minor,
+ gss_ctx_id_t ctx,
+ gss_buffer_t token);
+
+/* import_sec_context.c */
+OM_uint32
+gssEapImportContext(OM_uint32 *minor,
+ gss_buffer_t token,
+ gss_ctx_id_t ctx);
+
+/* inquire_sec_context_by_oid.c */
+#define NEGOEX_INITIATOR_SALT "gss-eap-negoex-initiator"
+#define NEGOEX_INITIATOR_SALT_LEN (sizeof(NEGOEX_INITIATOR_SALT) - 1)
+
+#define NEGOEX_ACCEPTOR_SALT "gss-eap-negoex-acceptor"
+#define NEGOEX_ACCEPTOR_SALT_LEN (sizeof(NEGOEX_ACCEPTOR_SALT) - 1)
+
+/* pseudo_random.c */
+OM_uint32
+gssEapPseudoRandom(OM_uint32 *minor,
+ gss_ctx_id_t ctx,
+ int prf_key,
+ const gss_buffer_t prf_in,
+ gss_buffer_t prf_out);
+
+/* query_mechanism_info.c */
+OM_uint32
+gssQueryMechanismInfo(OM_uint32 *minor,
+ gss_const_OID mech_oid,
+ unsigned char auth_scheme[16]);
+
+/* query_meta_data.c */
+OM_uint32
+gssEapQueryMetaData(OM_uint32 *minor,
+ gss_const_OID mech GSSEAP_UNUSED,
+ gss_cred_id_t cred,
+ gss_ctx_id_t *context_handle,
+ const gss_name_t name,
+ OM_uint32 req_flags GSSEAP_UNUSED,
+ gss_buffer_t meta_data);
+
+/* eap_mech.c */
+OM_uint32
+gssEapInitiatorInit(OM_uint32 *minor);
+
+void
+gssEapFinalize(void);
+
+ /* Debugging and tracing*/
+ #define gssEapTrace(_fmt, ...) wpa_printf(MSG_INFO, _fmt, __VA_ARGS__);
+
+static inline void
+gssEapTraceStatus(const char *function,
+ OM_uint32 major, OM_uint32 minor)
+{
+ gss_buffer_desc gss_code_buf, mech_buf;
+ OM_uint32 tmpmaj, tmpmin, ctx = 0;
+ gss_code_buf.value = NULL;
+ mech_buf.value = NULL;
+ tmpmaj = gss_display_status(&tmpmin, major,
+ GSS_C_GSS_CODE, GSS_C_NO_OID, &ctx,
+ &gss_code_buf);
+ if (!GSS_ERROR(tmpmaj)) {
+if (minor == 0)
+ tmpmaj = makeStringBuffer(&tmpmin, "no minor", &mech_buf);
+else tmpmaj = gssEapDisplayStatus(&tmpmin, minor, &mech_buf);
+}
+ if (!GSS_ERROR(tmpmaj))
+ wpa_printf(MSG_INFO, "%s: %.*s/%.*s",
+ function, (int) gss_code_buf.length, (char *) gss_code_buf.value,
+ (int) mech_buf.length, (char *) mech_buf.value);
+ else wpa_printf(MSG_INFO, "%s: %u/%u",
+ function, major, minor);
+ tmpmaj = gss_release_buffer(&tmpmin, &gss_code_buf);
+ tmpmaj = gss_release_buffer(&tmpmin, &mech_buf);
+ }
+
+
+ /*If built as a library on Linux, don't respect environment when set*uid*/
+#ifdef HAVE_SECURE_GETENV
+#define getenv secure_getenv
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GSSAPIP_EAP_H_ */
--- /dev/null
- eapPeerConfig->private_key_passwd = (unsigned char *)cred->password.value;
+/*
+ * Copyright (c) 2011, JANET(UK)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of JANET(UK) nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Establish a security context on the initiator (client). These functions
+ * wrap around libeap.
+ */
+
+#include "gssapiP_eap.h"
+#include "radius/radius.h"
+#include "util_radius.h"
+#include "utils/radius_utils.h"
+
+/* methods allowed for phase1 authentication*/
+static const struct eap_method_type allowed_eap_method_types[] = {
+ {EAP_VENDOR_IETF, EAP_TYPE_TTLS},
+ {EAP_VENDOR_IETF, EAP_TYPE_NONE}};
+
+static OM_uint32
+policyVariableToFlag(enum eapol_bool_var variable)
+{
+ OM_uint32 flag = 0;
+
+ switch (variable) {
+ case EAPOL_eapSuccess:
+ flag = CTX_FLAG_EAP_SUCCESS;
+ break;
+ case EAPOL_eapRestart:
+ flag = CTX_FLAG_EAP_RESTART;
+ break;
+ case EAPOL_eapFail:
+ flag = CTX_FLAG_EAP_FAIL;
+ break;
+ case EAPOL_eapResp:
+ flag = CTX_FLAG_EAP_RESP;
+ break;
+ case EAPOL_eapNoResp:
+ flag = CTX_FLAG_EAP_NO_RESP;
+ break;
+ case EAPOL_eapReq:
+ flag = CTX_FLAG_EAP_REQ;
+ break;
+ case EAPOL_portEnabled:
+ flag = CTX_FLAG_EAP_PORT_ENABLED;
+ break;
+ case EAPOL_altAccept:
+ flag = CTX_FLAG_EAP_ALT_ACCEPT;
+ break;
+ case EAPOL_altReject:
+ flag = CTX_FLAG_EAP_ALT_REJECT;
+ break;
++ case EAPOL_eapTriggerStart:
++ flag = CTX_FLAG_EAP_TRIGGER_START;
++ break;
+ }
+
+ return flag;
+}
+
+static struct eap_peer_config *
+peerGetConfig(void *ctx)
+{
+ gss_ctx_id_t gssCtx = (gss_ctx_id_t)ctx;
+
+ return &gssCtx->initiatorCtx.eapPeerConfig;
+}
+
+static Boolean
+peerGetBool(void *data, enum eapol_bool_var variable)
+{
+ gss_ctx_id_t ctx = data;
+ OM_uint32 flag;
+
+ if (ctx == GSS_C_NO_CONTEXT)
+ return FALSE;
+
+ flag = policyVariableToFlag(variable);
+
+ return ((ctx->flags & flag) != 0);
+}
+
+static void
+peerSetBool(void *data, enum eapol_bool_var variable,
+ Boolean value)
+{
+ gss_ctx_id_t ctx = data;
+ OM_uint32 flag;
+
+ if (ctx == GSS_C_NO_CONTEXT)
+ return;
+
+ flag = policyVariableToFlag(variable);
+
+ if (value)
+ ctx->flags |= flag;
+ else
+ ctx->flags &= ~(flag);
+}
+
+static unsigned int
+peerGetInt(void *data, enum eapol_int_var variable)
+{
+ gss_ctx_id_t ctx = data;
+
+ if (ctx == GSS_C_NO_CONTEXT)
+ return FALSE;
+
+ GSSEAP_ASSERT(CTX_IS_INITIATOR(ctx));
+
+ switch (variable) {
+ case EAPOL_idleWhile:
+ return ctx->initiatorCtx.idleWhile;
+ break;
+ }
+
+ return 0;
+}
+
+static void
+peerSetInt(void *data, enum eapol_int_var variable,
+ unsigned int value)
+{
+ gss_ctx_id_t ctx = data;
+
+ if (ctx == GSS_C_NO_CONTEXT)
+ return;
+
+ GSSEAP_ASSERT(CTX_IS_INITIATOR(ctx));
+
+ switch (variable) {
+ case EAPOL_idleWhile:
+ ctx->initiatorCtx.idleWhile = value;
+ break;
+ }
+}
+
+static struct wpabuf *
+peerGetEapReqData(void *ctx)
+{
+ gss_ctx_id_t gssCtx = (gss_ctx_id_t)ctx;
+
+ return &gssCtx->initiatorCtx.reqData;
+}
+
+static void
+peerSetConfigBlob(void *ctx GSSEAP_UNUSED,
+ struct wpa_config_blob *blob GSSEAP_UNUSED)
+{
+}
+
+static const struct wpa_config_blob *
+peerGetConfigBlob(void *ctx,
+ const char *name)
+{
+ gss_ctx_id_t gssCtx = (gss_ctx_id_t)ctx;
+ size_t index;
+
+ if (strcmp(name, "client-cert") == 0)
+ index = CONFIG_BLOB_CLIENT_CERT;
+ else if (strcmp(name, "private-key") == 0)
+ index = CONFIG_BLOB_PRIVATE_KEY;
+ else if (strcmp(name, "ca-cert") == 0)
+ index = CONFIG_BLOB_CA_CERT;
+ else
+ return NULL;
+
+ return &gssCtx->initiatorCtx.configBlobs[index];
+}
+
+static void
+peerNotifyPending(void *ctx GSSEAP_UNUSED)
+{
+}
+
+static struct eapol_callbacks gssEapPolicyCallbacks = {
+ peerGetConfig,
+ peerGetBool,
+ peerSetBool,
+ peerGetInt,
+ peerSetInt,
+ peerGetEapReqData,
+ peerSetConfigBlob,
+ peerGetConfigBlob,
+ peerNotifyPending,
+};
+
+
+#define CHBIND_SERVICE_NAME_FLAG 0x01
+#define CHBIND_HOST_NAME_FLAG 0x02
+#define CHBIND_SERVICE_SPECIFIC_FLAG 0x04
+#define CHBIND_REALM_NAME_FLAG 0x08
+
+static OM_uint32
+peerInitEapChannelBinding(OM_uint32 *minor, gss_ctx_id_t ctx)
+{
+ struct wpabuf *buf = NULL;
+ unsigned int chbindReqFlags = 0;
+ krb5_principal princ = NULL;
+ gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
+ OM_uint32 major = GSS_S_COMPLETE;
+ krb5_context krbContext = NULL;
+
+ /* XXX is this check redundant? */
+ if (ctx->acceptorName == GSS_C_NO_NAME) {
+ major = GSS_S_BAD_NAME;
+ *minor = GSSEAP_NO_ACCEPTOR_NAME;
+ goto cleanup;
+ }
+
+ princ = ctx->acceptorName->krbPrincipal;
+
+ krbPrincComponentToGssBuffer(princ, 0, &nameBuf);
+ if (nameBuf.length > 0) {
+ major = gssEapRadiusAddAttr(minor, &buf, PW_GSS_ACCEPTOR_SERVICE_NAME,
+ 0, &nameBuf);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ chbindReqFlags |= CHBIND_SERVICE_NAME_FLAG;
+ }
+
+ krbPrincComponentToGssBuffer(princ, 1, &nameBuf);
+ if (nameBuf.length > 0) {
+ major = gssEapRadiusAddAttr(minor, &buf, PW_GSS_ACCEPTOR_HOST_NAME,
+ 0, &nameBuf);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ chbindReqFlags |= CHBIND_HOST_NAME_FLAG;
+ }
+
+ GSSEAP_KRB_INIT(&krbContext);
+
+ *minor = krbPrincUnparseServiceSpecifics(krbContext, princ, &nameBuf);
+ if (*minor != 0)
+ goto cleanup;
+
+ if (nameBuf.length > 0) {
+ major = gssEapRadiusAddAttr(minor, &buf,
+ PW_GSS_ACCEPTOR_SERVICE_SPECIFICS,
+ 0, &nameBuf);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ chbindReqFlags |= CHBIND_SERVICE_SPECIFIC_FLAG;
+ }
+
+ krbFreeUnparsedName(krbContext, &nameBuf);
+ krbPrincRealmToGssBuffer(princ, &nameBuf);
+
+ if (nameBuf.length > 0) {
+ major = gssEapRadiusAddAttr(minor, &buf,
+ PW_GSS_ACCEPTOR_REALM_NAME,
+ 0, &nameBuf);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ chbindReqFlags |= CHBIND_REALM_NAME_FLAG;
+ }
+
+ if (chbindReqFlags == 0) {
+ major = GSS_S_BAD_NAME;
+ *minor = GSSEAP_BAD_ACCEPTOR_NAME;
+ goto cleanup;
+ }
+
+ ctx->initiatorCtx.chbindData = buf;
+ ctx->initiatorCtx.chbindReqFlags = chbindReqFlags;
+
+ buf = NULL;
+
+ major = GSS_S_COMPLETE;
+ *minor = 0;
+
+cleanup:
+ /*namebuf is freed when used and may be left with a unowned pointer*/
+ wpabuf_free(buf);
+
+ return major;
+}
+
+static void
+peerProcessChbindResponse(void *context, int code, int nsid,
+ u8 *data, size_t len)
+{
+ radius_parser msg;
+ gss_ctx_id_t ctx = (gss_ctx_id_t )context;
+ void *vsadata;
+ u8 type;
+ u32 vendor_id;
+ u32 chbindRetFlags = 0;
+ size_t vsadata_len;
+
+ if (nsid != CHBIND_NSID_RADIUS)
+ return;
+
+ if (data == NULL)
+ return;
+ msg = radius_parser_start(data, len);
+ if (msg == NULL)
+ return;
+
+ while (radius_parser_parse_tlv(msg, &type, &vendor_id, &vsadata,
+ &vsadata_len) == 0) {
+ switch (type) {
+ case PW_GSS_ACCEPTOR_SERVICE_NAME:
+ chbindRetFlags |= CHBIND_SERVICE_NAME_FLAG;
+ break;
+ case PW_GSS_ACCEPTOR_HOST_NAME:
+ chbindRetFlags |= CHBIND_HOST_NAME_FLAG;
+ break;
+ case PW_GSS_ACCEPTOR_SERVICE_SPECIFICS:
+ chbindRetFlags |= CHBIND_SERVICE_SPECIFIC_FLAG;
+ break;
+ case PW_GSS_ACCEPTOR_REALM_NAME:
+ chbindRetFlags |= CHBIND_REALM_NAME_FLAG;
+ break;
+ }
+ }
+
+ radius_parser_finish(msg);
+
+ if (code == CHBIND_CODE_SUCCESS &&
+ ((chbindRetFlags & ctx->initiatorCtx.chbindReqFlags) == ctx->initiatorCtx.chbindReqFlags)) {
+ ctx->flags |= CTX_FLAG_EAP_CHBIND_ACCEPT;
+ ctx->gssFlags |= GSS_C_MUTUAL_FLAG;
+ } /* else log failures? */
+}
+
+static OM_uint32
+peerConfigInit(OM_uint32 *minor, gss_ctx_id_t ctx)
+{
+ OM_uint32 major;
+ krb5_context krbContext;
+ struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig;
+ struct wpa_config_blob *configBlobs = ctx->initiatorCtx.configBlobs;
+ gss_buffer_desc identity = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc realm = GSS_C_EMPTY_BUFFER;
+ gss_cred_id_t cred = ctx->cred;
+
+ eapPeerConfig->identity = NULL;
+ eapPeerConfig->identity_len = 0;
+ eapPeerConfig->anonymous_identity = NULL;
+ eapPeerConfig->anonymous_identity_len = 0;
+ eapPeerConfig->password = NULL;
+ eapPeerConfig->password_len = 0;
+ eapPeerConfig->eap_methods = (struct eap_method_type *) allowed_eap_method_types;
+
+ GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
+
+ GSSEAP_KRB_INIT(&krbContext);
+
+ eapPeerConfig->fragment_size = 1024;
+
+ GSSEAP_ASSERT(cred->name != GSS_C_NO_NAME);
+
+ if ((cred->name->flags & (NAME_FLAG_NAI | NAME_FLAG_SERVICE)) == 0) {
+ *minor = GSSEAP_BAD_INITIATOR_NAME;
+ return GSS_S_BAD_NAME;
+ }
+
+ /* identity */
+ major = gssEapDisplayName(minor, cred->name, &identity, NULL);
+ if (GSS_ERROR(major))
+ return major;
+
+ eapPeerConfig->identity = (unsigned char *)identity.value;
+ eapPeerConfig->identity_len = identity.length;
+
+ krbPrincRealmToGssBuffer(cred->name->krbPrincipal, &realm);
+
+ /* anonymous_identity */
+ eapPeerConfig->anonymous_identity = GSSEAP_MALLOC(realm.length + 2);
+ if (eapPeerConfig->anonymous_identity == NULL) {
+ *minor = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ eapPeerConfig->anonymous_identity[0] = '@';
+ memcpy(eapPeerConfig->anonymous_identity + 1, realm.value, realm.length);
+ eapPeerConfig->anonymous_identity[1 + realm.length] = '\0';
+ eapPeerConfig->anonymous_identity_len = 1 + realm.length;
+
+ /* password */
+ if ((cred->flags & CRED_FLAG_CERTIFICATE) == 0) {
+ eapPeerConfig->password = (unsigned char *)cred->password.value;
+ eapPeerConfig->password_len = cred->password.length;
+ }
+
+ /* certs */
+ eapPeerConfig->ca_cert = (unsigned char *)cred->caCertificate.value;
+ eapPeerConfig->subject_match = (unsigned char *)cred->subjectNameConstraint.value;
+ eapPeerConfig->altsubject_match = (unsigned char *)cred->subjectAltNameConstraint.value;
+ configBlobs[CONFIG_BLOB_CA_CERT].data = cred->caCertificateBlob.value;
+ configBlobs[CONFIG_BLOB_CA_CERT].len = cred->caCertificateBlob.length;
+
+ /* eap channel binding */
+ if (ctx->initiatorCtx.chbindData != NULL) {
+ struct eap_peer_chbind_config *chbind_config =
+ (struct eap_peer_chbind_config *)GSSEAP_MALLOC(sizeof(struct eap_peer_chbind_config));
+ if (chbind_config == NULL) {
+ *minor = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ chbind_config->req_data = wpabuf_mhead_u8(ctx->initiatorCtx.chbindData);
+ chbind_config->req_data_len = wpabuf_len(ctx->initiatorCtx.chbindData);
+ chbind_config->nsid = CHBIND_NSID_RADIUS;
+ chbind_config->response_cb = &peerProcessChbindResponse;
+ chbind_config->ctx = ctx;
+ eapPeerConfig->chbind_config = chbind_config;
+ eapPeerConfig->chbind_config_len = 1;
+ } else {
+ eapPeerConfig->chbind_config = NULL;
+ eapPeerConfig->chbind_config_len = 0;
+ }
+ if (cred->flags & CRED_FLAG_CERTIFICATE) {
+ /*
+ * CRED_FLAG_CONFIG_BLOB is an internal flag which will be used in the
+ * future to directly pass certificate and private key data to the
+ * EAP implementation, rather than an indirected string pointer.
+ */
+ if (cred->flags & CRED_FLAG_CONFIG_BLOB) {
+ eapPeerConfig->client_cert = (unsigned char *)"blob://client-cert";
+ configBlobs[CONFIG_BLOB_CLIENT_CERT].data = cred->clientCertificate.value;
+ configBlobs[CONFIG_BLOB_CLIENT_CERT].len = cred->clientCertificate.length;
+
+ eapPeerConfig->client_cert = (unsigned char *)"blob://private-key";
+ configBlobs[CONFIG_BLOB_PRIVATE_KEY].data = cred->clientCertificate.value;
+ configBlobs[CONFIG_BLOB_PRIVATE_KEY].len = cred->privateKey.length;
+ } else {
+ eapPeerConfig->client_cert = (unsigned char *)cred->clientCertificate.value;
+ eapPeerConfig->private_key = (unsigned char *)cred->privateKey.value;
+ }
++ eapPeerConfig->private_key_passwd = (char *)cred->password.value;
+ }
+
+ *minor = 0;
+ return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+peerConfigFree(OM_uint32 *minor,
+ gss_ctx_id_t ctx)
+{
+ struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig;
+
+ if (eapPeerConfig->identity != NULL) {
+ GSSEAP_FREE(eapPeerConfig->identity);
+ eapPeerConfig->identity = NULL;
+ eapPeerConfig->identity_len = 0;
+ }
+
+ if (eapPeerConfig->anonymous_identity != NULL) {
+ GSSEAP_FREE(eapPeerConfig->anonymous_identity);
+ eapPeerConfig->anonymous_identity = NULL;
+ eapPeerConfig->anonymous_identity_len = 0;
+ }
+
+ *minor = 0;
+ return GSS_S_COMPLETE;
+}
+
+/*
+ * Mark an initiator context as ready for cryptographic operations
+ */
+static OM_uint32
+initReady(OM_uint32 *minor, gss_ctx_id_t ctx)
+{
+ OM_uint32 major;
+ const unsigned char *key;
+ size_t keyLength;
+
+ /* Cache encryption type derived from selected mechanism OID */
+ major = gssEapOidToEnctype(minor, ctx->mechanismUsed, &ctx->encryptionType);
+ if (GSS_ERROR(major))
+ return major;
+
+ if (!eap_key_available(ctx->initiatorCtx.eap)) {
+ *minor = GSSEAP_KEY_UNAVAILABLE;
+ return GSS_S_UNAVAILABLE;
+ }
+
+ key = eap_get_eapKeyData(ctx->initiatorCtx.eap, &keyLength);
+
+ if (keyLength < EAP_EMSK_LEN) {
+ *minor = GSSEAP_KEY_TOO_SHORT;
+ return GSS_S_UNAVAILABLE;
+ }
+
+ major = gssEapDeriveRfc3961Key(minor,
+ &key[EAP_EMSK_LEN / 2],
+ EAP_EMSK_LEN / 2,
+ ctx->encryptionType,
+ &ctx->rfc3961Key);
+ if (GSS_ERROR(major))
+ return major;
+
+ major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key,
+ &ctx->checksumType);
+ if (GSS_ERROR(major))
+ return major;
+
+ major = sequenceInit(minor,
+ &ctx->seqState,
+ ctx->recvSeq,
+ ((ctx->gssFlags & GSS_C_REPLAY_FLAG) != 0),
+ ((ctx->gssFlags & GSS_C_SEQUENCE_FLAG) != 0),
+ TRUE);
+ if (GSS_ERROR(major))
+ return major;
+
+ *minor = 0;
+ return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+initBegin(OM_uint32 *minor,
+ gss_ctx_id_t ctx,
+ gss_name_t target,
+ gss_OID mech,
+ OM_uint32 reqFlags GSSEAP_UNUSED,
+ OM_uint32 timeReq,
+ gss_channel_bindings_t chanBindings GSSEAP_UNUSED)
+{
+ OM_uint32 major;
+ gss_cred_id_t cred = ctx->cred;
+
+ GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
+
+ if (cred->expiryTime)
+ ctx->expiryTime = cred->expiryTime;
+ else if (timeReq == 0 || timeReq == GSS_C_INDEFINITE)
+ ctx->expiryTime = 0;
+ else
+ ctx->expiryTime = time(NULL) + timeReq;
+
+ /*
+ * The credential mutex protects its name, however we need to
+ * explicitly lock the acceptor name (unlikely as it may be
+ * that it has attributes set on it).
+ */
+ major = gssEapDuplicateName(minor, cred->name, &ctx->initiatorName);
+ if (GSS_ERROR(major))
+ return major;
+
+ if (target != GSS_C_NO_NAME) {
+ GSSEAP_MUTEX_LOCK(&target->mutex);
+
+ major = gssEapDuplicateName(minor, target, &ctx->acceptorName);
+ if (GSS_ERROR(major)) {
+ GSSEAP_MUTEX_UNLOCK(&target->mutex);
+ return major;
+ }
+
+ GSSEAP_MUTEX_UNLOCK(&target->mutex);
+ }
+
+ major = gssEapCanonicalizeOid(minor,
+ mech,
+ OID_FLAG_NULL_VALID | OID_FLAG_MAP_NULL_TO_DEFAULT_MECH,
+ &ctx->mechanismUsed);
+ if (GSS_ERROR(major))
+ return major;
+
+ /* If credentials were provided, check they're usable with this mech */
+ if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
+ *minor = GSSEAP_CRED_MECH_MISMATCH;
+ return GSS_S_BAD_MECH;
+ }
+
+ *minor = 0;
+ return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+eapGssSmInitError(OM_uint32 *minor,
+ gss_cred_id_t cred GSSEAP_UNUSED,
+ gss_ctx_id_t ctx GSSEAP_UNUSED,
+ gss_name_t target GSSEAP_UNUSED,
+ gss_OID mech GSSEAP_UNUSED,
+ OM_uint32 reqFlags GSSEAP_UNUSED,
+ OM_uint32 timeReq GSSEAP_UNUSED,
+ gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
+ gss_buffer_t inputToken,
+ gss_buffer_t outputToken GSSEAP_UNUSED,
+ OM_uint32 *smFlags GSSEAP_UNUSED)
+{
+ OM_uint32 major;
+ unsigned char *p;
+
+ if (inputToken->length < 8) {
+ *minor = GSSEAP_TOK_TRUNC;
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ p = (unsigned char *)inputToken->value;
+
+ major = load_uint32_be(&p[0]);
+ *minor = load_uint32_be(&p[4]);
+ if ((*minor >0) && (*minor < 128))
+ * minor += ERROR_TABLE_BASE_eapg;
+ else *minor = 0;
+
+ if (!GSS_ERROR(major) || !IS_WIRE_ERROR(*minor)) {
+ major = GSS_S_FAILURE;
+ *minor = GSSEAP_BAD_ERROR_TOKEN;
+ }
+
+ GSSEAP_ASSERT(GSS_ERROR(major));
+
+ return major;
+}
+
+#ifdef GSSEAP_ENABLE_REAUTH
+static OM_uint32
+eapGssSmInitGssReauth(OM_uint32 *minor,
+ gss_cred_id_t cred,
+ gss_ctx_id_t ctx,
+ gss_name_t target,
+ gss_OID mech GSSEAP_UNUSED,
+ OM_uint32 reqFlags,
+ OM_uint32 timeReq,
+ gss_channel_bindings_t chanBindings,
+ gss_buffer_t inputToken,
+ gss_buffer_t outputToken,
+ OM_uint32 *smFlags GSSEAP_UNUSED)
+{
+ OM_uint32 major, tmpMinor;
+ gss_name_t mechTarget = GSS_C_NO_NAME;
+ gss_OID actualMech = GSS_C_NO_OID;
+ OM_uint32 gssFlags, timeRec;
+
+ /*
+ * Here we use the passed in credential handle because the resolved
+ * context credential does not currently have the reauth creds.
+ */
+ if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL) {
+ if (!gssEapCanReauthP(cred, target, timeReq))
+ return GSS_S_CONTINUE_NEEDED;
+
+ ctx->flags |= CTX_FLAG_KRB_REAUTH;
+ } else if ((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0) {
+ major = GSS_S_DEFECTIVE_TOKEN;
+ *minor = GSSEAP_WRONG_ITOK;
+ goto cleanup;
+ }
+
+ GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
+
+ major = gssEapMechToGlueName(minor, target, &mechTarget);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ major = gssInitSecContext(minor,
+ cred->reauthCred,
+ &ctx->reauthCtx,
+ mechTarget,
+ (gss_OID)gss_mech_krb5,
+ reqFlags | GSS_C_MUTUAL_FLAG,
+ timeReq,
+ chanBindings,
+ inputToken,
+ &actualMech,
+ outputToken,
+ &gssFlags,
+ &timeRec);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ ctx->gssFlags = gssFlags;
+
+ if (major == GSS_S_COMPLETE) {
+ GSSEAP_ASSERT(GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_REAUTHENTICATE);
+
+ major = gssEapReauthComplete(minor, ctx, cred, actualMech, timeRec);
+ if (GSS_ERROR(major))
+ goto cleanup;
+ GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
+ } else {
+ GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_REAUTHENTICATE);
+ }
+
+cleanup:
+ gssReleaseName(&tmpMinor, &mechTarget);
+
+ return major;
+}
+#endif /* GSSEAP_ENABLE_REAUTH */
+
+#ifdef GSSEAP_DEBUG
+static OM_uint32
+eapGssSmInitVendorInfo(OM_uint32 *minor,
+ gss_cred_id_t cred GSSEAP_UNUSED,
+ gss_ctx_id_t ctx GSSEAP_UNUSED,
+ gss_name_t target GSSEAP_UNUSED,
+ gss_OID mech GSSEAP_UNUSED,
+ OM_uint32 reqFlags GSSEAP_UNUSED,
+ OM_uint32 timeReq GSSEAP_UNUSED,
+ gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
+ gss_buffer_t inputToken GSSEAP_UNUSED,
+ gss_buffer_t outputToken,
+ OM_uint32 *smFlags GSSEAP_UNUSED)
+{
+ OM_uint32 major;
+
+ major = makeStringBuffer(minor, "JANET(UK)", outputToken);
+ if (GSS_ERROR(major))
+ return major;
+
+ return GSS_S_CONTINUE_NEEDED;
+}
+#endif
+
+static OM_uint32
+eapGssSmInitAcceptorName(OM_uint32 *minor,
+ gss_cred_id_t cred GSSEAP_UNUSED,
+ gss_ctx_id_t ctx,
+ gss_name_t target GSSEAP_UNUSED,
+ gss_OID mech GSSEAP_UNUSED,
+ OM_uint32 reqFlags GSSEAP_UNUSED,
+ OM_uint32 timeReq GSSEAP_UNUSED,
+ gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
+ gss_buffer_t inputToken GSSEAP_UNUSED,
+ gss_buffer_t outputToken,
+ OM_uint32 *smFlags GSSEAP_UNUSED)
+{
+ OM_uint32 major;
+
+ if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL &&
+ ctx->acceptorName != GSS_C_NO_NAME) {
+
+ /* Send desired target name to acceptor */
+ major = gssEapDisplayName(minor, ctx->acceptorName,
+ outputToken, NULL);
+ if (GSS_ERROR(major))
+ return major;
+ } else if (inputToken != GSS_C_NO_BUFFER) {
+ OM_uint32 tmpMinor;
+ gss_name_t nameHint;
+ int equal;
+
+ /* Accept target name hint from acceptor or verify acceptor */
+ major = gssEapImportName(minor, inputToken,
+ GSS_C_NT_USER_NAME,
+ ctx->mechanismUsed,
+ &nameHint);
+ if (GSS_ERROR(major))
+ return major;
+
+ if (ctx->acceptorName != GSS_C_NO_NAME) {
+ /* verify name hint matched asserted acceptor name */
+ major = gssEapCompareName(minor,
+ nameHint,
+ ctx->acceptorName,
+ COMPARE_NAME_FLAG_IGNORE_EMPTY_REALMS,
+ &equal);
+ if (GSS_ERROR(major)) {
+ gssEapReleaseName(&tmpMinor, &nameHint);
+ return major;
+ }
+
+ gssEapReleaseName(&tmpMinor, &nameHint);
+
+ if (!equal) {
+ *minor = GSSEAP_WRONG_ACCEPTOR_NAME;
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+ } else { /* acceptor name is no_name */
+ /* accept acceptor name hint */
+ ctx->acceptorName = nameHint;
+ nameHint = GSS_C_NO_NAME;
+ }
+ }
+
+
+ /*
+ * Currently, other parts of the code assume that the acceptor name
+ * is available, hence this check.
+ */
+ if (ctx->acceptorName == GSS_C_NO_NAME) {
+ *minor = GSSEAP_NO_ACCEPTOR_NAME;
+ return GSS_S_FAILURE;
+ }
+
+ /*
+ * Generate channel binding data
+ */
+ if (ctx->initiatorCtx.chbindData == NULL) {
+ major = peerInitEapChannelBinding(minor, ctx);
+ if (GSS_ERROR(major))
+ return major;
+ }
+
+ return GSS_S_CONTINUE_NEEDED;
+}
+
+static OM_uint32
+eapGssSmInitIdentity(OM_uint32 *minor,
+ gss_cred_id_t cred GSSEAP_UNUSED,
+ gss_ctx_id_t ctx,
+ gss_name_t target GSSEAP_UNUSED,
+ gss_OID mech GSSEAP_UNUSED,
+ OM_uint32 reqFlags GSSEAP_UNUSED,
+ OM_uint32 timeReq GSSEAP_UNUSED,
+ gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
+ gss_buffer_t inputToken GSSEAP_UNUSED,
+ gss_buffer_t outputToken GSSEAP_UNUSED,
+ OM_uint32 *smFlags)
+{
+ struct eap_config eapConfig;
+
+#ifdef GSSEAP_ENABLE_REAUTH
+ if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_REAUTHENTICATE) {
+ OM_uint32 tmpMinor;
+
+ /* server didn't support reauthentication, sent EAP request */
+ gssDeleteSecContext(&tmpMinor, &ctx->reauthCtx, GSS_C_NO_BUFFER);
+ ctx->flags &= ~(CTX_FLAG_KRB_REAUTH);
+ GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_INITIAL);
+ } else
+#endif
+ *smFlags |= SM_FLAG_FORCE_SEND_TOKEN;
+
+ GSSEAP_ASSERT((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0);
+ GSSEAP_ASSERT(inputToken == GSS_C_NO_BUFFER);
+
+ memset(&eapConfig, 0, sizeof(eapConfig));
+
+ ctx->initiatorCtx.eap = eap_peer_sm_init(ctx,
+ &gssEapPolicyCallbacks,
+ ctx,
+ &eapConfig);
+ if (ctx->initiatorCtx.eap == NULL) {
+ *minor = GSSEAP_PEER_SM_INIT_FAILURE;
+ return GSS_S_FAILURE;
+ }
+
+ ctx->flags |= CTX_FLAG_EAP_RESTART | CTX_FLAG_EAP_PORT_ENABLED;
+
+ /* poke EAP state machine */
+ if (eap_peer_sm_step(ctx->initiatorCtx.eap) != 0) {
+ *minor = GSSEAP_PEER_SM_STEP_FAILURE;
+ return GSS_S_FAILURE;
+ }
+
+ GSSEAP_SM_TRANSITION_NEXT(ctx);
+
+ *minor = 0;
+
+ return GSS_S_CONTINUE_NEEDED;
+}
+
+static OM_uint32
+eapGssSmInitAuthenticate(OM_uint32 *minor,
+ gss_cred_id_t cred GSSEAP_UNUSED,
+ gss_ctx_id_t ctx,
+ gss_name_t target GSSEAP_UNUSED,
+ gss_OID mech GSSEAP_UNUSED,
+ OM_uint32 reqFlags GSSEAP_UNUSED,
+ OM_uint32 timeReq GSSEAP_UNUSED,
+ gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
+ gss_buffer_t inputToken GSSEAP_UNUSED,
+ gss_buffer_t outputToken,
+ OM_uint32 *smFlags)
+{
+ OM_uint32 major;
+ OM_uint32 tmpMinor;
+ struct wpabuf *resp = NULL;
+
+ *minor = 0;
+
+ GSSEAP_ASSERT(inputToken != GSS_C_NO_BUFFER);
+
+ major = peerConfigInit(minor, ctx);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ GSSEAP_ASSERT(ctx->initiatorCtx.eap != NULL);
+ GSSEAP_ASSERT(ctx->flags & CTX_FLAG_EAP_PORT_ENABLED);
+
+ ctx->flags |= CTX_FLAG_EAP_REQ; /* we have a Request from the acceptor */
+
+ wpabuf_set(&ctx->initiatorCtx.reqData,
+ inputToken->value, inputToken->length);
+
+ major = GSS_S_CONTINUE_NEEDED;
+
+ eap_peer_sm_step(ctx->initiatorCtx.eap);
+ if (ctx->flags & CTX_FLAG_EAP_RESP) {
+ ctx->flags &= ~(CTX_FLAG_EAP_RESP);
+
+ resp = eap_get_eapRespData(ctx->initiatorCtx.eap);
+ } else if (ctx->flags & CTX_FLAG_EAP_SUCCESS) {
+ major = initReady(minor, ctx);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ ctx->flags &= ~(CTX_FLAG_EAP_SUCCESS);
+ major = GSS_S_CONTINUE_NEEDED;
+ GSSEAP_SM_TRANSITION_NEXT(ctx);
+ } else if (ctx->flags & CTX_FLAG_EAP_FAIL) {
+ major = GSS_S_DEFECTIVE_CREDENTIAL;
+ *minor = GSSEAP_PEER_AUTH_FAILURE;
+ } else {
+ major = GSS_S_DEFECTIVE_TOKEN;
+ *minor = GSSEAP_PEER_BAD_MESSAGE;
+ }
+
+cleanup:
+ if (resp != NULL) {
+ OM_uint32 tmpMajor;
+ gss_buffer_desc respBuf;
+
+ GSSEAP_ASSERT(major == GSS_S_CONTINUE_NEEDED);
+
+ respBuf.length = wpabuf_len(resp);
+ respBuf.value = (void *)wpabuf_head(resp);
+
+ tmpMajor = duplicateBuffer(&tmpMinor, &respBuf, outputToken);
+ if (GSS_ERROR(tmpMajor)) {
+ major = tmpMajor;
+ *minor = tmpMinor;
+ }
+
+ *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
+ }
+
+ wpabuf_set(&ctx->initiatorCtx.reqData, NULL, 0);
+ peerConfigFree(&tmpMinor, ctx);
+
+ return major;
+}
+
+static OM_uint32
+eapGssSmInitGssFlags(OM_uint32 *minor,
+ gss_cred_id_t cred GSSEAP_UNUSED,
+ gss_ctx_id_t ctx,
+ gss_name_t target GSSEAP_UNUSED,
+ gss_OID mech GSSEAP_UNUSED,
+ OM_uint32 reqFlags GSSEAP_UNUSED,
+ OM_uint32 timeReq GSSEAP_UNUSED,
+ gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
+ gss_buffer_t inputToken GSSEAP_UNUSED,
+ gss_buffer_t outputToken,
+ OM_uint32 *smFlags GSSEAP_UNUSED)
+{
+ unsigned char wireFlags[4];
+ gss_buffer_desc flagsBuf;
+
+ /*
+ * As a temporary measure, force mutual authentication until channel binding is
+ * more widely deployed.
+ */
+ ctx->gssFlags |= GSS_C_MUTUAL_FLAG;
+ store_uint32_be(ctx->gssFlags & GSSEAP_WIRE_FLAGS_MASK, wireFlags);
+
+ flagsBuf.length = sizeof(wireFlags);
+ flagsBuf.value = wireFlags;
+
+ return duplicateBuffer(minor, &flagsBuf, outputToken);
+}
+
+static OM_uint32
+eapGssSmInitGssChannelBindings(OM_uint32 *minor,
+ gss_cred_id_t cred GSSEAP_UNUSED,
+ gss_ctx_id_t ctx,
+ gss_name_t target GSSEAP_UNUSED,
+ gss_OID mech GSSEAP_UNUSED,
+ OM_uint32 reqFlags GSSEAP_UNUSED,
+ OM_uint32 timeReq GSSEAP_UNUSED,
+ gss_channel_bindings_t chanBindings,
+ gss_buffer_t inputToken GSSEAP_UNUSED,
+ gss_buffer_t outputToken,
+ OM_uint32 *smFlags)
+{
+ OM_uint32 major;
+ krb5_error_code code;
+ krb5_context krbContext;
+ krb5_data data;
+ krb5_checksum cksum;
+ gss_buffer_desc cksumBuffer;
+
+ if (chanBindings == GSS_C_NO_CHANNEL_BINDINGS ||
+ chanBindings->application_data.length == 0)
+ return GSS_S_CONTINUE_NEEDED;
+
+ GSSEAP_KRB_INIT(&krbContext);
+
+ KRB_DATA_INIT(&data);
+
+ gssBufferToKrbData(&chanBindings->application_data, &data);
+
+ code = krb5_c_make_checksum(krbContext, ctx->checksumType,
+ &ctx->rfc3961Key,
+ KEY_USAGE_GSSEAP_CHBIND_MIC,
+ &data, &cksum);
+ if (code != 0) {
+ *minor = code;
+ return GSS_S_FAILURE;
+ }
+
+ cksumBuffer.length = KRB_CHECKSUM_LENGTH(&cksum);
+ cksumBuffer.value = KRB_CHECKSUM_DATA(&cksum);
+
+ major = duplicateBuffer(minor, &cksumBuffer, outputToken);
+ if (GSS_ERROR(major)) {
+ krb5_free_checksum_contents(krbContext, &cksum);
+ return major;
+ }
+
+ *minor = 0;
+ *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
+
+ krb5_free_checksum_contents(krbContext, &cksum);
+
+ return GSS_S_CONTINUE_NEEDED;
+}
+
+static OM_uint32
+eapGssSmInitInitiatorMIC(OM_uint32 *minor,
+ gss_cred_id_t cred GSSEAP_UNUSED,
+ gss_ctx_id_t ctx,
+ gss_name_t target GSSEAP_UNUSED,
+ gss_OID mech GSSEAP_UNUSED,
+ OM_uint32 reqFlags GSSEAP_UNUSED,
+ OM_uint32 timeReq GSSEAP_UNUSED,
+ gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
+ gss_buffer_t inputToken GSSEAP_UNUSED,
+ gss_buffer_t outputToken,
+ OM_uint32 *smFlags)
+{
+ OM_uint32 major;
+
+ major = gssEapMakeTokenMIC(minor, ctx, outputToken);
+ if (GSS_ERROR(major))
+ return major;
+
+ GSSEAP_SM_TRANSITION_NEXT(ctx);
+
+ *minor = 0;
+ *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
+
+ return GSS_S_CONTINUE_NEEDED;
+}
+
+#ifdef GSSEAP_ENABLE_REAUTH
+static OM_uint32
+eapGssSmInitReauthCreds(OM_uint32 *minor,
+ gss_cred_id_t cred,
+ gss_ctx_id_t ctx,
+ gss_name_t target GSSEAP_UNUSED,
+ gss_OID mech GSSEAP_UNUSED,
+ OM_uint32 reqFlags GSSEAP_UNUSED,
+ OM_uint32 timeReq GSSEAP_UNUSED,
+ gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
+ gss_buffer_t inputToken,
+ gss_buffer_t outputToken GSSEAP_UNUSED,
+ OM_uint32 *smFlags GSSEAP_UNUSED)
+{
+ OM_uint32 major;
+
+ if (ctx->gssFlags & GSS_C_MUTUAL_FLAG) {
+ major = gssEapStoreReauthCreds(minor, ctx, cred, inputToken);
+ if (GSS_ERROR(major))
+ return major;
+ }
+
+ *minor = 0;
+ return GSS_S_CONTINUE_NEEDED;
+}
+#endif /* GSSEAP_ENABLE_REAUTH */
+
+static OM_uint32
+eapGssSmInitAcceptorMIC(OM_uint32 *minor,
+ gss_cred_id_t cred GSSEAP_UNUSED,
+ gss_ctx_id_t ctx,
+ gss_name_t target GSSEAP_UNUSED,
+ gss_OID mech GSSEAP_UNUSED,
+ OM_uint32 reqFlags GSSEAP_UNUSED,
+ OM_uint32 timeReq GSSEAP_UNUSED,
+ gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
+ gss_buffer_t inputToken,
+ gss_buffer_t outputToken GSSEAP_UNUSED,
+ OM_uint32 *smFlags GSSEAP_UNUSED)
+{
+ OM_uint32 major;
+
+ major = gssEapVerifyTokenMIC(minor, ctx, inputToken);
+ if (GSS_ERROR(major))
+ return major;
+
+ GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
+
+ *minor = 0;
+
+ return GSS_S_COMPLETE;
+}
+
+static struct gss_eap_sm eapGssInitiatorSm[] = {
+ {
+ ITOK_TYPE_CONTEXT_ERR,
+ ITOK_TYPE_NONE,
+ GSSEAP_STATE_ALL & ~(GSSEAP_STATE_INITIAL),
+ 0,
+ eapGssSmInitError
+ },
+ {
+ ITOK_TYPE_ACCEPTOR_NAME_RESP,
+ ITOK_TYPE_ACCEPTOR_NAME_REQ,
+ GSSEAP_STATE_INITIAL | GSSEAP_STATE_AUTHENTICATE |
+ GSSEAP_STATE_ACCEPTOR_EXTS,
+ 0,
+ eapGssSmInitAcceptorName
+ },
+#ifdef GSSEAP_DEBUG
+ {
+ ITOK_TYPE_NONE,
+ ITOK_TYPE_VENDOR_INFO,
+ GSSEAP_STATE_INITIAL,
+ 0,
+ eapGssSmInitVendorInfo
+ },
+#endif
+#ifdef GSSEAP_ENABLE_REAUTH
+ {
+ ITOK_TYPE_REAUTH_RESP,
+ ITOK_TYPE_REAUTH_REQ,
+ GSSEAP_STATE_INITIAL | GSSEAP_STATE_REAUTHENTICATE,
+ 0,
+ eapGssSmInitGssReauth
+ },
+#endif
+ {
+ ITOK_TYPE_NONE,
+ ITOK_TYPE_NONE,
+#ifdef GSSEAP_ENABLE_REAUTH
+ GSSEAP_STATE_REAUTHENTICATE |
+#endif
+ GSSEAP_STATE_INITIAL,
+ SM_ITOK_FLAG_REQUIRED,
+ eapGssSmInitIdentity
+ },
+ {
+ ITOK_TYPE_EAP_REQ,
+ ITOK_TYPE_EAP_RESP,
+ GSSEAP_STATE_AUTHENTICATE,
+ SM_ITOK_FLAG_REQUIRED,
+ eapGssSmInitAuthenticate
+ },
+ {
+ ITOK_TYPE_NONE,
+ ITOK_TYPE_GSS_FLAGS,
+ GSSEAP_STATE_INITIATOR_EXTS,
+ 0,
+ eapGssSmInitGssFlags
+ },
+ {
+ ITOK_TYPE_NONE,
+ ITOK_TYPE_GSS_CHANNEL_BINDINGS,
+ GSSEAP_STATE_INITIATOR_EXTS,
+ 0,
+ eapGssSmInitGssChannelBindings
+ },
+ {
+ ITOK_TYPE_NONE,
+ ITOK_TYPE_INITIATOR_MIC,
+ GSSEAP_STATE_INITIATOR_EXTS,
+ SM_ITOK_FLAG_REQUIRED,
+ eapGssSmInitInitiatorMIC
+ },
+#ifdef GSSEAP_ENABLE_REAUTH
+ {
+ ITOK_TYPE_REAUTH_CREDS,
+ ITOK_TYPE_NONE,
+ GSSEAP_STATE_ACCEPTOR_EXTS,
+ 0,
+ eapGssSmInitReauthCreds
+ },
+#endif
+ /* other extensions go here */
+ {
+ ITOK_TYPE_ACCEPTOR_MIC,
+ ITOK_TYPE_NONE,
+ GSSEAP_STATE_ACCEPTOR_EXTS,
+ SM_ITOK_FLAG_REQUIRED,
+ eapGssSmInitAcceptorMIC
+ }
+};
+
+OM_uint32
+gssEapInitSecContext(OM_uint32 *minor,
+ gss_cred_id_t cred,
+ gss_ctx_id_t ctx,
+ gss_name_t target_name,
+ gss_OID mech_type,
+ OM_uint32 req_flags,
+ OM_uint32 time_req,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_buffer_t input_token,
+ gss_OID *actual_mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 *ret_flags,
+ OM_uint32 *time_rec)
+{
+ OM_uint32 major, tmpMinor;
+ int initialContextToken = (ctx->mechanismUsed == GSS_C_NO_OID);
+
+ /*
+ * XXX is acquiring the credential lock here necessary? The password is
+ * mutable but the contract could specify that this is not updated whilst
+ * a context is being initialized.
+ */
+ if (cred != GSS_C_NO_CREDENTIAL)
+ GSSEAP_MUTEX_LOCK(&cred->mutex);
+
+ if (ctx->cred == GSS_C_NO_CREDENTIAL) {
+ major = gssEapResolveInitiatorCred(minor, cred, target_name, &ctx->cred);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ GSSEAP_ASSERT(ctx->cred != GSS_C_NO_CREDENTIAL);
+ }
+
+ GSSEAP_MUTEX_LOCK(&ctx->cred->mutex);
+
+ GSSEAP_ASSERT(ctx->cred->flags & CRED_FLAG_RESOLVED);
+ GSSEAP_ASSERT(ctx->cred->flags & CRED_FLAG_INITIATE);
+
+ if (initialContextToken) {
+ major = initBegin(minor, ctx, target_name, mech_type,
+ req_flags, time_req, input_chan_bindings);
+ if (GSS_ERROR(major))
+ goto cleanup;
+ }
+
+ major = gssEapSmStep(minor,
+ cred,
+ ctx,
+ target_name,
+ mech_type,
+ req_flags,
+ time_req,
+ input_chan_bindings,
+ input_token,
+ output_token,
+ eapGssInitiatorSm,
+ sizeof(eapGssInitiatorSm) / sizeof(eapGssInitiatorSm[0]));
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ if (actual_mech_type != NULL) {
+ OM_uint32 tmpMajor;
+
+ tmpMajor = gssEapCanonicalizeOid(&tmpMinor, ctx->mechanismUsed, 0, actual_mech_type);
+ if (GSS_ERROR(tmpMajor)) {
+ major = tmpMajor;
+ *minor = tmpMinor;
+ goto cleanup;
+ }
+ }
+
+ if (ret_flags != NULL)
+ *ret_flags = ctx->gssFlags;
+
+ if (time_rec != NULL)
+ gssEapContextTime(&tmpMinor, ctx, time_rec);
+
+ GSSEAP_ASSERT(CTX_IS_ESTABLISHED(ctx) || major == GSS_S_CONTINUE_NEEDED);
+
+cleanup:
+ if (cred != GSS_C_NO_CREDENTIAL)
+ GSSEAP_MUTEX_UNLOCK(&cred->mutex);
+ if (ctx->cred != GSS_C_NO_CREDENTIAL)
+ GSSEAP_MUTEX_UNLOCK(&ctx->cred->mutex);
+
+ return major;
+}
+
+OM_uint32 GSSAPI_CALLCONV
+gss_init_sec_context(OM_uint32 *minor,
+ gss_cred_id_t cred,
+ gss_ctx_id_t *context_handle,
+ gss_name_t target_name,
+ gss_OID mech_type,
+ OM_uint32 req_flags,
+ OM_uint32 time_req,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_buffer_t input_token,
+ gss_OID *actual_mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 *ret_flags,
+ OM_uint32 *time_rec)
+{
+ OM_uint32 major, tmpMinor;
+ gss_ctx_id_t ctx = *context_handle;
+
+ *minor = 0;
+
+ output_token->length = 0;
+ output_token->value = NULL;
+
+ if (ctx == GSS_C_NO_CONTEXT) {
+ if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) {
+ *minor = GSSEAP_WRONG_SIZE;
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ major = gssEapAllocContext(minor, &ctx);
+ if (GSS_ERROR(major))
+ return major;
+
+ ctx->flags |= CTX_FLAG_INITIATOR;
+
+ *context_handle = ctx;
+ }
+
+ GSSEAP_MUTEX_LOCK(&ctx->mutex);
+
+ major = gssEapInitSecContext(minor,
+ cred,
+ ctx,
+ target_name,
+ mech_type,
+ req_flags,
+ time_req,
+ input_chan_bindings,
+ input_token,
+ actual_mech_type,
+ output_token,
+ ret_flags,
+ time_rec);
+
+ GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
+
+ if (GSS_ERROR(major))
+ gssEapReleaseContext(&tmpMinor, context_handle);
+
+ gssEapTraceStatus( "gss_init_sec_context", major, *minor);
+ return major;
+}