dpward / rpms / sssd

Forked from rpms/sssd 3 years ago
Clone

Blame SOURCES/0028-IPA-Populate-kdcinfo-files-on-trust-clients-with-con.patch

ca1eb8
From f1780cea77deb98789cc9c53cb6d7c83e2931e70 Mon Sep 17 00:00:00 2001
ca1eb8
From: Jakub Hrozek <jhrozek@redhat.com>
ca1eb8
Date: Mon, 25 Jun 2018 13:10:39 +0200
ca1eb8
Subject: [PATCH] IPA: Populate kdcinfo files on trust clients with configured
ca1eb8
 AD servers
ca1eb8
ca1eb8
Resolves:
ca1eb8
https://pagure.io/SSSD/sssd/issue/3291
ca1eb8
ca1eb8
Adds a new request into the IPA subdomains provider. This request runs on
ca1eb8
IPA clients only.
ca1eb8
ca1eb8
The request looks into the configuration for either the ad_site or ad_server
ca1eb8
options for each subdomain. If none are found, the subdomain is skipped.
ca1eb8
ca1eb8
If either is found, the request resolves the server names, or first the
ca1eb8
site and then the server names from the site and writes their addresses
ca1eb8
to the kdcinfo files for each subdomain. This allows programs such as
ca1eb8
kinit but also SSSD's krb5_child to use the configured servers.
ca1eb8
ca1eb8
Reviewed-by: Sumit Bose <sbose@redhat.com>
ca1eb8
(cherry picked from commit 18b7f0a30b4745b7d61b3e599e5fb8cd399c23f3)
ca1eb8
---
ca1eb8
 src/providers/ipa/ipa_subdomains.c | 718 ++++++++++++++++++++++++++++-
ca1eb8
 1 file changed, 716 insertions(+), 2 deletions(-)
ca1eb8
ca1eb8
diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
ca1eb8
index a8a18ad8a3ec08c137994a84d51851e996aad6dc..1b443559eada3b8feeb9c91fbebd4e2dcca87a23 100644
ca1eb8
--- a/src/providers/ipa/ipa_subdomains.c
ca1eb8
+++ b/src/providers/ipa/ipa_subdomains.c
ca1eb8
@@ -76,6 +76,18 @@
ca1eb8
                               "("IPA_ENABLED_FLAG"="IPA_TRUE_VALUE"))" \
ca1eb8
                           "("OBJECTCLASS"="IPA_OC_CERTMAP_CONFIG_OBJECT"))"
ca1eb8
 
ca1eb8
+/* It doesn't make sense to resolve more servers than this from the SRV
ca1eb8
+ * lookup because kinit would time out before we are able to cycle
ca1eb8
+ * through the whole list
ca1eb8
+ */
ca1eb8
+#define MAX_SERVERS_FROM_SRV    5
ca1eb8
+
ca1eb8
+struct ipa_sd_k5_svc_list {
ca1eb8
+    struct krb5_service *k5svc;
ca1eb8
+
ca1eb8
+    struct ipa_sd_k5_svc_list *next;
ca1eb8
+    struct ipa_sd_k5_svc_list *prev;
ca1eb8
+};
ca1eb8
 
ca1eb8
 struct ipa_subdomains_ctx {
ca1eb8
     struct be_ctx *be_ctx;
ca1eb8
@@ -88,6 +100,11 @@ struct ipa_subdomains_ctx {
ca1eb8
 
ca1eb8
     time_t last_refreshed;
ca1eb8
     bool view_read_at_init;
ca1eb8
+    /* List of krb5_service structures for each subdomain
ca1eb8
+     * in order to write the kdcinfo files. For use on
ca1eb8
+     * the client only
ca1eb8
+     */
ca1eb8
+    struct ipa_sd_k5_svc_list *k5svc_list;
ca1eb8
 };
ca1eb8
 
ca1eb8
 static errno_t
ca1eb8
@@ -635,6 +652,69 @@ done:
ca1eb8
     return ret;
ca1eb8
 }
ca1eb8
 
ca1eb8
+static struct krb5_service *
ca1eb8
+ipa_subdom_get_k5_svc(struct ipa_subdomains_ctx *ctx,
ca1eb8
+                      struct sss_domain_info *dom,
ca1eb8
+                      bool use_kdcinfo)
ca1eb8
+{
ca1eb8
+    struct ipa_sd_k5_svc_list *k5svc_ent;
ca1eb8
+
ca1eb8
+    /* get the service by realm */
ca1eb8
+    DLIST_FOR_EACH(k5svc_ent, ctx->k5svc_list) {
ca1eb8
+        if (strcasecmp(dom->realm, k5svc_ent->k5svc->realm) == 0) {
ca1eb8
+            break;
ca1eb8
+        }
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    if (k5svc_ent != NULL) {
ca1eb8
+        /* Already exists */
ca1eb8
+        return k5svc_ent->k5svc;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    /* Create a new service */
ca1eb8
+    k5svc_ent = talloc_zero(ctx, struct ipa_sd_k5_svc_list);
ca1eb8
+    if (k5svc_ent == NULL) {
ca1eb8
+        return NULL;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    k5svc_ent->k5svc = krb5_service_new(k5svc_ent,
ca1eb8
+                                        ctx->be_ctx,
ca1eb8
+                                        "IPA",
ca1eb8
+                                        dom->realm,
ca1eb8
+                                        use_kdcinfo);
ca1eb8
+    if (k5svc_ent->k5svc == NULL) {
ca1eb8
+        talloc_free(k5svc_ent);
ca1eb8
+        return NULL;
ca1eb8
+    }
ca1eb8
+    DLIST_ADD(ctx->k5svc_list, k5svc_ent);
ca1eb8
+
ca1eb8
+    return k5svc_ent->k5svc;
ca1eb8
+}
ca1eb8
+
ca1eb8
+static void ipa_subdom_remove_k5_svc(struct ipa_subdomains_ctx *ctx)
ca1eb8
+{
ca1eb8
+    /* Domain going away is such a rare operation that it makes
ca1eb8
+     * more sense to just throw away the whole k5svc_list and let
ca1eb8
+     * the write_kdcinfo request recreate them all again instead
ca1eb8
+     * of coding up complex logic..
ca1eb8
+     */
ca1eb8
+    talloc_zfree(ctx->k5svc_list);
ca1eb8
+}
ca1eb8
+
ca1eb8
+static void ipa_subdom_remove_step(struct ipa_subdomains_ctx *ctx,
ca1eb8
+                                   struct sss_domain_info *dom)
ca1eb8
+{
ca1eb8
+    if (dp_opt_get_bool(ctx->ipa_id_ctx->ipa_options->basic,
ca1eb8
+                        IPA_SERVER_MODE) == false) {
ca1eb8
+        /* IPA clients keep track of krb5_service wrappers */
ca1eb8
+        return ipa_subdom_remove_k5_svc(ctx);
ca1eb8
+    } else {
ca1eb8
+        /* IPA servers keeps track of AD contexts */
ca1eb8
+        return ipa_ad_subdom_remove(ctx->be_ctx, ctx->ipa_id_ctx, dom);
ca1eb8
+    }
ca1eb8
+
ca1eb8
+}
ca1eb8
+
ca1eb8
 static void ipa_subdom_store_step(struct sss_domain_info *parent,
ca1eb8
                                   struct ipa_id_ctx *id_ctx,
ca1eb8
                                   struct sdap_idmap_ctx *sdap_idmap_ctx,
ca1eb8
@@ -697,8 +777,7 @@ static errno_t ipa_subdomains_refresh(struct ipa_subdomains_ctx *ctx,
ca1eb8
                 goto done;
ca1eb8
             }
ca1eb8
 
ca1eb8
-            /* Remove the AD ID ctx from the list of LDAP domains */
ca1eb8
-            ipa_ad_subdom_remove(ctx->be_ctx, ctx->ipa_id_ctx, dom);
ca1eb8
+            ipa_subdom_remove_step(ctx, dom);
ca1eb8
         } else {
ca1eb8
             /* ok let's try to update it */
ca1eb8
             ipa_subdom_store_step(parent, ctx->ipa_id_ctx,
ca1eb8
@@ -1917,6 +1996,611 @@ static errno_t ipa_domain_resolution_order_recv(struct tevent_req *req)
ca1eb8
     return EOK;
ca1eb8
 }
ca1eb8
 
ca1eb8
+struct kdcinfo_from_server_list_state {
ca1eb8
+    struct resolv_hostport *hostport_list;
ca1eb8
+    enum host_database db[2];
ca1eb8
+
ca1eb8
+    struct resolv_hostport_addr **rhp_addrs;
ca1eb8
+    size_t rhp_len;
ca1eb8
+};
ca1eb8
+
ca1eb8
+static void kdcinfo_from_server_list_done(struct tevent_req *subreq);
ca1eb8
+
ca1eb8
+static struct tevent_req *
ca1eb8
+kdcinfo_from_server_list_send(TALLOC_CTX *mem_ctx,
ca1eb8
+                              struct tevent_context *ev,
ca1eb8
+                              struct be_resolv_ctx *be_res,
ca1eb8
+                              const char *servers)
ca1eb8
+{
ca1eb8
+    struct kdcinfo_from_server_list_state *state;
ca1eb8
+    struct tevent_req *req;
ca1eb8
+    struct tevent_req *subreq;
ca1eb8
+    errno_t ret;
ca1eb8
+    int server_list_len;
ca1eb8
+    char **server_list;
ca1eb8
+
ca1eb8
+    req = tevent_req_create(mem_ctx, &state,
ca1eb8
+                            struct kdcinfo_from_server_list_state);
ca1eb8
+    if (req == NULL) {
ca1eb8
+        return NULL;
ca1eb8
+    }
ca1eb8
+    state->db[0] = DB_DNS;
ca1eb8
+    state->db[1] = DB_SENTINEL;
ca1eb8
+
ca1eb8
+    if (servers == NULL) {
ca1eb8
+        ret = EOK;
ca1eb8
+        goto immediately;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    ret = split_on_separator(state, servers, ',', true, true,
ca1eb8
+                             &server_list,
ca1eb8
+                             &server_list_len);
ca1eb8
+    if (ret != EOK) {
ca1eb8
+        DEBUG(SSSDBG_OP_FAILURE, "Failed to parse server list!\n");
ca1eb8
+        goto immediately;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    state->hostport_list = talloc_array(state,
ca1eb8
+                                        struct resolv_hostport,
ca1eb8
+                                        server_list_len);
ca1eb8
+    if (state->hostport_list == NULL) {
ca1eb8
+        ret = ENOMEM;
ca1eb8
+        goto immediately;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    for (int i = 0; i < server_list_len; i++) {
ca1eb8
+        state->hostport_list[i].host = server_list[i];
ca1eb8
+        state->hostport_list[i].port = 0;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    subreq = resolv_hostport_list_send(state,
ca1eb8
+                                       ev,
ca1eb8
+                                       be_res->resolv,
ca1eb8
+                                       state->hostport_list,
ca1eb8
+                                       server_list_len,
ca1eb8
+                                       0,
ca1eb8
+                                       be_res->family_order,
ca1eb8
+                                       state->db);
ca1eb8
+    if (subreq == NULL) {
ca1eb8
+        ret = ENOMEM;
ca1eb8
+        goto immediately;
ca1eb8
+    }
ca1eb8
+    tevent_req_set_callback(subreq, kdcinfo_from_server_list_done, req);
ca1eb8
+    return req;
ca1eb8
+
ca1eb8
+immediately:
ca1eb8
+    if (ret != EOK) {
ca1eb8
+        tevent_req_error(req, ret);
ca1eb8
+    } else {
ca1eb8
+        tevent_req_done(req);
ca1eb8
+    }
ca1eb8
+    tevent_req_post(req, ev);
ca1eb8
+    return req;
ca1eb8
+}
ca1eb8
+
ca1eb8
+static void kdcinfo_from_server_list_done(struct tevent_req *subreq)
ca1eb8
+{
ca1eb8
+    errno_t ret;
ca1eb8
+    struct tevent_req *req =
ca1eb8
+            tevent_req_callback_data(subreq, struct tevent_req);
ca1eb8
+    struct kdcinfo_from_server_list_state *state = tevent_req_data(req,
ca1eb8
+                                        struct kdcinfo_from_server_list_state);
ca1eb8
+
ca1eb8
+    ret = resolv_hostport_list_recv(subreq,
ca1eb8
+                                    state,
ca1eb8
+                                    &state->rhp_len,
ca1eb8
+                                    &state->rhp_addrs);
ca1eb8
+    talloc_zfree(subreq);
ca1eb8
+    if (ret != EOK) {
ca1eb8
+        DEBUG(SSSDBG_OP_FAILURE,
ca1eb8
+              "Failed to resolve address list [%d]: %s\n", ret, sss_strerror(ret));
ca1eb8
+        tevent_req_error(req, ret);
ca1eb8
+        return;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    tevent_req_done(req);
ca1eb8
+}
ca1eb8
+
ca1eb8
+static errno_t kdcinfo_from_server_list_recv(TALLOC_CTX *mem_ctx,
ca1eb8
+                                             struct tevent_req *req,
ca1eb8
+                                             struct resolv_hostport_addr ***_rhp_addrs,
ca1eb8
+                                             size_t *_rhp_len)
ca1eb8
+{
ca1eb8
+    struct kdcinfo_from_server_list_state *state = tevent_req_data(req,
ca1eb8
+                                        struct kdcinfo_from_server_list_state);
ca1eb8
+
ca1eb8
+    TEVENT_REQ_RETURN_ON_ERROR(req);
ca1eb8
+
ca1eb8
+    if (_rhp_addrs != NULL) {
ca1eb8
+        *_rhp_addrs = talloc_steal(mem_ctx, state->rhp_addrs);
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    if (_rhp_len != NULL) {
ca1eb8
+        *_rhp_len = state->rhp_len;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    return EOK;
ca1eb8
+}
ca1eb8
+
ca1eb8
+struct kdcinfo_from_site_state {
ca1eb8
+    struct tevent_context *ev;
ca1eb8
+    struct be_resolv_ctx *be_res;
ca1eb8
+
ca1eb8
+    const char *discovery_domains[2];
ca1eb8
+    struct resolv_hostport *hostport_list;
ca1eb8
+    enum host_database db[2];
ca1eb8
+
ca1eb8
+    struct resolv_hostport_addr **rhp_addrs;
ca1eb8
+    size_t rhp_len;
ca1eb8
+};
ca1eb8
+
ca1eb8
+static void kdcinfo_from_site_srv_done(struct tevent_req *subreq);
ca1eb8
+static void kdcinfo_from_site_server_list_done(struct tevent_req *subreq);
ca1eb8
+
ca1eb8
+static struct tevent_req *
ca1eb8
+kdcinfo_from_site_send(TALLOC_CTX *mem_ctx,
ca1eb8
+                       struct tevent_context *ev,
ca1eb8
+                       struct be_resolv_ctx *be_res,
ca1eb8
+                       const char *site,
ca1eb8
+                       const char *domain)
ca1eb8
+{
ca1eb8
+    struct kdcinfo_from_site_state *state;
ca1eb8
+    struct tevent_req *req;
ca1eb8
+    struct tevent_req *subreq;
ca1eb8
+    errno_t ret;
ca1eb8
+
ca1eb8
+    req = tevent_req_create(mem_ctx, &state,
ca1eb8
+                            struct kdcinfo_from_site_state);
ca1eb8
+    if (req == NULL) {
ca1eb8
+        return NULL;
ca1eb8
+    }
ca1eb8
+    state->ev = ev;
ca1eb8
+    state->be_res = be_res;
ca1eb8
+    state->db[0] = DB_DNS;
ca1eb8
+    state->db[1] = DB_SENTINEL;
ca1eb8
+
ca1eb8
+    state->discovery_domains[0] = ad_site_dns_discovery_domain(state,
ca1eb8
+                                                               site,
ca1eb8
+                                                               domain);
ca1eb8
+    if (state->discovery_domains[0] == NULL) {
ca1eb8
+        ret = ENOMEM;
ca1eb8
+        goto immediately;
ca1eb8
+    }
ca1eb8
+    state->discovery_domains[1] = NULL;
ca1eb8
+
ca1eb8
+    subreq = fo_discover_srv_send(state,
ca1eb8
+                                  state->ev,
ca1eb8
+                                  state->be_res->resolv,
ca1eb8
+                                  "kerberos", "tcp",
ca1eb8
+                                  state->discovery_domains);
ca1eb8
+    if (subreq == NULL) {
ca1eb8
+        ret = ENOMEM;
ca1eb8
+        goto immediately;
ca1eb8
+    }
ca1eb8
+    tevent_req_set_callback(subreq, kdcinfo_from_site_srv_done, req);
ca1eb8
+    return req;
ca1eb8
+
ca1eb8
+immediately:
ca1eb8
+    if (ret != EOK) {
ca1eb8
+        tevent_req_error(req, ret);
ca1eb8
+    } else {
ca1eb8
+        tevent_req_done(req);
ca1eb8
+    }
ca1eb8
+    tevent_req_post(req, ev);
ca1eb8
+    return req;
ca1eb8
+}
ca1eb8
+
ca1eb8
+static void kdcinfo_from_site_srv_done(struct tevent_req *subreq)
ca1eb8
+{
ca1eb8
+    errno_t ret;
ca1eb8
+    struct tevent_req *req =
ca1eb8
+            tevent_req_callback_data(subreq, struct tevent_req);
ca1eb8
+    struct kdcinfo_from_site_state *state = tevent_req_data(req,
ca1eb8
+                                        struct kdcinfo_from_site_state);
ca1eb8
+    struct fo_server_info *servers;
ca1eb8
+    size_t num_servers;
ca1eb8
+
ca1eb8
+    ret = fo_discover_srv_recv(state, subreq,
ca1eb8
+                               NULL, NULL, /* not interested in TTL etc */
ca1eb8
+                               &servers, &num_servers);
ca1eb8
+    talloc_zfree(subreq);
ca1eb8
+    if (ret != EOK) {
ca1eb8
+        DEBUG(SSSDBG_OP_FAILURE,
ca1eb8
+              "Could not resolve the site [%d]: %s\n", ret, sss_strerror(ret));
ca1eb8
+        tevent_req_error(req, ret);
ca1eb8
+        return;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    state->hostport_list = talloc_array(state,
ca1eb8
+                                        struct resolv_hostport,
ca1eb8
+                                        num_servers);
ca1eb8
+    if (state->hostport_list == NULL) {
ca1eb8
+        tevent_req_error(req, ENOMEM);
ca1eb8
+        return;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    for (size_t i = 0; i < num_servers; i++) {
ca1eb8
+        state->hostport_list[i].host = servers[i].host;
ca1eb8
+        state->hostport_list[i].port = servers[i].port;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    subreq = resolv_hostport_list_send(state,
ca1eb8
+                                       state->ev,
ca1eb8
+                                       state->be_res->resolv,
ca1eb8
+                                       state->hostport_list,
ca1eb8
+                                       num_servers,
ca1eb8
+                                       MAX_SERVERS_FROM_SRV,
ca1eb8
+                                       state->be_res->family_order,
ca1eb8
+                                       state->db);
ca1eb8
+    if (subreq == NULL) {
ca1eb8
+        tevent_req_error(req, ENOMEM);
ca1eb8
+        return;
ca1eb8
+    }
ca1eb8
+    tevent_req_set_callback(subreq, kdcinfo_from_site_server_list_done, req);
ca1eb8
+}
ca1eb8
+
ca1eb8
+static void kdcinfo_from_site_server_list_done(struct tevent_req *subreq)
ca1eb8
+{
ca1eb8
+    errno_t ret;
ca1eb8
+    struct tevent_req *req =
ca1eb8
+            tevent_req_callback_data(subreq, struct tevent_req);
ca1eb8
+    struct kdcinfo_from_site_state *state = tevent_req_data(req,
ca1eb8
+                                        struct kdcinfo_from_site_state);
ca1eb8
+
ca1eb8
+    ret = resolv_hostport_list_recv(subreq,
ca1eb8
+                                    state,
ca1eb8
+                                    &state->rhp_len,
ca1eb8
+                                    &state->rhp_addrs);
ca1eb8
+    talloc_zfree(subreq);
ca1eb8
+    if (ret != EOK) {
ca1eb8
+        DEBUG(SSSDBG_OP_FAILURE,
ca1eb8
+              "Failed to resolve address list [%d]: %s\n",
ca1eb8
+              ret, sss_strerror(ret));
ca1eb8
+        tevent_req_error(req, ret);
ca1eb8
+        return;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    tevent_req_done(req);
ca1eb8
+}
ca1eb8
+
ca1eb8
+
ca1eb8
+static errno_t kdcinfo_from_site_recv(TALLOC_CTX *mem_ctx,
ca1eb8
+                                      struct tevent_req *req,
ca1eb8
+                                      struct resolv_hostport_addr ***_rhp_addrs,
ca1eb8
+                                      size_t *_rhp_len)
ca1eb8
+{
ca1eb8
+    struct kdcinfo_from_site_state *state = tevent_req_data(req,
ca1eb8
+                                        struct kdcinfo_from_site_state);
ca1eb8
+
ca1eb8
+    TEVENT_REQ_RETURN_ON_ERROR(req);
ca1eb8
+
ca1eb8
+    if (_rhp_addrs != NULL) {
ca1eb8
+        *_rhp_addrs = talloc_steal(mem_ctx, state->rhp_addrs);
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    if (_rhp_len != NULL) {
ca1eb8
+        *_rhp_len = state->rhp_len;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    return EOK;
ca1eb8
+}
ca1eb8
+
ca1eb8
+/* Anything per-domain in this request goes here so that we
ca1eb8
+ * can just free the whole struct without mixing data from
ca1eb8
+ * different domains or the overhead of another request
ca1eb8
+ */
ca1eb8
+struct ipa_sd_per_dom_kdcinfo_ctx {
ca1eb8
+    struct sss_domain_info *dom;
ca1eb8
+
ca1eb8
+    const char *servers;
ca1eb8
+    const char *site;
ca1eb8
+
ca1eb8
+    const char *discovery_domains[2];
ca1eb8
+    struct krb5_service *krb5_service;
ca1eb8
+};
ca1eb8
+
ca1eb8
+struct ipa_subdomains_write_kdcinfo_state {
ca1eb8
+    struct tevent_context *ev;
ca1eb8
+    struct ipa_subdomains_ctx *ipa_sd_ctx;
ca1eb8
+    struct be_ctx *be_ctx;
ca1eb8
+
ca1eb8
+    bool use_kdcinfo;
ca1eb8
+    struct ipa_sd_per_dom_kdcinfo_ctx *pdctx;
ca1eb8
+};
ca1eb8
+
ca1eb8
+static errno_t ipa_subdomains_write_kdcinfo_domain_step(struct sss_domain_info *start_dom,
ca1eb8
+                                                        struct tevent_req *req);
ca1eb8
+static void ipa_subdomains_write_kdcinfo_domain_done(struct tevent_req *subreq);
ca1eb8
+static errno_t ipa_subdomains_write_kdcinfo_write_step(struct sss_domain_info *dom,
ca1eb8
+                                                       struct krb5_service *krb5_service,
ca1eb8
+                                                       struct resolv_hostport_addr **rhp_addrs,
ca1eb8
+                                                       size_t rhp_len);
ca1eb8
+
ca1eb8
+static struct tevent_req *
ca1eb8
+ipa_subdomains_write_kdcinfo_send(TALLOC_CTX *mem_ctx,
ca1eb8
+                                  struct tevent_context *ev,
ca1eb8
+                                  struct ipa_subdomains_ctx *ipa_sd_ctx,
ca1eb8
+                                  struct be_ctx *be_ctx)
ca1eb8
+{
ca1eb8
+    struct ipa_subdomains_write_kdcinfo_state *state;
ca1eb8
+    struct tevent_req *req;
ca1eb8
+    errno_t ret;
ca1eb8
+
ca1eb8
+    req = tevent_req_create(mem_ctx, &state,
ca1eb8
+                            struct ipa_subdomains_write_kdcinfo_state);
ca1eb8
+    if (req == NULL) {
ca1eb8
+        return NULL;
ca1eb8
+    }
ca1eb8
+    state->ev = ev;
ca1eb8
+    state->ipa_sd_ctx = ipa_sd_ctx;
ca1eb8
+    state->be_ctx = be_ctx;
ca1eb8
+
ca1eb8
+    if (ipa_sd_ctx->ipa_id_ctx->server_mode != NULL) {
ca1eb8
+        /* This request is valid for clients only */
ca1eb8
+        ret = EOK;
ca1eb8
+        goto immediately;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    state->use_kdcinfo = dp_opt_get_bool(ipa_sd_ctx->ipa_id_ctx->ipa_options->auth,
ca1eb8
+                                         KRB5_USE_KDCINFO);
ca1eb8
+    if (state->use_kdcinfo == false) {
ca1eb8
+        DEBUG(SSSDBG_CONF_SETTINGS, "kdcinfo creation disabled\n");
ca1eb8
+        ret = EOK;
ca1eb8
+        goto immediately;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    if (be_ctx->domain->subdomains == NULL) {
ca1eb8
+        DEBUG(SSSDBG_CONF_SETTINGS, "No subdomains, done\n");
ca1eb8
+        ret = EOK;
ca1eb8
+        goto immediately;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    ret = ipa_subdomains_write_kdcinfo_domain_step(be_ctx->domain->subdomains,
ca1eb8
+                                                   req);
ca1eb8
+    if (ret != EAGAIN) {
ca1eb8
+        goto immediately;
ca1eb8
+    }
ca1eb8
+    return req;
ca1eb8
+
ca1eb8
+immediately:
ca1eb8
+    if (ret == EOK) {
ca1eb8
+        tevent_req_done(req);
ca1eb8
+    } else {
ca1eb8
+        tevent_req_error(req, ret);
ca1eb8
+    }
ca1eb8
+    tevent_req_post(req, ev);
ca1eb8
+    return req;
ca1eb8
+}
ca1eb8
+
ca1eb8
+static errno_t ipa_subdomains_write_kdcinfo_domain_step(struct sss_domain_info *start_dom,
ca1eb8
+                                                        struct tevent_req *req)
ca1eb8
+{
ca1eb8
+    struct ipa_subdomains_write_kdcinfo_state *state = \
ca1eb8
+                tevent_req_data(req,
ca1eb8
+                                struct ipa_subdomains_write_kdcinfo_state);
ca1eb8
+    struct dp_option *ipa_ad_subdom_opts;
ca1eb8
+    struct tevent_req *subreq = NULL;
ca1eb8
+    char *subdom_conf_path;
ca1eb8
+    errno_t ret;
ca1eb8
+    const char *servers;
ca1eb8
+    const char *site;
ca1eb8
+
ca1eb8
+    for (struct sss_domain_info *dom = start_dom;
ca1eb8
+            dom != NULL;
ca1eb8
+            dom = get_next_domain(dom, 0)) {
ca1eb8
+
ca1eb8
+        talloc_zfree(state->pdctx);
ca1eb8
+
ca1eb8
+        subdom_conf_path = subdomain_create_conf_path(state, dom);
ca1eb8
+        if (subdom_conf_path == NULL) {
ca1eb8
+            DEBUG(SSSDBG_MINOR_FAILURE,
ca1eb8
+                  "subdom_conf_path failed for %s\n", dom->name);
ca1eb8
+            /* Not fatal */
ca1eb8
+            continue;
ca1eb8
+        }
ca1eb8
+
ca1eb8
+        ret = dp_get_options(state, state->be_ctx->cdb,
ca1eb8
+                             subdom_conf_path,
ca1eb8
+                             ipa_cli_ad_subdom_opts,
ca1eb8
+                             IPA_OPTS_CLI_AD_SUBDOM,
ca1eb8
+                             &ipa_ad_subdom_opts);
ca1eb8
+        if (ret != EOK) {
ca1eb8
+            DEBUG(SSSDBG_MINOR_FAILURE,
ca1eb8
+                  "Cannot get options for %s: [%d]: %s\n",
ca1eb8
+                  dom->name, ret, sss_strerror(ret));
ca1eb8
+            /* Not fatal */
ca1eb8
+            continue;
ca1eb8
+        }
ca1eb8
+
ca1eb8
+        servers = dp_opt_get_string(ipa_ad_subdom_opts, IPA_CLI_AD_SERVER);
ca1eb8
+        site = dp_opt_get_string(ipa_ad_subdom_opts, IPA_CLI_AD_SITE);
ca1eb8
+
ca1eb8
+        if (servers == NULL && site == NULL) {
ca1eb8
+            /* If neither is set, just go to the next domain */
ca1eb8
+            DEBUG(SSSDBG_TRACE_INTERNAL,
ca1eb8
+                  "No site or server defined for %s, skipping\n",
ca1eb8
+                  dom->name);
ca1eb8
+            continue;
ca1eb8
+        }
ca1eb8
+
ca1eb8
+        /* We will resolve this domain, create a per-domain context */
ca1eb8
+        state->pdctx = talloc_zero(state, struct ipa_sd_per_dom_kdcinfo_ctx);
ca1eb8
+        if (state->pdctx == NULL) {
ca1eb8
+            return ENOMEM;
ca1eb8
+        }
ca1eb8
+        state->pdctx->dom = dom;
ca1eb8
+        state->pdctx->servers = servers;
ca1eb8
+        state->pdctx->site = site;
ca1eb8
+        state->pdctx->krb5_service = ipa_subdom_get_k5_svc(state->ipa_sd_ctx,
ca1eb8
+                                                           dom,
ca1eb8
+                                                           state->use_kdcinfo);
ca1eb8
+        if (state->pdctx->krb5_service == NULL) {
ca1eb8
+            continue;
ca1eb8
+        }
ca1eb8
+
ca1eb8
+        if (state->pdctx->servers != NULL) {
ca1eb8
+            DEBUG(SSSDBG_CONF_SETTINGS,
ca1eb8
+                  "Resolving servers [%s] for domain %s\n",
ca1eb8
+                  state->pdctx->servers, dom->name);
ca1eb8
+
ca1eb8
+            subreq = kdcinfo_from_server_list_send(state,
ca1eb8
+                                                   state->ev,
ca1eb8
+                                                   state->be_ctx->be_res,
ca1eb8
+                                                   state->pdctx->servers);
ca1eb8
+        } else if (state->pdctx->site != NULL) {
ca1eb8
+            DEBUG(SSSDBG_CONF_SETTINGS,
ca1eb8
+                  "Resolving site %s for domain %s\n",
ca1eb8
+                  state->pdctx->site, dom->name);
ca1eb8
+
ca1eb8
+            subreq = kdcinfo_from_site_send(state,
ca1eb8
+                                            state->ev,
ca1eb8
+                                            state->be_ctx->be_res,
ca1eb8
+                                            state->pdctx->site,
ca1eb8
+                                            state->pdctx->dom->name);
ca1eb8
+        } else {
ca1eb8
+            /* We should never get here */
ca1eb8
+            return EINVAL;
ca1eb8
+        }
ca1eb8
+
ca1eb8
+        if (subreq == NULL) {
ca1eb8
+            return ENOMEM;
ca1eb8
+        }
ca1eb8
+        tevent_req_set_callback(subreq, ipa_subdomains_write_kdcinfo_domain_done, req);
ca1eb8
+        return EAGAIN;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    return EOK;
ca1eb8
+}
ca1eb8
+
ca1eb8
+static void ipa_subdomains_write_kdcinfo_domain_done(struct tevent_req *subreq)
ca1eb8
+{
ca1eb8
+    errno_t ret;
ca1eb8
+    struct tevent_req *req =
ca1eb8
+            tevent_req_callback_data(subreq, struct tevent_req);
ca1eb8
+    struct ipa_subdomains_write_kdcinfo_state *state = \
ca1eb8
+                tevent_req_data(req,
ca1eb8
+                                struct ipa_subdomains_write_kdcinfo_state);
ca1eb8
+    struct sss_domain_info *next_domain;
ca1eb8
+    struct resolv_hostport_addr **rhp_addrs;
ca1eb8
+    size_t rhp_len;
ca1eb8
+
ca1eb8
+    if (state->pdctx->servers != NULL) {
ca1eb8
+        ret = kdcinfo_from_server_list_recv(state->pdctx, subreq,
ca1eb8
+                                            &rhp_addrs, &rhp_len);
ca1eb8
+    } else if (state->pdctx->site != NULL) {
ca1eb8
+        ret = kdcinfo_from_site_recv(state->pdctx, subreq,
ca1eb8
+                                     &rhp_addrs, &rhp_len);
ca1eb8
+    } else {
ca1eb8
+        DEBUG(SSSDBG_CRIT_FAILURE, "Neither site nor servers set\n");
ca1eb8
+        ret = EINVAL;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    if (ret == EOK) {
ca1eb8
+        ret = ipa_subdomains_write_kdcinfo_write_step(state->pdctx->dom,
ca1eb8
+                                                      state->pdctx->krb5_service,
ca1eb8
+                                                      rhp_addrs, rhp_len);
ca1eb8
+        if (ret != EOK) {
ca1eb8
+            DEBUG(SSSDBG_MINOR_FAILURE,
ca1eb8
+                  "Could not write kdcinfo file for %s\n", state->pdctx->dom->name);
ca1eb8
+            /* Not fatal, loop to the next domain below */
ca1eb8
+        }
ca1eb8
+    } else {
ca1eb8
+        DEBUG(SSSDBG_MINOR_FAILURE,
ca1eb8
+              "Could not get address list for %s\n", state->pdctx->dom->name);
ca1eb8
+        /* Not fatal, loop to the next domain below */
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    next_domain = get_next_domain(state->pdctx->dom, 0);
ca1eb8
+    ret = ipa_subdomains_write_kdcinfo_domain_step(next_domain, req);
ca1eb8
+    if (ret == EOK) {
ca1eb8
+        tevent_req_done(req);
ca1eb8
+        return;
ca1eb8
+    } else if (ret != EAGAIN) {
ca1eb8
+        /* the loop in ipa_subdomains_write_kdcinfo_domain_step already
ca1eb8
+         * tries to be quite permissive, so any error is fatal
ca1eb8
+         */
ca1eb8
+        tevent_req_error(req, ret);
ca1eb8
+        return;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    /* Continue to the next domain */
ca1eb8
+}
ca1eb8
+
ca1eb8
+static errno_t ipa_subdomains_write_kdcinfo_write_step(struct sss_domain_info *dom,
ca1eb8
+                                                       struct krb5_service *krb5_service,
ca1eb8
+                                                       struct resolv_hostport_addr **rhp_addrs,
ca1eb8
+                                                       size_t rhp_len)
ca1eb8
+{
ca1eb8
+    errno_t ret;
ca1eb8
+    char *address = NULL;
ca1eb8
+    char *safe_address = NULL;
ca1eb8
+    char **safe_addr_list;
ca1eb8
+    int addr_index = 0;
ca1eb8
+    TALLOC_CTX *tmp_ctx = NULL;
ca1eb8
+
ca1eb8
+    tmp_ctx = talloc_new(NULL);
ca1eb8
+    if (tmp_ctx == NULL) {
ca1eb8
+        return ENOMEM;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    safe_addr_list = talloc_zero_array(tmp_ctx, char *, rhp_len+1);
ca1eb8
+    if (safe_addr_list == NULL) {
ca1eb8
+        ret = ENOMEM;
ca1eb8
+        goto done;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    for (size_t i = 0; i < rhp_len; i++) {
ca1eb8
+        address = resolv_get_string_address(tmp_ctx, rhp_addrs[i]->reply);
ca1eb8
+        if (address == NULL) {
ca1eb8
+            DEBUG(SSSDBG_CRIT_FAILURE, "resolv_get_string_address failed.\n");
ca1eb8
+            continue;
ca1eb8
+        }
ca1eb8
+
ca1eb8
+        if (rhp_addrs[i]->origin.port != 0) {
ca1eb8
+            address = talloc_asprintf_append(address,
ca1eb8
+                                             ":%d",
ca1eb8
+                                             rhp_addrs[i]->origin.port);
ca1eb8
+        }
ca1eb8
+
ca1eb8
+        safe_address = sss_escape_ip_address(tmp_ctx,
ca1eb8
+                                             rhp_addrs[i]->reply->family,
ca1eb8
+                                             address);
ca1eb8
+        talloc_zfree(address);
ca1eb8
+        if (safe_address == NULL) {
ca1eb8
+            DEBUG(SSSDBG_CRIT_FAILURE, "sss_escape_ip_address failed.\n");
ca1eb8
+            continue;
ca1eb8
+        }
ca1eb8
+
ca1eb8
+        DEBUG(SSSDBG_CONF_SETTINGS,
ca1eb8
+              "Will write [%s] for %s\n",
ca1eb8
+              safe_address, dom->name);
ca1eb8
+
ca1eb8
+        safe_addr_list[addr_index] = talloc_steal(safe_addr_list,
ca1eb8
+                                                  safe_address);
ca1eb8
+        addr_index++;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    ret = write_krb5info_file(krb5_service,
ca1eb8
+                              safe_addr_list,
ca1eb8
+                              SSS_KRB5KDC_FO_SRV);
ca1eb8
+    if (ret != EOK) {
ca1eb8
+        DEBUG(SSSDBG_OP_FAILURE,
ca1eb8
+                "write_krb5info_file failed, authentication might fail.\n");
ca1eb8
+        goto done;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    ret = EOK;
ca1eb8
+done:
ca1eb8
+    talloc_free(tmp_ctx);
ca1eb8
+    return ret;
ca1eb8
+}
ca1eb8
+
ca1eb8
+static errno_t ipa_subdomains_write_kdcinfo_recv(struct tevent_req *req)
ca1eb8
+{
ca1eb8
+    TEVENT_REQ_RETURN_ON_ERROR(req);
ca1eb8
+    return EOK;
ca1eb8
+}
ca1eb8
+
ca1eb8
 struct ipa_subdomains_refresh_state {
ca1eb8
     struct tevent_context *ev;
ca1eb8
     struct ipa_subdomains_ctx *sd_ctx;
ca1eb8
@@ -1933,6 +2617,7 @@ static void ipa_subdomains_refresh_view_name_done(struct tevent_req *subreq);
ca1eb8
 static void ipa_subdomains_refresh_view_domain_resolution_order_done(
ca1eb8
                                                     struct tevent_req *subreq);
ca1eb8
 static void ipa_domain_refresh_resolution_order_done(struct tevent_req *subreq);
ca1eb8
+static void ipa_domain_refresh_kdcinfo_done(struct tevent_req *subreq);
ca1eb8
 
ca1eb8
 static struct tevent_req *
ca1eb8
 ipa_subdomains_refresh_send(TALLOC_CTX *mem_ctx,
ca1eb8
@@ -2253,6 +2938,35 @@ ipa_domain_refresh_resolution_order_done(struct tevent_req *subreq)
ca1eb8
         return;
ca1eb8
     }
ca1eb8
 
ca1eb8
+    subreq = ipa_subdomains_write_kdcinfo_send(state,
ca1eb8
+                                               state->ev,
ca1eb8
+                                               state->sd_ctx,
ca1eb8
+                                               state->sd_ctx->be_ctx);
ca1eb8
+    if (subreq == NULL) {
ca1eb8
+        tevent_req_error(req, ENOMEM);
ca1eb8
+        return;
ca1eb8
+    }
ca1eb8
+    tevent_req_set_callback(subreq, ipa_domain_refresh_kdcinfo_done, req);
ca1eb8
+}
ca1eb8
+
ca1eb8
+static void
ca1eb8
+ipa_domain_refresh_kdcinfo_done(struct tevent_req *subreq)
ca1eb8
+{
ca1eb8
+    struct tevent_req *req;
ca1eb8
+    errno_t ret;
ca1eb8
+
ca1eb8
+    req = tevent_req_callback_data(subreq, struct tevent_req);
ca1eb8
+
ca1eb8
+    ret = ipa_subdomains_write_kdcinfo_recv(subreq);
ca1eb8
+    talloc_zfree(subreq);
ca1eb8
+    if (ret != EOK) {
ca1eb8
+        DEBUG(SSSDBG_MINOR_FAILURE,
ca1eb8
+              "Unable to write the kdc info files, authentication might "
ca1eb8
+              "fail or time out [%d]: %s\n",
ca1eb8
+              ret, sss_strerror(ret));
ca1eb8
+        /* Not fatal, let's hope DNS is set correctly */
ca1eb8
+    }
ca1eb8
+
ca1eb8
     tevent_req_done(req);
ca1eb8
 }
ca1eb8
 
ca1eb8
-- 
ca1eb8
2.17.1
ca1eb8