From 5801760daac11350a58fde9ec1591cacf3a6af88 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Fri, 24 Feb 2017 13:55:47 +0100
Subject: [PATCH 1/5] krb5 locator: add support for multiple addresses

Read multiple addresses from the kdcinfo files add call the provided
callback with each of them.

Related to https://pagure.io/SSSD/sssd/issue/941
---
 src/krb5_plugin/sssd_krb5_locator_plugin.c | 342 +++++++++++++++++++++--------
 1 file changed, 245 insertions(+), 97 deletions(-)

diff --git a/src/krb5_plugin/sssd_krb5_locator_plugin.c b/src/krb5_plugin/sssd_krb5_locator_plugin.c
index 7c17fcb33..82fb5c7b2 100644
--- a/src/krb5_plugin/sssd_krb5_locator_plugin.c
+++ b/src/krb5_plugin/sssd_krb5_locator_plugin.c
@@ -42,7 +42,7 @@
 #define DEFAULT_KADMIN_PORT 749
 #define DEFAULT_KPASSWD_PORT 464
 
-#define BUFSIZE 512
+#define BUFSIZE 4096
 #define PORT_STR_SIZE 7
 #define SSSD_KRB5_LOCATOR_DEBUG "SSSD_KRB5_LOCATOR_DEBUG"
 #define SSSD_KRB5_LOCATOR_DISABLE "SSSD_KRB5_LOCATOR_DISABLE"
@@ -53,12 +53,15 @@
     } \
 } while(0)
 
+struct addr_port {
+    char *addr;
+    uint16_t port;
+};
+
 struct sssd_ctx {
     char *sssd_realm;
-    char *kdc_addr;
-    uint16_t kdc_port;
-    char *kpasswd_addr;
-    uint16_t kpasswd_port;
+    struct addr_port *kdc_addr;
+    struct addr_port *kpasswd_addr;
     bool debug;
     bool disabled;
 };
@@ -82,6 +85,186 @@ void plugin_debug_fn(const char *format, ...)
     free(s);
 }
 
+
+static void free_addr_port_list(struct addr_port **list)
+{
+    size_t c;
+
+    if (list == NULL || *list == NULL) {
+        return;
+    }
+
+    for (c = 0; (*list)[c].addr != NULL; c++) {
+        free((*list)[c].addr);
+    }
+    free(*list);
+    *list = NULL;
+}
+
+static int copy_addr_port_list(struct addr_port *src, bool clear_port,
+                               struct addr_port **dst)
+{
+    size_t c;
+    struct addr_port *d = NULL;
+    int ret;
+
+    /* only copy if dst is initialized to NULL */
+    if (dst == NULL || *dst != NULL) {
+        return EINVAL;
+    }
+
+    if (src == NULL) {
+        return 0;
+    }
+
+    for (c = 0; src[c].addr != NULL; c++);
+
+    d = calloc((c + 1), sizeof(struct addr_port));
+    if (d == NULL) {
+        return ENOMEM;
+    }
+
+    for (c = 0; src[c].addr != NULL; c++) {
+        d[c].addr = strdup(src[c].addr);
+        if (d[c].addr == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+        if (clear_port) {
+            d[c].port = 0;
+        } else {
+            d[c].port = src[c].port;
+        }
+    }
+
+    ret = EOK;
+
+done:
+    if (ret != EOK) {
+        free_addr_port_list(&d);
+    } else {
+        *dst = d;
+    }
+
+    return ret;
+}
+
+static int buf_to_addr_port_list(struct sssd_ctx *ctx,
+                                 uint8_t *buf, size_t buf_size,
+                                 struct addr_port **list)
+{
+    struct addr_port *l = NULL;
+    int ret;
+    uint8_t *p;
+    uint8_t *pn;
+    size_t c;
+    size_t len;
+    char *tmp = NULL;
+    char *port_str;
+    long port;
+    char *endptr;
+
+    /* only create if list is initialized to NULL */
+    if (buf == NULL || buf_size == 0 || list == NULL || *list != NULL) {
+        return EINVAL;
+    }
+
+    c = 1; /* to account for a missing \n at the very end */
+    p = buf;
+    while ((p - buf) < buf_size
+                && (p = memchr(p, '\n', buf_size - (p - buf))) != NULL) {
+        p++;
+        c++;
+    }
+
+    l = calloc((c + 1), sizeof(struct addr_port));
+    if (l == NULL) {
+        return ENOMEM;
+    }
+
+    c = 0;
+    p = buf;
+    do {
+        pn = memchr(p, '\n', buf_size - (p - buf));
+        if (pn != NULL) {
+            len = pn - p;
+        } else {
+            len = buf_size - (p - buf);
+        }
+        if (len == 0) {
+            /* empty line no more processing */
+            break;
+        }
+
+        free(tmp);
+        tmp = strndup((char *) p, len);
+        if (tmp == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+
+        port_str = strrchr(tmp, ':');
+        if (port_str == NULL) {
+            port = 0;
+        } else {
+            *port_str = '\0';
+            ++port_str;
+
+            if (isdigit(*port_str)) {
+                errno = 0;
+                port = strtol(port_str, &endptr, 10);
+                if (errno != 0) {
+                    ret = errno;
+                    PLUGIN_DEBUG(("strtol failed on [%s]: [%d][%s], "
+                                "assuming default.\n", port_str, ret,
+                                                       strerror(ret)));
+                    port = 0;
+                }
+                if (*endptr != '\0') {
+                    PLUGIN_DEBUG(("Found additional characters [%s] in port "
+                                "number [%s], assuming default.\n", endptr,
+                                                                    port_str));
+                    port = 0;
+                }
+
+                if (port < 0 || port > 65535) {
+                    PLUGIN_DEBUG(("Illegal port number [%ld], assuming "
+                                  "default.\n", port));
+                    port = 0;
+                }
+            } else {
+                PLUGIN_DEBUG(("Illegal port number [%s], assuming default.\n",
+                            port_str));
+                port = 0;
+            }
+        }
+
+        PLUGIN_DEBUG(("Found [%s][%d].\n", tmp, port));
+
+        l[c].addr = strdup(tmp);
+        if (l[c].addr == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+        l[c].port = port;
+
+        c++;
+        p = pn == NULL ? NULL : (pn + 1);
+    } while (p != NULL);
+
+    ret = EOK;
+
+done:
+    free(tmp);
+    if (ret != EOK) {
+        free_addr_port_list(&l);
+    } else {
+        *list = l;
+    }
+
+    return ret;
+}
+
 static int get_krb5info(const char *realm, struct sssd_ctx *ctx,
                         enum locate_service_type svc)
 {
@@ -91,9 +274,6 @@ static int get_krb5info(const char *realm, struct sssd_ctx *ctx,
     uint8_t buf[BUFSIZE + 1];
     int fd = -1;
     const char *name_tmpl = NULL;
-    char *port_str;
-    long port;
-    char *endptr;
 
     switch (svc) {
         case locate_service_kdc:
@@ -148,62 +328,21 @@ static int get_krb5info(const char *realm, struct sssd_ctx *ctx,
         PLUGIN_DEBUG(("Content of krb5info file [%s] is [%d] or larger.\n",
                       krb5info_name, BUFSIZE));
     }
-    PLUGIN_DEBUG(("Found [%s] in [%s].\n", buf, krb5info_name));
-
-    port_str = strrchr((char *) buf, ':');
-    if (port_str == NULL) {
-        port = 0;
-    } else {
-        *port_str = '\0';
-        ++port_str;
-
-        if (isdigit(*port_str)) {
-            errno = 0;
-            port = strtol(port_str, &endptr, 10);
-            if (errno != 0) {
-                ret = errno;
-                PLUGIN_DEBUG(("strtol failed on [%s]: [%d][%s], "
-                            "assuming default.\n", port_str, ret, strerror(ret)));
-                port = 0;
-            }
-            if (*endptr != '\0') {
-                PLUGIN_DEBUG(("Found additional characters [%s] in port number "
-                            "[%s], assuming default.\n", endptr, port_str));
-                port = 0;
-            }
-
-            if (port < 0 || port > 65535) {
-                PLUGIN_DEBUG(("Illegal port number [%ld], assuming default.\n",
-                            port));
-                port = 0;
-            }
-        } else {
-            PLUGIN_DEBUG(("Illegal port number [%s], assuming default.\n",
-                        port_str));
-            port = 0;
-        }
-    }
 
     switch (svc) {
         case locate_service_kdc:
-            free(ctx->kdc_addr);
-            ctx->kdc_addr = strdup((char *) buf);
-            if (ctx->kdc_addr == NULL) {
-                PLUGIN_DEBUG(("strdup failed.\n"));
-                ret = ENOMEM;
+            free_addr_port_list(&(ctx->kdc_addr));
+            ret = buf_to_addr_port_list(ctx, buf, len, &(ctx->kdc_addr));
+            if (ret != EOK) {
                 goto done;
             }
-            ctx->kdc_port = (uint16_t) port;
             break;
         case locate_service_kpasswd:
-            free(ctx->kpasswd_addr);
-            ctx->kpasswd_addr = strdup((char *) buf);
-            if (ctx->kpasswd_addr == NULL) {
-                PLUGIN_DEBUG(("strdup failed.\n"));
-                ret = ENOMEM;
+            free_addr_port_list(&(ctx->kpasswd_addr));
+            ret = buf_to_addr_port_list(ctx, buf, len, &(ctx->kpasswd_addr));
+            if (ret != EOK) {
                 goto done;
             }
-            ctx->kpasswd_port = (uint16_t) port;
             break;
         default:
             PLUGIN_DEBUG(("Unsupported service [%d].\n", svc));
@@ -256,8 +395,8 @@ void sssd_krb5_locator_close(void *private_data)
     ctx = (struct sssd_ctx *) private_data;
     PLUGIN_DEBUG(("sssd_krb5_locator_close called\n"));
 
-    free(ctx->kdc_addr);
-    free(ctx->kpasswd_addr);
+    free_addr_port_list(&(ctx->kdc_addr));
+    free_addr_port_list(&(ctx->kpasswd_addr));
     free(ctx->sssd_realm);
     free(ctx);
 
@@ -277,8 +416,10 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data,
     struct sssd_ctx *ctx;
     struct addrinfo ai_hints;
     uint16_t port = 0;
-    const char *addr = NULL;
+    uint16_t default_port = 0;
+    struct addr_port *addr = NULL;
     char port_str[PORT_STR_SIZE];
+    size_t c;
 
     if (private_data == NULL) return KRB5_PLUGIN_NO_HANDLE;
     ctx = (struct sssd_ctx *) private_data;
@@ -308,9 +449,13 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data,
             if (ret != EOK) {
                 PLUGIN_DEBUG(("reading kpasswd address failed, "
                               "using kdc address.\n"));
-                free(ctx->kpasswd_addr);
-                ctx->kpasswd_addr = strdup(ctx->kdc_addr);
-                ctx->kpasswd_port = 0;
+                free_addr_port_list(&(ctx->kpasswd_addr));
+                ret = copy_addr_port_list(ctx->kdc_addr, true,
+                                          &(ctx->kpasswd_addr));
+                if (ret != EOK) {
+                    PLUGIN_DEBUG(("copying address list failed.\n"));
+                    return KRB5_PLUGIN_NO_HANDLE;
+                }
             }
         }
     }
@@ -322,19 +467,19 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data,
     switch (svc) {
         case locate_service_kdc:
             addr = ctx->kdc_addr;
-            port = ctx->kdc_port ? ctx->kdc_port : DEFAULT_KERBEROS_PORT;
+            default_port = DEFAULT_KERBEROS_PORT;
             break;
         case locate_service_master_kdc:
             addr = ctx->kpasswd_addr;
-            port = DEFAULT_KERBEROS_PORT;
+            default_port = DEFAULT_KERBEROS_PORT;
             break;
         case locate_service_kadmin:
             addr = ctx->kpasswd_addr;
-            port = DEFAULT_KADMIN_PORT;
+            default_port = DEFAULT_KADMIN_PORT;
             break;
         case locate_service_kpasswd:
             addr = ctx->kpasswd_addr;
-            port = ctx->kpasswd_port ? ctx->kpasswd_port : DEFAULT_KPASSWD_PORT;
+            default_port = DEFAULT_KPASSWD_PORT;
             break;
         case locate_service_krb524:
             return KRB5_PLUGIN_NO_HANDLE;
@@ -362,46 +507,49 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data,
     if (strcmp(realm, ctx->sssd_realm) != 0)
         return KRB5_PLUGIN_NO_HANDLE;
 
-    memset(port_str, 0, PORT_STR_SIZE);
-    ret = snprintf(port_str, PORT_STR_SIZE-1, "%u", port);
-    if (ret < 0 || ret >= (PORT_STR_SIZE-1)) {
-        PLUGIN_DEBUG(("snprintf failed.\n"));
-        return KRB5_PLUGIN_NO_HANDLE;
-    }
+    for (c = 0; addr[c].addr != NULL; c++) {
+        port = (addr[c].port == 0 ? default_port : addr[c].port);
+        memset(port_str, 0, PORT_STR_SIZE);
+        ret = snprintf(port_str, PORT_STR_SIZE-1, "%u", port);
+        if (ret < 0 || ret >= (PORT_STR_SIZE-1)) {
+            PLUGIN_DEBUG(("snprintf failed.\n"));
+            return KRB5_PLUGIN_NO_HANDLE;
+        }
 
-    memset(&ai_hints, 0, sizeof(struct addrinfo));
-    ai_hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV;
-    ai_hints.ai_socktype = socktype;
-
-    ret = getaddrinfo(addr, port_str, &ai_hints, &ai);
-    if (ret != 0) {
-        PLUGIN_DEBUG(("getaddrinfo failed [%d][%s].\n", ret,
-                                                        gai_strerror(ret)));
-        if (ret == EAI_SYSTEM) {
-            PLUGIN_DEBUG(("getaddrinfo failed [%d][%s].\n", errno,
-                                                            strerror(errno)));
+        memset(&ai_hints, 0, sizeof(struct addrinfo));
+        ai_hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV;
+        ai_hints.ai_socktype = socktype;
+
+        ret = getaddrinfo(addr[c].addr, port_str, &ai_hints, &ai);
+        if (ret != 0) {
+            PLUGIN_DEBUG(("getaddrinfo failed [%d][%s].\n", ret,
+                                                            gai_strerror(ret)));
+            if (ret == EAI_SYSTEM) {
+                PLUGIN_DEBUG(("getaddrinfo failed [%d][%s].\n",
+                              errno, strerror(errno)));
+            }
+            return KRB5_PLUGIN_NO_HANDLE;
         }
-        return KRB5_PLUGIN_NO_HANDLE;
-    }
 
-    PLUGIN_DEBUG(("addr[%s:%s] family[%d] socktype[%d]\n", addr, port_str,
-                 ai->ai_family, ai->ai_socktype));
+        PLUGIN_DEBUG(("addr[%s:%s] family[%d] socktype[%d]\n", addr[c].addr,
+                     port_str, ai->ai_family, ai->ai_socktype));
 
-    if ((family == AF_UNSPEC || ai->ai_family == family) &&
-        ai->ai_socktype == socktype) {
+        if ((family == AF_UNSPEC || ai->ai_family == family) &&
+            ai->ai_socktype == socktype) {
 
-        ret = cbfunc(cbdata, socktype, ai->ai_addr);
-        if (ret != 0) {
-            PLUGIN_DEBUG(("cbfunc failed\n"));
-            freeaddrinfo(ai);
-            return ret;
+            ret = cbfunc(cbdata, socktype, ai->ai_addr);
+            if (ret != 0) {
+                PLUGIN_DEBUG(("cbfunc failed\n"));
+                freeaddrinfo(ai);
+                return ret;
+            } else {
+                PLUGIN_DEBUG(("[%s] used\n", addr[c].addr));
+            }
         } else {
-            PLUGIN_DEBUG(("[%s] used\n", addr));
+            PLUGIN_DEBUG(("[%s] NOT used\n", addr[c].addr));
         }
-    } else {
-        PLUGIN_DEBUG(("[%s] NOT used\n", addr));
+        freeaddrinfo(ai);
     }
-    freeaddrinfo(ai);
 
     return 0;
 }

From 53d362768ff935515bd24517c5e6d7df015508fb Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Tue, 22 May 2018 17:59:52 +0200
Subject: [PATCH 2/5] krb5 locator: fix IPv6 support

IPv6 addresses are added with surrounding '[' and ']' to the kdcinfo
file to be able to specify a port number properly. The Kerberos location
plugin didn't handle those entries properly.

Related to https://pagure.io/SSSD/sssd/issue/941
---
 src/krb5_plugin/sssd_krb5_locator_plugin.c | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/src/krb5_plugin/sssd_krb5_locator_plugin.c b/src/krb5_plugin/sssd_krb5_locator_plugin.c
index 82fb5c7b2..58cac7f4b 100644
--- a/src/krb5_plugin/sssd_krb5_locator_plugin.c
+++ b/src/krb5_plugin/sssd_krb5_locator_plugin.c
@@ -159,6 +159,8 @@ static int buf_to_addr_port_list(struct sssd_ctx *ctx,
     uint8_t *pn;
     size_t c;
     size_t len;
+    size_t addr_len;
+    char *addr_str = NULL;
     char *tmp = NULL;
     char *port_str;
     long port;
@@ -206,6 +208,9 @@ static int buf_to_addr_port_list(struct sssd_ctx *ctx,
         port_str = strrchr(tmp, ':');
         if (port_str == NULL) {
             port = 0;
+        } else if (tmp[0] == '[' && *(port_str - 1) != ']') {
+            /* IPv6 address without port number */
+            port = 0;
         } else {
             *port_str = '\0';
             ++port_str;
@@ -239,9 +244,19 @@ static int buf_to_addr_port_list(struct sssd_ctx *ctx,
             }
         }
 
-        PLUGIN_DEBUG(("Found [%s][%d].\n", tmp, port));
+        /* make sure tmp is not modified so that it can be freed later */
+        addr_str = tmp;
+        /* strip leading '[' and trailing ']' from IPv6 addresses */
+        if (addr_str[0] == '['
+                && (addr_len = strlen(addr_str))
+                && addr_str[addr_len - 1] == ']') {
+            addr_str[addr_len -1] = '\0';
+            addr_str++;
+        }
+
+        PLUGIN_DEBUG(("Found [%s][%d].\n", addr_str, port));
 
-        l[c].addr = strdup(tmp);
+        l[c].addr = strdup(addr_str);
         if (l[c].addr == NULL) {
             ret = ENOMEM;
             goto done;

From 75b8ff10b9656708bb3c886ad4c37cadd1fb6cfb Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Tue, 22 May 2018 18:03:05 +0200
Subject: [PATCH 3/5] krb5 locator: make plugin more robust

Although currently libkrb5 sets all parameters of the locator plugin
calls to suitable values we should make sure that provided pointers are
not NULL before trying to dereference them.

Related to https://pagure.io/SSSD/sssd/issue/941
---
 src/krb5_plugin/sssd_krb5_locator_plugin.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/krb5_plugin/sssd_krb5_locator_plugin.c b/src/krb5_plugin/sssd_krb5_locator_plugin.c
index 58cac7f4b..9874fd2d1 100644
--- a/src/krb5_plugin/sssd_krb5_locator_plugin.c
+++ b/src/krb5_plugin/sssd_krb5_locator_plugin.c
@@ -439,6 +439,10 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data,
     if (private_data == NULL) return KRB5_PLUGIN_NO_HANDLE;
     ctx = (struct sssd_ctx *) private_data;
 
+    if (realm == NULL || cbfunc == NULL || cbdata == NULL) {
+        return KRB5_PLUGIN_NO_HANDLE;
+    }
+
     if (ctx->disabled) {
         PLUGIN_DEBUG(("Plugin disabled, nothing to do.\n"));
         return KRB5_PLUGIN_NO_HANDLE;

From 9f0e10fa24deb3d3bdecd5c2c055108c0ed481b8 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Thu, 24 May 2018 17:14:42 +0200
Subject: [PATCH 4/5] krb5 locator: add unit tests

Unit test for existing and new functionality of the Kerberos locator
plugin.

Related to https://pagure.io/SSSD/sssd/issue/941
---
 Makefile.am                                      |  20 +
 src/krb5_plugin/sssd_krb5_locator_plugin.c       |  16 +
 src/tests/cmocka/test_sssd_krb5_locator_plugin.c | 631 +++++++++++++++++++++++
 3 files changed, 667 insertions(+)
 create mode 100644 src/tests/cmocka/test_sssd_krb5_locator_plugin.c

diff --git a/Makefile.am b/Makefile.am
index 9c65aede4..6b0d5b190 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -289,6 +289,7 @@ if HAVE_CMOCKA
         krb5_common_test \
         test_iobuf \
         sss_certmap_test \
+        test_sssd_krb5_locator_plugin \
         $(NULL)
 
 
@@ -3490,6 +3491,25 @@ sss_certmap_test_LDADD = \
     libsss_certmap.la \
     $(NULL)
 
+test_sssd_krb5_locator_plugin_SOURCES = \
+    src/tests/cmocka/test_sssd_krb5_locator_plugin.c \
+    src/krb5_plugin/sssd_krb5_locator_plugin.c \
+    $(NULL)
+test_sssd_krb5_locator_plugin_CFLAGS = \
+    $(AM_CFLAGS) \
+    $(POPT_CFLAGS) \
+    $(TALLOC_CFLAGS) \
+    $(KRB5_CFLAGS) \
+    -DTEST_PUBCONF_PATH=\"$(abs_builddir)/src/tests/cmocka/pubconf\" \
+    $(NULL)
+test_sssd_krb5_locator_plugin_LDADD = \
+    $(CMOCKA_LIBS) \
+    $(POPT_LIBS) \
+    $(TALLOC_LIBS) \
+    $(KRB5_LIBS) \
+    libsss_test_common.la \
+    $(NULL)
+
 if BUILD_KCM
 test_kcm_json_SOURCES = \
     src/tests/cmocka/test_kcm_json_marshalling.c \
diff --git a/src/krb5_plugin/sssd_krb5_locator_plugin.c b/src/krb5_plugin/sssd_krb5_locator_plugin.c
index 9874fd2d1..952d487c2 100644
--- a/src/krb5_plugin/sssd_krb5_locator_plugin.c
+++ b/src/krb5_plugin/sssd_krb5_locator_plugin.c
@@ -38,6 +38,22 @@
 
 #include "providers/krb5/krb5_common.h"
 
+/* The following override of KDCINFO_TMPL and KPASSWDINFO_TMPL is not very
+ * elegant but since they are defined in krb5_common.h with the help of
+ * PUBCONF_PATH from config.h and PUBCONF_PATH can by set by a configure
+ * options I didn't found another way to change the path for a unit test. */
+#ifdef TEST_PUBCONF_PATH
+#ifdef KDCINFO_TMPL
+#undef KDCINFO_TMPL
+#endif
+#define KDCINFO_TMPL TEST_PUBCONF_PATH"/kdcinfo.%s"
+
+#ifdef KPASSWDINFO_TMPL
+#undef KPASSWDINFO_TMPL
+#endif
+#define KPASSWDINFO_TMPL TEST_PUBCONF_PATH"/kpasswdinfo.%s"
+#endif /* TEST_PUBCONF_PATH */
+
 #define DEFAULT_KERBEROS_PORT 88
 #define DEFAULT_KADMIN_PORT 749
 #define DEFAULT_KPASSWD_PORT 464
diff --git a/src/tests/cmocka/test_sssd_krb5_locator_plugin.c b/src/tests/cmocka/test_sssd_krb5_locator_plugin.c
new file mode 100644
index 000000000..3e7d00632
--- /dev/null
+++ b/src/tests/cmocka/test_sssd_krb5_locator_plugin.c
@@ -0,0 +1,631 @@
+/*
+    SSSD
+
+    Unit test for SSSD's MIT Kerberos locator plugin
+
+    Authors:
+        Sumit Bose <sbose@redhat.com>
+
+    Copyright (C) 2018 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "config.h"
+
+#include <popt.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <krb5/krb5.h>
+#include <krb5/locate_plugin.h>
+
+#include "tests/cmocka/common_mock.h"
+
+#define TEST_REALM "TEST.REALM"
+#define TEST_IP_1 "123.231.132.213"
+#define TEST_IPV6_1_PURE "7025:4d2d:2b06:e321:d971:16c0:6eeb:cc41"
+#define TEST_IPV6_1 "["TEST_IPV6_1_PURE"]"
+#define TEST_SERVICE_1 "22334"
+#define TEST_SERVICE_2 "54321"
+#define TEST_IP_1_WITH_SERVICE TEST_IP_1":"TEST_SERVICE_1
+#define TEST_IPV6_1_WITH_SERVICE TEST_IPV6_1":"TEST_SERVICE_2
+
+struct test_state {
+    void *dummy;
+};
+
+static int setup(void **state)
+{
+    struct test_state *ts = NULL;
+
+    assert_true(leak_check_setup());
+
+    ts = talloc(global_talloc_context, struct test_state);
+    assert_non_null(ts);
+
+    check_leaks_push(ts);
+    *state = (void *)ts;
+
+    unlink(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM);
+    rmdir(TEST_PUBCONF_PATH);
+
+    return 0;
+}
+
+static int teardown(void **state)
+{
+    struct test_state *ts = talloc_get_type_abort(*state, struct test_state);
+
+    assert_non_null(ts);
+
+    assert_true(check_leaks_pop(ts));
+    talloc_free(ts);
+    assert_true(leak_check_teardown());
+    return 0;
+}
+
+/* Taken from MIT Kerberos src/lib/krb5/os/locate_kdc.c and
+ * lib/krb5/os/os-proto.h */
+
+typedef enum {
+    TCP_OR_UDP = 0,
+    TCP,
+    UDP,
+    HTTPS,
+} k5_transport;
+
+/* A single server hostname or address. */
+struct server_entry {
+    char *hostname;             /* NULL -> use addrlen/addr instead */
+    int port;                   /* Used only if hostname set */
+    k5_transport transport;     /* May be 0 for UDP/TCP if hostname set */
+    char *uri_path;             /* Used only if transport is HTTPS */
+    int family;                 /* May be 0 (aka AF_UNSPEC) if hostname set */
+    int master;                 /* True, false, or -1 for unknown. */
+    size_t addrlen;
+    struct sockaddr_storage addr;
+};
+
+/* A list of server hostnames/addresses. */
+struct serverlist {
+    struct server_entry *servers;
+    size_t nservers;
+};
+#define SERVERLIST_INIT { NULL, 0 }
+
+/* Free up everything pointed to by the serverlist structure, but don't
+ *  * free the structure itself. */
+void
+k5_free_serverlist (struct serverlist *list)
+{
+    size_t i;
+
+    for (i = 0; i < list->nservers; i++) {
+        free(list->servers[i].hostname);
+        free(list->servers[i].uri_path);
+    }
+    free(list->servers);
+    list->servers = NULL;
+    list->nservers = 0;
+}
+
+/* Make room for a new server entry in list and return a pointer to the new
+ * entry.  (Do not increment list->nservers.) */
+static struct server_entry *
+new_server_entry(struct serverlist *list)
+{
+    struct server_entry *newservers, *entry;
+    size_t newspace = (list->nservers + 1) * sizeof(struct server_entry);
+
+    newservers = realloc(list->servers, newspace);
+    if (newservers == NULL)
+        return NULL;
+    list->servers = newservers;
+    entry = &newservers[list->nservers];
+    memset(entry, 0, sizeof(*entry));
+    entry->master = -1;
+    return entry;
+}
+
+/* Add an address entry to list. */
+static int
+add_addr_to_list(struct serverlist *list, k5_transport transport, int family,
+                 size_t addrlen, struct sockaddr *addr)
+{
+    struct server_entry *entry;
+
+    entry = new_server_entry(list);
+    if (entry == NULL)
+        return ENOMEM;
+    entry->transport = transport;
+    entry->family = family;
+    entry->hostname = NULL;
+    entry->uri_path = NULL;
+    entry->addrlen = addrlen;
+    memcpy(&entry->addr, addr, addrlen);
+    list->nservers++;
+    return 0;
+}
+
+struct module_callback_data {
+    int out_of_mem;
+    struct serverlist *list;
+};
+
+static int
+module_callback(void *cbdata, int socktype, struct sockaddr *sa)
+{
+    struct module_callback_data *d = cbdata;
+    size_t addrlen;
+    k5_transport transport;
+
+    if (socktype != SOCK_STREAM && socktype != SOCK_DGRAM)
+        return 0;
+    if (sa->sa_family == AF_INET)
+        addrlen = sizeof(struct sockaddr_in);
+    else if (sa->sa_family == AF_INET6)
+        addrlen = sizeof(struct sockaddr_in6);
+    else
+        return 0;
+    transport = (socktype == SOCK_STREAM) ? TCP : UDP;
+    if (add_addr_to_list(d->list, transport, sa->sa_family, addrlen,
+                         sa) != 0) {
+        /* Assumes only error is ENOMEM.  */
+        d->out_of_mem = 1;
+        return 1;
+    }
+    return 0;
+}
+
+krb5_error_code sssd_krb5_locator_init(krb5_context context,
+                                       void **private_data);
+void sssd_krb5_locator_close(void *private_data);
+
+krb5_error_code sssd_krb5_locator_lookup(void *private_data,
+                    enum locate_service_type svc,
+                    const char *realm,
+                    int socktype,
+                    int family,
+                    int (*cbfunc)(void *, int, struct sockaddr *),
+                    void *cbdata);
+
+void test_init(void **state)
+{
+    krb5_context ctx;
+    krb5_error_code kerr;
+    void *priv;
+
+    kerr = krb5_init_context (&ctx);
+    assert_int_equal(kerr, 0);
+
+    kerr = sssd_krb5_locator_init(ctx, &priv);
+    assert_int_equal(kerr, 0);
+
+    sssd_krb5_locator_close(priv);
+
+    krb5_free_context(ctx);
+}
+
+void test_failed_lookup(void **state)
+{
+    krb5_context ctx;
+    krb5_error_code kerr;
+    void *priv;
+    struct module_callback_data cbdata = { 0 };
+
+
+    kerr = krb5_init_context (&ctx);
+    assert_int_equal(kerr, 0);
+
+    kerr = sssd_krb5_locator_init(ctx, &priv);
+    assert_int_equal(kerr, 0);
+
+    kerr = sssd_krb5_locator_lookup(NULL, -1, NULL, -1, -1, NULL, NULL);
+    assert_int_equal(kerr, KRB5_PLUGIN_NO_HANDLE);
+
+    kerr = sssd_krb5_locator_lookup(priv, -1, NULL, -1, -1, NULL, NULL);
+    assert_int_equal(kerr, KRB5_PLUGIN_NO_HANDLE);
+
+    kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , NULL, -1, -1,
+                                    NULL, NULL);
+    assert_int_equal(kerr, KRB5_PLUGIN_NO_HANDLE);
+
+    kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, -1,
+                                    -1, NULL, NULL);
+    assert_int_equal(kerr, KRB5_PLUGIN_NO_HANDLE);
+
+    kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM,
+                                    SOCK_DGRAM, -1, NULL, NULL);
+    assert_int_equal(kerr, KRB5_PLUGIN_NO_HANDLE);
+
+    kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM,
+                                    SOCK_DGRAM, AF_INET6, NULL, NULL);
+    assert_int_equal(kerr, KRB5_PLUGIN_NO_HANDLE);
+
+    kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM,
+                                    SOCK_DGRAM, AF_INET6, module_callback,
+                                    NULL);
+    assert_int_equal(kerr, KRB5_PLUGIN_NO_HANDLE);
+
+    kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM,
+                                    SOCK_DGRAM, AF_INET6, module_callback,
+                                    &cbdata);
+    assert_int_equal(kerr, KRB5_PLUGIN_NO_HANDLE);
+
+    sssd_krb5_locator_close(priv);
+
+    krb5_free_context(ctx);
+}
+
+void test_empty(void **state)
+{
+    krb5_context ctx;
+    krb5_error_code kerr;
+    void *priv;
+    int fd;
+    struct module_callback_data cbdata = { 0 };
+
+    kerr = krb5_init_context (&ctx);
+    assert_int_equal(kerr, 0);
+
+    kerr = sssd_krb5_locator_init(ctx, &priv);
+    assert_int_equal(kerr, 0);
+
+    mkdir(TEST_PUBCONF_PATH, 0777);
+    fd = open(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM, O_CREAT, 0777);
+    assert_int_not_equal(fd, -1);
+    close(fd);
+
+    kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM,
+                                    SOCK_DGRAM, AF_INET6, module_callback,
+                                    &cbdata);
+    assert_int_equal(kerr, KRB5_PLUGIN_NO_HANDLE);
+    unlink(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM);
+    rmdir(TEST_PUBCONF_PATH);
+
+    sssd_krb5_locator_close(priv);
+
+    krb5_free_context(ctx);
+}
+
+void test_single(void **state)
+{
+    krb5_context ctx;
+    krb5_error_code kerr;
+    void *priv;
+    int fd;
+    struct serverlist list = SERVERLIST_INIT;
+    struct module_callback_data cbdata = { 0 };
+    ssize_t s;
+    int ret;
+    char host[NI_MAXHOST];
+    char service[NI_MAXSERV];
+
+    cbdata.list = &list;
+
+    kerr = krb5_init_context (&ctx);
+    assert_int_equal(kerr, 0);
+
+    kerr = sssd_krb5_locator_init(ctx, &priv);
+    assert_int_equal(kerr, 0);
+
+    mkdir(TEST_PUBCONF_PATH, 0777);
+    fd = open(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM, O_CREAT|O_RDWR, 0777);
+    assert_int_not_equal(fd, -1);
+    s = write(fd, TEST_IP_1, sizeof(TEST_IP_1));
+    assert_int_equal(s, sizeof(TEST_IP_1));
+    close(fd);
+
+    kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM,
+                                    SOCK_DGRAM, AF_INET6, module_callback,
+                                    &cbdata);
+    assert_int_equal(kerr, 0);
+
+    /* We asked for AF_INET6, but TEST_IP_1 is IPv4 */
+    assert_int_equal(list.nservers, 0);
+    assert_null(list.servers);
+
+    kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM,
+                                    SOCK_DGRAM, AF_INET, module_callback,
+                                    &cbdata);
+    assert_int_equal(kerr, 0);
+    assert_int_equal(list.nservers, 1);
+    assert_non_null(list.servers);
+    assert_int_equal(list.servers[0].addrlen, 16);
+    ret = getnameinfo((struct sockaddr *) &list.servers[0].addr,
+                      list.servers[0].addrlen,
+                      host, sizeof(host), service, sizeof(service),
+                      NI_NUMERICHOST|NI_NUMERICSERV);
+    assert_int_equal(ret, 0);
+    assert_string_equal(TEST_IP_1, host);
+    assert_string_equal("88", service);
+
+    k5_free_serverlist(&list);
+
+    kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM,
+                                    SOCK_DGRAM, AF_UNSPEC, module_callback,
+                                    &cbdata);
+    assert_int_equal(kerr, 0);
+    assert_int_equal(list.nservers, 1);
+    assert_non_null(list.servers);
+    assert_int_equal(list.servers[0].addrlen, 16);
+    ret = getnameinfo((struct sockaddr *) &list.servers[0].addr,
+                      list.servers[0].addrlen,
+                      host, sizeof(host), service, sizeof(service),
+                      NI_NUMERICHOST|NI_NUMERICSERV);
+    assert_int_equal(ret, 0);
+    assert_string_equal(TEST_IP_1, host);
+    assert_string_equal("88", service);
+
+    k5_free_serverlist(&list);
+
+    unlink(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM);
+    rmdir(TEST_PUBCONF_PATH);
+    sssd_krb5_locator_close(priv);
+
+    krb5_free_context(ctx);
+}
+
+struct test_data {
+    const char *ip;
+    bool found;
+};
+
+void test_multi_check_results(struct test_data *test_data,
+                              struct serverlist *list,
+                              const char *exp_service)
+{
+    int ret;
+    char host[NI_MAXHOST];
+    char service[NI_MAXSERV];
+    size_t c;
+    size_t d;
+
+    /* To make sure each result from list has a matching entry in test_data we
+     * use a flag to mark found entries, this way we can properly detect is
+     * the same address is used multiple times. */
+    for (d = 0; test_data[d].ip != NULL; d++) {
+        test_data[d].found = false;
+    }
+
+    for (c = 0; c < list->nservers; c++) {
+        ret = getnameinfo((struct sockaddr *) &list->servers[c].addr,
+                          list->servers[c].addrlen,
+                          host, sizeof(host), service, sizeof(service),
+                          NI_NUMERICHOST|NI_NUMERICSERV);
+        assert_int_equal(ret, 0);
+        assert_string_equal(exp_service, service);
+        for (d = 0; test_data[d].ip != NULL; d++) {
+            /* Compare result with test_data, be aware that the test_data has
+             * '[]' around IPv& addresses */
+            if (strncmp(host,
+                        test_data[d].ip + (test_data[d].ip[0] == '[' ? 1 : 0),
+                        strlen(host)) == 0 && !test_data[d].found) {
+                test_data[d].found = true;
+                break;
+            }
+        }
+        /* Make sure we found the result in the list */
+        assert_non_null(test_data[d].ip);
+    }
+}
+
+void test_multi(void **state)
+{
+    krb5_context ctx;
+    krb5_error_code kerr;
+    void *priv;
+    int fd;
+    struct serverlist list = SERVERLIST_INIT;
+    struct module_callback_data cbdata = { 0 };
+    ssize_t s;
+    size_t c;
+    struct test_data test_data[] = {
+                           {TEST_IP_1, false},
+                           {TEST_IPV6_1, false},
+                           {"[c89a:565b:4510:5b9f:41fe:ea81:87a0:f21b]", false},
+                           {"155.42.66.53", false},
+                           {"[f812:5941:ba69:2bae:e806:3b68:770d:d75e]", false},
+                           {"[3ad3:9dda:50e4:3c82:548f:eaa1:e120:6dd]", false},
+                           {"55.116.79.183", false},
+                           {"[ce8a:ee99:98cd:d8cd:218d:393e:d5a9:dc52]", false},
+                           /* the following address is added twice to check if
+                            * an address can be added more than once. */
+                           {"37.230.88.162", false},
+                           {"37.230.88.162", false},
+                           {NULL, false} };
+
+    cbdata.list = &list;
+
+    kerr = krb5_init_context (&ctx);
+    assert_int_equal(kerr, 0);
+
+    kerr = sssd_krb5_locator_init(ctx, &priv);
+    assert_int_equal(kerr, 0);
+
+    mkdir(TEST_PUBCONF_PATH, 0777);
+    fd = open(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM, O_CREAT|O_RDWR, 0777);
+    assert_int_not_equal(fd, -1);
+    for (c = 0; test_data[c].ip != NULL; c++) {
+        s = write(fd, test_data[c].ip, strlen(test_data[c].ip));
+        assert_int_equal(s, strlen(test_data[c].ip));
+        s = write(fd, "\n", 1);
+        assert_int_equal(s, 1);
+    }
+    close(fd);
+
+    kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM,
+                                    SOCK_DGRAM, AF_INET6, module_callback,
+                                    &cbdata);
+    assert_int_equal(kerr, 0);
+
+    assert_int_equal(list.nservers, 5);
+    assert_non_null(list.servers);
+    test_multi_check_results(test_data, &list, "88");
+
+    k5_free_serverlist(&list);
+
+    kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM,
+                                    SOCK_DGRAM, AF_INET, module_callback,
+                                    &cbdata);
+    assert_int_equal(kerr, 0);
+
+    assert_int_equal(list.nservers, 5);
+    assert_non_null(list.servers);
+    test_multi_check_results(test_data, &list, "88");
+
+
+    k5_free_serverlist(&list);
+
+    kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM,
+                                    SOCK_DGRAM, AF_UNSPEC, module_callback,
+                                    &cbdata);
+    assert_int_equal(kerr, 0);
+
+    assert_int_equal(list.nservers, 10);
+    assert_non_null(list.servers);
+    test_multi_check_results(test_data, &list, "88");
+
+    k5_free_serverlist(&list);
+
+    unlink(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM);
+    rmdir(TEST_PUBCONF_PATH);
+    sssd_krb5_locator_close(priv);
+
+    krb5_free_context(ctx);
+}
+
+void test_service(void **state)
+{
+    krb5_context ctx;
+    krb5_error_code kerr;
+    void *priv;
+    int fd;
+    struct serverlist list = SERVERLIST_INIT;
+    struct module_callback_data cbdata = { 0 };
+    ssize_t s;
+    int ret;
+    char host[NI_MAXHOST];
+    char service[NI_MAXSERV];
+
+    cbdata.list = &list;
+
+    kerr = krb5_init_context (&ctx);
+    assert_int_equal(kerr, 0);
+
+    kerr = sssd_krb5_locator_init(ctx, &priv);
+    assert_int_equal(kerr, 0);
+
+    mkdir(TEST_PUBCONF_PATH, 0777);
+    fd = open(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM, O_CREAT|O_RDWR, 0777);
+    assert_int_not_equal(fd, -1);
+    s = write(fd, TEST_IP_1_WITH_SERVICE, sizeof(TEST_IP_1_WITH_SERVICE));
+    assert_int_equal(s, sizeof(TEST_IP_1_WITH_SERVICE));
+    s = write(fd, "\n", 1);
+    assert_int_equal(s, 1);
+    s = write(fd, TEST_IPV6_1_WITH_SERVICE, sizeof(TEST_IPV6_1_WITH_SERVICE));
+    assert_int_equal(s, sizeof(TEST_IPV6_1_WITH_SERVICE));
+    close(fd);
+
+    kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM,
+                                    SOCK_DGRAM, AF_INET6, module_callback,
+                                    &cbdata);
+    assert_int_equal(kerr, 0);
+
+    assert_int_equal(list.nservers, 1);
+    assert_non_null(list.servers);
+    ret = getnameinfo((struct sockaddr *) &list.servers[0].addr,
+                      list.servers[0].addrlen,
+                      host, sizeof(host), service, sizeof(service),
+                      NI_NUMERICHOST|NI_NUMERICSERV);
+    assert_int_equal(ret, 0);
+    assert_string_equal(TEST_IPV6_1_PURE, host);
+    assert_string_equal(TEST_SERVICE_2, service);
+
+    k5_free_serverlist(&list);
+
+    kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM,
+                                    SOCK_DGRAM, AF_INET, module_callback,
+                                    &cbdata);
+    assert_int_equal(kerr, 0);
+    assert_int_equal(list.nservers, 1);
+    assert_non_null(list.servers);
+    ret = getnameinfo((struct sockaddr *) &list.servers[0].addr,
+                      list.servers[0].addrlen,
+                      host, sizeof(host), service, sizeof(service),
+                      NI_NUMERICHOST|NI_NUMERICSERV);
+    assert_int_equal(ret, 0);
+    assert_string_equal(TEST_IP_1, host);
+    assert_string_equal(TEST_SERVICE_1, service);
+
+    k5_free_serverlist(&list);
+
+
+    unlink(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM);
+    rmdir(TEST_PUBCONF_PATH);
+    sssd_krb5_locator_close(priv);
+
+    krb5_free_context(ctx);
+}
+
+int main(int argc, const char *argv[])
+{
+    poptContext pc;
+    int opt;
+    int ret;
+    struct poptOption long_options[] = {
+        POPT_AUTOHELP
+        SSSD_DEBUG_OPTS
+        POPT_TABLEEND
+    };
+
+    const struct CMUnitTest tests[] = {
+        cmocka_unit_test_setup_teardown(test_init,
+                                        setup, teardown),
+        cmocka_unit_test_setup_teardown(test_failed_lookup,
+                                        setup, teardown),
+        cmocka_unit_test_setup_teardown(test_empty,
+                                        setup, teardown),
+        cmocka_unit_test_setup_teardown(test_single,
+                                        setup, teardown),
+        cmocka_unit_test_setup_teardown(test_multi,
+                                        setup, teardown),
+        cmocka_unit_test_setup_teardown(test_service,
+                                        setup, teardown),
+    };
+
+    /* Set debug level to invalid value so we can decide if -d 0 was used. */
+    debug_level = SSSDBG_INVALID;
+
+    pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+    while((opt = poptGetNextOpt(pc)) != -1) {
+        switch(opt) {
+        default:
+            fprintf(stderr, "\nInvalid option %s: %s\n\n",
+                    poptBadOption(pc, 0), poptStrerror(opt));
+            poptPrintUsage(pc, stderr, 0);
+            return 1;
+        }
+    }
+    poptFreeContext(pc);
+
+    DEBUG_CLI_INIT(debug_level);
+
+    ret = cmocka_run_group_tests(tests, NULL, NULL);
+
+    return ret;
+}

From 32fd85f3f205c1486557bca16604fddea62e3550 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Thu, 24 May 2018 17:15:38 +0200
Subject: [PATCH 5/5] AD/IPA: Create kdcinfo file for sub-domains

With this patch kdcinfo files are created for sub-domains by the AD
provider and by the IPA provider on the IPA servers
(ipa_server_mode=True).

Related to https://pagure.io/SSSD/sssd/issue/3652
---
 src/providers/ad/ad_common.c              |  9 +++++++++
 src/providers/ad/ad_common.h              |  1 +
 src/providers/ad/ad_init.c                |  1 +
 src/providers/ad/ad_subdomains.c          | 17 ++++++++++++++---
 src/providers/ipa/ipa_subdomains_server.c | 16 ++++++++++++++--
 5 files changed, 39 insertions(+), 5 deletions(-)

diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c
index c39dcfad6..d367dcb2c 100644
--- a/src/providers/ad/ad_common.c
+++ b/src/providers/ad/ad_common.c
@@ -727,6 +727,7 @@ ad_failover_init(TALLOC_CTX *mem_ctx, struct be_ctx *bectx,
                  const char *ad_service,
                  const char *ad_gc_service,
                  const char *ad_domain,
+                 bool use_kdcinfo,
                  struct ad_service **_service)
 {
     errno_t ret;
@@ -762,6 +763,14 @@ ad_failover_init(TALLOC_CTX *mem_ctx, struct be_ctx *bectx,
         goto done;
     }
 
+    /* Set flag that controls whether we want to write the
+     * kdcinfo files at all
+     */
+    service->krb5_service->write_kdcinfo = use_kdcinfo;
+    DEBUG(SSSDBG_CONF_SETTINGS, "write_kdcinfo for realm %s set to %s\n",
+                       krb5_realm,
+                       service->krb5_service->write_kdcinfo ? "true" : "false");
+
     ret = be_fo_add_service(bectx, ad_service, ad_user_data_cmp);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create failover service!\n");
diff --git a/src/providers/ad/ad_common.h b/src/providers/ad/ad_common.h
index 6eb2ba7e9..dd440da33 100644
--- a/src/providers/ad/ad_common.h
+++ b/src/providers/ad/ad_common.h
@@ -144,6 +144,7 @@ ad_failover_init(TALLOC_CTX *mem_ctx, struct be_ctx *ctx,
                  const char *ad_service,
                  const char *ad_gc_service,
                  const char *ad_domain,
+                 bool use_kdcinfo,
                  struct ad_service **_service);
 
 errno_t
diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c
index b19624782..637efb761 100644
--- a/src/providers/ad/ad_init.c
+++ b/src/providers/ad/ad_init.c
@@ -159,6 +159,7 @@ static errno_t ad_init_options(TALLOC_CTX *mem_ctx,
     ret = ad_failover_init(ad_options, be_ctx, ad_servers, ad_backup_servers,
                            ad_realm, AD_SERVICE_NAME, AD_GC_SERVICE_NAME,
                            dp_opt_get_string(ad_options->basic, AD_DOMAIN),
+                           false, /* will be set in ad_get_auth_options() */
                            &ad_options->service);
     if (ret != EOK) {
         DEBUG(SSSDBG_FATAL_FAILURE, "Failed to init AD failover service: "
diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
index 74b9f0751..84886e920 100644
--- a/src/providers/ad/ad_subdomains.c
+++ b/src/providers/ad/ad_subdomains.c
@@ -249,6 +249,7 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx,
     const char *hostname;
     const char *keytab;
     char *subdom_conf_path;
+    bool use_kdcinfo = false;
 
     realm = dp_opt_get_cstring(id_ctx->ad_options->basic, AD_KRB5_REALM);
     hostname = dp_opt_get_cstring(id_ctx->ad_options->basic, AD_HOSTNAME);
@@ -296,9 +297,19 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx,
     servers = dp_opt_get_string(ad_options->basic, AD_SERVER);
     backup_servers = dp_opt_get_string(ad_options->basic, AD_BACKUP_SERVER);
 
-    ret = ad_failover_init(ad_options, be_ctx, servers, backup_servers, realm,
-                           service_name, gc_service_name,
-                           subdom->name, &ad_options->service);
+    if (id_ctx->ad_options->auth_ctx != NULL
+            && id_ctx->ad_options->auth_ctx->opts != NULL) {
+        use_kdcinfo = dp_opt_get_bool(id_ctx->ad_options->auth_ctx->opts,
+                                      KRB5_USE_KDCINFO);
+    }
+
+    DEBUG(SSSDBG_TRACE_ALL,
+          "Init failover for [%s][%s] with use_kdcinfo [%s].\n",
+          subdom->name, subdom->realm, use_kdcinfo ? "true" : "false");
+
+    ret = ad_failover_init(ad_options, be_ctx, servers, backup_servers,
+                           subdom->realm, service_name, gc_service_name,
+                           subdom->name, use_kdcinfo, &ad_options->service);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD failover\n");
         talloc_free(ad_options);
diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c
index 1e53e7a95..02577c921 100644
--- a/src/providers/ipa/ipa_subdomains_server.c
+++ b/src/providers/ipa/ipa_subdomains_server.c
@@ -228,6 +228,7 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx,
     struct sdap_domain *sdom;
     errno_t ret;
     const char *extra_attrs;
+    bool use_kdcinfo = false;
 
     ad_domain = subdom->name;
     DEBUG(SSSDBG_TRACE_LIBS, "Setting up AD subdomain %s\n", subdom->name);
@@ -284,12 +285,23 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx,
     ad_servers = dp_opt_get_string(ad_options->basic, AD_SERVER);
     ad_backup_servers = dp_opt_get_string(ad_options->basic, AD_BACKUP_SERVER);
 
+    if (id_ctx->ipa_options != NULL && id_ctx->ipa_options->auth != NULL) {
+        use_kdcinfo = dp_opt_get_bool(id_ctx->ipa_options->auth,
+                                      KRB5_USE_KDCINFO);
+    }
+
+    DEBUG(SSSDBG_TRACE_ALL,
+          "Init failover for [%s][%s] with use_kdcinfo [%s].\n",
+          subdom->name, subdom->realm, use_kdcinfo ? "true" : "false");
+
     /* Set KRB5 realm to same as the one of IPA when IPA
      * is able to attach PAC. For testing, use hardcoded. */
+    /* Why? */
     ret = ad_failover_init(ad_options, be_ctx, ad_servers, ad_backup_servers,
-                           id_ctx->server_mode->realm,
+                           subdom->realm,
                            service_name, gc_service_name,
-                           subdom->name, &ad_options->service);
+                           subdom->name, use_kdcinfo,
+                           &ad_options->service);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD failover\n");
         talloc_free(ad_options);
