提交 c519e89f 编写于 作者: B Bodo Möller

Fix session handling.

上级 612fcfbd
...@@ -258,6 +258,16 @@ ...@@ -258,6 +258,16 @@
Changes between 1.0.0e and 1.0.1 [xx XXX xxxx] Changes between 1.0.0e and 1.0.1 [xx XXX xxxx]
*) Session-handling fixes:
- Fix handling of connections that are resuming with a session ID,
but also support Session Tickets.
- Fix a bug that suppressed issuing of a new ticket if the client
presented a ticket with an expired session.
- Try to set the ticket lifetime hint to something reasonable.
- Make tickets shorter by excluding irrelevant information.
- On the client side, don't ignore renewed tickets.
[Adam Langley, Bodo Moeller (Google)]
*) Fix PSK session representation. *) Fix PSK session representation.
[Bodo Moeller] [Bodo Moeller]
......
...@@ -638,9 +638,6 @@ int dtls1_accept(SSL *s) ...@@ -638,9 +638,6 @@ int dtls1_accept(SSL *s)
if (s->renegotiate == 2) /* skipped if we just sent a HelloRequest */ if (s->renegotiate == 2) /* skipped if we just sent a HelloRequest */
{ {
/* actually not necessarily a 'new' session unless
* SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION is set */
s->renegotiate=0; s->renegotiate=0;
s->new_session=0; s->new_session=0;
......
...@@ -298,7 +298,16 @@ int ssl3_connect(SSL *s) ...@@ -298,7 +298,16 @@ int ssl3_connect(SSL *s)
if (ret <= 0) goto end; if (ret <= 0) goto end;
if (s->hit) if (s->hit)
{
s->state=SSL3_ST_CR_FINISHED_A; s->state=SSL3_ST_CR_FINISHED_A;
#ifndef OPENSSL_NO_TLSEXT
if (s->tlsext_ticket_expected)
{
/* receive renewed session ticket */
s->state=SSL3_ST_CR_SESSION_TICKET_A;
}
#endif
}
else else
s->state=SSL3_ST_CR_CERT_A; s->state=SSL3_ST_CR_CERT_A;
s->init_num=0; s->init_num=0;
......
...@@ -695,14 +695,11 @@ int ssl3_accept(SSL *s) ...@@ -695,14 +695,11 @@ int ssl3_accept(SSL *s)
ret=ssl3_get_finished(s,SSL3_ST_SR_FINISHED_A, ret=ssl3_get_finished(s,SSL3_ST_SR_FINISHED_A,
SSL3_ST_SR_FINISHED_B); SSL3_ST_SR_FINISHED_B);
if (ret <= 0) goto end; if (ret <= 0) goto end;
#ifndef OPENSSL_NO_TLSEXT
if (s->tlsext_ticket_expected)
s->state=SSL3_ST_SW_SESSION_TICKET_A;
else if (s->hit)
s->state=SSL_ST_OK;
#else
if (s->hit) if (s->hit)
s->state=SSL_ST_OK; s->state=SSL_ST_OK;
#ifndef OPENSSL_NO_TLSEXT
else if (s->tlsext_ticket_expected)
s->state=SSL3_ST_SW_SESSION_TICKET_A;
#endif #endif
else else
s->state=SSL3_ST_SW_CHANGE_A; s->state=SSL3_ST_SW_CHANGE_A;
...@@ -789,9 +786,6 @@ int ssl3_accept(SSL *s) ...@@ -789,9 +786,6 @@ int ssl3_accept(SSL *s)
if (s->renegotiate == 2) /* skipped if we just sent a HelloRequest */ if (s->renegotiate == 2) /* skipped if we just sent a HelloRequest */
{ {
/* actually not necessarily a 'new' session unless
* SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION is set */
s->renegotiate=0; s->renegotiate=0;
s->new_session=0; s->new_session=0;
...@@ -983,13 +977,16 @@ int ssl3_get_client_hello(SSL *s) ...@@ -983,13 +977,16 @@ int ssl3_get_client_hello(SSL *s)
j= *(p++); j= *(p++);
s->hit=0; s->hit=0;
/* Versions before 0.9.7 always allow session reuse during renegotiation /* Versions before 0.9.7 always allow clients to resume sessions in renegotiation.
* (i.e. when s->new_session is true), option * 0.9.7 and later allow this by default, but optionally ignore resumption requests
* SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION is new with 0.9.7. * with flag SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION (it's a new flag rather
* Maybe this optional behaviour should always have been the default, * than a change to default behavior so that applications relying on this for security
* but we cannot safely change the default behaviour (or new applications * won't even compile against older library versions).
* might be written that become totally unsecure when compiled with *
* an earlier library version) * 1.0.1 and later also have a function SSL_renegotiate_abbreviated() to request
* renegotiation but not a new session (s->new_session remains unset): for servers,
* this essentially just means that the SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
* setting will be ignored.
*/ */
if ((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)))
{ {
...@@ -1444,20 +1441,20 @@ int ssl3_send_server_hello(SSL *s) ...@@ -1444,20 +1441,20 @@ int ssl3_send_server_hello(SSL *s)
memcpy(p,s->s3->server_random,SSL3_RANDOM_SIZE); memcpy(p,s->s3->server_random,SSL3_RANDOM_SIZE);
p+=SSL3_RANDOM_SIZE; p+=SSL3_RANDOM_SIZE;
/* now in theory we have 3 options to sending back the /* There are several cases for the session ID to send
* session id. If it is a re-use, we send back the * back in the server hello:
* old session-id, if it is a new session, we send * - For session reuse from the session cache,
* back the new session-id or we send back a 0 length * we send back the old session ID.
* session-id if we want it to be single use. * - If stateless session reuse (using a session ticket)
* Currently I will not implement the '0' length session-id * is successful, we send back the client's "session ID"
* 12-Jan-98 - I'll now support the '0' length stuff. * (which doesn't actually identify the session).
* * - If it is a new session, we send back the new
* We also have an additional case where stateless session * session ID.
* resumption is successful: we always send back the old * - However, if we want the new session to be single-use,
* session id. In this case s->hit is non zero: this can * we send back a 0-length session ID.
* only happen if stateless session resumption is succesful * s->hit is non-zero in either case of session reuse,
* if session caching is disabled so existing functionality * so the following won't overwrite an ID that we're supposed
* is unaffected. * to send back.
*/ */
if (s->session->not_resumable || if (s->session->not_resumable ||
(!(s->ctx->session_cache_mode & SSL_SESS_CACHE_SERVER) (!(s->ctx->session_cache_mode & SSL_SESS_CACHE_SERVER)
...@@ -3341,13 +3338,17 @@ int ssl3_send_server_certificate(SSL *s) ...@@ -3341,13 +3338,17 @@ int ssl3_send_server_certificate(SSL *s)
/* SSL3_ST_SW_CERT_B */ /* SSL3_ST_SW_CERT_B */
return(ssl3_do_write(s,SSL3_RT_HANDSHAKE)); return(ssl3_do_write(s,SSL3_RT_HANDSHAKE));
} }
#ifndef OPENSSL_NO_TLSEXT #ifndef OPENSSL_NO_TLSEXT
/* send a new session ticket (not necessarily for a new session) */
int ssl3_send_newsession_ticket(SSL *s) int ssl3_send_newsession_ticket(SSL *s)
{ {
if (s->state == SSL3_ST_SW_SESSION_TICKET_A) if (s->state == SSL3_ST_SW_SESSION_TICKET_A)
{ {
unsigned char *p, *senc, *macstart; unsigned char *p, *senc, *macstart;
int len, slen; const unsigned char *const_p;
int len, slen_full, slen;
SSL_SESSION *sess;
unsigned int hlen; unsigned int hlen;
EVP_CIPHER_CTX ctx; EVP_CIPHER_CTX ctx;
HMAC_CTX hctx; HMAC_CTX hctx;
...@@ -3356,12 +3357,38 @@ int ssl3_send_newsession_ticket(SSL *s) ...@@ -3356,12 +3357,38 @@ int ssl3_send_newsession_ticket(SSL *s)
unsigned char key_name[16]; unsigned char key_name[16];
/* get session encoding length */ /* get session encoding length */
slen = i2d_SSL_SESSION(s->session, NULL); slen_full = i2d_SSL_SESSION(s->session, NULL);
/* Some length values are 16 bits, so forget it if session is /* Some length values are 16 bits, so forget it if session is
* too long * too long
*/ */
if (slen > 0xFF00) if (slen_full > 0xFF00)
return -1;
senc = OPENSSL_malloc(slen_full);
if (!senc)
return -1;
p = senc;
i2d_SSL_SESSION(s->session, &p);
/* create a fresh copy (not shared with other threads) to clean up */
const_p = senc;
sess = d2i_SSL_SESSION(NULL, &const_p, slen_full);
if (sess == NULL)
{
OPENSSL_free(senc);
return -1; return -1;
}
sess->session_id_length = 0; /* ID is irrelevant for the ticket */
slen = i2d_SSL_SESSION(sess, NULL);
if (slen > slen_full) /* shouldn't ever happen */
{
OPENSSL_free(senc);
return -1;
}
p = senc;
i2d_SSL_SESSION(sess, &p);
SSL_SESSION_free(sess);
/* Grow buffer if need be: the length calculation is as /* Grow buffer if need be: the length calculation is as
* follows 1 (size of message name) + 3 (message length * follows 1 (size of message name) + 3 (message length
* bytes) + 4 (ticket lifetime hint) + 2 (ticket length) + * bytes) + 4 (ticket lifetime hint) + 2 (ticket length) +
...@@ -3373,11 +3400,6 @@ int ssl3_send_newsession_ticket(SSL *s) ...@@ -3373,11 +3400,6 @@ int ssl3_send_newsession_ticket(SSL *s)
26 + EVP_MAX_IV_LENGTH + EVP_MAX_BLOCK_LENGTH + 26 + EVP_MAX_IV_LENGTH + EVP_MAX_BLOCK_LENGTH +
EVP_MAX_MD_SIZE + slen)) EVP_MAX_MD_SIZE + slen))
return -1; 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; p=(unsigned char *)s->init_buf->data;
/* do the header */ /* do the header */
...@@ -3408,7 +3430,13 @@ int ssl3_send_newsession_ticket(SSL *s) ...@@ -3408,7 +3430,13 @@ int ssl3_send_newsession_ticket(SSL *s)
tlsext_tick_md(), NULL); tlsext_tick_md(), NULL);
memcpy(key_name, tctx->tlsext_tick_key_name, 16); memcpy(key_name, tctx->tlsext_tick_key_name, 16);
} }
l2n(s->session->tlsext_tick_lifetime_hint, p);
/* Ticket lifetime hint (advisory only):
* We leave this unspecified for resumed session (for simplicity),
* and guess that tickets for new sessions will live as long
* as their sessions. */
l2n(s->hit ? 0 : s->session->timeout, p);
/* Skip ticket length for now */ /* Skip ticket length for now */
p += 2; p += 2;
/* Output key name */ /* Output key name */
......
...@@ -1133,12 +1133,12 @@ struct ssl_st ...@@ -1133,12 +1133,12 @@ struct ssl_st
int server; /* are we the server side? - mostly used by SSL_clear*/ int server; /* are we the server side? - mostly used by SSL_clear*/
int new_session;/* Generate a new session or reuse an old one. int new_session;/* Generate a new session or reuse an old one.
* NB: For servers, the 'new' session may actually be a previously * NB: For servers, the 'new' session may actually be a previously
* cached session or even the previous session unless * cached session or even the previous session unless
* SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION is set */ * SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION is set */
int renegotiate;/* 1 if we are renegotiating. int renegotiate;/* 1 if we are renegotiating.
* 2 if we are a server and are inside a handshake * 2 if we are a server and are inside a handshake
* (i.e. not just sending a HelloRequest) */ * (i.e. not just sending a HelloRequest) */
int quiet_shutdown;/* don't send shutdown packets */ int quiet_shutdown;/* don't send shutdown packets */
int shutdown; /* we have shut things down, 0x01 sent, 0x02 int shutdown; /* we have shut things down, 0x01 sent, 0x02
......
...@@ -1027,14 +1027,14 @@ int SSL_renegotiate(SSL *s) ...@@ -1027,14 +1027,14 @@ int SSL_renegotiate(SSL *s)
} }
int SSL_renegotiate_abbreviated(SSL *s) int SSL_renegotiate_abbreviated(SSL *s)
{ {
if (s->renegotiate == 0) if (s->renegotiate == 0)
s->renegotiate=1; s->renegotiate=1;
s->new_session=0; s->new_session=0;
return(s->method->ssl_renegotiate(s)); return(s->method->ssl_renegotiate(s));
} }
int SSL_renegotiate_pending(SSL *s) int SSL_renegotiate_pending(SSL *s)
{ {
...@@ -3241,4 +3241,3 @@ IMPLEMENT_STACK_OF(SSL_CIPHER) ...@@ -3241,4 +3241,3 @@ IMPLEMENT_STACK_OF(SSL_CIPHER)
IMPLEMENT_STACK_OF(SSL_COMP) IMPLEMENT_STACK_OF(SSL_COMP)
IMPLEMENT_OBJ_BSEARCH_GLOBAL_CMP_FN(SSL_CIPHER, SSL_CIPHER, IMPLEMENT_OBJ_BSEARCH_GLOBAL_CMP_FN(SSL_CIPHER, SSL_CIPHER,
ssl_cipher_id); ssl_cipher_id);
...@@ -436,6 +436,25 @@ int ssl_get_new_session(SSL *s, int session) ...@@ -436,6 +436,25 @@ int ssl_get_new_session(SSL *s, int session)
return(1); return(1);
} }
/* ssl_get_prev attempts to find an SSL_SESSION to be used to resume this
* connection. It is only called by servers.
*
* session_id: points at the session ID in the ClientHello. This code will
* read past the end of this in order to parse out the session ticket
* extension, if any.
* len: the length of the session ID.
* limit: a pointer to the first byte after the ClientHello.
*
* Returns:
* -1: error
* 0: a session may have been found.
*
* Side effects:
* - If a session is found then s->session is pointed at it (after freeing an
* existing session if need be) and s->verify_result is set from the session.
* - Both for new and resumed sessions, s->tlsext_ticket_expected is set to 1
* if the server should issue a new session ticket (to 0 otherwise).
*/
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) const unsigned char *limit)
{ {
...@@ -443,27 +462,39 @@ int ssl_get_prev_session(SSL *s, unsigned char *session_id, int len, ...@@ -443,27 +462,39 @@ int ssl_get_prev_session(SSL *s, unsigned char *session_id, int len,
SSL_SESSION *ret=NULL; SSL_SESSION *ret=NULL;
int fatal = 0; int fatal = 0;
int try_session_cache = 1;
#ifndef OPENSSL_NO_TLSEXT #ifndef OPENSSL_NO_TLSEXT
int r; int r;
#endif #endif
if (len > SSL_MAX_SSL_SESSION_ID_LENGTH) if (len > SSL_MAX_SSL_SESSION_ID_LENGTH)
goto err; goto err;
if (len == 0)
try_session_cache = 0;
#ifndef OPENSSL_NO_TLSEXT #ifndef OPENSSL_NO_TLSEXT
r = tls1_process_ticket(s, session_id, len, limit, &ret); r = tls1_process_ticket(s, session_id, len, limit, &ret); /* sets s->tlsext_ticket_expected */
if (r == -1) switch (r)
{ {
case -1: /* Error during processing */
fatal = 1; fatal = 1;
goto err; goto err;
case 0: /* No ticket found */
case 1: /* Zero length ticket found */
break; /* Ok to carry on processing session id. */
case 2: /* Ticket found but not decrypted. */
case 3: /* Ticket decrypted, *ret has been set. */
try_session_cache = 0;
break;
default:
abort();
} }
else if (r == 0 || (!ret && !len))
goto err;
else if (!ret && !(s->session_ctx->session_cache_mode & SSL_SESS_CACHE_NO_INTERNAL_LOOKUP))
#else
if (len == 0)
goto err;
if (!(s->session_ctx->session_cache_mode & SSL_SESS_CACHE_NO_INTERNAL_LOOKUP))
#endif #endif
if (try_session_cache &&
ret == NULL &&
!(s->session_ctx->session_cache_mode & SSL_SESS_CACHE_NO_INTERNAL_LOOKUP))
{ {
SSL_SESSION data; SSL_SESSION data;
data.ssl_version=s->version; data.ssl_version=s->version;
...@@ -474,20 +505,22 @@ int ssl_get_prev_session(SSL *s, unsigned char *session_id, int len, ...@@ -474,20 +505,22 @@ int ssl_get_prev_session(SSL *s, unsigned char *session_id, int len,
CRYPTO_r_lock(CRYPTO_LOCK_SSL_CTX); CRYPTO_r_lock(CRYPTO_LOCK_SSL_CTX);
ret=lh_SSL_SESSION_retrieve(s->session_ctx->sessions,&data); ret=lh_SSL_SESSION_retrieve(s->session_ctx->sessions,&data);
if (ret != NULL) if (ret != NULL)
/* don't allow other threads to steal it: */ {
CRYPTO_add(&ret->references,1,CRYPTO_LOCK_SSL_SESSION); /* don't allow other threads to steal it: */
CRYPTO_add(&ret->references,1,CRYPTO_LOCK_SSL_SESSION);
}
CRYPTO_r_unlock(CRYPTO_LOCK_SSL_CTX); CRYPTO_r_unlock(CRYPTO_LOCK_SSL_CTX);
if (ret == NULL)
s->session_ctx->stats.sess_miss++;
} }
if (ret == NULL) if (try_session_cache &&
ret == NULL &&
s->session_ctx->get_session_cb != NULL)
{ {
int copy=1; int copy=1;
s->session_ctx->stats.sess_miss++; if ((ret=s->session_ctx->get_session_cb(s,session_id,len,&copy)))
ret=NULL;
if (s->session_ctx->get_session_cb != NULL
&& (ret=s->session_ctx->get_session_cb(s,session_id,len,&copy))
!= NULL)
{ {
s->session_ctx->stats.sess_cb_hit++; s->session_ctx->stats.sess_cb_hit++;
...@@ -506,23 +539,18 @@ int ssl_get_prev_session(SSL *s, unsigned char *session_id, int len, ...@@ -506,23 +539,18 @@ int ssl_get_prev_session(SSL *s, unsigned char *session_id, int len,
* things are very strange */ * things are very strange */
SSL_CTX_add_session(s->session_ctx,ret); SSL_CTX_add_session(s->session_ctx,ret);
} }
if (ret == NULL)
goto err;
} }
/* Now ret is non-NULL, and we own one of its reference counts. */ if (ret == NULL)
goto err;
/* Now ret is non-NULL and we own one of its reference counts. */
if (ret->sid_ctx_length != s->sid_ctx_length if (ret->sid_ctx_length != s->sid_ctx_length
|| memcmp(ret->sid_ctx,s->sid_ctx,ret->sid_ctx_length)) || memcmp(ret->sid_ctx,s->sid_ctx,ret->sid_ctx_length))
{ {
/* We've found the session named by the client, but we don't /* We have the session requested by the client, but we don't
* want to use it in this context. */ * want to use it in this context. */
#if 0 /* The client cannot always know when a session is not appropriate,
* so we shouldn't generate an error message. */
SSLerr(SSL_F_SSL_GET_PREV_SESSION,SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT);
#endif
goto err; /* treat like cache miss */ goto err; /* treat like cache miss */
} }
...@@ -559,39 +587,36 @@ int ssl_get_prev_session(SSL *s, unsigned char *session_id, int len, ...@@ -559,39 +587,36 @@ int ssl_get_prev_session(SSL *s, unsigned char *session_id, int len,
goto err; goto err;
} }
#if 0 /* This is way too late. */
/* If a thread got the session, then 'swaped', and another got
* it and then due to a time-out decided to 'OPENSSL_free' it we could
* be in trouble. So I'll increment it now, then double decrement
* later - am I speaking rubbish?. */
CRYPTO_add(&ret->references,1,CRYPTO_LOCK_SSL_SESSION);
#endif
if (ret->timeout < (long)(time(NULL) - ret->time)) /* timeout */ if (ret->timeout < (long)(time(NULL) - ret->time)) /* timeout */
{ {
s->session_ctx->stats.sess_timeout++; s->session_ctx->stats.sess_timeout++;
/* remove it from the cache */ if (try_session_cache)
SSL_CTX_remove_session(s->session_ctx,ret); {
/* session was from the cache, so remove it */
SSL_CTX_remove_session(s->session_ctx,ret);
}
goto err; goto err;
} }
s->session_ctx->stats.sess_hit++; s->session_ctx->stats.sess_hit++;
/* ret->time=time(NULL); */ /* rezero timeout? */
/* again, just leave the session
* if it is the same session, we have just incremented and
* then decremented the reference count :-) */
if (s->session != NULL) if (s->session != NULL)
SSL_SESSION_free(s->session); SSL_SESSION_free(s->session);
s->session=ret; s->session=ret;
s->verify_result = s->session->verify_result; s->verify_result = s->session->verify_result;
return(1); return 1;
err: err:
if (ret != NULL) if (ret != NULL)
{
SSL_SESSION_free(ret); SSL_SESSION_free(ret);
if (!try_session_cache)
{
/* The session was from a ticket, so we should
* issue a ticket for the new session */
s->tlsext_ticket_expected = 1;
}
}
if (fatal) if (fatal)
return -1; return -1;
else else
...@@ -770,10 +795,6 @@ int SSL_set_session(SSL *s, SSL_SESSION *session) ...@@ -770,10 +795,6 @@ int SSL_set_session(SSL *s, SSL_SESSION *session)
{ {
if (!SSL_set_ssl_method(s,meth)) if (!SSL_set_ssl_method(s,meth))
return(0); return(0);
if (s->ctx->session_timeout == 0)
session->timeout=SSL_get_default_timeout(s);
else
session->timeout=s->ctx->session_timeout;
} }
#ifndef OPENSSL_NO_KRB5 #ifndef OPENSSL_NO_KRB5
...@@ -858,17 +879,17 @@ X509 *SSL_SESSION_get0_peer(SSL_SESSION *s) ...@@ -858,17 +879,17 @@ X509 *SSL_SESSION_get0_peer(SSL_SESSION *s)
int SSL_SESSION_set1_id_context(SSL_SESSION *s,const unsigned char *sid_ctx, int SSL_SESSION_set1_id_context(SSL_SESSION *s,const unsigned char *sid_ctx,
unsigned int sid_ctx_len) unsigned int sid_ctx_len)
{
if(sid_ctx_len > SSL_MAX_SID_CTX_LENGTH)
{ {
SSLerr(SSL_F_SSL_SESSION_SET1_ID_CONTEXT,SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG); if(sid_ctx_len > SSL_MAX_SID_CTX_LENGTH)
return 0; {
} SSLerr(SSL_F_SSL_SESSION_SET1_ID_CONTEXT,SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG);
s->sid_ctx_length=sid_ctx_len; return 0;
memcpy(s->sid_ctx,sid_ctx,sid_ctx_len); }
s->sid_ctx_length=sid_ctx_len;
memcpy(s->sid_ctx,sid_ctx,sid_ctx_len);
return 1; return 1;
} }
long SSL_CTX_set_timeout(SSL_CTX *s, long t) long SSL_CTX_set_timeout(SSL_CTX *s, long t)
{ {
......
...@@ -1838,26 +1838,56 @@ int ssl_check_serverhello_tlsext(SSL *s) ...@@ -1838,26 +1838,56 @@ int ssl_check_serverhello_tlsext(SSL *s)
} }
} }
/* Since the server cache lookup is done early on in the processing of client /* Since the server cache lookup is done early on in the processing of the
* hello and other operations depend on the result we need to handle any TLS * ClientHello, and other operations depend on the result, we need to handle
* session ticket extension at the same time. * any TLS session ticket extension at the same time.
*
* session_id: points at the session ID in the ClientHello. This code will
* read past the end of this in order to parse out the session ticket
* extension, if any.
* len: the length of the session ID.
* limit: a pointer to the first byte after the ClientHello.
* ret: (output) on return, if a ticket was decrypted, then this is set to
* point to the resulting session.
*
* If s->tls_session_secret_cb is set then we are expecting a pre-shared key
* ciphersuite, in which case we have no use for session tickets and one will
* never be decrypted, nor will s->tlsext_ticket_expected be set to 1.
*
* Returns:
* -1: fatal error, either from parsing or decrypting the ticket.
* 0: no ticket was found (or was ignored, based on settings).
* 1: a zero length extension was found, indicating that the client supports
* session tickets but doesn't currently have one to offer.
* 2: either s->tls_session_secret_cb was set, or a ticket was offered but
* couldn't be decrypted because of a non-fatal error.
* 3: a ticket was successfully decrypted and *ret was set.
*
* Side effects:
* Sets s->tlsext_ticket_expected to 1 if the server will have to issue
* a new session ticket to the client because the client indicated support
* (and s->tls_session_secret_cb is NULL) but the client either doesn't have
* a session ticket or we couldn't use the one it gave us, or if
* s->ctx->tlsext_ticket_key_cb asked to renew the client's ticket.
* Otherwise, s->tlsext_ticket_expected is set to 0.
*/ */
int tls1_process_ticket(SSL *s, unsigned char *session_id, int len, int tls1_process_ticket(SSL *s, unsigned char *session_id, int len,
const unsigned char *limit, SSL_SESSION **ret) const unsigned char *limit, SSL_SESSION **ret)
{ {
/* Point after session ID in client hello */ /* Point after session ID in client hello */
const unsigned char *p = session_id + len; const unsigned char *p = session_id + len;
unsigned short i; unsigned short i;
*ret = NULL;
s->tlsext_ticket_expected = 0;
/* If tickets disabled behave as if no ticket present /* If tickets disabled behave as if no ticket present
* to permit stateful resumption. * to permit stateful resumption.
*/ */
if (SSL_get_options(s) & SSL_OP_NO_TICKET) if (SSL_get_options(s) & SSL_OP_NO_TICKET)
return 1; return 0;
if ((s->version <= SSL3_VERSION) || !limit) if ((s->version <= SSL3_VERSION) || !limit)
return 1; return 0;
if (p >= limit) if (p >= limit)
return -1; return -1;
/* Skip past DTLS cookie */ /* Skip past DTLS cookie */
...@@ -1880,7 +1910,7 @@ int tls1_process_ticket(SSL *s, unsigned char *session_id, int len, ...@@ -1880,7 +1910,7 @@ int tls1_process_ticket(SSL *s, unsigned char *session_id, int len,
return -1; return -1;
/* Now at start of extensions */ /* Now at start of extensions */
if ((p + 2) >= limit) if ((p + 2) >= limit)
return 1; return 0;
n2s(p, i); n2s(p, i);
while ((p + 4) <= limit) while ((p + 4) <= limit)
{ {
...@@ -1888,39 +1918,61 @@ int tls1_process_ticket(SSL *s, unsigned char *session_id, int len, ...@@ -1888,39 +1918,61 @@ int tls1_process_ticket(SSL *s, unsigned char *session_id, int len,
n2s(p, type); n2s(p, type);
n2s(p, size); n2s(p, size);
if (p + size > limit) if (p + size > limit)
return 1; return 0;
if (type == TLSEXT_TYPE_session_ticket) if (type == TLSEXT_TYPE_session_ticket)
{ {
/* If tickets disabled indicate cache miss which will int r;
* trigger a full handshake
*/
if (SSL_get_options(s) & SSL_OP_NO_TICKET)
return 1;
/* If zero length note client will accept a ticket
* and indicate cache miss to trigger full handshake
*/
if (size == 0) if (size == 0)
{ {
/* The client will accept a ticket but doesn't
* currently have one. */
s->tlsext_ticket_expected = 1; s->tlsext_ticket_expected = 1;
return 0; /* Cache miss */ return 1;
} }
if (s->tls_session_secret_cb) if (s->tls_session_secret_cb)
{ {
/* Indicate cache miss here and instead of /* Indicate that the ticket couldn't be
* generating the session from ticket now, * decrypted rather than generating the session
* trigger abbreviated handshake based on * from ticket now, trigger abbreviated
* external mechanism to calculate the master * handshake based on external mechanism to
* secret later. */ * calculate the master secret later. */
return 0; return 2;
}
r = tls_decrypt_ticket(s, p, size, session_id, len, ret);
switch (r)
{
case 2: /* ticket couldn't be decrypted */
s->tlsext_ticket_expected = 1;
return 2;
case 3: /* ticket was decrypted */
return r;
case 4: /* ticket decrypted but need to renew */
s->tlsext_ticket_expected = 1;
return 3;
default: /* fatal error */
return -1;
} }
return tls_decrypt_ticket(s, p, size, session_id, len,
ret);
} }
p += size; p += size;
} }
return 1; return 0;
} }
/* tls_decrypt_ticket attempts to decrypt a session ticket.
*
* etick: points to the body of the session ticket extension.
* eticklen: the length of the session tickets extenion.
* sess_id: points at the session ID.
* sesslen: the length of the session ID.
* psess: (output) on return, if a ticket was decrypted, then this is set to
* point to the resulting session.
*
* Returns:
* -1: fatal error, either from parsing or decrypting the ticket.
* 2: the ticket couldn't be decrypted.
* 3: a ticket was successfully decrypted and *psess was set.
* 4: same as 3, but the ticket needs to be renewed.
*/
static int tls_decrypt_ticket(SSL *s, const unsigned char *etick, int eticklen, static int tls_decrypt_ticket(SSL *s, const unsigned char *etick, int eticklen,
const unsigned char *sess_id, int sesslen, const unsigned char *sess_id, int sesslen,
SSL_SESSION **psess) SSL_SESSION **psess)
...@@ -1935,7 +1987,7 @@ static int tls_decrypt_ticket(SSL *s, const unsigned char *etick, int eticklen, ...@@ -1935,7 +1987,7 @@ static int tls_decrypt_ticket(SSL *s, const unsigned char *etick, int eticklen,
SSL_CTX *tctx = s->initial_ctx; SSL_CTX *tctx = s->initial_ctx;
/* Need at least keyname + iv + some encrypted data */ /* Need at least keyname + iv + some encrypted data */
if (eticklen < 48) if (eticklen < 48)
goto tickerr; return 2;
/* Initialize session ticket encryption and HMAC contexts */ /* Initialize session ticket encryption and HMAC contexts */
HMAC_CTX_init(&hctx); HMAC_CTX_init(&hctx);
EVP_CIPHER_CTX_init(&ctx); EVP_CIPHER_CTX_init(&ctx);
...@@ -1947,7 +1999,7 @@ static int tls_decrypt_ticket(SSL *s, const unsigned char *etick, int eticklen, ...@@ -1947,7 +1999,7 @@ static int tls_decrypt_ticket(SSL *s, const unsigned char *etick, int eticklen,
if (rv < 0) if (rv < 0)
return -1; return -1;
if (rv == 0) if (rv == 0)
goto tickerr; return 2;
if (rv == 2) if (rv == 2)
renew_ticket = 1; renew_ticket = 1;
} }
...@@ -1955,15 +2007,15 @@ static int tls_decrypt_ticket(SSL *s, const unsigned char *etick, int eticklen, ...@@ -1955,15 +2007,15 @@ static int tls_decrypt_ticket(SSL *s, const unsigned char *etick, int eticklen,
{ {
/* Check key name matches */ /* Check key name matches */
if (memcmp(etick, tctx->tlsext_tick_key_name, 16)) if (memcmp(etick, tctx->tlsext_tick_key_name, 16))
goto tickerr; return 2;
HMAC_Init_ex(&hctx, tctx->tlsext_tick_hmac_key, 16, HMAC_Init_ex(&hctx, tctx->tlsext_tick_hmac_key, 16,
tlsext_tick_md(), NULL); tlsext_tick_md(), NULL);
EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL,
tctx->tlsext_tick_aes_key, etick + 16); tctx->tlsext_tick_aes_key, etick + 16);
} }
/* Attempt to process session ticket, first conduct sanity and /* Attempt to process session ticket, first conduct sanity and
* integrity checks on ticket. * integrity checks on ticket.
*/ */
mlen = HMAC_size(&hctx); mlen = HMAC_size(&hctx);
if (mlen < 0) if (mlen < 0)
{ {
...@@ -1976,7 +2028,7 @@ static int tls_decrypt_ticket(SSL *s, const unsigned char *etick, int eticklen, ...@@ -1976,7 +2028,7 @@ static int tls_decrypt_ticket(SSL *s, const unsigned char *etick, int eticklen,
HMAC_Final(&hctx, tick_hmac, NULL); HMAC_Final(&hctx, tick_hmac, NULL);
HMAC_CTX_cleanup(&hctx); HMAC_CTX_cleanup(&hctx);
if (memcmp(tick_hmac, etick + eticklen, mlen)) if (memcmp(tick_hmac, etick + eticklen, mlen))
goto tickerr; return 2;
/* Attempt to decrypt session data */ /* Attempt to decrypt session data */
/* Move p after IV to start of encrypted ticket, update length */ /* Move p after IV to start of encrypted ticket, update length */
p = etick + 16 + EVP_CIPHER_CTX_iv_length(&ctx); p = etick + 16 + EVP_CIPHER_CTX_iv_length(&ctx);
...@@ -1989,33 +2041,33 @@ static int tls_decrypt_ticket(SSL *s, const unsigned char *etick, int eticklen, ...@@ -1989,33 +2041,33 @@ static int tls_decrypt_ticket(SSL *s, const unsigned char *etick, int eticklen,
} }
EVP_DecryptUpdate(&ctx, sdec, &slen, p, eticklen); EVP_DecryptUpdate(&ctx, sdec, &slen, p, eticklen);
if (EVP_DecryptFinal(&ctx, sdec + slen, &mlen) <= 0) if (EVP_DecryptFinal(&ctx, sdec + slen, &mlen) <= 0)
goto tickerr; return 2;
slen += mlen; slen += mlen;
EVP_CIPHER_CTX_cleanup(&ctx); EVP_CIPHER_CTX_cleanup(&ctx);
p = sdec; p = sdec;
sess = d2i_SSL_SESSION(NULL, &p, slen); sess = d2i_SSL_SESSION(NULL, &p, slen);
OPENSSL_free(sdec); OPENSSL_free(sdec);
if (sess) if (sess)
{ {
/* The session ID if non-empty is used by some clients to /* The session ID, if non-empty, is used by some clients to
* detect that the ticket has been accepted. So we copy it to * detect that the ticket has been accepted. So we copy it to
* the session structure. If it is empty set length to zero * the session structure. If it is empty set length to zero
* as required by standard. * as required by standard.
*/ */
if (sesslen) if (sesslen)
memcpy(sess->session_id, sess_id, sesslen); memcpy(sess->session_id, sess_id, sesslen);
sess->session_id_length = sesslen; sess->session_id_length = sesslen;
*psess = sess; *psess = sess;
s->tlsext_ticket_expected = renew_ticket; if (renew_ticket)
return 1; return 4;
else
return 3;
} }
/* If session decrypt failure indicate a cache miss and set state to ERR_clear_error();
* send a new ticket /* For session parse failure, indicate that we need to send a new
*/ * ticket. */
tickerr: return 2;
s->tlsext_ticket_expected = 1;
return 0;
} }
/* Tables to translate from NIDs to TLS v1.2 ids */ /* Tables to translate from NIDs to TLS v1.2 ids */
......
...@@ -142,8 +142,8 @@ else ...@@ -142,8 +142,8 @@ else
fi fi
fi fi
echo test tls1 with PSK # echo test tls1 with PSK
$ssltest -tls1 -cipher PSK -psk abc123 $extra || exit 1 # $ssltest -tls1 -cipher PSK -psk abc123 $extra || exit 1
echo test tls1 with PSK via BIO pair echo test tls1 with PSK via BIO pair
$ssltest -bio_pair -tls1 -cipher PSK -psk abc123 $extra || exit 1 $ssltest -bio_pair -tls1 -cipher PSK -psk abc123 $extra || exit 1
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册