From e27711cfddb15b3bd0c42c804d37ea0f33a3e4e5 Mon Sep 17 00:00:00 2001 From: Trevor Date: Sun, 12 May 2013 18:55:27 -0700 Subject: [PATCH] Trying cherrypick: Add support for arbitrary TLS extensions. Contributed by Trevor Perrin. Conflicts: CHANGES ssl/ssl.h ssl/ssltest.c test/testssl Fix compilation due to #endif. Cherrypicking more stuff. Cleanup of custom extension stuff. serverinfo rejects non-empty extensions. Omit extension if no relevant serverinfo data. Improve error-handling in serverinfo callback. Cosmetic cleanups. s_client documentation. s_server documentation. SSL_CTX_serverinfo documentation. Cleaup -1 and NULL callback handling for custom extensions, add tests. Cleanup ssl_rsa.c serverinfo code. Whitespace cleanup. Improve comments in ssl.h for serverinfo. Whitespace. Cosmetic cleanup. Reject non-zero-len serverinfo extensions. Whitespace. Make it build. Conflicts: test/testssl --- CHANGES | 3 + apps/s_client.c | 62 ++++++ apps/s_server.c | 15 ++ doc/apps/s_client.pod | 8 + doc/apps/s_server.pod | 9 + doc/ssl/SSL_CTX_use_serverinfo.pod | 45 ++++ ssl/s3_lib.c | 8 + ssl/ssl.h | 94 +++++++++ ssl/ssl3.h | 9 + ssl/ssl_cert.c | 33 ++- ssl/ssl_err.c | 3 + ssl/ssl_lib.c | 83 ++++++++ ssl/ssl_locl.h | 12 ++ ssl/ssl_rsa.c | 266 ++++++++++++++++++++++- ssl/ssltest.c | 325 +++++++++++++++++++++++++++++ ssl/t1_lib.c | 143 +++++++++++++ test/Makefile | 2 +- test/serverinfo.pem | 16 ++ test/testssl | 19 ++ util/pl/unix.pl | 1 + 20 files changed, 1151 insertions(+), 5 deletions(-) create mode 100644 doc/ssl/SSL_CTX_use_serverinfo.pod create mode 100644 test/serverinfo.pem diff --git a/CHANGES b/CHANGES index 7d332674db..751819bf6f 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,9 @@ Changes between 1.0.1 and 1.0.2 [xx XXX xxxx] + *) Add callbacks for arbitrary TLS extensions. + [Trevor Perrin and Ben Laurie] + *) New option -crl_download in several openssl utilities to download CRLs from CRLDP extension in certificates. [Steve Henson] diff --git a/apps/s_client.c b/apps/s_client.c index ad88c3770b..e08534b44d 100644 --- a/apps/s_client.c +++ b/apps/s_client.c @@ -364,6 +364,9 @@ static void sc_usage(void) # ifndef OPENSSL_NO_NEXTPROTONEG BIO_printf(bio_err," -nextprotoneg arg - enable NPN extension, considering named protocols supported (comma-separated list)\n"); # endif +#ifndef OPENSSL_NO_TLSEXT + BIO_printf(bio_err," -serverinfo types - send empty ClientHello extensions (comma-separated numbers)\n"); +#endif #endif BIO_printf(bio_err," -legacy_renegotiation - enable use of legacy renegotiation (dangerous)\n"); BIO_printf(bio_err," -use_srtp profiles - Offer SRTP key management with a colon-separated profile list\n"); @@ -541,6 +544,26 @@ static int next_proto_cb(SSL *s, unsigned char **out, unsigned char *outlen, con return SSL_TLSEXT_ERR_OK; } # endif /* ndef OPENSSL_NO_NEXTPROTONEG */ + +static int serverinfo_cli_cb(SSL* s, unsigned short ext_type, + const unsigned char* in, unsigned short inlen, + int* al, void* arg) + { + char pem_name[100]; + unsigned char ext_buf[4 + 65536]; + + /* Reconstruct the type/len fields prior to extension data */ + ext_buf[0] = ext_type >> 8; + ext_buf[1] = ext_type & 0xFF; + ext_buf[2] = inlen >> 8; + ext_buf[3] = inlen & 0xFF; + memcpy(ext_buf+4, in, inlen); + + BIO_snprintf(pem_name, sizeof(pem_name), "SERVER_INFO %d", ext_type); + PEM_write_bio(bio_c_out, pem_name, "", ext_buf, 4 + inlen); + return 1; + } + #endif enum @@ -613,6 +636,9 @@ int MAIN(int argc, char **argv) # ifndef OPENSSL_NO_NEXTPROTONEG const char *next_proto_neg_in = NULL; # endif +# define MAX_SI_TYPES 100 + unsigned short serverinfo_types[MAX_SI_TYPES]; + int serverinfo_types_count = 0; #endif char *sess_in = NULL; char *sess_out = NULL; @@ -949,6 +975,29 @@ static char *jpake_secret = NULL; next_proto_neg_in = *(++argv); } # endif + else if (strcmp(*argv,"-serverinfo") == 0) + { + char *c; + int start = 0; + int len; + + if (--argc < 1) goto bad; + c = *(++argv); + serverinfo_types_count = 0; + len = strlen(c); + for (i = 0; i <= len; ++i) + { + if (i == len || c[i] == ',') + { + serverinfo_types[serverinfo_types_count] + = atoi(c+start); + serverinfo_types_count++; + start = i+1; + } + if (serverinfo_types_count == MAX_SI_TYPES) + break; + } + } #endif #ifdef FIONBIO else if (strcmp(*argv,"-nbio") == 0) @@ -1242,6 +1291,19 @@ bad: if (next_proto.data) SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &next_proto); #endif +#ifndef OPENSSL_NO_TLSEXT + if (serverinfo_types_count) + { + for (i = 0; i < serverinfo_types_count; i++) + { + SSL_CTX_set_custom_cli_ext(ctx, + serverinfo_types[i], + NULL, + serverinfo_cli_cb, + NULL); + } + } +#endif if (state) SSL_CTX_set_info_callback(ctx,apps_ssl_info_callback); #if 0 diff --git a/apps/s_server.c b/apps/s_server.c index 9acc374c01..6bd3ac7a84 100644 --- a/apps/s_server.c +++ b/apps/s_server.c @@ -311,6 +311,8 @@ static int cert_chain = 0; #ifndef OPENSSL_NO_TLSEXT static BIO *authz_in = NULL; static const char *s_authz_file = NULL; +static BIO *serverinfo_in = NULL; +static const char *s_serverinfo_file = NULL; #endif #ifndef OPENSSL_NO_PSK @@ -471,6 +473,9 @@ static void sv_usage(void) BIO_printf(bio_err," -cert arg - certificate file to use\n"); BIO_printf(bio_err," (default is %s)\n",TEST_CERT); BIO_printf(bio_err," -authz arg - binary authz file for certificate\n"); +#ifndef OPENSSL_NO_TLSEXT + BIO_printf(bio_err," -serverinfo arg - PEM serverinfo file for certificate\n"); +#endif BIO_printf(bio_err," -crl_check - check the peer certificate has not been revoked by its CA.\n" \ " The CRL(s) are appended to the certificate file\n"); BIO_printf(bio_err," -crl_check_all - check the peer certificate has not been revoked by its CA\n" \ @@ -1065,6 +1070,11 @@ int MAIN(int argc, char *argv[]) if (--argc < 1) goto bad; s_authz_file = *(++argv); } + else if (strcmp(*argv,"-serverinfo") == 0) + { + if (--argc < 1) goto bad; + s_serverinfo_file = *(++argv); + } #endif else if (strcmp(*argv,"-certform") == 0) { @@ -1796,6 +1806,9 @@ bad: #ifndef OPENSSL_NO_TLSEXT if (s_authz_file != NULL && !SSL_CTX_use_authz_file(ctx, s_authz_file)) goto end; + if (s_serverinfo_file != NULL + && !SSL_CTX_use_serverinfo_file(ctx, s_serverinfo_file)) + goto end; #endif #ifndef OPENSSL_NO_TLSEXT if (ctx2 && !set_cert_key_stuff(ctx2,s_cert2,s_key2, NULL, build_chain)) @@ -1963,6 +1976,8 @@ end: EVP_PKEY_free(s_key2); if (authz_in != NULL) BIO_free(authz_in); + if (serverinfo_in != NULL) + BIO_free(serverinfo_in); #endif ssl_excert_free(exc); if (ssl_args) diff --git a/doc/apps/s_client.pod b/doc/apps/s_client.pod index 4ebf7b5854..8755930cec 100644 --- a/doc/apps/s_client.pod +++ b/doc/apps/s_client.pod @@ -43,6 +43,7 @@ B B [B<-sess_out filename>] [B<-sess_in filename>] [B<-rand file(s)>] +[B<-serverinfo types>] =head1 DESCRIPTION @@ -237,6 +238,13 @@ Multiple files can be specified separated by a OS-dependent character. The separator is B<;> for MS-Windows, B<,> for OpenVMS, and B<:> for all others. +=item B<-serverinfo types> + +a list of comma-separated TLS Extension Types (numbers between 0 and +65535). Each type will be sent as an empty ClientHello TLS Extension. +The server's response (if any) will be encoded and displayed as a PEM +file. + =back =head1 CONNECTED COMMANDS diff --git a/doc/apps/s_server.pod b/doc/apps/s_server.pod index 3e503e17e1..c1b40321c6 100644 --- a/doc/apps/s_server.pod +++ b/doc/apps/s_server.pod @@ -54,6 +54,7 @@ B B [B<-no_ticket>] [B<-id_prefix arg>] [B<-rand file(s)>] +[B<-serverinfo file>] =head1 DESCRIPTION @@ -276,6 +277,14 @@ Multiple files can be specified separated by a OS-dependent character. The separator is B<;> for MS-Windows, B<,> for OpenVMS, and B<:> for all others. +=item B<-serverinfo file> + +a file containing one or more blocks of PEM data. Each PEM block +must encode a TLS ServerHello extension (2 bytes type, 2 bytes length, +followed by "length" bytes of extension data). If the client sends +an empty TLS ClientHello extension matching the type, the corresponding +ServerHello extension will be returned. + =back =head1 CONNECTED COMMANDS diff --git a/doc/ssl/SSL_CTX_use_serverinfo.pod b/doc/ssl/SSL_CTX_use_serverinfo.pod new file mode 100644 index 0000000000..485b813b83 --- /dev/null +++ b/doc/ssl/SSL_CTX_use_serverinfo.pod @@ -0,0 +1,45 @@ +=pod + +=head1 NAME + +SSL_CTX_use_serverinfo, SSL_CTX_use_serverinfo_file + +=head1 SYNOPSIS + + #include + + int SSL_CTX_use_serverinfo(SSL_CTX *ctx, const unsigned char *serverinfo, + size_t serverinfo_length); + + int SSL_CTX_use_serverinfo_file(SSL_CTX *ctx, const char *file); + +=head1 DESCRIPTION + +These functions load "serverinfo" TLS ServerHello Extensions into the SSL_CTX. +A "serverinfo" extension is returned in response to an empty ClientHello +Extension. + +SSL_CTX_use_serverinfo_file() loads one or more serverinfo extensions from +a byte array into B. The extensions must be concatenated into a +sequence of bytes. Each extension must consist of a 2-byte Extension Type, +a 2-byte length, and then length bytes of extension_data. + +SSL_CTX_use_serverinfo_file() loads one or more serverinfo extensions from +B into B. The extensions must be in PEM format. Each extension +must consist of a 2-byte Extension Type, a 2-byte length, and then length +bytes of extension_data. + +=head1 NOTES + +=head1 RETURN VALUES + +On success, the functions return 1. +On failure, the functions return 0. Check out the error stack to find out +the reason. + +=head1 SEE ALSO + +=head1 HISTORY + + +=cut diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c index d71c819b00..9c2843b316 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c @@ -3008,6 +3008,8 @@ void ssl3_free(SSL *s) #ifndef OPENSSL_NO_TLSEXT if (s->s3->tlsext_authz_client_types != NULL) OPENSSL_free(s->s3->tlsext_authz_client_types); + if (s->s3->tlsext_custom_types != NULL) + OPENSSL_free(s->s3->tlsext_custom_types); #endif OPENSSL_cleanse(s->s3,sizeof *s->s3); OPENSSL_free(s->s3); @@ -3058,6 +3060,12 @@ void ssl3_clear(SSL *s) OPENSSL_free(s->s3->tlsext_authz_client_types); s->s3->tlsext_authz_client_types = NULL; } + if (s->s3->tlsext_custom_types != NULL) + { + OPENSSL_free(s->s3->tlsext_custom_types); + s->s3->tlsext_custom_types = NULL; + } + s->s3->tlsext_custom_types_count = 0; #endif rp = s->s3->rbuf.buf; diff --git a/ssl/ssl.h b/ssl/ssl.h index b8c8847a10..bd2b576308 100644 --- a/ssl/ssl.h +++ b/ssl/ssl.h @@ -383,6 +383,57 @@ DECLARE_STACK_OF(SRTP_PROTECTION_PROFILE) typedef int (*tls_session_ticket_ext_cb_fn)(SSL *s, const unsigned char *data, int len, void *arg); typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg); +#ifndef OPENSSL_NO_TLSEXT +/* Callbacks and structures for handling custom TLS Extensions: + * cli_ext_first_cb - sends data for ClientHello TLS Extension + * cli_ext_second_cb - receives data from ServerHello TLS Extension + * srv_ext_first_cb - receives data from ClientHello TLS Extension + * srv_ext_second_cb - sends data for ServerHello TLS Extension + * + * All these functions return nonzero on success. Zero will terminate + * the handshake (and return a specific TLS Fatal alert, if the function + * declaration has an "al" parameter). -1 for the "sending" functions + * will cause the TLS Extension to be omitted. + * + * "ext_type" is a TLS "ExtensionType" from 0-65535. + * "in" is a pointer to TLS "extension_data" being provided to the cb. + * "out" is used by the callback to return a pointer to "extension data" + * which OpenSSL will later copy into the TLS handshake. The contents + * of this buffer should not be changed until the handshake is complete. + * "inlen" and "outlen" are TLS Extension lengths from 0-65535. + * "al" is a TLS "AlertDescription" from 0-255 which WILL be sent as a + * fatal TLS alert, if the callback returns zero. + */ +typedef int (*custom_cli_ext_first_cb_fn)(SSL *s, unsigned short ext_type, + const unsigned char **out, + unsigned short *outlen, void *arg); +typedef int (*custom_cli_ext_second_cb_fn)(SSL *s, unsigned short ext_type, + const unsigned char *in, + unsigned short inlen, int *al, + void *arg); + +typedef int (*custom_srv_ext_first_cb_fn)(SSL *s, unsigned short ext_type, + const unsigned char *in, + unsigned short inlen, int *al, + void *arg); +typedef int (*custom_srv_ext_second_cb_fn)(SSL *s, unsigned short ext_type, + const unsigned char **out, + unsigned short *outlen, void *arg); + +typedef struct { + unsigned short ext_type; + custom_cli_ext_first_cb_fn fn1; + custom_cli_ext_second_cb_fn fn2; + void *arg; +} custom_cli_ext_record; + +typedef struct { + unsigned short ext_type; + custom_srv_ext_first_cb_fn fn1; + custom_srv_ext_second_cb_fn fn2; + void *arg; +} custom_srv_ext_record; +#endif #ifndef OPENSSL_NO_SSL_INTERN @@ -1058,6 +1109,12 @@ struct ssl_ctx_st int (*tlsext_authz_server_audit_proof_cb)(SSL *s, void *arg); void *tlsext_authz_server_audit_proof_cb_arg; #endif + + /* Arrays containing the callbacks for custom TLS Extensions. */ + custom_cli_ext_record *custom_cli_ext_records; + size_t custom_cli_ext_records_count; + custom_srv_ext_record *custom_srv_ext_records; + size_t custom_srv_ext_records_count; }; #endif @@ -1163,6 +1220,32 @@ const char *SSL_get_psk_identity_hint(const SSL *s); const char *SSL_get_psk_identity(const SSL *s); #endif +#ifndef OPENSSL_NO_TLSEXT +/* Register callbacks to handle custom TLS Extensions as client or server. + * + * Returns nonzero on success. You cannot register twice for the same + * extension number, and registering for an extension number already + * handled by OpenSSL will succeed, but the callbacks will not be invoked. + * + * NULL can be registered for any callback function. For the client + * functions, a NULL custom_cli_ext_first_cb_fn sends an empty ClientHello + * Extension, and a NULL custom_cli_ext_second_cb_fn ignores the ServerHello + * response (if any). + * + * For the server functions, a NULL custom_srv_ext_first_cb_fn means the + * ClientHello extension's data will be ignored, but the extension will still + * be noted and custom_srv_ext_second_cb_fn will still be invoked. A NULL + * custom_srv_ext_second_cb doesn't send a ServerHello extension. + */ +int SSL_CTX_set_custom_cli_ext(SSL_CTX *ctx, unsigned short ext_type, + custom_cli_ext_first_cb_fn fn1, + custom_cli_ext_second_cb_fn fn2, void *arg); + +int SSL_CTX_set_custom_srv_ext(SSL_CTX *ctx, unsigned short ext_type, + custom_srv_ext_first_cb_fn fn1, + custom_srv_ext_second_cb_fn fn2, void *arg); +#endif + #define SSL_NOTHING 1 #define SSL_WRITING 2 #define SSL_READING 3 @@ -1934,6 +2017,14 @@ const unsigned char *SSL_CTX_get_authz_data(SSL_CTX *ctx, unsigned char type, int SSL_CTX_use_authz_file(SSL_CTX *ctx, const char *file); int SSL_use_authz_file(SSL *ssl, const char *file); #endif + +/* Set serverinfo data for the current active cert. */ +int SSL_CTX_use_serverinfo(SSL_CTX *ctx, const unsigned char *serverinfo, + size_t serverinfo_length); +#ifndef OPENSSL_NO_STDIO +int SSL_CTX_use_serverinfo_file(SSL_CTX *ctx, const char *file); +#endif /* NO_STDIO */ + #endif #ifndef OPENSSL_NO_STDIO @@ -2469,6 +2560,8 @@ void ERR_load_SSL_strings(void); #define SSL_F_SSL_CTX_USE_RSAPRIVATEKEY 177 #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_FILE 337 #define SSL_F_SSL_DO_HANDSHAKE 180 #define SSL_F_SSL_GET_NEW_SESSION 181 #define SSL_F_SSL_GET_PREV_SESSION 217 @@ -2644,6 +2737,7 @@ void ERR_load_SSL_strings(void); #define SSL_R_INVALID_COMPRESSION_ALGORITHM 341 #define SSL_R_INVALID_NULL_CMD_NAME 385 #define SSL_R_INVALID_PURPOSE 278 +#define SSL_R_INVALID_SERVERINFO_DATA 388 #define SSL_R_INVALID_SRP_USERNAME 357 #define SSL_R_INVALID_STATUS_RESPONSE 328 #define SSL_R_INVALID_TICKET_KEYS_LENGTH 325 diff --git a/ssl/ssl3.h b/ssl/ssl3.h index d2a5208824..171c76a73c 100644 --- a/ssl/ssl3.h +++ b/ssl/ssl3.h @@ -571,6 +571,15 @@ typedef struct ssl3_state_st * server echoed our server_authz extension and therefore must send us * a supplemental data handshake message. */ char tlsext_authz_server_promised; + + /* tlsext_custom_types contains an array of TLS Extension types which + * were advertised by the client in its ClientHello, which were not + * otherwise handled by OpenSSL, and which the server has registered + * a custom_srv_ext_record to handle. + * The array does not contain any duplicates, and is in the same order + * as the types were received in the client hello. */ + unsigned short *tlsext_custom_types; + size_t tlsext_custom_types_count; /* how many tlsext_custom_types */ #endif } SSL3_STATE; diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c index 6a59316da6..491f527331 100644 --- a/ssl/ssl_cert.c +++ b/ssl/ssl_cert.c @@ -329,7 +329,8 @@ CERT *ssl_cert_dup(CERT *cert) } } rpk->valid_flags = 0; - if (cert->pkeys[i].authz != NULL) +#ifndef OPENSSL_NO_TLSEXT + if (cert->pkeys[i].authz != NULL) { /* Just copy everything. */ ret->pkeys[i].authz_length = @@ -339,12 +340,30 @@ CERT *ssl_cert_dup(CERT *cert) if (ret->pkeys[i].authz == NULL) { SSLerr(SSL_F_SSL_CERT_DUP, ERR_R_MALLOC_FAILURE); - return(NULL); + return NULL; } memcpy(ret->pkeys[i].authz, cert->pkeys[i].authz, cert->pkeys[i].authz_length); } + + if (cert->pkeys[i].serverinfo != NULL) + { + /* Just copy everything. */ + ret->pkeys[i].serverinfo_length = + cert->pkeys[i].serverinfo_length; + ret->pkeys[i].serverinfo = + OPENSSL_malloc(ret->pkeys[i].serverinfo_length); + if (ret->pkeys[i].serverinfo == NULL) + { + SSLerr(SSL_F_SSL_CERT_DUP, ERR_R_MALLOC_FAILURE); + return NULL; + } + memcpy(ret->pkeys[i].serverinfo, + cert->pkeys[i].serverinfo, + cert->pkeys[i].serverinfo_length); + } +#endif } ret->references=1; @@ -460,8 +479,16 @@ void ssl_cert_clear_certs(CERT *c) cpk->chain = NULL; } #ifndef OPENSSL_NO_TLSEXT - if (cpk->authz != NULL) + if (cpk->authz) + { OPENSSL_free(cpk->authz); + cpk->authz = NULL; + } + if (cpk->serverinfo) + { + OPENSSL_free(cpk->serverinfo); + cpk->serverinfo = NULL; + } #endif /* Clear all flags apart from explicit sign */ cpk->valid_flags &= CERT_PKEY_EXPLICIT_SIGN; diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c index ed435c6a60..fc13c36211 100644 --- a/ssl/ssl_err.c +++ b/ssl/ssl_err.c @@ -233,6 +233,8 @@ static ERR_STRING_DATA SSL_str_functs[]= {ERR_FUNC(SSL_F_SSL_CTX_USE_RSAPRIVATEKEY), "SSL_CTX_use_RSAPrivateKey"}, {ERR_FUNC(SSL_F_SSL_CTX_USE_RSAPRIVATEKEY_ASN1), "SSL_CTX_use_RSAPrivateKey_ASN1"}, {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_FILE), "SSL_CTX_use_serverinfo_file"}, {ERR_FUNC(SSL_F_SSL_DO_HANDSHAKE), "SSL_do_handshake"}, {ERR_FUNC(SSL_F_SSL_GET_NEW_SESSION), "ssl_get_new_session"}, {ERR_FUNC(SSL_F_SSL_GET_PREV_SESSION), "ssl_get_prev_session"}, @@ -411,6 +413,7 @@ static ERR_STRING_DATA SSL_str_reasons[]= {ERR_REASON(SSL_R_INVALID_COMPRESSION_ALGORITHM),"invalid compression algorithm"}, {ERR_REASON(SSL_R_INVALID_NULL_CMD_NAME) ,"invalid null cmd name"}, {ERR_REASON(SSL_R_INVALID_PURPOSE) ,"invalid purpose"}, +{ERR_REASON(SSL_R_INVALID_SERVERINFO_DATA),"invalid serverinfo data"}, {ERR_REASON(SSL_R_INVALID_SRP_USERNAME) ,"invalid srp username"}, {ERR_REASON(SSL_R_INVALID_STATUS_RESPONSE),"invalid status response"}, {ERR_REASON(SSL_R_INVALID_TICKET_KEYS_LENGTH),"invalid ticket keys length"}, diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index b30577c961..14634e2406 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -1710,6 +1710,61 @@ void SSL_CTX_set_next_proto_select_cb(SSL_CTX *ctx, int (*cb) (SSL *s, unsigned ctx->next_proto_select_cb_arg = arg; } # endif + +int SSL_CTX_set_custom_cli_ext(SSL_CTX *ctx, unsigned short ext_type, + custom_cli_ext_first_cb_fn fn1, + custom_cli_ext_second_cb_fn fn2, void* arg) + { + /* Check for duplicates */ + size_t i; + custom_cli_ext_record* record; + + for (i=0; i < ctx->custom_cli_ext_records_count; i++) + if (ext_type == ctx->custom_cli_ext_records[i].ext_type) + return 0; + + ctx->custom_cli_ext_records = OPENSSL_realloc(ctx->custom_cli_ext_records, + (ctx->custom_cli_ext_records_count+1) * sizeof(custom_cli_ext_record)); + if (!ctx->custom_cli_ext_records) { + ctx->custom_cli_ext_records_count = 0; + return 0; + } + ctx->custom_cli_ext_records_count++; + record = &ctx->custom_cli_ext_records[ctx->custom_cli_ext_records_count - 1]; + record->ext_type = ext_type; + record->fn1 = fn1; + record->fn2 = fn2; + record->arg = arg; + return 1; + } + +int SSL_CTX_set_custom_srv_ext(SSL_CTX *ctx, unsigned short ext_type, + custom_srv_ext_first_cb_fn fn1, + custom_srv_ext_second_cb_fn fn2, void* arg) + { + /* Check for duplicates */ + size_t i; + custom_srv_ext_record* record; + + for (i=0; i < ctx->custom_srv_ext_records_count; i++) + if (ext_type == ctx->custom_srv_ext_records[i].ext_type) + return 0; + + ctx->custom_srv_ext_records = OPENSSL_realloc(ctx->custom_srv_ext_records, + (ctx->custom_srv_ext_records_count+1) * sizeof(custom_srv_ext_record)); + if (!ctx->custom_srv_ext_records) { + ctx->custom_srv_ext_records_count = 0; + return 0; + } + ctx->custom_srv_ext_records_count++; + record = &ctx->custom_srv_ext_records[ctx->custom_srv_ext_records_count - 1]; + record->ext_type = ext_type; + record->fn1 = fn1; + record->fn2 = fn2; + record->arg = arg; + return 1; + } + #endif int SSL_export_keying_material(SSL *s, unsigned char *out, size_t olen, @@ -1909,6 +1964,10 @@ SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth) #ifndef OPENSSL_NO_SRP SSL_CTX_SRP_CTX_init(ret); #endif + ret->custom_cli_ext_records = NULL; + ret->custom_cli_ext_records_count = 0; + ret->custom_srv_ext_records = NULL; + ret->custom_srv_ext_records_count = 0; #ifndef OPENSSL_NO_BUF_FREELISTS ret->freelist_max_len = SSL_MAX_BUF_FREELIST_LEN_DEFAULT; ret->rbuf_freelist = OPENSSL_malloc(sizeof(SSL3_BUF_FREELIST)); @@ -2047,6 +2106,10 @@ void SSL_CTX_free(SSL_CTX *a) #ifndef OPENSSL_NO_SRP SSL_CTX_SRP_CTX_free(a); #endif +#ifndef OPENSSL_NO_TLSEXT + OPENSSL_free(a->custom_cli_ext_records); + OPENSSL_free(a->custom_srv_ext_records); +#endif #ifndef OPENSSL_NO_ENGINE if (a->client_cert_engine) ENGINE_finish(a->client_cert_engine); @@ -2492,6 +2555,26 @@ unsigned char *ssl_get_authz_data(SSL *s, size_t *authz_length) return c->pkeys[i].authz; } + +int ssl_get_server_cert_serverinfo(SSL *s, const unsigned char **serverinfo, + size_t *serverinfo_length) + { + CERT *c = NULL; + int i = 0; + *serverinfo_length = 0; + + c = s->cert; + i = ssl_get_server_cert_index(s); + + if (i == -1) + return 0; + if (c->pkeys[i].serverinfo == NULL) + return 0; + + *serverinfo = c->pkeys[i].serverinfo; + *serverinfo_length = c->pkeys[i].serverinfo_length; + return 1; + } #endif void ssl_update_cache(SSL *s,int mode) diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index 96006d9a62..3f15893973 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -493,6 +493,14 @@ typedef struct cert_pkey_st * uint8_t data[length]; */ unsigned char *authz; size_t authz_length; + + /* serverinfo data for this certificate. The data is in TLS Extension + * wire format, specifically it's a series of records like: + * uint16_t extension_type; // (RFC 5246, 7.4.1.4, Extension) + * uint16_t length; + * uint8_t data[length]; */ + unsigned char *serverinfo; + size_t serverinfo_length; #endif /* Set if CERT_PKEY can be used with current SSL session: e.g. * appropriate curve, signature algorithms etc. If zero it can't be @@ -949,7 +957,11 @@ int ssl_undefined_function(SSL *s); int ssl_undefined_void_function(void); int ssl_undefined_const_function(const SSL *s); CERT_PKEY *ssl_get_server_send_pkey(const SSL *s); +#ifndef OPENSSL_NO_TLSEXT unsigned char *ssl_get_authz_data(SSL *s, size_t *authz_length); +int ssl_get_server_cert_serverinfo(SSL *s, const unsigned char **serverinfo, + size_t *serverinfo_length); +#endif EVP_PKEY *ssl_get_sign_pkey(SSL *s,const SSL_CIPHER *c, const EVP_MD **pmd); int ssl_cert_type(X509 *x,EVP_PKEY *pkey); void ssl_set_cert_masks(CERT *c, const SSL_CIPHER *cipher); diff --git a/ssl/ssl_rsa.c b/ssl/ssl_rsa.c index 1babdef3c6..507a3d1773 100644 --- a/ssl/ssl_rsa.c +++ b/ssl/ssl_rsa.c @@ -471,6 +471,14 @@ static int ssl_set_cert(CERT *c, X509 *x) c->pkeys[i].authz = NULL; c->pkeys[i].authz_length = 0; } + + /* Free the old serverinfo data, if it exists. */ + if (c->pkeys[i].serverinfo != NULL) + { + OPENSSL_free(c->pkeys[i].serverinfo); + c->pkeys[i].serverinfo = NULL; + c->pkeys[i].serverinfo_length = 0; + } #endif c->key= &(c->pkeys[i]); @@ -855,6 +863,138 @@ static char authz_validate(const unsigned char *authz, size_t length) } } +static int serverinfo_find_extension(const unsigned char *serverinfo, + size_t serverinfo_length, + unsigned short extension_type, + const unsigned char **extension_data, + unsigned short *extension_length) + { + *extension_data = NULL; + *extension_length = 0; + if (serverinfo == NULL || serverinfo_length == 0) + return 0; + for (;;) + { + unsigned short type = 0; /* uint16 */ + unsigned short len = 0; /* uint16 */ + + /* end of serverinfo */ + if (serverinfo_length == 0) + return -1; /* Extension not found */ + + /* read 2-byte type field */ + if (serverinfo_length < 2) + return 0; /* Error */ + type = (serverinfo[0] << 8) + serverinfo[1]; + serverinfo += 2; + serverinfo_length -= 2; + + /* read 2-byte len field */ + if (serverinfo_length < 2) + return 0; /* Error */ + len = (serverinfo[0] << 8) + serverinfo[1]; + serverinfo += 2; + serverinfo_length -= 2; + + if (len > serverinfo_length) + return 0; /* Error */ + + if (type == extension_type) + { + *extension_data = serverinfo; + *extension_length = len; + return 1; /* Success */ + } + + serverinfo += len; + serverinfo_length -= len; + } + return 0; /* Error */ + } + +static int serverinfo_srv_first_cb(SSL *s, unsigned short ext_type, + const unsigned char *in, + unsigned short inlen, int *al, + void *arg) + { + if (inlen != 0) + { + *al = SSL_AD_DECODE_ERROR; + return 0; + } + return 1; + } + +static int serverinfo_srv_second_cb(SSL *s, unsigned short ext_type, + const unsigned char **out, unsigned short *outlen, + void *arg) + { + const unsigned char *serverinfo = NULL; + size_t serverinfo_length = 0; + + /* Is there serverinfo data for the chosen server cert? */ + if ((ssl_get_server_cert_serverinfo(s, &serverinfo, + &serverinfo_length)) != 0) + { + /* Find the relevant extension from the serverinfo */ + int retval = serverinfo_find_extension(serverinfo, serverinfo_length, + ext_type, out, outlen); + if (retval == 0) + return 0; /* Error */ + if (retval == -1) + return -1; /* No extension found, don't send extension */ + return 1; /* Send extension */ + } + return -1; /* No serverinfo data found, don't send extension */ + } + +/* 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, + size_t serverinfo_length, SSL_CTX *ctx) + { + if (serverinfo == NULL || serverinfo_length == 0) + return 0; + for (;;) + { + unsigned short ext_type = 0; /* uint16 */ + unsigned short len = 0; /* uint16 */ + + /* 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 && !SSL_CTX_set_custom_srv_ext(ctx, ext_type, + serverinfo_srv_first_cb, + serverinfo_srv_second_cb, NULL)) + return 0; + + serverinfo += 2; + serverinfo_length -= 2; + + /* read 2-byte len field */ + if (serverinfo_length < 2) + return 0; + len = (serverinfo[0] << 8) + serverinfo[1]; + serverinfo += 2; + serverinfo_length -= 2; + + if (len > serverinfo_length) + return 0; + + serverinfo += len; + serverinfo_length -= len; + } + } + static const unsigned char *authz_find_data(const unsigned char *authz, size_t authz_length, unsigned char data_type, @@ -906,13 +1046,18 @@ static int ssl_set_authz(CERT *c, unsigned char *authz, size_t authz_length) return(0); } current_key->authz = OPENSSL_realloc(current_key->authz, authz_length); + if (current_key->authz == NULL) + { + SSLerr(SSL_F_SSL_SET_AUTHZ,ERR_R_MALLOC_FAILURE); + return 0; + } current_key->authz_length = authz_length; memcpy(current_key->authz, authz, authz_length); return 1; } int SSL_CTX_use_authz(SSL_CTX *ctx, unsigned char *authz, - size_t authz_length) + size_t authz_length) { if (authz == NULL) { @@ -927,6 +1072,49 @@ int SSL_CTX_use_authz(SSL_CTX *ctx, unsigned char *authz, return ssl_set_authz(ctx->cert, authz, authz_length); } +int SSL_CTX_use_serverinfo(SSL_CTX *ctx, const unsigned char *serverinfo, + size_t serverinfo_length) + { + if (ctx == NULL || serverinfo == NULL || serverinfo_length == 0) + { + SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO,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); + return(0); + } + if (!ssl_cert_inst(&ctx->cert)) + { + SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO,ERR_R_MALLOC_FAILURE); + return 0; + } + if (ctx->cert->key == NULL) + { + SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO,ERR_R_INTERNAL_ERROR); + return 0; + } + ctx->cert->key->serverinfo = OPENSSL_realloc(ctx->cert->key->serverinfo, + serverinfo_length); + if (ctx->cert->key->serverinfo == NULL) + { + SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO,ERR_R_MALLOC_FAILURE); + return 0; + } + memcpy(ctx->cert->key->serverinfo, serverinfo, serverinfo_length); + ctx->cert->key->serverinfo_length = serverinfo_length; + + /* 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); + return(0); + } + return 1; + } + int SSL_use_authz(SSL *ssl, unsigned char *authz, size_t authz_length) { if (authz == NULL) @@ -1026,5 +1214,81 @@ int SSL_use_authz_file(SSL *ssl, const char *file) OPENSSL_free(authz); return ret; } + +int SSL_CTX_use_serverinfo_file(SSL_CTX *ctx, const char *file) + { + unsigned char *serverinfo = NULL; + size_t serverinfo_length = 0; + unsigned char* extension = 0; + long extension_length = 0; + char* name = NULL; + char* header = NULL; + int ret = 0; + BIO *bin = NULL; + size_t num_extensions = 0; + + if (ctx == NULL || file == NULL) + { + SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE,ERR_R_PASSED_NULL_PARAMETER); + goto end; + } + + bin = BIO_new(BIO_s_file_internal()); + if (bin == NULL) + { + SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, ERR_R_BUF_LIB); + goto end; + } + if (BIO_read_filename(bin, file) <= 0) + { + SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, ERR_R_SYS_LIB); + goto end; + } + + for (num_extensions=0;; num_extensions++) + { + if (PEM_read_bio(bin, &name, &header, &extension, &extension_length) == 0) + { + /* There must be at least one extension in this file */ + if (num_extensions == 0) + { + SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, ERR_R_PEM_LIB); + goto end; + } + else /* End of file, we're done */ + break; + } + /* 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, ERR_R_PEM_LIB); + goto end; + } + /* Append the decoded extension to the serverinfo buffer */ + serverinfo = OPENSSL_realloc(serverinfo, serverinfo_length + extension_length); + if (serverinfo == NULL) + { + SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, ERR_R_MALLOC_FAILURE); + goto end; + } + memcpy(serverinfo + serverinfo_length, extension, extension_length); + serverinfo_length += extension_length; + + OPENSSL_free(name); name = NULL; + OPENSSL_free(header); header = NULL; + OPENSSL_free(extension); extension = NULL; + } + + ret = SSL_CTX_use_serverinfo(ctx, serverinfo, serverinfo_length); +end: + /* SSL_CTX_use_serverinfo makes a local copy of the serverinfo. */ + OPENSSL_free(name); + OPENSSL_free(header); + OPENSSL_free(extension); + OPENSSL_free(serverinfo); + if (bin != NULL) + BIO_free(bin); + return ret; + } #endif /* OPENSSL_NO_STDIO */ #endif /* OPENSSL_NO_TLSEXT */ diff --git a/ssl/ssltest.c b/ssl/ssltest.c index f8c75c09d7..ab60664f1a 100644 --- a/ssl/ssltest.c +++ b/ssl/ssltest.c @@ -295,6 +295,242 @@ static int MS_CALLBACK ssl_srp_server_param_cb(SSL *s, int *ad, void *arg) static BIO *bio_err=NULL; static BIO *bio_stdout=NULL; +#define SCT_EXT_TYPE 18 + +/* WARNING : below extension types are *NOT* IETF assigned, and + could conflict if these types are reassigned and handled + specially by OpenSSL in the future */ +#define TACK_EXT_TYPE 62208 +#define CUSTOM_EXT_TYPE_0 1000 +#define CUSTOM_EXT_TYPE_1 1001 +#define CUSTOM_EXT_TYPE_2 1002 +#define CUSTOM_EXT_TYPE_3 1003 + +const char custom_ext_cli_string[] = "abc"; +const char custom_ext_srv_string[] = "defg"; + +/* These set from cmdline */ +char* serverinfo_file = NULL; +int serverinfo_sct = 0; +int serverinfo_tack = 0; + +/* These set based on extension callbacks */ +int serverinfo_sct_seen = 0; +int serverinfo_tack_seen = 0; +int serverinfo_other_seen = 0; + +/* This set from cmdline */ +int custom_ext = 0; + +/* This set based on extension callbacks */ +int custom_ext_error = 0; + +static int serverinfo_cli_cb(SSL* s, unsigned short ext_type, + const unsigned char* in, unsigned short inlen, + int* al, void* arg) + { + if (ext_type == SCT_EXT_TYPE) + serverinfo_sct_seen++; + else if (ext_type == TACK_EXT_TYPE) + serverinfo_tack_seen++; + else + serverinfo_other_seen++; + return 1; + } + +static int verify_serverinfo() + { + if (serverinfo_sct != serverinfo_sct_seen) + return -1; + if (serverinfo_tack != serverinfo_tack_seen) + return -1; + if (serverinfo_other_seen) + return -1; + return 0; + } + +/* Four test cases for custom extensions: + * 0 - no ClientHello extension or ServerHello response + * 1 - ClientHello with "abc", no response + * 2 - ClientHello with "abc", empty response + * 3 - ClientHello with "abc", "defg" response + */ + +static int custom_ext_0_cli_first_cb(SSL *s, unsigned short ext_type, + const unsigned char **out, + unsigned short *outlen, void *arg) + { + if (ext_type != CUSTOM_EXT_TYPE_0) + custom_ext_error = 1; + return -1; /* Don't send an extension */ + } + +static int custom_ext_0_cli_second_cb(SSL *s, unsigned short ext_type, + const unsigned char *in, + unsigned short inlen, int *al, + void *arg) + { + custom_ext_error = 1; /* Shouldn't be called */ + return 0; + } + +static int custom_ext_1_cli_first_cb(SSL *s, unsigned short ext_type, + const unsigned char **out, + unsigned short *outlen, void *arg) + { + if (ext_type != CUSTOM_EXT_TYPE_1) + custom_ext_error = 1; + *out = (const unsigned char*)custom_ext_cli_string; + *outlen = strlen(custom_ext_cli_string); + return 1; /* Send "abc" */ + } + +static int custom_ext_1_cli_second_cb(SSL *s, unsigned short ext_type, + const unsigned char *in, + unsigned short inlen, int *al, + void *arg) + { + custom_ext_error = 1; /* Shouldn't be called */ + return 0; + } + +static int custom_ext_2_cli_first_cb(SSL *s, unsigned short ext_type, + const unsigned char **out, + unsigned short *outlen, void *arg) + { + if (ext_type != CUSTOM_EXT_TYPE_2) + custom_ext_error = 1; + *out = (const unsigned char*)custom_ext_cli_string; + *outlen = strlen(custom_ext_cli_string); + return 1; /* Send "abc" */ + } + +static int custom_ext_2_cli_second_cb(SSL *s, unsigned short ext_type, + const unsigned char *in, + unsigned short inlen, int *al, + void *arg) + { + if (ext_type != CUSTOM_EXT_TYPE_2) + custom_ext_error = 1; + if (inlen != 0) + custom_ext_error = 1; /* Should be empty response */ + return 1; + } + +static int custom_ext_3_cli_first_cb(SSL *s, unsigned short ext_type, + const unsigned char **out, + unsigned short *outlen, void *arg) + { + if (ext_type != CUSTOM_EXT_TYPE_3) + custom_ext_error = 1; + *out = (const unsigned char*)custom_ext_cli_string; + *outlen = strlen(custom_ext_cli_string); + return 1; /* Send "abc" */ + } + +static int custom_ext_3_cli_second_cb(SSL *s, unsigned short ext_type, + const unsigned char *in, + unsigned short inlen, int *al, + void *arg) + { + if (ext_type != CUSTOM_EXT_TYPE_3) + custom_ext_error = 1; + if (inlen != strlen(custom_ext_srv_string)) + custom_ext_error = 1; + if (memcmp(custom_ext_srv_string, in, inlen) != 0) + custom_ext_error = 1; /* Check for "defg" */ + return 1; + } + + +static int custom_ext_0_srv_first_cb(SSL *s, unsigned short ext_type, + const unsigned char *in, + unsigned short inlen, int *al, + void *arg) + { + custom_ext_error = 1; + return 0; /* Shouldn't be called */ + } + +static int custom_ext_0_srv_second_cb(SSL *s, unsigned short ext_type, + const unsigned char **out, + unsigned short *outlen, void *arg) + { + custom_ext_error = 1; + return 0; /* Shouldn't be called */ + } + +static int custom_ext_1_srv_first_cb(SSL *s, unsigned short ext_type, + const unsigned char *in, + unsigned short inlen, int *al, + void *arg) + { + if (ext_type != CUSTOM_EXT_TYPE_1) + custom_ext_error = 1; + /* Check for "abc" */ + if (inlen != strlen(custom_ext_cli_string)) + custom_ext_error = 1; + if (memcmp(in, custom_ext_cli_string, inlen) != 0) + custom_ext_error = 1; + return 1; + } + +static int custom_ext_1_srv_second_cb(SSL *s, unsigned short ext_type, + const unsigned char **out, + unsigned short *outlen, void *arg) + { + return -1; /* Don't send an extension */ + } + +static int custom_ext_2_srv_first_cb(SSL *s, unsigned short ext_type, + const unsigned char *in, + unsigned short inlen, int *al, + void *arg) + { + if (ext_type != CUSTOM_EXT_TYPE_2) + custom_ext_error = 1; + /* Check for "abc" */ + if (inlen != strlen(custom_ext_cli_string)) + custom_ext_error = 1; + if (memcmp(in, custom_ext_cli_string, inlen) != 0) + custom_ext_error = 1; + return 1; + } + +static int custom_ext_2_srv_second_cb(SSL *s, unsigned short ext_type, + const unsigned char **out, + unsigned short *outlen, void *arg) + { + *out = NULL; + *outlen = 0; + return 1; /* Send empty extension */ + } + +static int custom_ext_3_srv_first_cb(SSL *s, unsigned short ext_type, + const unsigned char *in, + unsigned short inlen, int *al, + void *arg) + { + if (ext_type != CUSTOM_EXT_TYPE_3) + custom_ext_error = 1; + /* Check for "abc" */ + if (inlen != strlen(custom_ext_cli_string)) + custom_ext_error = 1; + if (memcmp(in, custom_ext_cli_string, inlen) != 0) + custom_ext_error = 1; + return 1; + } + +static int custom_ext_3_srv_second_cb(SSL *s, unsigned short ext_type, + const unsigned char **out, + unsigned short *outlen, void *arg) + { + *out = (const unsigned char*)custom_ext_srv_string; + *outlen = strlen(custom_ext_srv_string); + return 1; /* Send "defg" */ + } + + static char *cipher=NULL; static int verbose=0; static int debug=0; @@ -374,6 +610,10 @@ static void sv_usage(void) fprintf(stderr," -c_support_proof - indicate client support for server_authz audit proofs\n"); fprintf(stderr," -c_require_proof - fail if no audit proof is sent\n"); #endif + fprintf(stderr," -serverinfo_file file - have server use this file\n"); + fprintf(stderr," -serverinfo_sct - have client offer and expect SCT\n"); + fprintf(stderr," -serverinfo_tack - have client offer and expect TACK\n"); + fprintf(stderr," -custom_ext - try various custom extension callbacks\n"); } static void print_details(SSL *c_ssl, const char *prefix) @@ -845,6 +1085,23 @@ int main(int argc, char *argv[]) tls1 = 1; } #endif + else if (strcmp(*argv,"-serverinfo_sct") == 0) + { + serverinfo_sct = 1; + } + else if (strcmp(*argv,"-serverinfo_tack") == 0) + { + serverinfo_tack = 1; + } + else if (strcmp(*argv,"-serverinfo_file") == 0) + { + if (--argc < 1) goto bad; + serverinfo_file = *(++argv); + } + else if (strcmp(*argv,"-custom_ext") == 0) + { + custom_ext = 1; + } else { fprintf(stderr,"unknown option %s\n",*argv); @@ -1187,6 +1444,50 @@ bad: } #endif + if (serverinfo_sct) + SSL_CTX_set_custom_cli_ext(c_ctx, SCT_EXT_TYPE, NULL, + serverinfo_cli_cb, NULL); + if (serverinfo_tack) + SSL_CTX_set_custom_cli_ext(c_ctx, TACK_EXT_TYPE, NULL, + serverinfo_cli_cb, NULL); + + if (serverinfo_file) + if (!SSL_CTX_use_serverinfo_file(s_ctx, serverinfo_file)) + { + BIO_printf(bio_err, "missing serverinfo file\n"); + goto end; + } + + if (custom_ext) + { + SSL_CTX_set_custom_cli_ext(c_ctx, CUSTOM_EXT_TYPE_0, + custom_ext_0_cli_first_cb, + custom_ext_0_cli_second_cb, NULL); + SSL_CTX_set_custom_cli_ext(c_ctx, CUSTOM_EXT_TYPE_1, + custom_ext_1_cli_first_cb, + custom_ext_1_cli_second_cb, NULL); + SSL_CTX_set_custom_cli_ext(c_ctx, CUSTOM_EXT_TYPE_2, + custom_ext_2_cli_first_cb, + custom_ext_2_cli_second_cb, NULL); + SSL_CTX_set_custom_cli_ext(c_ctx, CUSTOM_EXT_TYPE_3, + custom_ext_3_cli_first_cb, + custom_ext_3_cli_second_cb, NULL); + + + SSL_CTX_set_custom_srv_ext(s_ctx, CUSTOM_EXT_TYPE_0, + custom_ext_0_srv_first_cb, + custom_ext_0_srv_second_cb, NULL); + SSL_CTX_set_custom_srv_ext(s_ctx, CUSTOM_EXT_TYPE_1, + custom_ext_1_srv_first_cb, + custom_ext_1_srv_second_cb, NULL); + SSL_CTX_set_custom_srv_ext(s_ctx, CUSTOM_EXT_TYPE_2, + custom_ext_2_srv_first_cb, + custom_ext_2_srv_second_cb, NULL); + SSL_CTX_set_custom_srv_ext(s_ctx, CUSTOM_EXT_TYPE_3, + custom_ext_3_srv_first_cb, + custom_ext_3_srv_second_cb, NULL); + } + c_ssl=SSL_new(c_ctx); s_ssl=SSL_new(s_ctx); @@ -1641,6 +1942,19 @@ int doit_biopair(SSL *s_ssl, SSL *c_ssl, long count, if (verbose) print_details(c_ssl, "DONE via BIO pair: "); + + if (verify_serverinfo() < 0) + { + ret = 1; + goto err; + } + + if (custom_ext_error) + { + ret = 1; + goto err; + } + end: ret = 0; @@ -1936,6 +2250,17 @@ int doit(SSL *s_ssl, SSL *c_ssl, long count) if (verbose) print_details(c_ssl, "DONE: "); + + if (verify_serverinfo() < 0) + { + ret = 1; + goto err; + } + if (custom_ext_error) + { + ret = 1; + goto err; + } ret=0; err: /* We have to set the BIO's to NULL otherwise they will be diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index 88570201a1..8d680a1bfe 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -1400,6 +1400,40 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned cha *(ret++) = TLSEXT_AUTHZDATAFORMAT_audit_proof; } + /* Add custom TLS Extensions to ClientHello */ + if (s->ctx->custom_cli_ext_records_count) + { + size_t i; + custom_cli_ext_record* record; + + for (i = 0; i < s->ctx->custom_cli_ext_records_count; i++) + { + const unsigned char* out = NULL; + unsigned short outlen = 0; + + record = &s->ctx->custom_cli_ext_records[i]; + /* NULL callback sends empty extension */ + /* -1 from callback omits extension */ + if (record->fn1) + { + int cb_retval = 0; + cb_retval = record->fn1(s, record->ext_type, + &out, &outlen, + record->arg); + if (cb_retval == 0) + return NULL; /* error */ + if (cb_retval == -1) + continue; /* skip this extension */ + } + if (limit < ret + 4 + outlen) + return NULL; + s2n(record->ext_type, ret); + s2n(outlen, ret); + memcpy(ret, out, outlen); + ret += outlen; + } + } + if ((extdatalen = ret-p-2) == 0) return p; @@ -1667,6 +1701,47 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha } } + /* If custom types were sent in ClientHello, add ServerHello responses */ + if (s->s3->tlsext_custom_types_count) + { + size_t i; + + for (i = 0; i < s->s3->tlsext_custom_types_count; i++) + { + size_t j; + custom_srv_ext_record *record; + + for (j = 0; j < s->ctx->custom_srv_ext_records_count; j++) + { + record = &s->ctx->custom_srv_ext_records[j]; + if (s->s3->tlsext_custom_types[i] == record->ext_type) + { + const unsigned char *out = NULL; + unsigned short outlen = 0; + int cb_retval = 0; + + /* NULL callback or -1 omits extension */ + if (!record->fn2) + break; + cb_retval = record->fn2(s, record->ext_type, + &out, &outlen, + record->arg); + if (cb_retval == 0) + return NULL; /* error */ + if (cb_retval == -1) + break; /* skip this extension */ + if (limit < ret + 4 + outlen) + return NULL; + s2n(record->ext_type, ret); + s2n(outlen, ret); + memcpy(ret, out, outlen); + ret += outlen; + break; + } + } + } + } + if ((extdatalen = ret-p-2)== 0) return p; @@ -2235,6 +2310,54 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char } } + /* If this ClientHello extension was unhandled and this is + * a nonresumed connection, check whether the extension is a + * custom TLS Extension (has a custom_srv_ext_record), and if + * so call the callback and record the extension number so that + * an appropriate ServerHello may be later returned. + */ + else if (!s->hit && s->ctx->custom_srv_ext_records_count) + { + custom_srv_ext_record *record; + + for (i=0; i < s->ctx->custom_srv_ext_records_count; i++) + { + record = &s->ctx->custom_srv_ext_records[i]; + if (type == record->ext_type) + { + /* Error on duplicate TLS Extensions */ + size_t j; + + for (j = 0; j < s->s3->tlsext_custom_types_count; j++) + { + if (s->s3->tlsext_custom_types[j] == type) + { + *al = TLS1_AD_DECODE_ERROR; + return 0; + } + } + + /* Callback */ + if (record->fn1 && !record->fn1(s, type, data, size, al, record->arg)) + return 0; + + /* Add the (non-duplicated) entry */ + s->s3->tlsext_custom_types_count++; + s->s3->tlsext_custom_types = OPENSSL_realloc( + s->s3->tlsext_custom_types, + s->s3->tlsext_custom_types_count*2); + if (s->s3->tlsext_custom_types == NULL) + { + s->s3->tlsext_custom_types = 0; + *al = TLS1_AD_INTERNAL_ERROR; + return 0; + } + s->s3->tlsext_custom_types[ + s->s3->tlsext_custom_types_count-1] = type; + } + } + } + data+=size; } @@ -2542,6 +2665,26 @@ static int ssl_scan_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char s->s3->tlsext_authz_server_promised = 1; } + + /* If this extension type was not otherwise handled, but + * matches a custom_cli_ext_record, then send it to the c + * callback */ + else if (s->ctx->custom_cli_ext_records_count) + { + size_t i; + custom_cli_ext_record* record; + + for (i = 0; i < s->ctx->custom_cli_ext_records_count; i++) + { + record = &s->ctx->custom_cli_ext_records[i]; + if (record->ext_type == type) + { + if (record->fn2 && !record->fn2(s, type, data, size, al, record->arg)) + return 0; + break; + } + } + } data += size; } diff --git a/test/Makefile b/test/Makefile index 77473f6bc8..9414e2492c 100644 --- a/test/Makefile +++ b/test/Makefile @@ -278,7 +278,7 @@ test_engine: $(ENGINETEST) test_ssl: keyU.ss certU.ss certCA.ss certP1.ss keyP1.ss certP2.ss keyP2.ss \ intP1.ss intP2.ss $(SSLTEST) testssl testsslproxy \ - ../apps/server2.pem + ../apps/server2.pem serverinfo.pem @echo "test SSL protocol" @if [ -n "$(FIPSCANLIB)" ]; then \ sh ./testfipsssl keyU.ss certU.ss certCA.ss; \ diff --git a/test/serverinfo.pem b/test/serverinfo.pem new file mode 100644 index 0000000000..0eb020a17f --- /dev/null +++ b/test/serverinfo.pem @@ -0,0 +1,16 @@ +-----BEGIN SCT----- +ABIAZMevsj4TC5rgwjZNciLGwh15YXoIK9t5aypGJIG4QzyMowmwwDdqxudkUcGa +DvuqlYL7psO5j4/BIHTe677CAZBBH3Ho2NOM5q1zub4AbfUMlKeufuQgeQ2Tj1oe +LJLRzrwDnPs= +-----END SCT----- + +-----BEGIN TACK EXTENSION----- +8wABTwFMh1Dz+3W6zULWJKjav5TNaFEXL1h98YtCXeyZnORYg4mbKpxH5CMbjpgx +To3amSqUPF4Ntjc/i9+poutxebYkbgAAAkMcxb8+RaM9YEywaJEGViKJJmpYG/gJ +HgfGaefI9kKbXSDmP9ntg8dLvDzuyYw14ktM2850Q9WvBiltpekilZxVuT2bFtfs +cmS++SAK9YOM8RrKhL1TLmrktoBEJZ6z5GTukYdQ8/t1us1C1iSo2r+UzWhRFy9Y +ffGLQl3smZzkWIOJmyqcR+QjG46YMU6N2pkqlDxeDbY3P4vfqaLrcXm2JG4AAAGN +xXQJPbdniI9rEydVXb1Cu1yT/t7FBEx6hLxuoypXjCI1wCGpXsd8zEnloR0Ank5h +VO/874E/BZlItzSPpcmDKl5Def6BrAJTErQlE9npo52S05YWORxJw1+VYBdqQ09A +x3wA +-----END TACK EXTENSION----- diff --git a/test/testssl b/test/testssl index 4e8542b556..f8a1121f28 100644 --- a/test/testssl +++ b/test/testssl @@ -30,6 +30,8 @@ else extra="$4" fi +serverinfo="./serverinfo.pem" + ############################################################################# echo test sslv2 @@ -165,6 +167,23 @@ $ssltest -tls1 -cipher PSK -psk abc123 $extra || exit 1 echo test tls1 with PSK via BIO pair $ssltest -bio_pair -tls1 -cipher PSK -psk abc123 $extra || exit 1 +############################################################################# +# Custom Extension tests + +echo test tls1 with custom extensions +$ssltest -bio_pair -tls1 -custom_ext || exit 1 + +############################################################################# +# Serverinfo tests + +echo test tls1 with serverinfo +$ssltest -bio_pair -tls1 -serverinfo_file $serverinfo || exit 1 +$ssltest -bio_pair -tls1 -serverinfo_file $serverinfo -serverinfo_sct || exit 1 +$ssltest -bio_pair -tls1 -serverinfo_file $serverinfo -serverinfo_tack || exit 1 +$ssltest -bio_pair -tls1 -serverinfo_file $serverinfo -serverinfo_sct -serverinfo_tack || exit 1 +$ssltest -bio_pair -tls1 -custom_ext -serverinfo_file $serverinfo -serverinfo_sct -serverinfo_tack || exit 1 + + if ../util/shlib_wrap.sh ../apps/openssl no-srp; then echo skipping SRP tests else diff --git a/util/pl/unix.pl b/util/pl/unix.pl index 556c137bcc..2e860ae35b 100644 --- a/util/pl/unix.pl +++ b/util/pl/unix.pl @@ -396,6 +396,7 @@ sub get_tests 'testss', 'testssl', 'testsslproxy', + 'serverinfo.pem', ); my $copies = copy_scripts(1, 'test', @copies); $copies .= copy_scripts(0, 'test', ('smcont.txt')); -- 2.34.1