From a13c20f60353d3cd3fdd4f23563819eeb4234528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bodo=20M=C3=B6ller?= Date: Mon, 9 Jan 2006 19:49:05 +0000 Subject: Further TLS extension updates Submitted by: Peter Sylvester --- CHANGES | 4 ++-- apps/s_client.c | 2 +- apps/s_server.c | 17 +++++++++------ crypto/ec/ec_err.c | 1 - ssl/s3_lib.c | 5 +---- ssl/s3_srvr.c | 12 +++++----- ssl/ssl.h | 8 ++++--- ssl/ssl_lib.c | 24 +++++++++++--------- ssl/ssl_sess.c | 18 +++++++++------ ssl/t1_lib.c | 64 ++++++++++++++++++++++++++++++++++++++++++++---------- ssl/tls1.h | 4 +++- 11 files changed, 106 insertions(+), 53 deletions(-) diff --git a/CHANGES b/CHANGES index dda03549fc..f4014aeefc 100644 --- a/CHANGES +++ b/CHANGES @@ -25,8 +25,6 @@ SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG - SSL_CTX_set_tlsext_servername_arg() SSL_CTRL_SET_TLSEXT_HOSTNAME - SSL_set_tlsext_hostname() - SSL_CTRL_SET_TLSEXT_SERVERNAME_DONE - - SSL_set_tlsext_servername_done() openssl s_client has a new '-servername' option. @@ -34,6 +32,8 @@ (subject to change); this allows testing the HostName extension for a specific single host name ('-cert' and '-key' remain fallbacks for handshakes without HostName negotiation). + The option servername_warn allows to return a warning alert instead of + a fatal alert in case of servername mismatch. [Peter Sylvester, Remy Allais, Christophe Renou] diff --git a/apps/s_client.c b/apps/s_client.c index 96cf511403..47cd9d93d5 100644 --- a/apps/s_client.c +++ b/apps/s_client.c @@ -647,7 +647,7 @@ bad: #ifndef OPENSSL_NO_TLSEXT if (servername != NULL) { - if (!SSL_set_tlsext_hostname(con,servername)) + if (!SSL_set_tlsext_host_name(con,servername)) { BIO_printf(bio_err,"Unable to set TLS servername extension.\n"); ERR_print_errors(bio_err); diff --git a/apps/s_server.c b/apps/s_server.c index 24a25d8156..e07f3dd20e 100644 --- a/apps/s_server.c +++ b/apps/s_server.c @@ -367,6 +367,7 @@ static void sv_usage(void) 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 - servername for HostName TLS extension\n"); + BIO_printf(bio_err," -servername_warn - on mismatch send warning (default fatal alert)\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"); @@ -533,6 +534,7 @@ static int ebcdic_puts(BIO *bp, const char *str) typedef struct tlsextctx_st { char * servername; BIO * biodebug; + int servername_warn; } tlsextctx; @@ -544,18 +546,16 @@ static int MS_CALLBACK ssl_servername_cb(SSL *s, int *ad, void *arg) BIO_printf(p->biodebug,"Hostname in TLS extension: \"%s\"\n",servername); if (!p->servername) - { - SSL_set_tlsext_servername_done(s,2); return 1; - } if (servername) { if (strcmp(servername,p->servername)) - return 0; - if (ctx2) + return p->servername_warn; + if (ctx2) { + BIO_printf(p->biodebug,"Swiching server context.\n"); SSL_set_SSL_CTX(s,ctx2); - SSL_set_tlsext_servername_done(s,1); + } } return 1; } @@ -597,7 +597,7 @@ int MAIN(int argc, char *argv[]) #endif #ifndef OPENSSL_NO_TLSEXT - tlsextctx tlsextcbp = {NULL, NULL}; + tlsextctx tlsextcbp = {NULL, NULL, -1}; #endif #if !defined(OPENSSL_NO_SSL2) && !defined(OPENSSL_NO_SSL3) meth=SSLv23_server_method(); @@ -846,6 +846,8 @@ int MAIN(int argc, char *argv[]) if (--argc < 1) goto bad; tlsextcbp.servername= *(++argv); } + else if (strcmp(*argv,"-servername_warn") == 0) + { tlsextcbp.servername_warn = 0; } else if (strcmp(*argv,"-cert2") == 0) { if (--argc < 1) goto bad; @@ -1553,6 +1555,7 @@ static int sv_body(char *hostname, int s, unsigned char *context) ret= -11;*/ goto err; } + if ((buf[0] == 'r') && ((buf[1] == '\n') || (buf[1] == '\r'))) { diff --git a/crypto/ec/ec_err.c b/crypto/ec/ec_err.c index 86b58271f4..18a300d753 100644 --- a/crypto/ec/ec_err.c +++ b/crypto/ec/ec_err.c @@ -131,7 +131,6 @@ static ERR_STRING_DATA EC_str_functs[]= {ERR_FUNC(EC_F_EC_GROUP_GET_ORDER), "EC_GROUP_get_order"}, {ERR_FUNC(EC_F_EC_GROUP_GET_PENTANOMIAL_BASIS), "EC_GROUP_get_pentanomial_basis"}, {ERR_FUNC(EC_F_EC_GROUP_GET_TRINOMIAL_BASIS), "EC_GROUP_get_trinomial_basis"}, -{ERR_FUNC(EC_F_EC_GROUP_GROUP2NID), "EC_GROUP_GROUP2NID"}, {ERR_FUNC(EC_F_EC_GROUP_NEW), "EC_GROUP_new"}, {ERR_FUNC(EC_F_EC_GROUP_NEW_BY_CURVE_NAME), "EC_GROUP_new_by_curve_name"}, {ERR_FUNC(EC_F_EC_GROUP_NEW_FROM_DATA), "EC_GROUP_NEW_FROM_DATA"}, diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c index 194b95d3f1..3b87222166 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c @@ -1654,7 +1654,7 @@ long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg) ret = 1; if (parg == NULL) break; - if (strlen((char *)parg) > 255) + if (strlen((char *)parg) > TLSEXT_MAXLEN_host_name) { SSLerr(SSL_F_SSL3_CTRL, SSL_R_SSL3_EXT_INVALID_SERVERNAME); return 0; @@ -1672,9 +1672,6 @@ long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg) } s->options |= SSL_OP_NO_SSLv2; /* can't use extension w/ SSL 2.0 format */ break; - case SSL_CTRL_SET_TLSEXT_SERVERNAME_DONE: - s->servername_done = larg; - break; #endif /* !OPENSSL_NO_TLSEXT */ default: break; diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c index 954959404a..c83505c0a5 100644 --- a/ssl/s3_srvr.c +++ b/ssl/s3_srvr.c @@ -283,14 +283,16 @@ int ssl3_accept(SSL *s) if (ret <= 0) goto end; #ifndef OPENSSL_NO_TLSEXT { - int al; - if (ssl_check_tlsext(s,&al) <= 0) - { - ssl3_send_alert(s,SSL3_AL_FATAL,al); /* XXX does this *have* to be fatal? */ + int al,warn; + warn = ssl_check_tlsext(s,&al); + if (warn == 0) + ssl3_send_alert(s,SSL3_AL_WARNING,al); + else if (warn < 0) { + ssl3_send_alert(s,SSL3_AL_FATAL,al); SSLerr(SSL_F_SSL3_ACCEPT,SSL_R_CLIENTHELLO_TLS_EXT); ret = -1; goto end; - } + } } #endif s->new_session = 2; diff --git a/ssl/ssl.h b/ssl/ssl.h index 30c11d7bb6..9370da1625 100644 --- a/ssl/ssl.h +++ b/ssl/ssl.h @@ -994,6 +994,9 @@ struct ssl_st 2 : don't call servername callback, no ack in server hello */ SSL_CTX * initial_ctx; /* initial ctx, used to store sessions */ +#define session_ctx initial_ctx +#else +#define session_ctx ctx #endif }; @@ -1114,7 +1117,7 @@ size_t SSL_get_peer_finished(const SSL *s, void *buf, size_t count); PEM_ASN1_write_bio_of(SSL_SESSION,i2d_SSL_SESSION,PEM_STRING_SSL_SESSION,bp,x,NULL,NULL,0,NULL,NULL) #endif -#define SSL_AD_REASON_OFFSET 1000 /* offset to get SSL_R_... value from SSL_AD_... / +#define SSL_AD_REASON_OFFSET 1000 /* offset to get SSL_R_... value from SSL_AD_... */ /* These alert types are for SSLv3 and TLSv1 */ #define SSL_AD_CLOSE_NOTIFY SSL3_AD_CLOSE_NOTIFY @@ -1206,12 +1209,11 @@ size_t SSL_get_peer_finished(const SSL *s, void *buf, size_t count); #define SSL_CTRL_SET_MAX_SEND_FRAGMENT 52 -/* see tls.h for macros based on these */ +/* see tls1.h for macros based on these */ #ifndef OPENSSL_NO_TLSEXT #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_SET_TLSEXT_SERVERNAME_DONE 56 #endif #define SSL_session_reused(ssl) \ diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index 42aa874542..7eec7f69d0 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -1332,15 +1332,15 @@ const char *SSL_get_servername(const SSL *s, const int type) { if (type != TLSEXT_NAMETYPE_host_name) return NULL; - /* XXX cf. SSL_CTRL_GET_TLSEXT_HOSTNAME case in ssl3_ctrl (s3_lib.c) */ - return s->session /*&&s->session->tlsext_hostname*/ ? + + return s->session && !s->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) + if (s->session && (!s->tlsext_hostname ? s->session->tlsext_hostname : s->tlsext_hostname)) return TLSEXT_NAMETYPE_host_name; return -1; } @@ -1930,14 +1930,14 @@ void ssl_update_cache(SSL *s,int mode) * and it would be rather hard to do anyway :-) */ if (s->session->session_id_length == 0) return; - i=s->ctx->session_cache_mode; + i=s->session_ctx->session_cache_mode; if ((i & mode) && (!s->hit) && ((i & SSL_SESS_CACHE_NO_INTERNAL_STORE) - || SSL_CTX_add_session(s->ctx,s->session)) - && (s->ctx->new_session_cb != NULL)) + || SSL_CTX_add_session(s->session_ctx,s->session)) + && (s->session_ctx->new_session_cb != NULL)) { CRYPTO_add(&s->session->references,1,CRYPTO_LOCK_SSL_SESSION); - if (!s->ctx->new_session_cb(s,s->session)) + if (!s->session_ctx->new_session_cb(s,s->session)) SSL_SESSION_free(s->session); } @@ -1946,10 +1946,10 @@ void ssl_update_cache(SSL *s,int mode) ((i & mode) == mode)) { if ( (((mode & SSL_SESS_CACHE_CLIENT) - ?s->ctx->stats.sess_connect_good - :s->ctx->stats.sess_accept_good) & 0xff) == 0xff) + ?s->session_ctx->stats.sess_connect_good + :s->session_ctx->stats.sess_accept_good) & 0xff) == 0xff) { - SSL_CTX_flush_sessions(s->ctx,(unsigned long)time(NULL)); + SSL_CTX_flush_sessions(s->session_ctx,(unsigned long)time(NULL)); } } } @@ -2452,6 +2452,10 @@ SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl) SSL_CTX *SSL_set_SSL_CTX(SSL *ssl, SSL_CTX* ctx) { + if (ssl->ctx == ctx) + return ssl->ctx; + if (ctx == NULL) + ctx = ssl->initial_ctx; if (ssl->cert != NULL) ssl_cert_free(ssl->cert); ssl->cert = ssl_cert_dup(ctx->cert); diff --git a/ssl/ssl_sess.c b/ssl/ssl_sess.c index 5dfc4c81bc..ffacf94cc7 100644 --- a/ssl/ssl_sess.c +++ b/ssl/ssl_sess.c @@ -114,12 +114,6 @@ #include #include "ssl_locl.h" -#ifndef OPENSSL_NO_TLSEXT -#define session_ctx initial_ctx -#else -#define session_ctx ctx -#endif - static void SSL_SESSION_list_remove(SSL_CTX *ctx, SSL_SESSION *s); static void SSL_SESSION_list_add(SSL_CTX *ctx,SSL_SESSION *s); static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *c, int lck); @@ -242,7 +236,7 @@ int ssl_get_new_session(SSL *s, int session) if (s->session_ctx->session_timeout == 0) ss->timeout=SSL_get_default_timeout(s); else - ss->timeout=s->ctx->session_timeout; + ss->timeout=s->session_ctx->session_timeout; if (s->session != NULL) { @@ -319,6 +313,16 @@ int ssl_get_new_session(SSL *s, int session) SSL_SESSION_free(ss); return(0); } +#ifndef OPENSSL_NO_TLSEXT + if (s->tlsext_hostname) { + ss->tlsext_hostname = BUF_strdup(s->tlsext_hostname); + if (ss->tlsext_hostname == NULL) { + SSLerr(SSL_F_SSL_GET_NEW_SESSION, ERR_R_INTERNAL_ERROR); + SSL_SESSION_free(ss); + return 0; + } + } +#endif } else { diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index b248dab361..cea8b8e851 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -193,15 +193,11 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha { 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; + ret+=2; if (ret>=limit) return NULL; /* this really never occurs, but ... */ - if (s->session->tlsext_hostname != NULL) + if (!s->hit && s->servername_done == 1 && s->session->tlsext_hostname != NULL) { if (limit - p - 4 < 0) return NULL; @@ -222,6 +218,10 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in unsigned short size; unsigned short len; unsigned char *data = *p; +#if 0 + fprintf(stderr,"ssl_parse_clienthello_tlsext %s\n",s->session->tlsext_hostname?s->session->tlsext_hostname:"NULL"); +#endif + s->servername_done = 0; if (data >= (d+n-2)) return 1; @@ -238,6 +238,29 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in if (data+size > (d+n)) return 1; +/* The servername extension is treated as follows: + + - Only the hostname type is supported with a maximum length of 255. + - The servername is rejected if too long or if it contains zeros, + in which case an fatal alert is generated. + - The servername field is maintained together with the session cache. + - When a session is resumed, the servername call back invoked in order + to allow the application to position itself to the right context. + - The servername is acknowledged if it is new for a session or when + it is identical to a previously used for the same session. + Applications can control the behaviour. They can at any time + set a 'desirable' servername for a new SSL object. This can be the + case for example with HTTPS when a Host: header field is received and + a renegotiation is requested. In this case, a possible servername + presented in the new client hello is only acknowledged if it matches + the value of the Host: field. + - Applications must use SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION + if they provide for changing an explicit servername context for the session, + i.e. when the session has been established with a servername extension. + - On session reconnect, the servername extension may be absent. + +*/ + if (type == TLSEXT_TYPE_server_name) { unsigned char *sdata = data; @@ -259,16 +282,29 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in case TLSEXT_NAMETYPE_host_name: if (s->session->tlsext_hostname == NULL) { - if (len > 255 || + if (len > TLSEXT_MAXLEN_host_name || ((s->session->tlsext_hostname = OPENSSL_malloc(len+1)) == NULL)) { *al = TLS1_AD_UNRECOGNIZED_NAME; return 0; } - memcpy(s->session->tlsext_hostname, sdata, len); - s->session->tlsext_hostname[len]='\0'; + s->session->tlsext_hostname[len]='\0'; + if (strlen(s->session->tlsext_hostname) != len) { + OPENSSL_free(s->session->tlsext_hostname); + *al = TLS1_AD_UNRECOGNIZED_NAME; + return 0; } + s->servername_done = 1; + +#if 0 + fprintf(stderr,"ssl_parse_clienthello_tlsext s->session->tlsext_hostname %s\n",s->session->tlsext_hostname); +#endif + } + else + s->servername_done = strlen(s->session->tlsext_hostname) == len + && strncmp(s->session->tlsext_hostname,sdata, len) == 0; + break; default: @@ -356,14 +392,18 @@ int ssl_check_tlsext(SSL *s,int *al) int ret; *al = SSL_AD_UNRECOGNIZED_NAME; - if (s->servername_done == 0 && (s->ctx != NULL && s->ctx->tlsext_servername_callback != 0)) + if (s->ctx != NULL && s->ctx->tlsext_servername_callback != 0) { ret = s->ctx->tlsext_servername_callback(s, al, s->ctx->tlsext_servername_arg); if (ret <= 0) return ret; } - if (s->servername_done == 1) - s->servername_done = 2; + else if (s->initial_ctx != NULL && s->initial_ctx->tlsext_servername_callback != 0) + { + ret = s->initial_ctx->tlsext_servername_callback(s, al, s->initial_ctx->tlsext_servername_arg); + if (ret <= 0) + return ret; + } return 1; } diff --git a/ssl/tls1.h b/ssl/tls1.h index 26a7ae52d6..8e56379963 100644 --- a/ssl/tls1.h +++ b/ssl/tls1.h @@ -170,10 +170,12 @@ extern "C" { #ifndef OPENSSL_NO_TLSEXT +#define TLSEXT_MAXLEN_host_name 255 + const char *SSL_get_servername(const SSL *s, const int type) ; int SSL_get_servername_type(const SSL *s) ; -#define SSL_set_tlsext_hostname(s,name) \ +#define SSL_set_tlsext_host_name(s,name) \ SSL_ctrl(s,SSL_CTRL_SET_TLSEXT_HOSTNAME,TLSEXT_NAMETYPE_host_name,(char *)name) #define SSL_CTX_set_tlsext_servername_callback(ctx, cb) \ -- cgit v1.2.3