[SSSD] [PATCH] Read KDC info from file instead from environment

Sumit Bose sbose at redhat.com
Mon Nov 23 15:06:05 UTC 2009


On Fri, Nov 20, 2009 at 04:18:25PM -0500, Simo Sorce wrote:
> On Thu, 2009-11-19 at 18:32 +0100, Sumit Bose wrote:
> > Hi,
> > 
> > this patch changes the way the locator plugin finds out about the KDC.
> > Now the information is written to a file which is read by the plugin.
> > 
> > Two thing will be address in different patches.
> > 
> > - the enviroment variables are still sent to the client. I haven't
> >   removed them in this patch, because it would introduce a dependency
> > to
> >   other not-committed patches. I thought it might be easier this way.
> > - integration of the fail-over framework.
> 
> ACK,
> 
> it works for me.
> 
> Simo.
> 

Attached is a rebased and extendend version of the patch. The changes
need to work together with the fail-over framework are included.

bye,
Sumit
-------------- next part --------------
>From 31af93f59adfc4a74df19e1fb56948a8b95e58e2 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Thu, 19 Nov 2009 17:53:38 +0100
Subject: [PATCH] Read KDC info from file instead from environment

Then name or IP adress of the KDC is written into the pubconf directory
into a file named kdcinfo.REALM. The locator plugin will then read this
file and pass the data to the kerberos libraries.
---
 contrib/sssd.spec.in                          |    3 +
 server/Makefile.am                            |    4 +-
 server/conf_macros.m4                         |   17 ++
 server/configure.ac                           |    1 +
 server/krb5_plugin/sssd_krb5_locator_plugin.c |  122 ++++++++++----
 server/providers/ipa/ipa_common.c             |   30 +++-
 server/providers/ipa/ipa_common.h             |    5 +-
 server/providers/ipa/ipa_init.c               |   13 +-
 server/providers/krb5/krb5_auth.c             |   61 ++++++-
 server/providers/krb5/krb5_auth.h             |    4 +-
 server/providers/krb5/krb5_common.c           |  224 +++++++++++++++++++++++--
 server/providers/krb5/krb5_common.h           |   12 ++-
 server/providers/krb5/krb5_init.c             |   52 +++++-
 13 files changed, 474 insertions(+), 74 deletions(-)

diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
index 173d049..1251fe5 100644
--- a/contrib/sssd.spec.in
+++ b/contrib/sssd.spec.in
@@ -28,6 +28,7 @@ Requires(postun): /sbin/service
 %define sssdstatedir %{_localstatedir}/lib/sss
 %define dbpath %{sssdstatedir}/db
 %define pipepath %{sssdstatedir}/pipes
+%define pubconfpath %{sssdstatedir}/pubconf
 
 ### Build Dependencies ###
 
@@ -80,6 +81,7 @@ KRB5_LIBS=-lkrb5 \
     --without-tests \
     --with-db-path=%{dbpath} \
     --with-pipe-path=%{pipepath} \
+    --with-pubconf-path=%{pubconfpath} \
     --with-init-dir=%{_initrddir} \
     --enable-nsslibdir=/%{_lib}
 
@@ -138,6 +140,7 @@ rm -rf $RPM_BUILD_ROOT
 %dir %{sssdstatedir}
 %attr(700,root,root) %dir %{dbpath}
 %attr(755,root,root) %dir %{pipepath}
+%attr(755,root,root) %dir %{pubconfpath}
 %attr(700,root,root) %dir %{pipepath}/private
 %attr(750,root,root) %dir %{_var}/log/%{name}
 %attr(700,root,root) %dir %{_sysconfdir}/sssd
diff --git a/server/Makefile.am b/server/Makefile.am
index c40942a..6db4f04 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -22,6 +22,7 @@ pidpath = @pidpath@
 pipepath = @pipepath@
 initdir = @initdir@
 logpath = @logpath@
+pubconfpath = @pubconfpath@
 
 AM_CFLAGS =
 if WANT_AUX_INFO
@@ -749,7 +750,8 @@ installsssddirs::
     $(DESTDIR)$(dbpath) \
     $(DESTDIR)$(pidpath) \
     $(DESTDIR)$(initdir) \
-    $(DESTDIR)$(logpath)
+    $(DESTDIR)$(logpath) \
+    $(DESTDIR)$(pubconfpath)
 
 install-exec-hook: installsssddirs
 	if [ "$(DESTDIR)" = "" ]; then \
diff --git a/server/conf_macros.m4 b/server/conf_macros.m4
index 0990e50..86ccf5d 100644
--- a/server/conf_macros.m4
+++ b/server/conf_macros.m4
@@ -66,6 +66,23 @@ AC_DEFUN([WITH_LOG_PATH],
     AC_DEFINE_UNQUOTED(LOG_PATH, "$config_logpath", [Where to store log files for the SSSD])
   ])
 
+AC_DEFUN([WITH_PUBCONF_PATH],
+  [ AC_ARG_WITH([pubconf-path],
+                [AC_HELP_STRING([--with-pubconf-path=PATH],
+                                [Where to store pubconf files for the SSSD [/var/lib/sss/pubconf]]
+                               )
+                ]
+               )
+    config_pubconfpath="\"VARDIR\"/lib/sss/pubconf"
+    pubconfpath="${localstatedir}/lib/sss/pubconf"
+    if test x"$with_pubconf_path" != x; then
+        config_pubconfpath=$with_pubconf_path
+        pubconfpath=$with_pubconf_path
+    fi
+    AC_SUBST(pubconfpath)
+    AC_DEFINE_UNQUOTED(PUBCONF_PATH, "$config_pubconfpath", [Where to store pubconf files for the SSSD])
+  ])
+
 AC_DEFUN([WITH_PIPE_PATH],
   [ AC_ARG_WITH([pipe-path],
                 [AC_HELP_STRING([--with-pipe-path=PATH],
diff --git a/server/configure.ac b/server/configure.ac
index 1a94158..696a5a4 100644
--- a/server/configure.ac
+++ b/server/configure.ac
@@ -45,6 +45,7 @@ WITH_DB_PATH
 WITH_PLUGIN_PATH
 WITH_PID_PATH
 WITH_LOG_PATH
+WITH_PUBCONF_PATH
 WITH_PIPE_PATH
 WITH_INIT_DIR
 WITH_SHADOW_UTILS_PATH
diff --git a/server/krb5_plugin/sssd_krb5_locator_plugin.c b/server/krb5_plugin/sssd_krb5_locator_plugin.c
index a30586c..5e79733 100644
--- a/server/krb5_plugin/sssd_krb5_locator_plugin.c
+++ b/server/krb5_plugin/sssd_krb5_locator_plugin.c
@@ -27,12 +27,14 @@
 #include <errno.h>
 #include <sys/types.h>
 #include <netdb.h>
-
+#include <sys/stat.h>
+#include <fcntl.h>
 
 #include <krb5/locate_plugin.h>
 
 #include "providers/krb5/krb5_common.h"
 
+#define BUFSIZE 512
 #define SSSD_KRB5_LOCATOR_DEBUG "SSSD_KRB5_LOCATOR_DEBUG"
 #define DEBUG_KEY "[sssd_krb5_locator] "
 #define PLUGIN_DEBUG(body) do { \
@@ -67,33 +69,60 @@ void debug_fn(const char *format, ...)
     free(s);
 }
 
-krb5_error_code sssd_krb5_locator_init(krb5_context context,
-                                       void **private_data)
+static int get_kdcinfo(const char *realm, struct sssd_ctx *ctx)
 {
-    struct sssd_ctx *ctx;
-    const char *dummy;
     int ret;
+    char *kdcinfo_name = NULL;
+    size_t len;
+    uint8_t buf[BUFSIZE + 1];
+    uint8_t *p;
+    int fd = -1;
+
+    len = strlen(realm) + strlen(KDCINFO_TMPL);
+
+    kdcinfo_name = calloc(1, len + 1);
+    if (kdcinfo_name == NULL) {
+        PLUGIN_DEBUG(("malloc failed.\n"));
+        return ENOMEM;
+    }
 
-    ctx = calloc(1,sizeof(struct sssd_ctx));
-    if (ctx == NULL) return ENOMEM;
+    ret = snprintf(kdcinfo_name, len, KDCINFO_TMPL, realm);
+    if (ret < 0) {
+        PLUGIN_DEBUG(("snprintf failed"));
+        ret = EINVAL;
+    }
+    kdcinfo_name[len] = '\0';
 
-    dummy = getenv(SSSD_KRB5_LOCATOR_DEBUG);
-    if (dummy == NULL) {
-        ctx->debug = false;
-    } else {
-        ctx->debug = true;
-        PLUGIN_DEBUG(("sssd_krb5_locator_init called\n"));
+    fd = open(kdcinfo_name, O_RDONLY);
+    if (fd == -1) {
+        PLUGIN_DEBUG(("open failed [%d][%s].\n", errno, strerror(errno)));
+        ret = errno;
+        goto done;
     }
 
-    dummy = getenv(SSSD_KRB5_REALM);
-    if (dummy == NULL) goto failed;
-    ctx->sssd_realm = strdup(dummy);
-    if (ctx->sssd_realm == NULL) goto failed;
+    len = BUFSIZE;
+    p = buf;
+    memset(buf, 0, BUFSIZE+1);
+    while (len != 0 && (ret = read(fd, p, len)) != 0) {
+        if (ret == -1) {
+            if (errno == EINTR) continue;
+            PLUGIN_DEBUG(("read failed [%d][%s].\n", errno, strerror(errno)));
+            close(fd);
+            goto done;
+        }
 
-    dummy = getenv(SSSD_KRB5_KDC);
-    if (dummy == NULL) goto failed;
+        len -= ret;
+        p += ret;
+    }
+    close(fd);
 
-    ret = getaddrinfo(dummy, "kerberos", NULL, &ctx->sssd_kdc_addrinfo);
+    if (len == 0) {
+        PLUGIN_DEBUG(("Content of kdcinfo file [%s] is [%d] or larger.\n",
+                      kdcinfo_name, BUFSIZE));
+    }
+    PLUGIN_DEBUG(("Found kdcinfo [%s].\n", buf));
+
+    ret = getaddrinfo((char *) buf, "kerberos", NULL, &ctx->sssd_kdc_addrinfo);
     if (ret != 0) {
         PLUGIN_DEBUG(("getaddrinfo failed [%d][%s].\n", ret,
                                                         gai_strerror(ret)));
@@ -101,20 +130,43 @@ krb5_error_code sssd_krb5_locator_init(krb5_context context,
             PLUGIN_DEBUG(("getaddrinfo failed [%d][%s].\n", errno,
                                                             strerror(errno)));
         }
-        goto failed;
+        goto done;
     }
 
-    *private_data = ctx;
+    ctx->sssd_realm = strdup(realm);
+    if (ctx->sssd_realm == NULL) {
+        PLUGIN_DEBUG(("strdup failed.\n"));
+        ret = ENOMEM;
+        goto done;
+    }
 
-    return 0;
-failed:
-    freeaddrinfo(ctx->sssd_kdc_addrinfo);
-    free(ctx->sssd_realm);
-    free(ctx);
 
-    private_data = NULL;
 
-    return EINVAL;
+done:
+    free(kdcinfo_name);
+    return ret;
+}
+
+krb5_error_code sssd_krb5_locator_init(krb5_context context,
+                                       void **private_data)
+{
+    struct sssd_ctx *ctx;
+    const char *dummy;
+
+    ctx = calloc(1,sizeof(struct sssd_ctx));
+    if (ctx == NULL) return ENOMEM;
+
+    dummy = getenv(SSSD_KRB5_LOCATOR_DEBUG);
+    if (dummy == NULL) {
+        ctx->debug = false;
+    } else {
+        ctx->debug = true;
+        PLUGIN_DEBUG(("sssd_krb5_locator_init called\n"));
+    }
+
+    *private_data = ctx;
+
+    return 0;
 }
 
 void sssd_krb5_locator_close(void *private_data)
@@ -150,6 +202,18 @@ 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 (ctx->sssd_realm == NULL || strcmp(ctx->sssd_realm, realm) != 0) {
+        freeaddrinfo(ctx->sssd_kdc_addrinfo);
+        ctx->sssd_kdc_addrinfo = NULL;
+        free(ctx->sssd_realm);
+        ctx->sssd_realm = NULL;
+        ret = get_kdcinfo(realm, ctx);
+        if (ret != EOK) {
+            PLUGIN_DEBUG(("get_kdcinfo failed.\n"));
+            return KRB5_PLUGIN_NO_HANDLE;
+        }
+    }
+
     PLUGIN_DEBUG(("sssd_realm[%s] requested realm[%s] family[%d] socktype[%d] "
                   "locate_service[%d]\n", ctx->sssd_realm, realm, family,
                                           socktype, svc));
diff --git a/server/providers/ipa/ipa_common.c b/server/providers/ipa/ipa_common.c
index 2bd9c76..98ac077 100644
--- a/server/providers/ipa/ipa_common.c
+++ b/server/providers/ipa/ipa_common.c
@@ -478,19 +478,19 @@ static void ipa_resolve_callback(void *private_data, struct fo_server *server)
     /* free old one and replace with new one */
     talloc_zfree(service->sdap->uri);
     service->sdap->uri = new_uri;
-    talloc_zfree(service->krb_server->address);
-    service->krb_server->address = address;
+    talloc_zfree(service->krb5_service->address);
+    service->krb5_service->address = address;
 
-    /* set also env variable */
-    ret = setenv(SSSD_KRB5_KDC, address, 1);
+    ret = write_kdcinfo_file(service->krb5_service->realm, address);
     if (ret != EOK) {
-        DEBUG(2, ("setenv %s failed, authentication might fail.\n",
-                  SSSD_KRB5_KDC));
+        DEBUG(2, ("write_kdcinfo_file failed, authentication might fail.\n"));
     }
+
 }
 
 int ipa_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx,
-                     const char *servers, struct ipa_service **_service)
+                     const char *servers, const char *domain,
+                     struct ipa_service **_service)
 {
     TALLOC_CTX *tmp_ctx;
     struct ipa_service *service;
@@ -514,8 +514,8 @@ int ipa_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx,
         ret = ENOMEM;
         goto done;
     }
-    service->krb_server = talloc_zero(service, struct krb_server);
-    if (!service->krb_server) {
+    service->krb5_service = talloc_zero(service, struct krb5_service);
+    if (!service->krb5_service) {
         ret = ENOMEM;
         goto done;
     }
@@ -532,6 +532,18 @@ int ipa_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx,
         goto done;
     }
 
+    service->krb5_service->name = talloc_strdup(service, "IPA");
+    if (!service->krb5_service->name) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    service->krb5_service->realm = talloc_strdup(service, domain);
+    if (!service->krb5_service->realm) {
+        ret = ENOMEM;
+        goto done;
+    }
+
     /* split server parm into a list */
     ret = sss_split_list(tmp_ctx, servers, ", ", &list, &count);
     if (ret != EOK) {
diff --git a/server/providers/ipa/ipa_common.h b/server/providers/ipa/ipa_common.h
index 8d0840c..8eaae71 100644
--- a/server/providers/ipa/ipa_common.h
+++ b/server/providers/ipa/ipa_common.h
@@ -29,7 +29,7 @@
 
 struct ipa_service {
     struct sdap_service *sdap;
-    struct krb_server *krb_server;
+    struct krb5_service *krb5_service;
 };
 
 enum ipa_basic_opt {
@@ -72,6 +72,7 @@ int ipa_get_auth_options(struct ipa_options *ipa_opts,
                          struct dp_option **_opts);
 
 int ipa_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx,
-                     const char *servers, struct ipa_service **_service);
+                     const char *servers, const char *domain,
+                     struct ipa_service **_service);
 
 #endif /* _IPA_COMMON_H_ */
diff --git a/server/providers/ipa/ipa_init.c b/server/providers/ipa/ipa_init.c
index ea27997..a3f381e 100644
--- a/server/providers/ipa/ipa_init.c
+++ b/server/providers/ipa/ipa_init.c
@@ -59,6 +59,7 @@ struct bet_ops ipa_access_ops = {
 int common_ipa_init(struct be_ctx *bectx)
 {
     const char *ipa_servers;
+    const char *ipa_domain;
     int ret;
 
     ret = ipa_get_options(bectx, bectx->cdb,
@@ -74,8 +75,14 @@ int common_ipa_init(struct be_ctx *bectx)
         return EINVAL;
     }
 
-    ret = ipa_service_init(ipa_options, bectx,
-                           ipa_servers, &ipa_options->service);
+    ipa_domain = dp_opt_get_string(ipa_options->basic, IPA_DOMAIN);
+    if (!ipa_domain) {
+        DEBUG(0, ("Missing ipa_domain option!\n"));
+        return EINVAL;
+    }
+
+    ret = ipa_service_init(ipa_options, bectx, ipa_servers, ipa_domain,
+                           &ipa_options->service);
     if (ret != EOK) {
         DEBUG(0, ("Failed to init IPA failover service!\n"));
         return ret;
@@ -171,7 +178,7 @@ int sssm_ipa_auth_init(struct be_ctx *bectx,
     if (!ctx) {
         return ENOMEM;
     }
-    ctx->server = ipa_options->service->krb_server;
+    ctx->service = ipa_options->service->krb5_service;
     ipa_options->auth_ctx = ctx;
 
     ret = ipa_get_auth_options(ipa_options, bectx->cdb,
diff --git a/server/providers/krb5/krb5_auth.c b/server/providers/krb5/krb5_auth.c
index 8068bce..7ac440f 100644
--- a/server/providers/krb5/krb5_auth.c
+++ b/server/providers/krb5/krb5_auth.c
@@ -528,6 +528,7 @@ static errno_t krb5_setup(struct be_req *req, struct krb5child_req **krb5_req)
     kr->read_from_child_fd = -1;
     kr->write_to_child_fd = -1;
     kr->is_offline = false;
+    kr->active_ccache_present = true;
     talloc_set_destructor((TALLOC_CTX *) kr, krb5_cleanup);
 
     kr->pd = pd;
@@ -929,6 +930,7 @@ static int handle_child_recv(struct tevent_req *req,
 }
 
 static void get_user_attr_done(void *pvt, int err, struct ldb_result *res);
+static void krb5_resolve_done(struct tevent_req *req);
 static void krb5_save_ccname_done(struct tevent_req *req);
 static void krb5_child_done(struct tevent_req *req);
 static void krb5_pam_handler_cache_done(struct tevent_req *treq);
@@ -988,7 +990,6 @@ static void get_user_attr_done(void *pvt, int err, struct ldb_result *res)
     int dp_err = DP_ERR_FATAL;
     const char *ccache_file = NULL;
     const char *dummy;
-    bool active_ccache_present = false;
 
     ret = krb5_setup(be_req, &kr);
     if (ret != EOK) {
@@ -1047,7 +1048,7 @@ static void get_user_attr_done(void *pvt, int err, struct ldb_result *res)
         }
 
         if (ccache_file == NULL) {
-            active_ccache_present = false;
+            kr->active_ccache_present = false;
             DEBUG(4, ("No active ccache file for user [%s] found.\n",
                       pd->user));
             ccache_file = expand_ccname_template(kr, kr,
@@ -1059,11 +1060,11 @@ static void get_user_attr_done(void *pvt, int err, struct ldb_result *res)
                 goto failed;
             }
         } else {
-            active_ccache_present = true;
+            kr->active_ccache_present = true;
         }
         DEBUG(9, ("Ccache_file is [%s] and %s.\n", ccache_file,
-                  active_ccache_present ? "will be kept/renewed" :
-                                          "will be generated"));
+                  kr->active_ccache_present ? "will be kept/renewed" :
+                                              "will be generated"));
         kr->ccname = ccache_file;
         break;
 
@@ -1074,6 +1075,44 @@ static void get_user_attr_done(void *pvt, int err, struct ldb_result *res)
         break;
     }
 
+    req = be_resolve_server_send(kr, be_req->be_ctx->ev, be_req->be_ctx,
+                                 krb5_ctx->service->name);
+    if (req == NULL) {
+        DEBUG(1, ("handle_child_send failed.\n"));
+        goto failed;
+    }
+
+    tevent_req_set_callback(req, krb5_resolve_done, kr);
+
+    return;
+
+failed:
+    talloc_free(kr);
+
+    pd->pam_status = pam_status;
+    krb_reply(be_req, dp_err, pd->pam_status);
+}
+
+static void krb5_resolve_done(struct tevent_req *req)
+{
+    struct krb5child_req *kr = tevent_req_callback_data(req,
+                                                        struct krb5child_req);
+    int ret;
+    int pam_status = PAM_SYSTEM_ERR;
+    int dp_err = DP_ERR_FATAL;
+    struct pam_data *pd = kr->pd;
+    struct be_req *be_req = kr->req;
+
+    ret = be_resolve_server_recv(req, &kr->srv);
+    talloc_zfree(req);
+    if (ret) {
+        /* all servers have been tried and none
+         * was found good, setting offline,
+         * but we still have to call the child to setup
+         * the ccache file. */
+        be_mark_offline(be_req->be_ctx);
+        kr->is_offline = true;
+    }
 
     if (be_is_offline(be_req->be_ctx)) {
         DEBUG(9, ("Preparing for offline operation.\n"));
@@ -1081,14 +1120,14 @@ static void get_user_attr_done(void *pvt, int err, struct ldb_result *res)
         memset(pd->authtok, 0, pd->authtok_size);
         pd->authtok_size = 0;
 
-        if (active_ccache_present) {
+        if (kr->active_ccache_present) {
             req = krb5_save_ccname_send(kr, be_req->be_ctx->ev,
                                         be_req->be_ctx->sysdb,
                                         be_req->be_ctx->domain, pd->user,
                                         kr->ccname);
             if (req == NULL) {
                 DEBUG(1, ("krb5_save_ccname_send failed.\n"));
-                goto failed;
+                goto done;
             }
 
             tevent_req_set_callback(req, krb5_save_ccname_done, kr);
@@ -1099,15 +1138,14 @@ static void get_user_attr_done(void *pvt, int err, struct ldb_result *res)
     req = handle_child_send(kr, be_req->be_ctx->ev, kr);
     if (req == NULL) {
         DEBUG(1, ("handle_child_send failed.\n"));
-        goto failed;
+        goto done;
     }
 
     tevent_req_set_callback(req, krb5_child_done, kr);
     return;
 
-failed:
+done:
     talloc_free(kr);
-
     pd->pam_status = pam_status;
     krb_reply(be_req, dp_err, pd->pam_status);
 }
@@ -1189,6 +1227,9 @@ static void krb5_child_done(struct tevent_req *req)
     }
 
     if (*msg_status == PAM_AUTHINFO_UNAVAIL) {
+        if (kr->srv != NULL) {
+            fo_set_server_status(kr->srv, SERVER_NOT_WORKING);
+        }
         be_mark_offline(be_req->be_ctx);
         kr->is_offline = true;
     }
diff --git a/server/providers/krb5/krb5_auth.h b/server/providers/krb5/krb5_auth.h
index 7851ebb..7facb00 100644
--- a/server/providers/krb5/krb5_auth.h
+++ b/server/providers/krb5/krb5_auth.h
@@ -50,6 +50,8 @@ struct krb5child_req {
     const char *ccname;
     const char *homedir;
     bool is_offline;
+    struct fo_server *srv;
+    bool active_ccache_present;
 };
 
 struct fo_service;
@@ -80,7 +82,7 @@ struct krb5_ctx {
     action_type action;
 
     struct dp_option *opts;
-    struct krb_server *server;
+    struct krb5_service *service;
     int child_debug_fd;
 };
 
diff --git a/server/providers/krb5/krb5_common.c b/server/providers/krb5/krb5_common.c
index 6c23536..6817d65 100644
--- a/server/providers/krb5/krb5_common.c
+++ b/server/providers/krb5/krb5_common.c
@@ -24,6 +24,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <netdb.h>
 
 #include "providers/dp_backend.h"
 #include "providers/krb5/krb5_common.h"
@@ -47,17 +48,8 @@ errno_t check_and_export_options(struct dp_option *opts,
     const char *realm;
     const char *dummy;
     struct stat stat_buf;
-
-    dummy = dp_opt_get_cstring(opts, KRB5_KDC);
-    if (dummy == NULL) {
-        DEBUG(2, ("No KDC expicitly configured, using defaults"));
-    } else {
-        ret = setenv(SSSD_KRB5_KDC, dummy, 1);
-        if (ret != EOK) {
-            DEBUG(2, ("setenv %s failed, authentication might fail.\n",
-                      SSSD_KRB5_KDC));
-        }
-    }
+    char **list;
+    int count;
 
     realm = dp_opt_get_cstring(opts, KRB5_REALM);
     if (realm == NULL) {
@@ -68,12 +60,30 @@ errno_t check_and_export_options(struct dp_option *opts,
         }
         realm = dom->name;
     }
+
     ret = setenv(SSSD_KRB5_REALM, realm, 1);
     if (ret != EOK) {
         DEBUG(2, ("setenv %s failed, authentication might fail.\n",
                   SSSD_KRB5_REALM));
     }
 
+    dummy = dp_opt_get_cstring(opts, KRB5_KDC);
+    if (dummy == NULL) {
+        DEBUG(1, ("No KDC expicitly configured, using defaults"));
+    } else {
+        ret = sss_split_list(opts, dummy, ", ", &list, &count);
+        if (ret != EOK) {
+            DEBUG(1, ("Failed to parse server list!\n"));
+            return ret;
+        }
+        ret = write_kdcinfo_file(realm, list[0]);
+        if (ret != EOK) {
+            DEBUG(1, ("write_kdcinfo_file failed, "
+                      "using kerberos defaults from /etc/krb5.conf"));
+        }
+        talloc_free(list);
+    }
+
     dummy = dp_opt_get_cstring(opts, KRB5_CCACHEDIR);
     ret = lstat(dummy, &stat_buf);
     if (ret != EOK) {
@@ -154,3 +164,195 @@ done:
 
     return ret;
 }
+
+errno_t write_kdcinfo_file(const char *realm, const char *kdc)
+{
+    int ret;
+    int fd = -1;
+    char *tmp_name = NULL;
+    char *kdcinfo_name = NULL;
+    TALLOC_CTX *tmp_ctx = NULL;
+    int kdc_len;
+
+    if (realm == NULL || *realm == '\0' || kdc == NULL || *kdc == '\0') {
+        DEBUG(1, ("Missing or empty realm or kdc.\n"));
+        return EINVAL;
+    }
+
+    kdc_len = strlen(kdc);
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(1, ("talloc_new failed.\n"));
+        return ENOMEM;
+    }
+
+    tmp_name = talloc_asprintf(tmp_ctx, PUBCONF_PATH"/.kdcinfo_dummy_XXXXXX");
+    if (tmp_name == NULL) {
+        DEBUG(1, ("talloc_asprintf failed.\n"));
+        ret = ENOMEM;
+        goto done;
+    }
+
+    kdcinfo_name = talloc_asprintf(tmp_ctx, KDCINFO_TMPL, realm);
+    if (kdcinfo_name == NULL) {
+        DEBUG(1, ("talloc_asprintf failed.\n"));
+        ret = ENOMEM;
+        goto done;
+    }
+
+    fd = mkstemp(tmp_name);
+    if (fd == -1) {
+        DEBUG(1, ("mkstemp failed [%d][%s].\n", errno, strerror(errno)));
+        ret = errno;
+        goto done;
+    }
+
+    ret = write(fd, kdc, kdc_len);
+    if (ret == -1) {
+        DEBUG(1, ("write failed [%d][%s].\n", errno, strerror(errno)));
+        goto done;
+    }
+    if (ret != kdc_len) {
+        DEBUG(1, ("Partial write occured, this should never happen.\n"));
+        ret = EINTR;
+        goto done;
+    }
+
+    ret = fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+    if (ret == -1) {
+        DEBUG(1, ("fchmod failed [%d][%s].\n", errno, strerror(errno)));
+        goto done;
+    }
+
+    ret = close(fd);
+    if (ret == -1) {
+        DEBUG(1, ("close failed [%d][%s].\n", errno, strerror(errno)));
+        goto done;
+    }
+
+    ret = rename(tmp_name, kdcinfo_name);
+    if (ret == -1) {
+        DEBUG(1, ("rename failed [%d][%s].\n", errno, strerror(errno)));
+        goto done;
+    }
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+static void krb5_resolve_callback(void *private_data, struct fo_server *server)
+{
+    struct krb5_service *krb5_service;
+    struct hostent *srvaddr;
+    char *address;
+    int ret;
+
+    krb5_service = talloc_get_type(private_data, struct krb5_service);
+    if (!krb5_service) {
+        DEBUG(1, ("FATAL: Bad private_data\n"));
+        return;
+    }
+
+    srvaddr = fo_get_server_hostent(server);
+    if (!srvaddr) {
+        DEBUG(1, ("FATAL: No hostent available for server (%s)\n",
+                  fo_get_server_name(server)));
+        return;
+    }
+
+    address = talloc_asprintf(krb5_service, srvaddr->h_name);
+    if (!address) {
+        DEBUG(1, ("Failed to copy address ...\n"));
+        return;
+    }
+
+    talloc_zfree(krb5_service->address);
+    krb5_service->address = address;
+
+    ret = write_kdcinfo_file(krb5_service->realm, address);
+    if (ret != EOK) {
+        DEBUG(2, ("write_kdcinfo_file failed, authentication might fail.\n"));
+    }
+
+    return;
+}
+
+
+int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx,
+                      const char *service_name, const char *servers,
+                      const char *realm, struct krb5_service **_service)
+{
+    TALLOC_CTX *tmp_ctx;
+    struct krb5_service *service;
+    char **list = NULL;
+    int count = 0;
+    int ret;
+    int i;
+
+    tmp_ctx = talloc_new(memctx);
+    if (!tmp_ctx) {
+        return ENOMEM;
+    }
+
+    service = talloc_zero(tmp_ctx, struct krb5_service);
+    if (!service) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = be_fo_add_service(ctx, service_name);
+    if (ret != EOK) {
+        DEBUG(1, ("Failed to create failover service!\n"));
+        goto done;
+    }
+
+    service->name = talloc_strdup(service, service_name);
+    if (!service->name) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    service->realm = talloc_strdup(service, realm);
+    if (!service->realm) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sss_split_list(tmp_ctx, servers, ", ", &list, &count);
+    if (ret != EOK) {
+        DEBUG(1, ("Failed to parse server list!\n"));
+        goto done;
+    }
+
+    for (i = 0; i < count; i++) {
+
+        talloc_steal(service, list[i]);
+
+        ret = be_fo_add_server(ctx, service_name, list[i], 0, NULL);
+        if (ret && ret != EEXIST) {
+            DEBUG(0, ("Failed to add server\n"));
+            goto done;
+        }
+
+        DEBUG(6, ("Added Server %s\n", list[i]));
+    }
+
+    ret = be_fo_service_add_callback(memctx, ctx, service_name,
+                                     krb5_resolve_callback, service);
+    if (ret != EOK) {
+        DEBUG(1, ("Failed to add failover callback!\n"));
+        goto done;
+    }
+
+    ret = EOK;
+
+done:
+    if (ret == EOK) {
+        *_service = talloc_steal(memctx, service);
+    }
+    talloc_zfree(tmp_ctx);
+    return ret;
+}
+
diff --git a/server/providers/krb5/krb5_common.h b/server/providers/krb5/krb5_common.h
index 42b0037..832ffcd 100644
--- a/server/providers/krb5/krb5_common.h
+++ b/server/providers/krb5/krb5_common.h
@@ -37,6 +37,8 @@
 #define SSSD_KRB5_REALM "SSSD_KRB5_REALM"
 #define SSSD_KRB5_CHANGEPW_PRINCIPLE "SSSD_KRB5_CHANGEPW_PRINCIPLE"
 
+#define KDCINFO_TMPL PUBCONF_PATH"/kdcinfo.%s"
+
 enum krb5_opts {
     KRB5_KDC = 0,
     KRB5_REALM,
@@ -50,8 +52,10 @@ enum krb5_opts {
     KRB5_OPTS
 };
 
-struct krb_server {
+struct krb5_service {
+    char *name;
     char *address;
+    char *realm;
 };
 
 errno_t check_and_export_options(struct dp_option *opts,
@@ -59,4 +63,10 @@ errno_t check_and_export_options(struct dp_option *opts,
 
 errno_t krb5_get_options(TALLOC_CTX *memctx, struct confdb_ctx *cdb,
                          const char *conf_path, struct dp_option **_opts);
+
+errno_t write_kdcinfo_file(const char *realm, const char *kdc);
+
+int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx,
+                      const char *service_name, const char *servers,
+                      const char *realm, struct krb5_service **_service);
 #endif /* __KRB5_COMMON_H__ */
diff --git a/server/providers/krb5/krb5_init.c b/server/providers/krb5/krb5_init.c
index 26cf5e3..a070c8c 100644
--- a/server/providers/krb5/krb5_init.c
+++ b/server/providers/krb5/krb5_init.c
@@ -29,12 +29,14 @@
 #include "providers/krb5/krb5_auth.h"
 #include "providers/krb5/krb5_common.h"
 
-struct bet_ops krb5_auth_ops = {
-    .handler = krb5_pam_handler,
-    .finalize = NULL,
+struct krb5_options {
+    struct dp_option *opts;
+    struct krb5_ctx *auth_ctx;
 };
 
-struct bet_ops krb5_chpass_ops = {
+struct krb5_options *krb5_options = NULL;
+
+struct bet_ops krb5_auth_ops = {
     .handler = krb5_pam_handler,
     .finalize = NULL,
 };
@@ -48,6 +50,28 @@ int sssm_krb5_auth_init(struct be_ctx *bectx,
     struct tevent_signal *sige;
     unsigned v;
     FILE *debug_filep;
+    const char *krb5_servers;
+    const char *krb5_realm;
+
+    if (krb5_options == NULL) {
+        krb5_options = talloc_zero(bectx, struct krb5_options);
+        if (krb5_options == NULL) {
+            DEBUG(1, ("talloc_zero failed.\n"));
+            return ENOMEM;
+        }
+        ret = krb5_get_options(krb5_options, bectx->cdb, bectx->conf_path,
+                               &krb5_options->opts);
+        if (ret != EOK) {
+            DEBUG(1, ("krb5_get_options failed.\n"));
+            return ret;
+        }
+    }
+
+    if (krb5_options->auth_ctx != NULL) {
+        *ops = &krb5_auth_ops;
+        *pvt_auth_data = krb5_options->auth_ctx;
+        return EOK;
+    }
 
     ctx = talloc_zero(bectx, struct krb5_ctx);
     if (!ctx) {
@@ -56,11 +80,25 @@ int sssm_krb5_auth_init(struct be_ctx *bectx,
     }
 
     ctx->action = INIT_PW;
+    ctx->opts = krb5_options->opts;
+
+    krb5_servers = dp_opt_get_string(ctx->opts, KRB5_KDC);
+    if (krb5_servers == NULL) {
+        DEBUG(0, ("Missing krb5_kdcip option!\n"));
+        return EINVAL;
+    }
+
+    krb5_realm = dp_opt_get_string(ctx->opts, KRB5_REALM);
+    if (krb5_realm == NULL) {
+        DEBUG(0, ("Missing krb5_realm option!\n"));
+        return EINVAL;
+    }
 
-    ret = krb5_get_options(ctx, bectx->cdb, bectx->conf_path, &ctx->opts);
+    ret = krb5_service_init(ctx, bectx, "KRB5", krb5_servers, krb5_realm,
+                            &ctx->service);
     if (ret != EOK) {
-        DEBUG(1, ("krb5_get_options failed.\n"));
-        goto fail;
+        DEBUG(0, ("Failed to init IPA failover service!\n"));
+        return ret;
     }
 
     ret = check_and_export_options(ctx->opts, bectx->domain);
-- 
1.6.2.5



More information about the sssd-devel mailing list