From 98581d80e7fc3ff9b176333831e92e6f4747bc64 Mon Sep 17 00:00:00 2001 From: Tomas Halman Date: Wed, 23 Oct 2024 17:26:14 +0200 Subject: [PATCH] Add DoT support for DNS updates DNS-over-TLS is a new standard for encrypting DNS traffic. SSSD does not implement the DoT itself but relies on other components of the system. This modification allows as to set a DoT for dynamic DNS updates :config: the `dyndns_server` option is extended so it can be in form of URI (dns+tls://1.2.3.4:853#servername). New set of options `dyndns_dot_cacert`, `dyndns_dot_cert` and `dyndns_dot_key` allows to configure DNS-over-TLS communication. :relnote: The DoT for dynamic DNS updates is supported now. It requires new version of `nsupdate` from BIND 9.20.1+ --- src/config/SSSDConfig/sssdoptions.py | 3 + src/config/SSSDConfigTest.py | 6 ++ src/config/cfg_rules.ini | 3 + src/config/etc/sssd.api.conf | 3 + src/man/sssd-ad.5.xml | 68 ++++++++++++- src/man/sssd-ipa.5.xml | 68 ++++++++++++- src/providers/ad/ad_opts.c | 3 + src/providers/be_dyndns.c | 139 ++++++++++++++++++++++++--- src/providers/be_dyndns.h | 20 +++- src/providers/ipa/ipa_opts.c | 3 + src/providers/ldap/sdap_dyndns.c | 54 ++++++++--- src/tests/cmocka/test_dyndns.c | 17 +++- src/tests/cmocka/test_utils.c | 60 ++++++++++++ src/util/child_common.c | 27 ++++++ src/util/util.c | 80 +++++++++++++++ src/util/util.h | 16 +++ 16 files changed, 530 insertions(+), 40 deletions(-) diff --git a/src/config/SSSDConfig/sssdoptions.py b/src/config/SSSDConfig/sssdoptions.py index 7eed403e4bd..88adcfbf28e 100644 --- a/src/config/SSSDConfig/sssdoptions.py +++ b/src/config/SSSDConfig/sssdoptions.py @@ -207,6 +207,9 @@ def __init__(self): 'dyndns_force_tcp': _("Whether the nsupdate utility should default to using TCP"), 'dyndns_auth': _("What kind of authentication should be used to perform the DNS update"), 'dyndns_server': _("Override the DNS server used to perform the DNS update"), + 'dyndns_dot_cacert': _("The file of the certificate authorities certificates for DoT"), + 'dyndns_dot_cert': _("The certificate(s) file for authentication for the DoT transport"), + 'dyndns_dot_key': _("The key file for authenticated encryption for the DoT transport"), 'subdomain_enumerate': _('Control enumeration of trusted domains'), 'subdomain_refresh_interval': _('How often should subdomains list be refreshed'), 'subdomain_refresh_interval_offset': _('Maximum period deviation when refreshing the subdomain list'), diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py index 6087f6e085b..e67f47c5e5e 100755 --- a/src/config/SSSDConfigTest.py +++ b/src/config/SSSDConfigTest.py @@ -567,6 +567,9 @@ def testListOptions(self): 'dyndns_force_tcp', 'dyndns_auth', 'dyndns_server', + 'dyndns_dot_cacert', + 'dyndns_dot_cert', + 'dyndns_dot_key', 'subdomain_enumerate', 'override_gid', 'case_sensitive', @@ -928,6 +931,9 @@ def testRemoveProvider(self): 'dyndns_force_tcp', 'dyndns_auth', 'dyndns_server', + 'dyndns_dot_cacert', + 'dyndns_dot_cert', + 'dyndns_dot_key', 'subdomain_enumerate', 'override_gid', 'case_sensitive', diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini index 950eae630fb..920902209c5 100644 --- a/src/config/cfg_rules.ini +++ b/src/config/cfg_rules.ini @@ -443,6 +443,9 @@ option = dyndns_force_tcp option = dyndns_auth option = dyndns_auth_ptr option = dyndns_server +option = dyndns_dot_cacert +option = dyndns_dot_cert +option = dyndns_dot_key # files provider specific options option = passwd_files diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf index 4377a1fc571..2ab4a92a29c 100644 --- a/src/config/etc/sssd.api.conf +++ b/src/config/etc/sssd.api.conf @@ -216,6 +216,9 @@ dyndns_update_ptr = bool, None, false dyndns_force_tcp = bool, None, false dyndns_auth = str, None, false dyndns_server = str, None, false +dyndns_dot_cacert = str, None, false +dyndns_dot_cert = str, None, false +dyndns_dot_key = str, None, false # Special providers [provider/permit] diff --git a/src/man/sssd-ad.5.xml b/src/man/sssd-ad.5.xml index 79724fff5da..ceb8b0cfcc7 100644 --- a/src/man/sssd-ad.5.xml +++ b/src/man/sssd-ad.5.xml @@ -1328,12 +1328,26 @@ ad_gpo_map_deny = +my_pam_service Setting this option makes sense for environments where the DNS server is different from the identity - server. + server or when we use encrypted DNS. + + + The parameter can be a simple string containing + DNS name or IP address. It can also be an URI. + The URI can look like + dns://servername/ or + dns+tls://1.2.3.4:853#servername/. + + + The second example enables DNS-over-TLS protocol for + DNS updates. The nsupdate utility must support DoT - + check the man nsupdate before + enabling it in SSSD. Please note that this option will be only used in fallback attempt when previous attempt using - autodetected settings failed. + autodetected settings failed or when DNS-over-TLS + is enabled. Default: None (let nsupdate choose the server) @@ -1356,6 +1370,56 @@ ad_gpo_map_deny = +my_pam_service + + dyndns_dot_cacert (string) + + + This option specifies the file of the certificate + authorities certificates (in PEM format) in order + to verify the remote server TLS certificate when + using DoT. + + + Default: None (use global certificate store) + + + + + + dyndns_dot_cert (string) + + + This option sets the certificate(s) file for + authentication for the DoT transport to the remote + server. The certificate chain file is expected to + be in PEM format. + + + The dyndns_dot_cert and + dyndns_dot_key options must be + both set to achive mutual TLS authentication. + + + Default: None (Do not use TLS authentication) + + + + + + dyndns_dot_key (string) + + + This option sets the key file for authenticated + encryption for the DoT transport to the remote + server. The private key file is expected to + be in PEM format. + + + Default: None (Do not use TLS authentication) + + + + diff --git a/src/man/sssd-ipa.5.xml b/src/man/sssd-ipa.5.xml index 5adfaefcd7d..a638e48f99d 100644 --- a/src/man/sssd-ipa.5.xml +++ b/src/man/sssd-ipa.5.xml @@ -321,12 +321,26 @@ Setting this option makes sense for environments where the DNS server is different from the identity - server. + server or when we use encrypted DNS. + + + The parameter can be a simple string containing + DNS name or IP address. It can also be an URI. + The URI can look like + dns://servername/ or + dns+tls://1.2.3.4:853#servername/. + + + The second example enables DNS-over-TLS protocol for + DNS updates. The nsupdate utility must support DoT - + check the man nsupdate before + enabling it in SSSD. Please note that this option will be only used in fallback attempt when previous attempt using - autodetected settings failed. + autodetected settings failed or when DNS-over-TLS + is enabled. Default: None (let nsupdate choose the server) @@ -349,6 +363,56 @@ + + dyndns_dot_cacert (string) + + + This option specifies the file of the certificate + authorities certificates (in PEM format) in order + to verify the remote server TLS certificate when + using DoT. + + + Default: None (use global certificate store) + + + + + + dyndns_dot_cert (string) + + + This option sets the certificate(s) file for + authentication for the DoT transport to the remote + server. The certificate chain file is expected to + be in PEM format. + + + The dyndns_dot_cert and + dyndns_dot_key options must be + both set to achive mutual TLS authentication. + + + Default: None (Do not use TLS authentication) + + + + + + dyndns_dot_key (string) + + + This option sets the key file for authenticated + encryption for the DoT transport to the remote + server. The private key file is expected to + be in PEM format. + + + Default: None (Do not use TLS authentication) + + + + ipa_access_order (string) diff --git a/src/providers/ad/ad_opts.c b/src/providers/ad/ad_opts.c index e00777e68d5..a3dd0547b7a 100644 --- a/src/providers/ad/ad_opts.c +++ b/src/providers/ad/ad_opts.c @@ -325,6 +325,9 @@ struct dp_option ad_dyndns_opts[] = { { "dyndns_auth", DP_OPT_STRING, { "gss-tsig" }, NULL_STRING }, { "dyndns_auth_ptr", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "dyndns_server", DP_OPT_STRING, NULL_STRING, NULL_STRING }, + { "dyndns_dot_cacert", DP_OPT_STRING, NULL_STRING, NULL_STRING }, + { "dyndns_dot_cert", DP_OPT_STRING, NULL_STRING, NULL_STRING }, + { "dyndns_dot_key", DP_OPT_STRING, NULL_STRING, NULL_STRING }, DP_OPTION_TERMINATOR }; diff --git a/src/providers/be_dyndns.c b/src/providers/be_dyndns.c index 8274f10cf9a..2032e7707b5 100644 --- a/src/providers/be_dyndns.c +++ b/src/providers/be_dyndns.c @@ -23,6 +23,7 @@ along with this program. If not, see . */ +#include #include #include #include @@ -30,6 +31,7 @@ #include #include #include +#include "util/debug.h" #include "util/util.h" #include "confdb/confdb.h" #include "util/child_common.h" @@ -267,7 +269,8 @@ sss_iface_addr_list_get(TALLOC_CTX *mem_ctx, const char *ifname, static char * nsupdate_msg_add_fwd(char *update_msg, struct sss_iface_addr *addresses, - const char *hostname, int ttl, uint8_t remove_af, bool update_per_family) + const char *hostname, int ttl, uint8_t remove_af, + bool update_per_family) { struct sss_iface_addr *new_record; char ip_addr[INET6_ADDRSTRLEN]; @@ -445,7 +448,7 @@ nsupdate_msg_add_realm_cmd(TALLOC_CTX *mem_ctx, const char *realm) static char * nsupdate_msg_create_common(TALLOC_CTX *mem_ctx, const char *realm, - const char *servername) + struct sss_parsed_dns_uri *server_uri) { char *realm_directive; char *update_msg; @@ -462,14 +465,17 @@ nsupdate_msg_create_common(TALLOC_CTX *mem_ctx, const char *realm, /* The realm_directive would now either contain an empty string or be * completely empty so we don't need to add another newline here */ - if (servername) { + if (server_uri) { DEBUG(SSSDBG_FUNC_DATA, "Creating update message for server [%s] and realm [%s].\n", - servername, realm); + server_uri->address, realm); /* Add the server, realm and headers */ - update_msg = talloc_asprintf(tmp_ctx, "server %s\n%s", - servername, realm_directive); + update_msg = talloc_asprintf(tmp_ctx, "server %s%s%s\n%s", + server_uri->address, + server_uri->port ? " " : "", + server_uri->port ? server_uri->port : "", + realm_directive); } else if (realm != NULL) { DEBUG(SSSDBG_FUNC_DATA, "Creating update message for realm [%s].\n", realm); @@ -496,7 +502,7 @@ nsupdate_msg_create_common(TALLOC_CTX *mem_ctx, const char *realm, errno_t be_nsupdate_create_fwd_msg(TALLOC_CTX *mem_ctx, const char *realm, - const char *servername, + struct sss_parsed_dns_uri *server_uri, const char *hostname, const unsigned int ttl, uint8_t remove_af, struct sss_iface_addr *addresses, bool update_per_family, @@ -514,7 +520,7 @@ be_nsupdate_create_fwd_msg(TALLOC_CTX *mem_ctx, const char *realm, tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) return ENOMEM; - update_msg = nsupdate_msg_create_common(tmp_ctx, realm, servername); + update_msg = nsupdate_msg_create_common(tmp_ctx, realm, server_uri); if (update_msg == NULL) { ret = ENOMEM; goto done; @@ -542,7 +548,7 @@ be_nsupdate_create_fwd_msg(TALLOC_CTX *mem_ctx, const char *realm, errno_t be_nsupdate_create_ptr_msg(TALLOC_CTX *mem_ctx, const char *realm, - const char *servername, + struct sss_parsed_dns_uri *server_uri, const char *hostname, const unsigned int ttl, uint8_t remove_af, struct sss_iface_addr *addresses, bool update_per_family, @@ -560,7 +566,7 @@ be_nsupdate_create_ptr_msg(TALLOC_CTX *mem_ctx, const char *realm, tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) return ENOMEM; - update_msg = nsupdate_msg_create_common(tmp_ctx, realm, servername); + update_msg = nsupdate_msg_create_common(tmp_ctx, realm, server_uri); if (update_msg == NULL) { ret = ENOMEM; goto done; @@ -1040,13 +1046,21 @@ struct be_nsupdate_state { static void be_nsupdate_done(struct tevent_req *subreq); static const char **be_nsupdate_args(TALLOC_CTX *mem_ctx, enum be_nsupdate_auth auth_type, - bool force_tcp); + bool force_tcp, + struct sss_parsed_dns_uri *server_uri, + const char *dot_cacert, + const char *dot_cert, + const char *dot_key); struct tevent_req *be_nsupdate_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, enum be_nsupdate_auth auth_type, char *nsupdate_msg, - bool force_tcp) + bool force_tcp, + struct sss_parsed_dns_uri *server_uri, + const char *dot_cacert, + const char *dot_cert, + const char *dot_key) { int pipefd_to_child[2] = PIPE_INIT; int pipefd_from_child[2] = PIPE_INIT; @@ -1078,7 +1092,8 @@ struct tevent_req *be_nsupdate_send(TALLOC_CTX *mem_ctx, goto done; } - args = be_nsupdate_args(state, auth_type, force_tcp); + args = be_nsupdate_args(state, auth_type, force_tcp, + server_uri, dot_cacert, dot_cert, dot_key); if (args == NULL) { ret = ENOMEM; goto done; @@ -1126,16 +1141,32 @@ struct tevent_req *be_nsupdate_send(TALLOC_CTX *mem_ctx, static const char ** be_nsupdate_args(TALLOC_CTX *mem_ctx, enum be_nsupdate_auth auth_type, - bool force_tcp) + bool force_tcp, + struct sss_parsed_dns_uri *server_uri, + const char *dot_cacert, + const char *dot_cert, + const char *dot_key) { const char **argv; int argc = 0; + bool use_dot; + bool have_dot_cert; + bool have_dot_key; - argv = talloc_zero_array(mem_ctx, const char *, 6); + argv = talloc_zero_array(mem_ctx, const char *, 14); if (argv == NULL) { return NULL; } + if (!sss_is_valid_dns_scheme(server_uri)) { + sss_log(SSS_LOG_WARNING, + "Invalid DNS scheme in SSSD config file: %s, using dns://\n", + server_uri->scheme); + } + + use_dot = sss_is_dot_scheme(server_uri); + DEBUG(SSSDBG_FUNC_DATA, "nsupdate DoT: %i\n", use_dot); + switch (auth_type) { case BE_NSUPDATE_AUTH_NONE: DEBUG(SSSDBG_FUNC_DATA, "nsupdate auth type: none\n"); @@ -1179,6 +1210,62 @@ be_nsupdate_args(TALLOC_CTX *mem_ctx, argc++; } + if (use_dot) { + DEBUG(SSSDBG_FUNC_DATA, "DoT option is set\n"); + argv[argc] = talloc_strdup(argv, "-S"); + if (argv[argc] == NULL) { + goto fail; + } + argc++; + + /* DoT server name */ + argv[argc] = talloc_strdup(argv, "-H"); + if (argv[argc] == NULL) { + goto fail; + } + argc++; + argv[argc] = talloc_strdup(argv, server_uri->host); + if (argv[argc] == NULL) { + goto fail; + } + argc++; + + /* DoT CA cert file */ + if (dot_cacert != NULL && dot_cacert[0] != 0) { + argv[argc + 1] = talloc_strdup(argv, "-A"); + argv[argc] = talloc_strdup(argv, dot_cacert); + if (argv[argc] == NULL || argv[argc+1] == NULL) { + goto fail; + } + argc += 2; + } + + /* DoT cert and key must be set both or none */ + have_dot_cert = (dot_cert != NULL && dot_cert[0] != 0); + have_dot_key = (dot_key != NULL && dot_key[0] != 0); + if (have_dot_key != have_dot_cert) { + DEBUG(SSSDBG_OP_FAILURE, + "The dyndns_dot_cert and dyndns_dot_key must be set both " + "(or none of them)\n"); + goto fail; + } + if (have_dot_cert && have_dot_key) { + /* we have both, key and cert file paths */ + argv[argc + 1] = talloc_strdup(argv, "-E"); + argv[argc] = talloc_strdup(argv, dot_cert); + if (argv[argc] == NULL || argv[argc+1] == NULL) { + goto fail; + } + argc += 2; + + argv[argc + 1] = talloc_strdup(argv, "-K"); + argv[argc] = talloc_strdup(argv, dot_key); + if (argv[argc] == NULL || argv[argc+1] == NULL) { + goto fail; + } + argc += 2; + } + } return argv; fail: @@ -1259,6 +1346,9 @@ struct dp_option default_dyndns_opts[] = { { "dyndns_auth", DP_OPT_STRING, { "gss-tsig" }, NULL_STRING }, { "dyndns_auth_ptr", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "dyndns_server", DP_OPT_STRING, NULL_STRING, NULL_STRING }, + { "dyndns_dot_cacert", DP_OPT_STRING, NULL_STRING, NULL_STRING }, + { "dyndns_dot_cert", DP_OPT_STRING, NULL_STRING, NULL_STRING }, + { "dyndns_dot_key", DP_OPT_STRING, NULL_STRING, NULL_STRING }, DP_OPTION_TERMINATOR }; @@ -1420,3 +1510,22 @@ errno_t sss_get_dualstack_addresses(TALLOC_CTX *mem_ctx, talloc_free(tmp_ctx); return ret; } + +bool +sss_is_valid_dns_scheme(struct sss_parsed_dns_uri *uri) +{ + return + uri == NULL || + uri->scheme == NULL || /* use default DNS scheme */ + strcasecmp(uri->scheme, "dns") == 0 || + strcasecmp(uri->scheme, "dns+tls") == 0; +} + +bool +sss_is_dot_scheme(struct sss_parsed_dns_uri *uri) +{ + return + uri != NULL && + uri->scheme != NULL && + strcasecmp(uri->scheme, "dns+tls") == 0; +} diff --git a/src/providers/be_dyndns.h b/src/providers/be_dyndns.h index 719c1394255..8aa14c4a1fa 100644 --- a/src/providers/be_dyndns.h +++ b/src/providers/be_dyndns.h @@ -27,6 +27,7 @@ #define DP_DYNDNS_H_ /* dynamic dns helpers */ +#include struct sss_iface_addr; typedef void (*nsupdate_timer_fn_t)(void *pvt); @@ -60,6 +61,9 @@ enum dp_dyndns_opts { DP_OPT_DYNDNS_AUTH, DP_OPT_DYNDNS_AUTH_PTR, DP_OPT_DYNDNS_SERVER, + DP_OPT_DYNDNS_DOT_CACERT, + DP_OPT_DYNDNS_DOT_CERT, + DP_OPT_DYNDNS_DOT_KEY, DP_OPT_DYNDNS /* attrs counter */ }; @@ -86,7 +90,7 @@ sss_iface_addr_list_as_str_list(TALLOC_CTX *mem_ctx, errno_t be_nsupdate_create_fwd_msg(TALLOC_CTX *mem_ctx, const char *realm, - const char *servername, + struct sss_parsed_dns_uri *server_uri, const char *hostname, const unsigned int ttl, uint8_t remove_af, struct sss_iface_addr *addresses, bool update_per_family, @@ -94,7 +98,7 @@ be_nsupdate_create_fwd_msg(TALLOC_CTX *mem_ctx, const char *realm, errno_t be_nsupdate_create_ptr_msg(TALLOC_CTX *mem_ctx, const char *realm, - const char *servername, + struct sss_parsed_dns_uri *server_uri, const char *hostname, const unsigned int ttl, uint8_t remove_af, struct sss_iface_addr *addresses, bool update_per_family, @@ -110,7 +114,11 @@ struct tevent_req *be_nsupdate_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, enum be_nsupdate_auth auth_type, char *nsupdate_msg, - bool force_tcp); + bool force_tcp, + struct sss_parsed_dns_uri *server_uri, + const char *dot_cacert, + const char *dot_cert, + const char *dot_key); errno_t be_nsupdate_recv(struct tevent_req *req, int *child_status); struct tevent_req * nsupdate_get_addrs_send(TALLOC_CTX *mem_ctx, @@ -138,4 +146,10 @@ sss_iface_addr_get_next(struct sss_iface_addr *address); struct sockaddr * sss_iface_addr_get_address(struct sss_iface_addr *address); +bool +sss_is_valid_dns_scheme(struct sss_parsed_dns_uri *uri); + +bool +sss_is_dot_scheme(struct sss_parsed_dns_uri *uri); + #endif /* DP_DYNDNS_H_ */ diff --git a/src/providers/ipa/ipa_opts.c b/src/providers/ipa/ipa_opts.c index 62423f868e2..5bfd65a6717 100644 --- a/src/providers/ipa/ipa_opts.c +++ b/src/providers/ipa/ipa_opts.c @@ -69,6 +69,9 @@ struct dp_option ipa_dyndns_opts[] = { { "dyndns_auth", DP_OPT_STRING, { "gss-tsig" }, NULL_STRING }, { "dyndns_auth_ptr", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "dyndns_server", DP_OPT_STRING, NULL_STRING, NULL_STRING }, + { "dyndns_dot_cacert", DP_OPT_STRING, NULL_STRING, NULL_STRING }, + { "dyndns_dot_cert", DP_OPT_STRING, NULL_STRING, NULL_STRING }, + { "dyndns_dot_key", DP_OPT_STRING, NULL_STRING, NULL_STRING }, DP_OPTION_TERMINATOR }; diff --git a/src/providers/ldap/sdap_dyndns.c b/src/providers/ldap/sdap_dyndns.c index 3535fb42658..ef99d4666a1 100644 --- a/src/providers/ldap/sdap_dyndns.c +++ b/src/providers/ldap/sdap_dyndns.c @@ -48,7 +48,8 @@ struct sdap_dyndns_update_state { const char *hostname; const char *realm; - const char *servername; + struct sss_parsed_dns_uri *server_uri; + bool dot; int ttl; struct sss_iface_addr *addresses; @@ -112,7 +113,8 @@ sdap_dyndns_update_send(TALLOC_CTX *mem_ctx, state->update_ptr = dp_opt_get_bool(opts, DP_OPT_DYNDNS_UPDATE_PTR); state->hostname = hostname; state->realm = realm; - state->servername = NULL; + state->server_uri = NULL; + state->dot = false; state->fallback_mode = false; state->ttl = ttl; state->be_res = be_ctx->be_res; @@ -124,7 +126,11 @@ sdap_dyndns_update_send(TALLOC_CTX *mem_ctx, /* fallback servername is overridden by user option */ conf_servername = dp_opt_get_string(opts, DP_OPT_DYNDNS_SERVER); if (conf_servername != NULL) { - state->servername = conf_servername; + ret = sss_parse_dns_uri(mem_ctx, conf_servername, &state->server_uri); + if (ret != EOK) { + goto done; + } + state->dot = sss_is_dot_scheme(state->server_uri); } if (ifname) { @@ -324,20 +330,24 @@ sdap_dyndns_update_step(struct tevent_req *req) { errno_t ret; struct sdap_dyndns_update_state *state; - const char *servername; + struct sss_parsed_dns_uri *server_uri; const char *realm; struct tevent_req *subreq; state = tevent_req_data(req, struct sdap_dyndns_update_state); - servername = NULL; + server_uri = NULL; realm = NULL; + if (state->dot) { + /* in DoT we have to set the server */ + state->fallback_mode = true; + } if (state->fallback_mode) { - servername = state->servername; + server_uri = state->server_uri; realm = state->realm; } - ret = be_nsupdate_create_fwd_msg(state, realm, servername, + ret = be_nsupdate_create_fwd_msg(state, realm, server_uri, state->hostname, state->ttl, state->remove_af, state->addresses, @@ -352,7 +362,14 @@ sdap_dyndns_update_step(struct tevent_req *req) subreq = be_nsupdate_send(state, state->ev, state->auth_type, state->update_msg, dp_opt_get_bool(state->opts, - DP_OPT_DYNDNS_FORCE_TCP)); + DP_OPT_DYNDNS_FORCE_TCP), + state->server_uri, + dp_opt_get_string(state->opts, + DP_OPT_DYNDNS_DOT_CACERT), + dp_opt_get_string(state->opts, + DP_OPT_DYNDNS_DOT_CERT), + dp_opt_get_string(state->opts, + DP_OPT_DYNDNS_DOT_KEY)); if (subreq == NULL) { return EIO; } @@ -409,20 +426,24 @@ sdap_dyndns_update_ptr_step(struct tevent_req *req) { errno_t ret; struct sdap_dyndns_update_state *state; - const char *servername; + struct sss_parsed_dns_uri *server_uri; const char *realm; struct tevent_req *subreq; state = tevent_req_data(req, struct sdap_dyndns_update_state); - servername = NULL; + server_uri = NULL; realm = NULL; + if (state->dot == true) { + /* in DoT we have to set the server */ + state->fallback_mode = true; + } if (state->fallback_mode == true) { - servername = state->servername; + server_uri = state->server_uri; realm = state->realm; } - ret = be_nsupdate_create_ptr_msg(state, realm, servername, + ret = be_nsupdate_create_ptr_msg(state, realm, server_uri, state->hostname, state->ttl, state->remove_af, state->addresses, @@ -438,7 +459,14 @@ sdap_dyndns_update_ptr_step(struct tevent_req *req) subreq = be_nsupdate_send(state, state->ev, state->auth_ptr_type, state->update_msg, dp_opt_get_bool(state->opts, - DP_OPT_DYNDNS_FORCE_TCP)); + DP_OPT_DYNDNS_FORCE_TCP), + state->server_uri, + dp_opt_get_string(state->opts, + DP_OPT_DYNDNS_DOT_CACERT), + dp_opt_get_string(state->opts, + DP_OPT_DYNDNS_DOT_CERT), + dp_opt_get_string(state->opts, + DP_OPT_DYNDNS_DOT_KEY)); if (subreq == NULL) { return EIO; } diff --git a/src/tests/cmocka/test_dyndns.c b/src/tests/cmocka/test_dyndns.c index 7526c16a86d..7b92fe32f16 100644 --- a/src/tests/cmocka/test_dyndns.c +++ b/src/tests/cmocka/test_dyndns.c @@ -35,6 +35,7 @@ #include "tests/cmocka/common_mock.h" #include "tests/cmocka/common_mock_be.h" #include "src/providers/be_dyndns.h" +#include "util/util.h" #define TESTS_PATH "tp_" BASE_FILE_STEM #define TEST_CONF_DB "test_dyndns_conf.ldb" @@ -361,6 +362,7 @@ void dyndns_test_create_fwd_msg(void **state) errno_t ret; char *msg; struct sss_iface_addr *addrlist; + struct sss_parsed_dns_uri *uri; int i; check_leaks_push(dyndns_test_ctx); @@ -411,7 +413,8 @@ void dyndns_test_create_fwd_msg(void **state) talloc_zfree(msg); /* fallback case realm and server */ - ret = be_nsupdate_create_fwd_msg(dyndns_test_ctx, "North", "Winterfell", + sss_parse_dns_uri(dyndns_test_ctx, "Winterfell", &uri); + ret = be_nsupdate_create_fwd_msg(dyndns_test_ctx, "North", uri, "bran_stark", 1234, DYNDNS_REMOVE_A | DYNDNS_REMOVE_AAAA, addrlist, true, &msg); @@ -446,7 +449,7 @@ void dyndns_test_create_fwd_msg(void **state) talloc_zfree(msg); /* just server */ - ret = be_nsupdate_create_fwd_msg(dyndns_test_ctx, NULL, "Winterfell", + ret = be_nsupdate_create_fwd_msg(dyndns_test_ctx, NULL, uri, "bran_stark", 1234, DYNDNS_REMOVE_A | DYNDNS_REMOVE_AAAA, addrlist, true, &msg); @@ -492,6 +495,7 @@ void dyndns_test_create_fwd_msg(void **state) talloc_zfree(msg); talloc_free(addrlist); + talloc_free(uri); assert_true(check_leaks_pop(dyndns_test_ctx) == true); } @@ -879,7 +883,8 @@ void dyndns_test_ok(void **state) req = be_nsupdate_send(tmp_ctx, dyndns_test_ctx->tctx->ev, BE_NSUPDATE_AUTH_GSS_TSIG, - discard_const("test message"), false); + discard_const("test message"), false, + false, NULL, NULL, NULL); assert_non_null(req); tevent_req_set_callback(req, dyndns_test_done, dyndns_test_ctx); @@ -910,7 +915,8 @@ void dyndns_test_error(void **state) req = be_nsupdate_send(tmp_ctx, dyndns_test_ctx->tctx->ev, BE_NSUPDATE_AUTH_GSS_TSIG, - discard_const("test message"), false); + discard_const("test message"), false, + false, NULL, NULL, NULL); assert_non_null(req); tevent_req_set_callback(req, dyndns_test_done, dyndns_test_ctx); @@ -941,7 +947,8 @@ void dyndns_test_timeout(void **state) req = be_nsupdate_send(tmp_ctx, dyndns_test_ctx->tctx->ev, BE_NSUPDATE_AUTH_GSS_TSIG, - discard_const("test message"), false); + discard_const("test message"), false, + false, NULL, NULL, NULL); assert_non_null(req); tevent_req_set_callback(req, dyndns_test_done, dyndns_test_ctx); diff --git a/src/tests/cmocka/test_utils.c b/src/tests/cmocka/test_utils.c index 760e7130723..d07c1b4d5d7 100644 --- a/src/tests/cmocka/test_utils.c +++ b/src/tests/cmocka/test_utils.c @@ -2370,6 +2370,63 @@ static void test_sss_filter_sanitize_dn(void **state) talloc_free(tmp_ctx); } +static void test_sss_parse_uri(void **state) +{ + TALLOC_CTX *tmp_ctx; + struct sss_parsed_dns_uri *parsed_uri; + int ret; + + tmp_ctx = talloc_new(NULL); + assert_non_null(tmp_ctx); + + ret = sss_parse_dns_uri(tmp_ctx, "http://host", &parsed_uri); + assert_non_null(parsed_uri); + assert_int_equal(ret, EOK); + assert_string_equal(parsed_uri->scheme, "http"); + assert_string_equal(parsed_uri->host, "host"); + assert_string_equal(parsed_uri->address, "host"); + assert_null(parsed_uri->port); + talloc_free(parsed_uri); + + ret = sss_parse_dns_uri(tmp_ctx, "http://10.0.0.1#host", &parsed_uri); + assert_int_equal(ret, EOK); + assert_non_null(parsed_uri); + assert_string_equal(parsed_uri->scheme, "http"); + assert_string_equal(parsed_uri->host, "host"); + assert_string_equal(parsed_uri->address, "10.0.0.1"); + assert_null(parsed_uri->port); + talloc_free(parsed_uri); + + ret = sss_parse_dns_uri(tmp_ctx, " dns+tls://10.0.0.1:853#host", &parsed_uri); + assert_int_equal(ret, EOK); + assert_non_null(parsed_uri); + assert_string_equal(parsed_uri->scheme, "dns+tls"); + assert_string_equal(parsed_uri->host, "host"); + assert_string_equal(parsed_uri->address, "10.0.0.1"); + assert_string_equal(parsed_uri->port, "853"); + talloc_free(parsed_uri); + + ret = sss_parse_dns_uri(tmp_ctx, "host", &parsed_uri); + assert_int_equal(ret, EOK); + assert_non_null(parsed_uri); + assert_null(parsed_uri->scheme); + assert_string_equal(parsed_uri->host, "host"); + assert_string_equal(parsed_uri->address, "host"); + assert_null(parsed_uri->port); + talloc_free(parsed_uri); + + ret = sss_parse_dns_uri(tmp_ctx, "dns+tls://[cafe::1]:853#host", &parsed_uri); + assert_int_equal(ret, EOK); + assert_non_null(parsed_uri); + assert_string_equal(parsed_uri->scheme, "dns+tls"); + assert_string_equal(parsed_uri->host, "host"); + assert_string_equal(parsed_uri->address, "[cafe::1]"); + assert_string_equal(parsed_uri->port, "853"); + talloc_free(parsed_uri); + + talloc_free(tmp_ctx); +} + int main(int argc, const char *argv[]) { poptContext pc; @@ -2495,6 +2552,9 @@ int main(int argc, const char *argv[]) cmocka_unit_test_setup_teardown(test_mod_defaults_list, setup_leak_tests, teardown_leak_tests), + cmocka_unit_test_setup_teardown(test_sss_parse_uri, + setup_leak_tests, + teardown_leak_tests), }; /* Set debug level to invalid value so we can decide if -d 0 was used. */ diff --git a/src/util/child_common.c b/src/util/child_common.c index 2633863396c..1aea1949593 100644 --- a/src/util/child_common.c +++ b/src/util/child_common.c @@ -25,11 +25,13 @@ #include #include #include +#include #include #include #include #include +#include "util/debug.h" #include "util/util.h" #include "util/find_uid.h" #include "db/sysdb.h" @@ -835,6 +837,30 @@ static errno_t prepare_child_argv(TALLOC_CTX *mem_ctx, return ret; } +static void log_child_command(TALLOC_CTX *mem_ctx, const char *binary, + char *argv[]) { + int n; + char *command; + + if(DEBUG_IS_SET(SSSDBG_TRACE_INTERNAL)){ + command = talloc_strdup(mem_ctx, binary); + if (command == NULL) { + return; + } + if (argv != NULL) { + for (n = 0; argv[n] != NULL; ++n) { + command = talloc_asprintf_append(command, " %s", argv[n]); + if (command == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory."); + return; + } + } + } + DEBUG(SSSDBG_TRACE_INTERNAL, "exec_child command: %s\n", command); + talloc_free(command); + } +} + void exec_child_ex(TALLOC_CTX *mem_ctx, int *pipefd_to_child, int *pipefd_from_child, const char *binary, const char *logfile, @@ -882,6 +908,7 @@ void exec_child_ex(TALLOC_CTX *mem_ctx, exit(EXIT_FAILURE); } + log_child_command(mem_ctx, binary, argv); execv(binary, argv); err = errno; DEBUG(SSSDBG_OP_FAILURE, "execv failed [%d][%s].\n", err, strerror(err)); diff --git a/src/util/util.c b/src/util/util.c index 226c746c693..bc34b0ba662 100644 --- a/src/util/util.c +++ b/src/util/util.c @@ -1091,3 +1091,83 @@ errno_t sss_getenv(TALLOC_CTX *mem_ctx, return value != NULL ? EOK : ENOENT; } + +errno_t sss_parse_dns_uri(TALLOC_CTX *mem_ctx, + const char *uri, + struct sss_parsed_dns_uri **_parsed_uri) +{ + char *s, *p; + const char *start; + struct sss_parsed_dns_uri *parsed_uri; + errno_t ret = EOK; + + if (uri == NULL || _parsed_uri == NULL) { + return EINVAL; + } + + parsed_uri = talloc_zero(mem_ctx, struct sss_parsed_dns_uri); + if (parsed_uri == NULL) { + ret = ENOMEM; + goto fail; + } + + start = uri; + while(isspace(start[0])) { + start++; + } + + parsed_uri->data = talloc_strdup(parsed_uri, start); + if (parsed_uri->data == NULL) { + ret = ENOMEM; + goto fail; + } + s = parsed_uri->data; + + /* scheme */ + p = strstr(s, "://"); + if (p != NULL) { + parsed_uri->scheme = s; + *p = '\000'; + s = &p[3]; + } + + /* path part */ + p = strchr(s, '/'); + if (p != NULL) { + parsed_uri->path = &p[1]; + *p = '\000'; + } + + p = strchr(s, '#'); + if (p != NULL) { + parsed_uri->host = &p[1]; + *p = '\000'; + } + + if (s[0] == '[') { + /* IPv6 address */ + p = strstr(s, "]:"); + if (p != NULL) { + ++p; + } + } else { + p = strchr(s, ':'); + } + if (p != NULL) { + parsed_uri->port = &p[1]; + *p = '\000'; + } + + parsed_uri->address = s; + if (parsed_uri->host == NULL) { + parsed_uri->host = parsed_uri->address; + } + + *_parsed_uri = parsed_uri; + return EOK; + + fail: + talloc_free(parsed_uri); + *_parsed_uri = NULL; + return ret; +} diff --git a/src/util/util.h b/src/util/util.h index 8674c6c9dbd..3c2b032088e 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -878,4 +878,20 @@ static inline struct timeval sss_tevent_timeval_current_ofs_time_t(time_t secs) uint32_t secs32 = (secs > UINT_MAX ? UINT_MAX : secs); return tevent_timeval_current_ofs(secs32, 0); } + +/* parsed uri */ +struct sss_parsed_dns_uri { + const char *scheme; + const char *address; + const char *port; + const char *host; + const char *path; + + char *data; +}; + +errno_t sss_parse_dns_uri(TALLOC_CTX *ctx, + const char *uri, + struct sss_parsed_dns_uri **_parsed_uri); + #endif /* __SSSD_UTIL_H__ */