+Makefile.in
+Makefile
+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
+ *.a
*.o
*.d
*.gcno
--- /dev/null
- CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256
+/*
+ * Wrapper functions for crypto libraries
+ * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file defines the cryptographic functions that need to be implemented
+ * for wpa_supplicant and hostapd. When TLS is not used, internal
+ * implementation of MD5, SHA1, and AES is used and no external libraries are
+ * required. When TLS is enabled (e.g., by enabling EAP-TLS or EAP-PEAP), the
+ * crypto library used by the TLS implementation is expected to be used for
+ * non-TLS needs, too, in order to save space by not implementing these
+ * functions twice.
+ *
+ * Wrapper code for using each crypto library is in its own file (crypto*.c)
+ * and one of these files is build and linked in to provide the functions
+ * defined here.
+ */
+
+#ifndef CRYPTO_H
+#define CRYPTO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * md4_vector - MD4 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
+
+/**
+ * md5_vector - MD5 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
+
+
+/**
+ * sha1_vector - SHA-1 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac);
+
+/**
+ * fips186_2-prf - NIST FIPS Publication 186-2 change notice 1 PRF
+ * @seed: Seed/key for the PRF
+ * @seed_len: Seed length in bytes
+ * @x: Buffer for PRF output
+ * @xlen: Output length in bytes
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function implements random number generation specified in NIST FIPS
+ * Publication 186-2 for EAP-SIM. This PRF uses a function that is similar to
+ * SHA-1, but has different message padding.
+ */
+int __must_check fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x,
+ size_t xlen);
+
+/**
+ * sha256_vector - SHA256 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac);
+
+/**
++ * sha384_vector - SHA384 hash for data vector
++ * @num_elem: Number of elements in the data vector
++ * @addr: Pointers to the data areas
++ * @len: Lengths of the data blocks
++ * @mac: Buffer for the hash
++ * Returns: 0 on success, -1 on failure
++ */
++int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
++ u8 *mac);
++
++/**
++ * sha512_vector - SHA512 hash for data vector
++ * @num_elem: Number of elements in the data vector
++ * @addr: Pointers to the data areas
++ * @len: Lengths of the data blocks
++ * @mac: Buffer for the hash
++ * Returns: 0 on success, -1 on failure
++ */
++int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
++ u8 *mac);
++
++/**
+ * des_encrypt - Encrypt one block with DES
+ * @clear: 8 octets (in)
+ * @key: 7 octets (in) (no parity bits included)
+ * @cypher: 8 octets (out)
+ */
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher);
+
+/**
+ * aes_encrypt_init - Initialize AES for encryption
+ * @key: Encryption key
+ * @len: Key length in bytes (usually 16, i.e., 128 bits)
+ * Returns: Pointer to context data or %NULL on failure
+ */
+void * aes_encrypt_init(const u8 *key, size_t len);
+
+/**
+ * aes_encrypt - Encrypt one AES block
+ * @ctx: Context pointer from aes_encrypt_init()
+ * @plain: Plaintext data to be encrypted (16 bytes)
+ * @crypt: Buffer for the encrypted data (16 bytes)
+ */
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
+
+/**
+ * aes_encrypt_deinit - Deinitialize AES encryption
+ * @ctx: Context pointer from aes_encrypt_init()
+ */
+void aes_encrypt_deinit(void *ctx);
+
+/**
+ * aes_decrypt_init - Initialize AES for decryption
+ * @key: Decryption key
+ * @len: Key length in bytes (usually 16, i.e., 128 bits)
+ * Returns: Pointer to context data or %NULL on failure
+ */
+void * aes_decrypt_init(const u8 *key, size_t len);
+
+/**
+ * aes_decrypt - Decrypt one AES block
+ * @ctx: Context pointer from aes_encrypt_init()
+ * @crypt: Encrypted data (16 bytes)
+ * @plain: Buffer for the decrypted data (16 bytes)
+ */
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
+
+/**
+ * aes_decrypt_deinit - Deinitialize AES decryption
+ * @ctx: Context pointer from aes_encrypt_init()
+ */
+void aes_decrypt_deinit(void *ctx);
+
+
+enum crypto_hash_alg {
+ CRYPTO_HASH_ALG_MD5, CRYPTO_HASH_ALG_SHA1,
+ CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1,
++ CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256,
++ CRYPTO_HASH_ALG_SHA384, CRYPTO_HASH_ALG_SHA512
+};
+
+struct crypto_hash;
+
+/**
+ * crypto_hash_init - Initialize hash/HMAC function
+ * @alg: Hash algorithm
+ * @key: Key for keyed hash (e.g., HMAC) or %NULL if not needed
+ * @key_len: Length of the key in bytes
+ * Returns: Pointer to hash context to use with other hash functions or %NULL
+ * on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+ size_t key_len);
+
+/**
+ * crypto_hash_update - Add data to hash calculation
+ * @ctx: Context pointer from crypto_hash_init()
+ * @data: Data buffer to add
+ * @len: Length of the buffer
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len);
+
+/**
+ * crypto_hash_finish - Complete hash calculation
+ * @ctx: Context pointer from crypto_hash_init()
+ * @hash: Buffer for hash value or %NULL if caller is just freeing the hash
+ * context
+ * @len: Pointer to length of the buffer or %NULL if caller is just freeing the
+ * hash context; on return, this is set to the actual length of the hash value
+ * Returns: 0 on success, -1 if buffer is too small (len set to needed length),
+ * or -2 on other failures (including failed crypto_hash_update() operations)
+ *
+ * This function calculates the hash value and frees the context buffer that
+ * was used for hash calculation.
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *hash, size_t *len);
+
+
+enum crypto_cipher_alg {
+ CRYPTO_CIPHER_NULL = 0, CRYPTO_CIPHER_ALG_AES, CRYPTO_CIPHER_ALG_3DES,
+ CRYPTO_CIPHER_ALG_DES, CRYPTO_CIPHER_ALG_RC2, CRYPTO_CIPHER_ALG_RC4
+};
+
+struct crypto_cipher;
+
+/**
+ * crypto_cipher_init - Initialize block/stream cipher function
+ * @alg: Cipher algorithm
+ * @iv: Initialization vector for block ciphers or %NULL for stream ciphers
+ * @key: Cipher key
+ * @key_len: Length of key in bytes
+ * Returns: Pointer to cipher context to use with other cipher functions or
+ * %NULL on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+ const u8 *iv, const u8 *key,
+ size_t key_len);
+
+/**
+ * crypto_cipher_encrypt - Cipher encrypt
+ * @ctx: Context pointer from crypto_cipher_init()
+ * @plain: Plaintext to cipher
+ * @crypt: Resulting ciphertext
+ * @len: Length of the plaintext
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_cipher_encrypt(struct crypto_cipher *ctx,
+ const u8 *plain, u8 *crypt, size_t len);
+
+/**
+ * crypto_cipher_decrypt - Cipher decrypt
+ * @ctx: Context pointer from crypto_cipher_init()
+ * @crypt: Ciphertext to decrypt
+ * @plain: Resulting plaintext
+ * @len: Length of the cipher text
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_cipher_decrypt(struct crypto_cipher *ctx,
+ const u8 *crypt, u8 *plain, size_t len);
+
+/**
+ * crypto_cipher_decrypt - Free cipher context
+ * @ctx: Context pointer from crypto_cipher_init()
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_cipher_deinit(struct crypto_cipher *ctx);
+
+
+struct crypto_public_key;
+struct crypto_private_key;
+
+/**
+ * crypto_public_key_import - Import an RSA public key
+ * @key: Key buffer (DER encoded RSA public key)
+ * @len: Key buffer length in bytes
+ * Returns: Pointer to the public key or %NULL on failure
+ *
+ * This function can just return %NULL if the crypto library supports X.509
+ * parsing. In that case, crypto_public_key_from_cert() is used to import the
+ * public key from a certificate.
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len);
+
+struct crypto_public_key *
+crypto_public_key_import_parts(const u8 *n, size_t n_len,
+ const u8 *e, size_t e_len);
+
+/**
+ * crypto_private_key_import - Import an RSA private key
+ * @key: Key buffer (DER encoded RSA private key)
+ * @len: Key buffer length in bytes
+ * @passwd: Key encryption password or %NULL if key is not encrypted
+ * Returns: Pointer to the private key or %NULL on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_private_key * crypto_private_key_import(const u8 *key,
+ size_t len,
+ const char *passwd);
+
+/**
+ * crypto_public_key_from_cert - Import an RSA public key from a certificate
+ * @buf: DER encoded X.509 certificate
+ * @len: Certificate buffer length in bytes
+ * Returns: Pointer to public key or %NULL on failure
+ *
+ * This function can just return %NULL if the crypto library does not support
+ * X.509 parsing. In that case, internal code will be used to parse the
+ * certificate and public key is imported using crypto_public_key_import().
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf,
+ size_t len);
+
+/**
+ * crypto_public_key_encrypt_pkcs1_v15 - Public key encryption (PKCS #1 v1.5)
+ * @key: Public key
+ * @in: Plaintext buffer
+ * @inlen: Length of plaintext buffer in bytes
+ * @out: Output buffer for encrypted data
+ * @outlen: Length of output buffer in bytes; set to used length on success
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_public_key_encrypt_pkcs1_v15(
+ struct crypto_public_key *key, const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen);
+
+/**
+ * crypto_private_key_decrypt_pkcs1_v15 - Private key decryption (PKCS #1 v1.5)
+ * @key: Private key
+ * @in: Encrypted buffer
+ * @inlen: Length of encrypted buffer in bytes
+ * @out: Output buffer for encrypted data
+ * @outlen: Length of output buffer in bytes; set to used length on success
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_private_key_decrypt_pkcs1_v15(
+ struct crypto_private_key *key, const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen);
+
+/**
+ * crypto_private_key_sign_pkcs1 - Sign with private key (PKCS #1)
+ * @key: Private key from crypto_private_key_import()
+ * @in: Plaintext buffer
+ * @inlen: Length of plaintext buffer in bytes
+ * @out: Output buffer for encrypted (signed) data
+ * @outlen: Length of output buffer in bytes; set to used length on success
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_private_key_sign_pkcs1(struct crypto_private_key *key,
+ const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen);
+
+/**
+ * crypto_public_key_free - Free public key
+ * @key: Public key
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_public_key_free(struct crypto_public_key *key);
+
+/**
+ * crypto_private_key_free - Free private key
+ * @key: Private key from crypto_private_key_import()
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_private_key_free(struct crypto_private_key *key);
+
+/**
+ * crypto_public_key_decrypt_pkcs1 - Decrypt PKCS #1 signature
+ * @key: Public key
+ * @crypt: Encrypted signature data (using the private key)
+ * @crypt_len: Encrypted signature data length
+ * @plain: Buffer for plaintext (at least crypt_len bytes)
+ * @plain_len: Plaintext length (max buffer size on input, real len on output);
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check crypto_public_key_decrypt_pkcs1(
+ struct crypto_public_key *key, const u8 *crypt, size_t crypt_len,
+ u8 *plain, size_t *plain_len);
+
+/**
+ * crypto_global_init - Initialize crypto wrapper
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_global_init(void);
+
+/**
+ * crypto_global_deinit - Deinitialize crypto wrapper
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_global_deinit(void);
+
+/**
+ * crypto_mod_exp - Modular exponentiation of large integers
+ * @base: Base integer (big endian byte array)
+ * @base_len: Length of base integer in bytes
+ * @power: Power integer (big endian byte array)
+ * @power_len: Length of power integer in bytes
+ * @modulus: Modulus integer (big endian byte array)
+ * @modulus_len: Length of modulus integer in bytes
+ * @result: Buffer for the result
+ * @result_len: Result length (max buffer size on input, real len on output)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function calculates result = base ^ power mod modulus. modules_len is
+ * used as the maximum size of modulus buffer. It is set to the used size on
+ * success.
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_mod_exp(const u8 *base, size_t base_len,
+ const u8 *power, size_t power_len,
+ const u8 *modulus, size_t modulus_len,
+ u8 *result, size_t *result_len);
+
+/**
+ * rc4_skip - XOR RC4 stream to given data with skip-stream-start
+ * @key: RC4 key
+ * @keylen: RC4 key length
+ * @skip: number of bytes to skip from the beginning of the RC4 stream
+ * @data: data to be XOR'ed with RC4 stream
+ * @data_len: buf length
+ * Returns: 0 on success, -1 on failure
+ *
+ * Generate RC4 pseudo random stream for the given key, skip beginning of the
+ * stream, and XOR the end result with the data buffer to perform RC4
+ * encryption/decryption.
+ */
+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
+ u8 *data, size_t data_len);
+
+/**
+ * crypto_get_random - Generate cryptographically strong pseudy-random bytes
+ * @buf: Buffer for data
+ * @len: Number of bytes to generate
+ * Returns: 0 on success, -1 on failure
+ *
+ * If the PRNG does not have enough entropy to ensure unpredictable byte
+ * sequence, this functions must return -1.
+ */
+int crypto_get_random(void *buf, size_t len);
+
+
+/**
+ * struct crypto_bignum - bignum
+ *
+ * Internal data structure for bignum implementation. The contents is specific
+ * to the used crypto library.
+ */
+struct crypto_bignum;
+
+/**
+ * crypto_bignum_init - Allocate memory for bignum
+ * Returns: Pointer to allocated bignum or %NULL on failure
+ */
+struct crypto_bignum * crypto_bignum_init(void);
+
+/**
+ * crypto_bignum_init_set - Allocate memory for bignum and set the value
+ * @buf: Buffer with unsigned binary value
+ * @len: Length of buf in octets
+ * Returns: Pointer to allocated bignum or %NULL on failure
+ */
+struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len);
+
+/**
+ * crypto_bignum_deinit - Free bignum
+ * @n: Bignum from crypto_bignum_init() or crypto_bignum_init_set()
+ * @clear: Whether to clear the value from memory
+ */
+void crypto_bignum_deinit(struct crypto_bignum *n, int clear);
+
+/**
+ * crypto_bignum_to_bin - Set binary buffer to unsigned bignum
+ * @a: Bignum
+ * @buf: Buffer for the binary number
+ * @len: Length of @buf in octets
+ * @padlen: Length in octets to pad the result to or 0 to indicate no padding
+ * Returns: Number of octets written on success, -1 on failure
+ */
+int crypto_bignum_to_bin(const struct crypto_bignum *a,
+ u8 *buf, size_t buflen, size_t padlen);
+
+/**
+ * crypto_bignum_add - c = a + b
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a + b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_add(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_mod - c = a % b
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a % b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_mod(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_exptmod - Modular exponentiation: d = a^b (mod c)
+ * @a: Bignum; base
+ * @b: Bignum; exponent
+ * @c: Bignum; modulus
+ * @d: Bignum; used to store the result of a^b (mod c)
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_exptmod(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ const struct crypto_bignum *c,
+ struct crypto_bignum *d);
+
+/**
+ * crypto_bignum_inverse - Inverse a bignum so that a * c = 1 (mod b)
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_inverse(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_sub - c = a - b
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a - b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_sub(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_div - c = a / b
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a / b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_div(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_mulmod - d = a * b (mod c)
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum
+ * @d: Bignum; used to store the result of (a * b) % c
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_mulmod(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ const struct crypto_bignum *c,
+ struct crypto_bignum *d);
+
+/**
+ * crypto_bignum_cmp - Compare two bignums
+ * @a: Bignum
+ * @b: Bignum
+ * Returns: -1 if a < b, 0 if a == b, or 1 if a > b
+ */
+int crypto_bignum_cmp(const struct crypto_bignum *a,
+ const struct crypto_bignum *b);
+
+/**
+ * crypto_bignum_bits - Get size of a bignum in bits
+ * @a: Bignum
+ * Returns: Number of bits in the bignum
+ */
+int crypto_bignum_bits(const struct crypto_bignum *a);
+
+/**
+ * crypto_bignum_is_zero - Is the given bignum zero
+ * @a: Bignum
+ * Returns: 1 if @a is zero or 0 if not
+ */
+int crypto_bignum_is_zero(const struct crypto_bignum *a);
+
+/**
+ * crypto_bignum_is_one - Is the given bignum one
+ * @a: Bignum
+ * Returns: 1 if @a is one or 0 if not
+ */
+int crypto_bignum_is_one(const struct crypto_bignum *a);
+
+/**
+ * crypto_bignum_legendre - Compute the Legendre symbol (a/p)
+ * @a: Bignum
+ * @p: Bignum
+ * Returns: Legendre symbol -1,0,1 on success; -2 on calculation failure
+ */
+int crypto_bignum_legendre(const struct crypto_bignum *a,
+ const struct crypto_bignum *p);
+
+/**
+ * struct crypto_ec - Elliptic curve context
+ *
+ * Internal data structure for EC implementation. The contents is specific
+ * to the used crypto library.
+ */
+struct crypto_ec;
+
+/**
+ * crypto_ec_init - Initialize elliptic curve context
+ * @group: Identifying number for the ECC group (IANA "Group Description"
+ * attribute registrty for RFC 2409)
+ * Returns: Pointer to EC context or %NULL on failure
+ */
+struct crypto_ec * crypto_ec_init(int group);
+
+/**
+ * crypto_ec_deinit - Deinitialize elliptic curve context
+ * @e: EC context from crypto_ec_init()
+ */
+void crypto_ec_deinit(struct crypto_ec *e);
+
+/**
+ * crypto_ec_prime_len - Get length of the prime in octets
+ * @e: EC context from crypto_ec_init()
+ * Returns: Length of the prime defining the group
+ */
+size_t crypto_ec_prime_len(struct crypto_ec *e);
+
+/**
+ * crypto_ec_prime_len_bits - Get length of the prime in bits
+ * @e: EC context from crypto_ec_init()
+ * Returns: Length of the prime defining the group in bits
+ */
+size_t crypto_ec_prime_len_bits(struct crypto_ec *e);
+
+/**
+ * crypto_ec_get_prime - Get prime defining an EC group
+ * @e: EC context from crypto_ec_init()
+ * Returns: Prime (bignum) defining the group
+ */
+const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e);
+
+/**
+ * crypto_ec_get_order - Get order of an EC group
+ * @e: EC context from crypto_ec_init()
+ * Returns: Order (bignum) of the group
+ */
+const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e);
+
+/**
+ * struct crypto_ec_point - Elliptic curve point
+ *
+ * Internal data structure for EC implementation to represent a point. The
+ * contents is specific to the used crypto library.
+ */
+struct crypto_ec_point;
+
+/**
+ * crypto_ec_point_init - Initialize data for an EC point
+ * @e: EC context from crypto_ec_init()
+ * Returns: Pointer to EC point data or %NULL on failure
+ */
+struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e);
+
+/**
+ * crypto_ec_point_deinit - Deinitialize EC point data
+ * @p: EC point data from crypto_ec_point_init()
+ * @clear: Whether to clear the EC point value from memory
+ */
+void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear);
+
+/**
+ * crypto_ec_point_to_bin - Write EC point value as binary data
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point data from crypto_ec_point_init()
+ * @x: Buffer for writing the binary data for x coordinate or %NULL if not used
+ * @y: Buffer for writing the binary data for y coordinate or %NULL if not used
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to write an EC point as binary data in a format
+ * that has the x and y coordinates in big endian byte order fields padded to
+ * the length of the prime defining the group.
+ */
+int crypto_ec_point_to_bin(struct crypto_ec *e,
+ const struct crypto_ec_point *point, u8 *x, u8 *y);
+
+/**
+ * crypto_ec_point_from_bin - Create EC point from binary data
+ * @e: EC context from crypto_ec_init()
+ * @val: Binary data to read the EC point from
+ * Returns: Pointer to EC point data or %NULL on failure
+ *
+ * This function readers x and y coordinates of the EC point from the provided
+ * buffer assuming the values are in big endian byte order with fields padded to
+ * the length of the prime defining the group.
+ */
+struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
+ const u8 *val);
+
+/**
+ * crypto_bignum_add - c = a + b
+ * @e: EC context from crypto_ec_init()
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a + b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
+ const struct crypto_ec_point *b,
+ struct crypto_ec_point *c);
+
+/**
+ * crypto_bignum_mul - res = b * p
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point
+ * @b: Bignum
+ * @res: EC point; used to store the result of b * p
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
+ const struct crypto_bignum *b,
+ struct crypto_ec_point *res);
+
+/**
+ * crypto_ec_point_invert - Compute inverse of an EC point
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point to invert (and result of the operation)
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p);
+
+/**
+ * crypto_ec_point_solve_y_coord - Solve y coordinate for an x coordinate
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point to use for the returning the result
+ * @x: x coordinate
+ * @y_bit: y-bit (0 or 1) for selecting the y value to use
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_solve_y_coord(struct crypto_ec *e,
+ struct crypto_ec_point *p,
+ const struct crypto_bignum *x, int y_bit);
+
+/**
+ * crypto_ec_point_compute_y_sqr - Compute y^2 = x^3 + ax + b
+ * @e: EC context from crypto_ec_init()
+ * @x: x coordinate
+ * Returns: y^2 on success, %NULL failure
+ */
+struct crypto_bignum *
+crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
+ const struct crypto_bignum *x);
+
+/**
+ * crypto_ec_point_is_at_infinity - Check whether EC point is neutral element
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point
+ * Returns: 1 if the specified EC point is the neutral element of the group or
+ * 0 if not
+ */
+int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
+ const struct crypto_ec_point *p);
+
+/**
+ * crypto_ec_point_is_on_curve - Check whether EC point is on curve
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point
+ * Returns: 1 if the specified EC point is on the curve or 0 if not
+ */
+int crypto_ec_point_is_on_curve(struct crypto_ec *e,
+ const struct crypto_ec_point *p);
+
+/**
+ * crypto_ec_point_cmp - Compare two EC points
+ * @e: EC context from crypto_ec_init()
+ * @a: EC point
+ * @b: EC point
+ * Returns: 0 on equal, non-zero otherwise
+ */
+int crypto_ec_point_cmp(const struct crypto_ec *e,
+ const struct crypto_ec_point *a,
+ const struct crypto_ec_point *b);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CRYPTO_H */
--- /dev/null
-
- /**
- * If non-null, specifies a callback method that can be used to
- * confirm the validity of a peer certificate.
- */
+/*
+ * SSL/TLS interface definition
+ * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TLS_H
+#define TLS_H
+
+#include <openssl/x509.h>
+
+struct tls_connection;
+
+struct tls_random {
+ const u8 *client_random;
+ size_t client_random_len;
+ const u8 *server_random;
+ size_t server_random_len;
+};
+
+enum tls_event {
+ TLS_CERT_CHAIN_SUCCESS,
+ TLS_CERT_CHAIN_FAILURE,
+ TLS_PEER_CERTIFICATE,
+ TLS_ALERT
+};
+
+/*
+ * Note: These are used as identifier with external programs and as such, the
+ * values must not be changed.
+ */
+enum tls_fail_reason {
+ TLS_FAIL_UNSPECIFIED = 0,
+ TLS_FAIL_UNTRUSTED = 1,
+ TLS_FAIL_REVOKED = 2,
+ TLS_FAIL_NOT_YET_VALID = 3,
+ TLS_FAIL_EXPIRED = 4,
+ TLS_FAIL_SUBJECT_MISMATCH = 5,
+ TLS_FAIL_ALTSUBJECT_MISMATCH = 6,
+ TLS_FAIL_BAD_CERTIFICATE = 7,
+ TLS_FAIL_SERVER_CHAIN_PROBE = 8,
+ TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9,
+ TLS_FAIL_DOMAIN_MISMATCH = 10,
+};
+
+
+#define TLS_MAX_ALT_SUBJECT 10
+
+union tls_event_data {
+ struct {
+ int depth;
+ const char *subject;
+ enum tls_fail_reason reason;
+ const char *reason_txt;
+ const struct wpabuf *cert;
+ } cert_fail;
+
+ struct {
+ int depth;
+ const char *subject;
+ const struct wpabuf *cert;
+ const u8 *hash;
+ size_t hash_len;
+ const char *altsubject[TLS_MAX_ALT_SUBJECT];
+ int num_altsubject;
+ } peer_cert;
+
+ struct {
+ int is_local;
+ const char *type;
+ const char *description;
+ } alert;
+};
+
+struct tls_config {
+ const char *opensc_engine_path;
+ const char *pkcs11_engine_path;
+ const char *pkcs11_module_path;
+ int fips_mode;
+ int cert_in_cb;
+ const char *openssl_ciphers;
+ unsigned int tls_session_lifetime;
+
+ void (*event_cb)(void *ctx, enum tls_event ev,
+ union tls_event_data *data);
+ void *cb_ctx;
+};
+
+#define TLS_CONN_ALLOW_SIGN_RSA_MD5 BIT(0)
+#define TLS_CONN_DISABLE_TIME_CHECKS BIT(1)
+#define TLS_CONN_DISABLE_SESSION_TICKET BIT(2)
+#define TLS_CONN_REQUEST_OCSP BIT(3)
+#define TLS_CONN_REQUIRE_OCSP BIT(4)
+#define TLS_CONN_DISABLE_TLSv1_1 BIT(5)
+#define TLS_CONN_DISABLE_TLSv1_2 BIT(6)
+#define TLS_CONN_EAP_FAST BIT(7)
+#define TLS_CONN_DISABLE_TLSv1_0 BIT(8)
++#define TLS_CONN_EXT_CERT_CHECK BIT(9)
++#define TLS_CONN_REQUIRE_OCSP_ALL BIT(10)
+
+struct X509; /* from OpenSSL */
+
+/**
+ * struct tls_connection_params - Parameters for TLS connection
+ * @ca_cert: File or reference name for CA X.509 certificate in PEM or DER
+ * format
+ * @ca_cert_blob: ca_cert as inlined data or %NULL if not used
+ * @ca_cert_blob_len: ca_cert_blob length
+ * @ca_path: Path to CA certificates (OpenSSL specific)
+ * @subject_match: String to match in the subject of the peer certificate or
+ * %NULL to allow all subjects
+ * @altsubject_match: String to match in the alternative subject of the peer
+ * certificate or %NULL to allow all alternative subjects
+ * @suffix_match: String to suffix match in the dNSName or CN of the peer
+ * certificate or %NULL to allow all domain names. This may allow subdomains an
+ * wildcard certificates. Each domain name label must have a full match.
+ * @domain_match: String to match in the dNSName or CN of the peer
+ * certificate or %NULL to allow all domain names. This requires a full,
+ * case-insensitive match.
+ * @client_cert: File or reference name for client X.509 certificate in PEM or
+ * DER format
+ * @client_cert_blob: client_cert as inlined data or %NULL if not used
+ * @client_cert_blob_len: client_cert_blob length
+ * @private_key: File or reference name for client private key in PEM or DER
+ * format (traditional format (RSA PRIVATE KEY) or PKCS#8 (PRIVATE KEY)
+ * @private_key_blob: private_key as inlined data or %NULL if not used
+ * @private_key_blob_len: private_key_blob length
+ * @private_key_passwd: Passphrase for decrypted private key, %NULL if no
+ * passphrase is used.
+ * @dh_file: File name for DH/DSA data in PEM format, or %NULL if not used
+ * @dh_blob: dh_file as inlined data or %NULL if not used
+ * @dh_blob_len: dh_blob length
+ * @engine: 1 = use engine (e.g., a smartcard) for private key operations
+ * (this is OpenSSL specific for now)
+ * @engine_id: engine id string (this is OpenSSL specific for now)
+ * @ppin: pointer to the pin variable in the configuration
+ * (this is OpenSSL specific for now)
+ * @key_id: the private key's id when using engine (this is OpenSSL
+ * specific for now)
+ * @cert_id: the certificate's id when using engine
+ * @ca_cert_id: the CA certificate's id when using engine
+ * @openssl_ciphers: OpenSSL cipher configuration
+ * @flags: Parameter options (TLS_CONN_*)
+ * @ocsp_stapling_response: DER encoded file with cached OCSP stapling response
+ * or %NULL if OCSP is not enabled
++ * @ocsp_stapling_response_multi: DER encoded file with cached OCSP stapling
++ * response list (OCSPResponseList for ocsp_multi in RFC 6961) or %NULL if
++ * ocsp_multi is not enabled
+ * @server_cert_cb: Optional callback to be used to validate server certificate
+ * when no CA or path was specified.
+ * @server_cert_ctx: Optional context arg for server_cert_cb.
+ *
+ * TLS connection parameters to be configured with tls_connection_set_params()
+ * and tls_global_set_params().
+ *
+ * Certificates and private key can be configured either as a reference name
+ * (file path or reference to certificate store) or by providing the same data
+ * as a pointer to the data in memory. Only one option will be used for each
+ * field.
+ */
+struct tls_connection_params {
+ const char *ca_cert;
+ const u8 *ca_cert_blob;
+ size_t ca_cert_blob_len;
+ const char *ca_path;
+ const char *subject_match;
+ const char *altsubject_match;
+ const char *suffix_match;
+ const char *domain_match;
+ const char *client_cert;
+ const u8 *client_cert_blob;
+ size_t client_cert_blob_len;
+ const char *private_key;
+ const u8 *private_key_blob;
+ size_t private_key_blob_len;
+ const char *private_key_passwd;
+ const char *dh_file;
+ const u8 *dh_blob;
+ size_t dh_blob_len;
+
+ /* OpenSSL specific variables */
+ int engine;
+ const char *engine_id;
+ const char *pin;
+ const char *key_id;
+ const char *cert_id;
+ const char *ca_cert_id;
+ const char *openssl_ciphers;
+
+ unsigned int flags;
+ const char *ocsp_stapling_response;
- * tls_connection_prf - Use TLS-PRF to derive keying material
++ const char *ocsp_stapling_response_multi;
+ int (*server_cert_cb)(int ok_so_far, X509* cert, void *ca_ctx);
+ void *server_cert_ctx;
+};
+
+
+/**
+ * tls_init - Initialize TLS library
+ * @conf: Configuration data for TLS library
+ * Returns: Context data to be used as tls_ctx in calls to other functions,
+ * or %NULL on failure.
+ *
+ * Called once during program startup and once for each RSN pre-authentication
+ * session. In other words, there can be two concurrent TLS contexts. If global
+ * library initialization is needed (i.e., one that is shared between both
+ * authentication types), the TLS library wrapper should maintain a reference
+ * counter and do global initialization only when moving from 0 to 1 reference.
+ */
+void * tls_init(const struct tls_config *conf);
+
+/**
+ * tls_deinit - Deinitialize TLS library
+ * @tls_ctx: TLS context data from tls_init()
+ *
+ * Called once during program shutdown and once for each RSN pre-authentication
+ * session. If global library deinitialization is needed (i.e., one that is
+ * shared between both authentication types), the TLS library wrapper should
+ * maintain a reference counter and do global deinitialization only when moving
+ * from 1 to 0 references.
+ */
+void tls_deinit(void *tls_ctx);
+
+/**
+ * tls_get_errors - Process pending errors
+ * @tls_ctx: TLS context data from tls_init()
+ * Returns: Number of found error, 0 if no errors detected.
+ *
+ * Process all pending TLS errors.
+ */
+int tls_get_errors(void *tls_ctx);
+
+/**
+ * tls_connection_init - Initialize a new TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * Returns: Connection context data, conn for other function calls
+ */
+struct tls_connection * tls_connection_init(void *tls_ctx);
+
+/**
+ * tls_connection_deinit - Free TLS connection data
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ *
+ * Release all resources allocated for TLS connection.
+ */
+void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_established - Has the TLS connection been completed?
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: 1 if TLS connection has been completed, 0 if not.
+ */
+int tls_connection_established(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_shutdown - Shutdown TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * Shutdown current TLS connection without releasing all resources. New
+ * connection can be started by using the same conn without having to call
+ * tls_connection_init() or setting certificates etc. again. The new
+ * connection should try to use session resumption.
+ */
+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn);
+
+enum {
+ TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN = -4,
+ TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED = -3,
+ TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED = -2
+};
+
+/**
+ * tls_connection_set_params - Set TLS connection parameters
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @params: Connection parameters
+ * Returns: 0 on success, -1 on failure,
+ * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on error causing PKCS#11 engine
+ * failure, or
+ * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the
+ * PKCS#11 engine private key, or
+ * TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN (-4) on PIN error causing PKCS#11 engine
+ * failure.
+ */
+int __must_check
+tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+ const struct tls_connection_params *params);
+
+/**
+ * tls_global_set_params - Set TLS parameters for all TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @params: Global TLS parameters
+ * Returns: 0 on success, -1 on failure,
+ * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on error causing PKCS#11 engine
+ * failure, or
+ * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the
+ * PKCS#11 engine private key, or
+ * TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN (-4) on PIN error causing PKCS#11 engine
+ * failure.
+ */
+int __must_check tls_global_set_params(
+ void *tls_ctx, const struct tls_connection_params *params);
+
+/**
+ * tls_global_set_verify - Set global certificate verification options
+ * @tls_ctx: TLS context data from tls_init()
+ * @check_crl: 0 = do not verify CRLs, 1 = verify CRL for the user certificate,
+ * 2 = verify CRL for all certificates
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_global_set_verify(void *tls_ctx, int check_crl);
+
+/**
+ * tls_connection_set_verify - Set certificate verification options
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @verify_peer: 1 = verify peer certificate
+ * @flags: Connection flags (TLS_CONN_*)
+ * @session_ctx: Session caching context or %NULL to use default
+ * @session_ctx_len: Length of @session_ctx in bytes.
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_connection_set_verify(void *tls_ctx,
+ struct tls_connection *conn,
+ int verify_peer,
+ unsigned int flags,
+ const u8 *session_ctx,
+ size_t session_ctx_len);
+
+/**
+ * tls_connection_get_random - Get random data from TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @data: Structure of client/server random data (filled on success)
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_connection_get_random(void *tls_ctx,
+ struct tls_connection *conn,
+ struct tls_random *data);
+
+/**
- * @server_random_first: seed is 0 = client_random|server_random,
- * 1 = server_random|client_random
- * @skip_keyblock: Skip TLS key block from the beginning of PRF output
++ * tls_connection_export_key - Derive keying material from a TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @label: Label (e.g., description of the key) for PRF
- * tls_connection_prf() is required so that further keying material can be
- * derived from the master secret. Example implementation of this function is in
- * tls_prf_sha1_md5() when it is called with seed set to
- * client_random|server_random (or server_random|client_random). For TLSv1.2 and
- * newer, a different PRF is needed, though.
+ * @out: Buffer for output data from TLS-PRF
+ * @out_len: Length of the output buffer
+ * Returns: 0 on success, -1 on failure
+ *
- int __must_check tls_connection_prf(void *tls_ctx,
- struct tls_connection *conn,
- const char *label,
- int server_random_first,
- int skip_keyblock,
- u8 *out, size_t out_len);
++ * Exports keying material using the mechanism described in RFC 5705.
++ */
++int __must_check tls_connection_export_key(void *tls_ctx,
++ struct tls_connection *conn,
++ const char *label,
++ u8 *out, size_t out_len);
++
++/**
++ * tls_connection_get_eap_fast_key - Derive key material for EAP-FAST
++ * @tls_ctx: TLS context data from tls_init()
++ * @conn: Connection context data from tls_connection_init()
++ * @out: Buffer for output data from TLS-PRF
++ * @out_len: Length of the output buffer
++ * Returns: 0 on success, -1 on failure
++ *
++ * Exports key material after the normal TLS key block for use with
++ * EAP-FAST. Most callers will want tls_connection_export_key(), but EAP-FAST
++ * uses a different legacy mechanism.
+ */
- TLS_CIPHER_ANON_DH_AES128_SHA /* 0x0034 */
++int __must_check tls_connection_get_eap_fast_key(void *tls_ctx,
++ struct tls_connection *conn,
++ u8 *out, size_t out_len);
+
+/**
+ * tls_connection_handshake - Process TLS handshake (client side)
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @in_data: Input data from TLS server
+ * @appl_data: Pointer to application data pointer, or %NULL if dropped
+ * Returns: Output data, %NULL on failure
+ *
+ * The caller is responsible for freeing the returned output data. If the final
+ * handshake message includes application data, this is decrypted and
+ * appl_data (if not %NULL) is set to point this data. The caller is
+ * responsible for freeing appl_data.
+ *
+ * This function is used during TLS handshake. The first call is done with
+ * in_data == %NULL and the library is expected to return ClientHello packet.
+ * This packet is then send to the server and a response from server is given
+ * to TLS library by calling this function again with in_data pointing to the
+ * TLS message from the server.
+ *
+ * If the TLS handshake fails, this function may return %NULL. However, if the
+ * TLS library has a TLS alert to send out, that should be returned as the
+ * output data. In this case, tls_connection_get_failed() must return failure
+ * (> 0).
+ *
+ * tls_connection_established() should return 1 once the TLS handshake has been
+ * completed successfully.
+ */
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data);
+
+struct wpabuf * tls_connection_handshake2(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data,
+ int *more_data_needed);
+
+/**
+ * tls_connection_server_handshake - Process TLS handshake (server side)
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @in_data: Input data from TLS peer
+ * @appl_data: Pointer to application data pointer, or %NULL if dropped
+ * Returns: Output data, %NULL on failure
+ *
+ * The caller is responsible for freeing the returned output data.
+ */
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data);
+
+/**
+ * tls_connection_encrypt - Encrypt data into TLS tunnel
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @in_data: Plaintext data to be encrypted
+ * Returns: Encrypted TLS data or %NULL on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * send data in the encrypted tunnel. The caller is responsible for freeing the
+ * returned output data.
+ */
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data);
+
+/**
+ * tls_connection_decrypt - Decrypt data from TLS tunnel
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @in_data: Encrypted TLS data
+ * Returns: Decrypted TLS data or %NULL on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * receive data from the encrypted tunnel. The caller is responsible for
+ * freeing the returned output data.
+ */
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data);
+
+struct wpabuf * tls_connection_decrypt2(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ int *more_data_needed);
+
+/**
+ * tls_connection_resumed - Was session resumption used
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: 1 if current session used session resumption, 0 if not
+ */
+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn);
+
+enum {
+ TLS_CIPHER_NONE,
+ TLS_CIPHER_RC4_SHA /* 0x0005 */,
+ TLS_CIPHER_AES128_SHA /* 0x002f */,
+ TLS_CIPHER_RSA_DHE_AES128_SHA /* 0x0031 */,
++ TLS_CIPHER_ANON_DH_AES128_SHA /* 0x0034 */,
++ TLS_CIPHER_RSA_DHE_AES256_SHA /* 0x0039 */,
++ TLS_CIPHER_AES256_SHA /* 0x0035 */,
+};
+
+/**
+ * tls_connection_set_cipher_list - Configure acceptable cipher suites
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers
+ * (TLS_CIPHER_*).
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_connection_set_cipher_list(void *tls_ctx,
+ struct tls_connection *conn,
+ u8 *ciphers);
+
+/**
+ * tls_get_version - Get the current TLS version number
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @buf: Buffer for returning the TLS version number
+ * @buflen: buf size
+ * Returns: 0 on success, -1 on failure
+ *
+ * Get the currently used TLS version number.
+ */
+int __must_check tls_get_version(void *tls_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen);
+
+/**
+ * tls_get_cipher - Get current cipher name
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @buf: Buffer for the cipher name
+ * @buflen: buf size
+ * Returns: 0 on success, -1 on failure
+ *
+ * Get the name of the currently used cipher.
+ */
+int __must_check tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen);
+
+/**
+ * tls_connection_enable_workaround - Enable TLS workaround options
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to enable connection-specific workaround options for
+ * buffer SSL/TLS implementations.
+ */
+int __must_check tls_connection_enable_workaround(void *tls_ctx,
+ struct tls_connection *conn);
+
+/**
+ * tls_connection_client_hello_ext - Set TLS extension for ClientHello
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @ext_type: Extension type
+ * @data: Extension payload (%NULL to remove extension)
+ * @data_len: Extension payload length
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_connection_client_hello_ext(void *tls_ctx,
+ struct tls_connection *conn,
+ int ext_type, const u8 *data,
+ size_t data_len);
+
+/**
+ * tls_connection_get_failed - Get connection failure status
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ *
+ * Returns >0 if connection has failed, 0 if not.
+ */
+int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_get_read_alerts - Get connection read alert status
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: Number of times a fatal read (remote end reported error) has
+ * happened during this connection.
+ */
+int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_get_write_alerts - Get connection write alert status
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: Number of times a fatal write (locally detected error) has happened
+ * during this connection.
+ */
+int tls_connection_get_write_alerts(void *tls_ctx,
+ struct tls_connection *conn);
+
+typedef int (*tls_session_ticket_cb)
+(void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
+ const u8 *server_random, u8 *master_secret);
+
+int __must_check tls_connection_set_session_ticket_cb(
+ void *tls_ctx, struct tls_connection *conn,
+ tls_session_ticket_cb cb, void *ctx);
+
+void tls_connection_set_log_cb(struct tls_connection *conn,
+ void (*log_cb)(void *ctx, const char *msg),
+ void *ctx);
+
+#define TLS_BREAK_VERIFY_DATA BIT(0)
+#define TLS_BREAK_SRV_KEY_X_HASH BIT(1)
+#define TLS_BREAK_SRV_KEY_X_SIGNATURE BIT(2)
+#define TLS_DHE_PRIME_511B BIT(3)
+#define TLS_DHE_PRIME_767B BIT(4)
+#define TLS_DHE_PRIME_15 BIT(5)
+#define TLS_DHE_PRIME_58B BIT(6)
+#define TLS_DHE_NON_PRIME BIT(7)
+
+void tls_connection_set_test_flags(struct tls_connection *conn, u32 flags);
+
+int tls_get_library_version(char *buf, size_t buf_len);
+
+void tls_connection_set_success_data(struct tls_connection *conn,
+ struct wpabuf *data);
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn);
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn);
+
+void tls_connection_remove_session(struct tls_connection *conn);
+
+#endif /* TLS_H */
--- /dev/null
- #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)
+/*
+ * SSL/TLS interface functions for OpenSSL
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ *
+ * 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/opensslv.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"
++#include "tls_openssl.h"
+
- #ifndef OPENSSL_NO_ENGINE
++#if !defined(CONFIG_FIPS) && \
++ (defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || \
++ defined(EAP_SERVER_FAST))
++#define OPENSSL_NEED_EAP_FAST_PRF
+#endif
+
+#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 */
+
++#if (OPENSSL_VERSION_NUMBER < 0x10100000L || \
++ defined(LIBRESSL_VERSION_NUMBER)) && \
++ !defined(BORINGSSL_API_VERSION)
++/*
++ * SSL_get_client_random() and SSL_get_server_random() were added in OpenSSL
++ * 1.1.0 and newer BoringSSL revisions. Provide compatibility wrappers for
++ * older versions.
++ */
++
++static size_t SSL_get_client_random(const SSL *ssl, unsigned char *out,
++ size_t outlen)
++{
++ if (!ssl->s3 || outlen < SSL3_RANDOM_SIZE)
++ return 0;
++ os_memcpy(out, ssl->s3->client_random, SSL3_RANDOM_SIZE);
++ return SSL3_RANDOM_SIZE;
++}
++
++
++static size_t SSL_get_server_random(const SSL *ssl, unsigned char *out,
++ size_t outlen)
++{
++ if (!ssl->s3 || outlen < SSL3_RANDOM_SIZE)
++ return 0;
++ os_memcpy(out, ssl->s3->server_random, SSL3_RANDOM_SIZE);
++ return SSL3_RANDOM_SIZE;
++}
++
++
++#ifdef OPENSSL_NEED_EAP_FAST_PRF
++static size_t SSL_SESSION_get_master_key(const SSL_SESSION *session,
++ unsigned char *out, size_t outlen)
++{
++ if (!session || session->master_key_length < 0 ||
++ (size_t) session->master_key_length > outlen)
++ return 0;
++ if ((size_t) session->master_key_length < outlen)
++ outlen = session->master_key_length;
++ os_memcpy(out, session->master_key, outlen);
++ return outlen;
++}
++#endif /* OPENSSL_NEED_EAP_FAST_PRF */
++
++#endif
++
+#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;
+}
++
++
++static int tls_add_ca_from_keystore(X509_STORE *ctx, const char *key_alias)
++{
++ BIO *bio = BIO_from_keystore(key_alias);
++ 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) {
++ wpa_printf(MSG_WARNING, "TLS: Failed to parse certificate: %s",
++ key_alias);
++ 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(ctx, info->x509);
++ if (info->crl)
++ X509_STORE_add_crl(ctx, info->crl);
++ }
++
++ sk_X509_INFO_pop_free(stack, X509_INFO_free);
++
++ return 0;
++}
++
++
++static int tls_add_ca_from_keystore_encoded(X509_STORE *ctx,
++ const char *encoded_key_alias)
++{
++ int rc = -1;
++ int len = os_strlen(encoded_key_alias);
++ unsigned char *decoded_alias;
++
++ if (len & 1) {
++ wpa_printf(MSG_WARNING, "Invalid hex-encoded alias: %s",
++ encoded_key_alias);
++ return rc;
++ }
++
++ decoded_alias = os_malloc(len / 2 + 1);
++ if (decoded_alias) {
++ if (!hexstr2bin(encoded_key_alias, decoded_alias, len / 2)) {
++ decoded_alias[len / 2] = '\0';
++ rc = tls_add_ca_from_keystore(
++ ctx, (const char *) decoded_alias);
++ }
++ os_free(decoded_alias);
++ }
++
++ return rc;
++}
++
+#endif /* ANDROID */
+
+static int tls_openssl_ref_count = 0;
+static int tls_ex_idx_session = -1;
+
+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;
+};
+
+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;
- #if OPENSSL_VERSION_NUMBER >= 0x10100000L
++#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE)
+ ENGINE *engine; /* functional reference to the engine */
+ EVP_PKEY *private_key; /* the private key if using engine */
+#endif /* OPENSSL_NO_ENGINE */
+ 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;
+
- #endif
+ unsigned char client_random[SSL3_RANDOM_SIZE];
+ unsigned char server_random[SSL3_RANDOM_SIZE];
- if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) {
+
+ int (*server_cert_cb)(int ok_so_far, X509* cert, void *ca_ctx);
+ void *server_cert_ctx;
+};
+
+
+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,
+ (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))) {
+ 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);
+
- ENGINE_free(engine);
++ if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx),
++ 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 (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) {
- return 0;
+ wpa_printf(MSG_DEBUG, "ENGINE: engine '%s' is already "
+ "available", id);
-
++ /*
++ * If it was auto-loaded by ENGINE_by_id() we might still
++ * need to tell it which PKCS#11 module to use in legacy
++ * (non-p11-kit) environments. Do so now; even if it was
++ * properly initialised before, setting it again will be
++ * harmless.
++ */
++ goto found;
+ }
+ 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;
+ }
- #ifndef OPENSSL_NO_ENGINE
++ found:
+ 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
+ };
+
+ if (!pkcs11_so_path)
+ return 0;
+
+ pre_cmd[1] = pkcs11_so_path;
+ pre_cmd[3] = engine_id;
+ 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) {
+ tls_global = context = tls_context_new(conf);
+ if (context == NULL)
+ return NULL;
+#ifdef CONFIG_FIPS
+#ifdef OPENSSL_FIPS
+ if (conf && conf->fips_mode) {
+ 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;
+ } 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 */
++#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ 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 */
++#endif /* < 1.1.0 */
+ } else {
+ context = tls_context_new(conf);
+ if (context == NULL)
+ return NULL;
+ }
+ tls_openssl_ref_count++;
+
+ 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;
+ }
++ os_free(data);
+ 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)) {
+ if (tls_engine_load_dynamic_opensc(conf->opensc_engine_path) ||
+ tls_engine_load_dynamic_pkcs11(conf->pkcs11_engine_path,
+ conf->pkcs11_module_path)) {
+ tls_deinit(data);
+ return NULL;
+ }
+ }
+#endif /* OPENSSL_NO_ENGINE */
+
+ 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)
+{
+ 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 OPENSSL_VERSION_NUMBER < 0x10100000L
+// 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();
++#endif /* < 1.1.0 */
+ 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 */
+
+
++#ifdef ANDROID
++/* EVP_PKEY_from_keystore comes from system/security/keystore-engine. */
++EVP_PKEY * EVP_PKEY_from_keystore(const char *key_id);
++#endif /* ANDROID */
++
+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)
+{
++#if defined(ANDROID) && defined(OPENSSL_IS_BORINGSSL)
++#if !defined(OPENSSL_NO_ENGINE)
++#error "This code depends on OPENSSL_NO_ENGINE being defined by BoringSSL."
++#endif
++ if (!key_id)
++ return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
++ conn->engine = NULL;
++ conn->private_key = EVP_PKEY_from_keystore(key_id);
++ 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));
++ return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
++ }
++#endif /* ANDROID && OPENSSL_IS_BORINGSSL */
++
+#ifndef OPENSSL_NO_ENGINE
+ int ret = -1;
+ if (engine_id == NULL) {
+ wpa_printf(MSG_ERROR, "ENGINE: Engine ID not set");
+ return -1;
+ }
+
+ 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");
+
+#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;
+ }
+#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)
+{
- #endif /* OPENSSL_NO_ENGINE */
++#if defined(ANDROID) || !defined(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) {
++#if !defined(OPENSSL_IS_BORINGSSL)
+ ENGINE_finish(conn->engine);
++#endif /* !OPENSSL_IS_BORINGSSL */
+ conn->engine = NULL;
+ }
- wpa_printf(MSG_DEBUG, "OpenSSL: %s ver=0x%x content_type=%d",
- write_p ? "TX" : "RX", version, content_type);
++#endif /* ANDROID || !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 const char * openssl_content_type(int content_type)
++{
++ switch (content_type) {
++ case 20:
++ return "change cipher spec";
++ case 21:
++ return "alert";
++ case 22:
++ return "handshake";
++ case 23:
++ return "application data";
++ case 24:
++ return "heartbeat";
++ case 256:
++ return "TLS header info"; /* pseudo content type */
++ default:
++ return "?";
++ }
++}
++
++
++static const char * openssl_handshake_type(int content_type, const u8 *buf,
++ size_t len)
++{
++ if (content_type != 22 || !buf || len == 0)
++ return "";
++ switch (buf[0]) {
++ case 0:
++ return "hello request";
++ case 1:
++ return "client hello";
++ case 2:
++ return "server hello";
++ case 4:
++ return "new session ticket";
++ case 11:
++ return "certificate";
++ case 12:
++ return "server key exchange";
++ case 13:
++ return "certificate request";
++ case 14:
++ return "server hello done";
++ case 15:
++ return "certificate verify";
++ case 16:
++ return "client key exchange";
++ case 20:
++ return "finished";
++ case 21:
++ return "certificate url";
++ case 22:
++ return "certificate status";
++ default:
++ return "?";
++ }
++}
++
++
+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;
+
- if (conn->cert_probe || context->cert_in_cb) {
++ if (write_p == 2) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: session ver=0x%x content_type=%d",
++ version, content_type);
++ wpa_hexdump_key(MSG_MSGDUMP, "OpenSSL: Data", buf, len);
++ return;
++ }
++
++ wpa_printf(MSG_DEBUG, "OpenSSL: %s ver=0x%x content_type=%d (%s/%s)",
++ write_p ? "TX" : "RX", version, content_type,
++ openssl_content_type(content_type),
++ openssl_handshake_type(content_type, buf, len));
+ 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)
+{
+ 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);
+ 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;
+ 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++;
+ }
+
++ sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
++
+ 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");
++ sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
+ return 1;
+ }
+ }
++ sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
+
+ 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;
+
+ 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;
+ 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 (context->event_cb == NULL)
+ return;
+
+ os_memset(&ev, 0, sizeof(ev));
- if (preverify_ok && context->event_cb != NULL)
++ if (conn->cert_probe || (conn->flags & TLS_CONN_EXT_CERT_CHECK) ||
++ 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;
+
+ 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';
+ }
++ sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
+
+ 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 void debug_print_cert(X509 *cert, const char *title);
+
+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;
+ 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;
+
+ // debug_print_cert(err_cert, "\n\n***** tls_verify_cb:\n");
+
+ 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 (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;
+
+ wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb(enter) - preverify_ok=%d "
+ "err=%d (%s) ca_cert_verify=%d depth=%d buf='%s' server_cert_cb=%p server_cert_only=%d",
+ preverify_ok, err, X509_verify_cert_error_string(err),
+ conn->ca_cert_verify, depth, buf, conn->server_cert_cb, conn->server_cert_only);
+
+
+ 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) {
+ wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb: allowing cert because depth > 0 && conn->server_cert_only\n");
+ 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, "tls_verify_cb: OpenSSL: Ignore certificate validity "
+ "time mismatch");
+ preverify_ok = 1;
+ }
+
+ err_str = X509_verify_cert_error_string(err);
+
+#ifdef CONFIG_SHA256
+ if (depth == 0) {
+ if (conn->server_cert_cb) {
+ preverify_ok = conn->server_cert_cb(preverify_ok, err_cert, conn->server_cert_ctx);
+ wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb: server_cert_cb returned %d", preverify_ok);
+ }
+ if (conn->server_cert_only) {
+ /*
+ * Do not require preverify_ok so we can explicity allow otherwise
+ * invalid pinned server certificates.
+ */
+ struct wpabuf *cert;
+ cert = get_x509_cert(err_cert);
+ if (!cert) {
+ wpa_printf(MSG_DEBUG, "tls_verify_cb: 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,
+ "tls_verify_cb: 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_verify_cb: 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(exit) - 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_verify_cb: 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_verify_cb: 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_verify_cb: 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_verify_cb: 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, "tls_verify_cb: 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);
+ }
+
- 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)
++#ifdef OPENSSL_IS_BORINGSSL
++ if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) &&
++ preverify_ok) {
++ enum ocsp_result res;
++
++ res = check_ocsp_resp(conn->ssl_ctx, conn->ssl, err_cert,
++ conn->peer_issuer,
++ conn->peer_issuer_issuer);
++ if (res == OCSP_REVOKED) {
++ preverify_ok = 0;
++ openssl_tls_fail_event(conn, err_cert, err, depth, buf,
++ "certificate revoked",
++ TLS_FAIL_REVOKED);
++ if (err == X509_V_OK)
++ X509_STORE_CTX_set_error(
++ x509_ctx, X509_V_ERR_CERT_REVOKED);
++ } else if (res != OCSP_GOOD &&
++ (conn->flags & TLS_CONN_REQUIRE_OCSP)) {
++ preverify_ok = 0;
++ openssl_tls_fail_event(conn, err_cert, err, depth, buf,
++ "bad certificate status response",
++ TLS_FAIL_UNSPECIFIED);
++ }
++ }
++#endif /* OPENSSL_IS_BORINGSSL */
++
++ if (depth == 0 && preverify_ok && context->event_cb != NULL)
+ context->event_cb(context->cb_ctx,
+ TLS_CERT_CHAIN_SUCCESS, NULL);
+
+ return preverify_ok;
+}
+
+
+#ifndef OPENSSL_NO_STDIO
+static int tls_load_ca_der(struct tls_data *data, const char *ca_cert)
+{
+ SSL_CTX *ssl_ctx = data->ssl;
+ X509_LOOKUP *lookup;
+ int ret = 0;
+
+ 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 */
+
+
+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,
+ int (*server_cert_cb)(int ok_so_far, X509* cert, void *ca_ctx),
+ void *server_cert_ctx)
+{
+ SSL_CTX *ssl_ctx = data->ssl;
+ X509_STORE *store;
+
+ /*
+ * Remove previously configured trusted CA certificates before adding
+ * new ones.
+ */
+ 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;
+ conn->server_cert_cb = server_cert_cb;
+ conn->server_cert_ctx = server_cert_ctx;
+
+ if (ca_cert && os_strncmp(ca_cert, "probe://", 8) == 0) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Probe for server certificate "
+ "chain; setting conn->ca_cert_verify=0");
+ 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) {
+ 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;
+ }
+
+ 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
++ /* Single alias */
+ if (ca_cert && os_strncmp("keystore://", ca_cert, 11) == 0) {
- 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);
++ if (tls_add_ca_from_keystore(SSL_CTX_get_cert_store(ssl_ctx),
++ &ca_cert[11]) < 0)
+ return -1;
++ SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
++ return 0;
++ }
+
- sk_X509_INFO_pop_free(stack, X509_INFO_free);
++ /* Multiple aliases separated by space */
++ if (ca_cert && os_strncmp("keystores://", ca_cert, 12) == 0) {
++ char *aliases = os_strdup(&ca_cert[12]);
++ const char *delim = " ";
++ int rc = 0;
++ char *savedptr;
++ char *alias;
++
++ if (!aliases)
++ return -1;
++ alias = strtok_r(aliases, delim, &savedptr);
++ for (; alias; alias = strtok_r(NULL, delim, &savedptr)) {
++ if (tls_add_ca_from_keystore_encoded(
++ SSL_CTX_get_cert_store(ssl_ctx), alias)) {
++ wpa_printf(MSG_WARNING,
++ "OpenSSL: %s - Failed to add ca_cert %s from keystore",
++ __func__, alias);
++ rc = -1;
++ break;
+ }
+ }
- #if OPENSSL_VERSION_NUMBER >= 0x10002000L
- SSL_clear_chain_certs(ssl);
++ os_free(aliases);
++ if (rc)
++ return rc;
++
+ 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_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");
+ 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 */
+ wpa_printf(MSG_DEBUG, "OpenSSL: tls_connection_ca_cert: No ca_cert; setting conn->ca_cert_verify=0");
+ conn->ca_cert_verify = 0;
+ }
+
+ return 0;
+}
+
+
+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) {
+ 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,
+ 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,
+ 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 {
+ wpa_printf(MSG_DEBUG, "OpenSSL: tls_connection_set_verify: !verify_peer; setting conn->ca_cert_verify=0");
+ 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);
+
+ 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;
+
++#ifdef PKCS12_FUNCS
++#if OPENSSL_VERSION_NUMBER < 0x10002000L
++ /*
++ * Clear previously set extra chain certificates, if any, from PKCS#12
++ * processing in tls_parse_pkcs12() to allow OpenSSL to build a new
++ * chain properly.
++ */
++ SSL_CTX_clear_extra_chain_certs(conn->ssl_ctx);
++#endif /* OPENSSL_VERSION_NUMBER < 0x10002000L */
++#endif /* PKCS12_FUNCS */
++
+ 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;
+ }
+
+ 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;
+ }
+
+ 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_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
+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_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_use_PrivateKey(data->ssl, pkey) != 1)
+ res = -1;
+ }
+ EVP_PKEY_free(pkey);
+ }
+
+ if (certs) {
- if (SSL_add1_chain_cert(ssl, cert) != 1) {
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(LIBRESSL_VERSION_NUMBER)
++ if (ssl)
++ SSL_clear_chain_certs(ssl);
++ else
++ SSL_CTX_clear_chain_certs(data->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);
- sk_X509_free(certs);
++ if ((ssl && SSL_add1_chain_cert(ssl, cert) != 1) ||
++ (!ssl && SSL_CTX_add1_chain_cert(data->ssl,
++ cert) != 1)) {
+ tls_show_errors(MSG_DEBUG, __func__,
+ "Failed to add additional certificate");
+ res = -1;
++ X509_free(cert);
+ break;
+ }
++ X509_free(cert);
+ }
+ if (!res) {
+ /* Try to continue anyway */
+ }
- res = SSL_build_cert_chain(ssl,
- SSL_BUILD_CHAIN_FLAG_CHECK |
- SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR);
++ sk_X509_pop_free(certs, X509_free);
+#ifndef OPENSSL_IS_BORINGSSL
- #if OPENSSL_VERSION_NUMBER >= 0x10001000L
++ if (ssl)
++ res = SSL_build_cert_chain(
++ ssl,
++ SSL_BUILD_CHAIN_FLAG_CHECK |
++ SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR);
++ else
++ res = SSL_CTX_build_cert_chain(
++ data->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 */
- #endif /* OPENSSL_VERSION_NUMBER >= 0x10001000L */
+ SSL_CTX_clear_extra_chain_certs(data->ssl);
- sk_X509_free(certs);
+ 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...
+ */
+ if (SSL_CTX_add_extra_chain_cert(data->ssl, cert) != 1)
+ {
++ X509_free(cert);
+ res = -1;
+ break;
+ }
+ }
- #ifndef OPENSSL_NO_ENGINE
++ sk_X509_pop_free(certs, X509_free);
+#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
+ }
+
+ PKCS12_free(p12);
+
+ if (res < 0)
+ tls_get_errors(data);
+
+ return res;
+}
+#endif /* PKCS12_FUNCS */
+
+
+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;
+ }
+
+ 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 */
+}
+
+
+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;
+
+ 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;
+ }
+
+ 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,
+ 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 */
+}
+
+
+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;
+ 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 */
+ 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;
+ }
+ 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)
+{
- #if OPENSSL_VERSION_NUMBER < 0x10100000L
- if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL)
- return -1;
-
- os_memset(keys, 0, sizeof(*keys));
- 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 defined(ANDROID) || !defined(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 */
+}
+
+
+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)
+{
+ 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;
+ }
+
+ 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;
+ }
+
+ 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(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;
+ }
+
+ 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;
+ }
+#else /* OPENSSL_NO_STDIO */
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO",
+ __func__);
+#endif /* OPENSSL_NO_STDIO */
+
+ 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) {
+ 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);
+
+ 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;
+}
+
+
+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);
+
+ 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 */
+}
+
+
+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 */
+}
+
+
+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;
- #endif
+ 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));
- #ifndef CONFIG_FIPS
+
+ return 0;
+}
+
+
- #if OPENSSL_VERSION_NUMBER < 0x10100000L
++#ifdef OPENSSL_NEED_EAP_FAST_PRF
+static int openssl_get_keyblock_size(SSL *ssl)
+{
- #if OPENSSL_VERSION_NUMBER >= 0x00909000L
++#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+ 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;
- #else
- h = ssl->read_hash;
- #endif
+ h = EVP_MD_CTX_md(ssl->read_hash);
- #if OPENSSL_VERSION_NUMBER >= 0x10000000L
+ if (h)
+ md_size = EVP_MD_size(h);
- #endif
+ else if (ssl->s3)
+ md_size = ssl->s3->tmp.new_mac_secret_size;
- #endif /* CONFIG_FIPS */
+ 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
+}
- 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)
++#endif /* OPENSSL_NEED_EAP_FAST_PRF */
+
+
- #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);
++int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
++ const char *label, u8 *out, size_t out_len)
+{
- }
++ if (!conn ||
++ SSL_export_keying_material(conn->ssl, out, out_len, label,
++ os_strlen(label), NULL, 0, 0) != 1)
+ 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);
- }
++ return 0;
++}
+
- 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
+
- * 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.
++int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
++ u8 *out, size_t out_len)
++{
++#ifdef OPENSSL_NEED_EAP_FAST_PRF
+ 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;
+
+ /*
- 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;
- }
++ * TLS library did not support EAP-FAST 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 (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);
- }
++ 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));
+
- label, rnd, 2 * SSL3_RANDOM_SIZE,
++ os_memcpy(rnd, server_random, SSL3_RANDOM_SIZE);
++ os_memcpy(rnd + SSL3_RANDOM_SIZE, client_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,
++ "key expansion", rnd, 2 * SSL3_RANDOM_SIZE,
+ _out, skip + out_len);
+ ret = 0;
+ } else if (tls_prf_sha1_md5(master_key, master_key_len,
- if (ret == 0 && skip_keyblock)
++ "key expansion", rnd, 2 * SSL3_RANDOM_SIZE,
+ _out, skip + out_len) == 0) {
+ ret = 0;
+ }
+ os_memset(master_key, 0, sizeof(master_key));
+ os_free(rnd);
- #endif
- #endif /* CONFIG_FIPS */
- }
-
-
- int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
- const char *label, int server_random_first,
- int skip_keyblock, u8 *out, size_t out_len)
- {
- #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);
++ if (ret == 0)
+ os_memcpy(out, _out + skip, out_len);
+ bin_clear_free(tmp_out, skip);
+
+ return ret;
- #if OPENSSL_VERSION_NUMBER >= 0x10001000L
++#else /* OPENSSL_NEED_EAP_FAST_PRF */
++ wpa_printf(MSG_ERROR,
++ "OpenSSL: EAP-FAST keys cannot be exported in FIPS mode");
++ return -1;
++#endif /* OPENSSL_NEED_EAP_FAST_PRF */
+}
+
+
+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 (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 (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)
+{
- #else
- return conn ? conn->ssl->hit : 0;
- #endif
+ return conn ? SSL_cache_hit(conn->ssl) : 0;
- char buf[100], *pos, *end;
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+ u8 *ciphers)
+{
- #if OPENSSL_VERSION_NUMBER >= 0x10100000L
++ char buf[500], *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;
++ case TLS_CIPHER_RSA_DHE_AES256_SHA:
++ suite = "DHE-RSA-AES256-SHA";
++ break;
++ case TLS_CIPHER_AES256_SHA:
++ suite = "AES256-SHA";
++ break;
+ default:
+ wpa_printf(MSG_DEBUG, "TLS: Unsupported "
+ "cipher selection: %d", *c);
+ return -1;
+ }
+ ret = os_snprintf(pos, end - pos, ":%s", suite);
+ if (os_snprintf_error(end - pos, ret))
+ break;
+ pos += ret;
+
+ c++;
+ }
+
+ wpa_printf(MSG_DEBUG, "OpenSSL: cipher suites: %s", buf + 1);
+
- #ifdef OPENSSL_IS_BORINGSSL
++#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
+#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;
+
+ if (SSL_set_session_ticket_ext(conn->ssl, (void *) data,
+ data_len) != 1)
+ return -1;
+
+ 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_CERTID_free(id);
+ OCSP_BASICRESP_free(basic);
+ OCSP_RESPONSE_free(rsp);
+ return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1;
+ }
++ OCSP_CERTID_free(id);
+
+ 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 (params->flags & TLS_CONN_REQUIRE_OCSP_ALL) {
++ wpa_printf(MSG_INFO,
++ "OpenSSL: ocsp=3 not supported");
++ 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));
+ }
+
+ if (engine_id) {
+ wpa_printf(MSG_DEBUG, "SSL: Initializing TLS engine");
+ 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,
+ params->altsubject_match,
+ params->suffix_match,
+ params->domain_match))
+ return -1;
+
+ 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;
+ } else {
+ if (tls_connection_ca_cert(data, conn, params->ca_cert,
+ params->ca_cert_blob,
+ params->ca_cert_blob_len,
+ params->ca_path, params->server_cert_cb,
+ params->server_cert_ctx))
+ return -1;
+ }
+
+ 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;
+
+ 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;
+ } 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;
+ }
+
+ 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 OPENSSL_IS_BORINGSSL
++ if (params->flags & TLS_CONN_REQUEST_OCSP) {
++ SSL_enable_ocsp_stapling(conn->ssl);
++ }
++#else /* OPENSSL_IS_BORINGSSL */
+#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 */
++#endif /* OPENSSL_IS_BORINGSSL */
+
+ conn->flags = params->flags;
+
+ tls_get_errors(data);
+
+ return 0;
+}
+
+
+int tls_global_set_params(void *tls_ctx,
+ const struct tls_connection_params *params)
+{
+ 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));
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+#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 */
+
+ return 0;
+}
+
+
+#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. */
+
- #if OPENSSL_VERSION_NUMBER < 0x10100000L
++#if (defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER)
+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 || defined(LIBRESSL_VERSION_NUMBER)
+ 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;
+}
+
+
+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;
+}
+#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;
+ SSL_set_session_ticket_ext_cb(conn->ssl,
+ tls_session_ticket_ext_cb, conn);
+ } else {
+ if (SSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1)
+ return -1;
+ 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)
+{
++#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
++ return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s",
++ OPENSSL_VERSION_TEXT,
++ OpenSSL_version(OPENSSL_VERSION));
++#else
+ return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s",
+ OPENSSL_VERSION_TEXT,
+ SSLeay_version(SSLEAY_VERSION));
++#endif
+}
+
+
+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
- 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);
+/*
+ * EAP-FAST definitions (RFC 4851)
+ * 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_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,
++ size_t len);
++int eap_fast_derive_eap_msk(const u8 *simck, u8 *msk);
++int 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
- * server certificate. If this string is set, the server sertificate is
+/*
+ * EAP peer configuration data
+ * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
+ *
+ * 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
+
+#include <openssl/x509.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.
+ */
+ 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
- * Note: Since this is a substring match, this cannot be used securily
++ * server certificate. If this string is set, the server certificate 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
+ *
- * If this string is set, the server sertificate is only accepted if it
++ * Note: Since this is a substring match, this cannot be used securely
+ * 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 non-null, specifies a callback method that can be used to
- * override the validity of a peer certificate.
++ * If this string is set, the server certificate 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.
+ */
+ 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;
+
++ /**
++ * pending_ext_cert_check - External server certificate check status
++ *
++ * This field should not be set in configuration step. It is only used
++ * internally when control interface is used to request external
++ * validation of server certificate chain.
++ */
++ enum {
++ NO_CHECK = 0,
++ PENDING_CHECK,
++ EXT_CERT_CHECK_GOOD,
++ EXT_CERT_CHECK_BAD,
++ } pending_ext_cert_check;
++
+ /**
++ * server_cert_cb -- if non-null, specifies a callback method that can
++ * be used to override the validity of a peer (server/acceptor) certificate.
+ */
+ int (*server_cert_cb)(int ok_so_far, X509* cert, void *ca_ctx);
+ void *server_cert_ctx;
+};
+
+
+/**
+ * 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
+/*
+ * EAP peer state machines internal structures (RFC 4137)
+ * 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_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;
+ 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;
++ unsigned int ext_cert_check:1;
++ unsigned int waiting_ext_cert_check: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
- void eap_peer_method_free(struct eap_method *method);
+/*
+ * 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);
+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
- if (config->ocsp == 2)
+/*
+ * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions
+ * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
+ *
+ * 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;
++ if (os_strstr(txt, "tls_ext_cert_check=1"))
++ params->flags |= TLS_CONN_EXT_CERT_CHECK;
++ if (os_strstr(txt, "tls_ext_cert_check=0"))
++ params->flags &= ~TLS_CONN_EXT_CERT_CHECK;
+}
+
+
+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);
+ params->server_cert_cb = config->server_cert_cb;
+ params->server_cert_ctx = config->server_cert_ctx;
+}
+
+
+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);
+ params->server_cert_cb = config->server_cert_cb;
+ params->server_cert_ctx = config->server_cert_ctx;
+}
+
+
+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;
+ }
+
+ /*
+ * 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;
+
++ sm->ext_cert_check = !!(params->flags & TLS_CONN_EXT_CERT_CHECK);
++
+ 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;
+
+ if (config->ocsp)
+ params->flags |= TLS_CONN_REQUEST_OCSP;
- if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, 0,
- out, len)) {
++ if (config->ocsp >= 2)
+ params->flags |= TLS_CONN_REQUIRE_OCSP;
++ if (config->ocsp == 3)
++ params->flags |= TLS_CONN_REQUIRE_OCSP_ALL;
+ 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;
+ }
+
+ res = tls_connection_set_params(data->ssl_ctx, data->conn, params);
+ if (res == TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN) {
+ /*
+ * 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");
+ sm->ignore = TRUE;
+ }
+ if (res) {
+ wpa_printf(MSG_INFO, "TLS: Failed to set TLS connection "
+ "parameters");
+ 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,
+ 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)
+{
+ 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)
+{
+ u8 *out;
+
+ out = os_malloc(len);
+ if (out == NULL)
+ return NULL;
+
- if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys))
- return NULL;
-
- if (keys.client_random == NULL || keys.server_random == NULL)
++ if (tls_connection_export_key(data->ssl_ctx, data->conn, label, out,
++ len)) {
+ os_free(out);
+ return NULL;
+ }
+
+ return out;
+}
+
+
+/**
+ * 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) ||
++ 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);
+
+ 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;
+ }
+ 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
+ * @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,
+ const struct wpabuf *in_data,
+ struct wpabuf **out_data)
+{
+ const struct wpabuf *msg;
+ int need_more_input;
+ struct wpabuf *appl_data;
+
+ 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;
+ data->tls_out = tls_connection_handshake(data->ssl_ctx, data->conn,
+ msg, &appl_data);
+
+ eap_peer_tls_reset_input(data);
+
+ if (appl_data &&
+ 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;
+ }
+
+ *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
+ * @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,
+ u8 id, const struct wpabuf *in_data,
+ struct wpabuf **out_data)
+{
+ int ret = 0;
+
+ *out_data = NULL;
+
+ 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.
+ */
+ 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;
+ }
+
+ 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 "
+ "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;
+ }
+ }
+
+ 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;
+
+ 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);
+ 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)
+{
+ char version[20], name[128];
+ int len = 0, ret;
+
+ 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);
+
+ //// 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;
+
+ *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);
+ 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;
+ 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);
++ os_free(methods);
++ os_free(buf);
++ return -1;
+ } 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-2011, Jouni Malinen <j@w1.fi>
+/*
+ * EAP peer method: EAP-TTLS (RFC 5281)
- EapDecision cbDecision;
++ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ *
+ * 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"
+
+
+#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;
+
+ const struct eap_method *phase2_method;
+ void *phase2_priv;
+ int phase2_success;
+ int phase2_start;
++ EapDecision decision_succ;
+
+ 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;
++ struct wpabuf *pending_resp;
+ int chbind_req_sent; /* channel binding request was sent */
+ int done_butfor_cb; /*we turned METHOD_DONE into METHOD_MAY_CONT to receive cb*/
- } 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;
++ 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);
++ int selected_non_eap;
+ char *selected;
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->ttls_version = EAP_TTLS_VERSION;
+ selected = "EAP";
++ selected_non_eap = 0;
+ data->phase2_type = EAP_TTLS_PHASE2_EAP;
+
++ /*
++ * Either one auth= type or one or more autheap= methods can be
++ * specified.
++ */
+ if (config && config->phase2) {
++ const char *token, *last = NULL;
++
++ while ((token = cstr_token(config->phase2, " \t", &last))) {
++ if (os_strncmp(token, "auth=", 5) != 0)
++ continue;
++ token += 5;
++
++ if (last - token == 8 &&
++ os_strncmp(token, "MSCHAPV2", 8) == 0) {
++ selected = "MSCHAPV2";
++ data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2;
++ } else if (last - token == 6 &&
++ os_strncmp(token, "MSCHAP", 6) == 0) {
++ selected = "MSCHAP";
++ data->phase2_type = EAP_TTLS_PHASE2_MSCHAP;
++ } else if (last - token == 3 &&
++ os_strncmp(token, "PAP", 3) == 0) {
++ selected = "PAP";
++ data->phase2_type = EAP_TTLS_PHASE2_PAP;
++ } else if (last - token == 4 &&
++ os_strncmp(token, "CHAP", 4) == 0) {
++ selected = "CHAP";
++ data->phase2_type = EAP_TTLS_PHASE2_CHAP;
++ } else {
++ wpa_printf(MSG_ERROR,
++ "EAP-TTLS: Unsupported Phase2 type '%s'",
++ token);
++ eap_ttls_deinit(sm, data);
++ return NULL;
++ }
++
++ if (selected_non_eap) {
++ wpa_printf(MSG_ERROR,
++ "EAP-TTLS: Only one Phase2 type can be specified");
++ eap_ttls_deinit(sm, data);
++ return NULL;
++ }
++
++ selected_non_eap = 1;
++ }
++
+ if (os_strstr(config->phase2, "autheap=")) {
++ if (selected_non_eap) {
++ wpa_printf(MSG_ERROR,
++ "EAP-TTLS: Both auth= and autheap= params cannot be specified");
++ eap_ttls_deinit(sm, data);
++ return NULL;
++ }
+ selected = "EAP";
+ data->phase2_type = EAP_TTLS_PHASE2_EAP;
- int ret;
+ }
+ }
++
+ 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;
+ }
+
+ 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;
+ }
+
+ 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);
+ eap_peer_tls_ssl_deinit(sm, &data->ssl);
+ eap_ttls_free_key(data);
+ os_free(data->session_id);
+ wpabuf_free(data->pending_phase2_req);
++ wpabuf_free(data->pending_resp);
+ 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);
+ 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;
+}
+
+static int eap_ttls_v0_derive_key(struct eap_sm *sm,
+ struct eap_ttls_data *data)
+{
+ eap_ttls_free_key(data);
+ data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
+ "ttls keying material",
+ 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_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");
+ }
+
+ return 0;
+}
+
+
+#ifndef CONFIG_FIPS
+static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm,
+ struct eap_ttls_data *data, size_t len)
+{
+ 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;
+ }
+
+ 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);
+}
+
+
+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;
+ }
+
+ 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 */
+ 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;
+
+ pos += 24;
+ os_free(challenge);
+ AVP_PAD(buf, pos);
+
+ wpabuf_put(msg, pos - buf);
+ *resp = msg;
+
+ 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;
+
+ /* 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;
+
+ /* 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;
+
+ /* 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;
+}
+
+
+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");
+ 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;
+}
+
+
+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;
+
+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;
+}
+
+
+static int eap_ttls_process_handshake(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)
+{
+ int res;
+
++ if (sm->waiting_ext_cert_check && data->pending_resp) {
++ struct eap_peer_config *config = eap_get_config(sm);
++
++ if (config->pending_ext_cert_check == EXT_CERT_CHECK_GOOD) {
++ wpa_printf(MSG_DEBUG,
++ "EAP-TTLS: External certificate check succeeded - continue handshake");
++ *out_data = data->pending_resp;
++ data->pending_resp = NULL;
++ sm->waiting_ext_cert_check = 0;
++ return 0;
++ }
++
++ if (config->pending_ext_cert_check == EXT_CERT_CHECK_BAD) {
++ wpa_printf(MSG_DEBUG,
++ "EAP-TTLS: External certificate check failed - force authentication failure");
++ ret->methodState = METHOD_DONE;
++ ret->decision = DECISION_FAIL;
++ sm->waiting_ext_cert_check = 0;
++ return 0;
++ }
++
++ wpa_printf(MSG_DEBUG,
++ "EAP-TTLS: Continuing to wait external server certificate validation");
++ return 0;
++ }
++
+ res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS,
+ data->ttls_version, identifier,
+ 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 (sm->waiting_ext_cert_check) {
++ wpa_printf(MSG_DEBUG,
++ "EAP-TTLS: Waiting external server certificate validation");
++ wpabuf_free(data->pending_resp);
++ data->pending_resp = *out_data;
++ *out_data = NULL;
++ return 0;
++ }
++
+ 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;
+ 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) {
+ /*
+ * Application data included in the handshake message.
+ */
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = *out_data;
+ *out_data = NULL;
+ 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)
+{
+ 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;
++ data->decision_succ = ret->decision;
+#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 */
+ }
+ } 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;
++ data->decision_succ = ret->decision;
++ } else if (data->decision_succ != DECISION_FAIL &&
++ data->phase2_success &&
++ !data->ssl.tls_out) {
++ /*
++ * This is needed to cover the case where the final Phase 2
++ * message gets fragmented since fragmentation clears
++ * decision back to FAIL.
++ */
++ wpa_printf(MSG_DEBUG,
++ "EAP-TTLS: Restore success decision after fragmented frame sent completely");
++ ret->decision = data->decision_succ;
+ }
+}
+
+
+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) {
+ 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;
+ }
+
+ wpabuf_set(&msg, pos, left);
+
+ resp = NULL;
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+ !data->resuming) {
+ res = eap_ttls_decrypt(sm, data, ret, id, &msg, &resp);
+ } else {
+ res = eap_ttls_process_handshake(sm, data, ret, id,
+ &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;
++ wpabuf_free(data->pending_resp);
++ data->pending_resp = NULL;
++ data->decision_succ = DECISION_FAIL;
+#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;
+ 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 (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;
- ret = eap_peer_method_register(eap);
- if (ret)
- eap_peer_method_free(eap);
- return 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;
+
++ return eap_peer_method_register(eap);
+}
--- /dev/null
+/*
+ * wpa_supplicant/hostapd / common helper functions, etc.
+ * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
+ *
+ * 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_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 */
+
+#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 */
+
+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_IP
++#define ETH_P_IP 0x0800
++#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))
++#undef __bitwise
+#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 */
+
++#define SSID_MAX_LEN 32
++
++struct wpa_ssid_value {
++ u8 ssid[SSID_MAX_LEN];
++ size_t ssid_len;
++};
++
+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);
++int ssid_parse(const char *buf, struct wpa_ssid_value *ssid);
+
+#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);
++int has_ctrl_char(const u8 *data, size_t len);
++int has_newline(const char *str);
+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);
+
++int str_starts(const char *str, const char *start);
++
+
+/*
+ * 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
+/*
+ * Event loop
+ * Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file defines an event loop interface that supports processing events
+ * from registered timeouts (i.e., do something after N seconds), sockets
+ * (e.g., a new packet available for reading), and signals. eloop.c is an
+ * implementation of this interface using select() and sockets. This is
+ * suitable for most UNIX/POSIX systems. When porting to other operating
+ * systems, it may be necessary to replace that implementation with OS specific
+ * mechanisms.
+ */
+
+#ifndef ELOOP_H
+#define ELOOP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * ELOOP_ALL_CTX - eloop_cancel_timeout() magic number to match all timeouts
+ */
+#define ELOOP_ALL_CTX (void *) -1
+
+/**
+ * eloop_event_type - eloop socket event type for eloop_register_sock()
+ * @EVENT_TYPE_READ: Socket has data available for reading
+ * @EVENT_TYPE_WRITE: Socket has room for new data to be written
+ * @EVENT_TYPE_EXCEPTION: An exception has been reported
+ */
+typedef enum {
+ EVENT_TYPE_READ = 0,
+ EVENT_TYPE_WRITE,
+ EVENT_TYPE_EXCEPTION
+} eloop_event_type;
+
+/**
+ * eloop_sock_handler - eloop socket event callback type
+ * @sock: File descriptor number for the socket
+ * @eloop_ctx: Registered callback context data (eloop_data)
+ * @sock_ctx: Registered callback context data (user_data)
+ */
+typedef void (*eloop_sock_handler)(int sock, void *eloop_ctx, void *sock_ctx);
+
+/**
+ * eloop_event_handler - eloop generic event callback type
+ * @eloop_ctx: Registered callback context data (eloop_data)
+ * @sock_ctx: Registered callback context data (user_data)
+ */
+typedef void (*eloop_event_handler)(void *eloop_data, void *user_ctx);
+
+/**
+ * eloop_timeout_handler - eloop timeout event callback type
+ * @eloop_ctx: Registered callback context data (eloop_data)
+ * @sock_ctx: Registered callback context data (user_data)
+ */
+typedef void (*eloop_timeout_handler)(void *eloop_data, void *user_ctx);
+
+/**
+ * eloop_signal_handler - eloop signal event callback type
+ * @sig: Signal number
+ * @signal_ctx: Registered callback context data (user_data from
+ * eloop_register_signal(), eloop_register_signal_terminate(), or
+ * eloop_register_signal_reconfig() call)
+ */
+typedef void (*eloop_signal_handler)(int sig, void *signal_ctx);
+
+/**
+ * eloop_init() - Initialize global event loop data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function must be called before any other eloop_* function.
+ */
+int eloop_init(void);
+
+/**
+ * eloop_register_read_sock - Register handler for read events
+ * @sock: File descriptor number for the socket
+ * @handler: Callback function to be called when data is available for reading
+ * @eloop_data: Callback context data (eloop_ctx)
+ * @user_data: Callback context data (sock_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a read socket notifier for the given file descriptor. The handler
+ * function will be called whenever data is available for reading from the
+ * socket. The handler function is responsible for clearing the event after
+ * having processed it in order to avoid eloop from calling the handler again
+ * for the same event.
+ */
+int eloop_register_read_sock(int sock, eloop_sock_handler handler,
+ void *eloop_data, void *user_data);
+
+/**
+ * eloop_unregister_read_sock - Unregister handler for read events
+ * @sock: File descriptor number for the socket
+ *
+ * Unregister a read socket notifier that was previously registered with
+ * eloop_register_read_sock().
+ */
+void eloop_unregister_read_sock(int sock);
+
+/**
+ * eloop_register_sock - Register handler for socket events
+ * @sock: File descriptor number for the socket
+ * @type: Type of event to wait for
+ * @handler: Callback function to be called when the event is triggered
+ * @eloop_data: Callback context data (eloop_ctx)
+ * @user_data: Callback context data (sock_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register an event notifier for the given socket's file descriptor. The
+ * handler function will be called whenever the that event is triggered for the
+ * socket. The handler function is responsible for clearing the event after
+ * having processed it in order to avoid eloop from calling the handler again
+ * for the same event.
+ */
+int eloop_register_sock(int sock, eloop_event_type type,
+ eloop_sock_handler handler,
+ void *eloop_data, void *user_data);
+
+/**
+ * eloop_unregister_sock - Unregister handler for socket events
+ * @sock: File descriptor number for the socket
+ * @type: Type of event for which sock was registered
+ *
+ * Unregister a socket event notifier that was previously registered with
+ * eloop_register_sock().
+ */
+void eloop_unregister_sock(int sock, eloop_event_type type);
+
+/**
+ * eloop_register_event - Register handler for generic events
+ * @event: Event to wait (eloop implementation specific)
+ * @event_size: Size of event data
+ * @handler: Callback function to be called when event is triggered
+ * @eloop_data: Callback context data (eloop_data)
+ * @user_data: Callback context data (user_data)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register an event handler for the given event. This function is used to
+ * register eloop implementation specific events which are mainly targeted for
+ * operating system specific code (driver interface and l2_packet) since the
+ * portable code will not be able to use such an OS-specific call. The handler
+ * function will be called whenever the event is triggered. The handler
+ * function is responsible for clearing the event after having processed it in
+ * order to avoid eloop from calling the handler again for the same event.
+ *
+ * In case of Windows implementation (eloop_win.c), event pointer is of HANDLE
+ * type, i.e., void*. The callers are likely to have 'HANDLE h' type variable,
+ * and they would call this function with eloop_register_event(h, sizeof(h),
+ * ...).
+ */
+int eloop_register_event(void *event, size_t event_size,
+ eloop_event_handler handler,
+ void *eloop_data, void *user_data);
+
+/**
+ * eloop_unregister_event - Unregister handler for a generic event
+ * @event: Event to cancel (eloop implementation specific)
+ * @event_size: Size of event data
+ *
+ * Unregister a generic event notifier that was previously registered with
+ * eloop_register_event().
+ */
+void eloop_unregister_event(void *event, size_t event_size);
+
+/**
+ * eloop_register_timeout - Register timeout
+ * @secs: Number of seconds to the timeout
+ * @usecs: Number of microseconds to the timeout
+ * @handler: Callback function to be called when timeout occurs
+ * @eloop_data: Callback context data (eloop_ctx)
+ * @user_data: Callback context data (sock_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a timeout that will cause the handler function to be called after
+ * given time.
+ */
+int eloop_register_timeout(unsigned int secs, unsigned int usecs,
+ eloop_timeout_handler handler,
+ void *eloop_data, void *user_data);
+
+/**
+ * eloop_cancel_timeout - Cancel timeouts
+ * @handler: Matching callback function
+ * @eloop_data: Matching eloop_data or %ELOOP_ALL_CTX to match all
+ * @user_data: Matching user_data or %ELOOP_ALL_CTX to match all
+ * Returns: Number of cancelled timeouts
+ *
+ * Cancel matching <handler,eloop_data,user_data> timeouts registered with
+ * eloop_register_timeout(). ELOOP_ALL_CTX can be used as a wildcard for
+ * cancelling all timeouts regardless of eloop_data/user_data.
+ */
+int eloop_cancel_timeout(eloop_timeout_handler handler,
+ void *eloop_data, void *user_data);
+
+/**
+ * eloop_cancel_timeout_one - Cancel a single timeout
+ * @handler: Matching callback function
+ * @eloop_data: Matching eloop_data
+ * @user_data: Matching user_data
+ * @remaining: Time left on the cancelled timer
+ * Returns: Number of cancelled timeouts
+ *
+ * Cancel matching <handler,eloop_data,user_data> timeout registered with
+ * eloop_register_timeout() and return the remaining time left.
+ */
+int eloop_cancel_timeout_one(eloop_timeout_handler handler,
+ void *eloop_data, void *user_data,
+ struct os_reltime *remaining);
+
+/**
+ * eloop_is_timeout_registered - Check if a timeout is already registered
+ * @handler: Matching callback function
+ * @eloop_data: Matching eloop_data
+ * @user_data: Matching user_data
+ * Returns: 1 if the timeout is registered, 0 if the timeout is not registered
+ *
+ * Determine if a matching <handler,eloop_data,user_data> timeout is registered
+ * with eloop_register_timeout().
+ */
+int eloop_is_timeout_registered(eloop_timeout_handler handler,
+ void *eloop_data, void *user_data);
+
+/**
+ * eloop_deplete_timeout - Deplete a timeout that is already registered
+ * @req_secs: Requested number of seconds to the timeout
+ * @req_usecs: Requested number of microseconds to the timeout
+ * @handler: Matching callback function
+ * @eloop_data: Matching eloop_data
+ * @user_data: Matching user_data
+ * Returns: 1 if the timeout is depleted, 0 if no change is made, -1 if no
+ * timeout matched
+ *
+ * Find a registered matching <handler,eloop_data,user_data> timeout. If found,
+ * deplete the timeout if remaining time is more than the requested time.
+ */
+int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs,
+ eloop_timeout_handler handler, void *eloop_data,
+ void *user_data);
+
+/**
+ * eloop_replenish_timeout - Replenish a timeout that is already registered
+ * @req_secs: Requested number of seconds to the timeout
+ * @req_usecs: Requested number of microseconds to the timeout
+ * @handler: Matching callback function
+ * @eloop_data: Matching eloop_data
+ * @user_data: Matching user_data
+ * Returns: 1 if the timeout is replenished, 0 if no change is made, -1 if no
+ * timeout matched
+ *
+ * Find a registered matching <handler,eloop_data,user_data> timeout. If found,
+ * replenish the timeout if remaining time is less than the requested time.
+ */
+int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs,
+ eloop_timeout_handler handler, void *eloop_data,
+ void *user_data);
+
+/**
+ * eloop_register_signal - Register handler for signals
+ * @sig: Signal number (e.g., SIGHUP)
+ * @handler: Callback function to be called when the signal is received
+ * @user_data: Callback context data (signal_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a callback function that will be called when a signal is received.
+ * The callback function is actually called only after the system signal
+ * handler has returned. This means that the normal limits for sighandlers
+ * (i.e., only "safe functions" allowed) do not apply for the registered
+ * callback.
+ */
+int eloop_register_signal(int sig, eloop_signal_handler handler,
+ void *user_data);
+
+/**
+ * eloop_register_signal_terminate - Register handler for terminate signals
+ * @handler: Callback function to be called when the signal is received
+ * @user_data: Callback context data (signal_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a callback function that will be called when a process termination
+ * signal is received. The callback function is actually called only after the
+ * system signal handler has returned. This means that the normal limits for
+ * sighandlers (i.e., only "safe functions" allowed) do not apply for the
+ * registered callback.
+ *
+ * This function is a more portable version of eloop_register_signal() since
+ * the knowledge of exact details of the signals is hidden in eloop
+ * implementation. In case of operating systems using signal(), this function
+ * registers handlers for SIGINT and SIGTERM.
+ */
+int eloop_register_signal_terminate(eloop_signal_handler handler,
+ void *user_data);
+
+/**
+ * eloop_register_signal_reconfig - Register handler for reconfig signals
+ * @handler: Callback function to be called when the signal is received
+ * @user_data: Callback context data (signal_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a callback function that will be called when a reconfiguration /
+ * hangup signal is received. The callback function is actually called only
+ * after the system signal handler has returned. This means that the normal
+ * limits for sighandlers (i.e., only "safe functions" allowed) do not apply
+ * for the registered callback.
+ *
+ * This function is a more portable version of eloop_register_signal() since
+ * the knowledge of exact details of the signals is hidden in eloop
+ * implementation. In case of operating systems using signal(), this function
+ * registers a handler for SIGHUP.
+ */
+int eloop_register_signal_reconfig(eloop_signal_handler handler,
+ void *user_data);
+
+/**
++ * eloop_sock_requeue - Requeue sockets
++ *
++ * Requeue sockets after forking because some implementations require this,
++ * such as epoll and kqueue.
++ */
++int eloop_sock_requeue(void);
++
++/**
+ * eloop_run - Start the event loop
+ *
+ * Start the event loop and continue running as long as there are any
+ * registered event handlers. This function is run after event loop has been
+ * initialized with event_init() and one or more events have been registered.
+ */
+void eloop_run(void);
+
+/**
+ * eloop_terminate - Terminate event loop
+ *
+ * Terminate event loop even if there are registered events. This can be used
+ * to request the program to be terminated cleanly.
+ */
+void eloop_terminate(void);
+
+/**
+ * eloop_destroy - Free any resources allocated for the event loop
+ *
+ * After calling eloop_destroy(), other eloop_* functions must not be called
+ * before re-running eloop_init().
+ */
+void eloop_destroy(void);
+
+/**
+ * eloop_terminated - Check whether event loop has been terminated
+ * Returns: 1 = event loop terminate, 0 = event loop still running
+ *
+ * This function can be used to check whether eloop_terminate() has been called
+ * to request termination of the event loop. This is normally used to abort
+ * operations that may still be queued to be run when eloop_terminate() was
+ * called.
+ */
+int eloop_terminated(void);
+
+/**
+ * eloop_wait_for_read_sock - Wait for a single reader
+ * @sock: File descriptor number for the socket
+ *
+ * Do a blocking wait for a single read socket.
+ */
+void eloop_wait_for_read_sock(int sock);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ELOOP_H */
--- /dev/null
- if (strcmp(fstype, "debugfs") == 0) {
+/*
+ * wpa_supplicant/hostapd / Debug prints
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+
+#ifdef CONFIG_DEBUG_SYSLOG
+#include <syslog.h>
+
+static int wpa_debug_syslog = 0;
+#endif /* CONFIG_DEBUG_SYSLOG */
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+
+static FILE *wpa_debug_tracing_file = NULL;
+
+#define WPAS_TRACE_PFX "wpas <%d>: "
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
+
+int wpa_debug_level = MSG_INFO;
+int wpa_debug_show_keys = 0;
+int wpa_debug_timestamp = 0;
+
+
+#ifdef CONFIG_ANDROID_LOG
+
+#include <android/log.h>
+
+#ifndef ANDROID_LOG_NAME
+#define ANDROID_LOG_NAME "wpa_supplicant"
+#endif /* ANDROID_LOG_NAME */
+
+static int wpa_to_android_level(int level)
+{
+ if (level == MSG_ERROR)
+ return ANDROID_LOG_ERROR;
+ if (level == MSG_WARNING)
+ return ANDROID_LOG_WARN;
+ if (level == MSG_INFO)
+ return ANDROID_LOG_INFO;
+ return ANDROID_LOG_DEBUG;
+}
+
+#endif /* CONFIG_ANDROID_LOG */
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+
+#ifdef CONFIG_DEBUG_FILE
+static FILE *out_file = NULL;
+#endif /* CONFIG_DEBUG_FILE */
+
+
+void wpa_debug_print_timestamp(void)
+{
+#ifndef CONFIG_ANDROID_LOG
+ struct os_time tv;
+
+ if (!wpa_debug_timestamp)
+ return;
+
+ os_get_time(&tv);
+#ifdef CONFIG_DEBUG_FILE
+ if (out_file) {
+ fprintf(out_file, "%ld.%06u: ", (long) tv.sec,
+ (unsigned int) tv.usec);
+ } else
+#endif /* CONFIG_DEBUG_FILE */
+ printf("%ld.%06u: ", (long) tv.sec, (unsigned int) tv.usec);
+#endif /* CONFIG_ANDROID_LOG */
+}
+
+
+#ifdef CONFIG_DEBUG_SYSLOG
+#ifndef LOG_HOSTAPD
+#define LOG_HOSTAPD LOG_DAEMON
+#endif /* LOG_HOSTAPD */
+
+void wpa_debug_open_syslog(void)
+{
+ openlog("wpa_supplicant", LOG_PID | LOG_NDELAY, LOG_HOSTAPD);
+ wpa_debug_syslog++;
+}
+
+
+void wpa_debug_close_syslog(void)
+{
+ if (wpa_debug_syslog)
+ closelog();
+}
+
+
+static int syslog_priority(int level)
+{
+ switch (level) {
+ case MSG_MSGDUMP:
+ case MSG_DEBUG:
+ return LOG_DEBUG;
+ case MSG_INFO:
+ return LOG_NOTICE;
+ case MSG_WARNING:
+ return LOG_WARNING;
+ case MSG_ERROR:
+ return LOG_ERR;
+ }
+ return LOG_INFO;
+}
+#endif /* CONFIG_DEBUG_SYSLOG */
+
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+
+int wpa_debug_open_linux_tracing(void)
+{
+ int mounts, trace_fd;
+ char buf[4096] = {};
+ ssize_t buflen;
+ char *line, *tmp1, *path = NULL;
+
+ mounts = open("/proc/mounts", O_RDONLY);
+ if (mounts < 0) {
+ printf("no /proc/mounts\n");
+ return -1;
+ }
+
+ buflen = read(mounts, buf, sizeof(buf) - 1);
+ close(mounts);
+ if (buflen < 0) {
+ printf("failed to read /proc/mounts\n");
+ return -1;
+ }
+
+ line = strtok_r(buf, "\n", &tmp1);
+ while (line) {
+ char *tmp2, *tmp_path, *fstype;
+ /* "<dev> <mountpoint> <fs type> ..." */
+ strtok_r(line, " ", &tmp2);
+ tmp_path = strtok_r(NULL, " ", &tmp2);
+ fstype = strtok_r(NULL, " ", &tmp2);
- if (last_path) {
- char *tmp = os_strdup(last_path);
- wpa_debug_close_file();
- rv = wpa_debug_open_file(tmp);
- os_free(tmp);
- } else {
- wpa_printf(MSG_ERROR, "Last-path was not set, cannot "
- "re-open log file.");
- rv = -1;
- }
++ if (fstype && strcmp(fstype, "debugfs") == 0) {
+ path = tmp_path;
+ break;
+ }
+
+ line = strtok_r(NULL, "\n", &tmp1);
+ }
+
+ if (path == NULL) {
+ printf("debugfs mountpoint not found\n");
+ return -1;
+ }
+
+ snprintf(buf, sizeof(buf) - 1, "%s/tracing/trace_marker", path);
+
+ trace_fd = open(buf, O_WRONLY);
+ if (trace_fd < 0) {
+ printf("failed to open trace_marker file\n");
+ return -1;
+ }
+ wpa_debug_tracing_file = fdopen(trace_fd, "w");
+ if (wpa_debug_tracing_file == NULL) {
+ close(trace_fd);
+ printf("failed to fdopen()\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void wpa_debug_close_linux_tracing(void)
+{
+ if (wpa_debug_tracing_file == NULL)
+ return;
+ fclose(wpa_debug_tracing_file);
+ wpa_debug_tracing_file = NULL;
+}
+
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
+
+/**
+ * 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, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (level >= wpa_debug_level) {
+#ifdef CONFIG_ANDROID_LOG
+ __android_log_vprint(wpa_to_android_level(level),
+ ANDROID_LOG_NAME, fmt, ap);
+#else /* CONFIG_ANDROID_LOG */
+#ifdef CONFIG_DEBUG_SYSLOG
+ if (wpa_debug_syslog) {
+ vsyslog(syslog_priority(level), fmt, ap);
+ } else {
+#endif /* CONFIG_DEBUG_SYSLOG */
+ wpa_debug_print_timestamp();
+#ifdef CONFIG_DEBUG_FILE
+ if (out_file) {
+ vfprintf(out_file, fmt, ap);
+ fprintf(out_file, "\n");
+ } else {
+#endif /* CONFIG_DEBUG_FILE */
+ vprintf(fmt, ap);
+ printf("\n");
+#ifdef CONFIG_DEBUG_FILE
+ }
+#endif /* CONFIG_DEBUG_FILE */
+#ifdef CONFIG_DEBUG_SYSLOG
+ }
+#endif /* CONFIG_DEBUG_SYSLOG */
+#endif /* CONFIG_ANDROID_LOG */
+ }
+ va_end(ap);
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+ if (wpa_debug_tracing_file != NULL) {
+ va_start(ap, fmt);
+ fprintf(wpa_debug_tracing_file, WPAS_TRACE_PFX, level);
+ vfprintf(wpa_debug_tracing_file, fmt, ap);
+ fprintf(wpa_debug_tracing_file, "\n");
+ fflush(wpa_debug_tracing_file);
+ va_end(ap);
+ }
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+}
+
+
+static void _wpa_hexdump(int level, const char *title, const u8 *buf,
+ size_t len, int show)
+{
+ size_t i;
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+ if (wpa_debug_tracing_file != NULL) {
+ fprintf(wpa_debug_tracing_file,
+ WPAS_TRACE_PFX "%s - hexdump(len=%lu):",
+ level, title, (unsigned long) len);
+ if (buf == NULL) {
+ fprintf(wpa_debug_tracing_file, " [NULL]\n");
+ } else if (!show) {
+ fprintf(wpa_debug_tracing_file, " [REMOVED]\n");
+ } else {
+ for (i = 0; i < len; i++)
+ fprintf(wpa_debug_tracing_file,
+ " %02x", buf[i]);
+ }
+ fflush(wpa_debug_tracing_file);
+ }
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
+ if (level < wpa_debug_level)
+ return;
+#ifdef CONFIG_ANDROID_LOG
+ {
+ const char *display;
+ char *strbuf = NULL;
+ size_t slen = len;
+ if (buf == NULL) {
+ display = " [NULL]";
+ } else if (len == 0) {
+ display = "";
+ } else if (show && len) {
+ /* Limit debug message length for Android log */
+ if (slen > 32)
+ slen = 32;
+ strbuf = os_malloc(1 + 3 * slen);
+ if (strbuf == NULL) {
+ wpa_printf(MSG_ERROR, "wpa_hexdump: Failed to "
+ "allocate message buffer");
+ return;
+ }
+
+ for (i = 0; i < slen; i++)
+ os_snprintf(&strbuf[i * 3], 4, " %02x",
+ buf[i]);
+
+ display = strbuf;
+ } else {
+ display = " [REMOVED]";
+ }
+
+ __android_log_print(wpa_to_android_level(level),
+ ANDROID_LOG_NAME,
+ "%s - hexdump(len=%lu):%s%s",
+ title, (long unsigned int) len, display,
+ len > slen ? " ..." : "");
+ bin_clear_free(strbuf, 1 + 3 * slen);
+ return;
+ }
+#else /* CONFIG_ANDROID_LOG */
+#ifdef CONFIG_DEBUG_SYSLOG
+ if (wpa_debug_syslog) {
+ const char *display;
+ char *strbuf = NULL;
+
+ if (buf == NULL) {
+ display = " [NULL]";
+ } else if (len == 0) {
+ display = "";
+ } else if (show && len) {
+ strbuf = os_malloc(1 + 3 * len);
+ if (strbuf == NULL) {
+ wpa_printf(MSG_ERROR, "wpa_hexdump: Failed to "
+ "allocate message buffer");
+ return;
+ }
+
+ for (i = 0; i < len; i++)
+ os_snprintf(&strbuf[i * 3], 4, " %02x",
+ buf[i]);
+
+ display = strbuf;
+ } else {
+ display = " [REMOVED]";
+ }
+
+ syslog(syslog_priority(level), "%s - hexdump(len=%lu):%s",
+ title, (unsigned long) len, display);
+ bin_clear_free(strbuf, 1 + 3 * len);
+ return;
+ }
+#endif /* CONFIG_DEBUG_SYSLOG */
+ wpa_debug_print_timestamp();
+#ifdef CONFIG_DEBUG_FILE
+ if (out_file) {
+ fprintf(out_file, "%s - hexdump(len=%lu):",
+ title, (unsigned long) len);
+ if (buf == NULL) {
+ fprintf(out_file, " [NULL]");
+ } else if (show) {
+ for (i = 0; i < len; i++)
+ fprintf(out_file, " %02x", buf[i]);
+ } else {
+ fprintf(out_file, " [REMOVED]");
+ }
+ fprintf(out_file, "\n");
+ } else {
+#endif /* CONFIG_DEBUG_FILE */
+ printf("%s - hexdump(len=%lu):", title, (unsigned long) len);
+ if (buf == NULL) {
+ printf(" [NULL]");
+ } else if (show) {
+ for (i = 0; i < len; i++)
+ printf(" %02x", buf[i]);
+ } else {
+ printf(" [REMOVED]");
+ }
+ printf("\n");
+#ifdef CONFIG_DEBUG_FILE
+ }
+#endif /* CONFIG_DEBUG_FILE */
+#endif /* CONFIG_ANDROID_LOG */
+}
+
+void wpa_hexdump(int level, const char *title, const void *buf, size_t len)
+{
+ _wpa_hexdump(level, title, buf, len, 1);
+}
+
+
+void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len)
+{
+ _wpa_hexdump(level, title, buf, len, wpa_debug_show_keys);
+}
+
+
+static void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
+ size_t len, int show)
+{
+ size_t i, llen;
+ const u8 *pos = buf;
+ const size_t line_len = 16;
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+ if (wpa_debug_tracing_file != NULL) {
+ fprintf(wpa_debug_tracing_file,
+ WPAS_TRACE_PFX "%s - hexdump_ascii(len=%lu):",
+ level, title, (unsigned long) len);
+ if (buf == NULL) {
+ fprintf(wpa_debug_tracing_file, " [NULL]\n");
+ } else if (!show) {
+ fprintf(wpa_debug_tracing_file, " [REMOVED]\n");
+ } else {
+ /* can do ascii processing in userspace */
+ for (i = 0; i < len; i++)
+ fprintf(wpa_debug_tracing_file,
+ " %02x", pos[i]);
+ }
+ fflush(wpa_debug_tracing_file);
+ }
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
+ if (level < wpa_debug_level)
+ return;
+#ifdef CONFIG_ANDROID_LOG
+ _wpa_hexdump(level, title, buf, len, show);
+#else /* CONFIG_ANDROID_LOG */
+ wpa_debug_print_timestamp();
+#ifdef CONFIG_DEBUG_FILE
+ if (out_file) {
+ if (!show) {
+ fprintf(out_file,
+ "%s - hexdump_ascii(len=%lu): [REMOVED]\n",
+ title, (unsigned long) len);
+ return;
+ }
+ if (buf == NULL) {
+ fprintf(out_file,
+ "%s - hexdump_ascii(len=%lu): [NULL]\n",
+ title, (unsigned long) len);
+ return;
+ }
+ fprintf(out_file, "%s - hexdump_ascii(len=%lu):\n",
+ title, (unsigned long) len);
+ while (len) {
+ llen = len > line_len ? line_len : len;
+ fprintf(out_file, " ");
+ for (i = 0; i < llen; i++)
+ fprintf(out_file, " %02x", pos[i]);
+ for (i = llen; i < line_len; i++)
+ fprintf(out_file, " ");
+ fprintf(out_file, " ");
+ for (i = 0; i < llen; i++) {
+ if (isprint(pos[i]))
+ fprintf(out_file, "%c", pos[i]);
+ else
+ fprintf(out_file, "_");
+ }
+ for (i = llen; i < line_len; i++)
+ fprintf(out_file, " ");
+ fprintf(out_file, "\n");
+ pos += llen;
+ len -= llen;
+ }
+ } else {
+#endif /* CONFIG_DEBUG_FILE */
+ if (!show) {
+ printf("%s - hexdump_ascii(len=%lu): [REMOVED]\n",
+ title, (unsigned long) len);
+ return;
+ }
+ if (buf == NULL) {
+ printf("%s - hexdump_ascii(len=%lu): [NULL]\n",
+ title, (unsigned long) len);
+ return;
+ }
+ printf("%s - hexdump_ascii(len=%lu):\n", title, (unsigned long) len);
+ while (len) {
+ llen = len > line_len ? line_len : len;
+ printf(" ");
+ for (i = 0; i < llen; i++)
+ printf(" %02x", pos[i]);
+ for (i = llen; i < line_len; i++)
+ printf(" ");
+ printf(" ");
+ for (i = 0; i < llen; i++) {
+ if (isprint(pos[i]))
+ printf("%c", pos[i]);
+ else
+ printf("_");
+ }
+ for (i = llen; i < line_len; i++)
+ printf(" ");
+ printf("\n");
+ pos += llen;
+ len -= llen;
+ }
+#ifdef CONFIG_DEBUG_FILE
+ }
+#endif /* CONFIG_DEBUG_FILE */
+#endif /* CONFIG_ANDROID_LOG */
+}
+
+
+void wpa_hexdump_ascii(int level, const char *title, const void *buf,
+ size_t len)
+{
+ _wpa_hexdump_ascii(level, title, buf, len, 1);
+}
+
+
+void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
+ size_t len)
+{
+ _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys);
+}
+
+
+#ifdef CONFIG_DEBUG_FILE
+static char *last_path = NULL;
+#endif /* CONFIG_DEBUG_FILE */
+
+int wpa_debug_reopen_file(void)
+{
+#ifdef CONFIG_DEBUG_FILE
+ int rv;
++ char *tmp;
++
++ if (!last_path)
++ return 0; /* logfile not used */
++
++ tmp = os_strdup(last_path);
++ if (!tmp)
++ return -1;
++
++ wpa_debug_close_file();
++ rv = wpa_debug_open_file(tmp);
++ os_free(tmp);
+ return rv;
+#else /* CONFIG_DEBUG_FILE */
+ return 0;
+#endif /* CONFIG_DEBUG_FILE */
+}
+
+
+int wpa_debug_open_file(const char *path)
+{
+#ifdef CONFIG_DEBUG_FILE
+ if (!path)
+ return 0;
+
+ if (last_path == NULL || os_strcmp(last_path, path) != 0) {
+ /* Save our path to enable re-open */
+ os_free(last_path);
+ last_path = os_strdup(path);
+ }
+
+ out_file = fopen(path, "a");
+ if (out_file == NULL) {
+ wpa_printf(MSG_ERROR, "wpa_debug_open_file: Failed to open "
+ "output file %s, using standard output", path);
+ return -1;
+ }
+#ifndef _WIN32
+ setvbuf(out_file, NULL, _IOLBF, 0);
+#endif /* _WIN32 */
+#else /* CONFIG_DEBUG_FILE */
+ (void)path;
+#endif /* CONFIG_DEBUG_FILE */
+ return 0;
+}
+
+
+void wpa_debug_close_file(void)
+{
+#ifdef CONFIG_DEBUG_FILE
+ if (!out_file)
+ return;
+ fclose(out_file);
+ out_file = NULL;
+ os_free(last_path);
+ last_path = NULL;
+#endif /* CONFIG_DEBUG_FILE */
+}
+
+
+void wpa_debug_setup_stdout(void)
+{
+#ifndef _WIN32
+ setvbuf(stdout, NULL, _IOLBF, 0);
+#endif /* _WIN32 */
+}
+
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+#ifndef CONFIG_NO_WPA_MSG
+static wpa_msg_cb_func wpa_msg_cb = NULL;
+
+void wpa_msg_register_cb(wpa_msg_cb_func func)
+{
+ wpa_msg_cb = func;
+}
+
+
+static wpa_msg_get_ifname_func wpa_msg_ifname_cb = NULL;
+
+void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func)
+{
+ wpa_msg_ifname_cb = func;
+}
+
+
+void wpa_msg(void *ctx, int level, const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+ int buflen;
+ int len;
+ char prefix[130];
+
+ va_start(ap, fmt);
+ buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+ va_end(ap);
+
+ buf = os_malloc(buflen);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR, "wpa_msg: Failed to allocate message "
+ "buffer");
+ return;
+ }
+ va_start(ap, fmt);
+ prefix[0] = '\0';
+ if (wpa_msg_ifname_cb) {
+ const char *ifname = wpa_msg_ifname_cb(ctx);
+ if (ifname) {
+ int res = os_snprintf(prefix, sizeof(prefix), "%s: ",
+ ifname);
+ if (os_snprintf_error(sizeof(prefix), res))
+ prefix[0] = '\0';
+ }
+ }
+ len = vsnprintf(buf, buflen, fmt, ap);
+ va_end(ap);
+ wpa_printf(level, "%s%s", prefix, buf);
+ if (wpa_msg_cb)
+ wpa_msg_cb(ctx, level, WPA_MSG_PER_INTERFACE, buf, len);
+ bin_clear_free(buf, buflen);
+}
+
+
+void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+ int buflen;
+ int len;
+
+ if (!wpa_msg_cb)
+ return;
+
+ va_start(ap, fmt);
+ buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+ va_end(ap);
+
+ buf = os_malloc(buflen);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR, "wpa_msg_ctrl: Failed to allocate "
+ "message buffer");
+ return;
+ }
+ va_start(ap, fmt);
+ len = vsnprintf(buf, buflen, fmt, ap);
+ va_end(ap);
+ wpa_msg_cb(ctx, level, WPA_MSG_PER_INTERFACE, buf, len);
+ bin_clear_free(buf, buflen);
+}
+
+
+void wpa_msg_global(void *ctx, int level, const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+ int buflen;
+ int len;
+
+ va_start(ap, fmt);
+ buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+ va_end(ap);
+
+ buf = os_malloc(buflen);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR, "wpa_msg_global: Failed to allocate "
+ "message buffer");
+ return;
+ }
+ va_start(ap, fmt);
+ len = vsnprintf(buf, buflen, fmt, ap);
+ va_end(ap);
+ wpa_printf(level, "%s", buf);
+ if (wpa_msg_cb)
+ wpa_msg_cb(ctx, level, WPA_MSG_GLOBAL, buf, len);
+ bin_clear_free(buf, buflen);
+}
+
+
+void wpa_msg_global_ctrl(void *ctx, int level, const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+ int buflen;
+ int len;
+
+ if (!wpa_msg_cb)
+ return;
+
+ va_start(ap, fmt);
+ buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+ va_end(ap);
+
+ buf = os_malloc(buflen);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR,
+ "wpa_msg_global_ctrl: Failed to allocate message buffer");
+ return;
+ }
+ va_start(ap, fmt);
+ len = vsnprintf(buf, buflen, fmt, ap);
+ va_end(ap);
+ wpa_msg_cb(ctx, level, WPA_MSG_GLOBAL, buf, len);
+ bin_clear_free(buf, buflen);
+}
+
+
+void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+ int buflen;
+ int len;
+
+ va_start(ap, fmt);
+ buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+ va_end(ap);
+
+ buf = os_malloc(buflen);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR, "wpa_msg_no_global: Failed to allocate "
+ "message buffer");
+ return;
+ }
+ va_start(ap, fmt);
+ len = vsnprintf(buf, buflen, fmt, ap);
+ va_end(ap);
+ wpa_printf(level, "%s", buf);
+ if (wpa_msg_cb)
+ wpa_msg_cb(ctx, level, WPA_MSG_NO_GLOBAL, buf, len);
+ bin_clear_free(buf, buflen);
+}
+
+
+void wpa_msg_global_only(void *ctx, int level, const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+ int buflen;
+ int len;
+
+ va_start(ap, fmt);
+ buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+ va_end(ap);
+
+ buf = os_malloc(buflen);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR, "%s: Failed to allocate message buffer",
+ __func__);
+ return;
+ }
+ va_start(ap, fmt);
+ len = vsnprintf(buf, buflen, fmt, ap);
+ va_end(ap);
+ wpa_printf(level, "%s", buf);
+ if (wpa_msg_cb)
+ wpa_msg_cb(ctx, level, WPA_MSG_ONLY_GLOBAL, buf, len);
+ os_free(buf);
+}
+
+#endif /* CONFIG_NO_WPA_MSG */
+
+
+#ifndef CONFIG_NO_HOSTAPD_LOGGER
+static hostapd_logger_cb_func hostapd_logger_cb = NULL;
+
+void hostapd_logger_register_cb(hostapd_logger_cb_func func)
+{
+ hostapd_logger_cb = func;
+}
+
+
+void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level,
+ const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+ int buflen;
+ int len;
+
+ va_start(ap, fmt);
+ buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+ va_end(ap);
+
+ buf = os_malloc(buflen);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR, "hostapd_logger: Failed to allocate "
+ "message buffer");
+ return;
+ }
+ va_start(ap, fmt);
+ len = vsnprintf(buf, buflen, fmt, ap);
+ va_end(ap);
+ if (hostapd_logger_cb)
+ hostapd_logger_cb(ctx, addr, module, level, buf, len);
+ else if (addr)
+ wpa_printf(MSG_DEBUG, "hostapd_logger: STA " MACSTR " - %s",
+ MAC2STR(addr), buf);
+ else
+ wpa_printf(MSG_DEBUG, "hostapd_logger: %s", buf);
+ bin_clear_free(buf, buflen);
+}
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+
+
+const char * debug_level_str(int level)
+{
+ switch (level) {
+ case MSG_EXCESSIVE:
+ return "EXCESSIVE";
+ case MSG_MSGDUMP:
+ return "MSGDUMP";
+ case MSG_DEBUG:
+ return "DEBUG";
+ case MSG_INFO:
+ return "INFO";
+ case MSG_WARNING:
+ return "WARNING";
+ case MSG_ERROR:
+ return "ERROR";
+ default:
+ return "?";
+ }
+}
+
+
+int str_to_debug_level(const char *s)
+{
+ if (os_strcasecmp(s, "EXCESSIVE") == 0)
+ return MSG_EXCESSIVE;
+ if (os_strcasecmp(s, "MSGDUMP") == 0)
+ return MSG_MSGDUMP;
+ if (os_strcasecmp(s, "DEBUG") == 0)
+ return MSG_DEBUG;
+ if (os_strcasecmp(s, "INFO") == 0)
+ return MSG_INFO;
+ if (os_strcasecmp(s, "WARNING") == 0)
+ return MSG_WARNING;
+ if (os_strcasecmp(s, "ERROR") == 0)
+ return MSG_ERROR;
+ return -1;
+}
--- /dev/null
- return (const u8 *)wpabuf_head(buf);
+/*
+ * Dynamic data buffer
+ * Copyright (c) 2007-2012, Jouni Malinen <j@w1.fi>
+ *
+ * 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 */
+ 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);
++struct wpabuf * wpabuf_parse_bin(const char *buf);
+
+
+/**
+ * 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)
+{
+ return buf->buf;
+}
+
+static inline const u8 * wpabuf_head_u8(const struct wpabuf *buf)
+{
- return (u8 *)wpabuf_mhead(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)
+{
+ return buf->buf;
+}
+
+static inline u8 * wpabuf_mhead_u8(struct wpabuf *buf)
+{
- u8 *pos = (u8 *)wpabuf_put(buf, 1);
++ return (u8 *) wpabuf_mhead(buf);
+}
+
+static inline void wpabuf_put_u8(struct wpabuf *buf, u8 data)
+{
- u8 *pos = (u8 *)wpabuf_put(buf, 2);
++ 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, 4);
++ 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, 2);
++ 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, 3);
++ 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, 4);
++ 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 */