From c08ecfd5fe40725951cd9cf49b20af0b9ff9b15c Mon Sep 17 00:00:00 2001 From: Tomas Korbar Date: Wed, 8 Jan 2025 11:21:46 +0100 Subject: [PATCH] dns: Add resolve-mode and certification-authority keys to global-dns Resolve-mode allows user to specify way how the global-dns domains and DNS connection information should be merged and used. Certification-authority allows user to specify certification authority that should be used to verify certificates of encrypted DNS servers. --- NEWS | 5 ++ man/NetworkManager.conf.xml | 23 +++++++ src/core/nm-config-data.c | 115 ++++++++++++++++++++++++++++++-- src/core/nm-config-data.h | 10 +++ src/core/nm-config.c | 4 +- src/libnm-base/nm-config-base.h | 6 +- 6 files changed, 154 insertions(+), 9 deletions(-) diff --git a/NEWS b/NEWS index 6f57b415d..6309d535a 100644 --- a/NEWS +++ b/NEWS @@ -32,6 +32,11 @@ USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE! * The initrd-generator understands the "rd.net.dns" option to configure global name servers. * Drop support for the "dhcpcanon" DHCP client. +* global-dns configuration section now has 2 additional keys: "resolve-mode" + and "certification-authority". +* Dnsconfd plugin can now be used for configuration of system-wide DNS + caching resolver. If dnsconfd plugin is enabled and ipvX.routed-dns is + set to -1 then adding routes is by default enabled. ============================================= NetworkManager-1.50 diff --git a/man/NetworkManager.conf.xml b/man/NetworkManager.conf.xml index 70ddea387..0363dd863 100644 --- a/man/NetworkManager.conf.xml +++ b/man/NetworkManager.conf.xml @@ -1547,6 +1547,29 @@ managed=1 + + resolve-mode + + + String indicating how DNS servers retrieved from global configuration and connections + should be used. backup - Indicates that they can be freely merged + and used for the same purposes. prefer - Forbids DNS servers + retrieved from connections to be used for general queries that are not subdomains of + domains set by connection. exclusive - Forbids use of connection + DNS servers for any query. Currently relevant only for Dnsconfd plugin. + + + + + certification-authority + + + String specifying absolute path to bundle of CA certificates that must be used for + validation of certificates presented by DNS servers when encrypted DNS is used. + Currently relevant only for Dnsconfd plugin. + + + diff --git a/src/core/nm-config-data.c b/src/core/nm-config-data.c index e40492851..5a84a2c89 100644 --- a/src/core/nm-config-data.c +++ b/src/core/nm-config-data.c @@ -46,11 +46,13 @@ struct _NMGlobalDnsDomain { }; struct _NMGlobalDnsConfig { - char **searches; - char **options; - GHashTable *domains; - const char **domain_list; - gboolean internal; + char **searches; + char **options; + GHashTable *domains; + const char **domain_list; + gboolean internal; + char *cert_authority; + NMDnsResolveMode resolve_mode; }; /*****************************************************************************/ @@ -955,6 +957,22 @@ nm_global_dns_config_get_options(const NMGlobalDnsConfig *dns_config) return (const char *const *) dns_config->options; } +const char * +nm_global_dns_config_get_certification_authority(const NMGlobalDnsConfig *dns_config) +{ + g_return_val_if_fail(dns_config, NULL); + + return (const char *) dns_config->cert_authority; +} + +guint +nm_global_dns_config_get_resolve_mode(const NMGlobalDnsConfig *dns_config) +{ + g_return_val_if_fail(dns_config, 0); + + return dns_config->resolve_mode; +} + guint nm_global_dns_config_get_num_domains(const NMGlobalDnsConfig *dns_config) { @@ -1155,6 +1173,9 @@ nm_global_dns_config_free(NMGlobalDnsConfig *dns_config) g_free(dns_config->domain_list); if (dns_config->domains) g_hash_table_unref(dns_config->domains); + if (dns_config->cert_authority) { + g_free(dns_config->cert_authority); + } g_free(dns_config); } } @@ -1180,6 +1201,29 @@ global_dns_config_seal_domains(NMGlobalDnsConfig *dns_config) dns_config->domain_list = nm_strdict_get_keys(dns_config->domains, TRUE, NULL); } +static const char * +nm_dns_resolve_mode_to_string(const NMDnsResolveMode mode) +{ + const char *to_string[3] = {"backup", "prefer", "exclusive"}; + + nm_assert(mode < _NM_NUM_DNS_RESOLVE_MODES); + + return to_string[mode]; +} + +static NMDnsResolveMode +nm_dns_resolve_mode_from_string(const char *string) +{ + if (nm_streq0(string, "backup")) { + return NM_DNS_RESOLVE_MODE_BACKUP; + } else if (nm_streq0(string, "prefer")) { + return NM_DNS_RESOLVE_MODE_PREFER; + } else if (nm_streq0(string, "exclusive")) { + return NM_DNS_RESOLVE_MODE_EXCLUSIVE; + } + return _NM_NUM_DNS_RESOLVE_MODES; +} + static NMGlobalDnsConfig * load_global_dns(GKeyFile *keyfile, gboolean internal) { @@ -1189,6 +1233,9 @@ load_global_dns(GKeyFile *keyfile, gboolean internal) int g, i, j, domain_prefix_len; gboolean default_found = FALSE; char **strv; + gs_free char *cert_authority = NULL; + gs_free char *resolve_mode = NULL; + NMDnsResolveMode parsed_resolve_mode; if (internal) { group = NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS; @@ -1202,7 +1249,31 @@ load_global_dns(GKeyFile *keyfile, gboolean internal) if (!nm_config_keyfile_has_global_dns_config(keyfile, internal)) return NULL; - dns_config = g_malloc0(sizeof(NMGlobalDnsConfig)); + dns_config = g_malloc0(sizeof(NMGlobalDnsConfig)); + + cert_authority = g_key_file_get_string(keyfile, + group, + NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_CERTIFICATION_AUTHORITY, + NULL); + if (cert_authority) { + dns_config->cert_authority = g_steal_pointer(&cert_authority); + } + + resolve_mode = + g_key_file_get_string(keyfile, group, NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_RESOLVE_MODE, NULL); + + if (resolve_mode) { + parsed_resolve_mode = nm_dns_resolve_mode_from_string(resolve_mode); + if (parsed_resolve_mode == _NM_NUM_DNS_RESOLVE_MODES) { + nm_log_dbg(LOGD_CORE, + "%s global DNS configuration has invalid value '%s', assuming backup", + internal ? "internal" : "user", + NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_RESOLVE_MODE); + } else { + dns_config->resolve_mode = parsed_resolve_mode; + } + } + dns_config->domains = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, @@ -1344,6 +1415,21 @@ nm_global_dns_config_to_dbus(const NMGlobalDnsConfig *dns_config, GValue *value) g_variant_new_strv((const char *const *) dns_config->options, -1)); } + if (dns_config->cert_authority) { + g_variant_builder_add(&conf_builder, + "{sv}", + "certification-authority", + g_variant_new("s", dns_config->cert_authority)); + } + + if (dns_config->resolve_mode) { + g_variant_builder_add( + &conf_builder, + "{sv}", + "resolve-mode", + g_variant_new("s", nm_dns_resolve_mode_to_string(dns_config->resolve_mode))); + } + g_variant_builder_init(&domains_builder, G_VARIANT_TYPE("a{sv}")); if (dns_config->domain_list) { for (i = 0; dns_config->domain_list[i]; i++) { @@ -1444,6 +1530,7 @@ nm_global_dns_config_from_dbus(const GValue *value, GError **error) GVariantIter iter; char **strv, *key; int i, j; + gs_free char *resolve_mode_buffer = NULL; if (!G_VALUE_HOLDS_VARIANT(value)) { g_set_error(error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED, "invalid value type"); @@ -1499,7 +1586,23 @@ nm_global_dns_config_from_dbus(const GValue *value, GError **error) } g_variant_unref(v); } + } else if (nm_streq0(key, "resolve-mode") + && g_variant_is_of_type(val, G_VARIANT_TYPE("s"))) { + g_variant_get(val, "s", &resolve_mode_buffer); + dns_config->resolve_mode = nm_dns_resolve_mode_from_string(resolve_mode_buffer); + if (dns_config->resolve_mode == _NM_NUM_DNS_RESOLVE_MODES) { + g_set_error_literal(error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_FAILED, + "Global DNS configuration contains invalid resolve-mode"); + nm_global_dns_config_free(dns_config); + return NULL; + } + } else if (nm_streq0(key, "certification-authority") + && g_variant_is_of_type(val, G_VARIANT_TYPE("s"))) { + g_variant_get(val, "s", &dns_config->cert_authority); } + g_variant_unref(val); } diff --git a/src/core/nm-config-data.h b/src/core/nm-config-data.h index 1ea0ccf25..d764a7c45 100644 --- a/src/core/nm-config-data.h +++ b/src/core/nm-config-data.h @@ -120,6 +120,13 @@ typedef enum { } NMConfigChangeFlags; +typedef enum { + NM_DNS_RESOLVE_MODE_BACKUP = 0, + NM_DNS_RESOLVE_MODE_PREFER = 1, + NM_DNS_RESOLVE_MODE_EXCLUSIVE = 2, + _NM_NUM_DNS_RESOLVE_MODES = 3 +} NMDnsResolveMode; + typedef struct _NMConfigDataClass NMConfigDataClass; typedef struct _NMGlobalDnsConfig NMGlobalDnsConfig; @@ -269,6 +276,9 @@ GKeyFile *nm_config_data_clone_keyfile_intern(const NMConfigData *self); const char *const *nm_global_dns_config_get_searches(const NMGlobalDnsConfig *dns_config); const char *const *nm_global_dns_config_get_options(const NMGlobalDnsConfig *dns_config); +const char *nm_global_dns_config_get_certification_authority(const NMGlobalDnsConfig *dns_config); +guint nm_global_dns_config_get_resolve_mode(const NMGlobalDnsConfig *dns_config); + guint nm_global_dns_config_get_num_domains(const NMGlobalDnsConfig *dns_config); NMGlobalDnsDomain *nm_global_dns_config_get_domain(const NMGlobalDnsConfig *dns_config, guint i); NMGlobalDnsDomain *nm_global_dns_config_lookup_domain(const NMGlobalDnsConfig *dns_config, diff --git a/src/core/nm-config.c b/src/core/nm-config.c index c6dc78d88..a55d2d139 100644 --- a/src/core/nm-config.c +++ b/src/core/nm-config.c @@ -905,7 +905,9 @@ static const ConfigGroup config_groups[] = { { .group = NM_CONFIG_KEYFILE_GROUP_GLOBAL_DNS, .keys = NM_MAKE_STRV(NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_OPTIONS, - NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_SEARCHES, ), + NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_SEARCHES, + NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_CERTIFICATION_AUTHORITY, + NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_RESOLVE_MODE, ), }, { .group = NM_CONFIG_KEYFILE_GROUPPREFIX_GLOBAL_DNS_DOMAIN, diff --git a/src/libnm-base/nm-config-base.h b/src/libnm-base/nm-config-base.h index 362a183c9..d101c95c8 100644 --- a/src/libnm-base/nm-config-base.h +++ b/src/libnm-base/nm-config-base.h @@ -55,8 +55,10 @@ #define NM_CONFIG_KEYFILE_KEY_IFUPDOWN_MANAGED "managed" -#define NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_SEARCHES "searches" -#define NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_OPTIONS "options" +#define NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_SEARCHES "searches" +#define NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_OPTIONS "options" +#define NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_CERTIFICATION_AUTHORITY "certification-authority" +#define NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_RESOLVE_MODE "resolve-mode" #define NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_DOMAIN_SERVERS "servers" #define NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_DOMAIN_OPTIONS "options"