From ed3883d21bb4ddfc21ec9d154e14e84c85db164d Mon Sep 17 00:00:00 2001 From: =?utf8?q?Bodo=20M=C3=B6ller?= Date: Mon, 2 Jan 2006 23:14:37 +0000 Subject: [PATCH] Support TLS extensions (specifically, HostName) Submitted by: Peter Sylvester --- apps/s_client.c | 70 +++++++++++-- apps/s_server.c | 269 ++++++++++++++++++++++++++++++++++++++++++++---- ssl/s23_clnt.c | 12 +++ ssl/s23_srvr.c | 10 +- ssl/s3_clnt.c | 27 +++++ ssl/s3_lib.c | 47 +++++++++ ssl/s3_srvr.c | 29 ++++++ ssl/ssl.h | 32 ++++++ ssl/ssl_asn1.c | 37 ++++++- ssl/ssl_err.c | 5 + ssl/ssl_lib.c | 38 +++++++ ssl/ssl_locl.h | 8 +- ssl/ssl_sess.c | 7 ++ ssl/t1_lib.c | 186 +++++++++++++++++++++++++++++++-- ssl/tls1.h | 47 +++++++++ 15 files changed, 784 insertions(+), 40 deletions(-) diff --git a/apps/s_client.c b/apps/s_client.c index 5679b09cf1..c24b6e4a26 100644 --- a/apps/s_client.c +++ b/apps/s_client.c @@ -222,9 +222,32 @@ static void sc_usage(void) BIO_printf(bio_err," -engine id - Initialise and use the specified engine\n"); #endif BIO_printf(bio_err," -rand file%cfile%c...\n", LIST_SEPARATOR_CHAR, LIST_SEPARATOR_CHAR); - +#ifndef OPENSSL_NO_TLSEXT + BIO_printf(bio_err," -servername host - Set TLS extension servername in ClientHello\n"); +#endif } +#ifndef OPENSSL_NO_TLSEXT + +/* This is a context that we pass to callbacks */ +typedef struct tlsextctx_st { + BIO * biodebug; + int ack; +} tlsextctx; + + +static int MS_CALLBACK ssl_servername_cb(SSL *s, int *ad, void *arg) { + tlsextctx * p = (tlsextctx *) arg; + const unsigned char * hn= SSL_get_servername(s, TLSEXT_TYPE_SERVER_host); + if (SSL_get_servername_type(s) != -1) + p->ack = !SSL_session_reused(s) && hn != NULL; + else + BIO_printf(bio_err,"SSL_get_tlsext_hostname does not work\n"); + + return SSL_ERROR_NONE; +} +#endif + int MAIN(int, char **); int MAIN(int argc, char **argv) @@ -254,10 +277,7 @@ int MAIN(int argc, char **argv) int starttls_proto = 0; int prexit = 0, vflags = 0; const SSL_METHOD *meth=NULL; -#ifdef sock_type -#undef sock_type -#endif - int sock_type=SOCK_STREAM; + int socketType=SOCK_STREAM; BIO *sbio; char *inrand=NULL; #ifndef OPENSSL_NO_ENGINE @@ -268,6 +288,11 @@ int MAIN(int argc, char **argv) struct timeval tv; #endif +#ifndef OPENSSL_NO_TLSEXT + char *servername = NULL; + tlsextctx tlsextcbp = + {NULL,0}; +#endif struct sockaddr peer; int peerlen = sizeof(peer); int enable_timeouts = 0 ; @@ -394,7 +419,7 @@ int MAIN(int argc, char **argv) else if (strcmp(*argv,"-dtls1") == 0) { meth=DTLSv1_client_method(); - sock_type=SOCK_DGRAM; + socketType=SOCK_DGRAM; } else if (strcmp(*argv,"-timeout") == 0) enable_timeouts=1; @@ -477,6 +502,14 @@ int MAIN(int argc, char **argv) if (--argc < 1) goto bad; inrand= *(++argv); } +#ifndef OPENSSL_NO_TLSEXT + else if (strcmp(*argv,"-servername") == 0) + { + if (--argc < 1) goto bad; + servername= *(++argv); + /* meth=TLSv1_client_method(); */ + } +#endif else { BIO_printf(bio_err,"unknown option %s\n",*argv); @@ -572,7 +605,7 @@ bad: /* DTLS: partial reads end up discarding unread UDP bytes :-( * Setting read ahead solves this problem. */ - if (sock_type == SOCK_DGRAM) SSL_CTX_set_read_ahead(ctx, 1); + if (socketType == SOCK_DGRAM) SSL_CTX_set_read_ahead(ctx, 1); if (state) SSL_CTX_set_info_callback(ctx,apps_ssl_info_callback); if (cipher != NULL) @@ -600,8 +633,24 @@ bad: store = SSL_CTX_get_cert_store(ctx); X509_STORE_set_flags(store, vflags); +#ifndef OPENSSL_NO_TLSEXT + if (servername != NULL) { + tlsextcbp.biodebug = bio_err; + SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_cb); + SSL_CTX_set_tlsext_servername_arg(ctx, &tlsextcbp); + } +#endif con=SSL_new(ctx); +#ifndef OPENSSL_NO_TLSEXT + if (servername != NULL){ + if (!SSL_set_tlsext_hostname(con,servername)){ + BIO_printf(bio_err,"Unable to set TLS servername extension.\n"); + ERR_print_errors(bio_err); + goto end; + } + } +#endif #ifndef OPENSSL_NO_KRB5 if (con && (con->kssl_ctx = kssl_ctx_new()) != NULL) { @@ -612,7 +661,7 @@ bad: re_start: - if (init_client(&s,host,port,sock_type) == 0) + if (init_client(&s,host,port,socketType) == 0) { BIO_printf(bio_err,"connect:errno=%d\n",get_last_socket_error()); SHUTDOWN(s); @@ -741,6 +790,11 @@ re_start: if (in_init) { in_init=0; +#ifndef OPENSSL_NO_TLSEXT + if (servername != NULL && !SSL_session_reused(con)) { + BIO_printf(bio_c_out,"Server did %sacknowledge servername extension.\n",tlsextcbp.ack?"":"not "); + } +#endif print_stuff(bio_c_out,con,full_log); if (full_log > 0) full_log--; diff --git a/apps/s_server.c b/apps/s_server.c index 918f776ee5..7dc864fab9 100644 --- a/apps/s_server.c +++ b/apps/s_server.c @@ -221,6 +221,9 @@ static int bufsize=BUFSIZZ; static int accept_socket= -1; #define TEST_CERT "server.pem" +#ifndef OPENSSL_NO_TLSEXT +#define TEST_CERT2 "server2.pem" +#endif #undef PROG #define PROG s_server_main @@ -230,6 +233,9 @@ static char *cipher=NULL; static int s_server_verify=SSL_VERIFY_NONE; static int s_server_session_id_context = 1; /* anything will do */ static const char *s_cert_file=TEST_CERT,*s_key_file=NULL; +#ifndef OPENSSL_NO_TLSEXT +static const char *s_cert_file2=TEST_CERT2,*s_key_file2=NULL; +#endif static char *s_dcert_file=NULL,*s_dkey_file=NULL; #ifdef FIONBIO static int s_nbio=0; @@ -237,6 +243,9 @@ static int s_nbio=0; static int s_nbio_test=0; int s_crlf=0; static SSL_CTX *ctx=NULL; +#ifndef OPENSSL_NO_TLSEXT +static SSL_CTX *ctx2=NULL; +#endif static int www=0; static BIO *bio_s_out=NULL; @@ -251,10 +260,7 @@ static char *engine_id=NULL; static const char *session_id_prefix=NULL; static int enable_timeouts = 0; -#ifdef mtu -#undef mtu -#endif -static long mtu; +static long socketMtu; static int cert_chain = 0; @@ -268,6 +274,11 @@ static void s_server_init(void) s_dkey_file=NULL; s_cert_file=TEST_CERT; s_key_file=NULL; +#ifndef OPENSSL_NO_TLSEXT + s_cert_file2=TEST_CERT2; + s_key_file2=NULL; + ctx2=NULL; +#endif #ifdef FIONBIO s_nbio=0; #endif @@ -354,6 +365,14 @@ static void sv_usage(void) #endif BIO_printf(bio_err," -id_prefix arg - Generate SSL/TLS session IDs prefixed by 'arg'\n"); BIO_printf(bio_err," -rand file%cfile%c...\n", LIST_SEPARATOR_CHAR, LIST_SEPARATOR_CHAR); +#ifndef OPENSSL_NO_TLSEXT + BIO_printf(bio_err," -servername host - check TLS1 servername\n"); + BIO_printf(bio_err," -cert2 arg - certificate file to use for servername\n"); + BIO_printf(bio_err," (default is %s)\n",TEST_CERT2); + BIO_printf(bio_err," -key2 arg - Private Key file to use for servername, in cert file if\n"); + BIO_printf(bio_err," not specified (default is %s)\n",TEST_CERT2); + BIO_printf(bio_err," -servername host - check TLS1 servername\n"); +#endif } static int local_argc=0; @@ -509,6 +528,37 @@ static int ebcdic_puts(BIO *bp, const char *str) } #endif +#ifndef OPENSSL_NO_TLSEXT + +/* This is a context that we pass to callbacks */ +typedef struct tlsextctx_st { + char * servername; + BIO * biodebug; +} tlsextctx; + + +static int MS_CALLBACK ssl_servername_cb(SSL *s, int *ad, void *arg) { + tlsextctx * p = (tlsextctx *) arg; + const char * servername = SSL_get_servername(s, TLSEXT_TYPE_SERVER_host); + if (servername) + BIO_printf(p->biodebug,"Hostname in TLS extension: \"%s\"\n",servername); + + if (!p->servername) { + SSL_set_tlsext_servername_done(s,2); + return SSL_ERROR_NONE; + } + + if (servername) { + if (strcmp(servername,p->servername)) + return TLS1_AD_UNRECOGNIZED_NAME; + if (ctx2) + SSL_set_SSL_CTX(s,ctx2); + SSL_set_tlsext_servername_done(s,1); + } + return SSL_ERROR_NONE; +} +#endif + int MAIN(int, char **); int MAIN(int argc, char *argv[]) @@ -528,10 +578,7 @@ int MAIN(int argc, char *argv[]) int no_tmp_rsa=0,no_dhe=0,no_ecdhe=0,nocert=0; int state=0; const SSL_METHOD *meth=NULL; -#ifdef sock_type -#undef sock_type -#endif - int sock_type=SOCK_STREAM; + int socketType=SOCK_STREAM; #ifndef OPENSSL_NO_ENGINE ENGINE *e=NULL; #endif @@ -542,7 +589,16 @@ int MAIN(int argc, char *argv[]) int s_dcert_format = FORMAT_PEM, s_dkey_format = FORMAT_PEM; X509 *s_cert = NULL, *s_dcert = NULL; EVP_PKEY *s_key = NULL, *s_dkey = NULL; +#ifndef OPENSSL_NO_TLSEXT + EVP_PKEY *s_key2 = NULL; + X509 *s_cert2 = NULL; +#endif +#ifndef OPENSSL_NO_TLSEXT + tlsextctx tlsextcbp = + {NULL,NULL + }; +#endif #if !defined(OPENSSL_NO_SSL2) && !defined(OPENSSL_NO_SSL3) meth=SSLv23_server_method(); #elif !defined(OPENSSL_NO_SSL3) @@ -755,14 +811,14 @@ int MAIN(int argc, char *argv[]) else if (strcmp(*argv,"-dtls1") == 0) { meth=DTLSv1_server_method(); - sock_type = SOCK_DGRAM; + socketType = SOCK_DGRAM; } else if (strcmp(*argv,"-timeout") == 0) enable_timeouts = 1; else if (strcmp(*argv,"-mtu") == 0) { if (--argc < 1) goto bad; - mtu = atol(*(++argv)); + socketMtu = atol(*(++argv)); } else if (strcmp(*argv, "-chain") == 0) cert_chain = 1; @@ -784,6 +840,24 @@ int MAIN(int argc, char *argv[]) if (--argc < 1) goto bad; inrand= *(++argv); } +#ifndef OPENSSL_NO_TLSEXT + else if (strcmp(*argv,"-servername") == 0) + { + if (--argc < 1) goto bad; + tlsextcbp.servername= *(++argv); + /* meth=TLSv1_server_method(); */ + } + else if (strcmp(*argv,"-cert2") == 0) + { + if (--argc < 1) goto bad; + s_cert_file2= *(++argv); + } + else if (strcmp(*argv,"-key2") == 0) + { + if (--argc < 1) goto bad; + s_key_file2= *(++argv); + } +#endif else { BIO_printf(bio_err,"unknown option %s\n",*argv); @@ -816,6 +890,10 @@ bad: if (s_key_file == NULL) s_key_file = s_cert_file; +#ifndef OPENSSL_NO_TLSEXT + if (s_key_file2 == NULL) + s_key_file2 = s_cert_file2; +#endif if (nocert == 0) { @@ -835,8 +913,31 @@ bad: ERR_print_errors(bio_err); goto end; } + +#ifndef OPENSSL_NO_TLSEXT + if (tlsextcbp.servername) + { + s_key2 = load_key(bio_err, s_key_file2, s_key_format, 0, pass, e, + "second server certificate private key file"); + if (!s_key2) + { + ERR_print_errors(bio_err); + goto end; + } + + s_cert2 = load_cert(bio_err,s_cert_file2,s_cert_format, + NULL, e, "second server certificate file"); + + if (!s_cert2) + { + ERR_print_errors(bio_err); + goto end; + } + } +#endif } + if (s_dcert_file) { @@ -893,6 +994,10 @@ bad: s_key_file=NULL; s_dcert_file=NULL; s_dkey_file=NULL; +#ifndef OPENSSL_NO_TLSEXT + s_cert_file2=NULL; + s_key_file2=NULL; +#endif } ctx=SSL_CTX_new(meth); @@ -924,7 +1029,7 @@ bad: /* DTLS: partial reads end up discarding unread UDP bytes :-( * Setting read ahead solves this problem. */ - if (sock_type == SOCK_DGRAM) SSL_CTX_set_read_ahead(ctx, 1); + if (socketType == SOCK_DGRAM) SSL_CTX_set_read_ahead(ctx, 1); if (state) SSL_CTX_set_info_callback(ctx,apps_ssl_info_callback); @@ -952,6 +1057,57 @@ bad: store = SSL_CTX_get_cert_store(ctx); X509_STORE_set_flags(store, vflags); +#ifndef OPENSSL_NO_TLSEXT + if (s_cert2) { + ctx2=SSL_CTX_new(meth); + if (ctx2 == NULL) + { + ERR_print_errors(bio_err); + goto end; + } + } + + if (ctx2) { + BIO_printf(bio_s_out,"Setting secondary ctx parameters\n"); + if (session_id_prefix) + { + if(strlen(session_id_prefix) >= 32) + BIO_printf(bio_err, +"warning: id_prefix is too long, only one new session will be possible\n"); + else if(strlen(session_id_prefix) >= 16) + BIO_printf(bio_err, +"warning: id_prefix is too long if you use SSLv2\n"); + if(!SSL_CTX_set_generate_session_id(ctx2, generate_session_id)) + { + BIO_printf(bio_err,"error setting 'id_prefix'\n"); + ERR_print_errors(bio_err); + goto end; + } + BIO_printf(bio_err,"id_prefix '%s' set.\n", session_id_prefix); + } + SSL_CTX_set_quiet_shutdown(ctx2,1); + if (bugs) SSL_CTX_set_options(ctx2,SSL_OP_ALL); + if (hack) SSL_CTX_set_options(ctx2,SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG); + SSL_CTX_set_options(ctx2,off); + /* DTLS: partial reads end up discarding unread UDP bytes :-( + * Setting read ahead solves this problem. + */ + if (socketType == SOCK_DGRAM) SSL_CTX_set_read_ahead(ctx2, 1); + + if (state) SSL_CTX_set_info_callback(ctx2,apps_ssl_info_callback); + + SSL_CTX_sess_set_cache_size(ctx2,128); + + if ((!SSL_CTX_load_verify_locations(ctx2,CAfile,CApath)) || + (!SSL_CTX_set_default_verify_paths(ctx2))) + { + ERR_print_errors(bio_err); + } + store = SSL_CTX_get_cert_store(ctx2); + X509_STORE_set_flags(store, vflags); + + } +#endif #ifndef OPENSSL_NO_DH if (!no_dhe) { @@ -974,6 +1130,22 @@ bad: (void)BIO_flush(bio_s_out); SSL_CTX_set_tmp_dh(ctx,dh); +#ifndef OPENSSL_NO_TLSEXT + if (ctx2) { + if (!dhfile) { + DH *dh2=load_dh_param(s_cert_file2); + if (dh2 != NULL) + { + BIO_printf(bio_s_out,"Setting temp DH parameters\n"); + (void)BIO_flush(bio_s_out); + + DH_free(dh); + dh = dh2; + } + } + SSL_CTX_set_tmp_dh(ctx2,dh); + } +#endif DH_free(dh); } #endif @@ -1019,12 +1191,20 @@ bad: (void)BIO_flush(bio_s_out); SSL_CTX_set_tmp_ecdh(ctx,ecdh); +#ifndef OPENSSL_NO_TLSEXT + if (ctx2) + SSL_CTX_set_tmp_ecdh(ctx2,ecdh); +#endif EC_KEY_free(ecdh); } #endif if (!set_cert_key_stuff(ctx,s_cert,s_key)) goto end; +#ifndef OPENSSL_NO_TLSEXT + if (ctx2 && !set_cert_key_stuff(ctx2,s_cert2,s_key2)) + goto end; +#endif if (s_dcert != NULL) { if (!set_cert_key_stuff(ctx,s_dcert,s_dkey)) @@ -1033,8 +1213,13 @@ bad: #ifndef OPENSSL_NO_RSA #if 1 - if (!no_tmp_rsa) + if (!no_tmp_rsa) { SSL_CTX_set_tmp_rsa_callback(ctx,tmp_rsa_cb); +#ifndef OPENSSL_NO_TLSEXT + if (ctx2) + SSL_CTX_set_tmp_rsa_callback(ctx2,tmp_rsa_cb); +#endif + } #else if (!no_tmp_rsa && SSL_CTX_need_tmp_RSA(ctx)) { @@ -1050,30 +1235,65 @@ bad: ERR_print_errors(bio_err); goto end; } +#ifndef OPENSSL_NO_TLSEXT + if (ctx2) { + if (!SSL_CTX_set_tmp_rsa(ctx2,rsa)) + { + ERR_print_errors(bio_err); + goto end; + } + } +#endif RSA_free(rsa); BIO_printf(bio_s_out,"\n"); } #endif #endif - if (cipher != NULL) + if (cipher != NULL) { if(!SSL_CTX_set_cipher_list(ctx,cipher)) { - BIO_printf(bio_err,"error setting cipher list\n"); - ERR_print_errors(bio_err); - goto end; + BIO_printf(bio_err,"error setting cipher list\n"); + ERR_print_errors(bio_err); + goto end; + } +#ifndef OPENSSL_NO_TLSEXT + if (ctx2 && !SSL_CTX_set_cipher_list(ctx2,cipher)) { + BIO_printf(bio_err,"error setting cipher list\n"); + ERR_print_errors(bio_err); + goto end; + } +#endif } SSL_CTX_set_verify(ctx,s_server_verify,verify_callback); SSL_CTX_set_session_id_context(ctx,(void*)&s_server_session_id_context, sizeof s_server_session_id_context); - if (CAfile != NULL) +#ifndef OPENSSL_NO_TLSEXT + if (ctx2) { + SSL_CTX_set_verify(ctx2,s_server_verify,verify_callback); + SSL_CTX_set_session_id_context(ctx2,(void*)&s_server_session_id_context, + sizeof s_server_session_id_context); + + } + tlsextcbp.biodebug = bio_s_out; + SSL_CTX_set_tlsext_servername_callback(ctx2, ssl_servername_cb); + SSL_CTX_set_tlsext_servername_arg(ctx2, &tlsextcbp); + SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_cb); + SSL_CTX_set_tlsext_servername_arg(ctx, &tlsextcbp); +#endif + if (CAfile != NULL) { SSL_CTX_set_client_CA_list(ctx,SSL_load_client_CA_file(CAfile)); +#ifndef OPENSSL_NO_TLSEXT + if (ctx2) + SSL_CTX_set_client_CA_list(ctx2,SSL_load_client_CA_file(CAfile)); +#endif + } BIO_printf(bio_s_out,"ACCEPT\n"); if (www) - do_server(port,sock_type,&accept_socket,www_body, context); + do_server(port,socketType,&accept_socket,www_body, context); else - do_server(port,sock_type,&accept_socket,sv_body, context); + do_server(port,socketType,&accept_socket,sv_body, context); print_stats(bio_s_out,ctx); ret=0; end: @@ -1090,6 +1310,13 @@ end: OPENSSL_free(pass); if (dpass) OPENSSL_free(dpass); +#ifndef OPENSSL_NO_TLSEXT + if (ctx2 != NULL) SSL_CTX_free(ctx2); + if (s_cert2) + X509_free(s_cert2); + if (s_key2) + EVP_PKEY_free(s_key2); +#endif if (bio_s_out != NULL) { BIO_free(bio_s_out); @@ -1189,10 +1416,10 @@ static int sv_body(char *hostname, int s, unsigned char *context) } - if ( mtu > 0) + if ( socketMtu > 0) { SSL_set_options(con, SSL_OP_NO_QUERY_MTU); - SSL_set_mtu(con, mtu); + SSL_set_mtu(con, socketMtu); } else /* want to do MTU discovery */ diff --git a/ssl/s23_clnt.c b/ssl/s23_clnt.c index 1d1110b6f0..5a07db158f 100644 --- a/ssl/s23_clnt.c +++ b/ssl/s23_clnt.c @@ -349,6 +349,10 @@ static int ssl23_client_hello(SSL *s) p+=i; /* COMPRESSION */ +#ifdef OPENSSL_NO_COMP + *(p++)=1; +#else + if ((s->options & SSL_OP_NO_COMPRESSION) || !s->ctx->comp_methods) j=0; @@ -360,7 +364,15 @@ static int ssl23_client_hello(SSL *s) comp=sk_SSL_COMP_value(s->ctx->comp_methods,i); *(p++)=comp->id; } +#endif *(p++)=0; /* Add the NULL method */ +#ifndef OPENSSL_NO_TLSEXT + if ((p = ssl_add_ClientHello_TLS_extensions(s, p, buf+SSL3_RT_MAX_PLAIN_LENGTH)) == NULL) + { + SSLerr(SSL_F_SSL3_CLIENT_HELLO,ERR_R_INTERNAL_ERROR); + return -1; + } +#endif l = p-d; *p = 42; diff --git a/ssl/s23_srvr.c b/ssl/s23_srvr.c index b33b699a1c..8bf044e15f 100644 --- a/ssl/s23_srvr.c +++ b/ssl/s23_srvr.c @@ -140,7 +140,7 @@ IMPLEMENT_ssl23_meth_func(SSLv23_server_method, int ssl23_accept(SSL *s) { BUF_MEM *buf; - unsigned long Time=(unsigned long)time(NULL); + unsigned long Time=time(NULL); void (*cb)(const SSL *ssl,int type,int val)=NULL; int ret= -1; int new_state,state; @@ -416,7 +416,7 @@ int ssl23_get_client_hello(SSL *s) n2s(p,sil); n2s(p,cl); d=(unsigned char *)s->init_buf->data; - if ((csl+sil+cl+11) != s->packet_length) + if ((csl+sil+cl+11) > s->packet_length) { SSLerr(SSL_F_SSL23_GET_CLIENT_HELLO,SSL_R_RECORD_LENGTH_MISMATCH); goto err; @@ -459,6 +459,12 @@ int ssl23_get_client_hello(SSL *s) *(d++)=1; *(d++)=0; + /* copy any remaining data with may be extensions */ + p = p+csl+sil+cl ; + while (p < s->packet+s->packet_length) { + *(d++)=*(p++); + } + i = (d-(unsigned char *)s->init_buf->data) - 4; l2n3((long)i, d_len); diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c index 0098f56178..7a4f256642 100644 --- a/ssl/s3_clnt.c +++ b/ssl/s3_clnt.c @@ -255,6 +255,16 @@ int ssl3_connect(SSL *s) case SSL3_ST_CR_SRVR_HELLO_B: ret=ssl3_get_server_hello(s); if (ret <= 0) goto end; +#ifndef OPENSSL_NO_TLSEXT + { + int extension_error = 0,al; + if ((al = ssl_check_Hello_TLS_extensions(s,&extension_error)) != SSL_ERROR_NONE){ + ret = -1; + SSLerr(SSL_F_SSL3_CONNECT,SSL_R_SERVERHELLO_TLS_EXT); + goto end; + } + } +#endif if (s->hit) s->state=SSL3_ST_CR_FINISHED_A; else @@ -602,6 +612,13 @@ int ssl3_client_hello(SSL *s) } #endif *(p++)=0; /* Add the NULL method */ +#ifndef OPENSSL_NO_TLSEXT + if ((p = ssl_add_ClientHello_TLS_extensions(s, p, buf+SSL3_RT_MAX_PLAIN_LENGTH)) == NULL) + { + SSLerr(SSL_F_SSL3_CLIENT_HELLO,ERR_R_INTERNAL_ERROR); + goto err; + } +#endif l=(p-d); d=buf; @@ -786,6 +803,16 @@ int ssl3_get_server_hello(SSL *s) s->s3->tmp.new_compression=comp; } #endif +#ifndef OPENSSL_NO_TLSEXT + /* TLS extensions*/ + if (s->version > SSL3_VERSION) + { + if ((al = ssl_parse_ServerHello_TLS_extensions(s,&p,d,n)) != SSL_ERROR_NONE){ + SSLerr(SSL_F_SSL3_GET_SERVER_HELLO,SSL_R_PARSE_TLS_EXT); + goto f_err; + } + } +#endif if (p != (d+n)) { diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c index 791c5e987c..845e5a16c5 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c @@ -1643,6 +1643,43 @@ long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg) } break; #endif /* !OPENSSL_NO_ECDH */ +#ifndef OPENSSL_NO_TLSEXT + case SSL_CTRL_GET_TLSEXT_HOSTNAME: + if (larg != TLSEXT_TYPE_SERVER_host) + { + SSLerr(SSL_F_SSL3_CTRL, SSL_R_SSL3_EXT_INVALID_SERVERNAME_TYPE); + return(0); + } + *((char **) parg) = s->session&&s->session->tlsext_hostname?s->session->tlsext_hostname:s->tlsext_hostname; + ret = 1; + break; + case SSL_CTRL_SET_TLSEXT_HOSTNAME: + if (larg == TLSEXT_TYPE_SERVER_host) { + if (s->tlsext_hostname != NULL) + OPENSSL_free(s->tlsext_hostname); + s->tlsext_hostname = NULL; + + ret = 1; + if (parg == NULL) + break; + if (strlen((char *)parg) > 255) { + SSLerr(SSL_F_SSL3_CTRL, SSL_R_SSL3_EXT_INVALID_SERVERNAME); + return 0; + } + if ((s->tlsext_hostname = BUF_strdup((char *)parg)) == NULL) { + SSLerr(SSL_F_SSL3_CTRL, ERR_R_INTERNAL_ERROR); + return 0; + } + } else { + SSLerr(SSL_F_SSL3_CTRL, SSL_R_SSL3_EXT_INVALID_SERVERNAME_TYPE); + return 0; + } + s->options |= SSL_OP_NO_SSLv2; + break; + case SSL_CTRL_SET_TLSEXT_SERVERNAME_DONE: + s->servername_done = larg; + break; +#endif /* !OPENSSL_NO_TLSEXT */ default: break; } @@ -1827,6 +1864,11 @@ long ssl3_ctx_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) } break; #endif /* !OPENSSL_NO_ECDH */ +#ifndef OPENSSL_NO_TLSEXT + case SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG: + ctx->tlsext_servername_arg=parg; + break; +#endif /* !OPENSSL_NO_TLSEXT */ /* A Thawte special :-) */ case SSL_CTRL_EXTRA_CHAIN_CERT: if (ctx->extra_certs == NULL) @@ -1871,6 +1913,11 @@ long ssl3_ctx_callback_ctrl(SSL_CTX *ctx, int cmd, void (*fp)(void)) cert->ecdh_tmp_cb = (EC_KEY *(*)(SSL *, int, int))fp; } break; +#endif +#ifndef OPENSSL_NO_TLSEXT + case SSL_CTRL_SET_TLSEXT_SERVERNAME_CB: + ctx->tlsext_servername_callback=(int (*)(SSL *,int *,void *))fp; + break; #endif default: return(0); diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c index 18030842b8..25b56fa6fa 100644 --- a/ssl/s3_srvr.c +++ b/ssl/s3_srvr.c @@ -281,6 +281,17 @@ int ssl3_accept(SSL *s) s->shutdown=0; ret=ssl3_get_client_hello(s); if (ret <= 0) goto end; +#ifndef OPENSSL_NO_TLSEXT + { + int extension_error = 0,al; + if ((al = ssl_check_Hello_TLS_extensions(s,&extension_error)) != SSL_ERROR_NONE){ + ret = -1; + SSLerr(SSL_F_SSL3_ACCEPT,SSL_R_CLIENTHELLO_TLS_EXT); + ssl3_send_alert(s,al,extension_error); + goto end; + } + } +#endif s->new_session = 2; s->state=SSL3_ST_SW_SRVR_HELLO_A; s->init_num=0; @@ -942,6 +953,17 @@ int ssl3_get_client_hello(SSL *s) } } #endif +#ifndef OPENSSL_NO_TLSEXT + /* TLS extensions*/ + if (s->version > SSL3_VERSION) + { + if ((al = ssl_parse_ClientHello_TLS_extensions(s,&p,d,n)) != SSL_ERROR_NONE){ + SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_PARSE_TLS_EXT); + ssl3_send_alert(s,SSL3_AL_WARNING,al); + return (ret = al); + } + } +#endif /* Given s->session->ciphers and SSL_get_ciphers, we must * pick a cipher */ @@ -1086,6 +1108,13 @@ int ssl3_send_server_hello(SSL *s) else *(p++)=s->s3->tmp.new_compression->id; #endif +#ifndef OPENSSL_NO_TLSEXT + if ((p = ssl_add_ServerHello_TLS_extensions(s, p, buf+SSL3_RT_MAX_PLAIN_LENGTH)) == NULL) + { + SSLerr(SSL_F_SSL3_SEND_SERVER_HELLO,ERR_R_INTERNAL_ERROR); + return -1; + } +#endif /* do the header */ l=(p-d); diff --git a/ssl/ssl.h b/ssl/ssl.h index 42e34b912b..29f1e0beec 100644 --- a/ssl/ssl.h +++ b/ssl/ssl.h @@ -172,6 +172,11 @@ #include +#ifdef OPENSSL_NO_TLS1 +# ifndef OPENSSL_NO_TLSEXT +# define OPENSSL_NO_TLSEXT +# endif +#endif #ifndef OPENSSL_NO_COMP #include #endif @@ -439,6 +444,9 @@ typedef struct ssl_session_st unsigned int krb5_client_princ_len; unsigned char krb5_client_princ[SSL_MAX_KRB5_PRINCIPAL_LENGTH]; #endif /* OPENSSL_NO_KRB5 */ +#ifndef OPENSSL_NO_TLSEXT + char *tlsext_hostname; +#endif int not_resumable; @@ -755,6 +763,13 @@ struct ssl_ctx_st * padding and MAC overheads. */ unsigned int max_send_fragment; + +#ifndef OPENSSL_NO_TLSEXT + /* TLS extensions servername callback */ + int (*tlsext_servername_callback)(SSL*, int *, void *); + void *tlsext_servername_arg; +#endif + }; #define SSL_SESS_CACHE_OFF 0x0000 @@ -977,6 +992,14 @@ struct ssl_st int client_version; /* what was passed, used for * SSLv3/TLS rollback check */ unsigned int max_send_fragment; +#ifndef OPENSSL_NO_TLSEXT + char *tlsext_hostname; + int servername_done; /* no further mod of servername + 0 : call the servername extension callback. + 1 : prepare 2, allow last ack just after in server callback. + 2 : don't call servername callback, no ack in server hello + */ +#endif }; #ifdef __cplusplus @@ -1122,6 +1145,9 @@ size_t SSL_get_peer_finished(const SSL *s, void *buf, size_t count); #define SSL_AD_INTERNAL_ERROR TLS1_AD_INTERNAL_ERROR /* fatal */ #define SSL_AD_USER_CANCELLED TLS1_AD_USER_CANCELLED #define SSL_AD_NO_RENEGOTIATION TLS1_AD_NO_RENEGOTIATION +#ifndef OPENSSL_NO_TLSEXT +#define SSL_AD_UNRECOGNIZED_NAME TLS1_AD_UNRECOGNIZED_NAME +#endif #define SSL_ERROR_NONE 0 #define SSL_ERROR_SSL 1 @@ -1454,6 +1480,7 @@ int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, SSL_SESSION *SSL_get_session(const SSL *ssl); SSL_SESSION *SSL_get1_session(SSL *ssl); /* obtain a reference count */ SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl); +SSL_CTX *SSL_set_SSL_CTX(SSL *ssl, SSL_CTX* ctx); void SSL_set_info_callback(SSL *ssl, void (*cb)(const SSL *ssl,int type,int val)); void (*SSL_get_info_callback(const SSL *ssl))(const SSL *ssl,int type,int val); @@ -1777,6 +1804,7 @@ void ERR_load_SSL_strings(void); #define SSL_R_CIPHER_CODE_WRONG_LENGTH 137 #define SSL_R_CIPHER_OR_HASH_UNAVAILABLE 138 #define SSL_R_CIPHER_TABLE_SRC_ERROR 139 +#define SSL_R_CLIENTHELLO_TLS_EXT 2003 #define SSL_R_COMPRESSED_LENGTH_TOO_LONG 140 #define SSL_R_COMPRESSION_FAILURE 141 #define SSL_R_COMPRESSION_ID_NOT_WITHIN_PRIVATE_RANGE 1120 @@ -1861,6 +1889,7 @@ void ERR_load_SSL_strings(void); #define SSL_R_NULL_SSL_METHOD_PASSED 196 #define SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED 197 #define SSL_R_PACKET_LENGTH_TOO_LONG 198 +#define SSL_R_PARSE_TLS_EXT 2004 #define SSL_R_PATH_TOO_LONG 270 #define SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE 199 #define SSL_R_PEER_ERROR 200 @@ -1884,11 +1913,14 @@ void ERR_load_SSL_strings(void); #define SSL_R_REUSE_CERT_LENGTH_NOT_ZERO 216 #define SSL_R_REUSE_CERT_TYPE_NOT_ZERO 217 #define SSL_R_REUSE_CIPHER_LIST_NOT_ZERO 218 +#define SSL_R_SERVERHELLO_TLS_EXT 2005 #define SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED 277 #define SSL_R_SHORT_READ 219 #define SSL_R_SIGNATURE_FOR_NON_SIGNING_CERTIFICATE 220 #define SSL_R_SSL23_DOING_SESSION_ID_REUSE 221 #define SSL_R_SSL2_CONNECTION_ID_TOO_LONG 1114 +#define SSL_R_SSL3_EXT_INVALID_SERVERNAME 2006 +#define SSL_R_SSL3_EXT_INVALID_SERVERNAME_TYPE 2007 #define SSL_R_SSL3_SESSION_ID_TOO_LONG 1113 #define SSL_R_SSL3_SESSION_ID_TOO_SHORT 222 #define SSL_R_SSLV3_ALERT_BAD_CERTIFICATE 1042 diff --git a/ssl/ssl_asn1.c b/ssl/ssl_asn1.c index d129acc329..e98a47fc9d 100644 --- a/ssl/ssl_asn1.c +++ b/ssl/ssl_asn1.c @@ -78,12 +78,15 @@ typedef struct ssl_session_asn1_st ASN1_INTEGER time; ASN1_INTEGER timeout; ASN1_INTEGER verify_result; +#ifndef OPENSSL_NO_TLSEXT + ASN1_OCTET_STRING tlsext_hostname; +#endif /* OPENSSL_NO_TLSEXT */ } SSL_SESSION_ASN1; int i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp) { #define LSIZE2 (sizeof(long)*2) - int v1=0,v2=0,v3=0,v4=0,v5=0; + int v1=0,v2=0,v3=0,v4=0,v5=0,v6=0; unsigned char buf[4],ibuf1[LSIZE2],ibuf2[LSIZE2]; unsigned char ibuf3[LSIZE2],ibuf4[LSIZE2],ibuf5[LSIZE2]; long l; @@ -178,6 +181,14 @@ int i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp) ASN1_INTEGER_set(&a.verify_result,in->verify_result); } +#ifndef OPENSSL_NO_TLSEXT + if (in->tlsext_hostname) + { + a.tlsext_hostname.length=strlen(in->tlsext_hostname); + a.tlsext_hostname.type=V_ASN1_OCTET_STRING; + a.tlsext_hostname.data=in->tlsext_hostname; + } +#endif /* OPENSSL_NO_TLSEXT */ M_ASN1_I2D_len(&(a.version), i2d_ASN1_INTEGER); M_ASN1_I2D_len(&(a.ssl_version), i2d_ASN1_INTEGER); @@ -200,6 +211,10 @@ int i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp) if (in->verify_result != X509_V_OK) M_ASN1_I2D_len_EXP_opt(&(a.verify_result),i2d_ASN1_INTEGER,5,v5); +#ifndef OPENSSL_NO_TLSEXT + if (in->tlsext_hostname) + M_ASN1_I2D_len_EXP_opt(&(a.tlsext_hostname), i2d_ASN1_OCTET_STRING,6,v6); +#endif /* OPENSSL_NO_TLSEXT */ M_ASN1_I2D_seq_total(); M_ASN1_I2D_put(&(a.version), i2d_ASN1_INTEGER); @@ -223,6 +238,10 @@ int i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp) v4); if (in->verify_result != X509_V_OK) M_ASN1_I2D_put_EXP_opt(&a.verify_result,i2d_ASN1_INTEGER,5,v5); +#ifndef OPENSSL_NO_TLSEXT + if (in->tlsext_hostname) + M_ASN1_I2D_put_EXP_opt(&(a.tlsext_hostname), i2d_ASN1_OCTET_STRING,6,v6); +#endif /* OPENSSL_NO_TLSEXT */ M_ASN1_I2D_finish(); } @@ -394,5 +413,21 @@ SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const unsigned char **pp, else ret->verify_result=X509_V_OK; +#ifndef OPENSSL_NO_TLSEXT + os.length=0; + os.data=NULL; + M_ASN1_D2I_get_EXP_opt(osp,d2i_ASN1_OCTET_STRING,6); + if (os.data) + { + ret->tlsext_hostname = BUF_strndup(os.data, os.length); + OPENSSL_free(os.data); + os.data = NULL; + os.length = 0; + } + else + ret->tlsext_hostname=NULL; + +#endif /* OPENSSL_NO_TLSEXT */ + M_ASN1_D2I_Finish(a,SSL_SESSION_free,SSL_F_D2I_SSL_SESSION); } diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c index 282f1a6755..133c4a7447 100644 --- a/ssl/ssl_err.c +++ b/ssl/ssl_err.c @@ -292,6 +292,7 @@ static ERR_STRING_DATA SSL_str_reasons[]= {ERR_REASON(SSL_R_CIPHER_CODE_WRONG_LENGTH),"cipher code wrong length"}, {ERR_REASON(SSL_R_CIPHER_OR_HASH_UNAVAILABLE),"cipher or hash unavailable"}, {ERR_REASON(SSL_R_CIPHER_TABLE_SRC_ERROR),"cipher table src error"}, +{ERR_REASON(SSL_R_CLIENTHELLO_TLS_EXT) ,"clienthello tls ext"}, {ERR_REASON(SSL_R_COMPRESSED_LENGTH_TOO_LONG),"compressed length too long"}, {ERR_REASON(SSL_R_COMPRESSION_FAILURE) ,"compression failure"}, {ERR_REASON(SSL_R_COMPRESSION_ID_NOT_WITHIN_PRIVATE_RANGE),"compression id not within private range"}, @@ -376,6 +377,7 @@ static ERR_STRING_DATA SSL_str_reasons[]= {ERR_REASON(SSL_R_NULL_SSL_METHOD_PASSED),"null ssl method passed"}, {ERR_REASON(SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED),"old session cipher not returned"}, {ERR_REASON(SSL_R_PACKET_LENGTH_TOO_LONG),"packet length too long"}, +{ERR_REASON(SSL_R_PARSE_TLS_EXT) ,"parse tls ext"}, {ERR_REASON(SSL_R_PATH_TOO_LONG) ,"path too long"}, {ERR_REASON(SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE),"peer did not return a certificate"}, {ERR_REASON(SSL_R_PEER_ERROR) ,"peer error"}, @@ -399,11 +401,14 @@ static ERR_STRING_DATA SSL_str_reasons[]= {ERR_REASON(SSL_R_REUSE_CERT_LENGTH_NOT_ZERO),"reuse cert length not zero"}, {ERR_REASON(SSL_R_REUSE_CERT_TYPE_NOT_ZERO),"reuse cert type not zero"}, {ERR_REASON(SSL_R_REUSE_CIPHER_LIST_NOT_ZERO),"reuse cipher list not zero"}, +{ERR_REASON(SSL_R_SERVERHELLO_TLS_EXT) ,"serverhello tls ext"}, {ERR_REASON(SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED),"session id context uninitialized"}, {ERR_REASON(SSL_R_SHORT_READ) ,"short read"}, {ERR_REASON(SSL_R_SIGNATURE_FOR_NON_SIGNING_CERTIFICATE),"signature for non signing certificate"}, {ERR_REASON(SSL_R_SSL23_DOING_SESSION_ID_REUSE),"ssl23 doing session id reuse"}, {ERR_REASON(SSL_R_SSL2_CONNECTION_ID_TOO_LONG),"ssl2 connection id too long"}, +{ERR_REASON(SSL_R_SSL3_EXT_INVALID_SERVERNAME),"ssl3 ext invalid servername"}, +{ERR_REASON(SSL_R_SSL3_EXT_INVALID_SERVERNAME_TYPE),"ssl3 ext invalid servername type"}, {ERR_REASON(SSL_R_SSL3_SESSION_ID_TOO_LONG),"ssl3 session id too long"}, {ERR_REASON(SSL_R_SSL3_SESSION_ID_TOO_SHORT),"ssl3 session id too short"}, {ERR_REASON(SSL_R_SSLV3_ALERT_BAD_CERTIFICATE),"sslv3 alert bad certificate"}, diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index b79ac1c9a2..afe7162ab7 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -1315,6 +1315,27 @@ err: return(NULL); } +#ifndef OPENSSL_TLSEXT +/** return a servername extension value if provided in CLIENT HELLO + * or NULL. + * For the moment, only hostname types are supported. + */ + +const char *SSL_get_servername(const SSL *s, const int type) { + + if (type != TLSEXT_TYPE_SERVER_host) + return NULL; + return s->session /*&&s->session->tlsext_hostname*/?s->session->tlsext_hostname:s->tlsext_hostname; +} + +int SSL_get_servername_type(const SSL *s) { + + if (s->session &&s->session->tlsext_hostname ?s->session->tlsext_hostname:s->tlsext_hostname) + return TLSEXT_TYPE_SERVER_host; + return -1; +} + +#endif unsigned long SSL_SESSION_hash(const SSL_SESSION *a) { unsigned long l; @@ -1466,6 +1487,10 @@ SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth) ret->max_send_fragment = SSL3_RT_MAX_PLAIN_LENGTH; +#ifndef OPENSSL_NO_TLSEXT + ret->tlsext_servername_callback = NULL; + ret->tlsext_servername_arg = NULL; +#endif return(ret); err: SSLerr(SSL_F_SSL_CTX_NEW,ERR_R_MALLOC_FAILURE); @@ -2415,6 +2440,19 @@ SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl) return(ssl->ctx); } +SSL_CTX *SSL_set_SSL_CTX(SSL *ssl, SSL_CTX* ctx) + { + + if (ssl->cert != NULL) + ssl_cert_free(ssl->cert); + ssl->cert = ssl_cert_dup(ctx->cert); + CRYPTO_add(&ctx->references,1,CRYPTO_LOCK_SSL_CTX); + if (ssl->ctx != NULL) + SSL_CTX_free(ssl->ctx); /* decrement reference count */ + ssl->ctx = ctx; + return(ssl->ctx); + } + #ifndef OPENSSL_NO_STDIO int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx) { diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index 6a56384a8c..26a062fdcd 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -940,5 +940,11 @@ int check_srvr_ecc_cert_and_alg(X509 *x, SSL_CIPHER *cs); SSL_COMP *ssl3_comp_find(STACK_OF(SSL_COMP) *sk, int n); - +#ifndef OPENSSL_NO_TLSEXT +unsigned char *ssl_add_ClientHello_TLS_extensions(SSL *s, unsigned char *p, unsigned char *limit); +unsigned char *ssl_add_ServerHello_TLS_extensions(SSL *s, unsigned char *p, unsigned char *limit); +int ssl_parse_ClientHello_TLS_extensions(SSL *s, unsigned char **data, unsigned char *d, int n); +int ssl_parse_ServerHello_TLS_extensions(SSL *s, unsigned char **data, unsigned char *d, int n); +int ssl_check_Hello_TLS_extensions(SSL *s,int *ad); +#endif #endif diff --git a/ssl/ssl_sess.c b/ssl/ssl_sess.c index 7a4bb92b5c..6c2c04bf0f 100644 --- a/ssl/ssl_sess.c +++ b/ssl/ssl_sess.c @@ -122,6 +122,9 @@ SSL_SESSION *SSL_SESSION_new(void) ss->prev=NULL; ss->next=NULL; ss->compress_meth=0; +#ifndef OPENSSL_NO_TLSEXT + ss->tlsext_hostname = NULL; +#endif CRYPTO_new_ex_data(CRYPTO_EX_INDEX_SSL_SESSION, ss, &ss->ex_data); return(ss); } @@ -546,6 +549,10 @@ void SSL_SESSION_free(SSL_SESSION *ss) if (ss->sess_cert != NULL) ssl_sess_cert_free(ss->sess_cert); if (ss->peer != NULL) X509_free(ss->peer); if (ss->ciphers != NULL) sk_SSL_CIPHER_free(ss->ciphers); +#ifndef OPENSSL_NO_TLSEXT + if (ss->tlsext_hostname != NULL) + OPENSSL_free(ss->tlsext_hostname); +#endif OPENSSL_cleanse(ss,sizeof(*ss)); OPENSSL_free(ss); } diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index fd0d8215b0..ff5bc58ce7 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -101,14 +101,186 @@ void tls1_clear(SSL *s) s->version=TLS1_VERSION; } -#if 0 -long tls1_ctrl(SSL *s, int cmd, long larg, char *parg) - { - return(0); +#ifndef OPENSSL_NO_TLSEXT +unsigned char *ssl_add_ClientHello_TLS_extensions(SSL *s, unsigned char *p, unsigned char *limit) { + int extdatalen=0; + unsigned char *ret = p; + + ret+=2; + + if (ret>=limit) return NULL; /* this really never occurs, but ... */ + if (s->servername_done == 0 && s->tlsext_hostname != NULL) { + /* Add TLS extension servername to the Client Hello message */ + unsigned long size_str; + long lenmax; + + if ((lenmax = limit - p - 7) < 0) return NULL; + if ((size_str = strlen(s->tlsext_hostname)) > (unsigned long)lenmax) return NULL; + + s2n(TLSEXT_TYPE_server_name,ret); + s2n(size_str+3,ret); + *(ret++) = (unsigned char) TLSEXT_TYPE_SERVER_host; + s2n(size_str,ret); + + memcpy(ret, s->tlsext_hostname, size_str); + ret+=size_str; } -long tls1_callback_ctrl(SSL *s, int cmd, void *(*fp)()) - { - return(0); + + if ((extdatalen = ret-p-2)== 0) + return p; + + s2n(extdatalen,p); + return ret; + +} + +unsigned char *ssl_add_ServerHello_TLS_extensions(SSL *s, unsigned char *p, unsigned char *limit) { + int extdatalen=0; + unsigned char *ret = p; + if (s->hit || s->servername_done == 2) + return p; + ret+=2; + if (s->servername_done == 1) + s->servername_done = 2; + + if (ret>=limit) return NULL; /* this really never occurs, but ... */ + + if (s->session->tlsext_hostname != NULL) { + + if (limit - p - 4 < 0) return NULL; + + s2n(TLSEXT_TYPE_server_name,ret); + s2n(0,ret); } + + + if ((extdatalen = ret-p-2)== 0) + return p; + + s2n(extdatalen,p); + return ret; + +} + +int ssl_parse_ClientHello_TLS_extensions(SSL *s, unsigned char **p, unsigned char *d, int n) { + unsigned short type; + unsigned short size; + unsigned short len; + unsigned char * data = *p; + + if (data >= (d+n-2)) + return SSL_ERROR_NONE; + n2s(data,len); + + if (data > (d+n-len)) + return SSL_ERROR_NONE; + + while(data <= (d+n-4)){ + n2s(data,type); + n2s(data,size); + + if (data+size > (d+n)) + return SSL_ERROR_SSL; + + if (type == TLSEXT_TYPE_server_name) { + unsigned char *sdata = data; + int servname_type; + int dsize = size-3 ; + + if (dsize > 0 ) { + servname_type = *(sdata++); + n2s(sdata,len); + if (len != dsize) + return SSL_ERROR_SSL; + + switch (servname_type) { + case TLSEXT_TYPE_SERVER_host: + if (s->session->tlsext_hostname == NULL) { + if (len > 255 || + ((s->session->tlsext_hostname = OPENSSL_malloc(len+1)) == NULL)) + return SSL_ERROR_SSL; + memcpy(s->session->tlsext_hostname, sdata, len); + s->session->tlsext_hostname[len]='\0'; + } + break; + default: + break; + } + + } + } + + data+=size; + } + *p = data; + + return SSL_ERROR_NONE; +} +int ssl_parse_ServerHello_TLS_extensions(SSL *s, unsigned char **p, unsigned char *d, int n) { + unsigned short type; + unsigned short size; + unsigned short len; + unsigned char *data = *p; + + int tlsext_servername = 0; + + if (data >= (d+n-2)) + return SSL_ERROR_NONE; + + + n2s(data,len); + + while(data <= (d+n-4)){ + n2s(data,type); + n2s(data,size); + + if (data+size > (d+n)) + return SSL_ERROR_SSL; + + if (type == TLSEXT_TYPE_server_name) { + if ( s->tlsext_hostname == NULL || size > 0 ) { + return SSL_ERROR_SSL; + } + tlsext_servername = 1; + } + + data+=size; + } + + + + if (data != d+n) + return SSL_ERROR_SSL; + + if (!s->hit && tlsext_servername == 1) { + if (s->tlsext_hostname) { + if (s->session->tlsext_hostname == NULL) { + s->session->tlsext_hostname = BUF_strdup(s->tlsext_hostname); + if (!s->session->tlsext_hostname) + return SSL_ERROR_SSL; + } + } else + return SSL_ERROR_SSL; + } + *p = data; + + return SSL_ERROR_NONE; +} + +int ssl_check_Hello_TLS_extensions(SSL *s,int *ad) +{ + int ret = SSL_ERROR_NONE; + + *ad = SSL_AD_UNRECOGNIZED_NAME; + if (s->servername_done == 0 && (s->ctx != NULL && s->ctx->tlsext_servername_callback != NULL) + && ((ret = s->ctx->tlsext_servername_callback(s, ad, s->ctx->tlsext_servername_arg))!= SSL_ERROR_NONE)) + return ret; + + else if (s->servername_done == 1) + s->servername_done = 2; + + return ret; +} #endif + diff --git a/ssl/tls1.h b/ssl/tls1.h index e7eaa835db..4c8a5daa56 100644 --- a/ssl/tls1.h +++ b/ssl/tls1.h @@ -97,6 +97,53 @@ extern "C" { #define TLS1_AD_USER_CANCELLED 90 #define TLS1_AD_NO_RENEGOTIATION 100 +#ifndef OPENSSL_NO_TLSEXT +#define TLS1_AD_UNRECOGNIZED_NAME 122 + +#define TLSEXT_TYPE_server_name 0 +#define TLSEXT_TYPE_max_fragment_length 1 +#define TLSEXT_TYPE_client_certificate_url 2 +#define TLSEXT_TYPE_trusted_ca_keys 3 +#define TLSEXT_TYPE_truncated_hmac 4 +#define TLSEXT_TYPE_status_request 5 +#define TLSEXT_TYPE_srp 6 + +#define TLSEXT_TYPE_SERVER_host 0 + +#define SSL_CTX_set_tlsext_hostname(ctx,name) \ +SSL_CTX_ctrl(ctx,SSL_CTRL_SET_TLSEXT_HOSTNAME,TLSEXT_TYPE_SERVER_host,(char *)name) +#define SSL_set_tlsext_hostname(s,name) \ +SSL_ctrl(s,SSL_CTRL_SET_TLSEXT_HOSTNAME,TLSEXT_TYPE_SERVER_host,(char *)name) + +#define SSL_CTX_set_tlsext_servername_callback(ctx, cb) \ +SSL_CTX_callback_ctrl(ctx,SSL_CTRL_SET_TLSEXT_SERVERNAME_CB,(void (*)(void))cb) +#define SSL_CTX_set_tlsext_servername_arg(ctx, arg) \ +SSL_CTX_ctrl(ctx,SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG,0, (void *)arg) + + +const char *SSL_get_servername(const SSL *s, const int type) ; +int SSL_get_servername_type(const SSL *s) ; + +#if 0 + #define SSL_get_tlsext_hostname(s,psn) \ + SSL_ctrl(s,SSL_CTRL_GET_TLSEXT_HOSTNAME,TLSEXT_TYPE_SERVER_host, (void *)psn) +#else + #define SSL_get_tlsext_hostname(s,psn) \ + (*psn = SSL_get_servername(s, TLSEXT_TYPE_SERVER_host),*psn != NULL) +#endif + #define SSL_set_tlsext_servername_done(s,t) \ + SSL_ctrl(s,SSL_CTRL_SET_TLSEXT_SERVERNAME_DONE,t, NULL) + +void SSL_set_ctx(SSL *s, SSL_CTX *ctx) ; + +#define SSL_CTRL_SET_TLSEXT_SERVERNAME_CB 53 +#define SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG 54 +#define SSL_CTRL_SET_TLSEXT_HOSTNAME 55 +#define SSL_CTRL_GET_TLSEXT_HOSTNAME 56 +#define SSL_CTRL_SET_TLSEXT_SERVERNAME_DONE 57 + +#endif + /* Additional TLS ciphersuites from expired Internet Draft * draft-ietf-tls-56-bit-ciphersuites-01.txt * (available if TLS1_ALLOW_EXPERIMENTAL_CIPHERSUITES is defined, see -- 2.34.1