From 858a88daf27975f67d9f63e18f95645be2886bfb Mon Sep 17 00:00:00 2001 From: Adam Langley Date: Fri, 20 Jun 2014 12:00:00 -0700 Subject: [PATCH] Equal preference cipher groups. This change implements equal-preference groups of cipher suites. This allows, for example, a server to prefer one of AES-GCM or ChaCha20 ciphers, but to allow the client to pick which one. When coupled with clients that will boost AES-GCM in their preferences when AES-NI is present, this allows us to use AES-GCM when the hardware exists and ChaCha20 otherwise. --- ssl/s3_lib.c | 74 ++++++++++++++++----- ssl/s3_srvr.c | 8 +-- ssl/ssl.h | 47 ++++++++++++- ssl/ssl_ciph.c | 170 +++++++++++++++++++++++++++++++++++++----------- ssl/ssl_error.c | 4 ++ ssl/ssl_lib.c | 90 ++++++++++++++++++++++--- ssl/ssl_locl.h | 11 +++- 7 files changed, 333 insertions(+), 71 deletions(-) diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c index dce8021c..8cc710c5 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c @@ -3737,15 +3737,40 @@ int ssl3_put_cipher_by_char(const SSL_CIPHER *c, unsigned char *p) return(2); } +struct ssl_cipher_preference_list_st* ssl_get_cipher_preferences(SSL *s) + { + if (s->cipher_list != NULL) + return(s->cipher_list); + + if (s->version >= TLS1_1_VERSION) + { + if (s->ctx != NULL && s->ctx->cipher_list_tls11 != NULL) + return s->ctx->cipher_list_tls11; + } + + if ((s->ctx != NULL) && (s->ctx->cipher_list != NULL)) + return(s->ctx->cipher_list); + + return NULL; + } + SSL_CIPHER *ssl3_choose_cipher(SSL *s, STACK_OF(SSL_CIPHER) *clnt, - STACK_OF(SSL_CIPHER) *srvr) + struct ssl_cipher_preference_list_st *server_pref) { SSL_CIPHER *c,*ret=NULL; - STACK_OF(SSL_CIPHER) *prio, *allow; + STACK_OF(SSL_CIPHER) *srvr = server_pref->ciphers, *prio, *allow; int i,ok; size_t cipher_index; CERT *cert; unsigned long alg_k,alg_a,mask_k,mask_a,emask_k,emask_a; + /* in_group_flags will either be NULL, or will point to an array of + * bytes which indicate equal-preference groups in the |prio| stack. + * See the comment about |in_group_flags| in the + * |ssl_cipher_preference_list_st| struct. */ + const unsigned char *in_group_flags; + /* group_min contains the minimal index so far found in a group, or -1 + * if no such value exists yet. */ + int group_min = -1; /* Let's see which ciphers we can support */ cert=s->cert; @@ -3778,11 +3803,13 @@ SSL_CIPHER *ssl3_choose_cipher(SSL *s, STACK_OF(SSL_CIPHER) *clnt, if (s->options & SSL_OP_CIPHER_SERVER_PREFERENCE || tls1_suiteb(s)) { prio = srvr; + in_group_flags = server_pref->in_group_flags; allow = clnt; } else { prio = clnt; + in_group_flags = NULL; allow = srvr; } @@ -3792,10 +3819,12 @@ SSL_CIPHER *ssl3_choose_cipher(SSL *s, STACK_OF(SSL_CIPHER) *clnt, { c=sk_SSL_CIPHER_value(prio,i); + ok = 1; + /* Skip TLS v1.2 only ciphersuites if not supported */ - if ((c->algorithm_ssl & SSL_TLSV1_2) && + if ((c->algorithm_ssl & SSL_TLSV1_2) && !SSL_USE_TLS1_2_CIPHERS(s)) - continue; + ok = 0; ssl_set_cert_masks(cert,c); mask_k = cert->mask_k; @@ -3813,12 +3842,12 @@ SSL_CIPHER *ssl3_choose_cipher(SSL *s, STACK_OF(SSL_CIPHER) *clnt, #ifndef OPENSSL_NO_PSK /* with PSK there must be server callback set */ if ((alg_a & SSL_aPSK) && s->psk_server_callback == NULL) - continue; + ok = 0; #endif /* OPENSSL_NO_PSK */ if (SSL_C_IS_EXPORT(c)) { - ok = (alg_k & emask_k) && (alg_a & emask_a); + ok = ok && (alg_k & emask_k) && (alg_a & emask_a); #ifdef CIPHER_DEBUG printf("%d:[%08lX:%08lX:%08lX:%08lX]%p:%s (export)\n",ok,alg_k,alg_a,emask_k,emask_a, (void *)c,c->name); @@ -3826,7 +3855,7 @@ SSL_CIPHER *ssl3_choose_cipher(SSL *s, STACK_OF(SSL_CIPHER) *clnt, } else { - ok = (alg_k & mask_k) && (alg_a & mask_a); + ok = ok && (alg_k & mask_k) && (alg_a & mask_a); #ifdef CIPHER_DEBUG printf("%d:[%08lX:%08lX:%08lX:%08lX]%p:%s\n",ok,alg_k,alg_a,mask_k,mask_a,(void *)c, c->name); @@ -3842,17 +3871,32 @@ SSL_CIPHER *ssl3_choose_cipher(SSL *s, STACK_OF(SSL_CIPHER) *clnt, #endif /* OPENSSL_NO_EC */ #endif /* OPENSSL_NO_TLSEXT */ - if (!ok) continue; - if (sk_SSL_CIPHER_find(allow, &cipher_index, c)) + if (ok && sk_SSL_CIPHER_find(allow, &cipher_index, c)) { -#if !defined(OPENSSL_NO_EC) && !defined(OPENSSL_NO_TLSEXT) - if ((alg_k & SSL_kEECDH) && (alg_a & SSL_aECDSA) && s->s3->is_probably_safari) + if (in_group_flags != NULL && in_group_flags[i] == 1) { - if (!ret) ret=sk_SSL_CIPHER_value(allow, cipher_index); - continue; + /* This element of |prio| is in a group. Update + * the minimum index found so far and continue + * looking. */ + if (group_min == -1 || group_min > cipher_index) + group_min = cipher_index; } -#endif - ret=sk_SSL_CIPHER_value(allow, cipher_index); + else + { + if (group_min != -1 && group_min < cipher_index) + cipher_index = group_min; + ret=sk_SSL_CIPHER_value(allow,cipher_index); + break; + } + } + + if (in_group_flags != NULL && + in_group_flags[i] == 0 && + group_min != -1) + { + /* We are about to leave a group, but we found a match + * in it, so that's our answer. */ + ret=sk_SSL_CIPHER_value(allow,group_min); break; } } diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c index 72538a00..1cbc26cf 100644 --- a/ssl/s3_srvr.c +++ b/ssl/s3_srvr.c @@ -1249,7 +1249,7 @@ int ssl3_get_client_hello(SSL *s) ciphers=NULL; /* check if some cipher was preferred by call back */ - pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s)); + pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, ssl_get_cipher_preferences(s)); if (pref_cipher == NULL) { al=SSL_AD_HANDSHAKE_FAILURE; @@ -1260,12 +1260,12 @@ int ssl3_get_client_hello(SSL *s) s->session->cipher=pref_cipher; if (s->cipher_list) - sk_SSL_CIPHER_free(s->cipher_list); + ssl_cipher_preference_list_free(s->cipher_list); if (s->cipher_list_by_id) sk_SSL_CIPHER_free(s->cipher_list_by_id); - s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers); + s->cipher_list = ssl_cipher_preference_list_from_ciphers(s->session->ciphers); s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers); } } @@ -1319,7 +1319,7 @@ int ssl3_get_client_hello(SSL *s) s->rwstate = SSL_NOTHING; } c=ssl3_choose_cipher(s,s->session->ciphers, - SSL_get_ciphers(s)); + ssl_get_cipher_preferences(s)); if (c == NULL) { diff --git a/ssl/ssl.h b/ssl/ssl.h index b478bbd1..4aa6d228 100644 --- a/ssl/ssl.h +++ b/ssl/ssl.h @@ -879,18 +879,55 @@ struct ssl_comp_st DECLARE_STACK_OF(SSL_COMP) DECLARE_LHASH_OF(SSL_SESSION); +/* ssl_cipher_preference_list_st contains a list of SSL_CIPHERs with + * equal-preference groups. For TLS clients, the groups are moot because the + * server picks the cipher and groups cannot be expressed on the wire. However, + * for servers, the equal-preference groups allow the client's preferences to + * be partially respected. (This only has an effect with + * SSL_OP_CIPHER_SERVER_PREFERENCE). + * + * The equal-preference groups are expressed by grouping SSL_CIPHERs together. + * All elements of a group have the same priority: no ordering is expressed + * within a group. + * + * The values in |ciphers| are in one-to-one correspondence with + * |in_group_flags|. (That is, sk_SSL_CIPHER_num(ciphers) is the number of + * bytes in |in_group_flags|.) The bytes in |in_group_flags| are either 1, to + * indicate that the corresponding SSL_CIPHER is not the last element of a + * group, or 0 to indicate that it is. + * + * For example, if |in_group_flags| contains all zeros then that indicates a + * traditional, fully-ordered preference. Every SSL_CIPHER is the last element + * of the group (i.e. they are all in a one-element group). + * + * For a more complex example, consider: + * ciphers: A B C D E F + * in_group_flags: 1 1 0 0 1 0 + * + * That would express the following, order: + * + * A E + * B -> D -> F + * C + */ +struct ssl_cipher_preference_list_st + { + STACK_OF(SSL_CIPHER) *ciphers; + unsigned char *in_group_flags; + }; + struct ssl_ctx_st { const SSL_METHOD *method; - STACK_OF(SSL_CIPHER) *cipher_list; + struct ssl_cipher_preference_list_st *cipher_list; /* same as above but sorted for lookup */ STACK_OF(SSL_CIPHER) *cipher_list_by_id; /* cipher_list_tls11 is the list of ciphers when TLS 1.1 or greater is * in use. This only applies to server connections as, for clients, the * version number is known at connect time and so the cipher list can * be set then. */ - STACK_OF(SSL_CIPHER) *cipher_list_tls11; + struct ssl_cipher_preference_list_st *cipher_list_tls11; struct x509_store_st /* X509_STORE */ *cert_store; LHASH_OF(SSL_SESSION) *sessions; @@ -1414,7 +1451,7 @@ struct ssl_st #endif /* crypto */ - STACK_OF(SSL_CIPHER) *cipher_list; + struct ssl_cipher_preference_list_st *cipher_list; STACK_OF(SSL_CIPHER) *cipher_list_by_id; /* These are the ones being used, the ones in SSL_SESSION are @@ -3016,5 +3053,9 @@ void ERR_load_SSL_strings(void); #define SSL_R_DTLS_MESSAGE_TOO_BIG 429 #define SSL_R_INVALID_SRP_USERNAME 430 #define SSL_R_TOO_MANY_EMPTY_FRAGMENTS 431 +#define SSL_R_NESTED_GROUP 432 +#define SSL_R_UNEXPECTED_GROUP_CLOSE 433 +#define SSL_R_UNEXPECTED_OPERATOR_IN_GROUP 434 +#define SSL_R_MIXED_SPECIAL_OPERATOR_WITH_GROUPS 435 #endif diff --git a/ssl/ssl_ciph.c b/ssl/ssl_ciph.c index e8f04d87..a10aa8f4 100644 --- a/ssl/ssl_ciph.c +++ b/ssl/ssl_ciph.c @@ -218,6 +218,7 @@ typedef struct cipher_order_st const SSL_CIPHER *cipher; int active; int dead; + int in_group; struct cipher_order_st *next,*prev; } CIPHER_ORDER; @@ -697,6 +698,7 @@ static void ssl_cipher_collect_ciphers(const SSL_METHOD *ssl_method, co_list[co_list_num].next = NULL; co_list[co_list_num].prev = NULL; co_list[co_list_num].active = 0; + co_list[co_list_num].in_group = 0; co_list_num++; #ifdef KSSL_DEBUG printf("\t%d: %s %lx %lx %lx\n",i,c->name,c->id,c->algorithm_mkey,c->algorithm_auth); @@ -808,7 +810,7 @@ static void ssl_cipher_apply_rule(unsigned long cipher_id, unsigned long alg_enc, unsigned long alg_mac, unsigned long alg_ssl, unsigned long algo_strength, - int rule, int strength_bits, + int rule, int strength_bits, int in_group, CIPHER_ORDER **head_p, CIPHER_ORDER **tail_p) { CIPHER_ORDER *head, *tail, *curr, *curr2, *last; @@ -816,8 +818,8 @@ static void ssl_cipher_apply_rule(unsigned long cipher_id, int reverse = 0; #ifdef CIPHER_DEBUG - printf("Applying rule %d with %08lx/%08lx/%08lx/%08lx/%08lx %08lx (%d)\n", - rule, alg_mkey, alg_auth, alg_enc, alg_mac, alg_ssl, algo_strength, strength_bits); + printf("Applying rule %d with %08lx/%08lx/%08lx/%08lx/%08lx %08lx (%d) in_group:%d\n", + rule, alg_mkey, alg_auth, alg_enc, alg_mac, alg_ssl, algo_strength, strength_bits, in_group); #endif if (rule == CIPHER_DEL) @@ -892,6 +894,7 @@ static void ssl_cipher_apply_rule(unsigned long cipher_id, { ll_append_tail(&head, curr, &tail); curr->active = 1; + curr->in_group = in_group; } } /* Move the added cipher to this location */ @@ -901,6 +904,7 @@ static void ssl_cipher_apply_rule(unsigned long cipher_id, if (curr->active) { ll_append_tail(&head, curr, &tail); + curr->in_group = 0; } } else if (rule == CIPHER_DEL) @@ -913,6 +917,7 @@ static void ssl_cipher_apply_rule(unsigned long cipher_id, * works in reverse to maintain the order) */ ll_append_head(&head, curr, &tail); curr->active = 0; + curr->in_group = 0; } } else if (rule == CIPHER_KILL) @@ -983,7 +988,7 @@ static int ssl_cipher_strength_sort(CIPHER_ORDER **head_p, */ for (i = max_strength_bits; i >= 0; i--) if (number_uses[i] > 0) - ssl_cipher_apply_rule(0, 0, 0, 0, 0, 0, 0, CIPHER_ORD, i, head_p, tail_p); + ssl_cipher_apply_rule(0, 0, 0, 0, 0, 0, 0, CIPHER_ORD, i, 0, head_p, tail_p); OPENSSL_free(number_uses); return(1); @@ -995,7 +1000,8 @@ static int ssl_cipher_process_rulestr(const char *rule_str, { unsigned long alg_mkey, alg_auth, alg_enc, alg_mac, alg_ssl, algo_strength; const char *l, *buf; - int j, multi, found, rule, retval, ok, buflen; + int j, multi, found, rule, retval, ok, buflen, in_group = 0, + has_group = 0; unsigned long cipher_id = 0; char ch; @@ -1007,14 +1013,68 @@ static int ssl_cipher_process_rulestr(const char *rule_str, if (ch == '\0') break; /* done */ - if (ch == '-') + if (in_group) + { + if (ch == ']') + { + if (!in_group) + { + OPENSSL_PUT_ERROR(SSL, ssl_cipher_process_rulestr, SSL_R_UNEXPECTED_GROUP_CLOSE); + retval = found = in_group = 0; + break; + } + if (*tail_p) + (*tail_p)->in_group = 0; + in_group = 0; + l++; + continue; + } + if (ch == '|') + { rule = CIPHER_ADD; l++; continue; } + else if (!(ch >= 'a' && ch <= 'z') && + !(ch >= 'A' && ch <= 'Z') && + !(ch >= '0' && ch <= '9')) + { + OPENSSL_PUT_ERROR(SSL, ssl_cipher_process_rulestr, SSL_R_UNEXPECTED_OPERATOR_IN_GROUP); + retval = found = in_group = 0; + break; + } + else + rule = CIPHER_ADD; + } + else if (ch == '-') { rule = CIPHER_DEL; l++; } else if (ch == '+') { rule = CIPHER_ORD; l++; } + else if (ch == '!' && has_group) + { + OPENSSL_PUT_ERROR(SSL, ssl_cipher_process_rulestr, SSL_R_MIXED_SPECIAL_OPERATOR_WITH_GROUPS); + retval = found = in_group = 0; + break; + } else if (ch == '!') { rule = CIPHER_KILL; l++; } + else if (ch == '@' && has_group) + { + OPENSSL_PUT_ERROR(SSL, ssl_cipher_process_rulestr, SSL_R_MIXED_SPECIAL_OPERATOR_WITH_GROUPS); + retval = found = in_group = 0; + break; + } else if (ch == '@') { rule = CIPHER_SPECIAL; l++; } + else if (ch == '[') + { + if (in_group) + { + OPENSSL_PUT_ERROR(SSL, ssl_cipher_process_rulestr, SSL_R_NESTED_GROUP); + retval = found = in_group = 0; + break; + } + in_group = 1; + has_group = 1; + l++; + continue; + } else { rule = CIPHER_ADD; } @@ -1057,7 +1117,7 @@ static int ssl_cipher_process_rulestr(const char *rule_str, * alphanumeric, so we call this an error. */ OPENSSL_PUT_ERROR(SSL, ssl_cipher_process_rulestr, SSL_R_INVALID_COMMAND); - retval = found = 0; + retval = found = in_group = 0; l++; break; } @@ -1224,7 +1284,7 @@ static int ssl_cipher_process_rulestr(const char *rule_str, { ssl_cipher_apply_rule(cipher_id, alg_mkey, alg_auth, alg_enc, alg_mac, alg_ssl, algo_strength, - rule, -1, head_p, tail_p); + rule, -1, in_group, head_p, tail_p); } else { @@ -1234,6 +1294,12 @@ static int ssl_cipher_process_rulestr(const char *rule_str, if (*l == '\0') break; /* done */ } + if (in_group) + { + OPENSSL_PUT_ERROR(SSL, ssl_cipher_process_rulestr, SSL_R_INVALID_COMMAND); + retval = 0; + } + return(retval); } #ifndef OPENSSL_NO_EC @@ -1297,16 +1363,19 @@ static int check_suiteb_cipher_list(const SSL_METHOD *meth, CERT *c, STACK_OF(SSL_CIPHER) *ssl_create_cipher_list(const SSL_METHOD *ssl_method, - STACK_OF(SSL_CIPHER) **cipher_list, + struct ssl_cipher_preference_list_st **cipher_list, STACK_OF(SSL_CIPHER) **cipher_list_by_id, const char *rule_str, CERT *c) { int ok, num_of_ciphers, num_of_alias_max, num_of_group_aliases; unsigned long disabled_mkey, disabled_auth, disabled_enc, disabled_mac, disabled_ssl; - STACK_OF(SSL_CIPHER) *cipherstack, *tmp_cipher_list; + STACK_OF(SSL_CIPHER) *cipherstack = NULL, *tmp_cipher_list = NULL; const char *rule_p; CIPHER_ORDER *co_list = NULL, *head = NULL, *tail = NULL, *curr; const SSL_CIPHER **ca_list = NULL; + unsigned char *in_group_flags = NULL; + unsigned int num_in_group_flags = 0; + struct ssl_cipher_preference_list_st *pref_list = NULL; /* * Return with error if nothing to do. @@ -1348,32 +1417,32 @@ STACK_OF(SSL_CIPHER) *ssl_create_cipher_list(const SSL_METHOD *ssl_method, /* Now arrange all ciphers by preference: */ /* Everything else being equal, prefer ephemeral ECDH over other key exchange mechanisms */ - ssl_cipher_apply_rule(0, SSL_kEECDH, 0, 0, 0, 0, 0, CIPHER_ADD, -1, &head, &tail); - ssl_cipher_apply_rule(0, SSL_kEECDH, 0, 0, 0, 0, 0, CIPHER_DEL, -1, &head, &tail); + ssl_cipher_apply_rule(0, SSL_kEECDH, 0, 0, 0, 0, 0, CIPHER_ADD, -1, 0, &head, &tail); + ssl_cipher_apply_rule(0, SSL_kEECDH, 0, 0, 0, 0, 0, CIPHER_DEL, -1, 0, &head, &tail); /* AES is our preferred symmetric cipher */ - ssl_cipher_apply_rule(0, 0, 0, SSL_AES, 0, 0, 0, CIPHER_ADD, -1, &head, &tail); + ssl_cipher_apply_rule(0, 0, 0, SSL_AES, 0, 0, 0, CIPHER_ADD, -1, 0, &head, &tail); /* Temporarily enable everything else for sorting */ - ssl_cipher_apply_rule(0, 0, 0, 0, 0, 0, 0, CIPHER_ADD, -1, &head, &tail); + ssl_cipher_apply_rule(0, 0, 0, 0, 0, 0, 0, CIPHER_ADD, -1, 0, &head, &tail); /* Low priority for MD5 */ - ssl_cipher_apply_rule(0, 0, 0, 0, SSL_MD5, 0, 0, CIPHER_ORD, -1, &head, &tail); + ssl_cipher_apply_rule(0, 0, 0, 0, SSL_MD5, 0, 0, CIPHER_ORD, -1, 0, &head, &tail); /* Move anonymous ciphers to the end. Usually, these will remain disabled. * (For applications that allow them, they aren't too bad, but we prefer * authenticated ciphers.) */ - ssl_cipher_apply_rule(0, 0, SSL_aNULL, 0, 0, 0, 0, CIPHER_ORD, -1, &head, &tail); + ssl_cipher_apply_rule(0, 0, SSL_aNULL, 0, 0, 0, 0, CIPHER_ORD, -1, 0, &head, &tail); /* Move ciphers without forward secrecy to the end */ - ssl_cipher_apply_rule(0, 0, SSL_aECDH, 0, 0, 0, 0, CIPHER_ORD, -1, &head, &tail); - /* ssl_cipher_apply_rule(0, 0, SSL_aDH, 0, 0, 0, 0, CIPHER_ORD, -1, &head, &tail); */ - ssl_cipher_apply_rule(0, SSL_kRSA, 0, 0, 0, 0, 0, CIPHER_ORD, -1, &head, &tail); - ssl_cipher_apply_rule(0, SSL_kPSK, 0,0, 0, 0, 0, CIPHER_ORD, -1, &head, &tail); - ssl_cipher_apply_rule(0, SSL_kKRB5, 0,0, 0, 0, 0, CIPHER_ORD, -1, &head, &tail); + ssl_cipher_apply_rule(0, 0, SSL_aECDH, 0, 0, 0, 0, CIPHER_ORD, -1, 0, &head, &tail); + /* ssl_cipher_apply_rule(0, 0, SSL_aDH, 0, 0, 0, 0, CIPHER_ORD, -1, 0, &head, &tail); */ + ssl_cipher_apply_rule(0, SSL_kRSA, 0, 0, 0, 0, 0, CIPHER_ORD, -1, 0, &head, &tail); + ssl_cipher_apply_rule(0, SSL_kPSK, 0,0, 0, 0, 0, CIPHER_ORD, -1, 0, &head, &tail); + ssl_cipher_apply_rule(0, SSL_kKRB5, 0,0, 0, 0, 0, CIPHER_ORD, -1, 0, &head, &tail); /* RC4 is sort-of broken -- move the the end */ - ssl_cipher_apply_rule(0, 0, 0, SSL_RC4, 0, 0, 0, CIPHER_ORD, -1, &head, &tail); + ssl_cipher_apply_rule(0, 0, 0, SSL_RC4, 0, 0, 0, CIPHER_ORD, -1, 0, &head, &tail); /* Now sort by symmetric encryption strength. The above ordering remains * in force within each class */ @@ -1384,7 +1453,7 @@ STACK_OF(SSL_CIPHER) *ssl_create_cipher_list(const SSL_METHOD *ssl_method, } /* Now disable everything (maintaining the ordering!) */ - ssl_cipher_apply_rule(0, 0, 0, 0, 0, 0, 0, CIPHER_DEL, -1, &head, &tail); + ssl_cipher_apply_rule(0, 0, 0, 0, 0, 0, 0, CIPHER_DEL, -1, 0, &head, &tail); /* @@ -1429,21 +1498,18 @@ STACK_OF(SSL_CIPHER) *ssl_create_cipher_list(const SSL_METHOD *ssl_method, OPENSSL_free((void *)ca_list); /* Not needed anymore */ if (!ok) - { /* Rule processing failure */ - OPENSSL_free(co_list); - return(NULL); - } + goto err; /* * Allocate new "cipherstack" for the result, return with error * if we cannot get one. */ if ((cipherstack = sk_SSL_CIPHER_new_null()) == NULL) - { - OPENSSL_free(co_list); - return(NULL); - } + goto err; + in_group_flags = OPENSSL_malloc(num_of_ciphers); + if (!in_group_flags) + goto err; /* * The cipher selection for the list is done. The ciphers are added * to the resulting precedence to the STACK_OF(SSL_CIPHER). @@ -1457,35 +1523,65 @@ STACK_OF(SSL_CIPHER) *ssl_create_cipher_list(const SSL_METHOD *ssl_method, #endif { sk_SSL_CIPHER_push(cipherstack, curr->cipher); + in_group_flags[num_in_group_flags++] = curr->in_group; #ifdef CIPHER_DEBUG printf("<%s>\n",curr->cipher->name); #endif } } OPENSSL_free(co_list); /* Not needed any longer */ + co_list = NULL; tmp_cipher_list = sk_SSL_CIPHER_dup(cipherstack); if (tmp_cipher_list == NULL) - { - sk_SSL_CIPHER_free(cipherstack); - return NULL; - } + goto err; + pref_list = OPENSSL_malloc(sizeof(struct ssl_cipher_preference_list_st)); + if (!pref_list) + goto err; + pref_list->ciphers = cipherstack; + pref_list->in_group_flags = OPENSSL_malloc(num_in_group_flags); + if (!pref_list->in_group_flags) + goto err; + memcpy(pref_list->in_group_flags, in_group_flags, num_in_group_flags); + OPENSSL_free(in_group_flags); + in_group_flags = NULL; if (*cipher_list != NULL) - sk_SSL_CIPHER_free(*cipher_list); - *cipher_list = cipherstack; + ssl_cipher_preference_list_free(*cipher_list); + *cipher_list = pref_list; + pref_list = NULL; + if (cipher_list_by_id != NULL) { if (*cipher_list_by_id != NULL) sk_SSL_CIPHER_free(*cipher_list_by_id); *cipher_list_by_id = tmp_cipher_list; + tmp_cipher_list = NULL; (void)sk_SSL_CIPHER_set_cmp_func(*cipher_list_by_id,ssl_cipher_ptr_id_cmp); sk_SSL_CIPHER_sort(*cipher_list_by_id); } else + { sk_SSL_CIPHER_free(tmp_cipher_list); + tmp_cipher_list = NULL; + } return(cipherstack); + +err: + if (co_list) + OPENSSL_free(co_list); + if (in_group_flags) + OPENSSL_free(in_group_flags); + if (cipherstack) + sk_SSL_CIPHER_free(cipherstack); + if (tmp_cipher_list) + sk_SSL_CIPHER_free(tmp_cipher_list); + if (pref_list && pref_list->in_group_flags) + OPENSSL_free(pref_list->in_group_flags); + if (pref_list) + OPENSSL_free(pref_list); + return NULL; } char *SSL_CIPHER_description(const SSL_CIPHER *cipher, char *buf, int len) diff --git a/ssl/ssl_error.c b/ssl/ssl_error.c index 76e27451..42715243 100644 --- a/ssl/ssl_error.c +++ b/ssl/ssl_error.c @@ -348,7 +348,9 @@ const ERR_STRING_DATA SSL_error_string_data[] = { {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_TMP_RSA_KEY), "MISSING_TMP_RSA_KEY"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_TMP_RSA_PKEY), "MISSING_TMP_RSA_PKEY"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_VERIFY_MESSAGE), "MISSING_VERIFY_MESSAGE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MIXED_SPECIAL_OPERATOR_WITH_GROUPS), "MIXED_SPECIAL_OPERATOR_WITH_GROUPS"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MULTIPLE_SGC_RESTARTS), "MULTIPLE_SGC_RESTARTS"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NESTED_GROUP), "NESTED_GROUP"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NON_SSLV2_INITIAL_PACKET), "NON_SSLV2_INITIAL_PACKET"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CERTIFICATES_RETURNED), "NO_CERTIFICATES_RETURNED"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CERTIFICATE_ASSIGNED), "NO_CERTIFICATE_ASSIGNED"}, @@ -491,7 +493,9 @@ const ERR_STRING_DATA SSL_error_string_data[] = { {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_LOAD_SSL2_MD5_ROUTINES), "UNABLE_TO_LOAD_SSL2_MD5_ROUTINES"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_LOAD_SSL3_MD5_ROUTINES), "UNABLE_TO_LOAD_SSL3_MD5_ROUTINES"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES), "UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNEXPECTED_GROUP_CLOSE), "UNEXPECTED_GROUP_CLOSE"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNEXPECTED_MESSAGE), "UNEXPECTED_MESSAGE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNEXPECTED_OPERATOR_IN_GROUP), "UNEXPECTED_OPERATOR_IN_GROUP"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNEXPECTED_RECORD), "UNEXPECTED_RECORD"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNINITIALIZED), "UNINITIALIZED"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_ALERT_TYPE), "UNKNOWN_ALERT_TYPE"}, diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index c0e463cd..46b3953c 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -250,9 +250,13 @@ int SSL_CTX_set_ssl_version(SSL_CTX *ctx,const SSL_METHOD *meth) ctx->method=meth; - sk=ssl_create_cipher_list(ctx->method,&(ctx->cipher_list), - &(ctx->cipher_list_by_id), - meth->version == SSL2_VERSION ? "SSLv2" : SSL_DEFAULT_CIPHER_LIST, ctx->cert); + sk=ssl_create_cipher_list( + ctx->method, &ctx->cipher_list, &ctx->cipher_list_by_id, + meth->version == SSL2_VERSION ? + "SSLv2" : + SSL_DEFAULT_CIPHER_LIST, + ctx->cert); + if ((sk == NULL) || (sk_SSL_CIPHER_num(sk) <= 0)) { OPENSSL_PUT_ERROR(SSL, SSL_CTX_set_ssl_version, SSL_R_SSL_LIBRARY_HAS_NO_CIPHERS); @@ -520,6 +524,71 @@ int SSL_set1_param(SSL *ssl, X509_VERIFY_PARAM *vpm) return X509_VERIFY_PARAM_set1(ssl->param, vpm); } +void ssl_cipher_preference_list_free( + struct ssl_cipher_preference_list_st *cipher_list) + { + sk_SSL_CIPHER_free(cipher_list->ciphers); + OPENSSL_free(cipher_list->in_group_flags); + OPENSSL_free(cipher_list); + } + +struct ssl_cipher_preference_list_st* +ssl_cipher_preference_list_dup( + struct ssl_cipher_preference_list_st *cipher_list) + { + struct ssl_cipher_preference_list_st* ret = NULL; + size_t n = sk_SSL_CIPHER_num(cipher_list->ciphers); + + ret = OPENSSL_malloc(sizeof(struct ssl_cipher_preference_list_st)); + if (!ret) + goto err; + ret->ciphers = NULL; + ret->in_group_flags = NULL; + ret->ciphers = sk_SSL_CIPHER_dup(cipher_list->ciphers); + if (!ret->ciphers) + goto err; + ret->in_group_flags = OPENSSL_malloc(n); + if (!ret->in_group_flags) + goto err; + memcpy(ret->in_group_flags, cipher_list->in_group_flags, n); + return ret; + +err: + if (ret->ciphers) + sk_SSL_CIPHER_free(ret->ciphers); + if (ret) + OPENSSL_free(ret); + return NULL; + } + +struct ssl_cipher_preference_list_st* +ssl_cipher_preference_list_from_ciphers(STACK_OF(SSL_CIPHER) *ciphers) + { + struct ssl_cipher_preference_list_st* ret = NULL; + size_t n = sk_SSL_CIPHER_num(ciphers); + + ret = OPENSSL_malloc(sizeof(struct ssl_cipher_preference_list_st)); + if (!ret) + goto err; + ret->ciphers = NULL; + ret->in_group_flags = NULL; + ret->ciphers = sk_SSL_CIPHER_dup(ciphers); + if (!ret->ciphers) + goto err; + ret->in_group_flags = OPENSSL_malloc(n); + if (!ret->in_group_flags) + goto err; + memset(ret->in_group_flags, 0, n); + return ret; + +err: + if (ret->ciphers) + sk_SSL_CIPHER_free(ret->ciphers); + if (ret) + OPENSSL_free(ret); + return NULL; + } + X509_VERIFY_PARAM *SSL_CTX_get0_param(SSL_CTX *ctx) { return ctx->param; @@ -578,7 +647,8 @@ void SSL_free(SSL *s) if (s->init_buf != NULL) BUF_MEM_free(s->init_buf); /* add extra stuff */ - if (s->cipher_list != NULL) sk_SSL_CIPHER_free(s->cipher_list); + if (s->cipher_list != NULL) + ssl_cipher_preference_list_free(s->cipher_list); if (s->cipher_list_by_id != NULL) sk_SSL_CIPHER_free(s->cipher_list_by_id); /* Make the next call work :-) */ @@ -1313,19 +1383,19 @@ STACK_OF(SSL_CIPHER) *SSL_get_ciphers(const SSL *s) if (s->cipher_list != NULL) { - return(s->cipher_list); + return(s->cipher_list->ciphers); } if (s->version >= TLS1_1_VERSION) { if (s->ctx != NULL && s->ctx->cipher_list_tls11 != NULL) - return s->ctx->cipher_list_tls11; + return s->ctx->cipher_list_tls11->ciphers; } if ((s->ctx != NULL) && (s->ctx->cipher_list != NULL)) { - return(s->ctx->cipher_list); + return(s->ctx->cipher_list->ciphers); } return(NULL); @@ -1981,7 +2051,7 @@ SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth) &ret->cipher_list,&ret->cipher_list_by_id, meth->version == SSL2_VERSION ? "SSLv2" : SSL_DEFAULT_CIPHER_LIST, ret->cert); if (ret->cipher_list == NULL - || sk_SSL_CIPHER_num(ret->cipher_list) <= 0) + || sk_SSL_CIPHER_num(ret->cipher_list->ciphers) <= 0) { OPENSSL_PUT_ERROR(SSL, SSL_CTX_new, SSL_R_LIBRARY_HAS_NO_CIPHERS); goto err2; @@ -2145,11 +2215,11 @@ void SSL_CTX_free(SSL_CTX *a) if (a->cert_store != NULL) X509_STORE_free(a->cert_store); if (a->cipher_list != NULL) - sk_SSL_CIPHER_free(a->cipher_list); + ssl_cipher_preference_list_free(a->cipher_list); if (a->cipher_list_by_id != NULL) sk_SSL_CIPHER_free(a->cipher_list_by_id); if (a->cipher_list_tls11 != NULL) - sk_SSL_CIPHER_free(a->cipher_list_tls11); + ssl_cipher_preference_list_free(a->cipher_list_tls11); if (a->cert != NULL) ssl_cert_free(a->cert); if (a->client_CA != NULL) diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index ea7e5638..0084613f 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -1004,10 +1004,17 @@ STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s,unsigned char *p,int num, int ssl_cipher_list_to_bytes(SSL *s,STACK_OF(SSL_CIPHER) *sk,unsigned char *p, int (*put_cb)(const SSL_CIPHER *, unsigned char *)); STACK_OF(SSL_CIPHER) *ssl_create_cipher_list(const SSL_METHOD *meth, - STACK_OF(SSL_CIPHER) **pref, + struct ssl_cipher_preference_list_st **pref, STACK_OF(SSL_CIPHER) **sorted, const char *rule_str, CERT *c); void ssl_update_cache(SSL *s, int mode); +struct ssl_cipher_preference_list_st* ssl_cipher_preference_list_dup( + struct ssl_cipher_preference_list_st *cipher_list); +void ssl_cipher_preference_list_free( + struct ssl_cipher_preference_list_st *cipher_list); +struct ssl_cipher_preference_list_st* ssl_cipher_preference_list_from_ciphers( + STACK_OF(SSL_CIPHER) *ciphers); +struct ssl_cipher_preference_list_st* ssl_get_cipher_preferences(SSL *s); int ssl_cipher_get_comp(const SSL_SESSION *s, SSL_COMP **comp); int ssl_cipher_get_evp_aead(const SSL_SESSION *s, const EVP_AEAD **aead); int ssl_cipher_get_evp(const SSL_SESSION *s,const EVP_CIPHER **enc, @@ -1106,7 +1113,7 @@ int n_ssl3_mac(SSL *ssl, unsigned char *md, int send_data); void ssl3_free_digest_list(SSL *s); unsigned long ssl3_output_cert_chain(SSL *s, CERT_PKEY *cpk); SSL_CIPHER *ssl3_choose_cipher(SSL *ssl,STACK_OF(SSL_CIPHER) *clnt, - STACK_OF(SSL_CIPHER) *srvr); + struct ssl_cipher_preference_list_st *srvr); int ssl3_setup_buffers(SSL *s); int ssl3_setup_read_buffer(SSL *s); int ssl3_setup_write_buffer(SSL *s);