Blob Blame History Raw
From f61c92c399531a5530dbb57a36b4e0db46c72b5b Mon Sep 17 00:00:00 2001
From: Tomas Halman <thalman@redhat.com>
Date: Wed, 13 Mar 2019 08:37:36 +0100
Subject: [PATCH 22/23] krb5: Write multiple dnsnames into kdc info file

Multiple servers should be written to kdc info file. In
this PR we iterate trough server list and we write
list of primary servers followed by backup servers.

Resolves:
https://pagure.io/SSSD/sssd/issue/3974

Reviewed-by: Sumit Bose <sbose@redhat.com>
(cherry picked from commit 208a79a83c76b6693bdf927c3d7d6267e3218b0b)
---
 src/providers/ad/ad_common.c       |  39 +++++----
 src/providers/fail_over.c          |  27 ++++++
 src/providers/fail_over.h          |   9 ++
 src/providers/ipa/ipa_common.c     |  25 +-----
 src/providers/ipa/ipa_subdomains.c |   4 +-
 src/providers/krb5/krb5_common.c   | 131 ++++++++++++++++++++---------
 src/providers/krb5/krb5_common.h   |   7 +-
 7 files changed, 158 insertions(+), 84 deletions(-)

diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c
index 0d154ca57..b7f34daa9 100644
--- a/src/providers/ad/ad_common.c
+++ b/src/providers/ad/ad_common.c
@@ -24,6 +24,7 @@
 #include "providers/ad/ad_common.h"
 #include "providers/ad/ad_opts.h"
 #include "providers/be_dyndns.h"
+#include "providers/fail_over.h"
 
 struct ad_server_data {
     bool gc;
@@ -839,6 +840,20 @@ done:
     return ret;
 }
 
+static bool
+ad_krb5info_file_filter(struct fo_server *server)
+{
+    struct ad_server_data *sdata = NULL;
+    if (server == NULL) return true;
+
+    sdata = fo_get_server_user_data(server);
+    if (sdata && sdata->gc) {
+        /* Only write kdcinfo files for local servers */
+        return true;
+    }
+    return false;
+}
+
 static void
 ad_resolve_callback(void *private_data, struct fo_server *server)
 {
@@ -848,7 +863,6 @@ ad_resolve_callback(void *private_data, struct fo_server *server)
     struct resolv_hostent *srvaddr;
     struct sockaddr_storage *sockaddr;
     char *address;
-    char *safe_addr_list[2] = { NULL, NULL };
     char *new_uri;
     int new_port;
     const char *srv_name;
@@ -953,25 +967,14 @@ ad_resolve_callback(void *private_data, struct fo_server *server)
         goto done;
     }
 
-    /* Only write kdcinfo files for local servers */
-    if ((sdata == NULL || sdata->gc == false) &&
-        service->krb5_service->write_kdcinfo) {
-        /* Write krb5 info files */
-        safe_addr_list[0] = sss_escape_ip_address(tmp_ctx,
-                                                  srvaddr->family,
-                                                  address);
-        if (safe_addr_list[0] == NULL) {
-            DEBUG(SSSDBG_CRIT_FAILURE, "sss_escape_ip_address failed.\n");
-            ret = ENOMEM;
-            goto done;
-        }
-
-        ret = write_krb5info_file(service->krb5_service,
-                                  safe_addr_list,
-                                  SSS_KRB5KDC_FO_SRV);
+    if (service->krb5_service->write_kdcinfo) {
+        ret = write_krb5info_file_from_fo_server(service->krb5_service,
+                                                 server,
+                                                 SSS_KRB5KDC_FO_SRV,
+                                                 ad_krb5info_file_filter);
         if (ret != EOK) {
             DEBUG(SSSDBG_MINOR_FAILURE,
-                "write_krb5info_file failed, authentication might fail.\n");
+                  "write_krb5info_file failed, authentication might fail.\n");
         }
     }
 
diff --git a/src/providers/fail_over.c b/src/providers/fail_over.c
index 168e59d6f..dc5c7c7d8 100644
--- a/src/providers/fail_over.c
+++ b/src/providers/fail_over.c
@@ -1637,6 +1637,33 @@ fo_get_server_hostname_last_change(struct fo_server *server)
     return server->common->last_status_change.tv_sec;
 }
 
+struct fo_server *fo_server_first(struct fo_server *server)
+{
+    if (!server) return NULL;
+
+    while (server->prev) { server = server->prev; }
+    return server;
+}
+
+struct fo_server *fo_server_next(struct fo_server *server)
+{
+    if (!server) return NULL;
+
+    return server->next;
+}
+
+size_t fo_server_count(struct fo_server *server)
+{
+    struct fo_server *item = fo_server_first(server);
+    size_t size = 0;
+
+    while (item) {
+        ++size;
+        item = item->next;
+    }
+    return size;
+}
+
 time_t fo_get_service_retry_timeout(struct fo_service *svc)
 {
     if (svc == NULL || svc->ctx == NULL || svc->ctx->opts == NULL) {
diff --git a/src/providers/fail_over.h b/src/providers/fail_over.h
index d70212fb7..bc8142710 100644
--- a/src/providers/fail_over.h
+++ b/src/providers/fail_over.h
@@ -216,6 +216,15 @@ const char **fo_svc_server_list(TALLOC_CTX *mem_ctx,
                                 struct fo_service *service,
                                 size_t *_count);
 
+/*
+ * Folowing functions allow to iterate trough list of servers.
+ */
+struct fo_server *fo_server_first(struct fo_server *server);
+
+struct fo_server *fo_server_next(struct fo_server *server);
+
+size_t fo_server_count(struct fo_server *server);
+
 /*
  * pvt will be talloc_stealed to ctx
  */
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index 17d14e6b0..1ed2e2203 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -819,8 +819,6 @@ static void ipa_resolve_callback(void *private_data, struct fo_server *server)
     struct ipa_service *service;
     struct resolv_hostent *srvaddr;
     struct sockaddr_storage *sockaddr;
-    char *address;
-    char *safe_addr_list[2] = { NULL, NULL };
     char *new_uri;
     const char *srv_name;
     int ret;
@@ -854,13 +852,6 @@ static void ipa_resolve_callback(void *private_data, struct fo_server *server)
         return;
     }
 
-    address = resolv_get_string_address(tmp_ctx, srvaddr);
-    if (address == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "resolv_get_string_address failed.\n");
-        talloc_free(tmp_ctx);
-        return;
-    }
-
     srv_name = fo_get_server_name(server);
     if (srv_name == NULL) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Could not get server host name\n");
@@ -883,18 +874,10 @@ static void ipa_resolve_callback(void *private_data, struct fo_server *server)
     service->sdap->sockaddr = talloc_steal(service, sockaddr);
 
     if (service->krb5_service->write_kdcinfo) {
-        safe_addr_list[0] = sss_escape_ip_address(tmp_ctx,
-                                             srvaddr->family,
-                                             address);
-        if (safe_addr_list[0] == NULL) {
-            DEBUG(SSSDBG_CRIT_FAILURE, "sss_escape_ip_address failed.\n");
-            talloc_free(tmp_ctx);
-            return;
-        }
-
-        ret = write_krb5info_file(service->krb5_service,
-                                  safe_addr_list,
-                                  SSS_KRB5KDC_FO_SRV);
+        ret = write_krb5info_file_from_fo_server(service->krb5_service,
+                                                 server,
+                                                 SSS_KRB5KDC_FO_SRV,
+                                                 NULL);
         if (ret != EOK) {
             DEBUG(SSSDBG_OP_FAILURE,
                   "write_krb5info_file failed, authentication might fail.\n");
diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
index d86ca4cc5..da1279e3e 100644
--- a/src/providers/ipa/ipa_subdomains.c
+++ b/src/providers/ipa/ipa_subdomains.c
@@ -2545,7 +2545,7 @@ static errno_t ipa_subdomains_write_kdcinfo_write_step(struct sss_domain_info *d
     errno_t ret;
     char *address = NULL;
     char *safe_address = NULL;
-    char **safe_addr_list;
+    const char **safe_addr_list;
     int addr_index = 0;
     TALLOC_CTX *tmp_ctx = NULL;
 
@@ -2554,7 +2554,7 @@ static errno_t ipa_subdomains_write_kdcinfo_write_step(struct sss_domain_info *d
         return ENOMEM;
     }
 
-    safe_addr_list = talloc_zero_array(tmp_ctx, char *, rhp_len+1);
+    safe_addr_list = talloc_zero_array(tmp_ctx, const char *, rhp_len+1);
     if (safe_addr_list == NULL) {
         ret = ENOMEM;
         goto done;
diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c
index 2b003e164..1e33fc0f5 100644
--- a/src/providers/krb5/krb5_common.c
+++ b/src/providers/krb5/krb5_common.c
@@ -33,6 +33,7 @@
 #include "providers/krb5/krb5_common.h"
 #include "providers/krb5/krb5_opts.h"
 #include "providers/krb5/krb5_utils.h"
+#include "providers/fail_over.h"
 
 #ifdef HAVE_KRB5_CC_COLLECTION
 /* krb5 profile functions */
@@ -592,7 +593,7 @@ done:
 }
 
 errno_t write_krb5info_file(struct krb5_service *krb5_service,
-                            char **server_list,
+                            const char **server_list,
                             const char *service)
 {
     int i;
@@ -635,73 +636,119 @@ done:
     return ret;
 }
 
-static void krb5_resolve_callback(void *private_data, struct fo_server *server)
+static const char* fo_server_address_or_name(TALLOC_CTX *tmp_ctx, struct fo_server *server)
 {
-    struct krb5_service *krb5_service;
     struct resolv_hostent *srvaddr;
     char *address;
-    char *safe_addr_list[2] = { NULL, NULL };
-    int ret;
+
+    if (!server) return NULL;
+
+    srvaddr = fo_get_server_hostent(server);
+    if (srvaddr) {
+        address = resolv_get_string_address(tmp_ctx, srvaddr);
+        if (address) {
+            return sss_escape_ip_address(tmp_ctx,
+                                         srvaddr->family,
+                                         address);
+        }
+    }
+
+    return fo_get_server_name(server);
+}
+
+errno_t write_krb5info_file_from_fo_server(struct krb5_service *krb5_service,
+                                           struct fo_server *server,
+                                           const char *service,
+                                           bool (*filter)(struct fo_server *))
+{
     TALLOC_CTX *tmp_ctx = NULL;
+    const char **server_list;
+    size_t server_idx;
+    struct fo_server *item;
+    int primary;
+    const char *address;
+    errno_t ret;
 
     tmp_ctx = talloc_new(NULL);
     if (tmp_ctx == NULL) {
         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed\n");
-        return;
+        return ENOMEM;
     }
 
-    krb5_service = talloc_get_type(private_data, struct krb5_service);
-    if (!krb5_service) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "FATAL: Bad private_data\n");
+    server_idx = 0;
+    server_list = talloc_zero_array(tmp_ctx,
+                                    const char *,
+                                    fo_server_count(server) + 1);
+    if (server_list == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero_array failed\n");
         talloc_free(tmp_ctx);
-        return;
+        return ENOMEM;
     }
 
-    srvaddr = fo_get_server_hostent(server);
-    if (!srvaddr) {
-        DEBUG(SSSDBG_CRIT_FAILURE,
-              "FATAL: No hostent available for server (%s)\n",
-                  fo_get_server_str_name(server));
-        talloc_free(tmp_ctx);
-        return;
+    if (filter == NULL || filter(server) == false) {
+        address = fo_server_address_or_name(tmp_ctx, server);
+        if (address) {
+            server_list[server_idx++] = address;
+        } else {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "Server without name and address found in list.\n");
+        }
     }
 
-    address = resolv_get_string_address(tmp_ctx, srvaddr);
-    if (address == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "resolv_get_string_address failed.\n");
-        talloc_free(tmp_ctx);
-        return;
+    for (primary = 1; primary >= 0; --primary) {
+        for (item = fo_server_next(server) ? fo_server_next(server) : fo_server_first(server);
+             item != server;
+             item = fo_server_next(item) ? fo_server_next(item) : fo_server_first(item)) {
+
+            if (primary && !fo_is_server_primary(item)) continue;
+            if (!primary && fo_is_server_primary(item)) continue;
+            if (filter != NULL && filter(item)) continue;
+
+            address = fo_server_address_or_name(tmp_ctx, item);
+            if (address == NULL) {
+                DEBUG(SSSDBG_CRIT_FAILURE,
+                      "Server without name and address found in list.\n");
+                continue;
+            }
+
+            server_list[server_idx++] = address;
+        }
     }
+    if (server_list[0] == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "There is no server that can be written into kdc info file.\n");
+        ret = EINVAL;
+    } else {
+        ret = write_krb5info_file(krb5_service,
+                                  server_list,
+                                  service);
+    }
+    talloc_free(tmp_ctx);
+    return ret;
+}
 
-    safe_addr_list[0] = sss_escape_ip_address(tmp_ctx,
-                                              srvaddr->family,
-                                              address);
-    if (safe_addr_list[0] == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "sss_escape_ip_address failed.\n");
-        talloc_free(tmp_ctx);
+
+static void krb5_resolve_callback(void *private_data, struct fo_server *server)
+{
+    struct krb5_service *krb5_service;
+    int ret;
+
+    krb5_service = talloc_get_type(private_data, struct krb5_service);
+    if (!krb5_service) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "FATAL: Bad private_data\n");
         return;
     }
 
     if (krb5_service->write_kdcinfo) {
-        safe_addr_list[0] = talloc_asprintf_append(safe_addr_list[0], ":%d",
-                                                   fo_get_server_port(server));
-        if (safe_addr_list[0] == NULL) {
-            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf_append failed.\n");
-            talloc_free(tmp_ctx);
-            return;
-        }
-
-        ret = write_krb5info_file(krb5_service,
-                                  safe_addr_list,
-                                  krb5_service->name);
+        ret = write_krb5info_file_from_fo_server(krb5_service,
+                                                 server,
+                                                 krb5_service->name,
+                                                 NULL);
         if (ret != EOK) {
             DEBUG(SSSDBG_OP_FAILURE,
                   "write_krb5info_file failed, authentication might fail.\n");
         }
     }
-
-    talloc_free(tmp_ctx);
-    return;
 }
 
 static errno_t _krb5_servers_init(struct be_ctx *ctx,
diff --git a/src/providers/krb5/krb5_common.h b/src/providers/krb5/krb5_common.h
index bf36a551a..be541626b 100644
--- a/src/providers/krb5/krb5_common.h
+++ b/src/providers/krb5/krb5_common.h
@@ -161,9 +161,14 @@ errno_t sss_krb5_get_options(TALLOC_CTX *memctx, struct confdb_ctx *cdb,
                              const char *conf_path, struct dp_option **_opts);
 
 errno_t write_krb5info_file(struct krb5_service *krb5_service,
-                            char **server_list,
+                            const char **server_list,
                             const char *service);
 
+errno_t write_krb5info_file_from_fo_server(struct krb5_service *krb5_service,
+                                           struct fo_server *server,
+                                           const char *service,
+                                           bool (*filter)(struct fo_server *));
+
 struct krb5_service *krb5_service_new(TALLOC_CTX *mem_ctx,
                                       struct be_ctx *be_ctx,
                                       const char *service_name,
-- 
2.19.1