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

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