[ 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