]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
priority: refactor config file parsing
authorDaiki Ueno <ueno@gnu.org>
Wed, 5 May 2021 14:27:55 +0000 (16:27 +0200)
committerDaiki Ueno <ueno@gnu.org>
Wed, 24 Nov 2021 12:18:04 +0000 (13:18 +0100)
This adds the following refactoring:

- avoid side-effects during parsing the config file, by separating
  application phase; the parsed configuration can be applied globally
  with cfg_apply, after validation
- make _gnutls_*_mark_{disabled,insecure} take an ID instead of the
  name

Signed-off-by: Daiki Ueno <ueno@gnu.org>
lib/algorithms.h
lib/algorithms/ecc.c
lib/algorithms/mac.c
lib/algorithms/protocols.c
lib/algorithms/sign.c
lib/priority.c

index 5172bd2784a38942fea90f24e83fd546cf54dab8..2f5366db6bc647031356a1c6ce1999b26129e34d 100644 (file)
@@ -345,11 +345,11 @@ typedef enum hash_security_level_t {
        _INSECURE
 } hash_security_level_t;
 
-int _gnutls_ecc_curve_mark_disabled(const char *name);
-int _gnutls_sign_mark_insecure(const char *name, hash_security_level_t);
-int _gnutls_digest_mark_insecure(const char *name);
+int _gnutls_ecc_curve_mark_disabled(gnutls_ecc_curve_t curve);
+int _gnutls_sign_mark_insecure(gnutls_sign_algorithm_t, hash_security_level_t);
+int _gnutls_digest_mark_insecure(gnutls_digest_algorithm_t dig);
 unsigned _gnutls_digest_is_insecure(gnutls_digest_algorithm_t dig);
-int _gnutls_version_mark_disabled(const char *name);
+int _gnutls_version_mark_disabled(gnutls_protocol_t version);
 gnutls_protocol_t _gnutls_protocol_get_id_if_supported(const char *name);
 
 #define GNUTLS_SIGN_FLAG_TLS13_OK      1 /* if it is ok to use under TLS1.3 */
index adab4e1c185d3c60883da927d271b2c3e87012aa..f5fb8ac76f5afa78c0f91c4e2b3fa56a3f54f134 100644 (file)
@@ -353,12 +353,12 @@ gnutls_ecc_curve_t gnutls_ecc_curve_get_id(const char *name)
        return ret;
 }
 
-int _gnutls_ecc_curve_mark_disabled(const char *name)
+int _gnutls_ecc_curve_mark_disabled(gnutls_ecc_curve_t curve)
 {
        gnutls_ecc_curve_entry_st *p;
 
        for(p = ecc_curves; p->name != NULL; p++) {
-               if (c_strcasecmp(p->name, name) == 0) {
+               if (p->id == curve) {
                        p->supported = 0;
                        return 0;
                }
index 518323bca1859f74d96c521f74d75ccfc72c66bb..57dfca95dee9046d652251787b576f15ab252a9c 100644 (file)
@@ -291,13 +291,13 @@ gnutls_digest_algorithm_t gnutls_digest_get_id(const char *name)
        return ret;
 }
 
-int _gnutls_digest_mark_insecure(const char *name)
+int _gnutls_digest_mark_insecure(gnutls_digest_algorithm_t dig)
 {
 #ifndef DISABLE_SYSTEM_CONFIG
        mac_entry_st *p;
 
        for(p = hash_algorithms; p->name != NULL; p++) {
-               if (p->oid != NULL && c_strcasecmp(p->name, name) == 0) {
+               if (p->oid != NULL && p->id == (gnutls_mac_algorithm_t)dig) {
                        p->flags |= GNUTLS_MAC_FLAG_PREIMAGE_INSECURE;
                        return 0;
                }
index a3d1edeb6c358a2fa22aa998cfba0be5c2c82865..4283cd0388a2e36736e021525380b45b02add96a 100644 (file)
@@ -198,13 +198,13 @@ version_is_valid_for_session(gnutls_session_t session,
        return 0;
 }
 
-int _gnutls_version_mark_disabled(const char *name)
+int _gnutls_version_mark_disabled(gnutls_protocol_t version)
 {
 #ifndef DISABLE_SYSTEM_CONFIG
        version_entry_st *p;
 
        for (p = sup_versions; p->name != NULL; p++)
-               if (c_strcasecmp(p->name, name) == 0) {
+               if (p->id == version) {
                        p->supported = 0;
                        return 0;
                }
index 2728a54478af17ca41f1e0ad489330493992f1fe..4c5619454fd6443779bcb0a6ca8da07c9132d2eb 100644 (file)
@@ -462,7 +462,7 @@ bool _gnutls_sign_is_secure2(const gnutls_sign_entry_st *se, unsigned int flags)
                return (se->slevel==_SECURE || se->slevel == _INSECURE_FOR_CERTS)?1:0;
 }
 
-int _gnutls_sign_mark_insecure(const char *name, hash_security_level_t level)
+int _gnutls_sign_mark_insecure(gnutls_sign_algorithm_t sign, hash_security_level_t level)
 {
 #ifndef DISABLE_SYSTEM_CONFIG
        gnutls_sign_entry_st *p;
@@ -471,7 +471,7 @@ int _gnutls_sign_mark_insecure(const char *name, hash_security_level_t level)
                return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
 
        for(p = sign_algorithms; p->name != NULL; p++) {
-               if (c_strcasecmp(p->name, name) == 0) {
+               if (p->id && p->id == sign) {
                                p->slevel = level;
                        return 0;
                }
index 2851d514743e623aefeb270d69f67f70393f890a..4ad5c9046245a0c67e152c01901d88788b3576d8 100644 (file)
@@ -1059,15 +1059,112 @@ static char *clear_spaces(const char *str, char out[MAX_ALGO_NAME])
        return out;
 }
 
-/* This function parses a gnutls configuration file and updates internal
- * settings accordingly.
+struct cfg {
+       name_val_array_t priority_strings;
+       char *default_priority_string;
+       gnutls_certificate_verification_profiles_t verification_profile;
+
+       gnutls_cipher_algorithm_t ciphers[MAX_ALGOS+1];
+       gnutls_mac_algorithm_t macs[MAX_ALGOS+1];
+       gnutls_group_t groups[MAX_ALGOS+1];
+       gnutls_kx_algorithm_t kxs[MAX_ALGOS+1];
+
+       gnutls_digest_algorithm_t *hashes;
+       size_t hashes_size;
+       gnutls_sign_algorithm_t *sigs;
+       size_t sigs_size;
+       gnutls_sign_algorithm_t *sigs_for_cert;
+       size_t sigs_for_cert_size;
+       gnutls_protocol_t *versions;
+       size_t versions_size;
+       gnutls_ecc_curve_t *curves;
+       size_t curves_size;
+};
+
+static inline void
+cfg_deinit(struct cfg *cfg)
+{
+       if (cfg->priority_strings) {
+               _name_val_array_clear(&cfg->priority_strings);
+       }
+       gnutls_free(cfg->default_priority_string);
+       gnutls_free(cfg->hashes);
+       gnutls_free(cfg->sigs);
+       gnutls_free(cfg->sigs_for_cert);
+       gnutls_free(cfg->versions);
+       gnutls_free(cfg->curves);
+}
+
+static inline int
+cfg_apply(struct cfg *cfg)
+{
+       size_t i;
+
+       system_wide_verification_profile = cfg->verification_profile;
+
+       system_wide_priority_strings = cfg->priority_strings;
+       cfg->priority_strings = NULL;
+
+       if (cfg->default_priority_string) {
+               _clear_default_system_priority();
+               _gnutls_default_priority_string = cfg->default_priority_string;
+               cfg->default_priority_string = NULL;
+               system_wide_default_priority_string = 1;
+       }
+
+       memcpy(system_wide_disabled_ciphers, cfg->ciphers, sizeof(cfg->ciphers));
+       memcpy(system_wide_disabled_macs, cfg->macs, sizeof(cfg->macs));
+       memcpy(system_wide_disabled_groups, cfg->groups, sizeof(cfg->groups));
+       memcpy(system_wide_disabled_kxs, cfg->kxs, sizeof(cfg->kxs));
+
+       for (i = 0; i < cfg->hashes_size; i++) {
+               int ret = _gnutls_digest_mark_insecure(cfg->hashes[i]);
+               if (unlikely(ret < 0)) {
+                       return ret;
+               }
+       }
+
+       for (i = 0; i < cfg->sigs_size; i++) {
+               int ret = _gnutls_sign_mark_insecure(cfg->sigs[i], _INSECURE);
+               if (unlikely(ret < 0)) {
+                       return ret;
+               }
+       }
+
+       for (i = 0; i < cfg->sigs_for_cert_size; i++) {
+               int ret = _gnutls_sign_mark_insecure(cfg->sigs_for_cert[i], _INSECURE_FOR_CERTS);
+               if (unlikely(ret < 0)) {
+                       return ret;
+               }
+       }
+
+       for (i = 0; i < cfg->versions_size; i++) {
+               int ret = _gnutls_version_mark_disabled(cfg->versions[i]);
+               if (unlikely(ret < 0)) {
+                       return ret;
+               }
+       }
+
+       for (i = 0; i < cfg->curves_size; i++) {
+               int ret = _gnutls_ecc_curve_mark_disabled(cfg->curves[i]);
+               if (unlikely(ret < 0)) {
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+/* This function parses a gnutls configuration file.  Updating internal settings
+ * according to the parsed configuration is done by cfg_apply.
  */
-static int cfg_ini_handler(void *_ctx, const char *section, const char *name, const char *value)
+static int cfg_ini_handler(void *ctx, const char *section, const char *name, const char *value)
 {
        char *p;
-       int ret, type;
+       int ret;
        unsigned i;
        char str[MAX_ALGO_NAME];
+       struct cfg *cfg = ctx;
 
        /* Note that we intentionally overwrite the value above; inih does
         * not use that value after we handle it. */
@@ -1076,86 +1173,178 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co
        if (section == NULL || section[0] == 0 || c_strcasecmp(section, CUSTOM_PRIORITY_SECTION)==0) {
                _gnutls_debug_log("cfg: adding priority: %s -> %s\n", name, value);
 
-               ret = _name_val_array_append(&system_wide_config.priority_strings, name, value);
+               ret = _name_val_array_append(&cfg->priority_strings, name, value);
                if (ret < 0)
                        return 0;
        } else if (c_strcasecmp(section, OVERRIDES_SECTION)==0) {
                if (c_strcasecmp(name, "default-priority-string")==0) {
-                       _clear_default_system_priority();
+                       if (cfg->default_priority_string) {
+                               gnutls_free(cfg->default_priority_string);
+                               cfg->default_priority_string = NULL;
+                       }
                        p = clear_spaces(value, str);
                        _gnutls_debug_log("cfg: setting default-priority-string to %s\n", p);
                        if (strlen(p) > 0) {
-                               _gnutls_default_priority_string = gnutls_strdup(p);
-                               if (!_gnutls_default_priority_string) {
-                                       _gnutls_default_priority_string = DEFAULT_PRIORITY_STRING;
+                               cfg->default_priority_string = gnutls_strdup(p);
+                               if (!cfg->default_priority_string) {
                                        _gnutls_debug_log("cfg: failed setting default-priority-string\n");
                                        return 0;
                                }
-                               system_wide_config.default_priority_string = 1;
                        } else {
                                _gnutls_debug_log("cfg: empty default-priority-string, using default\n");
                                if (fail_on_invalid_config)
                                        return 0;
                        }
                } else if (c_strcasecmp(name, "insecure-hash")==0) {
+                       gnutls_digest_algorithm_t dig, *tmp;
+
                        p = clear_spaces(value, str);
 
                        _gnutls_debug_log("cfg: marking hash %s as insecure\n",
                                          p);
 
-                       ret = _gnutls_digest_mark_insecure(p);
-                       if (ret < 0) {
+                       dig = gnutls_digest_get_id(p);
+                       if (dig == GNUTLS_DIG_UNKNOWN) {
                                _gnutls_debug_log("cfg: found unknown hash %s in %s\n",
                                                  p, name);
                                if (fail_on_invalid_config)
                                        return 0;
+                               goto exit;
                        }
-               } else if (c_strcasecmp(name, "insecure-sig")==0 || c_strcasecmp(name, "insecure-sig-for-cert")==0) {
+                       tmp = _gnutls_reallocarray(cfg->hashes,
+                                                  cfg->hashes_size + 1,
+                                                  sizeof(gnutls_digest_algorithm_t));
+                       if (!tmp) {
+                               _gnutls_debug_log("cfg: failed marking hash %s as insecure\n",
+                                                 p);
+                               if (fail_on_invalid_config)
+                                       return 0;
+                               goto exit;
+                       }
+
+                       cfg->hashes = tmp;
+                       cfg->hashes[cfg->hashes_size] = dig;
+                       cfg->hashes_size++;
+               } else if (c_strcasecmp(name, "insecure-sig")==0) {
+                       gnutls_sign_algorithm_t sig, *tmp;
+
                        p = clear_spaces(value, str);
 
-                       if (c_strcasecmp(name, "insecure-sig")==0) {
-                               type = _INSECURE;
-                               _gnutls_debug_log("cfg: marking signature %s as insecure\n",
-                                                 p);
-                       } else {
-                               _gnutls_debug_log("cfg: marking signature %s as insecure for certs\n",
+                       _gnutls_debug_log("cfg: marking signature %s as insecure\n",
+                                         p);
+
+                       sig = gnutls_sign_get_id(p);
+                       if (sig == GNUTLS_SIGN_UNKNOWN) {
+                               _gnutls_debug_log("cfg: found unknown signature algorithm %s in %s\n",
+                                                 p, name);
+                               if (fail_on_invalid_config)
+                                       return 0;
+                               goto exit;
+                       }
+                       tmp = _gnutls_reallocarray(cfg->sigs,
+                                                  cfg->sigs_size + 1,
+                                                  sizeof(gnutls_sign_algorithm_t));
+                       if (!tmp) {
+                               _gnutls_debug_log("cfg: failed marking signature %s as insecure\n",
                                                  p);
-                               type = _INSECURE_FOR_CERTS;
+                               if (fail_on_invalid_config)
+                                       return 0;
+                               goto exit;
                        }
 
-                       ret = _gnutls_sign_mark_insecure(p, type);
-                       if (ret < 0) {
+                       cfg->sigs = tmp;
+                       cfg->sigs[cfg->sigs_size] = sig;
+                       cfg->sigs_size++;
+               } else if (c_strcasecmp(name, "insecure-sig-for-cert")==0) {
+                       gnutls_sign_algorithm_t sig, *tmp;
+
+                       p = clear_spaces(value, str);
+
+                       _gnutls_debug_log("cfg: marking signature %s as insecure for certs\n",
+                                         p);
+
+                       sig = gnutls_sign_get_id(p);
+                       if (sig == GNUTLS_SIGN_UNKNOWN) {
                                _gnutls_debug_log("cfg: found unknown signature algorithm %s in %s\n",
                                                  p, name);
                                if (fail_on_invalid_config)
                                        return 0;
+                               goto exit;
+                       }
+                       tmp = _gnutls_reallocarray(cfg->sigs_for_cert,
+                                                  cfg->sigs_for_cert_size + 1,
+                                                  sizeof(gnutls_sign_algorithm_t));
+                       if (!tmp) {
+                               _gnutls_debug_log("cfg: failed marking signature %s as insecure for certs\n",
+                                                 p);
+                               if (fail_on_invalid_config)
+                                       return 0;
+                               goto exit;
                        }
+
+                       cfg->sigs_for_cert = tmp;
+                       cfg->sigs_for_cert[cfg->sigs_for_cert_size] = sig;
+                       cfg->sigs_for_cert_size++;
                } else if (c_strcasecmp(name, "disabled-version")==0) {
+                       gnutls_protocol_t prot, *tmp;
+
                        p = clear_spaces(value, str);
 
                        _gnutls_debug_log("cfg: disabling version %s\n",
                                          p);
 
-                       ret = _gnutls_version_mark_disabled(p);
-                       if (ret < 0) {
+                       prot = gnutls_protocol_get_id(p);
+                       if (prot == GNUTLS_VERSION_UNKNOWN) {
                                _gnutls_debug_log("cfg: found unknown version %s in %s\n",
                                                  p, name);
                                if (fail_on_invalid_config)
                                        return 0;
+                               goto exit;
+                       }
+                       tmp = _gnutls_reallocarray(cfg->versions,
+                                                  cfg->versions_size + 1,
+                                                  sizeof(gnutls_protocol_t));
+                       if (!tmp) {
+                               _gnutls_debug_log("cfg: failed disabling version %s\n",
+                                                 p);
+                               if (fail_on_invalid_config)
+                                       return 0;
+                               goto exit;
                        }
+
+                       cfg->versions = tmp;
+                       cfg->versions[cfg->versions_size] = prot;
+                       cfg->versions_size++;
                } else if (c_strcasecmp(name, "disabled-curve")==0) {
+                       gnutls_ecc_curve_t curve, *tmp;
+
                        p = clear_spaces(value, str);
 
                        _gnutls_debug_log("cfg: disabling curve %s\n",
                                          p);
 
-                       ret = _gnutls_ecc_curve_mark_disabled(p);
-                       if (ret < 0) {
+                       curve = gnutls_ecc_curve_get_id(p);
+                       if (curve == GNUTLS_ECC_CURVE_INVALID) {
                                _gnutls_debug_log("cfg: found unknown curve %s in %s\n",
                                                  p, name);
                                if (fail_on_invalid_config)
                                        return 0;
+                               goto exit;
                        }
+                       tmp = _gnutls_reallocarray(cfg->curves,
+                                                  cfg->curves_size + 1,
+                                                  sizeof(gnutls_ecc_curve_t));
+                       if (!tmp) {
+                               _gnutls_debug_log("cfg: failed disabling curve %s\n",
+                                                 p);
+                               if (fail_on_invalid_config)
+                                       return 0;
+                               goto exit;
+                       }
+
+                       cfg->curves = tmp;
+                       cfg->curves[cfg->curves_size] = curve;
+                       cfg->curves_size++;
                } else if (c_strcasecmp(name, "min-verification-profile")==0) {
                        gnutls_certificate_verification_profiles_t profile;
                        profile = gnutls_certificate_verification_profile_get_id(value);
@@ -1165,28 +1354,29 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co
                                                  value, name);
                                if (fail_on_invalid_config)
                                        return 0;
+                               goto exit;
                        }
 
-                       system_wide_config.verification_profile = profile;
+                       cfg->verification_profile = profile;
                } else if (c_strcasecmp(name, "tls-disabled-cipher")==0) {
-                       unsigned algo;
+                       gnutls_cipher_algorithm_t algo;
 
                        p = clear_spaces(value, str);
 
                        _gnutls_debug_log("cfg: disabling cipher %s for TLS\n",
                                          p);
 
-
                        algo = gnutls_cipher_get_id(p);
-                       if (algo == 0) {
+                       if (algo == GNUTLS_CIPHER_UNKNOWN) {
                                _gnutls_debug_log("cfg: unknown algorithm %s listed at %s\n",
                                                  p, name);
                                if (fail_on_invalid_config)
                                        return 0;
+                               goto exit;
                        }
 
                        i = 0;
-                       while (system_wide_config.disabled_ciphers[i] != 0)
+                       while (cfg->ciphers[i] != 0)
                                i++;
 
                        if (i > MAX_ALGOS-1) {
@@ -1196,11 +1386,11 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co
                                        return 0;
                                goto exit;
                        }
-                       system_wide_config.disabled_ciphers[i] = algo;
-                       system_wide_config.disabled_ciphers[i+1] = 0;
+                       cfg->ciphers[i] = algo;
+                       cfg->ciphers[i+1] = 0;
 
                } else if (c_strcasecmp(name, "tls-disabled-mac")==0) {
-                       unsigned algo;
+                       gnutls_mac_algorithm_t algo;
 
                        p = clear_spaces(value, str);
 
@@ -1217,7 +1407,7 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co
                        }
 
                        i = 0;
-                       while (system_wide_config.disabled_macs[i] != 0)
+                       while (cfg->macs[i] != 0)
                                i++;
 
                        if (i > MAX_ALGOS-1) {
@@ -1227,10 +1417,10 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co
                                        return 0;
                                goto exit;
                        }
-                       system_wide_config.disabled_macs[i] = algo;
-                       system_wide_config.disabled_macs[i+1] = 0;
+                       cfg->macs[i] = algo;
+                       cfg->macs[i+1] = 0;
                } else if (c_strcasecmp(name, "tls-disabled-group")==0) {
-                       unsigned algo;
+                       gnutls_group_t algo;
 
                        p = clear_spaces(value, str);
 
@@ -1250,7 +1440,7 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co
                        }
 
                        i = 0;
-                       while (system_wide_config.disabled_groups[i] != 0)
+                       while (cfg->groups[i] != 0)
                                i++;
 
                        if (i > MAX_ALGOS-1) {
@@ -1260,8 +1450,8 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co
                                        return 0;
                                goto exit;
                        }
-                       system_wide_config.disabled_groups[i] = algo;
-                       system_wide_config.disabled_groups[i+1] = 0;
+                       cfg->groups[i] = algo;
+                       cfg->groups[i+1] = 0;
                } else if (c_strcasecmp(name, "tls-disabled-kx")==0) {
                        unsigned algo;
 
@@ -1280,7 +1470,7 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co
                        }
 
                        i = 0;
-                       while (system_wide_config.disabled_kxs[i] != 0)
+                       while (cfg->kxs[i] != 0)
                                i++;
 
                        if (i > MAX_ALGOS-1) {
@@ -1290,8 +1480,8 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co
                                        return 0;
                                goto exit;
                        }
-                       system_wide_config.disabled_kxs[i] = algo;
-                       system_wide_config.disabled_kxs[i+1] = 0;
+                       cfg->kxs[i] = algo;
+                       cfg->kxs[i+1] = 0;
                } else {
                        _gnutls_debug_log("unknown parameter %s\n", name);
                        if (fail_on_invalid_config)
@@ -1313,6 +1503,7 @@ static int _gnutls_update_system_priorities(void)
        int ret, err = 0;
        struct stat sb;
        FILE *fp;
+       struct cfg cfg;
 
        ret = gnutls_rwlock_rdlock(&system_wide_config_rwlock);
        if (ret < 0) {
@@ -1357,13 +1548,17 @@ static int _gnutls_update_system_priorities(void)
                                  system_priority_file, errno);
                goto out;
        }
-       err = ini_parse_file(fp, cfg_ini_handler, NULL);
+       memset(&cfg, 0, sizeof(cfg));
+       err = ini_parse_file(fp, cfg_ini_handler, &cfg);
        fclose(fp);
        if (err) {
+               cfg_deinit(&cfg);
                _gnutls_debug_log("cfg: unable to parse: %s: %d\n",
                                  system_priority_file, err);
                goto out;
        }
+       cfg_apply(&cfg);
+       cfg_deinit(&cfg);
 
        _gnutls_debug_log("cfg: loaded system priority %s mtime %lld\n",
                          system_priority_file,