in src/ssl/ssl_cipher.cc [971:1150]
static bool ssl_cipher_process_rulestr(const char *rule_str,
CIPHER_ORDER **head_p,
CIPHER_ORDER **tail_p, bool strict) {
uint32_t alg_mkey, alg_auth, alg_enc, alg_mac;
uint16_t min_version;
const char *l, *buf;
int rule;
bool multi, skip_rule, in_group = false, has_group = false;
size_t j, buf_len;
uint32_t cipher_id;
char ch;
l = rule_str;
for (;;) {
ch = *l;
if (ch == '\0') {
break; // done
}
if (in_group) {
if (ch == ']') {
if (*tail_p) {
(*tail_p)->in_group = false;
}
in_group = false;
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_R_UNEXPECTED_OPERATOR_IN_GROUP);
return false;
} else {
rule = CIPHER_ADD;
}
} else if (ch == '-') {
rule = CIPHER_DEL;
l++;
} else if (ch == '+') {
rule = CIPHER_ORD;
l++;
} else if (ch == '!') {
rule = CIPHER_KILL;
l++;
} else if (ch == '@') {
rule = CIPHER_SPECIAL;
l++;
} else if (ch == '[') {
assert(!in_group);
in_group = true;
has_group = true;
l++;
continue;
} else {
rule = CIPHER_ADD;
}
// If preference groups are enabled, the only legal operator is +.
// Otherwise the in_group bits will get mixed up.
if (has_group && rule != CIPHER_ADD) {
OPENSSL_PUT_ERROR(SSL, SSL_R_MIXED_SPECIAL_OPERATOR_WITH_GROUPS);
return false;
}
if (is_cipher_list_separator(ch, strict)) {
l++;
continue;
}
multi = false;
cipher_id = 0;
alg_mkey = ~0u;
alg_auth = ~0u;
alg_enc = ~0u;
alg_mac = ~0u;
min_version = 0;
skip_rule = false;
for (;;) {
ch = *l;
buf = l;
buf_len = 0;
while ((ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') ||
(ch >= 'a' && ch <= 'z') || ch == '-' || ch == '.' || ch == '_') {
ch = *(++l);
buf_len++;
}
if (buf_len == 0) {
// We hit something we cannot deal with, it is no command or separator
// nor alphanumeric, so we call this an error.
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_COMMAND);
return false;
}
if (rule == CIPHER_SPECIAL) {
break;
}
// Look for a matching exact cipher. These aren't allowed in multipart
// rules.
if (!multi && ch != '+') {
for (j = 0; j < OPENSSL_ARRAY_SIZE(kCiphers); j++) {
const SSL_CIPHER *cipher = &kCiphers[j];
if (rule_equals(cipher->name, buf, buf_len) ||
rule_equals(cipher->standard_name, buf, buf_len)) {
cipher_id = cipher->id;
break;
}
}
}
if (cipher_id == 0) {
// If not an exact cipher, look for a matching cipher alias.
for (j = 0; j < kCipherAliasesLen; j++) {
if (rule_equals(kCipherAliases[j].name, buf, buf_len)) {
alg_mkey &= kCipherAliases[j].algorithm_mkey;
alg_auth &= kCipherAliases[j].algorithm_auth;
alg_enc &= kCipherAliases[j].algorithm_enc;
alg_mac &= kCipherAliases[j].algorithm_mac;
if (min_version != 0 &&
min_version != kCipherAliases[j].min_version) {
skip_rule = true;
} else {
min_version = kCipherAliases[j].min_version;
}
break;
}
}
if (j == kCipherAliasesLen) {
skip_rule = true;
if (strict) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_COMMAND);
return false;
}
}
}
// Check for a multipart rule.
if (ch != '+') {
break;
}
l++;
multi = true;
}
// Ok, we have the rule, now apply it.
if (rule == CIPHER_SPECIAL) {
if (buf_len != 8 || strncmp(buf, "STRENGTH", 8) != 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_COMMAND);
return false;
}
if (!ssl_cipher_strength_sort(head_p, tail_p)) {
return false;
}
// We do not support any "multi" options together with "@", so throw away
// the rest of the command, if any left, until end or ':' is found.
while (*l != '\0' && !is_cipher_list_separator(*l, strict)) {
l++;
}
} else if (!skip_rule) {
ssl_cipher_apply_rule(cipher_id, alg_mkey, alg_auth, alg_enc, alg_mac,
min_version, rule, -1, in_group, head_p, tail_p);
}
}
if (in_group) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_COMMAND);
return false;
}
return true;
}