Example #1
0
static int ssl_cipher_process_rulestr(const char *rule_str,
                CIPHER_ORDER **head_p, CIPHER_ORDER **tail_p,
                const SSL_CIPHER **ca_list)
	{
	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;
	unsigned long cipher_id = 0;
	char ch;

	retval = 1;
	l = rule_str;
	for (;;)
		{
		ch = *l;

		if (ch == '\0')
			break;		/* done */
		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
			{ rule = CIPHER_ADD; }

		if (ITEM_SEP(ch))
			{
			l++;
			continue;
			}

		alg_mkey = 0;
		alg_auth = 0;
		alg_enc = 0;
		alg_mac = 0;
		alg_ssl = 0;
		algo_strength = 0;

		for (;;)
			{
			ch = *l;
			buf = l;
			buflen = 0;
#ifndef CHARSET_EBCDIC
			while (	((ch >= 'A') && (ch <= 'Z')) ||
				((ch >= '0') && (ch <= '9')) ||
				((ch >= 'a') && (ch <= 'z')) ||
				 (ch == '-'))
#else
			while (	isalnum(ch) || (ch == '-'))
#endif
				 {
				 ch = *(++l);
				 buflen++;
				 }

			if (buflen == 0)
				{
				/*
				 * We hit something we cannot deal with,
				 * it is no command or separator nor
				 * alphanumeric, so we call this an error.
				 */
				SSLerr(SSL_F_SSL_CIPHER_PROCESS_RULESTR,
				       SSL_R_INVALID_COMMAND);
				retval = found = 0;
				l++;
				break;
				}

			if (rule == CIPHER_SPECIAL)
				{
				found = 0; /* unused -- avoid compiler warning */
				break;	/* special treatment */
				}

			/* check for multi-part specification */
			if (ch == '+')
				{
				multi=1;
				l++;
				}
			else
				multi=0;

			/*
			 * Now search for the cipher alias in the ca_list. Be careful
			 * with the strncmp, because the "buflen" limitation
			 * will make the rule "ADH:SOME" and the cipher
			 * "ADH-MY-CIPHER" look like a match for buflen=3.
			 * So additionally check whether the cipher name found
			 * has the correct length. We can save a strlen() call:
			 * just checking for the '\0' at the right place is
			 * sufficient, we have to strncmp() anyway. (We cannot
			 * use strcmp(), because buf is not '\0' terminated.)
			 */
			j = found = 0;
			cipher_id = 0;
			while (ca_list[j])
				{
				if (!strncmp(buf, ca_list[j]->name, buflen) &&
				    (ca_list[j]->name[buflen] == '\0'))
					{
					found = 1;
					break;
					}
				else
					j++;
				}

			if (!found)
				break;	/* ignore this entry */

			if (ca_list[j]->algorithm_mkey)
				{
				if (alg_mkey)
					{
					alg_mkey &= ca_list[j]->algorithm_mkey;
					if (!alg_mkey) { found = 0; break; }
					}
				else
					alg_mkey = ca_list[j]->algorithm_mkey;
				}

			if (ca_list[j]->algorithm_auth)
				{
				if (alg_auth)
					{
					alg_auth &= ca_list[j]->algorithm_auth;
					if (!alg_auth) { found = 0; break; }
					}
				else
					alg_auth = ca_list[j]->algorithm_auth;
				}
			
			if (ca_list[j]->algorithm_enc)
				{
				if (alg_enc)
					{
					alg_enc &= ca_list[j]->algorithm_enc;
					if (!alg_enc) { found = 0; break; }
					}
				else
					alg_enc = ca_list[j]->algorithm_enc;
				}
						
			if (ca_list[j]->algorithm_mac)
				{
				if (alg_mac)
					{
					alg_mac &= ca_list[j]->algorithm_mac;
					if (!alg_mac) { found = 0; break; }
					}
				else
					alg_mac = ca_list[j]->algorithm_mac;
				}
			
			if (ca_list[j]->algo_strength & SSL_EXP_MASK)
				{
				if (algo_strength & SSL_EXP_MASK)
					{
					algo_strength &= (ca_list[j]->algo_strength & SSL_EXP_MASK) | ~SSL_EXP_MASK;
					if (!(algo_strength & SSL_EXP_MASK)) { found = 0; break; }
					}
				else
					algo_strength |= ca_list[j]->algo_strength & SSL_EXP_MASK;
				}

			if (ca_list[j]->algo_strength & SSL_STRONG_MASK)
				{
				if (algo_strength & SSL_STRONG_MASK)
					{
					algo_strength &= (ca_list[j]->algo_strength & SSL_STRONG_MASK) | ~SSL_STRONG_MASK;
					if (!(algo_strength & SSL_STRONG_MASK)) { found = 0; break; }
					}
				else
					algo_strength |= ca_list[j]->algo_strength & SSL_STRONG_MASK;
				}
			
			if (ca_list[j]->valid)
				{
				/* explicit ciphersuite found; its protocol version
				 * does not become part of the search pattern!*/

				cipher_id = ca_list[j]->id;
				}
			else
				{
				/* not an explicit ciphersuite; only in this case, the
				 * protocol version is considered part of the search pattern */

				if (ca_list[j]->algorithm_ssl)
					{
					if (alg_ssl)
						{
						alg_ssl &= ca_list[j]->algorithm_ssl;
						if (!alg_ssl) { found = 0; break; }
						}
					else
						alg_ssl = ca_list[j]->algorithm_ssl;
					}
				}
			
			if (!multi) break;
			}

		/*
		 * Ok, we have the rule, now apply it
		 */
		if (rule == CIPHER_SPECIAL)
			{	/* special command */
			ok = 0;
			if ((buflen == 8) &&
				!strncmp(buf, "STRENGTH", 8))
				ok = ssl_cipher_strength_sort(head_p, tail_p);
			else
				SSLerr(SSL_F_SSL_CIPHER_PROCESS_RULESTR,
					SSL_R_INVALID_COMMAND);
			if (ok == 0)
				retval = 0;
			/*
			 * 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') && !ITEM_SEP(*l))
				l++;
			}
		else if (found)
			{
			ssl_cipher_apply_rule(cipher_id,
				alg_mkey, alg_auth, alg_enc, alg_mac, alg_ssl, algo_strength,
				rule, -1, head_p, tail_p);
			}
		else
			{
			while ((*l != '\0') && !ITEM_SEP(*l))
				l++;
			}
		if (*l == '\0') break; /* done */
		}

	return(retval);
	}
Example #2
0
static int ssl_cipher_process_rulestr(const SSL_PROTOCOL_METHOD *ssl_method,
                                      const char *rule_str,
                                      CIPHER_ORDER **head_p,
                                      CIPHER_ORDER **tail_p) {
  uint32_t alg_mkey, alg_auth, alg_enc, alg_mac, alg_ssl, algo_strength;
  const char *l, *buf;
  int multi, rule, retval, ok, in_group = 0, has_group = 0;
  size_t j, buf_len;
  uint32_t cipher_id;
  char ch;

  retval = 1;
  l = rule_str;
  for (;;) {
    ch = *l;

    if (ch == '\0') {
      break; /* done */
    }

    if (in_group) {
      if (ch == ']') {
        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 = 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 == '!') {
      rule = CIPHER_KILL;
      l++;
    } 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 = in_group = 0;
        break;
      }
      in_group = 1;
      has_group = 1;
      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_cipher_process_rulestr,
                        SSL_R_MIXED_SPECIAL_OPERATOR_WITH_GROUPS);
      retval = in_group = 0;
      break;
    }

    if (ITEM_SEP(ch)) {
      l++;
      continue;
    }

    multi = 0;
    cipher_id = 0;
    alg_mkey = ~0u;
    alg_auth = ~0u;
    alg_enc = ~0u;
    alg_mac = ~0u;
    alg_ssl = ~0u;
    algo_strength = ~0u;

    for (;;) {
      ch = *l;
      buf = l;
      buf_len = 0;
      while (((ch >= 'A') && (ch <= 'Z')) || ((ch >= '0') && (ch <= '9')) ||
             ((ch >= 'a') && (ch <= 'z')) || (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_cipher_process_rulestr,
                          SSL_R_INVALID_COMMAND);
        retval = in_group = 0;
        l++;
        break;
      }

      if (rule == CIPHER_SPECIAL) {
        break;
      }

      /* Look for a matching exact cipher. These aren't allowed in multipart
       * rules. */
      if (!multi && ch != '+') {
        size_t num_ciphers = ssl_method->num_ciphers();
        for (j = 0; j < num_ciphers; j++) {
          const SSL_CIPHER *cipher = ssl_method->get_cipher(j);
          if (cipher != NULL && rule_equals(cipher->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 < NUM_CIPHER_ALIASES; 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;
            alg_ssl &= kCipherAliases[j].algorithm_ssl;
            algo_strength &= kCipherAliases[j].algo_strength;
            break;
          }
        }
        if (j == NUM_CIPHER_ALIASES) {
          alg_mkey = alg_auth = alg_enc = alg_mac = alg_ssl = algo_strength = 0;
        }
      }

      /* Check for a multipart rule. */
      if (ch != '+') {
        break;
      }
      l++;
      multi = 1;
    }

    /* Ok, we have the rule, now apply it. */
    if (rule == CIPHER_SPECIAL) {
      /* special command */
      ok = 0;
      if (buf_len == 8 && !strncmp(buf, "STRENGTH", 8)) {
        ok = ssl_cipher_strength_sort(head_p, tail_p);
      } else {
        OPENSSL_PUT_ERROR(SSL, ssl_cipher_process_rulestr,
                          SSL_R_INVALID_COMMAND);
      }

      if (ok == 0) {
        retval = 0;
      }

      /* 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' && !ITEM_SEP(*l)) {
        l++;
      }
    } else {
      ssl_cipher_apply_rule(cipher_id, alg_mkey, alg_auth, alg_enc, alg_mac,
                            alg_ssl, algo_strength, rule, -1, in_group, head_p,
                            tail_p);
    }
  }

  if (in_group) {
    OPENSSL_PUT_ERROR(SSL, ssl_cipher_process_rulestr, SSL_R_INVALID_COMMAND);
    retval = 0;
  }

  return retval;
}
Example #3
0
static int ssl_cipher_process_rulestr(const char *rule_str,
		CIPHER_ORDER *co_list, CIPHER_ORDER **head_p,
		CIPHER_ORDER **tail_p, SSL_CIPHER **ca_list)
	{
	unsigned long algorithms, mask, algo_strength, mask_strength;
	const char *l, *start, *buf;
	int j, multi, found, rule, retval, ok, buflen;
	unsigned long cipher_id = 0, ssl_version = 0;
	char ch;

	retval = 1;
	l = rule_str;
	for (;;)
		{
		ch = *l;

		if (ch == '\0')
			break;		/* done */
		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
			{ rule = CIPHER_ADD; }

		if (ITEM_SEP(ch))
			{
			l++;
			continue;
			}

		algorithms = mask = algo_strength = mask_strength = 0;

		start=l;
		for (;;)
			{
			ch = *l;
			buf = l;
			buflen = 0;
#ifndef CHARSET_EBCDIC
			while (	((ch >= 'A') && (ch <= 'Z')) ||
				((ch >= '0') && (ch <= '9')) ||
				((ch >= 'a') && (ch <= 'z')) ||
				 (ch == '-'))
#else
			while (	isalnum(ch) || (ch == '-'))
#endif
				 {
				 ch = *(++l);
				 buflen++;
				 }

			if (buflen == 0)
				{
				/*
				 * We hit something we cannot deal with,
				 * it is no command or separator nor
				 * alphanumeric, so we call this an error.
				 */
				SSLerr(SSL_F_SSL_CIPHER_PROCESS_RULESTR,
				       SSL_R_INVALID_COMMAND);
				retval = found = 0;
				l++;
				break;
				}

			if (rule == CIPHER_SPECIAL)
				{
				found = 0; /* unused -- avoid compiler warning */
				break;	/* special treatment */
				}

			/* check for multi-part specification */
			if (ch == '+')
				{
				multi=1;
				l++;
				}
			else
				multi=0;

			/*
			 * Now search for the cipher alias in the ca_list. Be careful
			 * with the strncmp, because the "buflen" limitation
			 * will make the rule "ADH:SOME" and the cipher
			 * "ADH-MY-CIPHER" look like a match for buflen=3.
			 * So additionally check whether the cipher name found
			 * has the correct length. We can save a strlen() call:
			 * just checking for the '\0' at the right place is
			 * sufficient, we have to strncmp() anyway. (We cannot
			 * use strcmp(), because buf is not '\0' terminated.)
			 */
			 j = found = 0;
			 cipher_id = 0;
			 ssl_version = 0;
			 while (ca_list[j])
				{
				if (!strncmp(buf, ca_list[j]->name, buflen) &&
				    (ca_list[j]->name[buflen] == '\0'))
					{
					found = 1;
					break;
					}
				else
					j++;
				}
			if (!found)
				break;	/* ignore this entry */

			/* New algorithms:
			 *  1 - any old restrictions apply outside new mask
			 *  2 - any new restrictions apply outside old mask
			 *  3 - enforce old & new where masks intersect
			 */
			algorithms = (algorithms & ~ca_list[j]->mask) |		/* 1 */
			             (ca_list[j]->algorithms & ~mask) |		/* 2 */
			             (algorithms & ca_list[j]->algorithms);	/* 3 */
			mask |= ca_list[j]->mask;
			algo_strength = (algo_strength & ~ca_list[j]->mask_strength) |
			                (ca_list[j]->algo_strength & ~mask_strength) |
			                (algo_strength & ca_list[j]->algo_strength);
			mask_strength |= ca_list[j]->mask_strength;

			/* explicit ciphersuite found */
			if (ca_list[j]->valid)
				{
				cipher_id = ca_list[j]->id;
				ssl_version = ca_list[j]->algorithms & SSL_SSL_MASK;
				break;
				}

			if (!multi) break;
			}

		/*
		 * Ok, we have the rule, now apply it
		 */
		if (rule == CIPHER_SPECIAL)
			{	/* special command */
			ok = 0;
			if ((buflen == 8) &&
				!strncmp(buf, "STRENGTH", 8))
				ok = ssl_cipher_strength_sort(co_list,
					head_p, tail_p);
			else
				SSLerr(SSL_F_SSL_CIPHER_PROCESS_RULESTR,
					SSL_R_INVALID_COMMAND);
			if (ok == 0)
				retval = 0;
			/*
			 * 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') && !ITEM_SEP(*l))
				l++;
			}
		else if (found)
			{
			ssl_cipher_apply_rule(cipher_id, ssl_version, algorithms, mask,
				algo_strength, mask_strength, rule, -1,
				co_list, head_p, tail_p);
			}
		else
			{
			while ((*l != '\0') && !ITEM_SEP(*l))
				l++;
			}
		if (*l == '\0') break; /* done */
		}

	return(retval);
	}
Example #4
0
static int ssl_cipher_process_rulestr(const char *rule_str,
                                      CIPHER_ORDER **head_p,
                                      CIPHER_ORDER **tail_p,
                                      const SSL_CIPHER **ca_list) {
  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, in_group = 0, has_group = 0;
  unsigned long cipher_id = 0;
  char ch;

  retval = 1;
  l = rule_str;
  for (;;) {
    ch = *l;

    if (ch == '\0') {
      break; /* done */
    }

    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 == '!') {
      rule = CIPHER_KILL;
      l++;
    } 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;
    }

    /* 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_cipher_process_rulestr,
                        SSL_R_MIXED_SPECIAL_OPERATOR_WITH_GROUPS);
      retval = found = in_group = 0;
      break;
    }

    if (ITEM_SEP(ch)) {
      l++;
      continue;
    }

    alg_mkey = 0;
    alg_auth = 0;
    alg_enc = 0;
    alg_mac = 0;
    alg_ssl = 0;
    algo_strength = 0;

    for (;;) {
      ch = *l;
      buf = l;
      buflen = 0;
      while (((ch >= 'A') && (ch <= 'Z')) || ((ch >= '0') && (ch <= '9')) ||
             ((ch >= 'a') && (ch <= 'z')) || (ch == '-') || (ch == '.')) {
        ch = *(++l);
        buflen++;
      }

      if (buflen == 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_cipher_process_rulestr,
                          SSL_R_INVALID_COMMAND);
        retval = found = in_group = 0;
        l++;
        break;
      }

      if (rule == CIPHER_SPECIAL) {
        found = 0; /* unused -- avoid compiler warning */
        break;     /* special treatment */
      }

      /* check for multi-part specification */
      if (ch == '+') {
        multi = 1;
        l++;
      } else {
        multi = 0;
      }

      /* Now search for the cipher alias in the ca_list. Be careful with the
       * strncmp, because the "buflen" limitation will make the rule "ADH:SOME"
       * and the cipher "ADH-MY-CIPHER" look like a match for buflen=3. So
       * additionally check whether the cipher name found has the correct
       * length. We can save a strlen() call: just checking for the '\0' at the
       * right place is sufficient, we have to strncmp() anyway. (We cannot use
       * strcmp(), because buf is not '\0' terminated.) */
      j = found = 0;
      cipher_id = 0;
      while (ca_list[j]) {
        if (!strncmp(buf, ca_list[j]->name, buflen) &&
            (ca_list[j]->name[buflen] == '\0')) {
          found = 1;
          break;
        } else {
          j++;
        }
      }

      if (!found) {
        break; /* ignore this entry */
      }

      if (ca_list[j]->algorithm_mkey) {
        if (alg_mkey) {
          alg_mkey &= ca_list[j]->algorithm_mkey;
          if (!alg_mkey) {
            found = 0;
            break;
          }
        } else {
          alg_mkey = ca_list[j]->algorithm_mkey;
        }
      }

      if (ca_list[j]->algorithm_auth) {
        if (alg_auth) {
          alg_auth &= ca_list[j]->algorithm_auth;
          if (!alg_auth) {
            found = 0;
            break;
          }
        } else {
          alg_auth = ca_list[j]->algorithm_auth;
        }
      }

      if (ca_list[j]->algorithm_enc) {
        if (alg_enc) {
          alg_enc &= ca_list[j]->algorithm_enc;
          if (!alg_enc) {
            found = 0;
            break;
          }
        } else {
          alg_enc = ca_list[j]->algorithm_enc;
        }
      }

      if (ca_list[j]->algorithm_mac) {
        if (alg_mac) {
          alg_mac &= ca_list[j]->algorithm_mac;
          if (!alg_mac) {
            found = 0;
            break;
          }
        } else {
          alg_mac = ca_list[j]->algorithm_mac;
        }
      }

      if (ca_list[j]->algo_strength) {
        if (algo_strength) {
          algo_strength &= ca_list[j]->algo_strength;
          if (!algo_strength) {
            found = 0;
            break;
          }
        } else {
          algo_strength |= ca_list[j]->algo_strength;
        }
      }

      if (ca_list[j]->valid) {
        /* explicit ciphersuite found; its protocol version does not become
         * part of the search pattern! */
        cipher_id = ca_list[j]->id;
      } else {
        /* not an explicit ciphersuite; only in this case, the protocol version
         * is considered part of the search pattern. */
        if (ca_list[j]->algorithm_ssl) {
          if (alg_ssl) {
            alg_ssl &= ca_list[j]->algorithm_ssl;
            if (!alg_ssl) {
              found = 0;
              break;
            }
          } else {
            alg_ssl = ca_list[j]->algorithm_ssl;
          }
        }
      }

      if (!multi) {
        break;
      }
    }

    /* Ok, we have the rule, now apply it. */
    if (rule == CIPHER_SPECIAL) {
      /* special command */
      ok = 0;
      if (buflen == 8 && !strncmp(buf, "STRENGTH", 8)) {
        ok = ssl_cipher_strength_sort(head_p, tail_p);
      } else {
        OPENSSL_PUT_ERROR(SSL, ssl_cipher_process_rulestr,
                          SSL_R_INVALID_COMMAND);
      }

      if (ok == 0) {
        retval = 0;
      }

      /* 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' && !ITEM_SEP(*l)) {
        l++;
      }
    } else if (found) {
      ssl_cipher_apply_rule(cipher_id, alg_mkey, alg_auth, alg_enc, alg_mac,
                            alg_ssl, algo_strength, rule, -1, in_group, head_p,
                            tail_p);
    } else {
      while (*l != '\0' && !ITEM_SEP(*l)) {
        l++;
      }
    }
  }

  if (in_group) {
    OPENSSL_PUT_ERROR(SSL, ssl_cipher_process_rulestr, SSL_R_INVALID_COMMAND);
    retval = 0;
  }

  return retval;
}