389-ds-base/0026-Issue-6004-idletimeout-may-be-ignored-6005.patch
Viktor Ashirov 397f5c3e21 Bump version to 1.4.3.39-12
- Resolves: RHEL-85499 - [RFE] defer memberof nested updates [rhel-8.10.z]
- Resolves: RHEL-65663 - dsconf incorrectly setting up Pass-Through Authentication
- Resolves: RHEL-80704 - Increased memory consumption caused by NDN cache [rhel-8.10.z]
- Resolves: RHEL-81127 - nsslapd-idletimeout is ignored [rhel-8.10.z]
- Resolves: RHEL-81136 - Healthcheck tool should warn admin about creating a substring index on membership attribute [rhel-8.10.z]
- Resolves: RHEL-81143 - 389DirectoryServer Process Stops When Setting up Sorted VLV Index [rhel-8.10.z]
- Resolves: RHEL-81152 - AddressSanitizer: double-free [rhel-8.10.z]
- Resolves: RHEL-81176 - Verbose option for dsctl is not shown in help of actions [rhel-8.10.z]
2025-04-03 17:11:37 +02:00

231 lines
8.0 KiB
Diff

From bd2829d04491556c35a0b36b591c09a69baf6546 Mon Sep 17 00:00:00 2001
From: progier389 <progier@redhat.com>
Date: Mon, 11 Dec 2023 11:58:40 +0100
Subject: [PATCH] Issue 6004 - idletimeout may be ignored (#6005)
* Issue 6004 - idletimeout may be ignored
Problem: idletimeout is still not handled when binding as non root (unless there are some activity
on another connection)
Fix:
Add a slapi_eq_repeat_rel handler that walks all active connection every seconds and check if the timeout is expired.
Note about CI test:
Notice that idletimeout is never enforced for connections bound as root (i.e cn=directory manager).
Issue #6004
Reviewed by: @droideck, @tbordaz (Thanks!)
(cherry picked from commit 86b5969acbe124eec8c89bcf1ab2156b2b140c17)
(cherry picked from commit bdb0a72b4953678e5418406b3c202dfa2c7469a2)
(cherry picked from commit 61cebc191cd4090072dda691b9956dbde4cf7c48)
---
.../tests/suites/config/regression_test.py | 82 ++++++++++++++++++-
ldap/servers/slapd/daemon.c | 52 +++++++++++-
2 files changed, 128 insertions(+), 6 deletions(-)
diff --git a/dirsrvtests/tests/suites/config/regression_test.py b/dirsrvtests/tests/suites/config/regression_test.py
index 0000dd82d..8dbba8cd2 100644
--- a/dirsrvtests/tests/suites/config/regression_test.py
+++ b/dirsrvtests/tests/suites/config/regression_test.py
@@ -6,20 +6,49 @@
# See LICENSE for details.
# --- END COPYRIGHT BLOCK ---
#
+import os
import logging
import pytest
+import time
from lib389.utils import *
from lib389.dseldif import DSEldif
-from lib389.config import LDBMConfig
+from lib389.config import BDB_LDBMConfig, LDBMConfig, Config
from lib389.backend import Backends
from lib389.topologies import topology_st as topo
+from lib389.idm.user import UserAccounts, TEST_USER_PROPERTIES
+from lib389._constants import DEFAULT_SUFFIX, PASSWORD, DN_DM
pytestmark = pytest.mark.tier0
logging.getLogger(__name__).setLevel(logging.INFO)
log = logging.getLogger(__name__)
+DEBUGGING = os.getenv("DEBUGGING", default=False)
CUSTOM_MEM = '9100100100'
+IDLETIMEOUT = 5
+DN_TEST_USER = f'uid={TEST_USER_PROPERTIES["uid"]},ou=People,{DEFAULT_SUFFIX}'
+
+
+@pytest.fixture(scope="module")
+def idletimeout_topo(topo, request):
+ """Create an instance with a test user and set idletimeout"""
+ inst = topo.standalone
+ config = Config(inst)
+
+ users = UserAccounts(inst, DEFAULT_SUFFIX)
+ user = users.create(properties={
+ **TEST_USER_PROPERTIES,
+ 'userpassword' : PASSWORD,
+ })
+ config.replace('nsslapd-idletimeout', str(IDLETIMEOUT))
+
+ def fin():
+ if not DEBUGGING:
+ config.reset('nsslapd-idletimeout')
+ user.delete()
+
+ request.addfinalizer(fin)
+ return topo
# Function to return value of available memory in kb
@@ -79,7 +108,7 @@ def test_maxbersize_repl(topo):
nsslapd-errorlog-logmaxdiskspace are set in certain order
:id: 743e912c-2be4-4f5f-9c2a-93dcb18f51a0
- :setup: MMR with two suppliers
+ :setup: Standalone Instance
:steps:
1. Stop the instance
2. Set nsslapd-errorlog-maxlogsize before/after
@@ -112,3 +141,52 @@ def test_maxbersize_repl(topo):
log.info("Assert no init_dse_file errors in the error log")
assert not inst.ds_error_log.match('.*ERR - init_dse_file.*')
+
+def test_bdb_config(topo):
+ """Check that bdb config entry exists
+
+ :id: edbc6f54-7c98-11ee-b1c0-482ae39447e5
+ :setup: standalone
+ :steps:
+ 1. Check that bdb config instance exists.
+ :expectedresults:
+ 1. Success
+ """
+
+ inst = topo.standalone
+ assert BDB_LDBMConfig(inst).exists()
+
+
+@pytest.mark.parametrize("dn,expected_result", [(DN_TEST_USER, True), (DN_DM, False)])
+def test_idletimeout(idletimeout_topo, dn, expected_result):
+ """Check that bdb config entry exists
+
+ :id: b20f2826-942a-11ee-827b-482ae39447e5
+ :parametrized: yes
+ :setup: Standalone Instance with test user and idletimeout
+ :steps:
+ 1. Open new ldap connection
+ 2. Bind with the provided dn
+ 3. Wait longer than idletimeout
+ 4. Try to bind again the provided dn and check if
+ connection is closed or not.
+ 5. Check if result is the expected one.
+ :expectedresults:
+ 1. Success
+ 2. Success
+ 3. Success
+ 4. Success
+ 5. Success
+ """
+
+ inst = idletimeout_topo.standalone
+
+ l = ldap.initialize(f'ldap://localhost:{inst.port}')
+ l.bind_s(dn, PASSWORD)
+ time.sleep(IDLETIMEOUT+1)
+ try:
+ l.bind_s(dn, PASSWORD)
+ result = False
+ except ldap.SERVER_DOWN:
+ result = True
+ assert expected_result == result
diff --git a/ldap/servers/slapd/daemon.c b/ldap/servers/slapd/daemon.c
index 57e07e5f5..6df109760 100644
--- a/ldap/servers/slapd/daemon.c
+++ b/ldap/servers/slapd/daemon.c
@@ -68,6 +68,8 @@
#define SLAPD_ACCEPT_WAKEUP_TIMER 250
#endif
+#define MILLISECONDS_PER_SECOND 1000
+
int slapd_wakeup_timer = SLAPD_WAKEUP_TIMER; /* time in ms to wakeup */
int slapd_accept_wakeup_timer = SLAPD_ACCEPT_WAKEUP_TIMER; /* time in ms to wakeup */
#ifdef notdef /* GGOODREPL */
@@ -1045,6 +1047,48 @@ slapd_sockets_ports_free(daemon_ports_t *ports_info)
#endif
}
+/*
+ * Tells if idle timeout has expired
+ */
+static inline int __attribute__((always_inline))
+has_idletimeout_expired(Connection *c, time_t curtime)
+{
+ return (c->c_state != CONN_STATE_FREE && !c->c_gettingber &&
+ c->c_idletimeout > 0 && NULL == c->c_ops &&
+ curtime - c->c_idlesince >= c->c_idletimeout);
+}
+
+/*
+ * slapi_eq_repeat_rel callback that checks that idletimeout has not expired.
+ */
+void
+check_idletimeout(time_t when __attribute__((unused)), void *arg __attribute__((unused)) )
+{
+ Connection_Table *ct = the_connection_table;
+ time_t curtime = slapi_current_rel_time_t();
+ /* Walk all active connections of all connection listeners */
+ for (int list_num = 0; list_num < ct->list_num; list_num++) {
+ for (Connection *c = connection_table_get_first_active_connection(ct, list_num);
+ c != NULL; c = connection_table_get_next_active_connection(ct, c)) {
+ if (!has_idletimeout_expired(c, curtime)) {
+ continue;
+ }
+ /* Looks like idletimeout has expired, lets acquire the lock
+ * and double check.
+ */
+ if (pthread_mutex_trylock(&(c->c_mutex)) == EBUSY) {
+ continue;
+ }
+ if (has_idletimeout_expired(c, curtime)) {
+ /* idle timeout has expired */
+ disconnect_server_nomutex(c, c->c_connid, -1,
+ SLAPD_DISCONNECT_IDLE_TIMEOUT, ETIMEDOUT);
+ }
+ pthread_mutex_unlock(&(c->c_mutex));
+ }
+ }
+}
+
void
slapd_daemon(daemon_ports_t *ports)
{
@@ -1258,7 +1302,9 @@ slapd_daemon(daemon_ports_t *ports)
"MAINPID=%lu",
(unsigned long)getpid());
#endif
-
+ slapi_eq_repeat_rel(check_idletimeout, NULL,
+ slapi_current_rel_time_t(),
+ MILLISECONDS_PER_SECOND);
/* The meat of the operation is in a loop on a call to select */
while (!g_get_shutdown()) {
int select_return = 0;
@@ -1734,9 +1780,7 @@ handle_pr_read_ready(Connection_Table *ct, PRIntn num_poll __attribute__((unused
disconnect_server_nomutex(c, c->c_connid, -1,
SLAPD_DISCONNECT_POLL, EPIPE);
}
- } else if (c->c_idletimeout > 0 &&
- (curtime - c->c_idlesince) >= c->c_idletimeout &&
- NULL == c->c_ops) {
+ } else if (has_idletimeout_expired(c, curtime)) {
/* idle timeout */
disconnect_server_nomutex(c, c->c_connid, -1,
SLAPD_DISCONNECT_IDLE_TIMEOUT, ETIMEDOUT);
--
2.48.1