static bool ssl_cipher_process_rulestr()

in ssl/ssl_cipher.cc [1033:1213]


static bool ssl_cipher_process_rulestr(const char *rule_str,
                                       CIPHER_ORDER **head_p,
                                       CIPHER_ORDER **tail_p, bool strict,
                                       bool config_tls13) {
  uint32_t alg_mkey, alg_auth, alg_enc, alg_mac;
  uint16_t min_version;
  const char *l, *buf;

  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 (;;) {
    int rule = CIPHER_ADD;
    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 == '|') {
        l++;
        continue;
      } else if (!OPENSSL_isalnum(ch)) {
        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;
    }

    // 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 (OPENSSL_isalnum(ch) || 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 ((config_tls13 && cipher->algorithm_mkey != SSL_kGENERIC) ||
            (!config_tls13 && cipher->algorithm_mkey == SSL_kGENERIC)) {
            continue;
          }
          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;
}