summaryrefslogtreecommitdiffstats
path: root/ssl
diff options
context:
space:
mode:
authorDr. Stephen Henson <steve@openssl.org>2007-08-11 23:18:29 +0000
committerDr. Stephen Henson <steve@openssl.org>2007-08-11 23:18:29 +0000
commit6434abbfc6ac0d5cb882844ed10fef5821039cf6 (patch)
tree04b0b4626e1ccd18fb80965716957144ceb839b9 /ssl
parente45c100762baaaa0bfd9e9db136192ec09286a55 (diff)
RFC4507 (including RFC4507bis) TLS stateless session resumption support
for OpenSSL.
Diffstat (limited to 'ssl')
-rw-r--r--ssl/s2_srvr.c2
-rw-r--r--ssl/s3_clnt.c130
-rw-r--r--ssl/s3_lib.c9
-rw-r--r--ssl/s3_srvr.c107
-rw-r--r--ssl/ssl.h21
-rw-r--r--ssl/ssl3.h5
-rw-r--r--ssl/ssl_asn1.c69
-rw-r--r--ssl/ssl_err.c2
-rw-r--r--ssl/ssl_lib.c10
-rw-r--r--ssl/ssl_locl.h6
-rw-r--r--ssl/ssl_sess.c40
-rw-r--r--ssl/ssl_txt.c16
-rw-r--r--ssl/t1_lib.c202
-rw-r--r--ssl/tls1.h7
14 files changed, 604 insertions, 22 deletions
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;
@@ -2390,6 +2393,12 @@ long ssl3_callback_ctrl(SSL *s, int cmd, void (*fp)(void))
}
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;