From 241520e66d3ece1054beae93ff96978d0299cae4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bodo=20M=C3=B6ller?= <bodo@openssl.org>
Date: Wed, 11 Jan 2006 06:10:40 +0000
Subject: [PATCH] More TLS extension related changes.

Submitted by: Peter Sylvester
---
 CHANGES         | 15 ++++++++-------
 apps/s_cb.c     | 17 ++++++++++++++++-
 apps/s_client.c |  2 +-
 apps/s_server.c | 19 ++++++++++---------
 ssl/s23_clnt.c  |  4 ++++
 ssl/s3_clnt.c   | 11 +++++++++--
 ssl/s3_srvr.c   | 17 +++++++++++------
 ssl/t1_lib.c    | 21 ++++++++-------------
 ssl/tls1.h      |  9 ++++++---
 9 files changed, 73 insertions(+), 42 deletions(-)

diff --git a/CHANGES b/CHANGES
index f4014aeef..c3a784893 100644
--- a/CHANGES
+++ b/CHANGES
@@ -26,14 +26,15 @@
                                       - SSL_CTX_set_tlsext_servername_arg()
          SSL_CTRL_SET_TLSEXT_HOSTNAME           - SSL_set_tlsext_hostname()
 
-     openssl s_client has a new '-servername' option.
+     openssl s_client has a new '-servername ...' option.
 
-     openssl s_server has new options '-servername', '-cert2', and '-key2'
-     (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. 
+     openssl s_server has new options '-servername_host ...', '-cert2 ...',
+     '-key2 ...', '-servername_fatal' (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).  If the unrecogninzed_name alert has to be sent, this by
+     default is a warning; it becomes fatal with the '-servername_fatal'
+     option.
 
      [Peter Sylvester,  Remy Allais, Christophe Renou]
 
diff --git a/apps/s_cb.c b/apps/s_cb.c
index 9a35d46ad..573f98cea 100644
--- a/apps/s_cb.c
+++ b/apps/s_cb.c
@@ -56,7 +56,7 @@
  * [including the GNU Public Licence.]
  */
 /* ====================================================================
- * Copyright (c) 1998-2001 The OpenSSL Project.  All rights reserved.
+ * Copyright (c) 1998-2006 The OpenSSL Project.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -504,6 +504,21 @@ void MS_CALLBACK msg_cb(int write_p, int version, int content_type, const void *
 				case 100:
 					str_details2 = " no_renegotiation";
 					break;
+				case 110:
+					str_details2 = " unsupported_extension";
+					break;
+				case 111:
+					str_details2 = " certificate_unobtainable";
+					break;
+				case 112:
+					str_details2 = " unrecognized_name";
+					break;
+				case 113:
+					str_details2 = " bad_certificate_status_response";
+					break;
+				case 114:
+					str_details2 = " bad_certificate_hash_value";
+					break;
 					}
 				}
 			}
diff --git a/apps/s_client.c b/apps/s_client.c
index 47cd9d93d..50e27a073 100644
--- a/apps/s_client.c
+++ b/apps/s_client.c
@@ -245,7 +245,7 @@ static int MS_CALLBACK ssl_servername_cb(SSL *s, int *ad, void *arg)
 	else 
 		BIO_printf(bio_err,"Can't use SSL_get_servername\n");
 	
-	return 1;
+	return SSL_TLSEXT_ERR_OK;
 	}
 #endif
 
diff --git a/apps/s_server.c b/apps/s_server.c
index e07f3dd20..583bfccd5 100644
--- a/apps/s_server.c
+++ b/apps/s_server.c
@@ -367,7 +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," -servername_fatal - on mismatch send fatal alert (default warning 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");
@@ -534,7 +534,7 @@ static int ebcdic_puts(BIO *bp, const char *str)
 typedef struct tlsextctx_st {
    char * servername;
    BIO * biodebug;
-   int servername_warn;
+   int extension_error;
 } tlsextctx;
 
 
@@ -546,18 +546,19 @@ 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)
-		return 1;
+		return SSL_TLSEXT_ERR_NOACK;
 	
 	if (servername)
 		{
     		if (strcmp(servername,p->servername)) 
-			return  p->servername_warn;
-		if (ctx2) {
+			return p->extension_error;
+		if (ctx2)
+			{
 			BIO_printf(p->biodebug,"Swiching server context.\n");
 			SSL_set_SSL_CTX(s,ctx2);
 			}     
 		}
-	return 1;
+	return SSL_TLSEXT_ERR_OK;
 }
 #endif
 
@@ -597,7 +598,7 @@ int MAIN(int argc, char *argv[])
 #endif
 
 #ifndef OPENSSL_NO_TLSEXT
-        tlsextctx tlsextcbp = {NULL, NULL, -1};
+        tlsextctx tlsextcbp = {NULL, NULL, SSL_TLSEXT_ERR_ALERT_WARNING};
 #endif
 #if !defined(OPENSSL_NO_SSL2) && !defined(OPENSSL_NO_SSL3)
 	meth=SSLv23_server_method();
@@ -846,8 +847,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,"-servername_fatal") == 0)
+			{ tlsextcbp.extension_error = SSL_TLSEXT_ERR_ALERT_FATAL; }
 		else if	(strcmp(*argv,"-cert2") == 0)
 			{
 			if (--argc < 1) goto bad;
diff --git a/ssl/s23_clnt.c b/ssl/s23_clnt.c
index 66ecbc7ee..ab291928a 100644
--- a/ssl/s23_clnt.c
+++ b/ssl/s23_clnt.c
@@ -565,6 +565,7 @@ static int ssl23_get_server_hello(SSL *s)
 		 (p[5] == SSL3_MT_SERVER_HELLO))
 		{
 		/* we have sslv3 or tls1 */
+	have_sslv3_or_tls1:	
 
 		if (!ssl_init_wbio_buffer(s,1)) goto err;
 
@@ -623,6 +624,9 @@ static int ssl23_get_server_hello(SSL *s)
 			cb(s,SSL_CB_READ_ALERT,j);
 			}
 
+		if (p[5] == SSL3_AL_WARNING)
+			goto have_sslv3_or_tls1;
+
 		s->rwstate=SSL_NOTHING;
 		SSLerr(SSL_F_SSL23_GET_SERVER_HELLO,SSL_AD_REASON_OFFSET+p[6]);
 		goto err;
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c
index 578285230..d50f588b9 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -258,12 +258,19 @@ int ssl3_connect(SSL *s)
 #ifndef OPENSSL_NO_TLSEXT
 			{
 				int al;
-				if (ssl_check_tlsext(s,&al) <= 0)
+				switch (ssl_check_tlsext(s,&al))
 					{
-					ssl3_send_alert(s,SSL3_AL_FATAL,al); /* XXX does this *have* to be fatal? */
+				case SSL_TLSEXT_ERR_ALERT_FATAL:
+					ssl3_send_alert(s,SSL3_AL_FATAL,al);
 					SSLerr(SSL_F_SSL3_CONNECT,SSL_R_SERVERHELLO_TLS_EXT);
 					ret = -1;
 					goto end;
+
+				case SSL_TLSEXT_ERR_ALERT_WARNING:
+					ssl3_send_alert(s,SSL3_AL_WARNING,al); 
+					
+				default:
+					;
 					}
 			}
 #endif
diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c
index c83505c0a..28d425a46 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -283,16 +283,21 @@ int ssl3_accept(SSL *s)
 			if (ret <= 0) goto end;
 #ifndef OPENSSL_NO_TLSEXT
 			{
-				int al,warn;
-				warn = ssl_check_tlsext(s,&al);
-				if (warn == 0)
-					ssl3_send_alert(s,SSL3_AL_WARNING,al); 
-				else if (warn < 0) {
+				int al;
+				switch (ssl_check_tlsext(s,&al))
+					{
+				case SSL_TLSEXT_ERR_ALERT_FATAL:
 					ssl3_send_alert(s,SSL3_AL_FATAL,al); 
 					SSLerr(SSL_F_SSL3_ACCEPT,SSL_R_CLIENTHELLO_TLS_EXT);
 					ret = -1;
 					goto end;
-				}
+
+				case SSL_TLSEXT_ERR_ALERT_WARNING:
+					ssl3_send_alert(s,SSL3_AL_WARNING,al); 
+					
+				default:
+					break;
+					}
 			}
 #endif
 			s->new_session = 2;
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index cea8b8e85..1aa5e90bb 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -389,22 +389,17 @@ int ssl_parse_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in
 
 int ssl_check_tlsext(SSL *s,int *al)
 	{
-	int ret;
+	int ret=SSL_TLSEXT_ERR_NOACK;
 
 	*al = SSL_AD_UNRECOGNIZED_NAME;
-	if (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;
-		}
-	else if (s->initial_ctx != NULL && s->initial_ctx->tlsext_servername_callback != 0)
-		{
+	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;
+
+	if (ret == SSL_TLSEXT_ERR_NOACK) 
+		s->servername_done=0;
+	return ret;
 	}
 #endif
diff --git a/ssl/tls1.h b/ssl/tls1.h
index 8e5637996..0a9c1ea50 100644
--- a/ssl/tls1.h
+++ b/ssl/tls1.h
@@ -180,12 +180,15 @@ SSL_ctrl(s,SSL_CTRL_SET_TLSEXT_HOSTNAME,TLSEXT_NAMETYPE_host_name,(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_TLSEXT_ERR_OK 0    
+#define SSL_TLSEXT_ERR_ALERT_WARNING 1  
+#define SSL_TLSEXT_ERR_ALERT_FATAL 2 
+#define SSL_TLSEXT_ERR_NOACK 3
+
 #define SSL_CTX_set_tlsext_servername_arg(ctx, arg) \
 SSL_CTX_ctrl(ctx,SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG,0, (void *)arg)
 
-#define SSL_set_tlsext_servername_done(s,t) \
-SSL_ctrl(s,SSL_CTRL_SET_TLSEXT_SERVERNAME_DONE,t, NULL)
-  
 #endif