On Thu, Mar 11, 2010 at 11:00:58AM -0500, Stephen Gallagher wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> 
> On 03/11/2010 10:05 AM, Sumit Bose wrote:
> > On Thu, Mar 11, 2010 at 07:19:13AM -0500, Stephen Gallagher wrote:
> > On 03/10/2010 11:18 AM, Sumit Bose wrote:
> >>>> Hi,
> >>>>
> >>>> this is an attempt to fix bz#571896 aka trac#415. A new parameter
> >>>> krb5_kadmin is introduced together with a new failover service in the
> >>>> Kerberos provider. If the change password service is not available the
> >>>> backend is not switched to offline mode unless authentication isn't
> >>>> possible too.
> >>>>
> >>>> I haven't add to option to the IPA provider, because I think here the
> >>>> kpasswd always runs together with the KDC.
> >>>>
> >>>> bye,
> >>>> Sumit
> >>>>
> > 
> > To sum up a conversation we had on IRC yesterday:
> > 
> > Nack, please use krb5_kpasswd instead of krb5_kadmin.
> > 
> > Additionally, this patch brought to mind that we need to handle
> > non-standard ports for both krb5_kdcip and krb5_kpasswd.
> > 
> > 
> >> new version attached.
> > 
> >> bye,
> >> Sumit
> > 
> 
> Nack (nitpicks).
> 
> Using strtol() to assign a value to an int (rather than a long) can be
> unsafe, as on some platforms int == short.
> 
> I'd rather you used a uint16_t for the sssd_ctx->kdc_port and
> sssd_ctx->kpasswd_port. That's the only acceptable range in any case.
> 
> Same for the port in the sssd_krb5_locator_lookup(). And other places.
> 
> 
> Otherwise, the code looks sound to me.
> 

please find attached the new version.

bye,
Sumit


> - -- 
> Stephen Gallagher
> RHCE 804006346421761
> 
> Delivering value year after year.
> Red Hat ranks #1 in value among software vendors.
> http://www.redhat.com/promo/vendor/
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.10 (GNU/Linux)
> Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org/
> 
> iEYEARECAAYFAkuZE7oACgkQeiVVYja6o6OT3wCdEVRiDXbj+K3jXvAwp0fAaJhc
> xAUAoJ2GwOMAWIrMOnnfyNJRTV4/dg+A
> =2uDl
> -----END PGP SIGNATURE-----
> _______________________________________________
> sssd-devel mailing list
> sssd-devel@lists.fedorahosted.org
> https://fedorahosted.org/mailman/listinfo/sssd-devel
From ac206e5407bd10070580c63bd5a8b6c3bcbc5ab4 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Wed, 10 Mar 2010 17:03:23 +0100
Subject: [PATCH] Add krb5_kpasswd option

---
 src/config/SSSDConfig.py                   |    1 +
 src/config/SSSDConfigTest.py               |    3 +-
 src/config/etc/sssd.api.d/sssd-krb5.conf   |    1 +
 src/krb5_plugin/sssd_krb5_locator_plugin.c |  176 ++++++++++++++++++++++------
 src/man/sssd-krb5.5.xml                    |   23 ++++-
 src/providers/ipa/ipa_common.c             |    5 +-
 src/providers/krb5/krb5_auth.c             |   85 ++++++++++++--
 src/providers/krb5/krb5_auth.h             |    2 +
 src/providers/krb5/krb5_child.c            |    7 +
 src/providers/krb5/krb5_common.c           |  112 +++++++++++++++---
 src/providers/krb5/krb5_common.h           |    8 +-
 src/providers/krb5/krb5_init.c             |   21 +++-
 12 files changed, 373 insertions(+), 71 deletions(-)

diff --git a/src/config/SSSDConfig.py b/src/config/SSSDConfig.py
index c9e08ca..7dd90e7 100644
--- a/src/config/SSSDConfig.py
+++ b/src/config/SSSDConfig.py
@@ -100,6 +100,7 @@ option_strings = {
 
     # [provider/krb5/chpass]
     'krb5_changepw_principal' : _('The principal of the change password 
service'),
+    'krb5_kpasswd' : _('Server where the change password service is running if 
not on the KDC'),
 
     # [provider/ldap]
     'ldap_uri' : _('ldap_uri, The URI of the LDAP server'),
diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
index 4e9d091..7e882e7 100755
--- a/src/config/SSSDConfigTest.py
+++ b/src/config/SSSDConfigTest.py
@@ -677,7 +677,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
 
         #Test looking up all provider values
         options = domain.list_provider_options('krb5')
-        control_list.extend(['krb5_changepw_principal'])
+        control_list.extend(['krb5_changepw_principal',
+                             'krb5_kpasswd'])
 
         self.assertTrue(type(options) == dict,
                         "Options should be a dictionary")
diff --git a/src/config/etc/sssd.api.d/sssd-krb5.conf 
b/src/config/etc/sssd.api.d/sssd-krb5.conf
index 7ba0ab3..7b12e08 100644
--- a/src/config/etc/sssd.api.d/sssd-krb5.conf
+++ b/src/config/etc/sssd.api.d/sssd-krb5.conf
@@ -11,3 +11,4 @@ krb5_validate = bool, None, false
 
 [provider/krb5/chpass]
 krb5_changepw_principal = str, None, false
+krb5_kpasswd = str, None, false
diff --git a/src/krb5_plugin/sssd_krb5_locator_plugin.c 
b/src/krb5_plugin/sssd_krb5_locator_plugin.c
index 8f32a31..eb8666e 100644
--- a/src/krb5_plugin/sssd_krb5_locator_plugin.c
+++ b/src/krb5_plugin/sssd_krb5_locator_plugin.c
@@ -29,12 +29,18 @@
 #include <netdb.h>
 #include <sys/stat.h>
 #include <fcntl.h>
+#include <ctype.h>
 
 #include <krb5/locate_plugin.h>
 
 #include "providers/krb5/krb5_common.h"
 
+#define DEFAULT_KERBEROS_PORT 88
+#define DEFAULT_KADMIN_PORT 749
+#define DEFAULT_KPASSWD_PORT 464
+
 #define BUFSIZE 512
+#define PORT_STR_SIZE 7
 #define SSSD_KRB5_LOCATOR_DEBUG "SSSD_KRB5_LOCATOR_DEBUG"
 #define DEBUG_KEY "[sssd_krb5_locator] "
 #define PLUGIN_DEBUG(body) do { \
@@ -46,6 +52,9 @@
 struct sssd_ctx {
     char *sssd_realm;
     char *kdc_addr;
+    uint16_t kdc_port;
+    char *kpasswd_addr;
+    uint16_t kpasswd_port;
     bool debug;
 };
 
@@ -69,31 +78,50 @@ void debug_fn(const char *format, ...)
     free(s);
 }
 
-static int get_kdcinfo(const char *realm, struct sssd_ctx *ctx)
+static int get_krb5info(const char *realm, struct sssd_ctx *ctx,
+                        enum locate_service_type svc)
 {
     int ret;
-    char *kdcinfo_name = NULL;
+    char *krb5info_name = NULL;
     size_t len;
     uint8_t buf[BUFSIZE + 1];
     uint8_t *p;
     int fd = -1;
+    const char *name_tmpl = NULL;
+    char *port_str;
+    long port;
+    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;
+    }
+
 
-    len = strlen(realm) + strlen(KDCINFO_TMPL);
+    len = strlen(realm) + strlen(name_tmpl);
 
-    kdcinfo_name = calloc(1, len + 1);
-    if (kdcinfo_name == NULL) {
+    krb5info_name = calloc(1, len + 1);
+    if (krb5info_name == NULL) {
         PLUGIN_DEBUG(("malloc failed.\n"));
         return ENOMEM;
     }
 
-    ret = snprintf(kdcinfo_name, len, KDCINFO_TMPL, realm);
+    ret = snprintf(krb5info_name, len, name_tmpl, realm);
     if (ret < 0) {
-        PLUGIN_DEBUG(("snprintf failed"));
+        PLUGIN_DEBUG(("snprintf failed.\n"));
         ret = EINVAL;
+        goto done;
     }
-    kdcinfo_name[len] = '\0';
+    krb5info_name[len] = '\0';
 
-    fd = open(kdcinfo_name, O_RDONLY);
+    fd = open(krb5info_name, O_RDONLY);
     if (fd == -1) {
         PLUGIN_DEBUG(("open failed [%d][%s].\n", errno, strerror(errno)));
         ret = errno;
@@ -117,27 +145,73 @@ static int get_kdcinfo(const char *realm, struct sssd_ctx 
*ctx)
     close(fd);
 
     if (len == 0) {
-        PLUGIN_DEBUG(("Content of kdcinfo file [%s] is [%d] or larger.\n",
-                      kdcinfo_name, BUFSIZE));
+        PLUGIN_DEBUG(("Content of krb5info file [%s] is [%d] or larger.\n",
+                      krb5info_name, BUFSIZE));
     }
-    PLUGIN_DEBUG(("Found kdcinfo [%s].\n", buf));
+    PLUGIN_DEBUG(("Found [%s] in [%s].\n", buf, krb5info_name));
 
-    ctx->kdc_addr = strdup((char *) buf);
-    if (ctx->kdc_addr == NULL) {
-        PLUGIN_DEBUG(("strdup failed.\n"));
-        ret = ENOMEM;
-        goto done;
+    port_str = strrchr((char *) buf, ':');
+    if (port_str == NULL) {
+        port = 0;
     }
+    *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;
+        }
 
-    ctx->sssd_realm = strdup(realm);
-    if (ctx->sssd_realm == NULL) {
-        PLUGIN_DEBUG(("strdup failed.\n"));
-        ret = ENOMEM;
-        goto done;
+        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;
+    }
+
+    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;
+                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;
+                goto done;
+            }
+            ctx->kpasswd_port = (uint16_t) port;
+            break;
+        default:
+            PLUGIN_DEBUG(("Unsupported service [%d].\n", svc));
+            ret = EINVAL;
+            goto done;
     }
 
 done:
-    free(kdcinfo_name);
+    free(krb5info_name);
     return ret;
 }
 
@@ -173,6 +247,7 @@ void sssd_krb5_locator_close(void *private_data)
     PLUGIN_DEBUG(("sssd_krb5_locator_close called\n"));
 
     free(ctx->kdc_addr);
+    free(ctx->kpasswd_addr);
     free(ctx->sssd_realm);
     free(ctx);
     private_data = NULL;
@@ -192,21 +267,37 @@ krb5_error_code sssd_krb5_locator_lookup(void 
*private_data,
     struct addrinfo *ai;
     struct sssd_ctx *ctx;
     struct addrinfo ai_hints;
-    const char *service = NULL;
+    uint16_t port = 0;
+    const char *addr = NULL;
+    char port_str[PORT_STR_SIZE];
 
     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) {
-        free(ctx->kdc_addr);
-        ctx->kdc_addr = NULL;
         free(ctx->sssd_realm);
-        ctx->sssd_realm = NULL;
-        ret = get_kdcinfo(realm, ctx);
+        ctx->sssd_realm = strdup(realm);
+        if (ctx->sssd_realm == NULL) {
+            PLUGIN_DEBUG(("strdup failed.\n"));
+            return ENOMEM;
+        }
+
+        ret = get_krb5info(realm, ctx, locate_service_kdc);
         if (ret != EOK) {
-            PLUGIN_DEBUG(("get_kdcinfo failed.\n"));
+            PLUGIN_DEBUG(("get_krb5info failed.\n"));
             return KRB5_PLUGIN_NO_HANDLE;
         }
+
+        if (svc == locate_service_kadmin || svc == locate_service_kpasswd) {
+            ret = get_krb5info(realm, ctx, locate_service_kpasswd);
+            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;
+            }
+        }
     }
 
     PLUGIN_DEBUG(("sssd_realm[%s] requested realm[%s] family[%d] socktype[%d] "
@@ -216,13 +307,16 @@ krb5_error_code sssd_krb5_locator_lookup(void 
*private_data,
     switch (svc) {
         case locate_service_kdc:
         case locate_service_master_kdc:
-            service = "kerberos";
+            addr = ctx->kdc_addr;
+            port = ctx->kdc_port ? ctx->kdc_port : DEFAULT_KERBEROS_PORT;
             break;
         case locate_service_kadmin:
-            service = "kerberos-adm";
+            addr = ctx->kpasswd_addr;
+            port = DEFAULT_KADMIN_PORT;
             break;
         case locate_service_kpasswd:
-            service = "kpasswd";
+            addr = ctx->kpasswd_addr;
+            port = ctx->kpasswd_port ? ctx->kpasswd_port : 
DEFAULT_KPASSWD_PORT;
             break;
         case locate_service_krb524:
             return KRB5_PLUGIN_NO_HANDLE;
@@ -250,10 +344,18 @@ 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 EFAULT;
+    }
+
     memset(&ai_hints, 0, sizeof(struct addrinfo));
-    ai_hints.ai_flags = AI_NUMERICHOST;
+    ai_hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV;
     ai_hints.ai_socktype = socktype;
-    ret = getaddrinfo(ctx->kdc_addr, service, &ai_hints, &ai);
+
+    ret = getaddrinfo(addr, port_str, &ai_hints, &ai);
     if (ret != 0) {
         PLUGIN_DEBUG(("getaddrinfo failed [%d][%s].\n", ret,
                                                         gai_strerror(ret)));
@@ -264,7 +366,7 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data,
         return EFAULT;
     }
 
-    PLUGIN_DEBUG(("addr[%s] family[%d] socktype[%d]\n", ctx->kdc_addr,
+    PLUGIN_DEBUG(("addr[%s:%s] family[%d] socktype[%d]\n", addr, port_str,
                  ai->ai_family, ai->ai_socktype));
 
     if ((family == AF_UNSPEC || ai->ai_family == family) &&
@@ -275,10 +377,10 @@ krb5_error_code sssd_krb5_locator_lookup(void 
*private_data,
             PLUGIN_DEBUG(("cbfunc failed\n"));
             return ret;
         } else {
-            PLUGIN_DEBUG(("[%s] used\n", ctx->kdc_addr));
+            PLUGIN_DEBUG(("[%s] used\n", addr));
         }
     } else {
-        PLUGIN_DEBUG(("[%s] NOT used\n", ctx->kdc_addr));
+        PLUGIN_DEBUG(("[%s] NOT used\n", addr));
     }
 
     return 0;
diff --git a/src/man/sssd-krb5.5.xml b/src/man/sssd-krb5.5.xml
index ca4dae2..c291eca 100644
--- a/src/man/sssd-krb5.5.xml
+++ b/src/man/sssd-krb5.5.xml
@@ -69,7 +69,9 @@
                             of the Kerberos servers to which SSSD should
                             connect in the order of preference. For more
                             information on failover and server redundancy,
-                            see the <quote>FAILOVER</quote> section.
+                            see the <quote>FAILOVER</quote> section. An 
optional
+                            port number (preceded by a colon) may be appended 
to
+                            the addresses or hostnames.
                         </para>
                     </listitem>
                 </varlistentry>
@@ -99,6 +101,25 @@
                 </varlistentry>
 
                 <varlistentry>
+                    <term>krb5_kpasswd (string)</term>
+                    <listitem>
+                        <para>
+                            If the change password service is not running on 
the
+                            KDC alternative servers can be defined here. An
+                            optional port number (preceded by a colon) may be
+                            appended to the addresses or hostnames.
+                        </para>
+                        <para>
+                            For more information on failover and server
+                            redundancy, see the <quote>FAILOVER</quote> 
section.
+                            Please note that even if there are no more kpasswd
+                            servers to try the back end is not switch to 
offline
+                            if authentication against the KDC is still 
possible.
+                        </para>
+                    </listitem>
+                </varlistentry>
+
+                <varlistentry>
                     <term>krb5_ccachedir (string)</term>
                     <listitem>
                         <para>
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index a50b63b..1fc881f 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -499,9 +499,10 @@ static void ipa_resolve_callback(void *private_data, 
struct fo_server *server)
     talloc_zfree(service->krb5_service->address);
     service->krb5_service->address = address;
 
-    ret = write_kdcinfo_file(service->krb5_service->realm, address);
+    ret = write_krb5info_file(service->krb5_service->realm, address,
+                              SSS_KRB5KDC_FO_SRV);
     if (ret != EOK) {
-        DEBUG(2, ("write_kdcinfo_file failed, authentication might fail.\n"));
+        DEBUG(2, ("write_krb5info_file failed, authentication might fail.\n"));
     }
 
 }
diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index b8b498a..ce3aacd 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -676,7 +676,9 @@ 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_resolve_kdc_done(struct tevent_req *req);
+static void krb5_resolve_kpasswd_done(struct tevent_req *req);
+static void krb5_find_ccache_step(struct krb5child_req *kr);
 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);
@@ -852,14 +854,16 @@ static void get_user_attr_done(void *pvt, int err, struct 
ldb_result *res)
         break;
     }
 
+    kr->srv = NULL;
+    kr->kpasswd_srv = NULL;
     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"));
+        DEBUG(1, ("be_resolve_server_send failed.\n"));
         goto failed;
     }
 
-    tevent_req_set_callback(req, krb5_resolve_done, kr);
+    tevent_req_set_callback(req, krb5_resolve_kdc_done, kr);
 
     return;
 
@@ -870,18 +874,13 @@ failed:
     krb_reply(be_req, dp_err, pd->pam_status);
 }
 
-static void krb5_resolve_done(struct tevent_req *req)
+static void krb5_resolve_kdc_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;
-    char *msg;
-    size_t offset = 0;
-    bool private_path = false;
 
     ret = be_resolve_server_recv(req, &kr->srv);
     talloc_zfree(req);
@@ -892,8 +891,68 @@ static void krb5_resolve_done(struct tevent_req *req)
          * the ccache file. */
         be_mark_offline(be_req->be_ctx);
         kr->is_offline = true;
+    } else {
+        if (pd->cmd == SSS_PAM_CHAUTHTOK &&
+            kr->krb5_ctx->kpasswd_service != NULL) {
+            req = be_resolve_server_send(kr, be_req->be_ctx->ev, 
be_req->be_ctx,
+                                         kr->krb5_ctx->kpasswd_service->name);
+            if (req == NULL) {
+                DEBUG(1, ("be_resolve_server_send failed.\n"));
+                goto failed;
+            }
+
+            tevent_req_set_callback(req, krb5_resolve_kpasswd_done, kr);
+
+            return;
+        }
     }
 
+    krb5_find_ccache_step(kr);
+    return;
+
+failed:
+    talloc_free(kr);
+
+    pd->pam_status = PAM_SYSTEM_ERR;
+    krb_reply(be_req, DP_ERR_FATAL, pd->pam_status);
+}
+
+static void krb5_resolve_kpasswd_done(struct tevent_req *req)
+{
+    struct krb5child_req *kr = tevent_req_callback_data(req,
+                                                        struct krb5child_req);
+    int ret;
+    struct pam_data *pd = kr->pd;
+    struct be_req *be_req = kr->req;
+
+    ret = be_resolve_server_recv(req, &kr->kpasswd_srv);
+    talloc_zfree(req);
+    if (ret) {
+        /* all kpasswd servers have been tried and none was found good, but the
+         * kdc seems ok. Password changes are not possible but
+         * authentication. We return an PAM error here, but do not mark the
+         * backend offline. */
+
+        talloc_free(kr);
+        pd->pam_status = PAM_AUTHTOK_LOCK_BUSY;
+        krb_reply(be_req, DP_ERR_OK, pd->pam_status);
+    }
+
+    krb5_find_ccache_step(kr);
+}
+
+static void krb5_find_ccache_step(struct krb5child_req *kr)
+{
+    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;
+    char *msg;
+    size_t offset = 0;
+    bool private_path = false;
+    struct tevent_req *req = NULL;
+
     if (kr->ccname == NULL ||
         (be_is_offline(be_req->be_ctx) && !kr->active_ccache_present &&
             !kr->valid_tgt_present) ||
@@ -1081,6 +1140,14 @@ static void krb5_child_done(struct tevent_req *req)
         fo_set_port_status(kr->srv, PORT_WORKING);
     }
 
+    if (kr->kpasswd_srv != NULL) {
+        if (*msg_status == PAM_AUTHTOK_LOCK_BUSY) {
+            fo_set_port_status(kr->kpasswd_srv, PORT_NOT_WORKING);
+        } else {
+            fo_set_port_status(kr->kpasswd_srv, PORT_WORKING);
+        }
+    }
+
     struct sysdb_attrs *attrs;
     attrs = sysdb_new_attrs(kr);
     ret = sysdb_attrs_add_string(attrs, SYSDB_CCACHE_FILE, kr->ccname);
diff --git a/src/providers/krb5/krb5_auth.h b/src/providers/krb5/krb5_auth.h
index 825f3d6..9f8c414 100644
--- a/src/providers/krb5/krb5_auth.h
+++ b/src/providers/krb5/krb5_auth.h
@@ -57,6 +57,7 @@ struct krb5child_req {
     gid_t gid;
     bool is_offline;
     struct fo_server *srv;
+    struct fo_server *kpasswd_srv;
     bool active_ccache_present;
     bool valid_tgt_present;
 };
@@ -90,6 +91,7 @@ struct krb5_ctx {
 
     struct dp_option *opts;
     struct krb5_service *service;
+    struct krb5_service *kpasswd_service;
     int child_debug_fd;
 
     pcre *illegal_path_re;
diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index 234b838..86242ef 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -587,9 +587,16 @@ static errno_t changepw_child(int fd, struct krb5_req *kr)
         goto sendresponse;
     }
 
+    memset(&result_code_string, 0, sizeof(krb5_data));
+    memset(&result_string, 0, sizeof(krb5_data));
     kerr = krb5_change_password(kr->ctx, kr->creds, newpass_str, &result_code,
                                 &result_code_string, &result_string);
 
+    if (kerr == KRB5_KDC_UNREACH) {
+        pam_status = PAM_AUTHTOK_LOCK_BUSY;
+        goto sendresponse;
+    }
+
     if (kerr != 0 || result_code != 0) {
         if (kerr != 0) {
             KRB5_DEBUG(1, kerr);
diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c
index 8c1c7fa..2b3331e 100644
--- a/src/providers/krb5/krb5_common.c
+++ b/src/providers/krb5/krb5_common.c
@@ -26,6 +26,7 @@
 #include <unistd.h>
 #include <netdb.h>
 #include <arpa/inet.h>
+#include <ctype.h>
 
 #include "providers/dp_backend.h"
 #include "providers/krb5/krb5_common.h"
@@ -38,7 +39,8 @@ struct dp_option default_krb5_opts[] = {
     { "krb5_changepw_principal", DP_OPT_STRING, { "kadmin/changepw" }, 
NULL_STRING },
     { "krb5_auth_timeout", DP_OPT_NUMBER, { .number = 15 }, NULL_NUMBER },
     { "krb5_keytab", DP_OPT_STRING, { "/etc/krb5.keytab" }, NULL_STRING },
-    { "krb5_validate", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE }
+    { "krb5_validate", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
+    { "krb5_kpasswd", DP_OPT_STRING, NULL_STRING, NULL_STRING }
 };
 
 errno_t check_and_export_options(struct dp_option *opts,
@@ -67,7 +69,13 @@ errno_t check_and_export_options(struct dp_option *opts,
 
     dummy = dp_opt_get_cstring(opts, KRB5_KDC);
     if (dummy == NULL) {
-        DEBUG(1, ("No KDC expicitly configured, using defaults"));
+        DEBUG(1, ("No KDC explicitly configured, using defaults"));
+    }
+
+    dummy = dp_opt_get_cstring(opts, KRB5_KPASSWD);
+    if (dummy == NULL) {
+        DEBUG(1, ("No kpasswd server explicitly configured, "
+                  "using the KDC or defaults"));
     }
 
     dummy = dp_opt_get_cstring(opts, KRB5_CCNAME_TMPL);
@@ -139,21 +147,33 @@ done:
     return ret;
 }
 
-errno_t write_kdcinfo_file(const char *realm, const char *kdc)
+errno_t write_krb5info_file(const char *realm, const char *server,
+                           const char *service)
 {
     int ret;
     int fd = -1;
     char *tmp_name = NULL;
-    char *kdcinfo_name = NULL;
+    char *krb5info_name = NULL;
     TALLOC_CTX *tmp_ctx = NULL;
-    int kdc_len;
+    const char *name_tmpl = NULL;
+    int server_len;
+
+    if (realm == NULL || *realm == '\0' || server == NULL || *server == '\0' ||
+        service == NULL || service == '\0') {
+        DEBUG(1, ("Missing or empty realm, server or service.\n"));
+        return EINVAL;
+    }
 
-    if (realm == NULL || *realm == '\0' || kdc == NULL || *kdc == '\0') {
-        DEBUG(1, ("Missing or empty realm or kdc.\n"));
+    if (strcmp(service, SSS_KRB5KDC_FO_SRV) == 0) {
+        name_tmpl = KDCINFO_TMPL;
+    } else if (strcmp(service, SSS_KRB5KPASSWD_FO_SRV) == 0) {
+        name_tmpl = KPASSWDINFO_TMPL;
+    } else {
+        DEBUG(1, ("Unsupported service [%s]\n.", service));
         return EINVAL;
     }
 
-    kdc_len = strlen(kdc);
+    server_len = strlen(server);
 
     tmp_ctx = talloc_new(NULL);
     if (tmp_ctx == NULL) {
@@ -161,15 +181,15 @@ errno_t write_kdcinfo_file(const char *realm, const char 
*kdc)
         return ENOMEM;
     }
 
-    tmp_name = talloc_asprintf(tmp_ctx, PUBCONF_PATH"/.kdcinfo_dummy_XXXXXX");
+    tmp_name = talloc_asprintf(tmp_ctx, PUBCONF_PATH"/.krb5info_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) {
+    krb5info_name = talloc_asprintf(tmp_ctx, name_tmpl, realm);
+    if (krb5info_name == NULL) {
         DEBUG(1, ("talloc_asprintf failed.\n"));
         ret = ENOMEM;
         goto done;
@@ -182,12 +202,12 @@ errno_t write_kdcinfo_file(const char *realm, const char 
*kdc)
         goto done;
     }
 
-    ret = write(fd, kdc, kdc_len);
+    ret = write(fd, server, server_len);
     if (ret == -1) {
         DEBUG(1, ("write failed [%d][%s].\n", errno, strerror(errno)));
         goto done;
     }
-    if (ret != kdc_len) {
+    if (ret != server_len) {
         DEBUG(1, ("Partial write occured, this should never happen.\n"));
         ret = EINTR;
         goto done;
@@ -205,7 +225,7 @@ errno_t write_kdcinfo_file(const char *realm, const char 
*kdc)
         goto done;
     }
 
-    ret = rename(tmp_name, kdcinfo_name);
+    ret = rename(tmp_name, krb5info_name);
     if (ret == -1) {
         DEBUG(1, ("rename failed [%d][%s].\n", errno, strerror(errno)));
         goto done;
@@ -248,12 +268,20 @@ static void krb5_resolve_callback(void *private_data, 
struct fo_server *server)
         return;
     }
 
+    address = talloc_asprintf_append(address, ":%d",
+                                     fo_get_server_port(server));
+    if (address == NULL) {
+        DEBUG(1, ("talloc_asprintf_append failed.\n"));
+        return;
+    }
+
     talloc_zfree(krb5_service->address);
     krb5_service->address = address;
 
-    ret = write_kdcinfo_file(krb5_service->realm, address);
+    ret = write_krb5info_file(krb5_service->realm, address,
+                              krb5_service->name);
     if (ret != EOK) {
-        DEBUG(2, ("write_kdcinfo_file failed, authentication might fail.\n"));
+        DEBUG(2, ("write_krb5info_file failed, authentication might fail.\n"));
     }
 
     return;
@@ -269,6 +297,11 @@ int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx 
*ctx,
     char **list = NULL;
     int ret;
     int i;
+    char *port_str;
+    long port;
+    char *server_spec;
+    char *endptr;
+    struct servent *servent;
 
     tmp_ctx = talloc_new(memctx);
     if (!tmp_ctx) {
@@ -308,8 +341,53 @@ int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx 
*ctx,
     for (i = 0; list[i]; i++) {
 
         talloc_steal(service, list[i]);
+        server_spec = talloc_strdup(service, list[i]);
+        port_str = strrchr(server_spec, ':');
+        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;
+                    DEBUG(1, ("strtol failed on [%s]: [%d][%s].\n", port_str,
+                              ret, strerror(ret)));
+                    goto done;
+                }
+                if (*endptr != '\0') {
+                    DEBUG(1, ("Found additional characters [%s] in port number 
"
+                              "[%s].\n", endptr, port_str));
+                    ret = EINVAL;
+                    goto done;
+                }
+
+                if (port < 1 || port > 65535) {
+                    DEBUG(1, ("Illegal port number [%d].\n", port));
+                    ret = EINVAL;
+                    goto done;
+                }
+            } else if (isalpha(*port_str)) {
+                servent = getservbyname(port_str, NULL);
+                if (servent == NULL) {
+                    DEBUG(1, ("getservbyname cannot find service [%s].\n",
+                              port_str));
+                    ret = EINVAL;
+                    goto done;
+                }
+
+                port = servent->s_port;
+            } else {
+                DEBUG(1, ("Unsupported port specifier in [%s].\n", list[i]));
+                ret = EINVAL;
+                goto done;
+            }
+        }
 
-        ret = be_fo_add_server(ctx, service_name, list[i], 0, NULL);
+        ret = be_fo_add_server(ctx, service_name, server_spec, (int) port,
+                               list[i]);
         if (ret && ret != EEXIST) {
             DEBUG(0, ("Failed to add server\n"));
             goto done;
diff --git a/src/providers/krb5/krb5_common.h b/src/providers/krb5/krb5_common.h
index 832ffcd..0b0da31 100644
--- a/src/providers/krb5/krb5_common.h
+++ b/src/providers/krb5/krb5_common.h
@@ -38,6 +38,10 @@
 #define SSSD_KRB5_CHANGEPW_PRINCIPLE "SSSD_KRB5_CHANGEPW_PRINCIPLE"
 
 #define KDCINFO_TMPL PUBCONF_PATH"/kdcinfo.%s"
+#define KPASSWDINFO_TMPL PUBCONF_PATH"/kpasswdinfo.%s"
+
+#define SSS_KRB5KDC_FO_SRV "KRB5KDC"
+#define SSS_KRB5KPASSWD_FO_SRV "KRB5KPASSWD"
 
 enum krb5_opts {
     KRB5_KDC = 0,
@@ -48,6 +52,7 @@ enum krb5_opts {
     KRB5_AUTH_TIMEOUT,
     KRB5_KEYTAB,
     KRB5_VALIDATE,
+    KRB5_KPASSWD,
 
     KRB5_OPTS
 };
@@ -64,7 +69,8 @@ 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);
+errno_t write_krb5info_file(const char *realm, const char *kdc,
+                            const char *service);
 
 int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx,
                       const char *service_name, const char *servers,
diff --git a/src/providers/krb5/krb5_init.c b/src/providers/krb5/krb5_init.c
index 4d21238..83129d9 100644
--- a/src/providers/krb5/krb5_init.c
+++ b/src/providers/krb5/krb5_init.c
@@ -52,6 +52,7 @@ int sssm_krb5_auth_init(struct be_ctx *bectx,
     unsigned v;
     FILE *debug_filep;
     const char *krb5_servers;
+    const char *krb5_kpasswd_servers;
     const char *krb5_realm;
     const char *errstr;
     int errval;
@@ -98,13 +99,27 @@ int sssm_krb5_auth_init(struct be_ctx *bectx,
         return EINVAL;
     }
 
-    ret = krb5_service_init(ctx, bectx, "KRB5", krb5_servers, krb5_realm,
-                            &ctx->service);
+    ret = krb5_service_init(ctx, bectx, SSS_KRB5KDC_FO_SRV, krb5_servers,
+                            krb5_realm, &ctx->service);
     if (ret != EOK) {
-        DEBUG(0, ("Failed to init IPA failover service!\n"));
+        DEBUG(0, ("Failed to init KRB5 failover service!\n"));
         return ret;
     }
 
+    krb5_kpasswd_servers = dp_opt_get_string(ctx->opts, KRB5_KPASSWD);
+    if (krb5_kpasswd_servers == NULL) {
+        DEBUG(0, ("Missing krb5_kpasswd option, using KDC!\n"));
+        ctx->kpasswd_service = NULL;
+    } else {
+        ret = krb5_service_init(ctx, bectx, SSS_KRB5KPASSWD_FO_SRV,
+                                krb5_kpasswd_servers, krb5_realm,
+                                &ctx->kpasswd_service);
+        if (ret != EOK) {
+            DEBUG(0, ("Failed to init KRB5KPASSWD failover service!\n"));
+            return ret;
+        }
+    }
+
     ret = check_and_export_options(ctx->opts, bectx->domain);
     if (ret != EOK) {
         DEBUG(1, ("check_and_export_options failed.\n"));
-- 
1.6.6.1

_______________________________________________
sssd-devel mailing list
sssd-devel@lists.fedorahosted.org
https://fedorahosted.org/mailman/listinfo/sssd-devel

Reply via email to