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 tickets disabled behave as if no ticket present * to permit stateful resumption. */ if (SSL_get_options(s) & SSL_OP_NO_TICKET) return 1; 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 zero length note 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; }
/* Since the server cache lookup is done early on in the processing of the * ClientHello, and other operations depend on the result, we need to handle * 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->internal->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->internal->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->internal->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->internal->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->internal->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->internal->tlsext_ticket_expected is set to 0. */ int tls1_process_ticket(SSL *s, const unsigned char *session, int session_len, const unsigned char *limit, SSL_SESSION **ret) { /* Point after session ID in client hello */ CBS session_id, cookie, cipher_list, compress_algo, extensions; *ret = NULL; s->internal->tlsext_ticket_expected = 0; /* If tickets disabled behave as if no ticket present * to permit stateful resumption. */ if (SSL_get_options(s) & SSL_OP_NO_TICKET) return 0; if (!limit) return 0; if (limit < session) return -1; CBS_init(&session_id, session, limit - session); /* Skip past the session id */ if (!CBS_skip(&session_id, session_len)) return -1; /* Skip past DTLS cookie */ if (SSL_IS_DTLS(s)) { if (!CBS_get_u8_length_prefixed(&session_id, &cookie)) return -1; } /* Skip past cipher list */ if (!CBS_get_u16_length_prefixed(&session_id, &cipher_list)) return -1; /* Skip past compression algorithm list */ if (!CBS_get_u8_length_prefixed(&session_id, &compress_algo)) return -1; /* Now at start of extensions */ if (CBS_len(&session_id) == 0) return 0; if (!CBS_get_u16_length_prefixed(&session_id, &extensions)) return -1; while (CBS_len(&extensions) > 0) { CBS ext_data; uint16_t ext_type; if (!CBS_get_u16(&extensions, &ext_type) || !CBS_get_u16_length_prefixed(&extensions, &ext_data)) return -1; if (ext_type == TLSEXT_TYPE_session_ticket) { int r; if (CBS_len(&ext_data) == 0) { /* The client will accept a ticket but doesn't * currently have one. */ s->internal->tlsext_ticket_expected = 1; return 1; } if (s->internal->tls_session_secret_cb) { /* Indicate that the ticket couldn't be * decrypted rather than generating the session * from ticket now, trigger abbreviated * handshake based on external mechanism to * calculate the master secret later. */ return 2; } r = tls_decrypt_ticket(s, CBS_data(&ext_data), CBS_len(&ext_data), session, session_len, ret); switch (r) { case 2: /* ticket couldn't be decrypted */ s->internal->tlsext_ticket_expected = 1; return 2; case 3: /* ticket was decrypted */ return r; case 4: /* ticket decrypted but need to renew */ s->internal->tlsext_ticket_expected = 1; return 3; default: /* fatal error */ return -1; } } } return 0; }
int tls_parse_ctos_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx, int *al) { PACKET identities, binders, binder; size_t binderoffset, hashsize; SSL_SESSION *sess = NULL; unsigned int id, i, ext = 0; const EVP_MD *md = NULL; /* * If we have no PSK kex mode that we recognise then we can't resume so * ignore this extension */ if ((s->ext.psk_kex_mode & (TLSEXT_KEX_MODE_FLAG_KE | TLSEXT_KEX_MODE_FLAG_KE_DHE)) == 0) return 1; if (!PACKET_get_length_prefixed_2(pkt, &identities)) { *al = SSL_AD_DECODE_ERROR; return 0; } for (id = 0; PACKET_remaining(&identities) != 0; id++) { PACKET identity; unsigned long ticket_agel; if (!PACKET_get_length_prefixed_2(&identities, &identity) || !PACKET_get_net_4(&identities, &ticket_agel)) { *al = SSL_AD_DECODE_ERROR; return 0; } if (s->psk_find_session_cb != NULL && !s->psk_find_session_cb(s, PACKET_data(&identity), PACKET_remaining(&identity), &sess)) { *al = SSL_AD_INTERNAL_ERROR; return 0; } if (sess != NULL) { /* We found a PSK */ SSL_SESSION *sesstmp = ssl_session_dup(sess, 0); if (sesstmp == NULL) { *al = SSL_AD_INTERNAL_ERROR; return 0; } SSL_SESSION_free(sess); sess = sesstmp; /* * We've just been told to use this session for this context so * make sure the sid_ctx matches up. */ memcpy(sess->sid_ctx, s->sid_ctx, s->sid_ctx_length); sess->sid_ctx_length = s->sid_ctx_length; ext = 1; } else { uint32_t ticket_age = 0, now, agesec, agems; int ret = tls_decrypt_ticket(s, PACKET_data(&identity), PACKET_remaining(&identity), NULL, 0, &sess); if (ret == TICKET_FATAL_ERR_MALLOC || ret == TICKET_FATAL_ERR_OTHER) { *al = SSL_AD_INTERNAL_ERROR; return 0; } if (ret == TICKET_NO_DECRYPT) continue; ticket_age = (uint32_t)ticket_agel; now = (uint32_t)time(NULL); agesec = now - (uint32_t)sess->time; agems = agesec * (uint32_t)1000; ticket_age -= sess->ext.tick_age_add; /* * For simplicity we do our age calculations in seconds. If the * client does it in ms then it could appear that their ticket age * is longer than ours (our ticket age calculation should always be * slightly longer than the client's due to the network latency). * Therefore we add 1000ms to our age calculation to adjust for * rounding errors. */ if (sess->timeout >= (long)agesec && agems / (uint32_t)1000 == agesec && ticket_age <= agems + 1000 && ticket_age + TICKET_AGE_ALLOWANCE >= agems + 1000) { /* * Ticket age is within tolerance and not expired. We allow it * for early data */ s->ext.early_data_ok = 1; } } md = ssl_md(sess->cipher->algorithm2); if (md != ssl_md(s->s3->tmp.new_cipher->algorithm2)) { /* The ciphersuite is not compatible with this session. */ SSL_SESSION_free(sess); sess = NULL; continue; } break; } if (sess == NULL) return 1; binderoffset = PACKET_data(pkt) - (const unsigned char *)s->init_buf->data; hashsize = EVP_MD_size(md); if (!PACKET_get_length_prefixed_2(pkt, &binders)) { *al = SSL_AD_DECODE_ERROR; goto err; } for (i = 0; i <= id; i++) { if (!PACKET_get_length_prefixed_1(&binders, &binder)) { *al = SSL_AD_DECODE_ERROR; goto err; } } if (PACKET_remaining(&binder) != hashsize || tls_psk_do_binder(s, md, (const unsigned char *)s->init_buf->data, binderoffset, PACKET_data(&binder), NULL, sess, 0, ext) != 1) { *al = SSL_AD_DECODE_ERROR; SSLerr(SSL_F_TLS_PARSE_CTOS_PSK, ERR_R_INTERNAL_ERROR); goto err; } sess->ext.tick_identity = id; SSL_SESSION_free(s->session); s->session = sess; return 1; err: SSL_SESSION_free(sess); return 0; }
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 tickets disabled behave as if no ticket present * to permit stateful resumption. */ if (SSL_get_options(s) & SSL_OP_NO_TICKET) return 1; if ((s->version <= SSL3_VERSION) || !limit) return 1; if (p >= limit) return -1; /* Skip past DTLS cookie */ if (s->version == DTLS1_VERSION || s->version == DTLS1_BAD_VER) { i = *(p++); p+= i; 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 1; /* If zero length note 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 */ } if (s->tls_session_secret_cb) { /* Indicate cache miss here and instead of * generating the session from ticket now, * trigger abbreviated handshake based on * external mechanism to calculate the master * secret later. */ return 0; } return tls_decrypt_ticket(s, p, size, session_id, len, ret); } p += size; } return 1; }
int tls_parse_ctos_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx, int *al) { PACKET identities, binders, binder; size_t binderoffset, hashsize; SSL_SESSION *sess = NULL; unsigned int id, i; const EVP_MD *md = NULL; uint32_t ticket_age = 0, now, agesec, agems; /* * If we have no PSK kex mode that we recognise then we can't resume so * ignore this extension */ if ((s->ext.psk_kex_mode & (TLSEXT_KEX_MODE_FLAG_KE | TLSEXT_KEX_MODE_FLAG_KE_DHE)) == 0) return 1; if (!PACKET_get_length_prefixed_2(pkt, &identities)) { *al = SSL_AD_DECODE_ERROR; return 0; } for (id = 0; PACKET_remaining(&identities) != 0; id++) { PACKET identity; unsigned long ticket_agel; int ret; if (!PACKET_get_length_prefixed_2(&identities, &identity) || !PACKET_get_net_4(&identities, &ticket_agel)) { *al = SSL_AD_DECODE_ERROR; return 0; } ticket_age = (uint32_t)ticket_agel; ret = tls_decrypt_ticket(s, PACKET_data(&identity), PACKET_remaining(&identity), NULL, 0, &sess); if (ret == TICKET_FATAL_ERR_MALLOC || ret == TICKET_FATAL_ERR_OTHER) { *al = SSL_AD_INTERNAL_ERROR; return 0; } if (ret == TICKET_NO_DECRYPT) continue; md = ssl_md(sess->cipher->algorithm2); if (md == NULL) { /* * Don't recognise this cipher so we can't use the session. * Ignore it */ SSL_SESSION_free(sess); sess = NULL; continue; } /* * TODO(TLS1.3): Somehow we need to handle the case of a ticket renewal. * Ignored for now */ break; } if (sess == NULL) return 1; binderoffset = PACKET_data(pkt) - (const unsigned char *)s->init_buf->data; hashsize = EVP_MD_size(md); if (!PACKET_get_length_prefixed_2(pkt, &binders)) { *al = SSL_AD_DECODE_ERROR; goto err; } for (i = 0; i <= id; i++) { if (!PACKET_get_length_prefixed_1(&binders, &binder)) { *al = SSL_AD_DECODE_ERROR; goto err; } } if (PACKET_remaining(&binder) != hashsize || tls_psk_do_binder(s, md, (const unsigned char *)s->init_buf->data, binderoffset, PACKET_data(&binder), NULL, sess, 0) != 1) { *al = SSL_AD_DECODE_ERROR; SSLerr(SSL_F_TLS_PARSE_CTOS_PSK, ERR_R_INTERNAL_ERROR); goto err; } sess->ext.tick_identity = id; now = (uint32_t)time(NULL); agesec = now - (uint32_t)sess->time; agems = agesec * (uint32_t)1000; ticket_age -= sess->ext.tick_age_add; /* * For simplicity we do our age calculations in seconds. If the client does * it in ms then it could appear that their ticket age is longer than ours * (our ticket age calculation should always be slightly longer than the * client's due to the network latency). Therefore we add 1000ms to our age * calculation to adjust for rounding errors. */ if (sess->timeout >= (long)agesec && agems / (uint32_t)1000 == agesec && ticket_age <= agems + 1000 && ticket_age + TICKET_AGE_ALLOWANCE >= agems + 1000) { /* * Ticket age is within tolerance and not expired. We allow it for early * data */ s->ext.early_data_ok = 1; } SSL_SESSION_free(s->session); s->session = sess; return 1; err: SSL_SESSION_free(sess); return 0; }