From ac8605fd3cf64e917aefd6bc9a9babba4f46575b Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek@redhat.com>
Date: Thu, 22 Apr 2010 17:37:42 +0200
Subject: [PATCH] Try all servers during Kerberos auth

The Kerberos backend would previously try only the first server and if
it was unreachable, it immediatelly went offline.
---
 src/providers/krb5/krb5_auth.c |   96 +++++++++++++++++++++++++++-------------
 1 files changed, 65 insertions(+), 31 deletions(-)

diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index e1aaebf..1d3f5ab 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -790,6 +790,16 @@ done:
     krb_reply(be_req, dp_err, pd->pam_status);
 }
 
+static void kpasswd_fail(struct krb5child_req *kr)
+{
+    struct pam_data *pd = kr->pd;
+    struct be_req *be_req = kr->req;
+
+    talloc_free(kr);
+    pd->pam_status = PAM_AUTHTOK_LOCK_BUSY;
+    krb_reply(be_req, DP_ERR_OK, pd->pam_status);
+}
+
 static void krb5_resolve_kdc_done(struct tevent_req *req)
 {
     struct krb5child_req *kr = tevent_req_callback_data(req,
@@ -801,12 +811,17 @@ static void krb5_resolve_kdc_done(struct tevent_req *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 (pd->cmd == SSS_PAM_CHAUTHTOK) {
+            kpasswd_fail(kr);
+            return;
+        } else {
+            /* all servers have been tried for auth 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;
+        }
     } else {
         if (pd->cmd == SSS_PAM_CHAUTHTOK &&
             kr->krb5_ctx->kpasswd_service != NULL) {
@@ -838,8 +853,6 @@ 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);
@@ -848,10 +861,8 @@ static void krb5_resolve_kpasswd_done(struct tevent_req *req)
          * 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);
+        kpasswd_fail(kr);
+        return;
     }
 
     krb5_find_ccache_step(kr);
@@ -978,6 +989,7 @@ static void krb5_child_done(struct tevent_req *req)
     int32_t *msg_len;
     int pam_status = PAM_SYSTEM_ERR;
     int dp_err = DP_ERR_FATAL;
+    struct tevent_req *next_req;
 
     ret = handle_child_recv(req, pd, &buf, &len);
     talloc_zfree(kr->timeout_handler);
@@ -1010,7 +1022,8 @@ static void krb5_child_done(struct tevent_req *req)
         goto done;
     }
 
-    if (*msg_status != PAM_SUCCESS && *msg_status != PAM_AUTHINFO_UNAVAIL) {
+    if (*msg_status != PAM_SUCCESS && *msg_status != PAM_AUTHINFO_UNAVAIL &&
+        *msg_status != PAM_AUTHTOK_LOCK_BUSY) {
         pam_status = *msg_status;
         dp_err = DP_ERR_OK;
 
@@ -1030,6 +1043,45 @@ static void krb5_child_done(struct tevent_req *req)
         goto done;
     }
 
+    if (kr->kpasswd_srv != NULL) {
+        if (*msg_status == PAM_AUTHTOK_LOCK_BUSY) {
+            fo_set_port_status(kr->kpasswd_srv, PORT_NOT_WORKING);
+
+            /* try to resolve next KDC */
+            next_req = be_resolve_server_send(kr, kr->req->be_ctx->ev,
+                                            kr->req->be_ctx,
+                                            kr->krb5_ctx->kpasswd_service->name);
+            if (next_req == NULL) {
+                DEBUG(1, ("be_resolve_server_send failed.\n"));
+                goto done;
+            }
+            tevent_req_set_callback(next_req, krb5_resolve_kpasswd_done, kr);
+            return;
+        } else {
+            fo_set_port_status(kr->kpasswd_srv, PORT_WORKING);
+        }
+    }
+
+    if (*msg_status == PAM_AUTHINFO_UNAVAIL ||
+        (kr->kpasswd_srv == NULL && *msg_status == PAM_AUTHTOK_LOCK_BUSY)) {
+        if (kr->srv != NULL) {
+            fo_set_port_status(kr->srv, PORT_NOT_WORKING);
+        }
+
+        /* try to resolve next KDC */
+        next_req = be_resolve_server_send(kr, kr->req->be_ctx->ev,
+                                          kr->req->be_ctx,
+                                          kr->krb5_ctx->service->name);
+        if (next_req == NULL) {
+            DEBUG(1, ("be_resolve_server_send failed.\n"));
+            goto done;
+        }
+        tevent_req_set_callback(next_req, krb5_resolve_kdc_done, kr);
+        return;
+    } else if (kr->srv != NULL) {
+        fo_set_port_status(kr->srv, PORT_WORKING);
+    }
+
     pref_len = strlen(CCACHE_ENV_NAME)+1;
     if (*msg_len > pref_len &&
         strncmp((const char *) &buf[p], CCACHE_ENV_NAME"=", pref_len) == 0) {
@@ -1045,24 +1097,6 @@ static void krb5_child_done(struct tevent_req *req)
         goto done;
     }
 
-    if (*msg_status == PAM_AUTHINFO_UNAVAIL) {
-        if (kr->srv != NULL) {
-            fo_set_port_status(kr->srv, PORT_NOT_WORKING);
-        }
-        be_mark_offline(be_req->be_ctx);
-        kr->is_offline = true;
-    } else if (kr->srv != NULL) {
-        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);
-- 
1.6.6.1

