Esempio n. 1
0
/**
  * gnutls_priority_init - Sets priorities for the cipher suites supported by gnutls.
  * @priority_cache: is a #gnutls_prioritity_t structure.
  * @priorities: is a string describing priorities
  * @err_pos: In case of an error this will have the position in the string the error occured
  *
  * Sets priorities for the ciphers, key exchange methods, macs and
  * compression methods. This is to avoid using the
  * gnutls_*_priority() functions.
  *
  * The #priorities option allows you to specify a semi-colon
  * separated list of the cipher priorities to enable.
  *
  * Unless the first keyword is "NONE" the defaults are:
  * Protocols: TLS1.1, TLS1.0, and SSL3.0.
  * Compression: NULL.
  * Certificate types: X.509, OpenPGP.
  *
  * You can also use predefined sets of ciphersuites: "PERFORMANCE"
  * all the "secure" ciphersuites are enabled, limited to 128 bit
  * ciphers and sorted by terms of speed performance.
  *
  * "NORMAL" option enables all "secure" ciphersuites. The 256-bit ciphers
  * are included as a fallback only. The ciphers are sorted by security margin.
  *
  * "SECURE128" flag enables all "secure" ciphersuites with ciphers up to 
  * 128 bits, sorted by security margin.
  *
  * "SECURE256" flag enables all "secure" ciphersuites including the 256 bit
  * ciphers, sorted by security margin.
  *
  * "EXPORT" all the ciphersuites are enabled, including the
  * low-security 40 bit ciphers.
  *
  * "NONE" nothing is enabled. This disables even protocols and
  * compression methods.
  *
  * Special keywords:
  * '!' or '-' appended with an algorithm will remove this algorithm.
  * '+' appended with an algorithm will add this algorithm.
  * '%COMPAT' will enable compatibility features for a server.
  *
  * To avoid collisions in order to specify a compression algorithm in
  * this string you have to prefix it with "COMP-", protocol versions
  * with "VERS-" and certificate types with "CTYPE-". All other
  * algorithms don't need a prefix.
  *
  * For key exchange algorithms when in NORMAL or SECURE levels the
  * perfect forward secrecy algorithms take precendence of the other
  * protocols.  In all cases all the supported key exchange algorithms
  * are enabled (except for the RSA-EXPORT which is only enabled in
  * EXPORT level).
  *
  * Note that although one can select very long key sizes (such as 256 bits) 
  * for symmetric algorithms, to actually increase security the public key
  * algorithms have to use longer key sizes as well.
  *
  * Examples: "NORMAL:!AES-128-CBC",
  * "EXPORT:!VERS-TLS1.0:+COMP-DEFLATE:+CTYPE-OPENPGP",
  * "NONE:+VERS-TLS1.0:+AES-128-CBC:+RSA:+SHA1:+COMP-NULL", "NORMAL",
  * "NORMAL:%COMPAT".
  *
  * Returns: On syntax error GNUTLS_E_INVALID_REQUEST is returned and
  * 0 on success.
  **/
int
gnutls_priority_init (gnutls_priority_t * priority_cache,
		      const char *priorities, const char **err_pos)
{
  char *broken_list[MAX_ELEMENTS];
  int broken_list_size, i, j;
  char *darg;
  int algo;
  rmadd_func *fn;

  *priority_cache = gnutls_calloc (1, sizeof (struct gnutls_priority_st));
  if (*priority_cache == NULL)
    {
      gnutls_assert ();
      return GNUTLS_E_MEMORY_ERROR;
    }

  if (priorities == NULL)
    priorities = "NORMAL";

  darg = gnutls_strdup (priorities);
  if (darg == NULL)
    {
      gnutls_assert ();
      return GNUTLS_E_MEMORY_ERROR;
    }

  break_comma_list (darg, broken_list, &broken_list_size, MAX_ELEMENTS, ':');

  /* This is our default set of protocol version, certificate types and
   * compression methods.
   */
  if (strcasecmp (broken_list[0], "NONE") != 0)
    {
      _set_priority (&(*priority_cache)->protocol, protocol_priority);
      _set_priority (&(*priority_cache)->compression, comp_priority);
      _set_priority (&(*priority_cache)->cert_type, cert_type_priority);
    }

  for (i = 0; i < broken_list_size; i++)
    {
      if (strcasecmp (broken_list[i], "PERFORMANCE") == 0)
	{
	  _set_priority (&(*priority_cache)->cipher,
			 cipher_priority_performance);
	  _set_priority (&(*priority_cache)->kx, kx_priority_performance);
	  _set_priority (&(*priority_cache)->mac, mac_priority_performance);
	}
      else if (strcasecmp (broken_list[i], "NORMAL") == 0)
	{
	  _set_priority (&(*priority_cache)->cipher, cipher_priority_normal);
	  _set_priority (&(*priority_cache)->kx, kx_priority_secure);
	  _set_priority (&(*priority_cache)->mac, mac_priority_secure);
	}
      else if (strcasecmp (broken_list[i], "SECURE256") == 0 || strcasecmp (broken_list[i], "SECURE") == 0)
	{
	  _set_priority (&(*priority_cache)->cipher, cipher_priority_secure256);
	  _set_priority (&(*priority_cache)->kx, kx_priority_secure);
	  _set_priority (&(*priority_cache)->mac, mac_priority_secure);
	}
      else if (strcasecmp (broken_list[i], "SECURE128") == 0)
	{
	  _set_priority (&(*priority_cache)->cipher, cipher_priority_secure128);
	  _set_priority (&(*priority_cache)->kx, kx_priority_secure);
	  _set_priority (&(*priority_cache)->mac, mac_priority_secure);
	}
      else if (strcasecmp (broken_list[i], "EXPORT") == 0)
	{
	  _set_priority (&(*priority_cache)->cipher, cipher_priority_export);
	  _set_priority (&(*priority_cache)->kx, kx_priority_export);
	  _set_priority (&(*priority_cache)->mac, mac_priority_secure);
	}			/* now check if the element is something like -ALGO */
      else if (broken_list[i][0] == '!' || broken_list[i][0] == '+'
	       || broken_list[i][0] == '-')
	{
	  if (broken_list[i][0] == '+')
	    fn = prio_add;
	  else
	    fn = prio_remove;

	  if ((algo =
	       gnutls_mac_get_id (&broken_list[i][1])) != GNUTLS_MAC_UNKNOWN)
	    fn (&(*priority_cache)->mac, algo);
	  else if ((algo = gnutls_cipher_get_id (&broken_list[i][1])) !=
		   GNUTLS_CIPHER_UNKNOWN)
	    fn (&(*priority_cache)->cipher, algo);
	  else if ((algo = gnutls_kx_get_id (&broken_list[i][1])) !=
		   GNUTLS_KX_UNKNOWN)
	    fn (&(*priority_cache)->kx, algo);
	  else if (strncasecmp (&broken_list[i][1], "VERS-", 5) == 0)
	    {
	      if ((algo =
		   gnutls_protocol_get_id (&broken_list[i][6])) !=
		  GNUTLS_VERSION_UNKNOWN)
		fn (&(*priority_cache)->protocol, algo);
	    }			/* now check if the element is something like -ALGO */
	  else if (strncasecmp (&broken_list[i][1], "COMP-", 5) == 0)
	    {
	      if ((algo =
		   gnutls_compression_get_id (&broken_list[i][6])) !=
		  GNUTLS_COMP_UNKNOWN)
		fn (&(*priority_cache)->compression, algo);
	    }			/* now check if the element is something like -ALGO */
	  else if (strncasecmp (&broken_list[i][1], "CTYPE-", 6) == 0)
	    {
	      if ((algo =
		   gnutls_certificate_type_get_id (&broken_list[i][7])) !=
		  GNUTLS_CRT_UNKNOWN)
		fn (&(*priority_cache)->cert_type, algo);
	    }			/* now check if the element is something like -ALGO */
	  else
	    goto error;
	}
      else if (broken_list[i][0] == '%')
	{
	  if (strcasecmp (&broken_list[i][1], "COMPAT") == 0)
	    (*priority_cache)->no_padding = 1;
	  else
	    goto error;
	}
      else
	goto error;
    }

  gnutls_free (darg);
  return 0;

error:
  if (err_pos != NULL && i < broken_list_size)
    {
      *err_pos = priorities;
      for (j = 0; j < i; j++)
	{
	  (*err_pos) += strlen (broken_list[j]) + 1;
	}
    }
  gnutls_free (darg);

  return GNUTLS_E_INVALID_REQUEST;

}
Esempio n. 2
0
/**
 * gnutls_priority_init:
 * @priority_cache: is a #gnutls_prioritity_t structure.
 * @priorities: is a string describing priorities
 * @err_pos: In case of an error this will have the position in the string the error occured
 *
 * Sets priorities for the ciphers, key exchange methods, macs and
 * compression methods.
 *
 * The #priorities option allows you to specify a colon
 * separated list of the cipher priorities to enable.
 * Some keywords are defined to provide quick access
 * to common preferences.
 *
 * Unless there is a special need, using "NORMAL" or "NORMAL:%COMPAT" for compatibility 
 * is recommended.
 *
 * "PERFORMANCE" means all the "secure" ciphersuites are enabled,
 * limited to 128 bit ciphers and sorted by terms of speed
 * performance.
 *
 * "NORMAL" means all "secure" ciphersuites. The 256-bit ciphers are
 * included as a fallback only.  The ciphers are sorted by security
 * margin.
 *
 * "PFS" means all "secure" ciphersuites that support perfect forward secrecy. 
 * The 256-bit ciphers are included as a fallback only.  
 * The ciphers are sorted by security margin.
 *
 * "SECURE128" means all "secure" ciphersuites of security level 128-bit
 * or more.
 *
 * "SECURE192" means all "secure" ciphersuites of security level 192-bit
 * or more.
 *
 * "SUITEB128" means all the NSA SuiteB ciphersuites with security level
 * of 128.
 *
 * "SUITEB192" means all the NSA SuiteB ciphersuites with security level
 * of 192.
 *
 * "EXPORT" means all ciphersuites are enabled, including the
 * low-security 40 bit ciphers.
 *
 * "NONE" means nothing is enabled.  This disables even protocols and
 * compression methods.
 *
 * Special keywords are "!", "-" and "+".
 * "!" or "-" appended with an algorithm will remove this algorithm.
 * "+" appended with an algorithm will add this algorithm.
 *
 * Check the GnuTLS manual section "Priority strings" for detailed
 * information.
 *
 * Examples:
 *
 * "NONE:+VERS-TLS-ALL:+MAC-ALL:+RSA:+AES-128-CBC:+SIGN-ALL:+COMP-NULL"
 *
 * "NORMAL:-ARCFOUR-128" means normal ciphers except for ARCFOUR-128.
 *
 * "SECURE128:-VERS-SSL3.0:+COMP-DEFLATE" means that only secure ciphers are
 * enabled, SSL3.0 is disabled, and libz compression enabled.
 *
 * "NONE:+VERS-TLS-ALL:+AES-128-CBC:+RSA:+SHA1:+COMP-NULL:+SIGN-RSA-SHA1", 
 *
 * "NONE:+VERS-TLS-ALL:+AES-128-CBC:+ECDHE-RSA:+SHA1:+COMP-NULL:+SIGN-RSA-SHA1:+CURVE-SECP256R1", 
 *
 * "SECURE256:+SECURE128",
 *
 * Note that "NORMAL:%COMPAT" is the most compatible mode.
 *
 * Returns: On syntax error %GNUTLS_E_INVALID_REQUEST is returned,
 * %GNUTLS_E_SUCCESS on success, or an error code.
 **/
int
gnutls_priority_init(gnutls_priority_t * priority_cache,
		     const char *priorities, const char **err_pos)
{
	char *broken_list[MAX_ELEMENTS];
	int broken_list_size = 0, i = 0, j;
	char *darg = NULL;
	unsigned ikeyword_set = 0;
	int algo;
	rmadd_func *fn;
	bulk_rmadd_func *bulk_fn;

	*priority_cache =
	    gnutls_calloc(1, sizeof(struct gnutls_priority_st));
	if (*priority_cache == NULL) {
		gnutls_assert();
		return GNUTLS_E_MEMORY_ERROR;
	}

	if (err_pos)
		*err_pos = priorities;

	/* for now unsafe renegotiation is default on everyone. To be removed
	 * when we make it the default.
	 */
	(*priority_cache)->sr = SR_PARTIAL;
	(*priority_cache)->ssl3_record_version = 1;


	(*priority_cache)->max_empty_records = DEFAULT_MAX_EMPTY_RECORDS;

	if (priorities == NULL)
		priorities = LEVEL_NORMAL;

	darg = gnutls_strdup(priorities);
	if (darg == NULL) {
		gnutls_assert();
		goto error;
	}

	break_comma_list(darg, broken_list, &broken_list_size,
			 MAX_ELEMENTS, ':');
	/* This is our default set of protocol version, certificate types and
	 * compression methods.
	 */
	if (strcasecmp(broken_list[0], LEVEL_NONE) != 0) {
		_set_priority(&(*priority_cache)->protocol,
			      protocol_priority);
		_set_priority(&(*priority_cache)->compression,
			      comp_priority);
		_set_priority(&(*priority_cache)->cert_type,
			      cert_type_priority_default);
		_set_priority(&(*priority_cache)->sign_algo,
			      sign_priority_default);
		_set_priority(&(*priority_cache)->supported_ecc,
			      supported_ecc_normal);
		i = 0;
	} else {
		ikeyword_set = 1;
		i = 1;
	}

	for (; i < broken_list_size; i++) {
		if (check_level(broken_list[i], *priority_cache, ikeyword_set) != 0) {
			ikeyword_set = 1;
			continue;
		} else if (broken_list[i][0] == '!'
			   || broken_list[i][0] == '+'
			   || broken_list[i][0] == '-') {
			if (broken_list[i][0] == '+') {
				fn = prio_add;
				bulk_fn = _add_priority;
			} else {
				fn = prio_remove;
				bulk_fn = _clear_priorities;
			}

			if (broken_list[i][0] == '+'
			    && check_level(&broken_list[i][1],
					   *priority_cache, 1) != 0) {
				continue;
			} else if ((algo =
				    gnutls_mac_get_id(&broken_list[i][1]))
				   != GNUTLS_MAC_UNKNOWN)
				fn(&(*priority_cache)->mac, algo);
			else if ((algo =
				  gnutls_cipher_get_id(&broken_list[i][1]))
				 != GNUTLS_CIPHER_UNKNOWN)
				fn(&(*priority_cache)->cipher, algo);
			else if ((algo =
				  gnutls_kx_get_id(&broken_list[i][1])) !=
				 GNUTLS_KX_UNKNOWN)
				fn(&(*priority_cache)->kx, algo);
			else if (strncasecmp
				 (&broken_list[i][1], "VERS-", 5) == 0) {
				if (strncasecmp
				    (&broken_list[i][1], "VERS-TLS-ALL",
				     12) == 0) {
					bulk_fn(&(*priority_cache)->
						protocol,
						protocol_priority);
				} else
				    if (strncasecmp
					(&broken_list[i][1],
					 "VERS-DTLS-ALL", 13) == 0) {
					bulk_fn(&(*priority_cache)->
						protocol,
						dtls_protocol_priority);
				} else {
					if ((algo =
					     gnutls_protocol_get_id
					     (&broken_list[i][6])) !=
					    GNUTLS_VERSION_UNKNOWN)
						fn(&(*priority_cache)->
						   protocol, algo);
					else
						goto error;

				}
			} /* now check if the element is something like -ALGO */
			else if (strncasecmp
				 (&broken_list[i][1], "COMP-", 5) == 0) {
				if (strncasecmp
				    (&broken_list[i][1], "COMP-ALL",
				     8) == 0) {
					bulk_fn(&(*priority_cache)->
						compression,
						comp_priority);
				} else {
					if ((algo =
					     gnutls_compression_get_id
					     (&broken_list[i][6])) !=
					    GNUTLS_COMP_UNKNOWN)
						fn(&(*priority_cache)->
						   compression, algo);
					else
						goto error;
				}
			} /* now check if the element is something like -ALGO */
			else if (strncasecmp
				 (&broken_list[i][1], "CURVE-", 6) == 0) {
				if (strncasecmp
				    (&broken_list[i][1], "CURVE-ALL",
				     9) == 0) {
					bulk_fn(&(*priority_cache)->
						supported_ecc,
						supported_ecc_normal);
				} else {
					if ((algo =
					     _gnutls_ecc_curve_get_id
					     (&broken_list[i][7])) !=
					    GNUTLS_ECC_CURVE_INVALID)
						fn(&(*priority_cache)->
						   supported_ecc, algo);
					else
						goto error;
				}
			} /* now check if the element is something like -ALGO */
			else if (strncasecmp
				 (&broken_list[i][1], "CTYPE-", 6) == 0) {
				if (strncasecmp
				    (&broken_list[i][1], "CTYPE-ALL",
				     9) == 0) {
					bulk_fn(&(*priority_cache)->
						cert_type,
						cert_type_priority_all);
				} else {
					if ((algo =
					     gnutls_certificate_type_get_id
					     (&broken_list[i][7])) !=
					    GNUTLS_CRT_UNKNOWN)
						fn(&(*priority_cache)->
						   cert_type, algo);
					else
						goto error;
				}
			} /* now check if the element is something like -ALGO */
			else if (strncasecmp
				 (&broken_list[i][1], "SIGN-", 5) == 0) {
				if (strncasecmp
				    (&broken_list[i][1], "SIGN-ALL",
				     8) == 0) {
					bulk_fn(&(*priority_cache)->
						sign_algo,
						sign_priority_default);
				} else {
					if ((algo =
					     gnutls_sign_get_id
					     (&broken_list[i][6])) !=
					    GNUTLS_SIGN_UNKNOWN)
						fn(&(*priority_cache)->
						   sign_algo, algo);
					else
						goto error;
				}
			} else
			    if (strncasecmp
				(&broken_list[i][1], "MAC-ALL", 7) == 0) {
				bulk_fn(&(*priority_cache)->mac,
					mac_priority_normal);
			} else
			    if (strncasecmp
				(&broken_list[i][1], "CIPHER-ALL",
				 10) == 0) {
				bulk_fn(&(*priority_cache)->cipher,
					cipher_priority_normal);
			} else
			    if (strncasecmp
				(&broken_list[i][1], "KX-ALL", 6) == 0) {
				bulk_fn(&(*priority_cache)->kx,
					kx_priority_secure);
			} else
				goto error;
		} else if (broken_list[i][0] == '%') {
			if (strcasecmp(&broken_list[i][1], "COMPAT") == 0) {
				ENABLE_COMPAT((*priority_cache));
			} else
			  if (strcasecmp(&broken_list[i][1], "DUMBFW") == 0) {
				(*priority_cache)->dumbfw = 1;
			} else
			    if (strcasecmp
				(&broken_list[i][1],
				 "NO_EXTENSIONS") == 0) {
				(*priority_cache)->no_extensions = 1;
			} else
			    if (strcasecmp
				(&broken_list[i][1],
				 "STATELESS_COMPRESSION") == 0) {
				(*priority_cache)->stateless_compression =
				    1;
			} else
			    if (strcasecmp
				(&broken_list[i][1],
				 "VERIFY_ALLOW_SIGN_RSA_MD5") == 0) {
				prio_add(&(*priority_cache)->sign_algo,
					 GNUTLS_SIGN_RSA_MD5);
				(*priority_cache)->
				    additional_verify_flags |=
				    GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5;
			} else
			    if (strcasecmp
				(&broken_list[i][1],
				 "VERIFY_DISABLE_CRL_CHECKS") == 0) {
				(*priority_cache)->
				    additional_verify_flags |=
				    GNUTLS_VERIFY_DISABLE_CRL_CHECKS;
			} else
			    if (strcasecmp
				(&broken_list[i][1],
				 "SSL3_RECORD_VERSION") == 0)
				(*priority_cache)->ssl3_record_version = 1;
			else if (strcasecmp(&broken_list[i][1],
					    "LATEST_RECORD_VERSION") == 0)
				(*priority_cache)->ssl3_record_version = 0;
			else if (strcasecmp(&broken_list[i][1],
					    "VERIFY_ALLOW_X509_V1_CA_CRT")
				 == 0)
				(*priority_cache)->
				    additional_verify_flags |=
				    GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT;
			else if (strcasecmp
				 (&broken_list[i][1],
				  "UNSAFE_RENEGOTIATION") == 0) {
				(*priority_cache)->sr = SR_UNSAFE;
			} else
			    if (strcasecmp
				(&broken_list[i][1],
				 "SAFE_RENEGOTIATION") == 0) {
				(*priority_cache)->sr = SR_SAFE;
			} else if (strcasecmp(&broken_list[i][1],
					      "PARTIAL_RENEGOTIATION") ==
				   0) {
				(*priority_cache)->sr = SR_PARTIAL;
			} else if (strcasecmp(&broken_list[i][1],
					      "DISABLE_SAFE_RENEGOTIATION")
				   == 0) {
				(*priority_cache)->sr = SR_DISABLED;
			} else if (strcasecmp(&broken_list[i][1],
					      "SERVER_PRECEDENCE") == 0) {
				(*priority_cache)->server_precedence = 1;
			} else if (strcasecmp(&broken_list[i][1],
					      "NEW_PADDING") == 0) {
				(*priority_cache)->new_record_padding = 1;
			} else
				goto error;
		} else
			goto error;
	}

	gnutls_free(darg);
	return 0;

      error:
	if (err_pos != NULL && i < broken_list_size) {
		*err_pos = priorities;
		for (j = 0; j < i; j++) {
			(*err_pos) += strlen(broken_list[j]) + 1;
		}
	}
	gnutls_free(darg);
	gnutls_free(*priority_cache);
	*priority_cache = NULL;

	return GNUTLS_E_INVALID_REQUEST;

}
Esempio n. 3
0
/**
 * gnutls_priority_init:
 * @priority_cache: is a #gnutls_prioritity_t structure.
 * @priorities: is a string describing priorities
 * @err_pos: In case of an error this will have the position in the string the error occurred
 *
 * Sets priorities for the ciphers, key exchange methods, macs and
 * compression methods.
 *
 * The #priorities option allows you to specify a colon
 * separated list of the cipher priorities to enable.
 * Some keywords are defined to provide quick access
 * to common preferences.
 *
 * Unless there is a special need, use the "NORMAL" keyword to
 * apply a reasonable security level, or "NORMAL:%COMPAT" for compatibility.
 *
 * "PERFORMANCE" means all the "secure" ciphersuites are enabled,
 * limited to 128 bit ciphers and sorted by terms of speed
 * performance.
 *
 * "LEGACY" the NORMAL settings for GnuTLS 3.2.x or earlier. There is
 * no verification profile set, and the allowed DH primes are considered
 * weak today.
 *
 * "NORMAL" means all "secure" ciphersuites. The 256-bit ciphers are
 * included as a fallback only.  The ciphers are sorted by security
 * margin.
 *
 * "PFS" means all "secure" ciphersuites that support perfect forward secrecy. 
 * The 256-bit ciphers are included as a fallback only.  
 * The ciphers are sorted by security margin.
 *
 * "SECURE128" means all "secure" ciphersuites of security level 128-bit
 * or more.
 *
 * "SECURE192" means all "secure" ciphersuites of security level 192-bit
 * or more.
 *
 * "SUITEB128" means all the NSA SuiteB ciphersuites with security level
 * of 128.
 *
 * "SUITEB192" means all the NSA SuiteB ciphersuites with security level
 * of 192.
 *
 * "EXPORT" means all ciphersuites are enabled, including the
 * low-security 40 bit ciphers.
 *
 * "NONE" means nothing is enabled.  This disables even protocols and
 * compression methods.
 *
 * "@KEYWORD" The system administrator imposed settings. The provided keywords
 * will be expanded from a configuration-time provided file - default is:
 * /etc/gnutls/default-priorities. Any keywords that follow it, will 
 * be appended to the expanded string. If there is no system string,
 * then the function will fail. The system file should be formatted
 * as "KEYWORD=VALUE", e.g., "SYSTEM=NORMAL:-ARCFOUR-128".
 *
 * Special keywords are "!", "-" and "+".
 * "!" or "-" appended with an algorithm will remove this algorithm.
 * "+" appended with an algorithm will add this algorithm.
 *
 * Check the GnuTLS manual section "Priority strings" for detailed
 * information.
 *
 * Examples:
 *
 * "NONE:+VERS-TLS-ALL:+MAC-ALL:+RSA:+AES-128-CBC:+SIGN-ALL:+COMP-NULL"
 *
 * "NORMAL:-ARCFOUR-128" means normal ciphers except for ARCFOUR-128.
 *
 * "SECURE128:-VERS-SSL3.0:+COMP-DEFLATE" means that only secure ciphers are
 * enabled, SSL3.0 is disabled, and libz compression enabled.
 *
 * "NONE:+VERS-TLS-ALL:+AES-128-CBC:+RSA:+SHA1:+COMP-NULL:+SIGN-RSA-SHA1", 
 *
 * "NONE:+VERS-TLS-ALL:+AES-128-CBC:+ECDHE-RSA:+SHA1:+COMP-NULL:+SIGN-RSA-SHA1:+CURVE-SECP256R1", 
 *
 * "SECURE256:+SECURE128",
 *
 * Note that "NORMAL:%COMPAT" is the most compatible mode.
 *
 * Returns: On syntax error %GNUTLS_E_INVALID_REQUEST is returned,
 * %GNUTLS_E_SUCCESS on success, or an error code.
 **/
int
gnutls_priority_init(gnutls_priority_t * priority_cache,
		     const char *priorities, const char **err_pos)
{
	char *broken_list[MAX_ELEMENTS];
	int broken_list_size = 0, i = 0, j;
	char *darg = NULL;
	unsigned ikeyword_set = 0;
	int algo;
	rmadd_func *fn;
	bulk_rmadd_func *bulk_fn;

	if (err_pos)
		*err_pos = priorities;

	*priority_cache =
	    gnutls_calloc(1, sizeof(struct gnutls_priority_st));
	if (*priority_cache == NULL) {
		gnutls_assert();
		return GNUTLS_E_MEMORY_ERROR;
	}

	/* for now unsafe renegotiation is default on everyone. To be removed
	 * when we make it the default.
	 */
	(*priority_cache)->sr = SR_PARTIAL;
	(*priority_cache)->ssl3_record_version = 1;

	(*priority_cache)->max_empty_records = DEFAULT_MAX_EMPTY_RECORDS;

	if (priorities == NULL)
		priorities = "NORMAL";

	darg = resolve_priorities(priorities);
	if (darg == NULL) {
		gnutls_assert();
		goto error;
	}

	break_list(darg, broken_list, &broken_list_size);
	/* This is our default set of protocol version, certificate types and
	 * compression methods.
	 */
	if (strcasecmp(broken_list[0], LEVEL_NONE) != 0) {
		_set_priority(&(*priority_cache)->protocol,
			      protocol_priority);
		_set_priority(&(*priority_cache)->compression,
			      comp_priority);
		_set_priority(&(*priority_cache)->cert_type,
			      cert_type_priority_default);
		_set_priority(&(*priority_cache)->sign_algo,
			      sign_priority_default);
		_set_priority(&(*priority_cache)->supported_ecc,
			      supported_ecc_normal);
		i = 0;
	} else {
		ikeyword_set = 1;
		i = 1;
	}

	for (; i < broken_list_size; i++) {
		if (check_level(broken_list[i], *priority_cache, ikeyword_set) != 0) {
			ikeyword_set = 1;
			continue;
		} else if (broken_list[i][0] == '!'
			   || broken_list[i][0] == '+'
			   || broken_list[i][0] == '-') {
			if (broken_list[i][0] == '+') {
				fn = prio_add;
				bulk_fn = _add_priority;
			} else {
				fn = prio_remove;
				bulk_fn = _clear_priorities;
			}

			if (broken_list[i][0] == '+'
			    && check_level(&broken_list[i][1],
					   *priority_cache, 1) != 0) {
				continue;
			} else if ((algo =
				    gnutls_mac_get_id(&broken_list[i][1]))
				   != GNUTLS_MAC_UNKNOWN)
				fn(&(*priority_cache)->mac, algo);
			else if ((algo =
				  gnutls_cipher_get_id(&broken_list[i][1]))
				 != GNUTLS_CIPHER_UNKNOWN)
				fn(&(*priority_cache)->cipher, algo);
			else if ((algo =
				  gnutls_kx_get_id(&broken_list[i][1])) !=
				 GNUTLS_KX_UNKNOWN)
				fn(&(*priority_cache)->kx, algo);
			else if (strncasecmp
				 (&broken_list[i][1], "VERS-", 5) == 0) {
				if (strncasecmp
				    (&broken_list[i][1], "VERS-TLS-ALL",
				     12) == 0) {
					bulk_fn(&(*priority_cache)->
						protocol,
						protocol_priority);
				} else
				    if (strncasecmp
					(&broken_list[i][1],
					 "VERS-DTLS-ALL", 13) == 0) {
					bulk_fn(&(*priority_cache)->
						protocol,
						dtls_protocol_priority);
				} else {
					if ((algo =
					     gnutls_protocol_get_id
					     (&broken_list[i][6])) !=
					    GNUTLS_VERSION_UNKNOWN)
						fn(&(*priority_cache)->
						   protocol, algo);
					else
						goto error;

				}
			} /* now check if the element is something like -ALGO */
			else if (strncasecmp
				 (&broken_list[i][1], "COMP-", 5) == 0) {
				if (strncasecmp
				    (&broken_list[i][1], "COMP-ALL",
				     8) == 0) {
					bulk_fn(&(*priority_cache)->
						compression,
						comp_priority);
				} else {
					if ((algo =
					     gnutls_compression_get_id
					     (&broken_list[i][6])) !=
					    GNUTLS_COMP_UNKNOWN)
						fn(&(*priority_cache)->
						   compression, algo);
					else
						goto error;
				}
			} /* now check if the element is something like -ALGO */
			else if (strncasecmp
				 (&broken_list[i][1], "CURVE-", 6) == 0) {
				if (strncasecmp
				    (&broken_list[i][1], "CURVE-ALL",
				     9) == 0) {
					bulk_fn(&(*priority_cache)->
						supported_ecc,
						supported_ecc_normal);
				} else {
					if ((algo =
					     _gnutls_ecc_curve_get_id
					     (&broken_list[i][7])) !=
					    GNUTLS_ECC_CURVE_INVALID)
						fn(&(*priority_cache)->
						   supported_ecc, algo);
					else
						goto error;
				}
			} /* now check if the element is something like -ALGO */
			else if (strncasecmp
				 (&broken_list[i][1], "CTYPE-", 6) == 0) {
				if (strncasecmp
				    (&broken_list[i][1], "CTYPE-ALL",
				     9) == 0) {
					bulk_fn(&(*priority_cache)->
						cert_type,
						cert_type_priority_all);
				} else {
					if ((algo =
					     gnutls_certificate_type_get_id
					     (&broken_list[i][7])) !=
					    GNUTLS_CRT_UNKNOWN)
						fn(&(*priority_cache)->
						   cert_type, algo);
					else
						goto error;
				}
			} /* now check if the element is something like -ALGO */
			else if (strncasecmp
				 (&broken_list[i][1], "SIGN-", 5) == 0) {
				if (strncasecmp
				    (&broken_list[i][1], "SIGN-ALL",
				     8) == 0) {
					bulk_fn(&(*priority_cache)->
						sign_algo,
						sign_priority_default);
				} else {
					if ((algo =
					     gnutls_sign_get_id
					     (&broken_list[i][6])) !=
					    GNUTLS_SIGN_UNKNOWN)
						fn(&(*priority_cache)->
						   sign_algo, algo);
					else
						goto error;
				}
			} else
			    if (strncasecmp
				(&broken_list[i][1], "MAC-ALL", 7) == 0) {
				bulk_fn(&(*priority_cache)->mac,
					mac_priority_normal);
			} else
			    if (strncasecmp
				(&broken_list[i][1], "CIPHER-ALL",
				 10) == 0) {
				bulk_fn(&(*priority_cache)->cipher,
					cipher_priority_normal);
			} else
			    if (strncasecmp
				(&broken_list[i][1], "KX-ALL", 6) == 0) {
				bulk_fn(&(*priority_cache)->kx,
					kx_priority_secure);
			} else
				goto error;
		} else if (broken_list[i][0] == '%') {
			const struct priority_options_st * o;
			/* to add a new option modify
			 * priority_options.gperf */
			o = in_word_set(&broken_list[i][1], strlen(&broken_list[i][1]));
			if (o == NULL) {
				goto error;
			}
			o->func(*priority_cache);
		} else
			goto error;
	}

	free(darg);
	return 0;

      error:
	if (err_pos != NULL && i < broken_list_size) {
		*err_pos = priorities;
		for (j = 0; j < i; j++) {
			(*err_pos) += strlen(broken_list[j]) + 1;
		}
	}
	free(darg);
	gnutls_free(*priority_cache);
	*priority_cache = NULL;

	return GNUTLS_E_INVALID_REQUEST;

}