Fedora Account System
Red Hat Associate
Red Hat Customer
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
Tracker filed for rhel-10.3: https://issues.redhat.com/browse/RHEL-189007