Early callback support.
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 726ec51..d1a67b3 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -213,6 +213,176 @@
s->version = s->method->version;
}
+char ssl_early_callback_init(struct ssl_early_callback_ctx *ctx)
+ {
+ size_t len = ctx->client_hello_len;
+ const unsigned char *p = ctx->client_hello;
+ uint16_t *extension_types;
+ unsigned num_extensions;
+
+ /* Skip client version. */
+ if (len < 2)
+ return 0;
+ len -= 2; p += 2;
+
+ /* Skip client nonce. */
+ if (len < 32)
+ return 0;
+ len -= 32; p += 32;
+
+ /* Get length of session id. */
+ if (len < 1)
+ return 0;
+ ctx->session_id_len = *p;
+ p++; len--;
+
+ ctx->session_id = p;
+ if (len < ctx->session_id_len)
+ return 0;
+ p += ctx->session_id_len; len -= ctx->session_id_len;
+
+ /* Skip past DTLS cookie */
+ if (ctx->ssl->version == DTLS1_VERSION || ctx->ssl->version == DTLS1_BAD_VER)
+ {
+ unsigned cookie_len;
+
+ if (len < 1)
+ return 0;
+ cookie_len = *p;
+ p++; len--;
+ if (len < cookie_len)
+ return 0;
+ p += cookie_len; len -= cookie_len;
+ }
+
+ /* Skip cipher suites. */
+ if (len < 2)
+ return 0;
+ n2s(p, ctx->cipher_suites_len);
+ len -= 2;
+
+ if ((ctx->cipher_suites_len & 1) != 0)
+ return 0;
+
+ ctx->cipher_suites = p;
+ if (len < ctx->cipher_suites_len)
+ return 0;
+ p += ctx->cipher_suites_len; len -= ctx->cipher_suites_len;
+
+ /* Skip compression methods. */
+ if (len < 1)
+ return 0;
+ ctx->compression_methods_len = *p;
+ p++; len--;
+
+ ctx->compression_methods = p;
+ if (len < ctx->compression_methods_len)
+ return 0;
+ p += ctx->compression_methods_len; len -= ctx->compression_methods_len;
+
+ /* If the ClientHello ends here then it's valid, but doesn't have any
+ * extensions. (E.g. SSLv3.) */
+ if (len == 0)
+ {
+ ctx->extensions = NULL;
+ ctx->extensions_len = 0;
+ return 1;
+ }
+
+ if (len < 2)
+ return 0;
+ n2s(p, ctx->extensions_len);
+ len -= 2;
+
+ if (ctx->extensions_len == 0 && len == 0)
+ {
+ ctx->extensions = NULL;
+ return 1;
+ }
+
+ ctx->extensions = p;
+ if (len != ctx->extensions_len)
+ return 0;
+
+ /* Verify that the extensions have valid lengths and that there are
+ * no duplicates. Each extension takes, at least, four bytes, so
+ * we can allocate a buffer of extensions_len/4 elements and be sure
+ * that we have enough space for all the extension types. */
+ extension_types =
+ OPENSSL_malloc(sizeof(uint16_t) * ctx->extensions_len/4);
+ if (extension_types == NULL)
+ return 0;
+ num_extensions = 0;
+
+ while (len != 0)
+ {
+ uint16_t extension_type, extension_len;
+ unsigned i;
+
+ if (len < 4)
+ goto err;
+ n2s(p, extension_type);
+ n2s(p, extension_len);
+ len -= 4;
+
+ if (len < extension_len)
+ goto err;
+ p += extension_len; len -= extension_len;
+
+ for (i = 0; i < num_extensions; i++)
+ {
+ if (extension_types[i] == extension_type)
+ {
+ /* Duplicate extension type. */
+ goto err;
+ }
+ }
+ extension_types[num_extensions] = extension_type;
+ num_extensions++;
+ }
+
+ OPENSSL_free(extension_types);
+ return 1;
+
+err:
+ OPENSSL_free(extension_types);
+ return 0;
+ }
+
+char
+SSL_early_callback_ctx_extension_get(const struct ssl_early_callback_ctx *ctx,
+ uint16_t extension_type,
+ const unsigned char **out_data,
+ size_t *out_len)
+ {
+ size_t len = ctx->extensions_len;
+ const unsigned char *p = ctx->extensions;
+
+ while (len != 0)
+ {
+ uint16_t ext_type, ext_len;
+
+ if (len < 4)
+ return 0;
+ n2s(p, ext_type);
+ n2s(p, ext_len);
+ len -= 4;
+
+ if (len < ext_len)
+ return 0;
+ if (ext_type == extension_type)
+ {
+ *out_data = p;
+ *out_len = ext_len;
+ return 1;
+ }
+
+ p += ext_len; len -= ext_len;
+ }
+
+ return 0;
+ }
+
#ifndef OPENSSL_NO_EC
static int nid_list[] =
@@ -3423,11 +3593,8 @@
* 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.
+ * ctx: contains the early callback context, which is the result of a
+ * shallow parse of the ClientHello.
* ret: (output) on return, if a ticket was decrypted, then this is set to
* point to the resulting session.
*
@@ -3452,91 +3619,58 @@
* 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,
- const unsigned char *limit, SSL_SESSION **ret)
+int tls1_process_ticket(SSL *s, const struct ssl_early_callback_ctx *ctx,
+ SSL_SESSION **ret)
{
- /* Point after session ID in client hello */
- const unsigned char *p = session_id + len;
- unsigned short i;
-
*ret = NULL;
s->tlsext_ticket_expected = 0;
+ const unsigned char *data;
+ size_t len;
+ int r;
/* 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 ((s->version <= SSL3_VERSION) || !limit)
+ if ((s->version <= SSL3_VERSION) && !ctx->extensions)
return 0;
- if (p >= limit)
- return -1;
- /* Skip past DTLS cookie */
- if (SSL_IS_DTLS(s))
+ if (!SSL_early_callback_ctx_extension_get(
+ ctx, TLSEXT_TYPE_session_ticket, &data, &len))
{
- i = *(p++);
- p+= i;
- if (p >= limit)
+ return 0;
+ }
+ if (len == 0)
+ {
+ /* The client will accept a ticket but doesn't
+ * currently have one. */
+ s->tlsext_ticket_expected = 1;
+ return 1;
+ }
+ if (s->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, data, len, ctx->session_id,
+ ctx->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;
}
- /* 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 0;
- n2s(p, i);
- while ((p + 4) <= limit)
- {
- unsigned short type, size;
- n2s(p, type);
- n2s(p, size);
- if (p + size > limit)
- return 0;
- if (type == TLSEXT_TYPE_session_ticket)
- {
- int r;
- if (size == 0)
- {
- /* The client will accept a ticket but doesn't
- * currently have one. */
- s->tlsext_ticket_expected = 1;
- return 1;
- }
- if (s->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, 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;
- }
- }
- p += size;
- }
- return 0;
}
/* tls_decrypt_ticket attempts to decrypt a session ticket.