Skip to content

Commit 0ae2fb7

Browse files
committed
17495 SEC Stop python-ldap from logging clear passwords
We are currently enabling tracing and OPT_DEBUG_LEVEL in python-ldap / the underlying libldap library. This will log all internal method calls and their arguments and send them to stderr. This in turn will show up in our apache error_log. As there is no fool-proof way to only hide sensitive information with the current tools, we completely remove this functionality. For reference, here's short discussion on this topic: python-ldap/python-ldap#384 SUP-22379 Change-Id: I3c5f9eba7cf92526964cc12d1fbdc406603727b2
1 parent 5fead1e commit 0ae2fb7

File tree

3 files changed

+68
-16
lines changed

3 files changed

+68
-16
lines changed

.werks/17495.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
[//]: # (werk v2)
2+
# Stop LDAP integration from logging passwords to Apache error_log when LDAP log level is debug
3+
4+
key | value
5+
---------- | ---
6+
date | 2025-02-06T09:55:28+00:00
7+
version | 2.5.0b1
8+
class | security
9+
edition | cre
10+
component | wato
11+
level | 1
12+
compatible | yes
13+
14+
We allow specifying the log level of various checkmk components in the
15+
global settings. In previous versions, when setting the "LDAP" log level
16+
to "Debug" under Global Settings > User Interface > Logging while having
17+
an LDAP integration configured, a trace of each function call to the
18+
`python-ldap` library would be written to the `var/log/apache/error_log`
19+
file. These traces might for example include user names and clear
20+
passwords of each login attempt.
21+
22+
With this werk, we rework the "Debug" log level of our LDAP integration
23+
and will no longer log these traces to the `var/log/apache/error_log`
24+
file.
25+
26+
We thank an external contributer for reporting this issue.
27+
28+
*Affected Versions*:
29+
30+
* 2.3.0
31+
* 2.2.0
32+
* 2.1.0 (EOL)
33+
34+
*Mitigations*:
35+
36+
If you are unable to apply this update, set the log level of "LDAP" to
37+
any other value than "Debug" to not be affected by this vulnerability.
38+
39+
*Indicators of Compromise*:
40+
41+
You have been exposed to this vulnerability if the file
42+
`var/log/apache/error_log` lists any function calls to the `ldap`
43+
library as follows
44+
45+
```
46+
*** <ldap.ldapobject.ReconnectLDAPObject object at {addr}> {ldap_url} - ReconnectLDAPObject.simple_bind, referer: {checkmk_site}/check_mk/login.py
47+
(('cn={user_name},ou=benutzer,dc=corp,dc=de', {password}, None, None), {}), referer: {checkmk_site}/check_mk/login.py
48+
```
49+
50+
*Vulnerability Management:*
51+
52+
We have rated the issue with a CVSS score of 5.6 Medium
53+
(`CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:N/VI:L/VA:H/SC:N/SI:N/SA:N`) and
54+
assigned `CVE-2025-1075`.

cmk/gui/userdb/ldap_connector.py

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,13 @@
3131

3232
import abc
3333
import copy
34-
import logging
35-
import os
3634
import shutil
37-
import sys
3835
import time
3936
import traceback
4037
from collections.abc import Callable, Iterator, Mapping, Sequence
4138
from datetime import datetime
4239
from pathlib import Path
43-
from typing import Any, cast, IO, Literal
40+
from typing import Any, cast, Literal
4441

4542
# docs: http://www.python-ldap.org/doc/html/index.html
4643
import ldap # type: ignore[import-untyped]
@@ -304,20 +301,21 @@ def customer_id(self) -> None | str:
304301
def connect_server(self, server: str) -> tuple[ldap.ldapobject.ReconnectLDAPObject, str | None]:
305302
"""Connects to an LDAP server using the provided server uri"""
306303
try:
307-
# Set up debug logging if enabled
308-
if self._logger.isEnabledFor(logging.DEBUG):
309-
os.environ["GNUTLS_DEBUG_LEVEL"] = "99"
310-
ldap.set_option(ldap.OPT_DEBUG_LEVEL, 4095)
311-
trace_level = 2
312-
trace_file: IO[str] | None = sys.stderr
313-
else:
314-
trace_level = 0
315-
trace_file = None
304+
# We don't want this debugging possibly enabled
305+
# in production as it leaks sensitive information.
306+
# if self._logger.isEnabledFor(logging.DEBUG):
307+
# os.environ["GNUTLS_DEBUG_LEVEL"] = "99"
308+
# ldap.set_option(ldap.OPT_DEBUG_LEVEL, 4095)
309+
# trace_level = 2
310+
# trace_file: IO[str] | None = sys.stderr
311+
# else:
312+
# trace_level = 0
313+
# trace_file = None
316314

317315
# Format the LDAP URI and create the connection object
318316
uri = self._format_ldap_uri(server)
319317
conn = ldap.ldapobject.ReconnectLDAPObject(
320-
uri, trace_level=trace_level, trace_file=trace_file
318+
uri, # trace_level=trace_level, trace_file=trace_file
321319
)
322320
conn.protocol_version = self._config.get("version", 3)
323321
conn.network_timeout = self._config.get("connect_timeout", 2.0)

tests/unit/cmk/gui/userdb/test_ldap_golden.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def test_connect(mock_ldap: MagicMock) -> None:
9797
assert connector._ldap_obj_config == cfg, "Connector sets config for mock"
9898

9999
assert len(mock_ldap.call_args_list) == 2, "Connected to main and failover server"
100-
mock_ldap.assert_called_with("ldap://internet", trace_level=0, trace_file=None) # most recent
100+
mock_ldap.assert_called_with("ldap://internet") # most recent
101101

102102
# assumes "ldap_golden_unknown_password" is not in the password store, hence the 'None'.
103103
connector._ldap_obj.simple_bind_s.assert_called_with(cfg["bind"][0], None)
@@ -276,4 +276,4 @@ def test_remove_trailing_dot_from_hostname(mock_ldap: MagicMock) -> None:
276276
connector = LDAPUserConnector(cfg)
277277
connector.connect()
278278

279-
mock_ldap.assert_called_with("ldap://lolcathorst", trace_level=0, trace_file=None)
279+
mock_ldap.assert_called_with("ldap://lolcathorst")

0 commit comments

Comments
 (0)