Add support for arbitrary TLS extensions.

Contributed by Trevor Perrin.
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index 7ad8a54..67eb920 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -3026,6 +3026,8 @@
 #ifndef OPENSSL_NO_TLSEXT
 	if (s->s3->tlsext_authz_client_types != NULL)
 		OPENSSL_free(s->s3->tlsext_authz_client_types);
+	if (s->s3->tlsext_custom_types != NULL)
+		OPENSSL_free(s->s3->tlsext_custom_types);
 #endif
 	OPENSSL_cleanse(s->s3,sizeof *s->s3);
 	OPENSSL_free(s->s3);
@@ -3076,6 +3078,12 @@
 		OPENSSL_free(s->s3->tlsext_authz_client_types);
 		s->s3->tlsext_authz_client_types = NULL;
 		}
+	if (s->s3->tlsext_custom_types != NULL)
+		{
+		OPENSSL_free(s->s3->tlsext_custom_types);
+		s->s3->tlsext_custom_types = NULL;
+		}
+	s->s3->tlsext_custom_types_count = 0;	
 #endif
 
 	rp = s->s3->rbuf.buf;
diff --git a/ssl/ssl.h b/ssl/ssl.h
index e14a8d4..67397cf 100644
--- a/ssl/ssl.h
+++ b/ssl/ssl.h
@@ -383,6 +383,56 @@
 typedef int (*tls_session_ticket_ext_cb_fn)(SSL *s, const unsigned char *data, int len, void *arg);
 typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg);
 
+#ifndef OPENSSL_NO_TLSEXT
+/* Callbacks and structures for handling custom TLS Extensions: 
+ *   cli_ext_first_cb  - sends data for ClientHello TLS Extension
+ *   cli_ext_second_cb - receives data from ServerHello TLS Extension
+ *   srv_ext_first_cb  - receives data from ClientHello TLS Extension
+ *   srv_ext_second_cb - sends data for ServerHello TLS Extension
+ *
+ *   All these functions return nonzero on success.  Zero will terminate
+ *   the handshake (and return a specific TLS Fatal alert, if the function
+ *   declaration has an "al" parameter).
+ * 
+ *   "ext_type" is a TLS "ExtensionType" from 0-65535.
+ *   "in" is a pointer to TLS "extension_data" being provided to the cb.
+ *   "out" is used by the callback to return a pointer to "extension data"
+ *     which OpenSSL will later copy into the TLS handshake.  The contents
+ *     of this buffer should not be changed until the handshake is complete.
+ *   "inlen" and "outlen" are TLS Extension lengths from 0-65535.
+ *   "al" is a TLS "AlertDescription" from 0-255 which WILL be sent as a 
+ *     fatal TLS alert, if the callback returns zero.
+ */
+typedef int (*custom_cli_ext_first_cb_fn)(SSL *s, unsigned short ext_type,
+					  const unsigned char **out,
+					  unsigned short *outlen, void *arg);
+typedef int (*custom_cli_ext_second_cb_fn)(SSL *s, unsigned short ext_type,
+					   const unsigned char *in,
+					   unsigned short inlen, int *al,
+					   void *arg); 
+
+typedef int (*custom_srv_ext_first_cb_fn)(SSL *s, unsigned short ext_type,
+					  const unsigned char *in,
+					  unsigned short inlen, int *al,
+					  void *arg);
+typedef int (*custom_srv_ext_second_cb_fn)(SSL *s, unsigned short ext_type,
+					   const unsigned char **out,
+					   unsigned short *outlen, void *arg); 
+
+typedef struct {
+	unsigned short ext_type;
+	custom_cli_ext_first_cb_fn fn1; 
+	custom_cli_ext_second_cb_fn fn2; 
+	void *arg;
+} custom_cli_ext_record;
+
+typedef struct {
+	unsigned short ext_type;
+	custom_srv_ext_first_cb_fn fn1; 
+	custom_srv_ext_second_cb_fn fn2; 
+	void *arg;
+} custom_srv_ext_record;
+#endif
 
 #ifndef OPENSSL_NO_SSL_INTERN
 
@@ -1064,6 +1114,12 @@
 # endif /* OPENSSL_NO_EC */
 	int (*tlsext_authz_server_audit_proof_cb)(SSL *s, void *arg);
 	void *tlsext_authz_server_audit_proof_cb_arg;
+
+	/* Arrays containing the callbacks for custom TLS Extensions. */
+	custom_cli_ext_record *custom_cli_ext_records;
+	size_t custom_cli_ext_records_count;
+	custom_srv_ext_record *custom_srv_ext_records;
+	size_t custom_srv_ext_records_count;
 	};
 
 #endif
@@ -1170,6 +1226,33 @@
 const char *SSL_get_psk_identity(const SSL *s);
 #endif
 
+#ifndef OPENSSL_NO_TLSEXT
+/* Register callbacks to handle custom TLS Extensions as client or server.
+ * 
+ * Returns nonzero on success.  You cannot register twice for the same 
+ * extension number, and registering for an extension number already 
+ * handled by OpenSSL will succeed, but the callbacks will not be invoked.
+ *
+ * NULL can be registered for any callback function.  For the client
+ * functions, a NULL custom_cli_ext_first_cb_fn sends an empty ClientHello
+ * Extension, and a NULL custom_cli_ext_second_cb_fn ignores the ServerHello
+ * response (if any).
+ *
+ * For the server functions, a NULL custom_srv_ext_first_cb_fn means the
+ * ClientHello extension's data will be ignored, but the extension will still
+ * be noted and custom_srv_ext_second_cb_fn will still be invoked.  If 
+ * custom_srv_ext_second_cb_fn is NULL, an empty ServerHello extension is 
+ * sent.
+ */
+int SSL_CTX_set_custom_cli_ext(SSL_CTX *ctx, unsigned short ext_type,
+			       custom_cli_ext_first_cb_fn fn1, 
+			       custom_cli_ext_second_cb_fn fn2, void *arg);
+
+int SSL_CTX_set_custom_srv_ext(SSL_CTX *ctx, unsigned short ext_type,
+			       custom_srv_ext_first_cb_fn fn1, 
+			       custom_srv_ext_second_cb_fn fn2, void *arg);
+#endif
+
 #define SSL_NOTHING	1
 #define SSL_WRITING	2
 #define SSL_READING	3
@@ -1934,6 +2017,14 @@
 int	SSL_CTX_use_authz_file(SSL_CTX *ctx, const char *file);
 int	SSL_use_authz_file(SSL *ssl, const char *file);
 #endif
+
+/* Set serverinfo data for the current active cert. */
+int	SSL_CTX_use_serverinfo(SSL_CTX *ctx, const unsigned char *serverinfo,
+			       size_t serverinfo_length);
+#ifndef OPENSSL_NO_STDIO
+int	SSL_CTX_use_serverinfo_file(SSL_CTX *ctx, const char *file);
+#endif /* NO_STDIO */
+
 #endif
 
 #ifndef OPENSSL_NO_STDIO
@@ -2481,6 +2572,8 @@
 #define SSL_F_SSL_CTX_USE_RSAPRIVATEKEY			 177
 #define SSL_F_SSL_CTX_USE_RSAPRIVATEKEY_ASN1		 178
 #define SSL_F_SSL_CTX_USE_RSAPRIVATEKEY_FILE		 179
+#define SSL_F_SSL_CTX_USE_SERVERINFO			 336
+#define SSL_F_SSL_CTX_USE_SERVERINFO_FILE		 337
 #define SSL_F_SSL_DO_HANDSHAKE				 180
 #define SSL_F_SSL_GET_NEW_SESSION			 181
 #define SSL_F_SSL_GET_PREV_SESSION			 217
@@ -2655,6 +2748,7 @@
 #define SSL_R_INVALID_COMPRESSION_ALGORITHM		 341
 #define SSL_R_INVALID_NULL_CMD_NAME			 385
 #define SSL_R_INVALID_PURPOSE				 278
+#define SSL_R_INVALID_SERVERINFO_DATA			 388
 #define SSL_R_INVALID_SRP_USERNAME			 357
 #define SSL_R_INVALID_STATUS_RESPONSE			 328
 #define SSL_R_INVALID_TICKET_KEYS_LENGTH		 325
diff --git a/ssl/ssl3.h b/ssl/ssl3.h
index d8ed725..77747c4 100644
--- a/ssl/ssl3.h
+++ b/ssl/ssl3.h
@@ -577,6 +577,15 @@
 	 * server echoed our server_authz extension and therefore must send us
 	 * a supplemental data handshake message. */
 	char tlsext_authz_server_promised;
+
+	/* tlsext_custom_types contains an array of TLS Extension types which 
+	 * were advertised by the client in its ClientHello, which were not 
+	 * otherwise handled by OpenSSL, and which the server has registered
+	 * a custom_srv_ext_record to handle.
+	 * The array does not contain any duplicates, and is in the same order
+	 * as the types were received in the client hello. */
+	unsigned short *tlsext_custom_types;
+	size_t tlsext_custom_types_count; /* how many tlsext_custom_types */
 #endif
 	} SSL3_STATE;
 
diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c
index a0da7d3..f86511d 100644
--- a/ssl/ssl_cert.c
+++ b/ssl/ssl_cert.c
@@ -329,7 +329,8 @@
 				}
 			}
 		rpk->valid_flags = 0;
-                if (cert->pkeys[i].authz != NULL)
+#ifndef OPENSSL_NO_TLSEXT
+     if (cert->pkeys[i].authz != NULL)
 			{
 			/* Just copy everything. */
 			ret->pkeys[i].authz_length =
@@ -339,12 +340,30 @@
 			if (ret->pkeys[i].authz == NULL)
 				{
 				SSLerr(SSL_F_SSL_CERT_DUP, ERR_R_MALLOC_FAILURE);
-				return(NULL);
+				return NULL;
 				}
 			memcpy(ret->pkeys[i].authz,
 			       cert->pkeys[i].authz,
 			       cert->pkeys[i].authz_length);
 			}
+
+		if (cert->pkeys[i].serverinfo != NULL)
+			{
+			/* Just copy everything. */
+			ret->pkeys[i].serverinfo_length =
+				cert->pkeys[i].serverinfo_length;
+			ret->pkeys[i].serverinfo =
+				OPENSSL_malloc(ret->pkeys[i].serverinfo_length);
+			if (ret->pkeys[i].serverinfo == NULL)
+				{
+				SSLerr(SSL_F_SSL_CERT_DUP, ERR_R_MALLOC_FAILURE);
+				return NULL;
+				}
+			memcpy(ret->pkeys[i].serverinfo,
+			       cert->pkeys[i].serverinfo,
+			       cert->pkeys[i].serverinfo_length);
+			}
+#endif
 		}
 	
 	ret->references=1;
@@ -460,8 +479,16 @@
 			cpk->chain = NULL;
 			}
 #ifndef OPENSSL_NO_TLSEXT
-                if (cpk->authz != NULL)
+		if (cpk->authz)
+			{
 			OPENSSL_free(cpk->authz);
+			cpk->authz = NULL;
+			}
+		if (cpk->serverinfo)
+			{
+			OPENSSL_free(cpk->serverinfo);
+			cpk->serverinfo = NULL;
+			}
 #endif
 		/* Clear all flags apart from explicit sign */
 		cpk->valid_flags &= CERT_PKEY_EXPLICIT_SIGN;
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index d8b4d70..2a6ae5b 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -233,6 +233,8 @@
 {ERR_FUNC(SSL_F_SSL_CTX_USE_RSAPRIVATEKEY),	"SSL_CTX_use_RSAPrivateKey"},
 {ERR_FUNC(SSL_F_SSL_CTX_USE_RSAPRIVATEKEY_ASN1),	"SSL_CTX_use_RSAPrivateKey_ASN1"},
 {ERR_FUNC(SSL_F_SSL_CTX_USE_RSAPRIVATEKEY_FILE),	"SSL_CTX_use_RSAPrivateKey_file"},
+{ERR_FUNC(SSL_F_SSL_CTX_USE_SERVERINFO),	"SSL_CTX_use_serverinfo"},
+{ERR_FUNC(SSL_F_SSL_CTX_USE_SERVERINFO_FILE),	"SSL_CTX_use_serverinfo_file"},
 {ERR_FUNC(SSL_F_SSL_DO_HANDSHAKE),	"SSL_do_handshake"},
 {ERR_FUNC(SSL_F_SSL_GET_NEW_SESSION),	"ssl_get_new_session"},
 {ERR_FUNC(SSL_F_SSL_GET_PREV_SESSION),	"ssl_get_prev_session"},
@@ -410,6 +412,7 @@
 {ERR_REASON(SSL_R_INVALID_COMPRESSION_ALGORITHM),"invalid compression algorithm"},
 {ERR_REASON(SSL_R_INVALID_NULL_CMD_NAME) ,"invalid null cmd name"},
 {ERR_REASON(SSL_R_INVALID_PURPOSE)       ,"invalid purpose"},
+{ERR_REASON(SSL_R_INVALID_SERVERINFO_DATA),"invalid serverinfo data"},
 {ERR_REASON(SSL_R_INVALID_SRP_USERNAME)  ,"invalid srp username"},
 {ERR_REASON(SSL_R_INVALID_STATUS_RESPONSE),"invalid status response"},
 {ERR_REASON(SSL_R_INVALID_TICKET_KEYS_LENGTH),"invalid ticket keys length"},
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index c3b4032..b08e826 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -1697,6 +1697,61 @@
 	ctx->next_proto_select_cb_arg = arg;
 	}
 # endif
+
+int SSL_CTX_set_custom_cli_ext(SSL_CTX *ctx, unsigned short ext_type,
+			       custom_cli_ext_first_cb_fn fn1, 
+			       custom_cli_ext_second_cb_fn fn2, void* arg)
+	{
+	/* Check for duplicates */
+	size_t i;
+	custom_cli_ext_record* record;
+
+	for (i=0; i < ctx->custom_cli_ext_records_count; i++)
+		if (ext_type == ctx->custom_cli_ext_records[i].ext_type)
+			return 0;
+
+	ctx->custom_cli_ext_records = OPENSSL_realloc(ctx->custom_cli_ext_records,
+														(ctx->custom_cli_ext_records_count+1) * sizeof(custom_cli_ext_record));
+	if (!ctx->custom_cli_ext_records) {
+		ctx->custom_cli_ext_records_count = 0;
+		return 0;
+	}
+	ctx->custom_cli_ext_records_count++;
+	record = &ctx->custom_cli_ext_records[ctx->custom_cli_ext_records_count - 1];
+	record->ext_type = ext_type;
+	record->fn1 = fn1;
+	record->fn2 = fn2;
+	record->arg = arg;
+	return 1;
+	}
+
+int SSL_CTX_set_custom_srv_ext(SSL_CTX *ctx, unsigned short ext_type,
+															 custom_srv_ext_first_cb_fn fn1, 
+															 custom_srv_ext_second_cb_fn fn2, void* arg)
+	{
+	/* Check for duplicates */
+	size_t i;
+	custom_srv_ext_record* record;
+
+	for (i=0; i < ctx->custom_srv_ext_records_count; i++)
+		if (ext_type == ctx->custom_srv_ext_records[i].ext_type)
+			return 0;
+
+	ctx->custom_srv_ext_records = OPENSSL_realloc(ctx->custom_srv_ext_records,
+														(ctx->custom_srv_ext_records_count+1) * sizeof(custom_srv_ext_record));
+	if (!ctx->custom_srv_ext_records) {
+		ctx->custom_srv_ext_records_count = 0;
+		return 0;
+	}
+	ctx->custom_srv_ext_records_count++;
+	record = &ctx->custom_srv_ext_records[ctx->custom_srv_ext_records_count - 1];
+	record->ext_type = ext_type;
+	record->fn1 = fn1;
+	record->fn2 = fn2;
+	record->arg = arg;
+	return 1;
+	}
+
 #endif
 
 int SSL_export_keying_material(SSL *s, unsigned char *out, size_t olen,
@@ -1896,6 +1951,10 @@
 #ifndef OPENSSL_NO_SRP
 	SSL_CTX_SRP_CTX_init(ret);
 #endif
+	ret->custom_cli_ext_records = NULL;
+	ret->custom_cli_ext_records_count = 0;
+	ret->custom_srv_ext_records = NULL;
+	ret->custom_srv_ext_records_count = 0;
 #ifndef OPENSSL_NO_BUF_FREELISTS
 	ret->freelist_max_len = SSL_MAX_BUF_FREELIST_LEN_DEFAULT;
 	ret->rbuf_freelist = OPENSSL_malloc(sizeof(SSL3_BUF_FREELIST));
@@ -2034,6 +2093,10 @@
 #ifndef OPENSSL_NO_SRP
 	SSL_CTX_SRP_CTX_free(a);
 #endif
+#ifndef OPENSSL_NO_TLSEXT
+	OPENSSL_free(a->custom_cli_ext_records);
+	OPENSSL_free(a->custom_srv_ext_records);
+#endif
 #ifndef OPENSSL_NO_ENGINE
 	if (a->client_cert_engine)
 		ENGINE_finish(a->client_cert_engine);
@@ -2479,6 +2542,26 @@
 
 	return c->pkeys[i].authz;
 	}
+
+int ssl_get_server_cert_serverinfo(SSL *s, const unsigned char **serverinfo,
+				   size_t *serverinfo_length)
+	{
+	CERT *c = NULL;
+	int i = 0;
+	*serverinfo_length = 0;
+
+	c = s->cert;
+	i = ssl_get_server_cert_index(s);
+
+	if (i == -1)
+		return 0;
+	if (c->pkeys[i].serverinfo == NULL)
+		return 0;
+
+	*serverinfo = c->pkeys[i].serverinfo;
+	*serverinfo_length = c->pkeys[i].serverinfo_length;
+	return 1;
+	}
 #endif
 
 void ssl_update_cache(SSL *s,int mode)
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index d06f912..56f9b4b 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -510,6 +510,14 @@
 	 *   uint8_t data[length]; */
 	unsigned char *authz;
 	size_t authz_length;
+
+	/* serverinfo data for this certificate.  The data is in TLS Extension
+	 * wire format, specifically it's a series of records like:
+	 *   uint16_t extension_type; // (RFC 5246, 7.4.1.4, Extension)
+	 *   uint16_t length;
+	 *   uint8_t data[length]; */
+	unsigned char *serverinfo;
+	size_t serverinfo_length;
 #endif
 	/* Set if CERT_PKEY can be used with current SSL session: e.g.
 	 * appropriate curve, signature algorithms etc. If zero it can't be
@@ -1000,7 +1008,11 @@
 int ssl_undefined_void_function(void);
 int ssl_undefined_const_function(const SSL *s);
 CERT_PKEY *ssl_get_server_send_pkey(const SSL *s);
+#ifndef OPENSSL_NO_TLSEXT
 unsigned char *ssl_get_authz_data(SSL *s, size_t *authz_length);
+int ssl_get_server_cert_serverinfo(SSL *s, const unsigned char **serverinfo,
+				   size_t *serverinfo_length);
+#endif
 EVP_PKEY *ssl_get_sign_pkey(SSL *s,const SSL_CIPHER *c, const EVP_MD **pmd);
 int ssl_cert_type(X509 *x,EVP_PKEY *pkey);
 void ssl_set_cert_masks(CERT *c, const SSL_CIPHER *cipher);
diff --git a/ssl/ssl_rsa.c b/ssl/ssl_rsa.c
index 1babdef..ac16b85 100644
--- a/ssl/ssl_rsa.c
+++ b/ssl/ssl_rsa.c
@@ -471,6 +471,14 @@
 		c->pkeys[i].authz = NULL;
 		c->pkeys[i].authz_length = 0;
 		}
+
+	/* Free the old serverinfo data, if it exists. */
+	if (c->pkeys[i].serverinfo != NULL)
+		{
+		OPENSSL_free(c->pkeys[i].serverinfo);
+		c->pkeys[i].serverinfo = NULL;
+		c->pkeys[i].serverinfo_length = 0;
+		}
 #endif
 	c->key= &(c->pkeys[i]);
 
@@ -855,6 +863,116 @@
 		}
 	}
 
+static int serverinfo_find_extension(const unsigned char *serverinfo,
+				     size_t serverinfo_length,
+				     unsigned short extension_type,
+				     const unsigned char **extension_data,
+				     unsigned short *extension_length)
+	{
+	*extension_data = NULL;
+	*extension_length = 0;
+	if (serverinfo == NULL || serverinfo_length == 0)
+		return 0;
+	for (;;)
+		{
+		unsigned short type = 0; /* uint16 */
+		unsigned short len = 0;  /* uint16 */
+
+		/* end of serverinfo */
+		if (serverinfo_length == 0)
+			return 0;
+
+		/* read 2-byte type field */
+		if (serverinfo_length < 2)
+			return 0;	/* error */
+		type = (serverinfo[0] << 8) + serverinfo[1];
+		serverinfo += 2;
+		serverinfo_length -= 2;
+
+		/* read 2-byte len field */
+		if (serverinfo_length < 2)
+			return 0;	/* error */
+		len = (serverinfo[0] << 8) + serverinfo[1];
+		serverinfo += 2;
+		serverinfo_length -= 2;
+
+		if (len > serverinfo_length)
+			return 0;	/* error */
+
+		if (type == extension_type)
+			{
+			*extension_data = serverinfo;
+			*extension_length = len;
+			return 1;
+			}
+
+		serverinfo += len;
+		serverinfo_length -= len;
+		}
+	return 0;
+	}
+
+static int serverinfo_srv_cb(SSL *s, unsigned short ext_type,
+			     const unsigned char **out, unsigned short *outlen, 
+			     void *arg)
+	{
+	const unsigned char *serverinfo = NULL;
+	size_t serverinfo_length = 0;
+
+	/* Is there a serverinfo for the chosen server cert? */
+	if ((ssl_get_server_cert_serverinfo(s, &serverinfo,
+					    &serverinfo_length)) != 0)
+		{
+		/* Find the relevant extension from the serverinfo */
+		serverinfo_find_extension(serverinfo, serverinfo_length,
+					  ext_type, out, outlen);
+		}
+		return 1;
+	}
+
+static int serverinfo_validate(const unsigned char *serverinfo, 
+			       size_t serverinfo_length, SSL_CTX *ctx)
+	{
+	if (serverinfo == NULL || serverinfo_length == 0)
+		return 0;
+	for (;;)
+		{
+		unsigned short ext_type = 0; /* uint16 */
+		unsigned short len = 0;  /* uint16 */
+
+		/* end of serverinfo */
+		if (serverinfo_length == 0)
+			return 1;
+
+		/* read 2-byte type field */
+		if (serverinfo_length < 2)
+			return 0;
+		/* FIXME: check for types we understand explicitly? */
+
+		/* Register callbacks for extensions */
+		ext_type = (serverinfo[0] << 8) + serverinfo[1];
+		if (ctx && !SSL_CTX_set_custom_srv_ext(ctx, ext_type, NULL,
+						       serverinfo_srv_cb, NULL))
+			return 0;
+
+		serverinfo += 2;
+		serverinfo_length -= 2;
+
+		/* read 2-byte len field */
+		if (serverinfo_length < 2)
+			return 0;
+		len = (serverinfo[0] << 8) + serverinfo[1];
+		serverinfo += 2;
+		serverinfo_length -= 2;
+
+		if (len > serverinfo_length)
+			return 0;
+
+		serverinfo += len;
+		serverinfo_length -= len;
+		}
+	}
+
 static const unsigned char *authz_find_data(const unsigned char *authz,
 					    size_t authz_length,
 					    unsigned char data_type,
@@ -906,13 +1024,18 @@
 		return(0);
 		}
 	current_key->authz = OPENSSL_realloc(current_key->authz, authz_length);
+	if (current_key->authz == NULL)
+		{
+		SSLerr(SSL_F_SSL_SET_AUTHZ,ERR_R_MALLOC_FAILURE);
+		return 0;
+		}
 	current_key->authz_length = authz_length;
 	memcpy(current_key->authz, authz, authz_length);
 	return 1;
 	}
 
 int SSL_CTX_use_authz(SSL_CTX *ctx, unsigned char *authz,
-	size_t authz_length)
+		      size_t authz_length)
 	{
 	if (authz == NULL)
 		{
@@ -927,6 +1050,49 @@
 	return ssl_set_authz(ctx->cert, authz, authz_length);
 	}
 
+int SSL_CTX_use_serverinfo(SSL_CTX *ctx, const unsigned char *serverinfo,
+			   size_t serverinfo_length)
+	{
+	if (ctx == NULL || serverinfo == NULL || serverinfo_length == 0)
+		{
+		SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO,ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+		}
+	if (!serverinfo_validate(serverinfo, serverinfo_length, NULL))
+		{
+		SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO,SSL_R_INVALID_SERVERINFO_DATA);
+		return(0);
+		}
+	if (!ssl_cert_inst(&ctx->cert))
+		{
+		SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO,ERR_R_MALLOC_FAILURE);
+		return 0;
+		}
+	if (ctx->cert->key == NULL)
+		{
+		SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO,ERR_R_INTERNAL_ERROR);
+		return 0;
+		}
+	ctx->cert->key->serverinfo = OPENSSL_realloc(ctx->cert->key->serverinfo,
+												serverinfo_length);
+	if (ctx->cert->key->serverinfo == NULL)
+		{
+		SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO,ERR_R_MALLOC_FAILURE);
+		return 0;
+		}
+	memcpy(ctx->cert->key->serverinfo, serverinfo, serverinfo_length);
+	ctx->cert->key->serverinfo_length = serverinfo_length;
+
+	/* Now that the serverinfo is validated and stored, go ahead and 
+	 * register callbacks. */
+	if (!serverinfo_validate(serverinfo, serverinfo_length, ctx))
+		{
+		SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO,SSL_R_INVALID_SERVERINFO_DATA);
+		return(0);
+		}
+	return 1;
+	}
+
 int SSL_use_authz(SSL *ssl, unsigned char *authz, size_t authz_length)
 	{
 	if (authz == NULL)
@@ -1026,5 +1192,81 @@
 	OPENSSL_free(authz);
 	return ret;
 	}
+
+int SSL_CTX_use_serverinfo_file(SSL_CTX *ctx, const char *file)
+	{
+	unsigned char *serverinfo = NULL;
+	size_t serverinfo_length = 0;
+	unsigned char* extension = 0;
+	long extension_length = 0;
+	char* name = NULL;
+	char* header = NULL;
+	int ret = 0;
+	BIO *bin = NULL;
+	size_t num_extensions = 0;
+
+	if (ctx == NULL || file == NULL)
+		{
+		SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE,ERR_R_PASSED_NULL_PARAMETER);
+		goto end;
+		}
+
+	bin = BIO_new(BIO_s_file_internal());
+	if (bin == NULL)
+		{
+		SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, ERR_R_BUF_LIB);
+		goto end;
+		}
+	if (BIO_read_filename(bin, file) <= 0)
+		{
+		SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, ERR_R_SYS_LIB);
+		goto end;
+		}
+
+	for (num_extensions=0;; num_extensions++)
+		{
+		if (PEM_read_bio(bin, &name, &header, &extension, &extension_length) == 0)
+			{
+			/* There must be at least one extension in this file */
+			if (num_extensions == 0)
+				{
+				SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, ERR_R_PEM_LIB);
+				goto end;
+				}
+			else /* End of file, we're done */
+				break;
+			}
+		/* Check that the decoded PEM data is plausible (valid length field) */
+		if (extension_length < 4 || (extension[2] << 8) + extension[3] != extension_length - 4)
+			{
+				SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, ERR_R_PEM_LIB);
+				goto end;
+			}
+		/* Append the decoded extension to the serverinfo buffer */
+		serverinfo = OPENSSL_realloc(serverinfo, serverinfo_length + extension_length);
+		if (serverinfo == NULL)
+			{
+			SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, ERR_R_MALLOC_FAILURE);
+			goto end;
+			}
+		memcpy(serverinfo + serverinfo_length, extension, extension_length);
+		serverinfo_length += extension_length;
+
+		OPENSSL_free(name); name = NULL;
+		OPENSSL_free(header); header = NULL;
+		OPENSSL_free(extension); extension = NULL;
+		}
+
+	ret = SSL_CTX_use_serverinfo(ctx, serverinfo, serverinfo_length);
+end:
+	/* SSL_CTX_use_serverinfo makes a local copy of the serverinfo. */
+	OPENSSL_free(name);
+	OPENSSL_free(header);
+	OPENSSL_free(extension);
+	OPENSSL_free(serverinfo);
+	if (bin != NULL)
+		BIO_free(bin);
+	return ret;
+	}
 #endif /* OPENSSL_NO_STDIO */
 #endif /* OPENSSL_NO_TLSEXT */
diff --git a/ssl/ssltest.c b/ssl/ssltest.c
index db87b1a..f67c7ea 100644
--- a/ssl/ssltest.c
+++ b/ssl/ssltest.c
@@ -370,6 +370,41 @@
 	}
 #endif
 
+#define SCT_EXT_TYPE 18
+#define TACK_EXT_TYPE 62208
+
+/* These set from cmdline */
+char* serverinfo_file = NULL;
+int serverinfo_sct = 0;
+int serverinfo_tack = 0;
+int serverinfo_sct_seen = 0;
+int serverinfo_tack_seen = 0;
+int serverinfo_other_seen = 0;
+
+static int serverinfo_cli_cb(SSL* s, unsigned short ext_type,
+												     const unsigned char* in, unsigned short inlen, 
+												     int* al, void* arg)
+	{
+		if (ext_type == SCT_EXT_TYPE)
+			serverinfo_sct_seen++;
+		else if (ext_type == TACK_EXT_TYPE)
+			serverinfo_tack_seen++;
+		else
+			serverinfo_other_seen++;
+		return 1;
+	}
+
+static int verify_serverinfo()
+	{
+	if (serverinfo_sct != serverinfo_sct_seen)
+		return -1;
+	if (serverinfo_tack != serverinfo_tack_seen)
+		return -1;
+	if (serverinfo_other_seen)
+		return -1;
+	return 0;
+	}
+
 static char *cipher=NULL;
 static int verbose=0;
 static int debug=0;
@@ -449,6 +484,9 @@
 	fprintf(stderr," -npn_server - have server side offer NPN\n");
 	fprintf(stderr," -npn_server_reject - have server reject NPN\n");
 #endif
+	fprintf(stderr," -serverinfo_file - have server use this file\n");
+	fprintf(stderr," -serverinfo_sct  - have client offer and expect SCT\n");
+	fprintf(stderr," -serverinfo_tack - have client offer and expect TACK\n");
 	}
 
 static void print_details(SSL *c_ssl, const char *prefix)
@@ -861,6 +899,19 @@
 			npn_server_reject = 1;
 			}
 #endif
+		else if (strcmp(*argv,"-serverinfo_sct") == 0)
+			{
+			serverinfo_sct = 1;
+			}
+		else if (strcmp(*argv,"-serverinfo_tack") == 0)
+			{
+			serverinfo_tack = 1;
+			}
+		else if (strcmp(*argv,"-serverinfo_file") == 0)
+			{
+			if (--argc < 1) goto bad;
+			serverinfo_file = *(++argv);
+			}
 		else
 			{
 			fprintf(stderr,"unknown option %s\n",*argv);
@@ -1186,6 +1237,18 @@
 		}
 #endif
 
+	if (serverinfo_sct)
+		SSL_CTX_set_custom_cli_ext(c_ctx, SCT_EXT_TYPE, NULL, serverinfo_cli_cb, NULL);
+	if (serverinfo_tack)
+		SSL_CTX_set_custom_cli_ext(c_ctx, TACK_EXT_TYPE, NULL, serverinfo_cli_cb, NULL);
+
+	if (serverinfo_file)
+		if (!SSL_CTX_use_serverinfo_file(s_ctx, serverinfo_file))
+			{
+			BIO_printf(bio_err, "missing serverinfo file\n");
+			goto end;
+			}
+
 	c_ssl=SSL_new(c_ctx);
 	s_ssl=SSL_new(s_ctx);
 
@@ -1643,6 +1706,12 @@
 		goto end;
 		}
 #endif
+	if (verify_serverinfo() < 0)
+		{
+		ret = 1;
+		goto err;
+		}
+
 end:
 	ret = 0;
 
@@ -1945,6 +2014,11 @@
 		goto err;
 		}
 #endif
+	if (verify_serverinfo() < 0)
+		{
+		ret = 1;
+		goto err;
+		}
 	ret=0;
 err:
 	/* We have to set the BIO's to NULL otherwise they will be
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 31daa50..19769c5 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -1443,6 +1443,31 @@
 		*(ret++) = TLSEXT_AUTHZDATAFORMAT_audit_proof;
 		}
 
+	/* Add custom TLS Extensions to ClientHello */
+	if (s->ctx->custom_cli_ext_records_count)
+		{
+		size_t i;
+		custom_cli_ext_record* record;
+
+		for (i = 0; i < s->ctx->custom_cli_ext_records_count; i++)
+			{
+			const unsigned char* out = NULL;
+			unsigned short outlen = 0;
+
+			record = &s->ctx->custom_cli_ext_records[i];
+			if (record->fn1 && !record->fn1(s, record->ext_type,
+							&out, &outlen,
+							record->arg))
+				return NULL;
+			if (limit < ret + 4 + outlen)
+				return NULL;
+			s2n(record->ext_type, ret);
+			s2n(outlen, ret);
+			memcpy(ret, out, outlen);
+			ret += outlen;
+			}
+		}
+
 	if ((extdatalen = ret-p-2) == 0)
 		return p;
 
@@ -1709,6 +1734,40 @@
 			}
 		}
 
+	/* If custom types were sent in ClientHello, add ServerHello responses */
+	if (s->s3->tlsext_custom_types_count)
+		{
+		size_t i;
+
+		for (i = 0; i < s->s3->tlsext_custom_types_count; i++)
+			{
+			size_t j;
+			custom_srv_ext_record *record;
+
+			for (j = 0; j < s->ctx->custom_srv_ext_records_count; j++)
+				{
+				record = &s->ctx->custom_srv_ext_records[j];
+				if (s->s3->tlsext_custom_types[i] == record->ext_type)
+					{
+					const unsigned char *out = NULL;
+					unsigned short outlen = 0;
+					if (record->fn2
+					    && !record->fn2(s, record->ext_type,
+							    &out, &outlen,
+							    record->arg))
+						return NULL;
+					if (limit < ret + 4 + outlen)
+						return NULL;
+					s2n(record->ext_type, ret);
+					s2n(outlen, ret);
+					memcpy(ret, out, outlen);
+					ret += outlen;
+					break;
+					}
+				}
+			}
+		}
+
 	if ((extdatalen = ret-p-2)== 0) 
 		return p;
 
@@ -2274,6 +2333,54 @@
 				}
 			}
 
+		/* If this ClientHello extension was unhandled and this is 
+		 * a nonresumed connection, check whether the extension is a 
+		 * custom TLS Extension (has a custom_srv_ext_record), and if
+		 * so call the callback and record the extension number so that
+		 * an appropriate ServerHello may be later returned.
+		 */
+		else if (!s->hit && s->ctx->custom_srv_ext_records_count)
+			{
+			custom_srv_ext_record *record;
+
+			for (i=0; i < s->ctx->custom_srv_ext_records_count; i++)
+				{
+				record = &s->ctx->custom_srv_ext_records[i];
+				if (type == record->ext_type)
+					{
+					/* Error on duplicate TLS Extensions */
+					size_t j;
+
+					for (j = 0; j < s->s3->tlsext_custom_types_count; j++)
+						{
+						if (s->s3->tlsext_custom_types[j] == type)
+							{
+							*al = TLS1_AD_DECODE_ERROR;
+							return 0;
+							}
+						}
+
+					/* Callback */
+					if (record->fn1 && !record->fn1(s, type, data, size, al, record->arg))
+						return 0;
+						
+					/* Add the (non-duplicated) entry */
+					s->s3->tlsext_custom_types_count++;
+					s->s3->tlsext_custom_types = OPENSSL_realloc(
+							s->s3->tlsext_custom_types,
+							s->s3->tlsext_custom_types_count*2);
+					if (s->s3->tlsext_custom_types == NULL)
+						{
+						s->s3->tlsext_custom_types = 0;
+						*al = TLS1_AD_INTERNAL_ERROR;
+						return 0;
+						}
+					s->s3->tlsext_custom_types[
+							s->s3->tlsext_custom_types_count-1] = type;
+					}						
+				}
+			}
+
 		data+=size;
 		}
 
@@ -2578,6 +2685,26 @@
 
 			s->s3->tlsext_authz_server_promised = 1;
 			}
+
+		/* If this extension type was not otherwise handled, but 
+		 * matches a custom_cli_ext_record, then send it to the c
+		 * callback */
+		else if (s->ctx->custom_cli_ext_records_count)
+			{
+			size_t i;
+			custom_cli_ext_record* record;
+
+			for (i = 0; i < s->ctx->custom_cli_ext_records_count; i++)
+				{
+				record = &s->ctx->custom_cli_ext_records[i];
+				if (record->ext_type == type)
+					{
+					if (record->fn2 && !record->fn2(s, type, data, size, al, record->arg))
+						return 0;
+					break;
+					}
+				}			
+			}
  
 		data += size;
 		}