ldap_server: Terminate LDAP connections on krb ticket expiry
authorVolker Lendecke <vl@samba.org>
Mon, 10 Aug 2020 14:24:04 +0000 (16:24 +0200)
committerVolker Lendecke <vl@samba.org>
Fri, 21 Aug 2020 19:14:32 +0000 (19:14 +0000)
See RFC4511 section 4.4.1 and

https://lists.samba.org/archive/cifs-protocol/2020-August/003515.html

for details: Windows terminates LDAP connections when the krb5 ticket
expires, Samba should do the same. This patch slightly deviates from
Windows behaviour by sending a LDAP exop response with msgid 0 that is
ASN1-encoded conforming to RFC4511.

Bug: https://bugzilla.samba.org/show_bug.cgi?id=14465

Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
selftest/knownfail.d/ldap
source4/ldap_server/ldap_backend.c
source4/ldap_server/ldap_server.c
source4/ldap_server/ldap_server.h

index 21c1aca7e6e72b3b37bb639682e1a703671503fc..0331d3687d41a6e27f52505aca16d3edf1da7305 100644 (file)
@@ -1,4 +1,3 @@
 # the attributes too long test returns the wrong error
 ^samba4.ldap.python.+test_attribute_ranges_too_long
 samba4.ldap.python\(ad_dc_default\).*__main__.BasicTests.test_ldapSearchNoAttributes
-^samba4.ldap.session-expiry.session-expiry\(ad_dc_default\)
index bf724335a250d9b55d8bde85b2858a1a5fa8c4fc..2839082daefd4042fcbc04ac7ca2247a20894cf5 100644 (file)
@@ -1384,11 +1384,48 @@ static NTSTATUS ldapsrv_AbandonRequest(struct ldapsrv_call *call)
        return NT_STATUS_OK;
 }
 
+static NTSTATUS ldapsrv_expired(struct ldapsrv_call *call)
+{
+       struct ldapsrv_reply *reply = NULL;
+       struct ldap_ExtendedResponse *r = NULL;
+
+       DBG_DEBUG("Sending connection expired message\n");
+
+       reply = ldapsrv_init_reply(call, LDAP_TAG_ExtendedResponse);
+       if (reply == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /*
+        * According to RFC4511 section 4.4.1 this has a msgid of 0
+        */
+       reply->msg->messageid = 0;
+
+       r = &reply->msg->r.ExtendedResponse;
+       r->response.resultcode = LDB_ERR_UNAVAILABLE;
+       r->response.errormessage = "The server has timed out this connection";
+       r->oid = "1.3.6.1.4.1.1466.20036"; /* see rfc4511 section 4.4.1 */
+
+       ldapsrv_queue_reply(call, reply);
+       return NT_STATUS_OK;
+}
+
 NTSTATUS ldapsrv_do_call(struct ldapsrv_call *call)
 {
        unsigned int i;
        struct ldap_message *msg = call->request;
+       struct ldapsrv_connection *conn = call->conn;
        NTSTATUS status;
+       bool expired;
+
+       expired = timeval_expired(&conn->limits.expire_time);
+       if (expired) {
+               status = ldapsrv_expired(call);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+               return NT_STATUS_NETWORK_SESSION_EXPIRED;
+       }
 
        /* Check for undecoded critical extensions */
        for (i=0; msg->controls && msg->controls[i]; i++) {
index 036dfaab5ae96bb886dda084a4032c3ce20e8eaa..5cd72d3e2583fe89de7abc7d2e41a822f319e6c2 100644 (file)
@@ -69,6 +69,7 @@ static void ldapsrv_terminate_connection(struct ldapsrv_connection *conn,
 
        tevent_queue_stop(conn->sockets.send_queue);
        TALLOC_FREE(conn->sockets.read_req);
+       TALLOC_FREE(conn->deferred_expire_disconnect);
        if (conn->active_call) {
                tevent_req_cancel(conn->active_call);
                conn->active_call = NULL;
@@ -1014,16 +1015,62 @@ static struct tevent_req *ldapsrv_process_call_send(TALLOC_CTX *mem_ctx,
        return req;
 }
 
+static void ldapsrv_disconnect_ticket_expired(struct tevent_req *subreq);
+
 static void ldapsrv_process_call_trigger(struct tevent_req *req,
                                         void *private_data)
 {
        struct ldapsrv_process_call_state *state =
                tevent_req_data(req,
                struct ldapsrv_process_call_state);
+       struct ldapsrv_connection *conn = state->call->conn;
        NTSTATUS status;
 
+       if (conn->deferred_expire_disconnect != NULL) {
+               /*
+                * Just drop this on the floor
+                */
+               tevent_req_done(req);
+               return;
+       }
+
        /* make the call */
        status = ldapsrv_do_call(state->call);
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)) {
+               /*
+                * For testing purposes, defer the TCP disconnect
+                * after having sent the msgid 0
+                * 1.3.6.1.4.1.1466.20036 exop response. LDAP clients
+                * should not wait for the TCP connection to close but
+                * handle this packet equivalent to a TCP
+                * disconnect. This delay enables testing both cases
+                * in LDAP client libraries.
+                */
+
+               int defer_msec = lpcfg_parm_int(
+                       conn->lp_ctx,
+                       NULL,
+                       "ldap_server",
+                       "delay_expire_disconnect",
+                       0);
+
+               conn->deferred_expire_disconnect = tevent_wakeup_send(
+                       conn,
+                       conn->connection->event.ctx,
+                       timeval_current_ofs_msec(defer_msec));
+               if (tevent_req_nomem(conn->deferred_expire_disconnect, req)) {
+                       return;
+               }
+               tevent_req_set_callback(
+                       conn->deferred_expire_disconnect,
+                       ldapsrv_disconnect_ticket_expired,
+                       conn);
+
+               tevent_req_done(req);
+               return;
+       }
+
        if (!NT_STATUS_IS_OK(status)) {
                tevent_req_nterror(req, status);
                return;
@@ -1032,6 +1079,21 @@ static void ldapsrv_process_call_trigger(struct tevent_req *req,
        tevent_req_done(req);
 }
 
+static void ldapsrv_disconnect_ticket_expired(struct tevent_req *subreq)
+{
+       struct ldapsrv_connection *conn = tevent_req_callback_data(
+               subreq, struct ldapsrv_connection);
+       bool ok;
+
+       ok = tevent_wakeup_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (!ok) {
+               DBG_WARNING("tevent_wakeup_recv failed\n");
+       }
+       conn->deferred_expire_disconnect = NULL;
+       ldapsrv_terminate_connection(conn, "network session expired");
+}
+
 static NTSTATUS ldapsrv_process_call_recv(struct tevent_req *req)
 {
        NTSTATUS status;
index 74c19fd2fbc4f1ccd8ea7943ab325b2db3401c79..c94bd914b9b748f934c2e326ff360d668f105cf9 100644 (file)
@@ -66,6 +66,7 @@ struct ldapsrv_connection {
        } limits;
 
        struct tevent_req *active_call;
+       struct tevent_req *deferred_expire_disconnect;
 
        struct ldapsrv_call *pending_calls;
 };