Add TLS exporter.
diff --git a/CHANGES b/CHANGES
index 927b9e7..142dc80 100644
--- a/CHANGES
+++ b/CHANGES
@@ -198,6 +198,9 @@
      security.
      [Emilia Käsper <emilia.kasper@esat.kuleuven.be> (Google)]
 
+  *) Add TLS key material exporter from RFC 5705.
+     [Eric Rescorla]
+
   *) Add DTLS-SRTP negotiation from RFC 5764.
      [Eric Rescorla]
 
diff --git a/apps/s_client.c b/apps/s_client.c
index 5527dc2..1621a01 100644
--- a/apps/s_client.c
+++ b/apps/s_client.c
@@ -206,6 +206,9 @@
 static int c_msg=0;
 static int c_showcerts=0;
 
+static char *keymatexportlabel=NULL;
+static int keymatexportlen=20;
+
 static void sc_usage(void);
 static void print_stuff(BIO *berr,SSL *con,int full);
 #ifndef OPENSSL_NO_TLSEXT
@@ -360,6 +363,8 @@
 #endif
 	BIO_printf(bio_err," -legacy_renegotiation - enable use of legacy renegotiation (dangerous)\n");
 	BIO_printf(bio_err," -use_srtp profiles - Offer SRTP key management with a colon-separated profile list");
+ 	BIO_printf(bio_err," -keymatexport label   - Export keying material using label\n");
+ 	BIO_printf(bio_err," -keymatexportlen len  - Export len bytes of keying material (default 20)\n");
 	}
 
 #ifndef OPENSSL_NO_TLSEXT
@@ -942,6 +947,17 @@
 			if (--argc < 1) goto bad;
 			srtp_profiles = *(++argv);
 			}
+		else if (strcmp(*argv,"-keymatexport") == 0)
+			{
+			if (--argc < 1) goto bad;
+			keymatexportlabel= *(++argv);
+			}
+		else if (strcmp(*argv,"-keymatexportlen") == 0)
+			{
+			if (--argc < 1) goto bad;
+			keymatexportlen=atoi(*(++argv));
+			if (keymatexportlen == 0) goto bad;
+			}
                 else
 			{
 			BIO_printf(bio_err,"unknown option %s\n",*argv);
@@ -1900,6 +1916,7 @@
 #ifndef OPENSSL_NO_COMP
 	const COMP_METHOD *comp, *expansion;
 #endif
+	unsigned char *exportedkeymat;
 
 	if (full)
 		{
@@ -2045,6 +2062,30 @@
 	}
  
 	SSL_SESSION_print(bio,SSL_get_session(s));
+	if (keymatexportlabel != NULL) {
+		BIO_printf(bio, "Keying material exporter:\n");
+		BIO_printf(bio, "    Label: '%s'\n", keymatexportlabel);
+		BIO_printf(bio, "    Length: %i bytes\n", keymatexportlen);
+		exportedkeymat = OPENSSL_malloc(keymatexportlen);
+		if (exportedkeymat != NULL) {
+			i = SSL_export_keying_material(s, exportedkeymat,
+						       keymatexportlen,
+						       keymatexportlabel,
+						     strlen(keymatexportlabel),
+						       NULL, 0, 0);
+			if (i != keymatexportlen) {
+				BIO_printf(bio,
+					   "    Error: return value %i\n", i);
+			} else {
+				BIO_printf(bio, "    Keying material: ");
+				for (i=0; i<keymatexportlen; i++)
+					BIO_printf(bio, "%02X",
+						   exportedkeymat[i]);
+				BIO_printf(bio, "\n");
+			}
+			OPENSSL_free(exportedkeymat);
+		}
+	}
 	BIO_printf(bio,"---\n");
 	if (peer != NULL)
 		X509_free(peer);
diff --git a/apps/s_server.c b/apps/s_server.c
index e462c99..59e16ec 100644
--- a/apps/s_server.c
+++ b/apps/s_server.c
@@ -297,6 +297,9 @@
 static int s_msg=0;
 static int s_quiet=0;
 
+static char *keymatexportlabel=NULL;
+static int keymatexportlen=20;
+
 static int hack=0;
 #ifndef OPENSSL_NO_ENGINE
 static char *engine_id=NULL;
@@ -548,6 +551,8 @@
 # endif
         BIO_printf(bio_err," -use_srtp profiles - Offer SRTP key management with a colon-separated profile list");
 #endif
+	BIO_printf(bio_err," -keymatexport label   - Export keying material using label\n");
+	BIO_printf(bio_err," -keymatexportlen len  - Export len bytes of keying material (default 20)\n");
 	}
 
 static int local_argc=0;
@@ -1332,6 +1337,17 @@
 			if (--argc < 1) goto bad;
 			srtp_profiles = *(++argv);
 			}
+		else if (strcmp(*argv,"-keymatexport") == 0)
+			{
+			if (--argc < 1) goto bad;
+			keymatexportlabel= *(++argv);
+			}
+		else if (strcmp(*argv,"-keymatexportlen") == 0)
+			{
+			if (--argc < 1) goto bad;
+			keymatexportlen=atoi(*(++argv));
+			if (keymatexportlen == 0) goto bad;
+			}
 		else
 			{
 			BIO_printf(bio_err,"unknown option %s\n",*argv);
@@ -2354,6 +2370,8 @@
 #ifndef OPENSSL_NO_KRB5
 	char *client_princ;
 #endif
+	unsigned char *exportedkeymat;
+
 
 	if ((i=SSL_accept(con)) <= 0)
 		{
@@ -2425,6 +2443,32 @@
 #endif /* OPENSSL_NO_KRB5 */
 	BIO_printf(bio_s_out, "Secure Renegotiation IS%s supported\n",
 		      SSL_get_secure_renegotiation_support(con) ? "" : " NOT");
+ 	if (keymatexportlabel != NULL) {
+ 		BIO_printf(bio_s_out, "Keying material exporter:\n");
+ 		BIO_printf(bio_s_out, "    Label: '%s'\n", keymatexportlabel);
+ 		BIO_printf(bio_s_out, "    Length: %i bytes\n",
+			   keymatexportlen);
+ 		exportedkeymat = OPENSSL_malloc(keymatexportlen);
+ 		if (exportedkeymat != NULL) {
+ 			i = SSL_export_keying_material(con, exportedkeymat,
+						       keymatexportlen,
+						       keymatexportlabel,
+						     strlen(keymatexportlabel),
+						       NULL, 0, 0);
+ 			if (i != keymatexportlen) {
+ 				BIO_printf(bio_s_out,
+					   "    Error: return value %i\n", i);
+ 			} else {
+ 				BIO_printf(bio_s_out, "    Keying material: ");
+ 				for (i=0; i<keymatexportlen; i++)
+ 					BIO_printf(bio_s_out, "%02X",
+						   exportedkeymat[i]);
+ 				BIO_printf(bio_s_out, "\n");
+ 			}
+ 			OPENSSL_free(exportedkeymat);
+ 		}
+ 	}
+
 	return(1);
 	}
 
diff --git a/ssl/d1_lib.c b/ssl/d1_lib.c
index c3b77c8..a94290a 100644
--- a/ssl/d1_lib.c
+++ b/ssl/d1_lib.c
@@ -82,6 +82,7 @@
 	TLS_MD_CLIENT_FINISH_CONST,TLS_MD_CLIENT_FINISH_CONST_SIZE,
 	TLS_MD_SERVER_FINISH_CONST,TLS_MD_SERVER_FINISH_CONST_SIZE,
 	tls1_alert_code,
+	tls1_export_keying_material,
 	};
 
 long dtls1_default_timeout(void)
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index a81c80d..3dcf50e 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -2904,6 +2904,9 @@
 	SSL3_MD_CLIENT_FINISHED_CONST,4,
 	SSL3_MD_SERVER_FINISHED_CONST,4,
 	ssl3_alert_code,
+	(int (*)(SSL *, unsigned char *, unsigned int, const char *,
+		 unsigned int, const unsigned char *, unsigned int,
+		 int use_context))ssl_undefined_function,
 	};
 
 long ssl3_default_timeout(void)
diff --git a/ssl/ssl.h b/ssl/ssl.h
index 9d46d6b..ffbe611 100644
--- a/ssl/ssl.h
+++ b/ssl/ssl.h
@@ -2259,6 +2259,7 @@
 #define SSL_F_TLS1_CHANGE_CIPHER_STATE			 209
 #define SSL_F_TLS1_CHECK_SERVERHELLO_TLSEXT		 274
 #define SSL_F_TLS1_ENC					 210
+#define SSL_F_TLS1_EXPORT_KEYING_MATERIAL		 312
 #define SSL_F_TLS1_PREPARE_CLIENTHELLO_TLSEXT		 275
 #define SSL_F_TLS1_PREPARE_SERVERHELLO_TLSEXT		 276
 #define SSL_F_TLS1_PRF					 284
@@ -2513,6 +2514,7 @@
 #define SSL_R_TLSV1_UNRECOGNIZED_NAME			 1112
 #define SSL_R_TLSV1_UNSUPPORTED_EXTENSION		 1110
 #define SSL_R_TLS_CLIENT_CERT_REQ_WITH_ANON_CIPHER	 232
+#define SSL_R_TLS_ILLEGAL_EXPORTER_LABEL		 367
 #define SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST		 157
 #define SSL_R_TLS_PEER_DID_NOT_RESPOND_WITH_CERTIFICATE_LIST 233
 #define SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG	 234
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index b899dcb..0426aaf 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -277,6 +277,7 @@
 {ERR_FUNC(SSL_F_TLS1_CHANGE_CIPHER_STATE),	"TLS1_CHANGE_CIPHER_STATE"},
 {ERR_FUNC(SSL_F_TLS1_CHECK_SERVERHELLO_TLSEXT),	"TLS1_CHECK_SERVERHELLO_TLSEXT"},
 {ERR_FUNC(SSL_F_TLS1_ENC),	"TLS1_ENC"},
+{ERR_FUNC(SSL_F_TLS1_EXPORT_KEYING_MATERIAL),	"TLS1_EXPORT_KEYING_MATERIAL"},
 {ERR_FUNC(SSL_F_TLS1_PREPARE_CLIENTHELLO_TLSEXT),	"TLS1_PREPARE_CLIENTHELLO_TLSEXT"},
 {ERR_FUNC(SSL_F_TLS1_PREPARE_SERVERHELLO_TLSEXT),	"TLS1_PREPARE_SERVERHELLO_TLSEXT"},
 {ERR_FUNC(SSL_F_TLS1_PRF),	"tls1_prf"},
@@ -534,6 +535,7 @@
 {ERR_REASON(SSL_R_TLSV1_UNRECOGNIZED_NAME),"tlsv1 unrecognized name"},
 {ERR_REASON(SSL_R_TLSV1_UNSUPPORTED_EXTENSION),"tlsv1 unsupported extension"},
 {ERR_REASON(SSL_R_TLS_CLIENT_CERT_REQ_WITH_ANON_CIPHER),"tls client cert req with anon cipher"},
+{ERR_REASON(SSL_R_TLS_ILLEGAL_EXPORTER_LABEL),"tls illegal exporter label"},
 {ERR_REASON(SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST),"tls invalid ecpointformat list"},
 {ERR_REASON(SSL_R_TLS_PEER_DID_NOT_RESPOND_WITH_CERTIFICATE_LIST),"tls peer did not respond with certificate list"},
 {ERR_REASON(SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG),"tls rsa encrypted value length is wrong"},
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 14664cf..df78447 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -177,7 +177,10 @@
 	0,	/* client_finished_label_len */
 	NULL,	/* server_finished_label */
 	0,	/* server_finished_label_len */
-	(int (*)(int))ssl_undefined_function
+	(int (*)(int))ssl_undefined_function,
+	(int (*)(SSL *, unsigned char *, unsigned int, const char *,
+		 unsigned int, const unsigned char *, unsigned int,
+		 int use_context))ssl_undefined_function,
 	};
 
 int SSL_clear(SSL *s)
@@ -1627,6 +1630,17 @@
 # endif
 #endif
 
+int SSL_export_keying_material(SSL *s, unsigned char *out, int olen, 
+        char *label, int llen, unsigned char *p, int plen, int use_context)
+	{
+	if (s->version < TLS1_VERSION)
+		return -1;
+
+	return s->method->ssl3_enc->export_keying_material(s, out, olen, label,
+							   llen, p, plen,
+							   use_context);
+	}
+
 static unsigned long ssl_session_hash(const SSL_SESSION *a)
 	{
 	unsigned long l;
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index e5eec19..bda365d 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -569,6 +569,10 @@
 	const char *server_finished_label;
 	int server_finished_label_len;
 	int (*alert_value)(int);
+        int (*export_keying_material)(SSL *, unsigned char *, unsigned int,
+				      const char *, unsigned int,
+				      const unsigned char *, unsigned int,
+				      int use_context);
 	} SSL3_ENC_METHOD;
 
 #ifndef OPENSSL_NO_COMP
@@ -1055,6 +1059,9 @@
 int tls1_mac(SSL *ssl, unsigned char *md, int snd);
 int tls1_generate_master_secret(SSL *s, unsigned char *out,
 	unsigned char *p, int len);
+int tls1_export_keying_material(SSL *s, unsigned char *out, unsigned int olen, 
+	const char *label, unsigned int llen, const unsigned char *p, 
+        unsigned int plen, int use_context);
 int tls1_alert_code(int code);
 int ssl3_alert_code(int code);
 int ssl_ok(SSL *s);
diff --git a/ssl/t1_enc.c b/ssl/t1_enc.c
index 3452b25..b9d0dd3 100644
--- a/ssl/t1_enc.c
+++ b/ssl/t1_enc.c
@@ -1119,6 +1119,95 @@
 	return(SSL3_MASTER_SECRET_SIZE);
 	}
 
+int tls1_export_keying_material(SSL *s, unsigned char *out, unsigned int olen, 
+         const char *label, unsigned int llen, const unsigned char *context, 
+         unsigned int contextlen, int use_context)
+	{
+	unsigned char *buff;
+	unsigned char *val;
+	unsigned int vallen, currentvalpos, rv;
+
+#ifdef KSSL_DEBUG
+	printf ("tls1_export_keying_material(%p, %p,%d, %s,%d, %p,%d)\n", s, out,olen, label,llen, p,plen);
+#endif	/* KSSL_DEBUG */
+
+	buff = OPENSSL_malloc(olen);
+	if (buff == NULL) goto err2;
+
+	/* construct PRF arguments
+	 * we construct the PRF argument ourself rather than passing separate
+	 * values into the TLS PRF to ensure that the concatenation of values
+	 * does not create a prohibited label.
+	 */
+	vallen = llen + SSL3_RANDOM_SIZE * 2;
+        if (use_context) 
+                {
+                vallen +=  2 + contextlen;
+                }
+
+	val = OPENSSL_malloc(vallen);
+	if (val == NULL) goto err2;
+	currentvalpos = 0;
+	memcpy(val + currentvalpos, (unsigned char *) label, llen);
+	currentvalpos += llen;
+	memcpy(val + currentvalpos, s->s3->client_random, SSL3_RANDOM_SIZE);
+	currentvalpos += SSL3_RANDOM_SIZE;
+	memcpy(val + currentvalpos, s->s3->server_random, SSL3_RANDOM_SIZE);
+	currentvalpos += SSL3_RANDOM_SIZE;
+
+        if (use_context)
+                {
+                val[currentvalpos] = (contextlen << 8) & 0xff;
+                currentvalpos++;
+                val[currentvalpos] = contextlen & 0xff;
+                currentvalpos++;
+                if ((contextlen > 0) || (context != NULL)) 
+                        {
+                        memcpy(val + currentvalpos, context, contextlen);
+                        }
+                }
+
+	/* disallow prohibited labels
+	 * note that SSL3_RANDOM_SIZE > max(prohibited label len) =
+	 * 15, so size of val > max(prohibited label len) = 15 and the
+	 * comparisons won't have buffer overflow
+	 */
+	if (bcmp(val, TLS_MD_CLIENT_FINISH_CONST,
+		 TLS_MD_CLIENT_FINISH_CONST_SIZE) == 0) goto err1;
+	if (bcmp(val, TLS_MD_SERVER_FINISH_CONST,
+		 TLS_MD_SERVER_FINISH_CONST_SIZE) == 0) goto err1;
+	if (bcmp(val, TLS_MD_MASTER_SECRET_CONST,
+		 TLS_MD_MASTER_SECRET_CONST_SIZE) == 0) goto err1;
+	if (bcmp(val, TLS_MD_KEY_EXPANSION_CONST,
+		 TLS_MD_KEY_EXPANSION_CONST_SIZE) == 0) goto err1;
+
+	tls1_PRF(s->s3->tmp.new_cipher->algorithm2,
+		val, vallen,
+		NULL, 0,
+		NULL, 0,
+		NULL, 0,
+		NULL, 0,
+		s->session->master_key,s->session->master_key_length,
+		out,buff,olen);
+
+#ifdef KSSL_DEBUG
+	printf ("tls1_export_keying_material() complete\n");
+#endif	/* KSSL_DEBUG */
+	rv = olen;
+	goto ret;
+err1:
+	SSLerr(SSL_F_TLS1_EXPORT_KEYING_MATERIAL, SSL_R_TLS_ILLEGAL_EXPORTER_LABEL);
+	rv = 0;
+	goto ret;
+err2:
+	SSLerr(SSL_F_TLS1_EXPORT_KEYING_MATERIAL, ERR_R_MALLOC_FAILURE);
+	rv = 0;
+ret:
+	if (buff != NULL) OPENSSL_free(buff);
+	if (val != NULL) OPENSSL_free(val);
+	return(rv);
+	}
+
 int tls1_alert_code(int code)
 	{
 	switch (code)
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 473bd94..7b54dc9 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -136,6 +136,7 @@
 	TLS_MD_CLIENT_FINISH_CONST,TLS_MD_CLIENT_FINISH_CONST_SIZE,
 	TLS_MD_SERVER_FINISH_CONST,TLS_MD_SERVER_FINISH_CONST_SIZE,
 	tls1_alert_code,
+	tls1_export_keying_material,
 	};
 
 long tls1_default_timeout(void)
diff --git a/ssl/tls1.h b/ssl/tls1.h
index f443bb1..545383a 100644
--- a/ssl/tls1.h
+++ b/ssl/tls1.h
@@ -262,8 +262,10 @@
 
 #define TLSEXT_MAXLEN_host_name 255
 
-const char *SSL_get_servername(const SSL *s, const int type) ;
-int SSL_get_servername_type(const SSL *s) ;
+const char *SSL_get_servername(const SSL *s, const int type);
+int SSL_get_servername_type(const SSL *s);
+int SSL_export_keying_material(SSL *s, unsigned char *out, int olen, 
+        char *label, int llen, unsigned char *p, int plen, int use_context);
 
 #define SSL_set_tlsext_host_name(s,name) \
 SSL_ctrl(s,SSL_CTRL_SET_TLSEXT_HOSTNAME,TLSEXT_NAMETYPE_host_name,(char *)name)