From: Dr. Stephen Henson <steve@openssl.org>
Date: Sat, 11 Aug 2007 23:18:29 +0000 (+0000)
Subject: RFC4507 (including RFC4507bis) TLS stateless session resumption support
X-Git-Tag: OpenSSL_0_9_8k^2~751
X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=6434abbfc6ac0d5cb882844ed10fef5821039cf6;p=oweals%2Fopenssl.git

RFC4507 (including RFC4507bis) TLS stateless session resumption support
for OpenSSL.
---

diff --git a/CHANGES b/CHANGES
index 7ff2ec4299..9a16133f03 100644
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,24 @@
 
  Changes between 0.9.8f and 0.9.9  [xx XXX xxxx]
 
+  *) Add RFC4507 support to OpenSSL. This includes the corrections in
+     RFC4507bis. The encrypted ticket format is an encrypted encoded
+     SSL_SESSION structure, that way new session features are automatically
+     supported.
+
+     If a client application caches session in an SSL_SESSION support it
+     should automatically be supported because an extension includes the
+     ticket in the structure. The SSL_CTX structure automatically generates
+     keys for ticket protection in servers so again support should be possible
+     with no application modification.
+
+     If a client or server wishes to disable RFC4507 support then the option
+     SSL_OP_NO_TICKET can be set.
+
+     Add a TLS extension debugging callback to allow the contents of any client
+     or server extensions to be examined.
+     [Steve Henson]
+
   *) Final changes to avoid use of pointer pointer casts in OpenSSL.
      OpenSSL should now compile cleanly on gcc 4.2
      [Peter Hartley <pdh@utter.chaos.org.uk>, Steve Henson]
diff --git a/apps/s_apps.h b/apps/s_apps.h
index 886a95a2b8..08fbbc2229 100644
--- a/apps/s_apps.h
+++ b/apps/s_apps.h
@@ -167,4 +167,7 @@ long MS_CALLBACK bio_dump_callback(BIO *bio, int cmd, const char *argp,
 #ifdef HEADER_SSL_H
 void MS_CALLBACK apps_ssl_info_callback(const SSL *s, int where, int ret);
 void MS_CALLBACK msg_cb(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg);
+void MS_CALLBACK tlsext_cb(SSL *s, int client_server, int type,
+					unsigned char *data, int len,
+					void *arg);
 #endif
diff --git a/apps/s_cb.c b/apps/s_cb.c
index 6d322d4f40..fb33dee287 100644
--- a/apps/s_cb.c
+++ b/apps/s_cb.c
@@ -592,3 +592,62 @@ void MS_CALLBACK msg_cb(int write_p, int version, int content_type, const void *
 		}
 	BIO_flush(bio);
 	}
+
+void MS_CALLBACK tlsext_cb(SSL *s, int client_server, int type,
+					unsigned char *data, int len,
+					void *arg)
+	{
+	BIO *bio = arg;
+	char *extname;
+
+	switch(type)
+		{
+		case TLSEXT_TYPE_server_name:
+		extname = "server name";
+		break;
+
+		case TLSEXT_TYPE_max_fragment_length:
+		extname = "max fragment length";
+		break;
+
+		case TLSEXT_TYPE_client_certificate_url:
+		extname = "client certificate URL";
+		break;
+
+		case TLSEXT_TYPE_trusted_ca_keys:
+		extname = "trusted CA keys";
+		break;
+
+		case TLSEXT_TYPE_truncated_hmac:
+		extname = "truncated HMAC";
+		break;
+
+		case TLSEXT_TYPE_status_request:
+		extname = "status request";
+		break;
+
+		case TLSEXT_TYPE_elliptic_curves:
+		extname = "elliptic curves";
+		break;
+
+		case TLSEXT_TYPE_ec_point_formats:
+		extname = "EC point formats";
+		break;
+
+		case TLSEXT_TYPE_session_ticket:
+		extname = "server ticket";
+		break;
+
+
+		default:
+		extname = "unknown";
+		break;
+
+		}
+	
+	BIO_printf(bio, "TLS %s extension \"%s\" (id=%d), len=%d\n",
+			client_server ? "server": "client",
+			extname, type, len);
+	BIO_dump(bio, data, len);
+	BIO_flush(bio);
+	}
diff --git a/apps/s_client.c b/apps/s_client.c
index 66c0f8aa33..2d234a6926 100644
--- a/apps/s_client.c
+++ b/apps/s_client.c
@@ -194,6 +194,9 @@ static int c_nbio=0;
 #endif
 static int c_Pause=0;
 static int c_debug=0;
+#ifndef OPENSSL_NO_TLSEXT
+static int c_tlsextdebug=0;
+#endif
 static int c_msg=0;
 static int c_showcerts=0;
 
@@ -406,6 +409,8 @@ int MAIN(int argc, char **argv)
         tlsextctx tlsextcbp = 
         {NULL,0};
 #endif
+	char *sess_in = NULL;
+	char *sess_out = NULL;
 	struct sockaddr peer;
 	int peerlen = sizeof(peer);
 	int enable_timeouts = 0 ;
@@ -480,6 +485,16 @@ int MAIN(int argc, char **argv)
 			if (--argc < 1) goto bad;
 			cert_file= *(++argv);
 			}
+		else if	(strcmp(*argv,"-sess_out") == 0)
+			{
+			if (--argc < 1) goto bad;
+			sess_out = *(++argv);
+			}
+		else if	(strcmp(*argv,"-sess_in") == 0)
+			{
+			if (--argc < 1) goto bad;
+			sess_in = *(++argv);
+			}
 		else if	(strcmp(*argv,"-certform") == 0)
 			{
 			if (--argc < 1) goto bad;
@@ -506,6 +521,10 @@ int MAIN(int argc, char **argv)
 			c_Pause=1;
 		else if	(strcmp(*argv,"-debug") == 0)
 			c_debug=1;
+#ifndef OPENSSL_NO_TLSEXT
+		else if	(strcmp(*argv,"-tlsextdebug") == 0)
+			c_tlsextdebug=1;
+#endif
 #ifdef WATT32
 		else if (strcmp(*argv,"-wdebug") == 0)
 			dbug_init();
@@ -604,6 +623,10 @@ int MAIN(int argc, char **argv)
 			off|=SSL_OP_NO_SSLv2;
 		else if	(strcmp(*argv,"-no_comp") == 0)
 			{ off|=SSL_OP_NO_COMPRESSION; }
+#ifndef OPENSSL_NO_TLSEXT
+		else if	(strcmp(*argv,"-no_ticket") == 0)
+			{ off|=SSL_OP_NO_TICKET; }
+#endif
 		else if (strcmp(*argv,"-serverpref") == 0)
 			off|=SSL_OP_CIPHER_SERVER_PREFERENCE;
 		else if	(strcmp(*argv,"-cipher") == 0)
@@ -791,6 +814,29 @@ bad:
 #endif
 
 	con=SSL_new(ctx);
+	if (sess_in)
+		{
+		SSL_SESSION *sess;
+		BIO *stmp = BIO_new_file(sess_in, "r");
+		if (!stmp)
+			{
+			BIO_printf(bio_err, "Can't open session file %s\n",
+						sess_in);
+			ERR_print_errors(bio_err);
+			goto end;
+			}
+		sess = PEM_read_bio_SSL_SESSION(stmp, NULL, 0, NULL);
+		BIO_free(stmp);
+		if (!sess)
+			{
+			BIO_printf(bio_err, "Can't open session file %s\n",
+						sess_in);
+			ERR_print_errors(bio_err);
+			goto end;
+			}
+		SSL_set_session(con, sess);
+		SSL_SESSION_free(sess);
+		}
 #ifndef OPENSSL_NO_TLSEXT
 	if (servername != NULL)
 		{
@@ -893,6 +939,13 @@ re_start:
 		SSL_set_msg_callback(con, msg_cb);
 		SSL_set_msg_callback_arg(con, bio_c_out);
 		}
+#ifndef OPENSSL_NO_TLSEXT
+	if (c_tlsextdebug)
+		{
+		SSL_set_tlsext_debug_callback(con, tlsext_cb);
+		SSL_set_tlsext_debug_arg(con, bio_c_out);
+		}
+#endif
 
 	SSL_set_bio(con,sbio,sbio);
 	SSL_set_connect_state(con);
@@ -1022,6 +1075,17 @@ re_start:
 					BIO_printf(bio_c_out,"Server did %sacknowledge servername extension.\n",tlsextcbp.ack?"":"not ");
 					}
 #endif
+				if (sess_out)
+					{
+					BIO *stmp = BIO_new_file(sess_out, "w");
+					if (stmp)
+						{
+						PEM_write_bio_SSL_SESSION(stmp, SSL_get_session(con));
+						BIO_free(stmp);
+						}
+					else 
+						BIO_printf(bio_err, "Error writing session file %s\n", sess_out);
+					}
 				print_stuff(bio_c_out,con,full_log);
 				if (full_log > 0) full_log--;
 
diff --git a/apps/s_server.c b/apps/s_server.c
index f9ee28e527..fc6256afe8 100644
--- a/apps/s_server.c
+++ b/apps/s_server.c
@@ -281,6 +281,9 @@ static int www=0;
 
 static BIO *bio_s_out=NULL;
 static int s_debug=0;
+#ifndef OPENSSL_NO_TLSEXT
+static int s_tlsextdebug=0;
+#endif
 static int s_msg=0;
 static int s_quiet=0;
 
@@ -869,6 +872,10 @@ int MAIN(int argc, char *argv[])
 			}
 		else if	(strcmp(*argv,"-debug") == 0)
 			{ s_debug=1; }
+#ifndef OPENSSL_NO_TLSEXT
+		else if	(strcmp(*argv,"-tlsextdebug") == 0)
+			s_tlsextdebug=1;
+#endif
 		else if	(strcmp(*argv,"-msg") == 0)
 			{ s_msg=1; }
 		else if	(strcmp(*argv,"-hack") == 0)
@@ -922,6 +929,10 @@ int MAIN(int argc, char *argv[])
 			{ off|=SSL_OP_NO_TLSv1; }
 		else if	(strcmp(*argv,"-no_comp") == 0)
 			{ off|=SSL_OP_NO_COMPRESSION; }
+#ifndef OPENSSL_NO_TLSEXT
+		else if	(strcmp(*argv,"-no_ticket") == 0)
+			{ off|=SSL_OP_NO_TICKET; }
+#endif
 #ifndef OPENSSL_NO_SSL2
 		else if	(strcmp(*argv,"-ssl2") == 0)
 			{ meth=SSLv2_server_method(); }
@@ -1541,6 +1552,13 @@ static int sv_body(char *hostname, int s, unsigned char *context)
 
 	if (con == NULL) {
 		con=SSL_new(ctx);
+#ifndef OPENSSL_NO_TLSEXT
+	if (s_tlsextdebug)
+		{
+		SSL_set_tlsext_debug_callback(con, tlsext_cb);
+		SSL_set_tlsext_debug_arg(con, bio_s_out);
+		}
+#endif
 #ifndef OPENSSL_NO_KRB5
 		if ((con->kssl_ctx = kssl_ctx_new()) != NULL)
                         {
@@ -1610,6 +1628,13 @@ static int sv_body(char *hostname, int s, unsigned char *context)
 		SSL_set_msg_callback(con, msg_cb);
 		SSL_set_msg_callback_arg(con, bio_s_out);
 		}
+#ifndef OPENSSL_NO_TLSEXT
+	if (s_tlsextdebug)
+		{
+		SSL_set_tlsext_debug_callback(con, tlsext_cb);
+		SSL_set_tlsext_debug_arg(con, bio_s_out);
+		}
+#endif
 
 	width=s+1;
 	for (;;)
@@ -1989,6 +2014,13 @@ static int www_body(char *hostname, int s, unsigned char *context)
 	if (!BIO_set_write_buffer_size(io,bufsize)) goto err;
 
 	if ((con=SSL_new(ctx)) == NULL) goto err;
+#ifndef OPENSSL_NO_TLSEXT
+		if (s_tlsextdebug)
+			{
+			SSL_set_tlsext_debug_callback(con, tlsext_cb);
+			SSL_set_tlsext_debug_arg(con, bio_s_out);
+			}
+#endif
 #ifndef OPENSSL_NO_KRB5
 	if ((con->kssl_ctx = kssl_ctx_new()) != NULL)
 		{
diff --git a/ssl/s2_srvr.c b/ssl/s2_srvr.c
index 44c1ee3527..fa21d6fe68 100644
--- a/ssl/s2_srvr.c
+++ b/ssl/s2_srvr.c
@@ -607,7 +607,7 @@ static int get_client_hello(SSL *s)
 	else
 		{
 		i=ssl_get_prev_session(s,&(p[s->s2->tmp.cipher_spec_length]),
-			s->s2->tmp.session_id_length);
+			s->s2->tmp.session_id_length, NULL);
 		if (i == 1)
 			{ /* previous session */
 			s->hit=1;
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c
index adc8738377..fc628b5dac 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -163,6 +163,9 @@
 
 static const SSL_METHOD *ssl3_get_client_method(int ver);
 static int ca_dn_cmp(const X509_NAME * const *a,const X509_NAME * const *b);
+#ifndef OPENSSL_NO_TLSEXT
+static int ssl3_check_finished(SSL *s);
+#endif
 
 static const SSL_METHOD *ssl3_get_client_method(int ver)
 	{
@@ -286,6 +289,17 @@ int ssl3_connect(SSL *s)
 
 		case SSL3_ST_CR_CERT_A:
 		case SSL3_ST_CR_CERT_B:
+#ifndef OPENSSL_NO_TLSEXT
+			ret=ssl3_check_finished(s);
+			if (ret <= 0) goto end;
+			if (ret == 2)
+				{
+				s->hit = 1;
+				s->state=SSL3_ST_CR_FINISHED_A;
+				s->init_num=0;
+				break;
+				}
+#endif
 			/* Check if it is anon DH/ECDH */
 			/* or PSK */
 			if (!(s->s3->tmp.new_cipher->algorithm_auth & SSL_aNULL) &&
@@ -439,11 +453,27 @@ int ssl3_connect(SSL *s)
 				}
 			else
 				{
+#ifndef OPENSSL_NO_TLSEXT
+				/* Allow NewSessionTicket if ticket expected */
+				if (s->tlsext_ticket_expected)
+					s->s3->tmp.next_state=SSL3_ST_CR_SESSION_TICKET_A;
+				else
+#endif
+				
 				s->s3->tmp.next_state=SSL3_ST_CR_FINISHED_A;
 				}
 			s->init_num=0;
 			break;
 
+#ifndef OPENSSL_NO_TLSEXT
+		case SSL3_ST_CR_SESSION_TICKET_A:
+		case SSL3_ST_CR_SESSION_TICKET_B:
+			ret=ssl3_get_new_session_ticket(s);
+			s->state=SSL3_ST_CR_FINISHED_A;
+			s->init_num=0;
+		break;
+#endif
+
 		case SSL3_ST_CR_FINISHED_A:
 		case SSL3_ST_CR_FINISHED_B:
 
@@ -671,7 +701,7 @@ int ssl3_get_server_hello(SSL *s)
 		SSL3_ST_CR_SRVR_HELLO_A,
 		SSL3_ST_CR_SRVR_HELLO_B,
 		-1,
-		300, /* ?? */
+		20000, /* ?? */
 		&ok);
 
 	if (!ok) return((int)n);
@@ -1693,6 +1723,74 @@ static int ca_dn_cmp(const X509_NAME * const *a, const X509_NAME * const *b)
 	{
 	return(X509_NAME_cmp(*a,*b));
 	}
+#ifndef OPENSSL_NO_TLSEXT
+int ssl3_get_new_session_ticket(SSL *s)
+	{
+	int ok,al,ret=0, ticklen;
+	long n;
+	const unsigned char *p;
+	unsigned char *d;
+
+	n=s->method->ssl_get_message(s,
+		SSL3_ST_CR_SESSION_TICKET_A,
+		SSL3_ST_CR_SESSION_TICKET_B,
+		-1,
+		16384,
+		&ok);
+
+	if (!ok)
+		return((int)n);
+
+	if (s->s3->tmp.message_type == SSL3_MT_FINISHED)
+		{
+		s->s3->tmp.reuse_message=1;
+		return(1);
+		}
+	if (s->s3->tmp.message_type != SSL3_MT_NEWSESSION_TICKET)
+		{
+		al=SSL_AD_UNEXPECTED_MESSAGE;
+		SSLerr(SSL_F_SSL3_GET_NEW_SESSION_TICKET,SSL_R_BAD_MESSAGE_TYPE);
+		goto f_err;
+		}
+	if (n < 6)
+		{
+		/* need at least ticket_lifetime_hint + ticket length */
+		al = SSL3_AL_FATAL,SSL_AD_DECODE_ERROR;
+		SSLerr(SSL_F_SSL3_GET_NEW_SESSION_TICKET,SSL_R_LENGTH_MISMATCH);
+		goto f_err;
+		}
+	p=d=(unsigned char *)s->init_msg;
+	n2l(p, s->session->tlsext_tick_lifetime_hint);
+	n2s(p, ticklen);
+	/* ticket_lifetime_hint + ticket_length + ticket */
+	if (ticklen + 6 != n)
+		{
+		al = SSL3_AL_FATAL,SSL_AD_DECODE_ERROR;
+		SSLerr(SSL_F_SSL3_NEW_SESSION_TICKET,SSL_R_LENGTH_MISMATCH);
+		goto f_err;
+		}
+	if (s->session->tlsext_tick)
+		{
+		OPENSSL_free(s->session->tlsext_tick);
+		s->session->tlsext_ticklen = 0;
+		}
+	s->session->tlsext_tick = OPENSSL_malloc(ticklen);
+	if (!s->session->tlsext_tick)
+		{
+		SSLerr(SSL_F_SSL3_NEW_SESSION_TICKET,ERR_R_MALLOC_FAILURE);
+		goto err;
+		}
+	memcpy(s->session->tlsext_tick, p, ticklen);
+	s->session->tlsext_ticklen = ticklen;
+	
+	ret=1;
+	return(ret);
+f_err:
+	ssl3_send_alert(s,SSL3_AL_FATAL,al);
+err:
+	return(-1);
+	}
+#endif
 
 int ssl3_get_server_done(SSL *s)
 	{
@@ -2600,3 +2698,33 @@ f_err:
 err:
 	return(0);
 	}
+
+/* Check to see if handshake is full or resumed. Usually this is just a
+ * case of checking to see if a cache hit has occurred. In the case of
+ * session tickets we have to check the next message to be sure.
+ */
+
+#ifndef OPENSSL_NO_TLSEXT
+static int ssl3_check_finished(SSL *s)
+	{
+	int ok;
+	long n;
+	if (!s->session->tlsext_tick)
+		return 1;
+	/* this function is called when we really expect a Certificate
+	 * message, so permit appropriate message length */
+	n=s->method->ssl_get_message(s,
+		SSL3_ST_CR_CERT_A,
+		SSL3_ST_CR_CERT_B,
+		-1,
+		s->max_cert_list,
+		&ok);
+	if (!ok) return((int)n);
+	s->s3->tmp.reuse_message = 1;
+	if ((s->s3->tmp.message_type == SSL3_MT_FINISHED)
+		|| (s->s3->tmp.message_type == SSL3_MT_NEWSESSION_TICKET))
+		return 2;
+
+	return 1;
+	}
+#endif
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index bd0056b9fe..cdad4e017b 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -2338,6 +2338,9 @@ long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg)
 			}
 		s->options |= SSL_OP_NO_SSLv2; /* can't use extension w/ SSL 2.0 format */
  		break;
+	case SSL_CTRL_SET_TLSEXT_DEBUG_ARG:
+		s->tlsext_debug_arg=parg;
+		break;
 #endif /* !OPENSSL_NO_TLSEXT */
 	default:
 		break;
@@ -2389,6 +2392,12 @@ long ssl3_callback_ctrl(SSL *s, int cmd, void (*fp)(void))
 		s->cert->ecdh_tmp_cb = (EC_KEY *(*)(SSL *, int, int))fp;
 		}
 		break;
+#endif
+#ifndef OPENSSL_NO_TLSEXT
+	case SSL_CTRL_SET_TLSEXT_DEBUG_CB:
+		s->tlsext_debug_cb=(void (*)(SSL *,int ,int,
+					unsigned char *, int, void *))fp;
+		break;
 #endif
 	default:
 		break;
diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c
index 7f6df69164..0d90198a8f 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -158,6 +158,7 @@
 #include <openssl/rand.h>
 #include <openssl/objects.h>
 #include <openssl/evp.h>
+#include <openssl/hmac.h>
 #include <openssl/x509.h>
 #ifndef OPENSSL_NO_DH
 #include <openssl/dh.h>
@@ -529,11 +530,26 @@ int ssl3_accept(SSL *s)
 			if (ret <= 0) goto end;
 			if (s->hit)
 				s->state=SSL_ST_OK;
+#ifndef OPENSSL_NO_TLSEXT
+			else if (s->tlsext_ticket_expected)
+				s->state=SSL3_ST_SW_SESSION_TICKET_A;
+#endif
 			else
 				s->state=SSL3_ST_SW_CHANGE_A;
 			s->init_num=0;
 			break;
 
+#ifndef OPENSSL_NO_TLSEXT
+		case SSL3_ST_SW_SESSION_TICKET_A:
+		case SSL3_ST_SW_SESSION_TICKET_B:
+			ret=ssl3_send_newsession_ticket(s);
+			if (ret <= 0) goto end;
+			s->state=SSL3_ST_SW_CHANGE_A;
+			s->init_num=0;
+			break;
+
+#endif
+
 		case SSL3_ST_SW_CHANGE_A:
 		case SSL3_ST_SW_CHANGE_B:
 
@@ -762,14 +778,14 @@ int ssl3_get_client_hello(SSL *s)
 	 * might be written that become totally unsecure when compiled with
 	 * an earlier library version)
 	 */
-	if (j == 0 || (s->new_session && (s->options & SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION)))
+	if ((s->new_session && (s->options & SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION)))
 		{
 		if (!ssl_get_new_session(s,1))
 			goto err;
 		}
 	else
 		{
-		i=ssl_get_prev_session(s,p,j);
+		i=ssl_get_prev_session(s, p, j, d + n);
 		if (i == 1)
 			{ /* previous session */
 			s->hit=1;
@@ -2714,3 +2730,90 @@ int ssl3_send_server_certificate(SSL *s)
 	/* SSL3_ST_SW_CERT_B */
 	return(ssl3_do_write(s,SSL3_RT_HANDSHAKE));
 	}
+#ifndef OPENSSLP_NO_TLSEXT
+int ssl3_send_newsession_ticket(SSL *s)
+	{
+	if (s->state == SSL3_ST_SW_SESSION_TICKET_A)
+		{
+		unsigned char *p, *senc, *macstart;
+		int len, slen;
+		unsigned int hlen;
+		EVP_CIPHER_CTX ctx;
+		HMAC_CTX hctx;
+
+		/* get session encoding length */
+		slen = i2d_SSL_SESSION(s->session, NULL);
+		/* Some length values are 16 bits, so forget it if session is
+ 		 * too long
+ 		 */
+		if (slen > 0xFF00)
+			return -1;
+		/* Grow buffer if need be: the length calculation is as
+ 		 * follows 1 (size of message name) + 3 (message length
+ 		 * bytes) + 4 (ticket lifetime hint) + 2 (ticket length) +
+ 		 * 16 (key name) + max_iv_len (iv length) +
+ 		 * session_length + max_enc_block_size (max encrypted session
+ 		 * length) + max_md_size (HMAC).
+ 		 */
+		if (!BUF_MEM_grow(s->init_buf,
+			26 + EVP_MAX_IV_LENGTH + EVP_MAX_BLOCK_LENGTH +
+			EVP_MAX_MD_SIZE + slen))
+			return -1;
+		senc = OPENSSL_malloc(slen);
+		if (!senc)
+			return -1;
+		p = senc;
+		i2d_SSL_SESSION(s->session, &p);
+
+		p=(unsigned char *)s->init_buf->data;
+		/* do the header */
+		*(p++)=SSL3_MT_NEWSESSION_TICKET;
+		/* Skip message length for now */
+		p += 3;
+		l2n(s->session->tlsext_tick_lifetime_hint, p);
+		/* Skip ticket length for now */
+		p += 2;
+		/* Output key name */
+		macstart = p;
+		memcpy(p, s->ctx->tlsext_tick_key_name, 16);
+		p += 16;
+		/* Generate and output IV */
+		RAND_pseudo_bytes(p, 16);
+		EVP_CIPHER_CTX_init(&ctx);
+		/* Encrypt session data */
+		EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL,
+					s->ctx->tlsext_tick_aes_key, p);
+		p += 16;
+		EVP_EncryptUpdate(&ctx, p, &len, senc, slen);
+		p += len;
+		EVP_EncryptFinal(&ctx, p, &len);
+		p += len;
+		EVP_CIPHER_CTX_cleanup(&ctx);
+
+		HMAC_CTX_init(&hctx);
+		HMAC_Init_ex(&hctx, s->ctx->tlsext_tick_hmac_key, 16,
+				EVP_sha1(), NULL);
+		HMAC_Update(&hctx, macstart, p - macstart);
+		HMAC_Final(&hctx, p, &hlen);
+		HMAC_CTX_cleanup(&hctx);
+
+		p += hlen;
+		/* Now write out lengths: p points to end of data written */
+		/* Total length */
+		len = p - (unsigned char *)s->init_buf->data;
+		p=(unsigned char *)s->init_buf->data + 1;
+		l2n3(len - 4, p); /* Message length */
+		p += 4;
+		s2n(len - 10, p);  /* Ticket length */
+
+		/* number of bytes to write */
+		s->init_num= len;
+		s->state=SSL3_ST_SW_SESSION_TICKET_B;
+		s->init_off=0;
+		OPENSSL_free(senc);
+		}
+
+	/* SSL3_ST_SW_SESSION_TICKET_B */
+	return(ssl3_do_write(s,SSL3_RT_HANDSHAKE));
+	}
+#endif
diff --git a/ssl/ssl.h b/ssl/ssl.h
index a4f02177c6..dc04c7bfab 100644
--- a/ssl/ssl.h
+++ b/ssl/ssl.h
@@ -500,6 +500,10 @@ typedef struct ssl_session_st
 	size_t tlsext_ellipticcurvelist_length;
 	unsigned char *tlsext_ellipticcurvelist; /* peer's list */
 #endif /* OPENSSL_NO_EC */
+	/* RFC4507 info */
+	unsigned char *tlsext_tick;	/* Session ticket */
+	size_t	tlsext_ticklen;		/* Session ticket length */	
+	long tlsext_tick_lifetime_hint;	/* Session lifetime hint in seconds */
 #endif
 	} SSL_SESSION;
 
@@ -529,6 +533,8 @@ typedef struct ssl_session_st
 #define SSL_OP_NO_QUERY_MTU                 0x00001000L
 /* Turn on Cookie Exchange (on relevant for servers) */
 #define SSL_OP_COOKIE_EXCHANGE              0x00002000L
+/* Don't use RFC4507 ticket extension */
+#define SSL_OP_NO_TICKET	            0x00004000L
 
 /* As server, disallow session resumption on renegotiation */
 #define SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION	0x00010000L
@@ -789,6 +795,10 @@ struct ssl_ctx_st
 	/* TLS extensions servername callback */
 	int (*tlsext_servername_callback)(SSL*, int *, void *);
 	void *tlsext_servername_arg;
+	/* RFC 4507 session ticket keys */
+	unsigned char tlsext_tick_key_name[16];
+	unsigned char tlsext_tick_hmac_key[16];
+	unsigned char tlsext_tick_aes_key[16];
 #endif
 #ifndef OPENSSL_NO_PSK
 	char *psk_identity_hint;
@@ -1057,12 +1067,19 @@ struct ssl_st
 				 * SSLv3/TLS rollback check */
 	unsigned int max_send_fragment;
 #ifndef OPENSSL_NO_TLSEXT
+	/* TLS extension debug callback */
+	void (*tlsext_debug_cb)(SSL *s, int client_server, int type,
+					unsigned char *data, int len,
+					void *arg);
+	void *tlsext_debug_arg;
 	char *tlsext_hostname;
 	int servername_done;   /* no further mod of servername 
 	                          0 : call the servername extension callback.
 	                          1 : prepare 2, allow last ack just after in server callback.
 	                          2 : don't call servername callback, no ack in server hello
 	                       */
+	/* RFC4507 session ticket expected to be received or sent */
+	int tlsext_ticket_expected;
 #ifndef OPENSSL_NO_EC
 	size_t tlsext_ecpointformatlist_length;
 	unsigned char *tlsext_ecpointformatlist; /* our list */
@@ -1283,6 +1300,8 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION)
 #define SSL_CTRL_SET_TLSEXT_SERVERNAME_CB	53
 #define SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG	54
 #define SSL_CTRL_SET_TLSEXT_HOSTNAME		55
+#define SSL_CTRL_SET_TLSEXT_DEBUG_CB		56
+#define SSL_CTRL_SET_TLSEXT_DEBUG_ARG		57
 #endif
 
 #define SSL_session_reused(ssl) \
@@ -1739,10 +1758,12 @@ void ERR_load_SSL_strings(void);
 #define SSL_F_SSL3_GET_FINISHED				 140
 #define SSL_F_SSL3_GET_KEY_EXCHANGE			 141
 #define SSL_F_SSL3_GET_MESSAGE				 142
+#define SSL_F_SSL3_GET_NEW_SESSION_TICKET		 283
 #define SSL_F_SSL3_GET_RECORD				 143
 #define SSL_F_SSL3_GET_SERVER_CERTIFICATE		 144
 #define SSL_F_SSL3_GET_SERVER_DONE			 145
 #define SSL_F_SSL3_GET_SERVER_HELLO			 146
+#define SSL_F_SSL3_NEW_SESSION_TICKET			 284
 #define SSL_F_SSL3_OUTPUT_CERT_CHAIN			 147
 #define SSL_F_SSL3_PEEK					 235
 #define SSL_F_SSL3_READ_BYTES				 148
diff --git a/ssl/ssl3.h b/ssl/ssl3.h
index 2d5db780be..71ba3068b1 100644
--- a/ssl/ssl3.h
+++ b/ssl/ssl3.h
@@ -533,6 +533,8 @@ typedef struct ssl3_state_st
 #define SSL3_ST_CR_CHANGE_B		(0x1C1|SSL_ST_CONNECT)
 #define SSL3_ST_CR_FINISHED_A		(0x1D0|SSL_ST_CONNECT)
 #define SSL3_ST_CR_FINISHED_B		(0x1D1|SSL_ST_CONNECT)
+#define SSL3_ST_CR_SESSION_TICKET_A	(0x1E0|SSL_ST_CONNECT)
+#define SSL3_ST_CR_SESSION_TICKET_B	(0x1E1|SSL_ST_CONNECT)
 
 /* server */
 /* extra state */
@@ -574,10 +576,13 @@ typedef struct ssl3_state_st
 #define SSL3_ST_SW_CHANGE_B		(0x1D1|SSL_ST_ACCEPT)
 #define SSL3_ST_SW_FINISHED_A		(0x1E0|SSL_ST_ACCEPT)
 #define SSL3_ST_SW_FINISHED_B		(0x1E1|SSL_ST_ACCEPT)
+#define SSL3_ST_SW_SESSION_TICKET_A	(0x1F0|SSL_ST_CONNECT)
+#define SSL3_ST_SW_SESSION_TICKET_B	(0x1F1|SSL_ST_CONNECT)
 
 #define SSL3_MT_HELLO_REQUEST			0
 #define SSL3_MT_CLIENT_HELLO			1
 #define SSL3_MT_SERVER_HELLO			2
+#define	SSL3_MT_NEWSESSION_TICKET		4
 #define SSL3_MT_CERTIFICATE			11
 #define SSL3_MT_SERVER_KEY_EXCHANGE		12
 #define SSL3_MT_CERTIFICATE_REQUEST		13
diff --git a/ssl/ssl_asn1.c b/ssl/ssl_asn1.c
index 8b0fa6093c..2e8e1bcef4 100644
--- a/ssl/ssl_asn1.c
+++ b/ssl/ssl_asn1.c
@@ -106,6 +106,8 @@ typedef struct ssl_session_asn1_st
 	ASN1_INTEGER verify_result;
 #ifndef OPENSSL_NO_TLSEXT
 	ASN1_OCTET_STRING tlsext_hostname;
+	ASN1_INTEGER tlsext_tick_lifetime;
+	ASN1_OCTET_STRING tlsext_tick;
 #endif /* OPENSSL_NO_TLSEXT */
 #ifndef OPENSSL_NO_PSK
 	ASN1_OCTET_STRING psk_identity_hint;
@@ -116,9 +118,10 @@ typedef struct ssl_session_asn1_st
 int i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp)
 	{
 #define LSIZE2 (sizeof(long)*2)
-	int v1=0,v2=0,v3=0,v4=0,v5=0,v6=0,v7=0,v8=0;
+	int v1=0,v2=0,v3=0,v4=0,v5=0,v6=0,v7=0,v8=0,v9=0,v10=0;
 	unsigned char buf[4],ibuf1[LSIZE2],ibuf2[LSIZE2];
 	unsigned char ibuf3[LSIZE2],ibuf4[LSIZE2],ibuf5[LSIZE2];
+	unsigned char ibuf6[LSIZE2];
 	long l;
 	SSL_SESSION_ASN1 a;
 	M_ASN1_I2D_vars(in);
@@ -217,7 +220,25 @@ int i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp)
                 a.tlsext_hostname.length=strlen(in->tlsext_hostname);
                 a.tlsext_hostname.type=V_ASN1_OCTET_STRING;
                 a.tlsext_hostname.data=(unsigned char *)in->tlsext_hostname;
-                }                
+                }
+	if (in->tlsext_tick)
+                {
+                a.tlsext_tick.length= in->tlsext_ticklen;
+                a.tlsext_tick.type=V_ASN1_OCTET_STRING;
+                a.tlsext_tick.data=(unsigned char *)in->tlsext_tick;
+		/* If we have a ticket set session ID to empty because
+		 * it will be bogus. 
+		 */
+		if (in->tlsext_ticklen)
+			a.session_id.length=0;
+                }
+	if (in->tlsext_tick_lifetime_hint != 0)
+		{
+		a.tlsext_tick_lifetime.length=LSIZE2;
+		a.tlsext_tick_lifetime.type=V_ASN1_INTEGER;
+		a.tlsext_tick_lifetime.data=ibuf6;
+		ASN1_INTEGER_set(&a.tlsext_tick_lifetime,in->tlsext_tick_lifetime_hint);
+		}
 #endif /* OPENSSL_NO_TLSEXT */
 #ifndef OPENSSL_NO_PSK
 	if (in->psk_identity_hint)
@@ -256,6 +277,10 @@ int i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp)
 		M_ASN1_I2D_len_EXP_opt(&(a.verify_result),i2d_ASN1_INTEGER,5,v5);
 
 #ifndef OPENSSL_NO_TLSEXT
+	if (in->tlsext_tick_lifetime_hint)
+      	 	M_ASN1_I2D_len_EXP_opt(&a.tlsext_tick_lifetime, i2d_ASN1_INTEGER,9,v9);
+	if (in->tlsext_tick)
+        	M_ASN1_I2D_len_EXP_opt(&(a.tlsext_tick), i2d_ASN1_OCTET_STRING,10,v10);
 	if (in->tlsext_hostname)
         	M_ASN1_I2D_len_EXP_opt(&(a.tlsext_hostname), i2d_ASN1_OCTET_STRING,6,v6);
 #endif /* OPENSSL_NO_TLSEXT */
@@ -299,6 +324,12 @@ int i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp)
 	if (in->psk_identity)
 		M_ASN1_I2D_put_EXP_opt(&(a.psk_identity), i2d_ASN1_OCTET_STRING,8,v8);
 #endif /* OPENSSL_NO_PSK */
+#ifndef OPENSSL_NO_TLSEXT
+	if (in->tlsext_tick_lifetime_hint)
+      	 	M_ASN1_I2D_put_EXP_opt(&a.tlsext_tick_lifetime, i2d_ASN1_INTEGER,9,v9);
+	if (in->tlsext_tick)
+        	M_ASN1_I2D_put_EXP_opt(&(a.tlsext_tick), i2d_ASN1_OCTET_STRING,10,v10);
+#endif /* OPENSSL_NO_TLSEXT */
 	M_ASN1_I2D_finish();
 	}
 
@@ -488,7 +519,7 @@ SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const unsigned char **pp,
 #ifndef OPENSSL_NO_PSK
 	os.length=0;
 	os.data=NULL;
-	M_ASN1_D2I_get_EXP_opt(osp,d2i_ASN1_OCTET_STRING,9);
+	M_ASN1_D2I_get_EXP_opt(osp,d2i_ASN1_OCTET_STRING,7);
 	if (os.data)
 		{
 		ret->psk_identity_hint = BUF_strndup((char *)os.data, os.length);
@@ -498,20 +529,44 @@ SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const unsigned char **pp,
 		}
 	else
 		ret->psk_identity_hint=NULL;
+#endif /* OPENSSL_NO_PSK */
 
+#ifndef OPENSSL_NO_TLSEXT
+	ai.length=0;
+	M_ASN1_D2I_get_EXP_opt(aip,d2i_ASN1_INTEGER,9);
+	if (ai.data != NULL)
+		{
+		ret->tlsext_tick_lifetime_hint=ASN1_INTEGER_get(aip);
+		OPENSSL_free(ai.data); ai.data=NULL; ai.length=0;
+		}
+	else
+		ret->tlsext_tick_lifetime_hint=0;
 	os.length=0;
 	os.data=NULL;
 	M_ASN1_D2I_get_EXP_opt(osp,d2i_ASN1_OCTET_STRING,10);
 	if (os.data)
 		{
-		ret->psk_identity = BUF_strndup((char *)os.data, os.length);
-		OPENSSL_free(os.data);
+		ret->tlsext_tick = os.data;
+		ret->tlsext_ticklen = os.length;
 		os.data = NULL;
 		os.length = 0;
+#if 0
+		/* There are two ways to detect a resumed ticket sesion.
+		 * One is to set a random session ID and then the server
+		 * must return a match in ServerHello. This allows the normal
+		 * client session ID matching to work.
+		 */ 
+		if (ret->session_id_length == 0)
+			{
+			ret->session_id_length=SSL3_MAX_SSL_SESSION_ID_LENGTH;
+			RAND_pseudo_bytes(ret->session_id,
+						ret->session_id_length);
+			}
+#endif
 		}
 	else
-		ret->psk_identity=NULL;
-#endif /* OPENSSL_NO_KRB5 */
+		ret->tlsext_tick=NULL;
+#endif /* OPENSSL_NO_TLSEXT */
 
 	M_ASN1_D2I_Finish(a,SSL_SESSION_free,SSL_F_D2I_SSL_SESSION);
 	}
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index 52df85ad0a..2d5dc7a8dc 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -147,10 +147,12 @@ static ERR_STRING_DATA SSL_str_functs[]=
 {ERR_FUNC(SSL_F_SSL3_GET_FINISHED),	"SSL3_GET_FINISHED"},
 {ERR_FUNC(SSL_F_SSL3_GET_KEY_EXCHANGE),	"SSL3_GET_KEY_EXCHANGE"},
 {ERR_FUNC(SSL_F_SSL3_GET_MESSAGE),	"SSL3_GET_MESSAGE"},
+{ERR_FUNC(SSL_F_SSL3_GET_NEW_SESSION_TICKET),	"SSL3_GET_NEW_SESSION_TICKET"},
 {ERR_FUNC(SSL_F_SSL3_GET_RECORD),	"SSL3_GET_RECORD"},
 {ERR_FUNC(SSL_F_SSL3_GET_SERVER_CERTIFICATE),	"SSL3_GET_SERVER_CERTIFICATE"},
 {ERR_FUNC(SSL_F_SSL3_GET_SERVER_DONE),	"SSL3_GET_SERVER_DONE"},
 {ERR_FUNC(SSL_F_SSL3_GET_SERVER_HELLO),	"SSL3_GET_SERVER_HELLO"},
+{ERR_FUNC(SSL_F_SSL3_NEW_SESSION_TICKET),	"SSL3_NEW_SESSION_TICKET"},
 {ERR_FUNC(SSL_F_SSL3_OUTPUT_CERT_CHAIN),	"SSL3_OUTPUT_CERT_CHAIN"},
 {ERR_FUNC(SSL_F_SSL3_PEEK),	"SSL3_PEEK"},
 {ERR_FUNC(SSL_F_SSL3_READ_BYTES),	"SSL3_READ_BYTES"},
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index ab15575eec..7fc60e46ec 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -151,6 +151,7 @@
 #include <openssl/objects.h>
 #include <openssl/lhash.h>
 #include <openssl/x509v3.h>
+#include <openssl/rand.h>
 #ifndef OPENSSL_NO_DH
 #include <openssl/dh.h>
 #endif
@@ -336,6 +337,9 @@ SSL *SSL_new(SSL_CTX *ctx)
 	CRYPTO_add(&ctx->references,1,CRYPTO_LOCK_SSL_CTX);
 	s->ctx=ctx;
 #ifndef OPENSSL_NO_TLSEXT
+	s->tlsext_debug_cb = 0;
+	s->tlsext_debug_arg = NULL;
+	s->tlsext_ticket_expected = 0;
 	CRYPTO_add(&ctx->references,1,CRYPTO_LOCK_SSL_CTX);
 	s->initial_ctx=ctx;
 #endif
@@ -1545,6 +1549,12 @@ SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth)
 #ifndef OPENSSL_NO_TLSEXT
 	ret->tlsext_servername_callback = 0;
 	ret->tlsext_servername_arg = NULL;
+	/* Setup RFC4507 ticket keys */
+	if ((RAND_pseudo_bytes(ret->tlsext_tick_key_name, 16) <= 0)
+		|| (RAND_bytes(ret->tlsext_tick_hmac_key, 16) <= 0)
+		|| (RAND_bytes(ret->tlsext_tick_aes_key, 16) <= 0))
+		ret->options |= SSL_OP_NO_TICKET;
+
 #endif
 #ifndef OPENSSL_NO_PSK
 	ret->psk_identity_hint=NULL;
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 004e21ddb8..c958f4a32b 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -740,7 +740,7 @@ SESS_CERT *ssl_sess_cert_new(void);
 void ssl_sess_cert_free(SESS_CERT *sc);
 int ssl_set_peer_cert_type(SESS_CERT *c, int type);
 int ssl_get_new_session(SSL *s, int session);
-int ssl_get_prev_session(SSL *s, unsigned char *session,int len);
+int ssl_get_prev_session(SSL *s, unsigned char *session,int len, const unsigned char *limit);
 int ssl_cipher_id_cmp(const SSL_CIPHER *a,const SSL_CIPHER *b);
 int ssl_cipher_ptr_id_cmp(const SSL_CIPHER * const *ap,
 			const SSL_CIPHER * const *bp);
@@ -800,6 +800,7 @@ SSL_CIPHER *ssl3_get_cipher_by_char(const unsigned char *p);
 int ssl3_put_cipher_by_char(const SSL_CIPHER *c,unsigned char *p);
 void ssl3_init_finished_mac(SSL *s);
 int ssl3_send_server_certificate(SSL *s);
+int ssl3_send_newsession_ticket(SSL *s);
 int ssl3_get_finished(SSL *s,int state_a,int state_b);
 int ssl3_setup_key_block(SSL *s);
 int ssl3_send_change_cipher_spec(SSL *s,int state_a,int state_b);
@@ -890,6 +891,7 @@ long dtls1_default_timeout(void);
 int ssl3_client_hello(SSL *s);
 int ssl3_get_server_hello(SSL *s);
 int ssl3_get_certificate_request(SSL *s);
+int ssl3_get_new_session_ticket(SSL *s);
 int ssl3_get_server_done(SSL *s);
 int ssl3_send_client_verify(SSL *s);
 int ssl3_send_client_certificate(SSL *s);
@@ -985,6 +987,8 @@ int ssl_prepare_clienthello_tlsext(SSL *s);
 int ssl_prepare_serverhello_tlsext(SSL *s);
 int ssl_check_clienthello_tlsext(SSL *s);
 int ssl_check_serverhello_tlsext(SSL *s);
+int tls1_process_ticket(SSL *s, unsigned char *session_id, int len,
+				const unsigned char *limit, SSL_SESSION **ret);
 EVP_MD_CTX* ssl_replace_hash(EVP_MD_CTX **hash,const EVP_MD *md) ;
 void ssl_clear_hash_ctx(EVP_MD_CTX **hash);
 #endif
diff --git a/ssl/ssl_sess.c b/ssl/ssl_sess.c
index 3401d0062b..c408b074e2 100644
--- a/ssl/ssl_sess.c
+++ b/ssl/ssl_sess.c
@@ -308,6 +308,14 @@ int ssl_get_new_session(SSL *s, int session)
 			SSL_SESSION_free(ss);
 			return(0);
 			}
+#ifndef OPENSSL_NO_TLSEXT
+		/* If RFC4507 ticket use empty session ID */
+		if (s->tlsext_ticket_expected)
+			{
+			ss->session_id_length = 0;
+			goto sess_id_done;
+			}
+#endif
 		/* Choose which callback will set the session ID */
 		CRYPTO_r_lock(CRYPTO_LOCK_SSL_CTX);
 		if(s->generate_session_id)
@@ -350,6 +358,7 @@ int ssl_get_new_session(SSL *s, int session)
 			return(0);
 			}
 #ifndef OPENSSL_NO_TLSEXT
+		sess_id_done:
 		if (s->tlsext_hostname) {
 			ss->tlsext_hostname = BUF_strdup(s->tlsext_hostname);
 			if (ss->tlsext_hostname == NULL) {
@@ -406,21 +415,39 @@ int ssl_get_new_session(SSL *s, int session)
 	return(1);
 	}
 
-int ssl_get_prev_session(SSL *s, unsigned char *session_id, int len)
+int ssl_get_prev_session(SSL *s, unsigned char *session_id, int len,
+			const unsigned char *limit)
 	{
 	/* This is used only by servers. */
 
-	SSL_SESSION *ret=NULL,data;
+	SSL_SESSION *ret=NULL;
 	int fatal = 0;
+#ifndef OPENSSL_NO_TLSEXT
+	int r;
+#endif
 
-	data.ssl_version=s->version;
-	data.session_id_length=len;
 	if (len > SSL_MAX_SSL_SESSION_ID_LENGTH)
 		goto err;
-	memcpy(data.session_id,session_id,len);
-
+#ifndef OPENSSL_NO_TLSEXT
+	r = tls1_process_ticket(s, session_id, len, limit, &ret);
+	if (r == -1)
+		{
+		fatal = 1;
+		goto err;
+		}
+	else if (r == 0)
+		goto err;
+	else if (!ret && !(s->session_ctx->session_cache_mode & SSL_SESS_CACHE_NO_INTERNAL_LOOKUP))
+#else
 	if (!(s->session_ctx->session_cache_mode & SSL_SESS_CACHE_NO_INTERNAL_LOOKUP))
+#endif
 		{
+		SSL_SESSION data;
+		data.ssl_version=s->version;
+		data.session_id_length=len;
+		if (len == 0)
+			return 0;
+		memcpy(data.session_id,session_id,len);
 		CRYPTO_r_lock(CRYPTO_LOCK_SSL_CTX);
 		ret=(SSL_SESSION *)lh_retrieve(s->session_ctx->sessions,&data);
 		if (ret != NULL)
@@ -678,6 +705,7 @@ void SSL_SESSION_free(SSL_SESSION *ss)
 	if (ss->ciphers != NULL) sk_SSL_CIPHER_free(ss->ciphers);
 #ifndef OPENSSL_NO_TLSEXT
 	if (ss->tlsext_hostname != NULL) OPENSSL_free(ss->tlsext_hostname);
+	if (ss->tlsext_tick != NULL) OPENSSL_free(ss->tlsext_tick);
 #ifndef OPENSSL_NO_EC
 	ss->tlsext_ecpointformatlist_length = 0;
 	if (ss->tlsext_ecpointformatlist != NULL) OPENSSL_free(ss->tlsext_ecpointformatlist);
diff --git a/ssl/ssl_txt.c b/ssl/ssl_txt.c
index 22f9a403af..26dee73bfa 100644
--- a/ssl/ssl_txt.c
+++ b/ssl/ssl_txt.c
@@ -183,6 +183,22 @@ int SSL_SESSION_print(BIO *bp, const SSL_SESSION *x)
 	if (BIO_puts(bp,"\n    PSK identity hint: ") <= 0) goto err;
 	if (BIO_printf(bp, "%s", x->psk_identity_hint ? x->psk_identity_hint : "None") <= 0) goto err;
 #endif
+#ifndef OPENSSL_NO_TLSEXT
+	if (x->tlsext_tick_lifetime_hint)
+		{
+		if (BIO_printf(bp,
+			"\n    TLS session ticket lifetime hint: %ld (seconds)",
+			x->tlsext_tick_lifetime_hint) <=0)
+			goto err;
+		}
+	if (x->tlsext_tick)
+		{
+		if (BIO_puts(bp, "\n    TLS session ticket:\n") <= 0) goto err;
+		if (BIO_dump_indent(bp, (char *)x->tlsext_tick, x->tlsext_ticklen, 4) <= 0)
+			goto err;
+		}
+#endif
+
 #ifndef OPENSSL_NO_COMP
 	if (x->compress_meth != 0)
 		{
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index e0ca4ac307..6c78abe7fc 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -111,10 +111,16 @@
 
 #include <stdio.h>
 #include <openssl/objects.h>
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
 #include "ssl_locl.h"
 
 const char tls1_version_str[]="TLSv1" OPENSSL_VERSION_PTEXT;
 
+static int tls_decrypt_ticket(SSL *s, const unsigned char *tick, int ticklen,
+				const unsigned char *sess_id, int sesslen,
+				SSL_SESSION **psess);
+
 SSL3_ENC_METHOD TLSv1_enc_data={
 	tls1_enc,
 	tls1_mac,
@@ -164,6 +170,7 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned cha
 	ret+=2;
 
 	if (ret>=limit) return NULL; /* this really never occurs, but ... */
+
  	if (s->tlsext_hostname != NULL)
 		{ 
 		/* Add TLS extension servername to the Client Hello message */
@@ -243,6 +250,27 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned cha
 		}
 #endif /* OPENSSL_NO_EC */
 
+	if (!(SSL_get_options(s) & SSL_OP_NO_TICKET))
+		{
+		int ticklen;
+		if (s->session && s->session->tlsext_tick)
+			ticklen = s->session->tlsext_ticklen;
+		else
+			ticklen = 0;
+		/* Check for enough room 2 for extension type, 2 for len
+ 		 * rest for ticket
+  		 */
+		if (limit - p - 4 - ticklen < 0)
+			return NULL;
+		s2n(TLSEXT_TYPE_session_ticket,ret); 
+		s2n(ticklen,ret);
+		if (ticklen)
+			{
+			memcpy(ret, s->session->tlsext_tick, ticklen);
+			ret += ticklen;
+			}
+		}
+
 	if ((extdatalen = ret-p-2)== 0) 
 		return p;
 
@@ -289,6 +317,14 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha
 	/* Currently the server should not respond with a SupportedCurves extension */
 #endif /* OPENSSL_NO_EC */
 	
+	if (s->tlsext_ticket_expected
+		&& !(SSL_get_options(s) & SSL_OP_NO_TICKET)) 
+		{ 
+		if (limit - p - 4 < 0) return NULL; 
+		s2n(TLSEXT_TYPE_session_ticket,ret);
+		s2n(0,ret);
+		}
+		
 	if ((extdatalen = ret-p-2)== 0) 
 		return p;
 
@@ -318,7 +354,10 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in
 
 		if (data+size > (d+n))
 	   		return 1;
-		
+
+		if (s->tlsext_debug_cb)
+			s->tlsext_debug_cb(s, 0, type, data, size,
+						s->tlsext_debug_arg);
 /* The servername extension is treated as follows:
 
    - Only the hostname type is supported with a maximum length of 255.
@@ -472,9 +511,10 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in
 #endif
 			}
 #endif /* OPENSSL_NO_EC */
-		data+=size;		
+		/* session ticket processed earlier */
+		data+=size;
 		}
-
+				
 	*p = data;
 	return 1;
 	}
@@ -501,6 +541,10 @@ int ssl_parse_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in
 		if (data+size > (d+n))
 	   		return 1;
 
+		if (s->tlsext_debug_cb)
+			s->tlsext_debug_cb(s, 1, type, data, size,
+						s->tlsext_debug_arg);
+
 		if (type == TLSEXT_TYPE_server_name)
 			{
 			if (s->tlsext_hostname == NULL || size > 0)
@@ -540,6 +584,17 @@ int ssl_parse_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in
 #endif
 			}
 #endif /* OPENSSL_NO_EC */
+
+		else if (type == TLSEXT_TYPE_session_ticket)
+			{
+			if ((SSL_get_options(s) & SSL_OP_NO_TICKET)
+				|| (size > 0))
+				{
+				*al = TLS1_AD_UNSUPPORTED_EXTENSION;
+				return 0;
+				}
+			s->tlsext_ticket_expected = 1;
+			}
 		data+=size;		
 		}
 
@@ -854,5 +909,144 @@ int ssl_check_serverhello_tlsext(SSL *s)
 		return 1;
 		}
 	}
-#endif
 
+/* Since the server cache lookup is done early on in the processing of client
+ * hello and other operations depend on the result we need to handle any TLS
+ * session ticket extension at the same time.
+ */
+
+int tls1_process_ticket(SSL *s, unsigned char *session_id, int len,
+				const unsigned char *limit, SSL_SESSION **ret)
+	{
+	/* Point after session ID in client hello */
+	const unsigned char *p = session_id + len;
+	unsigned short i;
+	if ((s->version <= SSL3_VERSION) || !limit)
+		return 1;
+	if (p >= limit)
+		return -1;
+	/* Skip past cipher list */
+	n2s(p, i);
+	p+= i;
+	if (p >= limit)
+		return -1;
+	/* Skip past compression algorithm list */
+	i = *(p++);
+	p += i;
+	if (p > limit)
+		return -1;
+	/* Now at start of extensions */
+	if ((p + 2) >= limit)
+		return 1;
+	n2s(p, i);
+	while ((p + 4) <= limit)
+		{
+		unsigned short type, size;
+		n2s(p, type);
+		n2s(p, size);
+		if (p + size > limit)
+			return 1;
+		if (type == TLSEXT_TYPE_session_ticket)
+			{
+			/* If tickets disabled indicate cache miss which will
+ 			 * trigger a full handshake
+ 			 */
+			if (SSL_get_options(s) & SSL_OP_NO_TICKET)
+				return 0;
+			/* If zero length not client will accept a ticket
+ 			 * and indicate cache miss to trigger full handshake
+ 			 */
+			if (size == 0)
+				{
+				s->tlsext_ticket_expected = 1;
+				return 0;	/* Cache miss */
+				}
+			return tls_decrypt_ticket(s, p, size, session_id, len,
+									ret);
+			}
+		p += size;
+		}
+	return 1;
+	}
+
+static int tls_decrypt_ticket(SSL *s, const unsigned char *etick, int eticklen,
+				const unsigned char *sess_id, int sesslen,
+				SSL_SESSION **psess)
+	{
+	SSL_SESSION *sess;
+	unsigned char *sdec;
+	const unsigned char *p;
+	int slen, mlen;
+	unsigned char tick_hmac[EVP_MAX_MD_SIZE];
+	HMAC_CTX hctx;
+	EVP_CIPHER_CTX ctx;
+	/* Attempt to process session ticket, first conduct sanity and
+ 	 * integrity checks on ticket.
+ 	 */
+	mlen = EVP_MD_size(EVP_sha1());
+	eticklen -= mlen;
+	/* Need at least keyname + iv + some encrypted data */
+	if (eticklen < 48)
+		goto tickerr;
+	/* Check key name matches */
+	if (memcmp(etick, s->ctx->tlsext_tick_key_name, 16))
+		goto tickerr;
+fprintf(stderr, "Ticket match OK\n");
+	/* Check HMAC of encrypted ticket */
+	HMAC_CTX_init(&hctx);
+	HMAC_Init_ex(&hctx, s->ctx->tlsext_tick_hmac_key, 16,
+				EVP_sha1(), NULL);
+	HMAC_Update(&hctx, etick, eticklen);
+	HMAC_Final(&hctx, tick_hmac, NULL);
+	HMAC_CTX_cleanup(&hctx);
+	if (memcmp(tick_hmac, etick + eticklen, mlen))
+		goto tickerr;
+fprintf(stderr, "HMAC match OK\n");
+	/* Set p to start of IV */
+	p = etick + 16;
+	EVP_CIPHER_CTX_init(&ctx);
+	/* Attempt to decrypt session data */
+	EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL,
+					s->ctx->tlsext_tick_aes_key, p);
+	/* Move p after IV to start of encrypted ticket, update length */
+	p += 16;
+	eticklen -= 32;
+	sdec = OPENSSL_malloc(eticklen);
+	if (!sdec)
+		{
+		EVP_CIPHER_CTX_cleanup(&ctx);
+		return -1;
+		}
+	EVP_DecryptUpdate(&ctx, sdec, &slen, p, eticklen);
+	if (EVP_DecryptFinal(&ctx, sdec + slen, &mlen) <= 0)
+		goto tickerr;
+fprintf(stderr, "Decrypt OK\n");
+	slen += mlen;
+	EVP_CIPHER_CTX_cleanup(&ctx);
+	p = sdec;
+		
+	sess = d2i_SSL_SESSION(NULL, &p, slen);
+	OPENSSL_free(sdec);
+	if (sess)
+		{
+		/* The session ID if non-empty is used by some clients to
+ 		 * detect that the ticket has been accepted. So we copy it to
+ 		 * the session structure. If it is empty set length to zero
+ 		 * as required by standard.
+ 		 */
+		if (sesslen)
+			memcpy(sess->session_id, sess_id, sesslen);
+		sess->session_id_length = sesslen;
+		*psess = sess;
+		return 1;
+		}
+	/* If session decrypt failure indicate a cache miss and set state to
+ 	 * send a new ticket
+ 	 */
+	tickerr:	
+	s->tlsext_ticket_expected = 1;
+	return 0;
+	}
+	
+
+#endif
diff --git a/ssl/tls1.h b/ssl/tls1.h
index 84ab246f14..e166bcb1fc 100644
--- a/ssl/tls1.h
+++ b/ssl/tls1.h
@@ -192,6 +192,7 @@ extern "C" {
 #define TLSEXT_TYPE_status_request		5
 #define TLSEXT_TYPE_elliptic_curves		10
 #define TLSEXT_TYPE_ec_point_formats		11
+#define TLSEXT_TYPE_session_ticket		35
 
 /* NameType value from RFC 3546 */
 #define TLSEXT_NAMETYPE_host_name 0
@@ -213,6 +214,12 @@ int SSL_get_servername_type(const SSL *s) ;
 #define SSL_set_tlsext_host_name(s,name) \
 SSL_ctrl(s,SSL_CTRL_SET_TLSEXT_HOSTNAME,TLSEXT_NAMETYPE_host_name,(char *)name)
 
+#define SSL_set_tlsext_debug_callback(ssl, cb) \
+SSL_callback_ctrl(ssl,SSL_CTRL_SET_TLSEXT_DEBUG_CB,(void (*)(void))cb)
+
+#define SSL_set_tlsext_debug_arg(ssl, arg) \
+SSL_ctrl(ssl,SSL_CTRL_SET_TLSEXT_DEBUG_ARG,0, (void *)arg)
+
 #define SSL_CTX_set_tlsext_servername_callback(ctx, cb) \
 SSL_CTX_callback_ctrl(ctx,SSL_CTRL_SET_TLSEXT_SERVERNAME_CB,(void (*)(void))cb)