diff options
-rw-r--r-- | CHANGES | 3 | ||||
-rw-r--r-- | apps/s_client.c | 62 | ||||
-rw-r--r-- | apps/s_server.c | 15 | ||||
-rw-r--r-- | doc/apps/s_client.pod | 8 | ||||
-rw-r--r-- | doc/apps/s_server.pod | 9 | ||||
-rw-r--r-- | doc/ssl/SSL_CTX_use_serverinfo.pod | 45 | ||||
-rw-r--r-- | ssl/s3_lib.c | 8 | ||||
-rw-r--r-- | ssl/ssl.h | 94 | ||||
-rw-r--r-- | ssl/ssl3.h | 9 | ||||
-rw-r--r-- | ssl/ssl_cert.c | 33 | ||||
-rw-r--r-- | ssl/ssl_err.c | 3 | ||||
-rw-r--r-- | ssl/ssl_lib.c | 83 | ||||
-rw-r--r-- | ssl/ssl_locl.h | 12 | ||||
-rw-r--r-- | ssl/ssl_rsa.c | 266 | ||||
-rw-r--r-- | ssl/ssltest.c | 325 | ||||
-rw-r--r-- | ssl/t1_lib.c | 143 | ||||
-rw-r--r-- | test/Makefile | 2 | ||||
-rw-r--r-- | test/serverinfo.pem | 16 | ||||
-rw-r--r-- | test/testssl | 19 | ||||
-rw-r--r-- | util/pl/unix.pl | 1 |
20 files changed, 1151 insertions, 5 deletions
@@ -4,6 +4,9 @@ Changes between 1.0.1 and 1.0.2 [xx XXX xxxx] + *) Add callbacks for arbitrary TLS extensions. + [Trevor Perrin <trevp@trevp.net> 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<openssl> B<s_client> [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<openssl> B<s_server> [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 <openssl/ssl.h> + + 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<ctx>. 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<file> into B<ctx>. 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; @@ -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_CT |