New Option SSL_OP_CIPHER_SERVER_PREFERENCE allows TLS/SSLv3 server to override
authorLutz Jänicke <jaenicke@openssl.org>
Fri, 9 Feb 2001 19:56:31 +0000 (19:56 +0000)
committerLutz Jänicke <jaenicke@openssl.org>
Fri, 9 Feb 2001 19:56:31 +0000 (19:56 +0000)
the clients choice; in SSLv2 the client uses the server's preferences.

CHANGES
apps/s_client.c
apps/s_server.c
ssl/s2_clnt.c
ssl/s2_srvr.c
ssl/s3_lib.c
ssl/s3_srvr.c
ssl/ssl.h
ssl/ssl_locl.h

diff --git a/CHANGES b/CHANGES
index 3efa8005e991fa18a3fe86905e8edd127b9d5455..227de87b1eb8d5c64c1438db67f2c2d7b1da0b61 100644 (file)
--- 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.
index 1a94cdec65008671add63a45b5e354ed2368619e..2e71b428906e22e8c5006fd740a086445b072f8b 100644 (file)
@@ -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;
index 383e2304d7ac7ff28060a3ec6f39c4db3725c81c..1a9ce281c3bf63ef07d6ce42ade15b206164aad6 100644 (file)
@@ -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;
index 28d6d652961684101c317392e42a75a8d29413f0..3e062863755e971c0c10b3584c77c70746915a46 100644 (file)
@@ -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<sk_SSL_CIPHER_num(cl); i++)
+               for (i=0; i<sk_SSL_CIPHER_num(prio); i++)
                        {
-                       if (sk_SSL_CIPHER_find(sk,
-                                              sk_SSL_CIPHER_value(cl,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*/
index 1ed02540aec4d7bdc6521c501801370fbf6a8bd3..974d6e6de742df31064595026bd89a1a56498023 100644 (file)
@@ -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; z<sk_SSL_CIPHER_num(cs); z++)
+               if (s->options & 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; z<sk_SSL_CIPHER_num(prio); z++)
                        {
-                       if (sk_SSL_CIPHER_find(cl,sk_SSL_CIPHER_value(cs,z)) < 0)
+                       if (sk_SSL_CIPHER_find(allow,sk_SSL_CIPHER_value(prio,z)) < 0)
                                {
-                               sk_SSL_CIPHER_delete(cs,z);
+                               sk_SSL_CIPHER_delete(prio,z);
                                z--;
                                }
                        }
-
+               if (s->options & 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;
index 700a4ddbbf473e489d11c889db812d1f584e0108..76c499e67a08ab612ffbbded9c7517467d8c5f02 100644 (file)
@@ -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; i<sk_SSL_CIPHER_num(have); i++)
+       if (s->options & SSL_OP_CIPHER_SERVER_PREFERENCE)
+           {
+           prio = srvr;
+           allow = clnt;
+           }
+       else
+           {
+           prio = clnt;
+           allow = srvr;
+           }
+
+       for (i=0; i<sk_SSL_CIPHER_num(prio); i++)
                {
-               c=sk_SSL_CIPHER_value(have,i);
+               c=sk_SSL_CIPHER_value(prio,i);
 
                ssl_set_cert_masks(cert,c);
                mask=cert->mask;
@@ -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;
                        }
                }
index 54e0a03057fcf64c11f25163db3f944101ed150a..4841109c4bf5c7a5fb24bed67b03b3a3bbab8578 100644 (file)
@@ -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)
                        {
index 6b7487724fe2e7b70fdea4b5324bca2733d460cd..9fa02fda0b1f09f6fdf220ba68ba1e51df1d6b88 100644 (file)
--- 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 */
index cc45cbddd003e8c4839137b6665147ffb6a0c6b7..f0ee7443aa2bb4e8aae6526ee83a122ce3a8459e 100644 (file)
@@ -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);