From a8f56d7734c043da844e474db1803daa92a0c882 Mon Sep 17 00:00:00 2001
From: Gary Ching-Pang Lin <glin@suse.com>
Date: Thu, 20 Oct 2011 15:03:46 +0800
Subject: [PATCH] Add TLS client events, server probing, and srv cert matching

Based on upstream patches:

0a5d68aba50c385e316a30d834d5b6174a4041d2
Make hash functions return error value

00468b4650998144f794762206c695c962c54734
Add TLS client events, server probing, and srv cert matching
---
 src/common/wpa_ctrl.h              |    4 +
 src/crypto/crypto.h                |   18 ++-
 src/crypto/crypto_cryptoapi.c      |   12 +-
 src/crypto/crypto_gnutls.c         |   15 ++-
 src/crypto/crypto_libtomcrypt.c    |    9 +-
 src/crypto/crypto_none.c           |    3 +-
 src/crypto/crypto_openssl.c        |   38 ++++--
 src/crypto/md4.c                   |    3 +-
 src/crypto/md5.c                   |   20 ++-
 src/crypto/md5.h                   |   10 +-
 src/crypto/sha1.c                  |   78 +++++++----
 src/crypto/sha1.h                  |   20 ++--
 src/crypto/sha256.c                |   11 +-
 src/crypto/tls.h                   |   43 ++++++
 src/crypto/tls_openssl.c           |  278 +++++++++++++++++++++++++++++++++---
 src/eap_peer/eap.c                 |   52 +++++++-
 src/eap_peer/eap_config.h          |    9 ++
 wpa_supplicant/Makefile            |    1 +
 wpa_supplicant/wpa_supplicant.conf |   10 ++
 19 files changed, 521 insertions(+), 113 deletions(-)

diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 1bfc0d6..7abeaf8 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -42,6 +42,10 @@ extern "C" {
 #define WPA_EVENT_EAP_STARTED "CTRL-EVENT-EAP-STARTED "
 /** EAP method selected */
 #define WPA_EVENT_EAP_METHOD "CTRL-EVENT-EAP-METHOD "
+/** EAP peer certificate from TLS */
+#define WPA_EVENT_EAP_PEER_CERT "CTRL-EVENT-EAP-PEER-CERT "
+/** EAP TLS certificate chain validation error */
+#define WPA_EVENT_EAP_TLS_CERT_ERROR "CTRL-EVENT-EAP-TLS-CERT-ERROR "
 /** EAP authentication completed successfully */
 #define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS "
 /** EAP authentication failed (EAP-Failure received) */
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index a5129bb..e2d2112 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / wrapper functions for crypto libraries
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -33,8 +33,9 @@
  * @addr: Pointers to the data areas
  * @len: Lengths of the data blocks
  * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
  */
-void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
 
 /**
  * md5_vector - MD5 hash for data vector
@@ -42,8 +43,9 @@ void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
  * @addr: Pointers to the data areas
  * @len: Lengths of the data blocks
  * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
  */
-void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
 
 /**
  * sha1_vector - SHA-1 hash for data vector
@@ -51,9 +53,10 @@ void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
  * @addr: Pointers to the data areas
  * @len: Lengths of the data blocks
  * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
  */
-void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len,
-		 u8 *mac);
+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
@@ -76,9 +79,10 @@ int __must_check fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x,
  * @addr: Pointers to the data areas
  * @len: Lengths of the data blocks
  * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
  */
-void sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
-		   u8 *mac);
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac);
 
 /**
  * des_encrypt - Encrypt one block with DES
diff --git a/src/crypto/crypto_cryptoapi.c b/src/crypto/crypto_cryptoapi.c
index bb05730..2746b1d 100644
--- a/src/crypto/crypto_cryptoapi.c
+++ b/src/crypto/crypto_cryptoapi.c
@@ -167,9 +167,9 @@ int cryptoapi_hash_vector(ALG_ID alg, size_t hash_len, size_t num_elem,
 }
 
 
-void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 {
-	cryptoapi_hash_vector(CALG_MD4, 16, num_elem, addr, len, mac);
+	return cryptoapi_hash_vector(CALG_MD4, 16, num_elem, addr, len, mac);
 }
 
 
@@ -239,15 +239,15 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
 
 
 #ifdef EAP_TLS_FUNCS
-void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 {
-	cryptoapi_hash_vector(CALG_MD5, 16, num_elem, addr, len, mac);
+	return cryptoapi_hash_vector(CALG_MD5, 16, num_elem, addr, len, mac);
 }
 
 
-void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 {
-	cryptoapi_hash_vector(CALG_SHA, 20, num_elem, addr, len, mac);
+	return cryptoapi_hash_vector(CALG_SHA, 20, num_elem, addr, len, mac);
 }
 
 
diff --git a/src/crypto/crypto_gnutls.c b/src/crypto/crypto_gnutls.c
index 8f8611c..5ec803b 100644
--- a/src/crypto/crypto_gnutls.c
+++ b/src/crypto/crypto_gnutls.c
@@ -18,20 +18,21 @@
 #include "common.h"
 #include "crypto.h"
 
-void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 {
 	gcry_md_hd_t hd;
 	unsigned char *p;
 	size_t i;
 
 	if (gcry_md_open(&hd, GCRY_MD_MD4, 0) != GPG_ERR_NO_ERROR)
-		return;
+		return -1;
 	for (i = 0; i < num_elem; i++)
 		gcry_md_write(hd, addr[i], len[i]);
 	p = gcry_md_read(hd, GCRY_MD_MD4);
 	if (p)
 		memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD4));
 	gcry_md_close(hd);
+	return 0;
 }
 
 
@@ -57,37 +58,39 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
 }
 
 
-void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 {
 	gcry_md_hd_t hd;
 	unsigned char *p;
 	size_t i;
 
 	if (gcry_md_open(&hd, GCRY_MD_MD5, 0) != GPG_ERR_NO_ERROR)
-		return;
+		return -1;
 	for (i = 0; i < num_elem; i++)
 		gcry_md_write(hd, addr[i], len[i]);
 	p = gcry_md_read(hd, GCRY_MD_MD5);
 	if (p)
 		memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD5));
 	gcry_md_close(hd);
+	return 0;
 }
 
 
-void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 {
 	gcry_md_hd_t hd;
 	unsigned char *p;
 	size_t i;
 
 	if (gcry_md_open(&hd, GCRY_MD_SHA1, 0) != GPG_ERR_NO_ERROR)
-		return;
+		return -1;
 	for (i = 0; i < num_elem; i++)
 		gcry_md_write(hd, addr[i], len[i]);
 	p = gcry_md_read(hd, GCRY_MD_SHA1);
 	if (p)
 		memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_SHA1));
 	gcry_md_close(hd);
+	return 0;
 }
 
 
diff --git a/src/crypto/crypto_libtomcrypt.c b/src/crypto/crypto_libtomcrypt.c
index e82097f..77a54e3 100644
--- a/src/crypto/crypto_libtomcrypt.c
+++ b/src/crypto/crypto_libtomcrypt.c
@@ -29,7 +29,7 @@
 #endif
 
 
-void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 {
 	hash_state md;
 	size_t i;
@@ -38,6 +38,7 @@ void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 	for (i = 0; i < num_elem; i++)
 		md4_process(&md, addr[i], len[i]);
 	md4_done(&md, mac);
+	return 0;
 }
 
 
@@ -63,7 +64,7 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
 
 
 #ifdef EAP_TLS_FUNCS
-void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 {
 	hash_state md;
 	size_t i;
@@ -72,10 +73,11 @@ void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 	for (i = 0; i < num_elem; i++)
 		md5_process(&md, addr[i], len[i]);
 	md5_done(&md, mac);
+	return 0;
 }
 
 
-void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 {
 	hash_state md;
 	size_t i;
@@ -84,6 +86,7 @@ void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 	for (i = 0; i < num_elem; i++)
 		sha1_process(&md, addr[i], len[i]);
 	sha1_done(&md, mac);
+	return 0;
 }
 
 
diff --git a/src/crypto/crypto_none.c b/src/crypto/crypto_none.c
index f18c2a8..9f43775 100644
--- a/src/crypto/crypto_none.c
+++ b/src/crypto/crypto_none.c
@@ -18,8 +18,9 @@
 #include "crypto.h"
 
 
-void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 {
+	return 0;
 }
 
 
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index a4c3415..b97d65b 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / wrapper functions for libcrypto
- * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -34,15 +34,19 @@
 #endif /* openssl < 0.9.7 */
 
 
-void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 {
 	MD4_CTX ctx;
 	size_t i;
 
-	MD4_Init(&ctx);
+	if (!MD4_Init(&ctx))
+		return -1;
 	for (i = 0; i < num_elem; i++)
-		MD4_Update(&ctx, addr[i], len[i]);
-	MD4_Final(mac, &ctx);
+		if (!MD4_Update(&ctx, addr[i], len[i]))
+			return -1;
+	if (!MD4_Final(mac, &ctx))
+		return -1;
+	return 0;
 }
 
 
@@ -67,27 +71,35 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
 }
 
 
-void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 {
 	MD5_CTX ctx;
 	size_t i;
 
-	MD5_Init(&ctx);
+	if (!MD5_Init(&ctx))
+		return -1;
 	for (i = 0; i < num_elem; i++)
-		MD5_Update(&ctx, addr[i], len[i]);
-	MD5_Final(mac, &ctx);
+		if (!MD5_Update(&ctx, addr[i], len[i]))
+			return -1;
+	if (!MD5_Final(mac, &ctx))
+		return -1;
+	return 0;
 }
 
 
-void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 {
 	SHA_CTX ctx;
 	size_t i;
 
-	SHA1_Init(&ctx);
+	if (!SHA1_Init(&ctx))
+		return -1;
 	for (i = 0; i < num_elem; i++)
-		SHA1_Update(&ctx, addr[i], len[i]);
-	SHA1_Final(mac, &ctx);
+		if (!SHA1_Update(&ctx, addr[i], len[i]))
+			return -1;
+	if (!SHA1_Final(mac, &ctx))
+		return -1;
+	return 0;
 }
 
 
diff --git a/src/crypto/md4.c b/src/crypto/md4.c
index 41c84a3..a5dc3d3 100644
--- a/src/crypto/md4.c
+++ b/src/crypto/md4.c
@@ -35,7 +35,7 @@ static void MD4Update(MD4_CTX *ctx, const unsigned char *input, size_t len);
 static void MD4Final(unsigned char digest[MD4_DIGEST_LENGTH], MD4_CTX *ctx);
 
 
-void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 {
 	MD4_CTX ctx;
 	size_t i;
@@ -44,6 +44,7 @@ void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 	for (i = 0; i < num_elem; i++)
 		MD4Update(&ctx, addr[i], len[i]);
 	MD4Final(mac, &ctx);
+	return 0;
 }
 
 
diff --git a/src/crypto/md5.c b/src/crypto/md5.c
index a7db7aa..17396af 100644
--- a/src/crypto/md5.c
+++ b/src/crypto/md5.c
@@ -27,9 +27,10 @@
  * @addr: Pointers to the data areas
  * @len: Lengths of the data blocks
  * @mac: Buffer for the hash (16 bytes)
+ * Returns: 0 on success, -1 on failure
  */
-void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
-		     const u8 *addr[], const size_t *len, u8 *mac)
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+		    const u8 *addr[], const size_t *len, u8 *mac)
 {
 	u8 k_pad[64]; /* padding - key XORd with ipad/opad */
 	u8 tk[16];
@@ -41,12 +42,13 @@ void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
 		 * Fixed limit on the number of fragments to avoid having to
 		 * allocate memory (which could fail).
 		 */
-		return;
+		return -1;
 	}
 
         /* if key is longer than 64 bytes reset it to key = MD5(key) */
         if (key_len > 64) {
-		md5_vector(1, &key, &key_len, tk);
+		if (md5_vector(1, &key, &key_len, tk))
+			return -1;
 		key = tk;
 		key_len = 16;
         }
@@ -75,7 +77,8 @@ void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
 		_addr[i + 1] = addr[i];
 		_len[i + 1] = len[i];
 	}
-	md5_vector(1 + num_elem, _addr, _len, mac);
+	if (md5_vector(1 + num_elem, _addr, _len, mac))
+		return -1;
 
 	os_memset(k_pad, 0, sizeof(k_pad));
 	os_memcpy(k_pad, key, key_len);
@@ -88,7 +91,7 @@ void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
 	_len[0] = 64;
 	_addr[1] = mac;
 	_len[1] = MD5_MAC_LEN;
-	md5_vector(2, _addr, _len, mac);
+	return md5_vector(2, _addr, _len, mac);
 }
 
 
@@ -99,11 +102,12 @@ void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
  * @data: Pointers to the data area
  * @data_len: Length of the data area
  * @mac: Buffer for the hash (16 bytes)
+ * Returns: 0 on success, -1 on failure
  */
-void hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
 	      u8 *mac)
 {
-	hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+	return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
 }
 
 
diff --git a/src/crypto/md5.h b/src/crypto/md5.h
index e82f396..975de29 100644
--- a/src/crypto/md5.h
+++ b/src/crypto/md5.h
@@ -1,6 +1,6 @@
 /*
  * MD5 hash implementation and interface functions
- * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -17,10 +17,10 @@
 
 #define MD5_MAC_LEN 16
 
-void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
-		     const u8 *addr[], const size_t *len, u8 *mac);
-void hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
-	      u8 *mac);
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+		    const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	     u8 *mac);
 
 #ifdef CONFIG_CRYPTO_INTERNAL
 struct MD5Context;
diff --git a/src/crypto/sha1.c b/src/crypto/sha1.c
index ceec1a4..3eda248 100644
--- a/src/crypto/sha1.c
+++ b/src/crypto/sha1.c
@@ -28,9 +28,10 @@
  * @addr: Pointers to the data areas
  * @len: Lengths of the data blocks
  * @mac: Buffer for the hash (20 bytes)
+ * Returns: 0 on success, -1 on failure
  */
-void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
-		      const u8 *addr[], const size_t *len, u8 *mac)
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
 {
 	unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */
 	unsigned char tk[20];
@@ -42,12 +43,13 @@ void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
 		 * Fixed limit on the number of fragments to avoid having to
 		 * allocate memory (which could fail).
 		 */
-		return;
+		return -1;
 	}
 
         /* if key is longer than 64 bytes reset it to key = SHA1(key) */
         if (key_len > 64) {
-		sha1_vector(1, &key, &key_len, tk);
+		if (sha1_vector(1, &key, &key_len, tk))
+			return -1;
 		key = tk;
 		key_len = 20;
         }
@@ -75,7 +77,8 @@ void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
 		_addr[i + 1] = addr[i];
 		_len[i + 1] = len[i];
 	}
-	sha1_vector(1 + num_elem, _addr, _len, mac);
+	if (sha1_vector(1 + num_elem, _addr, _len, mac))
+		return -1;
 
 	os_memset(k_pad, 0, sizeof(k_pad));
 	os_memcpy(k_pad, key, key_len);
@@ -88,7 +91,7 @@ void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
 	_len[0] = 64;
 	_addr[1] = mac;
 	_len[1] = SHA1_MAC_LEN;
-	sha1_vector(2, _addr, _len, mac);
+	return sha1_vector(2, _addr, _len, mac);
 }
 
 
@@ -99,11 +102,12 @@ void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
  * @data: Pointers to the data area
  * @data_len: Length of the data area
  * @mac: Buffer for the hash (20 bytes)
+ * Returns: 0 on success, -1 of failure
  */
-void hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
 	       u8 *mac)
 {
-	hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+	return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
 }
 
 
@@ -116,12 +120,13 @@ void hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
  * @data_len: Length of the data
  * @buf: Buffer for the generated pseudo-random key
  * @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success, -1 of failure
  *
  * This function is used to derive new, cryptographically separate keys from a
  * given key (e.g., PMK in IEEE 802.11i).
  */
-void sha1_prf(const u8 *key, size_t key_len, const char *label,
-	      const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+int sha1_prf(const u8 *key, size_t key_len, const char *label,
+	     const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
 {
 	u8 counter = 0;
 	size_t pos, plen;
@@ -141,17 +146,21 @@ void sha1_prf(const u8 *key, size_t key_len, const char *label,
 	while (pos < buf_len) {
 		plen = buf_len - pos;
 		if (plen >= SHA1_MAC_LEN) {
-			hmac_sha1_vector(key, key_len, 3, addr, len,
-					 &buf[pos]);
+			if (hmac_sha1_vector(key, key_len, 3, addr, len,
+					     &buf[pos]))
+				return -1;
 			pos += SHA1_MAC_LEN;
 		} else {
-			hmac_sha1_vector(key, key_len, 3, addr, len,
-					 hash);
+			if (hmac_sha1_vector(key, key_len, 3, addr, len,
+					     hash))
+				return -1;
 			os_memcpy(&buf[pos], hash, plen);
 			break;
 		}
 		counter++;
 	}
+
+	return 0;
 }
 
 
@@ -165,12 +174,13 @@ void sha1_prf(const u8 *key, size_t key_len, const char *label,
  * @seed_len: Length of the seed
  * @buf: Buffer for the generated pseudo-random key
  * @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success, -1 of failure
  *
  * This function is used to derive new, cryptographically separate keys from a
  * given key for EAP-FAST. T-PRF is defined in RFC 4851, Section 5.5.
  */
-void sha1_t_prf(const u8 *key, size_t key_len, const char *label,
-		const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len)
+int sha1_t_prf(const u8 *key, size_t key_len, const char *label,
+	       const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len)
 {
 	unsigned char counter = 0;
 	size_t pos, plen;
@@ -197,7 +207,8 @@ void sha1_t_prf(const u8 *key, size_t key_len, const char *label,
 	while (pos < buf_len) {
 		counter++;
 		plen = buf_len - pos;
-		hmac_sha1_vector(key, key_len, 5, addr, len, hash);
+		if (hmac_sha1_vector(key, key_len, 5, addr, len, hash))
+			return -1;
 		if (plen >= SHA1_MAC_LEN) {
 			os_memcpy(&buf[pos], hash, SHA1_MAC_LEN);
 			pos += SHA1_MAC_LEN;
@@ -207,6 +218,8 @@ void sha1_t_prf(const u8 *key, size_t key_len, const char *label,
 		}
 		len[0] = SHA1_MAC_LEN;
 	}
+
+	return 0;
 }
 #endif /* CONFIG_NO_T_PRF */
 
@@ -301,9 +314,9 @@ int tls_prf(const u8 *secret, size_t secret_len, const char *label,
 
 #ifndef CONFIG_NO_PBKDF2
 
-static void pbkdf2_sha1_f(const char *passphrase, const char *ssid,
-			  size_t ssid_len, int iterations, unsigned int count,
-			  u8 *digest)
+static int pbkdf2_sha1_f(const char *passphrase, const char *ssid,
+			 size_t ssid_len, int iterations, unsigned int count,
+			 u8 *digest)
 {
 	unsigned char tmp[SHA1_MAC_LEN], tmp2[SHA1_MAC_LEN];
 	int i, j;
@@ -327,16 +340,20 @@ static void pbkdf2_sha1_f(const char *passphrase, const char *ssid,
 	count_buf[1] = (count >> 16) & 0xff;
 	count_buf[2] = (count >> 8) & 0xff;
 	count_buf[3] = count & 0xff;
-	hmac_sha1_vector((u8 *) passphrase, passphrase_len, 2, addr, len, tmp);
+	if (hmac_sha1_vector((u8 *) passphrase, passphrase_len, 2, addr, len, tmp))
+		return -1;
 	os_memcpy(digest, tmp, SHA1_MAC_LEN);
 
 	for (i = 1; i < iterations; i++) {
-		hmac_sha1((u8 *) passphrase, passphrase_len, tmp, SHA1_MAC_LEN,
-			  tmp2);
+		if (hmac_sha1((u8 *) passphrase, passphrase_len, tmp, SHA1_MAC_LEN,
+			  tmp2))
+			return -1;
 		os_memcpy(tmp, tmp2, SHA1_MAC_LEN);
 		for (j = 0; j < SHA1_MAC_LEN; j++)
 			digest[j] ^= tmp2[j];
 	}
+
+	return 0;
 }
 
 
@@ -353,8 +370,8 @@ static void pbkdf2_sha1_f(const char *passphrase, const char *ssid,
  * iterations is set to 4096 and buflen to 32. This function is described in
  * IEEE Std 802.11-2004, Clause H.4. The main construction is from PKCS#5 v2.0.
  */
-void pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len,
-		 int iterations, u8 *buf, size_t buflen)
+int pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len,
+		int iterations, u8 *buf, size_t buflen)
 {
 	unsigned int count = 0;
 	unsigned char *pos = buf;
@@ -363,13 +380,16 @@ void pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len,
 
 	while (left > 0) {
 		count++;
-		pbkdf2_sha1_f(passphrase, ssid, ssid_len, iterations, count,
-			      digest);
+		if (pbkdf2_sha1_f(passphrase, ssid, ssid_len, iterations,
+				  count, digest))
+			return -1;
 		plen = left > SHA1_MAC_LEN ? SHA1_MAC_LEN : left;
 		os_memcpy(pos, digest, plen);
 		pos += plen;
 		left -= plen;
 	}
+
+	return 0;
 }
 
 #endif /* CONFIG_NO_PBKDF2 */
@@ -399,8 +419,9 @@ static void SHA1Transform(u32 state[5], const unsigned char buffer[64]);
  * @addr: Pointers to the data areas
  * @len: Lengths of the data blocks
  * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 of failure
  */
-void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len,
 		 u8 *mac)
 {
 	SHA1_CTX ctx;
@@ -410,6 +431,7 @@ void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len,
 	for (i = 0; i < num_elem; i++)
 		SHA1Update(&ctx, addr[i], len[i]);
 	SHA1Final(mac, &ctx);
+	return 0;
 }
 
 
diff --git a/src/crypto/sha1.h b/src/crypto/sha1.h
index 9c365e2..7f89606 100644
--- a/src/crypto/sha1.h
+++ b/src/crypto/sha1.h
@@ -1,6 +1,6 @@
 /*
  * SHA1 hash implementation and interface functions
- * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -17,19 +17,19 @@
 
 #define SHA1_MAC_LEN 20
 
-void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
-		      const u8 *addr[], const size_t *len, u8 *mac);
-void hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
 	       u8 *mac);
-void sha1_prf(const u8 *key, size_t key_len, const char *label,
-	      const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
-void sha1_t_prf(const u8 *key, size_t key_len, const char *label,
-		const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len);
+int sha1_prf(const u8 *key, size_t key_len, const char *label,
+	     const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+int sha1_t_prf(const u8 *key, size_t key_len, const char *label,
+	       const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len);
 int __must_check tls_prf(const u8 *secret, size_t secret_len,
 			 const char *label, const u8 *seed, size_t seed_len,
 			 u8 *out, size_t outlen);
-void pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len,
-		 int iterations, u8 *buf, size_t buflen);
+int pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len,
+		int iterations, u8 *buf, size_t buflen);
 
 #ifdef CONFIG_CRYPTO_INTERNAL
 struct SHA1Context;
diff --git a/src/crypto/sha256.c b/src/crypto/sha256.c
index 96dac0e..49e42f6 100644
--- a/src/crypto/sha256.c
+++ b/src/crypto/sha256.c
@@ -177,8 +177,9 @@ static int sha256_done(struct sha256_state *md, unsigned char *out);
  * @addr: Pointers to the data areas
  * @len: Lengths of the data blocks
  * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 of failure
  */
-void sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
 		 u8 *mac)
 {
 	struct sha256_state ctx;
@@ -186,8 +187,12 @@ void sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
 
 	sha256_init(&ctx);
 	for (i = 0; i < num_elem; i++)
-		sha256_process(&ctx, addr[i], len[i]);
-	sha256_done(&ctx, mac);
+		if (sha256_process(&ctx, addr[i], len[i]))
+			return -1;
+	if (sha256_done(&ctx, mac))
+		return -1;
+
+	return 0;
 }
 
 
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index 21825e6..4b44328 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -28,11 +28,54 @@ struct tls_keys {
 	size_t inner_secret_len;
 };
 
+enum tls_event {
+	TLS_CERT_CHAIN_FAILURE,
+	TLS_PEER_CERTIFICATE
+};
+
+/*
+ * 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
+};
+
+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;
+	} peer_cert;
+};
+
 struct tls_config {
 	const char *opensc_engine_path;
 	const char *pkcs11_engine_path;
 	const char *pkcs11_module_path;
 	const char *pkcs11_module_init_args;
+
+	void (*event_cb)(void *ctx, enum tls_event ev,
+			 union tls_event_data *data);
+	void *cb_ctx;
 };
 
 /**
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index a8e3f46..c8c92f6 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -29,6 +29,7 @@
 #endif /* OPENSSL_NO_ENGINE */
 
 #include "common.h"
+#include "crypto.h"
 #include "tls.h"
 
 #if OPENSSL_VERSION_NUMBER >= 0x0090800fL
@@ -49,6 +50,15 @@
 
 static int tls_openssl_ref_count = 0;
 
+struct tls_global {
+	void (*event_cb)(void *ctx, enum tls_event ev,
+			 union tls_event_data *data);
+	void *cb_ctx;
+};
+
+static struct tls_global *tls_global = NULL;
+
+
 struct tls_connection {
 	SSL *ssl;
 	BIO *ssl_in, *ssl_out;
@@ -65,6 +75,12 @@ struct tls_connection {
 	/* SessionTicket received from OpenSSL hello_extension_cb (server) */
 	u8 *session_ticket;
 	size_t session_ticket_len;
+
+	int ca_cert_verify:1;
+	int cert_probe:1;
+	int server_cert_only:1;
+
+	u8 srv_cert_hash[32];
 };
 
 
@@ -739,6 +755,14 @@ void * tls_init(const struct tls_config *conf)
 	SSL_CTX *ssl;
 
 	if (tls_openssl_ref_count == 0) {
+		tls_global = os_zalloc(sizeof(*tls_global));
+		if (tls_global == NULL)
+			return NULL;
+		if (conf) {
+			tls_global->event_cb = conf->event_cb;
+			tls_global->cb_ctx = conf->cb_ctx;
+		}
+
 		SSL_load_error_strings();
 		SSL_library_init();
 		/* TODO: if /dev/urandom is available, PRNG is seeded
@@ -793,6 +817,8 @@ void tls_deinit(void *ssl_ctx)
 		ERR_remove_state(0);
 		ERR_free_strings();
 		EVP_cleanup();
+		os_free(tls_global);
+		tls_global = NULL;
 	}
 }
 
@@ -1059,6 +1085,124 @@ static int tls_match_altsubject(X509 *cert, const char *match)
 }
 
 
+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;
+
+	if (tls_global->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;
+	tls_global->event_cb(tls_global->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;
+#ifdef CONFIG_SHA256
+	u8 hash[32];
+#endif /* CONFIG_SHA256 */
+
+	if (tls_global->event_cb == NULL)
+		return;
+
+	os_memset(&ev, 0, sizeof(ev));
+	if (conn->cert_probe) {
+		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;
+	tls_global->event_cb(tls_global->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
+	wpabuf_free(cert);
+}
+
+
 static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
 {
 	char buf[256];
@@ -1067,6 +1211,7 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
 	SSL *ssl;
 	struct tls_connection *conn;
 	char *match, *altmatch;
+	const char *err_str;
 
 	err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
 	err = X509_STORE_CTX_get_error(x509_ctx);
@@ -1079,26 +1224,77 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
 	match = conn ? conn->subject_match : NULL;
 	altmatch = conn ? conn->altsubject_match : NULL;
 
-	if (!preverify_ok) {
-		wpa_printf(MSG_WARNING, "TLS: Certificate verification failed,"
-			   " error %d (%s) depth %d for '%s'", err,
-			   X509_verify_cert_error_string(err), depth, buf);
-	} else {
-		wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - "
-			   "preverify_ok=%d err=%d (%s) depth=%d buf='%s'",
-			   preverify_ok, err,
-			   X509_verify_cert_error_string(err), depth, buf);
-		if (depth == 0 && match && os_strstr(buf, match) == NULL) {
-			wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not "
-				   "match with '%s'", buf, match);
-			preverify_ok = 0;
-		} else if (depth == 0 && altmatch &&
-			   !tls_match_altsubject(err_cert, altmatch)) {
-			wpa_printf(MSG_WARNING, "TLS: altSubjectName match "
-				   "'%s' not found", altmatch);
+	if (!preverify_ok && !conn->ca_cert_verify)
+		preverify_ok = 1;
+	if (!preverify_ok && depth > 0 && conn->server_cert_only)
+		preverify_ok = 1;
+
+	err_str = X509_verify_cert_error_string(err);
+
+#ifdef CONFIG_SHA256
+	if (preverify_ok && depth == 0 && conn->server_cert_only) {
+		struct wpabuf *cert;
+		cert = get_x509_cert(err_cert);
+		if (!cert) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: Could not fetch "
+				   "server certificate data");
 			preverify_ok = 0;
+		} else {
+			u8 hash[32];
+			const u8 *addr[1];
+			size_t len[1];
+			addr[0] = wpabuf_head(cert);
+			len[0] = wpabuf_len(cert);
+			if (sha256_vector(1, addr, len, hash) < 0 ||
+			    os_memcmp(conn->srv_cert_hash, hash, 32) != 0) {
+				err_str = "Server certificate mismatch";
+				err = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
+				preverify_ok = 0;
+			}
+			wpabuf_free(cert);
 		}
 	}
+#endif /* CONFIG_SHA256 */
+
+	if (!preverify_ok) {
+		wpa_printf(MSG_WARNING, "TLS: Certificate verification failed,"
+			   " error %d (%s) depth %d for '%s'", err, err_str,
+			   depth, buf);
+		openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       err_str, TLS_FAIL_UNSPECIFIED);
+		return preverify_ok;
+	}
+
+	wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - preverify_ok=%d "
+		   "err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'",
+		   preverify_ok, err, err_str,
+		   conn->ca_cert_verify, depth, buf);
+	if (depth == 0 && match && os_strstr(buf, match) == NULL) {
+		wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not "
+			   "match with '%s'", buf, match);
+		preverify_ok = 0;
+		openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       "Subject mismatch",
+				       TLS_FAIL_SUBJECT_MISMATCH);
+	} else if (depth == 0 && altmatch &&
+		   !tls_match_altsubject(err_cert, altmatch)) {
+		wpa_printf(MSG_WARNING, "TLS: altSubjectName match "
+			   "'%s' not found", altmatch);
+		preverify_ok = 0;
+		openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       "AltSubject mismatch",
+				       TLS_FAIL_ALTSUBJECT_MISMATCH);
+	} else
+		openssl_tls_cert_event(conn, err_cert, depth, buf);
+
+	if (conn->cert_probe && preverify_ok && depth == 0) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Reject server certificate "
+			   "on probe-only run");
+		preverify_ok = 0;
+		openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       "Server certificate chain probe",
+				       TLS_FAIL_SERVER_CHAIN_PROBE);
+	}
 
 	return preverify_ok;
 }
@@ -1155,6 +1351,47 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn,
 		return -1;
 	}
 
+	SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
+	conn->ca_cert_verify = 1;
+
+	if (ca_cert && os_strncmp(ca_cert, "probe://", 8) == 0) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Probe for server certificate "
+			   "chain");
+		conn->cert_probe = 1;
+		conn->ca_cert_verify = 0;
+		return 0;
+	}
+
+	if (ca_cert && os_strncmp(ca_cert, "hash://", 7) == 0) {
+#ifdef CONFIG_SHA256
+		const char *pos = ca_cert + 7;
+		if (os_strncmp(pos, "server/sha256/", 14) != 0) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: Unsupported ca_cert "
+				   "hash value '%s'", ca_cert);
+			return -1;
+		}
+		pos += 14;
+		if (os_strlen(pos) != 32 * 2) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: Unexpected SHA256 "
+				   "hash length in ca_cert '%s'", ca_cert);
+			return -1;
+		}
+		if (hexstr2bin(pos, conn->srv_cert_hash, 32) < 0) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: Invalid SHA256 hash "
+				   "value in ca_cert '%s'", ca_cert);
+			return -1;
+		}
+		conn->server_cert_only = 1;
+		wpa_printf(MSG_DEBUG, "OpenSSL: Checking only server "
+			   "certificate match");
+		return 0;
+#else /* CONFIG_SHA256 */
+		wpa_printf(MSG_INFO, "No SHA256 included in the build - "
+			   "cannot validate server certificate hash");
+		return -1;
+#endif /* CONFIG_SHA256 */
+	}
+
 	if (ca_cert_blob) {
 		X509 *cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ca_cert_blob,
 				      ca_cert_blob_len);
@@ -1183,7 +1420,6 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn,
 		X509_free(cert);
 		wpa_printf(MSG_DEBUG, "OpenSSL: %s - added ca_cert_blob "
 			   "to certificate store", __func__);
-		SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
 		return 0;
 	}
 
@@ -1192,7 +1428,6 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn,
 	    0) {
 		wpa_printf(MSG_DEBUG, "OpenSSL: Added CA certificates from "
 			   "system certificate store");
-		SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
 		return 0;
 	}
 #endif /* CONFIG_NATIVE_WINDOWS */
@@ -1215,7 +1450,6 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn,
 				   "certificate(s) loaded");
 			tls_get_errors(ssl_ctx);
 		}
-		SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
 #else /* OPENSSL_NO_STDIO */
 		wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO",
 			   __func__);
@@ -1224,7 +1458,7 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn,
 	} else {
 		/* No ca_cert configured - do not try to verify server
 		 * certificate */
-		SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
+		conn->ca_cert_verify = 0;
 	}
 
 	return 0;
@@ -1309,10 +1543,12 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
 		return -1;
 
 	if (verify_peer) {
+		conn->ca_cert_verify = 1;
 		SSL_set_verify(conn->ssl, SSL_VERIFY_PEER |
 			       SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
 			       SSL_VERIFY_CLIENT_ONCE, tls_verify_cb);
 	} else {
+		conn->ca_cert_verify = 0;
 		SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
 	}
 
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index 5e37828..bea23bc 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -1,6 +1,6 @@
 /*
  * EAP peer state machines (RFC 4137)
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -1154,6 +1154,54 @@ static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req)
 }
 
 
+static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev,
+				  union tls_event_data *data)
+{
+	struct eap_sm *sm = ctx;
+	char *hash_hex = NULL;
+	char *cert_hex = NULL;
+
+	switch (ev) {
+	case TLS_CERT_CHAIN_FAILURE:
+		wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TLS_CERT_ERROR
+			"reason=%d depth=%d subject='%s' err='%s'",
+			data->cert_fail.reason,
+			data->cert_fail.depth,
+			data->cert_fail.subject,
+			data->cert_fail.reason_txt);
+		break;
+	case TLS_PEER_CERTIFICATE:
+		if (data->peer_cert.hash) {
+			size_t len = data->peer_cert.hash_len * 2 + 1;
+			hash_hex = os_malloc(len);
+			if (hash_hex) {
+				wpa_snprintf_hex(hash_hex, len,
+						 data->peer_cert.hash,
+						 data->peer_cert.hash_len);
+			}
+		}
+		wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PEER_CERT
+			"depth=%d subject='%s'%s%s",
+			data->peer_cert.depth, data->peer_cert.subject,
+			hash_hex ? " hash=" : "", hash_hex ? hash_hex : "");
+
+		if (data->peer_cert.cert) {
+			size_t len = wpabuf_len(data->peer_cert.cert) * 2 + 1;
+			cert_hex = os_malloc(len);
+			if (cert_hex == NULL)
+				break;
+			wpa_snprintf_hex(cert_hex, len,
+					 wpabuf_head(data->peer_cert.cert),
+					 wpabuf_len(data->peer_cert.cert));
+		}
+		break;
+	}
+
+	os_free(hash_hex);
+	os_free(cert_hex);
+}
+
+
 /**
  * eap_peer_sm_init - Allocate and initialize EAP peer state machine
  * @eapol_ctx: Context data to be used with eapol_cb calls
@@ -1189,6 +1237,8 @@ struct eap_sm * eap_peer_sm_init(void *eapol_ctx,
 	tlsconf.pkcs11_engine_path = conf->pkcs11_engine_path;
 	tlsconf.pkcs11_module_path = conf->pkcs11_module_path;
 	tlsconf.pkcs11_module_init_args = conf->pkcs11_module_init_args;
+	tlsconf.event_cb = eap_peer_sm_tls_event;
+	tlsconf.cb_ctx = sm;
 	sm->ssl_ctx = tls_init(&tlsconf);
 	if (sm->ssl_ctx == NULL) {
 		wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS "
diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h
index 94245c3..b64b68f 100644
--- a/src/eap_peer/eap_config.h
+++ b/src/eap_peer/eap_config.h
@@ -85,6 +85,15 @@ struct eap_peer_config {
 	 * 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".
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 45d6ada..d1996bd 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -853,6 +853,7 @@ NEED_SHA256=y
 endif
 
 ifdef NEED_SHA256
+CFLAGS += -DCONFIG_SHA256
 OBJS += ../src/crypto/sha256.o
 CFLAGS += -DNEED_SHA256
 endif
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index 43e81a1..f3e8ffb 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -371,6 +371,16 @@ fast_reauth=1
 #	a trusted CA certificate should always be configured when using
 #	EAP-TLS/TTLS/PEAP. Full path should be used since working directory may
 #	change when wpa_supplicant is run in the background.
+#
+#	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".
-- 
1.7.3.4

