diff options
author | Dr. Stephen Henson <steve@openssl.org> | 2007-08-11 23:18:29 +0000 |
---|---|---|
committer | Dr. Stephen Henson <steve@openssl.org> | 2007-08-11 23:18:29 +0000 |
commit | 6434abbfc6ac0d5cb882844ed10fef5821039cf6 (patch) | |
tree | 04b0b4626e1ccd18fb80965716957144ceb839b9 /ssl | |
parent | e45c100762baaaa0bfd9e9db136192ec09286a55 (diff) |
RFC4507 (including RFC4507bis) TLS stateless session resumption support
for OpenSSL.
Diffstat (limited to 'ssl')
-rw-r--r-- | ssl/s2_srvr.c | 2 | ||||
-rw-r--r-- | ssl/s3_clnt.c | 130 | ||||
-rw-r--r-- | ssl/s3_lib.c | 9 | ||||
-rw-r--r-- | ssl/s3_srvr.c | 107 | ||||
-rw-r--r-- | ssl/ssl.h | 21 | ||||
-rw-r--r-- | ssl/ssl3.h | 5 | ||||
-rw-r--r-- | ssl/ssl_asn1.c | 69 | ||||
-rw-r--r-- | ssl/ssl_err.c | 2 | ||||
-rw-r--r-- | ssl/ssl_lib.c | 10 | ||||
-rw-r--r-- | ssl/ssl_locl.h | 6 | ||||
-rw-r--r-- | ssl/ssl_sess.c | 40 | ||||
-rw-r--r-- | ssl/ssl_txt.c | 16 | ||||
-rw-r--r-- | ssl/t1_lib.c | 202 | ||||
-rw-r--r-- | ssl/tls1.h | 7 |
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 @@ -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_serverhell |