From f780f11f6dc75f202fabfebf92941816828d07bf Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <sgallagh@redhat.com>
Date: Tue, 17 Jul 2012 13:01:28 -0400
Subject: [PATCH 3/4] LOCATOR: Support multiple entries in kdcinfo file

---
 src/krb5_plugin/sssd_krb5_locator_plugin.c |  196 +++++++++++++++++++---------
 1 file changed, 135 insertions(+), 61 deletions(-)

diff --git a/src/krb5_plugin/sssd_krb5_locator_plugin.c b/src/krb5_plugin/sssd_krb5_locator_plugin.c
index 07a93d7e6e0c0018157f87fde38ec608b2e8f3a4..c0edec037d6e7728763256d1c37986fd7b5bf5b4 100644
--- a/src/krb5_plugin/sssd_krb5_locator_plugin.c
+++ b/src/krb5_plugin/sssd_krb5_locator_plugin.c
@@ -63,6 +63,7 @@ struct sssd_kserver_ctx {
 
 struct sssd_ctx {
     char *sssd_realm;
+
     struct sssd_kserver_ctx *kdc;
     struct sssd_kserver_ctx *kpasswd;
     bool debug;
@@ -105,49 +106,46 @@ sssd_kserver_ctx_init(TALLOC_CTX *mem_ctx, const char *address, uint16_t port)
     return kctx;
 }
 
-
-static int get_krb5info(const char *realm, struct sssd_ctx *ctx,
-                        enum locate_service_type svc)
+static errno_t
+parse_krb5info(TALLOC_CTX *mem_ctx,
+               struct sssd_ctx *ctx,
+               const char *krb5info_name,
+               struct sssd_kserver_ctx **_kctx)
 {
-    int ret;
-    char *krb5info_name = NULL;
-    size_t len;
-    uint8_t buf[BUFSIZE + 1];
+    errno_t ret;
     int fd = -1;
-    const char *name_tmpl = NULL;
+    TALLOC_CTX *tmp_ctx = NULL;
+    TALLOC_CTX *kmem_ctx = NULL;
+    struct sssd_kserver_ctx *kctx;
+    struct sssd_kserver_ctx *kctx_list = NULL;
+    uint8_t buf[BUFSIZE + 1];
+    char *p;
+    char *eol;
     char *port_str;
     long port;
+    ssize_t len;
     char *endptr;
 
-    switch (svc) {
-        case locate_service_kdc:
-            name_tmpl = KDCINFO_TMPL;
-            break;
-        case locate_service_kpasswd:
-            name_tmpl = KPASSWDINFO_TMPL;
-            break;
-        default:
-            PLUGIN_DEBUG(("Unsupported service [%d].\n", svc));
-            return EINVAL;
-    }
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) return ENOMEM;
 
-    krb5info_name = talloc_asprintf(ctx, name_tmpl, realm);
-    if (krb5info_name == NULL) {
-        PLUGIN_DEBUG(("asprintf failed.\n"));
-        ret = EINVAL;
+    /* Create a hidden memory context as a parent
+     * for created kserver contexts
+     */
+    kmem_ctx = talloc_new(tmp_ctx);
+    if (!tmp_ctx) {
+        ret = ENOMEM;
         goto done;
     }
 
     fd = open(krb5info_name, O_RDONLY);
     if (fd == -1) {
+        ret = errno;
         PLUGIN_DEBUG(("open failed [%s][%d][%s].\n",
-                      krb5info_name, errno, strerror(errno)));
-        ret = errno;
+                      krb5info_name, ret, strerror(ret)));
         goto done;
     }
 
-    memset(buf, 0, BUFSIZE+1);
-
     errno = 0;
     len = sss_atomic_read_s(fd, buf, BUFSIZE);
     if (len == -1) {
@@ -158,61 +156,137 @@ static int get_krb5info(const char *realm, struct sssd_ctx *ctx,
     }
     close(fd);
 
+    /* Make sure there's always a trailing NULL */
+    buf[len] = '\0';
+
     if (len == BUFSIZE) {
         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;
+    p = (char *)buf;
 
-        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 [%d], assuming default.\n",
-                            port));
-                port = 0;
-            }
+    while (*p) {
+        /* First get a complete line */
+        eol = strchr(p, '\n');
+        if (eol) {
+            *eol = '\0';
         } else {
-            PLUGIN_DEBUG(("Illegal port number [%s], assuming default.\n",
-                        port_str));
+            eol = (char *)&(buf[len]);
+        }
+
+        if (eol == p) {
+            /* Zero-length line
+             * Skip it unless we're at the end of the buffer.
+             */
+            if (eol == (char *)&(buf[len])) {
+                break;
+            }
+
+            p++;
+            continue;
+        }
+
+        port_str = strrchr(p, ':');
+        if (!port_str) {
             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 [%d], assuming "
+                                  "default.\n",
+                                  port));
+                    port = 0;
+                }
+            } else {
+                PLUGIN_DEBUG(("Illegal port number [%s], assuming default.\n",
+                              port_str));
+                port = 0;
+            }
         }
+
+        kctx = sssd_kserver_ctx_init(kmem_ctx, p, port);
+        if (!kctx) {
+            ret = ENOMEM;
+            goto done;
+        }
+        PLUGIN_DEBUG(("Created kserver context: [%s][%lu]\n",
+                      kctx->addr, kctx->port));
+
+        DLIST_ADD_END(kctx_list, kctx, struct sssd_kserver_ctx *);
+
+        p = eol + 1;
+    }
+
+    talloc_steal(mem_ctx, kmem_ctx);
+
+    *_kctx = kctx_list;
+    ret = EOK;
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+static int get_krb5info(const char *realm, struct sssd_ctx *ctx,
+                        enum locate_service_type svc)
+{
+    int ret;
+    char *krb5info_name = NULL;
+    const char *name_tmpl = NULL;
+
+    switch (svc) {
+        case locate_service_kdc:
+            name_tmpl = KDCINFO_TMPL;
+            break;
+        case locate_service_kpasswd:
+            name_tmpl = KPASSWDINFO_TMPL;
+            break;
+        default:
+            PLUGIN_DEBUG(("Unsupported service [%d].\n", svc));
+            return EINVAL;
+    }
+
+    krb5info_name = talloc_asprintf(ctx, name_tmpl, realm);
+    if (krb5info_name == NULL) {
+        PLUGIN_DEBUG(("asprintf failed.\n"));
+        ret = EINVAL;
+        goto done;
     }
 
     switch (svc) {
         case locate_service_kdc:
             talloc_zfree(ctx->kdc);
-            ctx->kdc = sssd_kserver_ctx_init(ctx, (char *)buf, port);
-            if (ctx->kdc == NULL) {
-                PLUGIN_DEBUG(("kserver ctx init failed.\n"));
+            ret = parse_krb5info(ctx, ctx, krb5info_name, &ctx->kdc);
+            if (ret != EOK) {
+                PLUGIN_DEBUG(("Parsing [%s] failed.\n", krb5info_name));
                 ret = ENOMEM;
                 goto done;
             }
             break;
         case locate_service_kpasswd:
             talloc_zfree(ctx->kpasswd);
-            ctx->kpasswd = sssd_kserver_ctx_init(ctx, (char *)buf, port);
-            if (ctx->kpasswd == NULL) {
-                PLUGIN_DEBUG(("kserver ctx init failed.\n"));
+            ret = parse_krb5info(ctx, ctx, krb5info_name, &ctx->kpasswd);
+            if (ret != EOK) {
+                PLUGIN_DEBUG(("Parsing [%s] failed.\n", krb5info_name));
                 ret = ENOMEM;
                 goto done;
             }
-- 
1.7.10.4

