[ Also submitted privately to Lutz Jaenicke and Bodo Moeller, but Lutz
  suggested that I should get this into "RT" ]

I have been fighting a 3-party (to blame) problem with cipher selection
for Postfix 2.3, SunOS 5.10 and the OpenSSL 0.9.7 libraries they bundle:

    - My fault (Postfix 2.3): Enable non DEFAULT ciphers, specifically
    anonymous ciphers for SMTP with opportunistic TLS where the client
    does not bother to check certificates. e.g. "ALL:+RC4:@STRENGTH"

    - Sun's fault: Include AES 128 in the libcrypto they ship, but
    exclude AES 256 for some reason that likely makes sense to them
    (perhaps import/export controls on crypto in some random country).
    They partly paper over the problem with a DEFAULT cipherlist that
    excludes the AES256 members of the usual DEFAULT list, but this is
    not a complete solution.

    - OpenSSL's fault: Only test AES-128 (and with 0.9.9 CAMELLIA-128)
    when deciding whether AES (or CAMELLIA) is available, and negotiate
    these if not disabled by the cipherlist, only to find later that
    the underlying crypto algorithm is missing in action.

Now each of the three parties can point fingers, but I think we can
solve this problem.

    - For now, Postfix 2.3.8 and 2.4.0 will soon have a "cipher probe"
    list, that tests and excludes missing members of the AES and CAMELLIA
    families. The Sun and OpenSSL contributions to this are not my fault,
    but they are, as Wietse says, "our problem". This is not a long-term
    solution.

    - I have found a clean solution for this issue in ssl/ssl_ciph.c,
    by computing two "disabled cipher" masks that agree on all the
    regular ciphers, but (potentially) diverge on ciphers that have
    two EVP_CIPHER_CTX handles with possibly just one implemented by
    libcrypto.

I have tested the OpenSSL ssl/ssl_ciph.c solution, and it is much cleaner
and more general than the work-arounds in Postfix. Please consider my
patch for inclusion in OpenSSL 0.9.9 and ideally inclusion in any future
patch updates of OpenSSL 0.9.7m/0.9.8e.

Patch attached.

-- 
        Viktor.

*** openssl-SNAP-20070125/ssl/ssl_ciph.c        Wed Jan  3 15:01:16 2007
--- openssl-SNAP-20070125-new/ssl/ssl_ciph.c    Wed Jan 31 01:05:06 2007
***************
*** 478,486 ****
        *tail=curr;
        }
  
! static unsigned long ssl_cipher_get_disabled(void)
        {
        unsigned long mask = 0;
  
  #ifdef OPENSSL_NO_RSA
        mask |= SSL_aRSA|SSL_kRSA;
--- 478,488 ----
        *tail=curr;
        }
  
! static unsigned long ssl_cipher_get_disabled(long *m256Ptr)
        {
        unsigned long mask = 0;
+       unsigned long m256;
+ 
  
  #ifdef OPENSSL_NO_RSA
        mask |= SSL_aRSA|SSL_kRSA;
***************
*** 512,529 ****
        mask |= (ssl_cipher_methods[SSL_ENC_RC4_IDX ] == NULL) ? SSL_RC4 :0;
        mask |= (ssl_cipher_methods[SSL_ENC_RC2_IDX ] == NULL) ? SSL_RC2 :0;
        mask |= (ssl_cipher_methods[SSL_ENC_IDEA_IDX] == NULL) ? SSL_IDEA:0;
-       mask |= (ssl_cipher_methods[SSL_ENC_AES128_IDX] == NULL) ? SSL_AES:0;
-       mask |= (ssl_cipher_methods[SSL_ENC_CAMELLIA128_IDX] == NULL) ? 
SSL_CAMELLIA:0;
  
        mask |= (ssl_digest_methods[SSL_MD_MD5_IDX ] == NULL) ? SSL_MD5 :0;
        mask |= (ssl_digest_methods[SSL_MD_SHA1_IDX] == NULL) ? SSL_SHA1:0;
  
        return(mask);
        }
  
  static void ssl_cipher_collect_ciphers(const SSL_METHOD *ssl_method,
!               int num_of_ciphers, unsigned long mask, CIPHER_ORDER *co_list,
!               CIPHER_ORDER **head_p, CIPHER_ORDER **tail_p)
        {
        int i, co_list_num;
        SSL_CIPHER *c;
--- 514,541 ----
        mask |= (ssl_cipher_methods[SSL_ENC_RC4_IDX ] == NULL) ? SSL_RC4 :0;
        mask |= (ssl_cipher_methods[SSL_ENC_RC2_IDX ] == NULL) ? SSL_RC2 :0;
        mask |= (ssl_cipher_methods[SSL_ENC_IDEA_IDX] == NULL) ? SSL_IDEA:0;
  
        mask |= (ssl_digest_methods[SSL_MD_MD5_IDX ] == NULL) ? SSL_MD5 :0;
        mask |= (ssl_digest_methods[SSL_MD_SHA1_IDX] == NULL) ? SSL_SHA1:0;
  
+       /*
+        * So far so good, but for some ciphers mask and m256 differ
+        */
+       m256 = mask;
+ 
+       mask |= (ssl_cipher_methods[SSL_ENC_AES128_IDX] == NULL) ? SSL_AES:0;
+       m256 |= (ssl_cipher_methods[SSL_ENC_AES256_IDX] == NULL) ? SSL_AES:0;
+       mask |= (ssl_cipher_methods[SSL_ENC_CAMELLIA128_IDX] == NULL) ? 
SSL_CAMELLIA:0;
+       m256 |= (ssl_cipher_methods[SSL_ENC_CAMELLIA256_IDX] == NULL) ? 
SSL_CAMELLIA:0;
+ 
+       *m256Ptr = m256;
        return(mask);
        }
  
  static void ssl_cipher_collect_ciphers(const SSL_METHOD *ssl_method,
!               int num_of_ciphers, unsigned long mask, unsigned long m256,
!               CIPHER_ORDER *co_list, CIPHER_ORDER **head_p,
!               CIPHER_ORDER **tail_p)
        {
        int i, co_list_num;
        SSL_CIPHER *c;
***************
*** 541,547 ****
                {
                c = ssl_method->get_cipher(i);
                /* drop those that use any of that is not available */
!               if ((c != NULL) && c->valid && !(c->algorithms & mask))
                        {
                        co_list[co_list_num].cipher = c;
                        co_list[co_list_num].next = NULL;
--- 553,560 ----
                {
                c = ssl_method->get_cipher(i);
                /* drop those that use any of that is not available */
!               if ((c != NULL) && c->valid
!                   && !(c->algorithms & ((c->alg_bits == 256) ? m256 : mask)))
                        {
                        co_list[co_list_num].cipher = c;
                        co_list[co_list_num].next = NULL;
***************
*** 982,987 ****
--- 995,1001 ----
        {
        int ok, num_of_ciphers, num_of_alias_max, num_of_group_aliases;
        unsigned long disabled_mask;
+       unsigned long disabled_m256;
        STACK_OF(SSL_CIPHER) *cipherstack, *tmp_cipher_list;
        const char *rule_p;
        CIPHER_ORDER *co_list = NULL, *head = NULL, *tail = NULL, *curr;
***************
*** 997,1003 ****
         * To reduce the work to do we only want to process the compiled
         * in algorithms, so we first get the mask of disabled ciphers.
         */
!       disabled_mask = ssl_cipher_get_disabled();
  
        /*
         * Now we have to collect the available ciphers from the compiled
--- 1011,1017 ----
         * To reduce the work to do we only want to process the compiled
         * in algorithms, so we first get the mask of disabled ciphers.
         */
!       disabled_mask = ssl_cipher_get_disabled(&disabled_m256);
  
        /*
         * Now we have to collect the available ciphers from the compiled
***************
*** 1016,1022 ****
                }
  
        ssl_cipher_collect_ciphers(ssl_method, num_of_ciphers, disabled_mask,
!                                  co_list, &head, &tail);
  
        /*
         * We also need cipher aliases for selecting based on the rule_str.
--- 1030,1036 ----
                }
  
        ssl_cipher_collect_ciphers(ssl_method, num_of_ciphers, disabled_mask,
!                                  disabled_m256, co_list, &head, &tail);
  
        /*
         * We also need cipher aliases for selecting based on the rule_str.
***************
*** 1036,1043 ****
                SSLerr(SSL_F_SSL_CREATE_CIPHER_LIST,ERR_R_MALLOC_FAILURE);
                return(NULL);   /* Failure */
                }
!       ssl_cipher_collect_aliases(ca_list, num_of_group_aliases, disabled_mask,
!                                  head);
  
        /*
         * If the rule_string begins with DEFAULT, apply the default rule
--- 1050,1057 ----
                SSLerr(SSL_F_SSL_CREATE_CIPHER_LIST,ERR_R_MALLOC_FAILURE);
                return(NULL);   /* Failure */
                }
!       ssl_cipher_collect_aliases(ca_list, num_of_group_aliases,
!                                  (disabled_mask & disabled_m256), head);
  
        /*
         * If the rule_string begins with DEFAULT, apply the default rule

Reply via email to