commit 1b414f59fc46b8c88e606de122debf69e8b5faa8
Author: Jouni Malinen <j@w1.fi>
Date:   Sat Sep 17 22:42:54 2011 +0300

    eapol_test: Add option for writing server certificate chain to a file
    
    eapol_test command line argument -o<file> can now be used to request
    the received server certificate chain to be written to the specified
    file. The certificates will be written in PEM format. [Bug 391]

================================================================================
--- wpa_supplicant-0.7.1/src/crypto/tls.h
+++ wpa_supplicant-0.7.1/src/crypto/tls.h
@@ -73,6 +73,7 @@
 	const char *pkcs11_engine_path;
 	const char *pkcs11_module_path;
 	int fips_mode;
+	int cert_in_cb;
 
 	void (*event_cb)(void *ctx, enum tls_event ev,
 			 union tls_event_data *data);
--- wpa_supplicant-0.7.1/src/crypto/tls_openssl.c
+++ wpa_supplicant-0.7.1/src/crypto/tls_openssl.c
@@ -54,6 +54,7 @@
 	void (*event_cb)(void *ctx, enum tls_event ev,
 			 union tls_event_data *data);
 	void *cb_ctx;
+	int cert_in_cb;
 };
 
 static struct tls_global *tls_global = NULL;
@@ -687,6 +688,7 @@
 		if (conf) {
 			tls_global->event_cb = conf->event_cb;
 			tls_global->cb_ctx = conf->cb_ctx;
+			tls_global->cert_in_cb = conf->cert_in_cb;
 		}
 
 #ifdef CONFIG_FIPS
@@ -1230,7 +1232,7 @@
 		return;
 
 	os_memset(&ev, 0, sizeof(ev));
-	if (conn->cert_probe) {
+	if (conn->cert_probe || tls_global->cert_in_cb) {
 		cert = get_x509_cert(err_cert);
 		ev.peer_cert.cert = cert;
 	}
--- wpa_supplicant-0.7.1/src/eap_peer/eap.c
+++ wpa_supplicant-0.7.1/src/eap_peer/eap.c
@@ -1254,6 +1254,7 @@
 #endif /* CONFIG_FIPS */
 	tlsconf.event_cb = eap_peer_sm_tls_event;
 	tlsconf.cb_ctx = sm;
+	tlsconf.cert_in_cb = conf->cert_in_cb;
 	sm->ssl_ctx = tls_init(&tlsconf);
 	if (sm->ssl_ctx == NULL) {
 		wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS "
--- wpa_supplicant-0.7.1/src/eap_peer/eap.h
+++ wpa_supplicant-0.7.1/src/eap_peer/eap.h
@@ -262,6 +262,11 @@
 	 * This is only used by EAP-WSC and can be left %NULL if not available.
 	 */
 	struct wps_context *wps;
+
+	/**
+	 * cert_in_cb - Include server certificates in callback
+	 */
+	int cert_in_cb;
 };
 
 struct eap_sm * eap_peer_sm_init(void *eapol_ctx,
--- wpa_supplicant-0.7.1/src/eapol_supp/eapol_supp_sm.c
+++ wpa_supplicant-0.7.1/src/eapol_supp/eapol_supp_sm.c
@@ -1868,6 +1868,7 @@
 	conf.pkcs11_engine_path = ctx->pkcs11_engine_path;
 	conf.pkcs11_module_path = ctx->pkcs11_module_path;
 	conf.wps = ctx->wps;
+	conf.cert_in_cb = ctx->cert_in_cb;
 
 	sm->eap = eap_peer_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx, &conf);
 	if (sm->eap == NULL) {
--- wpa_supplicant-0.7.1/src/eapol_supp/eapol_supp_sm.h
+++ wpa_supplicant-0.7.1/src/eapol_supp/eapol_supp_sm.h
@@ -231,6 +231,11 @@
 	 */
 	void (*cert_cb)(void *ctx, int depth, const char *subject,
 			const char *cert_hash, const struct wpabuf *cert);
+
+	/**
+	 * cert_in_cb - Include server certificates in callback
+	 */
+	int cert_in_cb;
 };
 
 
--- wpa_supplicant-0.7.1/wpa_supplicant/eapol_test.c
+++ wpa_supplicant-0.7.1/wpa_supplicant/eapol_test.c
@@ -23,6 +23,7 @@
 #include "eapol_supp/eapol_supp_sm.h"
 #include "eap_peer/eap.h"
 #include "eloop.h"
+#include "utils/base64.h"
 #include "rsn_supp/wpa.h"
 #include "eap_peer/eap_i.h"
 #include "wpa_supplicant_i.h"
@@ -74,6 +75,8 @@
 	char *connect_info;
 	u8 own_addr[ETH_ALEN];
 	struct extra_radius_attr *extra_attrs;
+
+	FILE *server_cert_file;
 };
 
 static struct eapol_test_data eapol_test;
@@ -290,7 +293,6 @@
 static int eapol_test_eapol_send(void *ctx, int type, const u8 *buf,
 				 size_t len)
 {
-	/* struct wpa_supplicant *wpa_s = ctx; */
 	printf("WPA: eapol_test_eapol_send(type=%d len=%lu)\n",
 	       type, (unsigned long) len);
 	if (type == IEEE802_1X_TYPE_EAP_PACKET) {
@@ -304,16 +306,16 @@
 static void eapol_test_set_config_blob(void *ctx,
 				       struct wpa_config_blob *blob)
 {
-	struct wpa_supplicant *wpa_s = ctx;
-	wpa_config_set_blob(wpa_s->conf, blob);
+	struct eapol_test_data *e = ctx;
+	wpa_config_set_blob(e->wpa_s->conf, blob);
 }
 
 
 static const struct wpa_config_blob *
 eapol_test_get_config_blob(void *ctx, const char *name)
 {
-	struct wpa_supplicant *wpa_s = ctx;
-	return wpa_config_get_blob(wpa_s->conf, name);
+	struct eapol_test_data *e = ctx;
+	return wpa_config_get_blob(e->wpa_s->conf, name);
 }
 
 
@@ -381,6 +383,50 @@
 	}
 }
 
+static void eapol_test_write_cert(FILE *f, const char *subject,
+                                  const struct wpabuf *cert)
+{
+	unsigned char *encoded;
+
+	encoded = base64_encode(wpabuf_head(cert), wpabuf_len(cert), NULL);
+	if (encoded == NULL)
+		return;
+	fprintf(f, "%s\n-----BEGIN CERTIFICATE-----\n%s"
+		"-----END CERTIFICATE-----\n\n", subject, encoded);
+	os_free(encoded);
+}
+
+static void eapol_test_cert_cb(void *ctx, int depth, const char *subject,
+                              const char *cert_hash,
+                              const struct wpabuf *cert)
+{
+	struct eapol_test_data *e = ctx;
+
+	wpa_msg(e->wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_CERT
+		"depth=%d subject='%s'%s%s",
+		depth, subject,
+		cert_hash ? " hash=" : "",
+		cert_hash ? cert_hash : "");
+
+	if (cert) {
+		char *cert_hex;
+		size_t len = wpabuf_len(cert) * 2 + 1;
+		cert_hex = os_malloc(len);
+		if (cert_hex) {
+			wpa_snprintf_hex(cert_hex, len, wpabuf_head(cert),
+					wpabuf_len(cert));
+			wpa_msg_ctrl(e->wpa_s, MSG_INFO,
+				WPA_EVENT_EAP_PEER_CERT
+				"depth=%d subject='%s' cert=%s",
+				depth, subject, cert_hex);
+			os_free(cert_hex);
+		}
+		if (e->server_cert_file)
+			eapol_test_write_cert(e->server_cert_file,
+						subject, cert);
+	}
+}
+
 
 static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s,
 		      struct wpa_ssid *ssid)
@@ -393,7 +439,7 @@
 		printf("Failed to allocate EAPOL context.\n");
 		return -1;
 	}
-	ctx->ctx = wpa_s;
+	ctx->ctx = e;
 	ctx->msg_ctx = wpa_s;
 	ctx->scard_ctx = wpa_s->scard;
 	ctx->cb = eapol_sm_cb;
@@ -407,6 +453,8 @@
 	ctx->opensc_engine_path = wpa_s->conf->opensc_engine_path;
 	ctx->pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path;
 	ctx->pkcs11_module_path = wpa_s->conf->pkcs11_module_path;
+	ctx->cert_cb = eapol_test_cert_cb;
+	ctx->cert_in_cb = 1;
 
 	wpa_s->eapol = eapol_sm_init(ctx);
 	if (wpa_s->eapol == NULL) {
@@ -963,7 +1011,7 @@
 	       "eapol_test [-nWS] -c<conf> [-a<AS IP>] [-p<AS port>] "
 	       "[-s<AS secret>]\\\n"
 	       "           [-r<count>] [-t<timeout>] [-C<Connect-Info>] \\\n"
-	       "           [-M<client MAC address>] \\\n"
+	       "           [-M<client MAC address>] [-o<server cert file] \\\n"
 	       "           [-N<attr spec>] \\\n"
 	       "           [-A<client IP>]\n"
 	       "eapol_test scard\n"
@@ -989,6 +1037,8 @@
 	       "  -M<client MAC address> = Set own MAC address "
 	       "(Calling-Station-Id,\n"
 	       "                           default: 02:00:00:00:00:01)\n"
+	       "  -o<server cert file> = Write received server certificate\n"
+	       "                         chain to the specified file\n"
 	       "  -N<attr spec> = send arbitrary attribute specified by:\n"
 	       "                  attr_id:syntax:value or attr_id\n"
 	       "                  attr_id - number id of the attribute\n"
@@ -1030,7 +1080,7 @@
 	wpa_debug_show_keys = 1;
 
 	for (;;) {
-		c = getopt(argc, argv, "a:A:c:C:M:nN:p:r:s:St:W");
+		c = getopt(argc, argv, "a:A:c:C:M:nN:o:p:r:s:St:W");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -1055,6 +1105,16 @@
 		case 'n':
 			eapol_test.no_mppe_keys++;
 			break;
+		case 'o':
+			if (eapol_test.server_cert_file)
+				fclose(eapol_test.server_cert_file);
+			eapol_test.server_cert_file = fopen(optarg, "w");
+			if (eapol_test.server_cert_file == NULL) {
+				printf("Could not open '%s' for writing\n",
+				       optarg);
+				return -1;
+			}
+			break;
 		case 'p':
 			as_port = atoi(optarg);
 			break;
@@ -1194,6 +1254,9 @@
 
 	eloop_destroy();
 
+	if (eapol_test.server_cert_file)
+		fclose(eapol_test.server_cert_file);
+
 	printf("MPPE keys OK: %d  mismatch: %d\n",
 	       eapol_test.num_mppe_ok, eapol_test.num_mppe_mismatch);
 	if (eapol_test.num_mppe_mismatch)
