>From b8419bbf7ad5a2beeb6650711c83a63fa5458bc7 Mon Sep 17 00:00:00 2001
From: Pavel Reichl <preichl@redhat.com>
Date: Mon, 15 Sep 2014 14:13:21 +0100
Subject: [PATCH 2/2] SDAP: check that connection is open before BIND

Tivoli server does not return an empty response when being asked for the
rootDSE data but an error. In this case the rootDSE lookup in SSSD will
terminate the connection to the server and return a error. But since
errors except timeouts are ignored SSSD will try to continue with the
bind, but since the connection is already terminated this will fail as
well. And this will terminate the whole operation.

Make sure the connection is open before performing BIND operation.

Resolves:
https://fedorahosted.org/sssd/ticket/2435
---
 src/providers/ldap/sdap_async_connection.c | 87 ++++++++++++++++++++++++++++++
 1 file changed, 87 insertions(+)

diff --git a/src/providers/ldap/sdap_async_connection.c b/src/providers/ldap/sdap_async_connection.c
index 8f8d67a9b43bb82c2f0e776f36af886ff86c65f8..9951e8b62e49eae310893a3f0844cf4ee2027e83 100644
--- a/src/providers/ldap/sdap_async_connection.c
+++ b/src/providers/ldap/sdap_async_connection.c
@@ -1398,6 +1398,7 @@ struct sdap_cli_connect_state {
 
     enum connect_tls force_tls;
     bool do_auth;
+    bool use_tls;
 };
 
 static int sdap_cli_resolve_next(struct tevent_req *req);
@@ -1410,6 +1411,8 @@ static void sdap_cli_kinit_step(struct tevent_req *req);
 static void sdap_cli_kinit_done(struct tevent_req *subreq);
 static void sdap_cli_auth_step(struct tevent_req *req);
 static void sdap_cli_auth_done(struct tevent_req *subreq);
+static errno_t sdap_cli_auth_reconnect(struct tevent_req *subreq);
+static void sdap_cli_auth_reconnect_done(struct tevent_req *subreq);
 static void sdap_cli_rootdse_auth_done(struct tevent_req *subreq);
 
 static errno_t
@@ -1781,6 +1784,16 @@ static void sdap_cli_auth_step(struct tevent_req *req)
     struct sss_auth_token *authtok;
     errno_t ret;
 
+    /* It's possible that connection was terminated by server (e.g. #2435),
+       to overcome this try to connect again. */
+    if (state->sh == NULL || !state->sh->connected) {
+        ret = sdap_cli_auth_reconnect(req);
+        if (ret != EOK) {
+            tevent_req_error(req, ret);
+        }
+        return;
+    }
+
     /* Set the LDAP expiration time
      * If SASL has already set it, use the sooner of the two
      */
@@ -1842,6 +1855,80 @@ static void sdap_cli_auth_step(struct tevent_req *req)
     tevent_req_set_callback(subreq, sdap_cli_auth_done, req);
 }
 
+static errno_t sdap_cli_auth_reconnect(struct tevent_req *req)
+{
+    struct sdap_cli_connect_state *state;
+    struct tevent_req *subreq;
+    errno_t ret;
+
+    state = tevent_req_data(req, struct sdap_cli_connect_state);
+
+    ret = decide_tls_usage(state->force_tls, state->opts->basic,
+                           state->service->uri, &state->use_tls);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    subreq = sdap_connect_send(state, state->ev, state->opts,
+                               state->service->uri,
+                               state->service->sockaddr,
+                               state->use_tls);
+
+    if (subreq == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    tevent_req_set_callback(subreq, sdap_cli_auth_reconnect_done, req);
+
+    ret = EOK;
+
+done:
+    return ret;
+}
+
+static void sdap_cli_auth_reconnect_done(struct tevent_req *subreq)
+{
+    struct sdap_cli_connect_state *state;
+    struct tevent_req *req;
+    errno_t ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct sdap_cli_connect_state);
+
+    talloc_zfree(state->sh);
+    ret = sdap_connect_recv(subreq, state, &state->sh);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    /* if TLS was used, the sdap handle is already marked as connected */
+    if (!state->use_tls) {
+        /* we need to mark handle as connected to allow anonymous bind */
+        ret = sdap_set_connected(state->sh, state->ev);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "sdap_set_connected() failed.\n");
+            goto done;
+        }
+    }
+
+    /* End request if reconnecting failed to avoid endless loop */
+    if (state->sh == NULL || !state->sh->connected) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to reconnect.\n");
+        ret = EIO;
+        goto done;
+    } else {
+        sdap_cli_auth_step(req);
+    }
+    ret = EOK;
+
+done:
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+    }
+}
+
 static void sdap_cli_auth_done(struct tevent_req *subreq)
 {
     struct tevent_req *req = tevent_req_callback_data(subreq,
-- 
1.9.3

