From 836f996010d6a5f38d9a13279c37e84a42819966 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Lutz=20J=C3=A4nicke?= Date: Fri, 9 Feb 2001 19:56:31 +0000 Subject: [PATCH] New Option SSL_OP_CIPHER_SERVER_PREFERENCE allows TLS/SSLv3 server to override the clients choice; in SSLv2 the client uses the server's preferences. --- CHANGES | 6 ++++++ apps/s_client.c | 3 +++ apps/s_server.c | 3 +++ ssl/s2_clnt.c | 32 ++++++++++++++++++++++++-------- ssl/s2_srvr.c | 29 +++++++++++++++++++++++------ ssl/s3_lib.c | 46 ++++++++++++++++++++++++++++++++++++---------- ssl/s3_srvr.c | 4 ++-- ssl/ssl.h | 3 +++ ssl/ssl_locl.h | 4 ++-- 9 files changed, 102 insertions(+), 28 deletions(-) diff --git a/CHANGES b/CHANGES index 3efa8005e9..227de87b1e 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,12 @@ Changes between 0.9.6 and 0.9.7 [xx XXX 2000] + *) New option SSL_OP_CIPHER_SERVER_PREFERENCE allows the server to override + the clients preferred ciphersuites and rather use its own preferences. + Should help to work around M$ SGC (Server Gated Cryptography) bug in + Internet Explorer by ensuring unchanged hash method during stepup. + [Lutz Jaenicke] + *) Make mkdef.pl recognise all DECLARE_ASN1 macros, change rijndael to aes and add a new 'exist' option to print out symbols that don't appear to exist. diff --git a/apps/s_client.c b/apps/s_client.c index 1a94cdec65..2e71b42890 100644 --- a/apps/s_client.c +++ b/apps/s_client.c @@ -151,6 +151,7 @@ static void sc_usage(void) BIO_printf(bio_err," -tls1 - just use TLSv1\n"); BIO_printf(bio_err," -no_tls1/-no_ssl3/-no_ssl2 - turn off that protocol\n"); BIO_printf(bio_err," -bugs - Switch on all SSL implementation bug workarounds\n"); + BIO_printf(bio_err," -serverpref - Use server's cipher preferences (only SSLv2)\n"); BIO_printf(bio_err," -cipher - preferred cipher to use, use the 'openssl ciphers'\n"); BIO_printf(bio_err," command to see what is available\n"); BIO_printf(bio_err," -engine id - Initialise and use the specified engine\n"); @@ -311,6 +312,8 @@ int MAIN(int argc, char **argv) off|=SSL_OP_NO_SSLv3; else if (strcmp(*argv,"-no_ssl2") == 0) off|=SSL_OP_NO_SSLv2; + else if (strcmp(*argv,"-serverpref") == 0) + off|=SSL_OP_CIPHER_SERVER_PREFERENCE; else if (strcmp(*argv,"-cipher") == 0) { if (--argc < 1) goto bad; diff --git a/apps/s_server.c b/apps/s_server.c index 383e2304d7..1a9ce281c3 100644 --- a/apps/s_server.c +++ b/apps/s_server.c @@ -231,6 +231,7 @@ static void sv_usage(void) BIO_printf(bio_err," -CAfile arg - PEM format file of CA's\n"); BIO_printf(bio_err," -nocert - Don't use any certificates (Anon-DH)\n"); BIO_printf(bio_err," -cipher arg - play with 'openssl ciphers' to see what goes here\n"); + BIO_printf(bio_err," -serverpref - Use server's cipher preferences\n"); BIO_printf(bio_err," -quiet - No server output\n"); BIO_printf(bio_err," -no_tmp_rsa - Do not generate a tmp RSA key\n"); BIO_printf(bio_err," -ssl2 - Just talk SSLv2\n"); @@ -508,6 +509,8 @@ int MAIN(int argc, char *argv[]) if (--argc < 1) goto bad; CApath= *(++argv); } + else if (strcmp(*argv,"-serverpref") == 0) + { off|=SSL_OP_CIPHER_SERVER_PREFERENCE; } else if (strcmp(*argv,"-cipher") == 0) { if (--argc < 1) goto bad; diff --git a/ssl/s2_clnt.c b/ssl/s2_clnt.c index 28d6d65296..3e06286375 100644 --- a/ssl/s2_clnt.c +++ b/ssl/s2_clnt.c @@ -287,7 +287,7 @@ static int get_server_hello(SSL *s) unsigned char *buf; unsigned char *p; int i,j; - STACK_OF(SSL_CIPHER) *sk=NULL,*cl; + STACK_OF(SSL_CIPHER) *sk=NULL,*cl, *prio, *allow; buf=(unsigned char *)s->init_buf->data; p=buf; @@ -414,27 +414,43 @@ static int get_server_hello(SSL *s) sk_SSL_CIPHER_set_cmp_func(sk,ssl_cipher_ptr_id_cmp); /* get the array of ciphers we will accept */ - cl=ssl_get_ciphers_by_id(s); - + cl=SSL_get_ciphers(s); + sk_SSL_CIPHER_set_cmp_func(cl,ssl_cipher_ptr_id_cmp); + + /* + * If server preference flag set, choose the first + * (highest priority) cipher the server sends, otherwise + * client preference has priority. + */ + if (s->options & SSL_OP_CIPHER_SERVER_PREFERENCE) + { + prio = sk; + allow = cl; + } + else + { + prio = cl; + allow = sk; + } /* In theory we could have ciphers sent back that we * don't want to use but that does not matter since we * will check against the list we originally sent and * for performance reasons we should not bother to match * the two lists up just to check. */ - for (i=0; i= 0) + if (sk_SSL_CIPHER_find(allow, + sk_SSL_CIPHER_value(prio,i)) >= 0) break; } - if (i >= sk_SSL_CIPHER_num(cl)) + if (i >= sk_SSL_CIPHER_num(prio)) { ssl2_return_error(s,SSL2_PE_NO_CIPHER); SSLerr(SSL_F_GET_SERVER_HELLO,SSL_R_NO_CIPHER_MATCH); return(-1); } - s->session->cipher=sk_SSL_CIPHER_value(cl,i); + s->session->cipher=sk_SSL_CIPHER_value(prio,i); if (s->session->peer != NULL) /* can't happen*/ diff --git a/ssl/s2_srvr.c b/ssl/s2_srvr.c index 1ed02540ae..974d6e6de7 100644 --- a/ssl/s2_srvr.c +++ b/ssl/s2_srvr.c @@ -450,6 +450,7 @@ static int get_client_hello(SSL *s) unsigned char *p; STACK_OF(SSL_CIPHER) *cs; /* a stack of SSL_CIPHERS */ STACK_OF(SSL_CIPHER) *cl; /* the ones we want to use */ + STACK_OF(SSL_CIPHER) *prio, *allow; int z; /* This is a bit of a hack to check for the correct packet @@ -555,21 +556,37 @@ static int get_client_hello(SSL *s) &s->session->ciphers); if (cs == NULL) goto mem_err; - cl=ssl_get_ciphers_by_id(s); + cl=SSL_get_ciphers(s); - for (z=0; zoptions & SSL_OP_CIPHER_SERVER_PREFERENCE) + { + prio=sk_SSL_CIPHER_dup(cl); + if (prio == NULL) goto mem_err; + allow = cs; + } + else + { + prio = cs; + allow = cl; + } + for (z=0; zoptions & SSL_OP_CIPHER_SERVER_PREFERENCE) + { + sk_SSL_CIPHER_free(s->session->ciphers); + s->session->ciphers = prio; + } /* s->session->ciphers should now have a list of * ciphers that are on both the client and server. * This list is ordered by the order the client sent - * the ciphers. + * the ciphers or in the order of the server's preference + * if SSL_OP_CIPHER_SERVER_PREFERENCE was set. */ } p+=s->s2->tmp.cipher_spec_length; diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c index 700a4ddbbf..76c499e67a 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c @@ -1364,10 +1364,11 @@ int ssl3_put_cipher_by_char(const SSL_CIPHER *c, unsigned char *p) return(2); } -SSL_CIPHER *ssl3_choose_cipher(SSL *s, STACK_OF(SSL_CIPHER) *have, - STACK_OF(SSL_CIPHER) *pref) +SSL_CIPHER *ssl3_choose_cipher(SSL *s, STACK_OF(SSL_CIPHER) *clnt, + STACK_OF(SSL_CIPHER) *srvr) { SSL_CIPHER *c,*ret=NULL; + STACK_OF(SSL_CIPHER) *prio, *allow; int i,j,ok; CERT *cert; unsigned long alg,mask,emask; @@ -1375,20 +1376,45 @@ SSL_CIPHER *ssl3_choose_cipher(SSL *s, STACK_OF(SSL_CIPHER) *have, /* Let's see which ciphers we can support */ cert=s->cert; - sk_SSL_CIPHER_set_cmp_func(pref,ssl_cipher_ptr_id_cmp); +#if 0 + /* Do not set the compare functions, because this may lead to a + * reordering by "id". We want to keep the original ordering. + * We may pay a price in performance during sk_SSL_CIPHER_find(), + * but would have to pay with the price of sk_SSL_CIPHER_dup(). + */ + sk_SSL_CIPHER_set_cmp_func(srvr, ssl_cipher_ptr_id_cmp); + sk_SSL_CIPHER_set_cmp_func(clnt, ssl_cipher_ptr_id_cmp); +#endif #ifdef CIPHER_DEBUG - printf("Have %d from %p:\n", sk_SSL_CIPHER_num(pref), pref); - for(i=0 ; i < sk_SSL_CIPHER_num(pref) ; ++i) + printf("Server has %d from %p:\n", sk_SSL_CIPHER_num(srvr), srvr); + for(i=0 ; i < sk_SSL_CIPHER_num(srvr) ; ++i) + { + c=sk_SSL_CIPHER_value(srvr,i); + printf("%p:%s\n",c,c->name); + } + printf("Client sent %d from %p:\n", sk_SSL_CIPHER_num(clnt), clnt); + for(i=0 ; i < sk_SSL_CIPHER_num(clnt) ; ++i) { - c=sk_SSL_CIPHER_value(pref,i); + c=sk_SSL_CIPHER_value(clnt,i); printf("%p:%s\n",c,c->name); } #endif - for (i=0; ioptions & SSL_OP_CIPHER_SERVER_PREFERENCE) + { + prio = srvr; + allow = clnt; + } + else + { + prio = clnt; + allow = srvr; + } + + for (i=0; imask; @@ -1418,10 +1444,10 @@ SSL_CIPHER *ssl3_choose_cipher(SSL *s, STACK_OF(SSL_CIPHER) *have, if (!ok) continue; - j=sk_SSL_CIPHER_find(pref,c); + j=sk_SSL_CIPHER_find(allow,c); if (j >= 0) { - ret=sk_SSL_CIPHER_value(pref,j); + ret=sk_SSL_CIPHER_value(allow,j); break; } } diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c index 54e0a03057..4841109c4b 100644 --- a/ssl/s3_srvr.c +++ b/ssl/s3_srvr.c @@ -755,7 +755,7 @@ static int ssl3_get_client_hello(SSL *s) } } - /* Given s->session->ciphers and ssl_get_ciphers_by_id(s), we must + /* Given s->session->ciphers and SSL_get_ciphers, we must * pick a cipher */ if (!s->hit) @@ -772,7 +772,7 @@ static int ssl3_get_client_hello(SSL *s) } ciphers=NULL; c=ssl3_choose_cipher(s,s->session->ciphers, - ssl_get_ciphers_by_id(s)); + SSL_get_ciphers(s)); if (c == NULL) { diff --git a/ssl/ssl.h b/ssl/ssl.h index 6b7487724f..9fa02fda0b 100644 --- a/ssl/ssl.h +++ b/ssl/ssl.h @@ -337,6 +337,9 @@ typedef struct ssl_session_st #define SSL_OP_SINGLE_DH_USE 0x00100000L /* Set to also use the tmp_rsa key when doing RSA operations. */ #define SSL_OP_EPHEMERAL_RSA 0x00200000L +/* Set on servers to choose the cipher according to the server's + * preferences */ +#define SSL_OP_CIPHER_SERVER_PREFERENCE 0x00400000L /* The next flag deliberately changes the ciphertest, this is a check * for the PKCS#1 attack */ diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index cc45cbddd0..f0ee7443aa 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -565,8 +565,8 @@ void ssl3_finish_mac(SSL *s, const unsigned char *buf, int len); int ssl3_enc(SSL *s, int send_data); int ssl3_mac(SSL *ssl, unsigned char *md, int send_data); unsigned long ssl3_output_cert_chain(SSL *s, X509 *x); -SSL_CIPHER *ssl3_choose_cipher(SSL *ssl,STACK_OF(SSL_CIPHER) *have, - STACK_OF(SSL_CIPHER) *pref); +SSL_CIPHER *ssl3_choose_cipher(SSL *ssl,STACK_OF(SSL_CIPHER) *clnt, + STACK_OF(SSL_CIPHER) *srvr); int ssl3_setup_buffers(SSL *s); int ssl3_new(SSL *s); void ssl3_free(SSL *s); -- 2.25.1