diff options
author | Matt Caswell <matt@openssl.org> | 2017-04-10 16:13:20 +0100 |
---|---|---|
committer | Matt Caswell <matt@openssl.org> | 2017-05-03 14:37:42 +0100 |
commit | 84c34ba8762463057d372e22ad98a045dbd9a51f (patch) | |
tree | 0562b023b190ead09b239fd71d0a77676de81922 /ssl | |
parent | f0ef20bf386b5c37ba5a4ce5c1de9a819bbeffb2 (diff) | |
download | openssl-84c34ba8762463057d372e22ad98a045dbd9a51f.tar.gz |
Extend the SERVERINFO file format to include an extensions context
This enables us to know what messages the extensions are relevant for in
TLSv1.3. The new file format is not compatible with the previous one so
we call it SERVERINFOV2.
Reviewed-by: Rich Salz <rsalz@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/3298)
Diffstat (limited to 'ssl')
-rw-r--r-- | ssl/ssl_err.c | 1 | ||||
-rw-r--r-- | ssl/ssl_rsa.c | 242 |
2 files changed, 154 insertions, 89 deletions
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c index 296ce0de03..a845dae421 100644 --- a/ssl/ssl_err.c +++ b/ssl/ssl_err.c @@ -174,6 +174,7 @@ static ERR_STRING_DATA SSL_str_functs[] = { {ERR_FUNC(SSL_F_SSL_CTX_USE_RSAPRIVATEKEY_FILE), "SSL_CTX_use_RSAPrivateKey_file"}, {ERR_FUNC(SSL_F_SSL_CTX_USE_SERVERINFO), "SSL_CTX_use_serverinfo"}, + {ERR_FUNC(SSL_F_SSL_CTX_USE_SERVERINFO_EX), "SSL_CTX_use_serverinfo_ex"}, {ERR_FUNC(SSL_F_SSL_CTX_USE_SERVERINFO_FILE), "SSL_CTX_use_serverinfo_file"}, {ERR_FUNC(SSL_F_SSL_DANE_DUP), "ssl_dane_dup"}, diff --git a/ssl/ssl_rsa.c b/ssl/ssl_rsa.c index 87be6460b5..a6824b0f6c 100644 --- a/ssl/ssl_rsa.c +++ b/ssl/ssl_rsa.c @@ -9,6 +9,7 @@ #include <stdio.h> #include "ssl_locl.h" +#include "packet_locl.h" #include <openssl/bio.h> #include <openssl/objects.h> #include <openssl/evp.h> @@ -693,50 +694,43 @@ static int serverinfo_find_extension(const unsigned char *serverinfo, const unsigned char **extension_data, size_t *extension_length) { + PACKET pkt, data; + *extension_data = NULL; *extension_length = 0; if (serverinfo == NULL || serverinfo_length == 0) return -1; + + if (!PACKET_buf_init(&pkt, serverinfo, serverinfo_length)) + return -1; + for (;;) { unsigned int type = 0; - size_t len = 0; + unsigned long context = 0; /* end of serverinfo */ - if (serverinfo_length == 0) + if (PACKET_remaining(&pkt) == 0) return 0; /* Extension not found */ - /* read 2-byte type field */ - if (serverinfo_length < 2) - return -1; /* Error */ - type = (serverinfo[0] << 8) + serverinfo[1]; - serverinfo += 2; - serverinfo_length -= 2; - - /* read 2-byte len field */ - if (serverinfo_length < 2) - return -1; /* Error */ - len = (serverinfo[0] << 8) + serverinfo[1]; - serverinfo += 2; - serverinfo_length -= 2; - - if (len > serverinfo_length) - return -1; /* Error */ + if (!PACKET_get_net_4(&pkt, &context) + || !PACKET_get_net_2(&pkt, &type) + || !PACKET_get_length_prefixed_2(&pkt, &data)) + return -1; if (type == extension_type) { - *extension_data = serverinfo; - *extension_length = len; + *extension_data = PACKET_data(&data); + *extension_length = PACKET_remaining(&data);; return 1; /* Success */ } - - serverinfo += len; - serverinfo_length -= len; } /* Unreachable */ } -static int serverinfo_srv_parse_cb(SSL *s, unsigned int ext_type, - const unsigned char *in, - size_t inlen, int *al, void *arg) +static int serverinfoex_srv_parse_cb(SSL *s, unsigned int ext_type, + unsigned int context, + const unsigned char *in, + size_t inlen, X509 *x, size_t chainidx, + int *al, void *arg) { if (inlen != 0) { @@ -747,9 +741,19 @@ static int serverinfo_srv_parse_cb(SSL *s, unsigned int ext_type, return 1; } -static int serverinfo_srv_add_cb(SSL *s, unsigned int ext_type, - const unsigned char **out, size_t *outlen, - int *al, void *arg) +static int serverinfo_srv_parse_cb(SSL *s, unsigned int ext_type, + const unsigned char *in, + size_t inlen, int *al, void *arg) +{ + return serverinfoex_srv_parse_cb(s, ext_type, 0, in, inlen, NULL, 0, al, + arg); +} + +static int serverinfoex_srv_add_cb(SSL *s, unsigned int ext_type, + unsigned int context, + const unsigned char **out, + size_t *outlen, X509 *x, size_t chainidx, + int *al, void *arg) { const unsigned char *serverinfo = NULL; size_t serverinfo_length = 0; @@ -772,81 +776,90 @@ static int serverinfo_srv_add_cb(SSL *s, unsigned int ext_type, * extension */ } +static int serverinfo_srv_add_cb(SSL *s, unsigned int ext_type, + const unsigned char **out, size_t *outlen, + int *al, void *arg) +{ + return serverinfoex_srv_add_cb(s, ext_type, 0, out, outlen, NULL, 0, al, + arg); +} + /* * With a NULL context, this function just checks that the serverinfo data * parses correctly. With a non-NULL context, it registers callbacks for * the included extensions. */ -static int serverinfo_process_buffer(const unsigned char *serverinfo, +static int serverinfo_process_buffer(unsigned int version, + const unsigned char *serverinfo, size_t serverinfo_length, SSL_CTX *ctx) { + PACKET pkt; + if (serverinfo == NULL || serverinfo_length == 0) return 0; - for (;;) { - unsigned int ext_type = 0; - size_t len = 0; - - /* end of serverinfo */ - if (serverinfo_length == 0) - return 1; - /* read 2-byte type field */ - if (serverinfo_length < 2) - return 0; - /* FIXME: check for types we understand explicitly? */ - - /* Register callbacks for extensions */ - ext_type = (serverinfo[0] << 8) + serverinfo[1]; - if (ctx != NULL - && custom_ext_find(&ctx->cert->custext, ENDPOINT_SERVER, - ext_type, NULL) - == NULL - && !SSL_CTX_add_server_custom_ext(ctx, ext_type, - serverinfo_srv_add_cb, - NULL, NULL, - serverinfo_srv_parse_cb, - NULL)) - return 0; + if (version != SERVERINFOV1 && version != SERVERINFOV2) + return 0; - serverinfo += 2; - serverinfo_length -= 2; + if (!PACKET_buf_init(&pkt, serverinfo, serverinfo_length)) + return 0; - /* read 2-byte len field */ - if (serverinfo_length < 2) - return 0; - len = (serverinfo[0] << 8) + serverinfo[1]; - serverinfo += 2; - serverinfo_length -= 2; + while (PACKET_remaining(&pkt)) { + unsigned long context = 0; + unsigned int ext_type = 0; + PACKET data; - if (len > serverinfo_length) + if (!PACKET_get_net_4(&pkt, &context) + || !PACKET_get_net_2(&pkt, &ext_type) + || !PACKET_get_length_prefixed_2(&pkt, &data)) return 0; - serverinfo += len; - serverinfo_length -= len; + if (ctx == NULL) + continue; + + if (version == SERVERINFOV1) { + if (!SSL_CTX_add_server_custom_ext(ctx, ext_type, + serverinfo_srv_add_cb, + NULL, NULL, + serverinfo_srv_parse_cb, + NULL)) + return 0; + } else { + if (!SSL_CTX_add_custom_ext(ctx, ext_type, context, + serverinfoex_srv_add_cb, + NULL, NULL, + serverinfoex_srv_parse_cb, + NULL)) + return 0; + } } + + return 1; } -int SSL_CTX_use_serverinfo(SSL_CTX *ctx, const unsigned char *serverinfo, - size_t serverinfo_length) +int SSL_CTX_use_serverinfo_ex(SSL_CTX *ctx, unsigned int version, + const unsigned char *serverinfo, + size_t serverinfo_length) { unsigned char *new_serverinfo; if (ctx == NULL || serverinfo == NULL || serverinfo_length == 0) { - SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO, ERR_R_PASSED_NULL_PARAMETER); + SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_EX, ERR_R_PASSED_NULL_PARAMETER); return 0; } - if (!serverinfo_process_buffer(serverinfo, serverinfo_length, NULL)) { - SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO, SSL_R_INVALID_SERVERINFO_DATA); + if (!serverinfo_process_buffer(version, serverinfo, serverinfo_length, + NULL)) { + SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_EX, SSL_R_INVALID_SERVERINFO_DATA); return 0; } if (ctx->cert->key == NULL) { - SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO, ERR_R_INTERNAL_ERROR); + SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_EX, ERR_R_INTERNAL_ERROR); return 0; } new_serverinfo = OPENSSL_realloc(ctx->cert->key->serverinfo, serverinfo_length); if (new_serverinfo == NULL) { - SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO, ERR_R_MALLOC_FAILURE); + SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_EX, ERR_R_MALLOC_FAILURE); return 0; } ctx->cert->key->serverinfo = new_serverinfo; @@ -857,13 +870,21 @@ int SSL_CTX_use_serverinfo(SSL_CTX *ctx, const unsigned char *serverinfo, * Now that the serverinfo is validated and stored, go ahead and * register callbacks. */ - if (!serverinfo_process_buffer(serverinfo, serverinfo_length, ctx)) { - SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO, SSL_R_INVALID_SERVERINFO_DATA); + if (!serverinfo_process_buffer(version, serverinfo, serverinfo_length, + ctx)) { + SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_EX, SSL_R_INVALID_SERVERINFO_DATA); return 0; } return 1; } +int SSL_CTX_use_serverinfo(SSL_CTX *ctx, const unsigned char *serverinfo, + size_t serverinfo_length) +{ + return SSL_CTX_use_serverinfo_ex(ctx, SERVERINFOV1, serverinfo, + serverinfo_length); +} + int SSL_CTX_use_serverinfo_file(SSL_CTX *ctx, const char *file) { unsigned char *serverinfo = NULL; @@ -873,10 +894,12 @@ int SSL_CTX_use_serverinfo_file(SSL_CTX *ctx, const char *file) long extension_length = 0; char *name = NULL; char *header = NULL; - char namePrefix[] = "SERVERINFO FOR "; + char namePrefix1[] = "SERVERINFO FOR "; + char namePrefix2[] = "SERVERINFOV2 FOR "; int ret = 0; BIO *bin = NULL; - size_t num_extensions = 0; + size_t num_extensions = 0, contextoff = 0; + unsigned int version; if (ctx == NULL || file == NULL) { SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, ERR_R_PASSED_NULL_PARAMETER); @@ -907,32 +930,72 @@ int SSL_CTX_use_serverinfo_file(SSL_CTX *ctx, const char *file) break; } /* Check that PEM name starts with "BEGIN SERVERINFO FOR " */ - if (strlen(name) < strlen(namePrefix)) { + if (strlen(name) < strlen(namePrefix1)) { SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, SSL_R_PEM_NAME_TOO_SHORT); goto end; } - if (strncmp(name, namePrefix, strlen(namePrefix)) != 0) { - SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, - SSL_R_PEM_NAME_BAD_PREFIX); - goto end; + if (strncmp(name, namePrefix1, strlen(namePrefix1)) == 0) { + version = SERVERINFOV1; + } else { + if (strlen(name) < strlen(namePrefix2)) { + SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, + SSL_R_PEM_NAME_TOO_SHORT); + goto end; + } + if (strncmp(name, namePrefix2, strlen(namePrefix2)) != 0) { + SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, + SSL_R_PEM_NAME_BAD_PREFIX); + goto end; + } + version = SERVERINFOV2; } /* * Check that the decoded PEM data is plausible (valid length field) */ - if (extension_length < 4 - || (extension[2] << 8) + extension[3] != extension_length - 4) { - SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, SSL_R_BAD_DATA); - goto end; + if (version == SERVERINFOV1) { + /* 4 byte header: 2 bytes type, 2 bytes len */ + if (extension_length < 4 + || (extension[2] << 8) + extension[3] + != extension_length - 4) { + SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, SSL_R_BAD_DATA); + goto end; + } + /* + * File does not have a context value so we must take account of + * this later. + */ + contextoff = 4; + } else { + /* 8 byte header: 4 bytes context, 2 bytes type, 2 bytes len */ + if (extension_length < 8 + || (extension[6] << 8) + extension[7] + != extension_length - 8) { + SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, SSL_R_BAD_DATA); + goto end; + } } /* Append the decoded extension to the serverinfo buffer */ - tmp = OPENSSL_realloc(serverinfo, serverinfo_length + extension_length); + tmp = OPENSSL_realloc(serverinfo, serverinfo_length + extension_length + + contextoff); if (tmp == NULL) { SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, ERR_R_MALLOC_FAILURE); goto end; } serverinfo = tmp; - memcpy(serverinfo + serverinfo_length, extension, extension_length); - serverinfo_length += extension_length; + if (contextoff > 0) { + unsigned int synthcontext = SSL_EXT_CLIENT_HELLO + | SSL_EXT_TLS1_2_SERVER_HELLO; + unsigned char *sinfo = serverinfo + serverinfo_length; + + /* We know this only uses the last 2 bytes */ + sinfo[0] = 0; + sinfo[1] = 0; + sinfo[2] = (synthcontext >> 8) & 0xff; + sinfo[3] = synthcontext & 0xff; + } + memcpy(serverinfo + serverinfo_length + contextoff, + extension, extension_length); + serverinfo_length += extension_length + contextoff; OPENSSL_free(name); name = NULL; @@ -942,7 +1005,8 @@ int SSL_CTX_use_serverinfo_file(SSL_CTX *ctx, const char *file) extension = NULL; } - ret = SSL_CTX_use_serverinfo(ctx, serverinfo, serverinfo_length); + ret = SSL_CTX_use_serverinfo_ex(ctx, version, serverinfo, + serverinfo_length); end: /* SSL_CTX_use_serverinfo makes a local copy of the serverinfo. */ OPENSSL_free(name); |