Bug 2478864 (CVE-2026-104041) - CVE-2026-104041 sssd: sssd: Denial of Service via unbounded negative cache growth
Summary: CVE-2026-104041 sssd: sssd: Denial of Service via unbounded negative cache gr...
Keywords:
Status: NEW
Alias: CVE-2026-104041
Product: Security Response
Classification: Other
Component: vulnerability
Version: unspecified
Hardware: All
OS: Linux
medium
medium
Target Milestone: ---
Assignee: Product Security DevOps Team
QA Contact:
URL:
Whiteboard:
Depends On: 2546256
Blocks:
TreeView+ depends on / blocked
 
Reported: 2026-05-18 03:47 UTC by OSIDB Bzimport
Modified: 2026-10-06 00:48 UTC (History)
18 users (show)

Fixed In Version:
Clone Of:
Environment:
Last Closed:
Embargoed:


Attachments (Terms of Use)

Description OSIDB Bzimport 2026-05-18 03:47:54 UTC
AI_ONLY_REPORT
package: sssd-2.12.0-1.el10
------
Summary: Unbounded NSS Negative Cache Growth Can Cause Local DoS (Memory  
Exhaustion): an unprivileged local user can trigger large numbers of  
distinct negative NSS lookups and drive application-level unbounded growth  
of the in-memory negative cache, potentially exhausting responder memory  
and disrupting local name-service availability.
Requirements to exploit: Unprivileged local access on a system where the  
`sssd` NSS responder is active, the responder socket is reachable by local  
users, and `entry_negative_timeout` is non-zero (default `15`). The  
attacker must be able to trigger many distinct NSS misses through normal  
lookup paths. The available evidence does not establish remote  
exploitability by default.
Component affected: `sssd-2.12.0-1.el10` NSS responder negative cache in  
`src/responder/common/negcache.c` (`sss_ncache_init()`,  
`sss_ncache_check_str()`, `sss_ncache_set_str()`), with local reachability  
through `src/responder/nss/nsssrv.c`
Version affected: `sssd-2.12.0-1.el10` when the NSS responder is active and  
`entry_negative_timeout` is non-zero (default `15`)
Patch available: no released package fix established; proposed patch  
included below
Version fixed: unknown
Upstream coordination: Not notified.
CVSS: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H - 5.5 (MEDIUM)
AV:L - The established attack path requires local access to issue NSS  
lookups on the host.
AC:L - Repeated distinct nonexistent lookups are sufficient; no race or  
special conditions are required.
PR:L - The demonstrated exploit requires an unprivileged local execution  
context or local account on the system.
UI:N - No victim interaction is needed.
S:U - The impact remains within the vulnerable component's security  
scope.
C:N - No confidentiality impact is established by the available evidence.
I:N - No integrity impact is established by the available evidence.
A:H - Responder memory can grow until service becomes unstable or the  
process is OOM-killed.
Impact: Moderate. The established outcome is a practical local denial of  
service against the NSS responder, not remote compromise, privilege  
escalation, or data exposure. Under Red Hat's severity guidance, that is  
more than Low because it can materially disrupt host functionality in  
active NSS deployments, but below Important because the supported attack is  
local-only and availability-focused.
Embargo: no
Reason: The available evidence supports a local denial of service with  
operational mitigations and no evidence of remote compromise,  
confidentiality loss, or privilege escalation. This class of issue can  
usually be fixed and disclosed without a prolonged embargo.
Acknowledgement: Aisle Research
Vulnerability Details:
In `src/responder/common/negcache.c`, the negative cache is created as an  
in-memory TDB and expired entries are only removed lazily when the same key  
is checked again:
```c
ctx->tdb = tdb_open("memcache", 0, TDB_INTERNAL, O_RDWR|O_CREAT, 0);
...
if (expired) {
/* expired, remove and return no entry */
tdb_delete(ctx->tdb, key);
ret = ENOENT;
}
```
New negative entries are inserted through `tdb_store()` with no  
application-level global size or entry bound shown in this path:
```c
ret = tdb_store(ctx->tdb, key, data, TDB_REPLACE);
```
Non-permanent negative caching is enabled by default. In  
`src/responder/common/responder_common.c`, the responder reads  
`entry_negative_timeout` with a default of `15`:
```c
ret = confdb_get_int(cdb, CONFDB_NSS_CONF_ENTRY,
CONFDB_NSS_ENTRY_NEG_TIMEOUT,
15, &tmp_value);
```
Local reachability is realistic when the NSS responder is enabled, because  
the responder defines a public socket umask:
```c
/* Public sockets must be readable and writable by anybody on the system.
So we set umask to 0111. */
#define SCKT_RSP_UMASK 0111
```


Based on the available code and reproduction, many distinct nonexistent  
lookups can accumulate in memory even though each entry has a timeout,  
because unique keys are not revisited and therefore do not trigger lazy  
expiry removal. The supported attack path is through successful "not found"  
handling in the normal cache-request flow, not through malformed packet  
delivery. There is an operational clear path for the negative cache, but  
that is a cleanup mechanism rather than a preventive limit.
Steps to reproduce:
1. Ensure the system is using the `sssd` NSS responder and that  
`entry_negative_timeout` is non-zero.
2. Monitor responder memory usage, for example:
```bash
watch -n1 'ps -o pid,rss,cmd -C sssd_nss'
```
3. As an unprivileged local user, generate a large number of distinct  
negative NSS lookups:
```bash
for i in $(seq 1 500000); do getent passwd no_such_user_$i >/dev/null;  
done
```
4. Observe steady RSS growth in `sssd_nss` during and after the run.
5. As a control, clear the negative cache through the existing operational  
path or send SIGHUP to the monitor; this should remove the accumulated  
negative-cache state and typically relieve the observed memory pressure.
Mitigation:
Set `entry_negative_timeout = 0` to disable non-permanent negative-cache  
insertions. This reduces exposure but increases backend lookup traffic.

Use the existing negative-cache clear path or restart the responder if  
memory growth is observed.

Restrict untrusted local users on systems where the NSS responder is  
exposed and this behavior would be materially disruptive.


Proposed Fix:
A minimal hardening approach is to opportunistically prune expired entries  
before insertion and reject new inserts once a configured maximum entry  
count is reached.
```diff
diff --git a/src/responder/common/negcache.c  
b/src/responder/common/negcache.c
...
— a/src/responder/common/negcache.c
+++ b/src/responder/common/negcache.c
@@ -41,10 +41,15 @@
#define NC_DOMAIN_ACCT_LOCATE_PREFIX NC_ENTRY_PREFIX"DOM_LOCATE"
#define NC_DOMAIN_ACCT_LOCATE_TYPE_PREFIX NC_ENTRY_PREFIX"DOM_LOCATE_TYPE"
+#define NCACHE_DEFAULT_MAX_ENTRIES 20000
+
struct sss_nc_ctx {
struct tdb_context *tdb;
uint32_t timeout;
+    uint32_t max_entries;
};
+struct ncache_prune_state { time_t now; uint32_t alive; };
+
typedef int (*ncache_set_byname_fn_t)(struct sss_nc_ctx *, bool,
const char *, const char *);
@@ -79,6 +84,7 @@ int sss_ncache_init(TALLOC_CTX *memctx, uint32_t timeout,
if (!ctx->tdb) return errno;
ctx->timeout = timeout;
+    ctx->max_entries = NCACHE_DEFAULT_MAX_ENTRIES;
*_ctx = ctx;
return EOK;
@@ -146,6 +152,36 @@ done:
return ret;
}
+static int ncache_prune_expired(struct tdb_context *tdb,
+                                TDB_DATA key, TDB_DATA data, void *pvt)
+{
+    struct ncache_prune_state *st = talloc_get_type(pvt, struct  
ncache_prune_state);
+    unsigned long long ts;
+    char *ep = NULL;
+
+    errno = 0;
+    ts = strtoull((const char *)data.dptr, &ep, 10);
+    if (errno != 0 || *ep != '\0') {
+        return tdb_delete(tdb, key);
+    }
+
+    if (ts != 0 && ts < (unsigned long long)st->now) {
+        return tdb_delete(tdb, key);
+    }
+
+    st->alive++;
+    return 0;
+}
+
static int sss_ncache_set_str(struct sss_nc_ctx *ctx, char *str, bool  
permanent)
{
+    struct ncache_prune_state st = { 0 };
TDB_DATA key;
TDB_DATA data;
@@ -167,6 +203,18 @@ static int sss_ncache_set_str(struct sss_nc_ctx *ctx,  
char *str, bool permanent)
}
if (!timest) return ENOMEM;
+    st.now = time(NULL);
+    if (tdb_traverse(ctx->tdb, ncache_prune_expired, &st) < 0) {
+        return EIO;
+    }
+    if (st.alive >= ctx->max_entries) {
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              "Negative cache limit reached (%u), dropping [%s]\n",
+              ctx->max_entries, str);
+        talloc_free(timest);
+        return ENOSPC;
+    }
+
ret = string_to_tdb_data(timest, &data);
if (ret != EOK) goto done;
```
------
This report was generated using AI technology. Always review AI-generated  
content prior to use


Note You need to log in before you can comment on or make changes to this bug.