Bug 2569226 - CVE-2026-112322 krb5: MIT krb5 kadmind unauthenticated CPU DoS via AUTH_GSSAPI client states cleanup [fedora-all]
Summary: CVE-2026-112322 krb5: MIT krb5 kadmind unauthenticated CPU DoS via AUTH_GSSAP...
Keywords:
Status: NEW
Alias: None
Product: Fedora
Classification: Fedora
Component: krb5
Version: rawhide
Hardware: Unspecified
OS: Unspecified
high
high
Target Milestone: ---
Assignee: Julien Rische
QA Contact: Fedora Extras Quality Assurance
URL:
Whiteboard: {"flaws": ["d741d20d-a443-4170-8f5e-6...
Depends On:
Blocks: CVE-2026-112322
TreeView+ depends on / blocked
 
Reported: 2026-11-09 18:06 UTC by Samuele Negrini
Modified: 2026-11-09 18:06 UTC (History)
7 users (show)

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


Attachments (Terms of Use)

Description Samuele Negrini 2026-11-09 18:06:11 UTC
Disclaimer: Community trackers are created by Red Hat Product Security team on a best effort basis. Package maintainers are required to ascertain if the flaw indeed affects their package, before starting the update process.

AI_ONLY_REPORT
package: krb5-1.21.3-8.el10_0
------
Summary: Unauthenticated CPU DoS in AUTH_GSSAPI client cleanup  
(`svc_auth_gssapi.c`): quadratic cleanup of expired AUTH_GSSAPI client  
state can be triggered remotely by accumulating partial `AUTH_GSSAPI_INIT`  
entries and then forcing rescans of mixed live and expired records.
Requirements to exploit: Network reachability to a service shipped in this  
SRPM that accepts the legacy `AUTH_GSSAPI` server path, such as `kadmind`;  
no credentials or user interaction are required, but the attacker must  
sustain crafted `AUTH_GSSAPI_INIT` traffic across the `INITIATION_TIMEOUT`  
window to build up expiring client records.
Component affected: `krb5-1.21.3-8.el10_0`, `src/lib/rpc/svc_auth_gssapi.c`  
(`clean_client()`, `create_client()`, and pre-auth `AUTH_GSSAPI_INIT`  
handling)
Version affected: `krb5-1.21.3-8.el10_0` when a network-reachable service  
from this SRPM uses the legacy `AUTH_GSSAPI` server path, such as `kadmind`
Patch available: no released package fix established; proposed patch  
included below
Version fixed: unknown
Upstream coordination: Not notified.
CVSS: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - 7.5 (HIGH)
AV:N - The issue is triggered by network requests to a service such as  
`kadmind`.
AC:L - The attacker only needs repeated unauthenticated  
`AUTH_GSSAPI_INIT` requests over the normal timeout window; no race or  
special environment is required.
PR:N - No prior authentication is needed to create the partial client  
records involved in cleanup.
UI:N - The attack is fully programmatic and does not require user  
participation.
S:U - The impact is confined to the vulnerable service process.
C:N - No confidentiality impact is established by the available evidence.
I:N - No integrity impact is established by the available evidence.
A:H - Cleanup can be forced into excessive CPU work, causing prolonged  
loss of service availability.
Impact: Important. This matches Red Hat's Important rating because the  
available evidence supports an unauthenticated remote denial of service  
against a network-reachable service from this SRPM that uses the legacy  
`AUTH_GSSAPI` path. Critical is not appropriate because no confidentiality,  
integrity, or code-execution impact is shown.
Embargo: no
Reason: The issue is availability-only, and exploitation still depends  
on a reachable service exposing legacy `AUTH_GSSAPI` plus sustained request  
shaping over the initiation timeout window. Restricting access to `kadmind`  
or similar RPC services provides an operational mitigation while a fix is  
prepared.
Acknowledgement: Aisle Research
Vulnerability Details: `clean_client()` removes expired AUTH_GSSAPI client  
state, but after deleting one expired entry it restarts scanning from the  
head of the global client list:
```c
static void clean_client(void)
{
svc_auth_gssapi_data *client_data;
client_list *c;
c = clients;
while (c) {
client_data = c->client;
if (client_data->expiration < time(0)) {
destroy_client(client_data);
c = clients; /* start over, just to be safe */
} else {
c = c->next;
}
}
}
```
New client records are prepended to that list:
```c
if (clients == NULL)
clients = c;
else {
c->next = clients;
clients = c;
}
```
Unauthenticated `AUTH_GSSAPI_INIT` handling creates a client record before  
context establishment succeeds:
```c
/* clean up expired entries */
clean_client();
if (creds.auth_msg && rqst->rq_proc == AUTH_GSSAPI_INIT) {
client_data = create_client();
...
}
```
If `gss_accept_sec_context()` later fails, the partial record remains until  
timeout-based cleanup rather than being destroyed immediately. Because  
newer entries are added at the head and older entries expire at the tail,  
an attacker can shape a list with a live prefix and an expired suffix. Each  
expired entry then causes another scan across the live prefix, turning one  
cleanup pass into quadratic CPU work. In affected deployments, `kadmind`  
explicitly enables the older `AUTH_GSSAPI` server path via  
`svcauth_gssapi_set_names(...)`, so this is not just unreachable dead code  
for that service path.
Steps to reproduce:
1. Start `kadmind` (or another service using this `AUTH_GSSAPI` path) with  
network access to the attacker.
2. Send many RPC calls with `oa_flavor=AUTH_GSSAPI`, creds `version=2`,  
`auth_msg=true`, `rq_proc=AUTH_GSSAPI_INIT`, and empty `client_handle`. An  
invalid init token is sufficient if it fails after `create_client()`  
executes.
3. Continue sending INIT requests over roughly 15 minutes  
(`INITIATION_TIMEOUT`) so older partial entries expire while newer ones  
remain fresh.
4. Send one additional `AUTH_GSSAPI` request to trigger `clean_client()`.
5. Observe a CPU spike and elevated latency while cleanup repeatedly  
rescans the live prefix of the list.
Mitigation: Restrict network access to `kadmind` and any other service from  
this SRPM that accepts legacy `AUTH_GSSAPI` requests to trusted management  
hosts only. If possible, rate-limit or filter repeated unauthenticated  
`AUTH_GSSAPI_INIT` RPC traffic at the network boundary. If a deployment can  
avoid exposing the legacy `AUTH_GSSAPI` server path and rely on  
`RPCSEC_GSS` instead, that would reduce exposure, although the available  
material does not establish a universal supported switch for every packaged  
deployment.
Proposed Fix: This patch removes head-restart behavior and list rescans by  
unlinking expired entries in-place via a pointer-to-pointer walk, while  
preserving existing `destroy_client()` behavior for other callers.
```diff
diff --git a/src/lib/rpc/svc_auth_gssapi.c b/src/lib/rpc/svc_auth_gssapi.c
— a/src/lib/rpc/svc_auth_gssapi.c
+++ b/src/lib/rpc/svc_auth_gssapi.c
@@ -104,6 +104,7 @@ static svc_auth_gssapi_data *get_client
(gss_buffer_t client_handle);
static void destroy_client
(svc_auth_gssapi_data *client_data);
+static void free_client_data(svc_auth_gssapi_data *client_data);
static void clean_client(void), cleanup(void);
static void client_expire
(svc_auth_gssapi_data *client_data, uint32_t exp);
@@ -818,6 +819,29 @@ static svc_auth_gssapi_data *get_client(gss_buffer_t  
client_handle)
freed.
   */
  static void destroy_client(svc_auth_gssapi_data *client_data)
+{
+     client_list *c, *c2;
+
+     if (clients == NULL) {
+          PRINTF(("destroy_client: called on empty database\n"));
+          abort();
+     } else if (clients->client == client_data) {
+          c = clients;
+          clients = clients->next;
+          free(c);
+     } else {
+          c2 = clients;
+          c = clients->next;
+          while (c) {
+               if (c->client == client_data) {
+                    c2->next = c->next;
+                    free(c);
+                    goto unlinked;
+               }
+               c2 = c;
+               c = c->next;
+          }
+          PRINTF(("destroy_client: client_handle delete failed\n"));
+          abort();
+     }
+unlinked:
+     free_client_data(client_data);
+}
+
+static void free_client_data(svc_auth_gssapi_data *client_data)
  {
       OM_uint32 gssstat, minor_stat;
       gss_buffer_desc out_buf;

    client_list *c, *c2;
@@ -841,31 +865,6 @@ static void destroy_client(svc_auth_gssapi_data  
*client_data)
       if (client_data->prev_verf.length != 0)
            gss_release_buffer(&minor_stat, &client_data->prev_verf);


    if (clients == NULL) {

         PRINTF(("destroy_client: called on empty database\n"));

         abort();

    } else if (clients->client == client_data) {

         c = clients;

         clients = clients->next;

         free(c);

    } else {

         c2 = clients;

         c = clients->next;

         while (c) {

              if (c->client == client_data) {

                   c2->next = c->next;

                   free(c);

                   goto done;

              } else {

                   c2 = c;

                   c = c->next;

              }

         }

         PRINTF(("destroy_client: client_handle delete failed\n"));

         abort();

    }
-
-done:
-
       L_PRINTF(2, ("destroy_client: client %d destroyed\n",  
client_data->key));


free(client_data);
@@ -895,25 +894,27 @@ static void clean_client(void)
{
svc_auth_gssapi_data *client_data;
    client_list *c;
+     client_list **pp, *cur;
+     time_t now;


PRINTF(("clean_client: starting\n"));
    c = clients;

    while (c) {

         client_data = c->client;
+     now = time(0);
+     pp = &clients;
+     while ((cur = *pp) != NULL) {
+          client_data = cur->client;


L_PRINTF(2, ("clean_client: client_data = %p\n",
(void *) client_data));
         if (client_data->expiration < time(0)) {
+          if (client_data->expiration < now) {
                 PRINTF(("clean_client: client %d expired\n",
                         client_data->key));

              destroy_client(client_data);

              c = clients; /* start over, just to be safe */
+               *pp = cur->next;
+               free(cur);
+               free_client_data(client_data);
            } else {

              c = c->next;
+               pp = &cur->next;
            }
       }
-
       PRINTF(("clean_client: done\n"));
  }
```


------
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.