diff options
author | Trevor <trevp@trevp.net> | 2013-05-12 18:55:27 -0700 |
---|---|---|
committer | Ben Laurie <ben@links.org> | 2013-06-12 17:01:13 +0100 |
commit | a398f821fa98b9923a426cf45b268cf4d56c89bd (patch) | |
tree | 7bedc4b2a027f86e9d2d8cd9b4814ebddaf0c39e /ssl | |
parent | 6d84daa5d665e0de30c3d970cab65e8ade4d5b14 (diff) |
Add support for arbitrary TLS extensions.
Contributed by Trevor Perrin.
Diffstat (limited to 'ssl')
-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 | 244 | ||||
-rw-r--r-- | ssl/ssltest.c | 74 | ||||
-rw-r--r-- | ssl/t1_lib.c | 127 |
10 files changed, 683 insertions, 4 deletions
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c index 7ad8a541fa..67eb92089e 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c @@ -3026,6 +3026,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); @@ -3076,6 +3078,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,56 @@ 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). + * + * "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 @@ -1064,6 +1114,12 @@ struct ssl_ctx_st # endif /* OPENSSL_NO_EC */ int (*tlsext_authz_server_audit_proof_cb)(SSL *s, void *arg); void *tlsext_authz_server_audit_proof_cb_arg; + + /* 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 @@ -1170,6 +1226,33 @@ 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. If + * custom_srv_ext_second_cb_fn is NULL, an empty ServerHello extension is + * sent. + */ +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 @@ -2481,6 +2572,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 @@ -2655,6 +2748,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 d8ed725d2d..77747c4c1e 100644 --- a/ssl/ssl3.h +++ b/ssl/ssl3.h @@ -577,6 +577,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 a0da7d3741..f86511d81c 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 d8b4d70c13..2a6ae5b993 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"}, @@ -410,6 +412,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 c3b4032368..b08e82688f 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -1697,6 +1697,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, @@ -1896,6 +1951,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)); @@ -2034,6 +2093,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); @@ -2479,6 +2542,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 d06f912343..56f9b4b260 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -510,6 +510,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 @@ -1000,7 +1008,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..ac16b8506d 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,116 @@ 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 0; + + /* 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; + } + + serverinfo += len; + serverinfo_length -= len; + } + return 0; + } + +static int serverinfo_srv_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 a serverinfo for the chosen server cert? */ + if ((ssl_get_server_cert_serverinfo(s, &serverinfo, + &serverinfo_length)) != 0) + { + /* Find the relevant extension from the serverinfo */ + serverinfo_find_extension(serverinfo, serverinfo_length, + ext_type, out, outlen); + } + return 1; + } + +static int serverinfo_validate(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, NULL, + serverinfo_srv_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 +1024,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 +1050,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_validate(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_validate(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 +1192,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 db87b1a35e..f67c7eab8e 100644 --- a/ssl/ssltest.c +++ b/ssl/ssltest.c @@ -370,6 +370,41 @@ static int verify_npn(SSL *client, SSL *server) } #endif +#define SCT_EXT_TYPE 18 +#define TACK_EXT_TYPE 62208 + +/* These set from cmdline */ +char* serverinfo_file = NULL; +int serverinfo_sct = 0; +int serverinfo_tack = 0; +int serverinfo_sct_seen = 0; +int serverinfo_tack_seen = 0; +int serverinfo_other_seen = 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; + } + static char *cipher=NULL; static int verbose=0; static int debug=0; @@ -449,6 +484,9 @@ static void sv_usage(void) fprintf(stderr," -npn_server - have server side offer NPN\n"); fprintf(stderr," -npn_server_reject - have server reject NPN\n"); #endif + fprintf(stderr," -serverinfo_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"); } static void print_details(SSL *c_ssl, const char *prefix) @@ -861,6 +899,19 @@ int main(int argc, char *argv[]) npn_server_reject = 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 { fprintf(stderr,"unknown option %s\n",*argv); @@ -1186,6 +1237,18 @@ 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; + } + c_ssl=SSL_new(c_ctx); s_ssl=SSL_new(s_ctx); @@ -1643,6 +1706,12 @@ int doit_biopair(SSL *s_ssl, SSL *c_ssl, long count, goto end; } #endif + if (verify_serverinfo() < 0) + { + ret = 1; + goto err; + } + end: ret = 0; @@ -1945,6 +2014,11 @@ int doit(SSL *s_ssl, SSL *c_ssl, long count) goto err; } #endif + if (verify_serverinfo() < 0) + { + 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 31daa50d3e..19769c5888 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -1443,6 +1443,31 @@ 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]; + if (record->fn1 && !record->fn1(s, record->ext_type, + &out, &outlen, + record->arg)) + return NULL; + 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; @@ -1709,6 +1734,40 @@ 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; + if (record->fn2 + && !record->fn2(s, record->ext_type, + &out, &outlen, + record->arg)) + return NULL; + 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; @@ -2274,6 +2333,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; } @@ -2578,6 +2685,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; } |