From: Matt Caswell Date: Mon, 10 Apr 2017 15:13:20 +0000 (+0100) Subject: Extend the SERVERINFO file format to include an extensions context X-Git-Tag: OpenSSL_1_1_1-pre1~1609 X-Git-Url: https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff_plain;h=84c34ba8762463057d372e22ad98a045dbd9a51f;ds=sidebyside 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 (Merged from https://github.com/openssl/openssl/pull/3298) --- diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index b1da6c5a69..0fbd7425d4 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -1451,9 +1451,17 @@ __owur int SSL_use_PrivateKey_ASN1(int pk, SSL *ssl, const unsigned char *d, __owur int SSL_use_certificate(SSL *ssl, X509 *x); __owur int SSL_use_certificate_ASN1(SSL *ssl, const unsigned char *d, int len); + +/* serverinfo file format versions */ +# define SERVERINFOV1 1 +# define SERVERINFOV2 2 + /* Set serverinfo data for the current active cert. */ __owur int SSL_CTX_use_serverinfo(SSL_CTX *ctx, const unsigned char *serverinfo, size_t serverinfo_length); +__owur int SSL_CTX_use_serverinfo_ex(SSL_CTX *ctx, unsigned int version, + const unsigned char *serverinfo, + size_t serverinfo_length); __owur int SSL_CTX_use_serverinfo_file(SSL_CTX *ctx, const char *file); #ifndef OPENSSL_NO_RSA @@ -2328,6 +2336,7 @@ int ERR_load_SSL_strings(void); # define SSL_F_SSL_CTX_USE_RSAPRIVATEKEY_ASN1 178 # define SSL_F_SSL_CTX_USE_RSAPRIVATEKEY_FILE 179 # define SSL_F_SSL_CTX_USE_SERVERINFO 336 +# define SSL_F_SSL_CTX_USE_SERVERINFO_EX 543 # define SSL_F_SSL_CTX_USE_SERVERINFO_FILE 337 # define SSL_F_SSL_DANE_DUP 403 # define SSL_F_SSL_DANE_ENABLE 395 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 #include "ssl_locl.h" +#include "packet_locl.h" #include #include #include @@ -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); diff --git a/util/libssl.num b/util/libssl.num index a17ebbc6ad..b4acb5db35 100644 --- a/util/libssl.num +++ b/util/libssl.num @@ -449,3 +449,4 @@ SSL_get_record_padding_callback_arg 449 1_1_1 EXIST::FUNCTION: SSL_set_block_padding 450 1_1_1 EXIST::FUNCTION: SSL_set_record_padding_callback_arg 451 1_1_1 EXIST::FUNCTION: SSL_CTX_set_record_padding_callback_arg 452 1_1_1 EXIST::FUNCTION: +SSL_CTX_use_serverinfo_ex 453 1_1_1 EXIST::FUNCTION: