Skip Menu |
 

Subject: krb5_cc_set_config() fails to overwrite existing entries and creates duplicate credential objects
From: "Steffen Kieß" <steffen.kiess@cis.iti.uni-stuttgart.de>
To: krb5-bugs@mit.edu
Date: Wed, 25 Jun 2025 16:58:42 +0200
Download (untitled) / with headers
text/plain 2.6KiB
krb5_cc_set_config() fails to overwrite existing entries and creates
duplicate credential objects, at least for FILE:, MEMORY: and KCM: (with
Heimdal kcm) CCs. This can cause the CC to be filled hundreds of
"refresh_time" entries (which can be seen e.g. with "klist -AC".)

This is probably related to
<https://github.com/krb5/krb5/commit/c0a51fe0c8051e27c6cee4f4f0c705356a715e1e>.

The following code demonstrates the problem:


#include <stdio.h>
#include <string.h>

#include <krb5.h>

int main() {
krb5_context context;
if (krb5_init_context(&context)) abort();

const char* cname = "FILE:/tmp/test-cache";
// const char* cname = "KCM:1000:12345";
// const char* cname = "MEMORY:";
// const char* cname = "KEYRING:";

// Create credential cache
krb5_ccache cache;
if (krb5_cc_resolve(context, cname, &cache)) abort();
krb5_principal princ;
if (krb5_build_principal(context, &princ, 5, "REALM", "user", NULL))
abort();
if (krb5_cc_initialize(context, cache, princ)) abort();

// Set config "test" to "foo"
krb5_data data;
data.data = "foo";
data.length = 3;
if (krb5_cc_set_config(context, cache, NULL, "test", &data)) abort();

// Read config "test"
if (krb5_cc_get_config(context, cache, NULL, "test", &data)) abort();
{
char* data2 = malloc(data.length + 1);
if (!data2) abort();
memcpy(data2, data.data, data.length);
data2[data.length] = 0;
printf("krb5_cc_get_config: '%s'\n", data2);
}

// Set config "test" to "foo2"
data.data = "foo2";
data.length = 4;
if (krb5_cc_set_config(context, cache, NULL, "test", &data)) abort();

// Read config "test"
if (krb5_cc_get_config(context, cache, NULL, "test", &data)) abort();
{
char* data2 = malloc(data.length + 1);
if (!data2) abort();
memcpy(data2, data.data, data.length);
data2[data.length] = 0;
printf("krb5_cc_get_config: '%s'\n", data2);
}

// Print entries in cache
krb5_cc_cursor cursor;
if (krb5_cc_start_seq_get(context, cache, &cursor)) abort();
for (;;) {
krb5_creds creds;
if (krb5_cc_next_cred(context, cache, &cursor, &creds)) break;
char* data2 = malloc(creds.ticket.length + 1);
if (!data2) abort();
memcpy(data2, creds.ticket.data, creds.ticket.length);
data2[creds.ticket.length] = 0;
printf("Got: '%s'\n", data2);
}
}


The output on my system is:

krb5_cc_get_config: 'foo'
krb5_cc_get_config: 'foo'
Got: 'foo'
Got: 'foo2'

It should be:

krb5_cc_get_config: 'foo'
krb5_cc_get_config: 'foo2'
Got: 'foo2'

This affects the current git master (and probably krb5 versions going
back more than a decade).
The referenced commit  c0a51fe0c8051e27c6cee4f4f0c705356a715e1e uses as justification that krb5_cc_store_cred() will remove duplicates, but a few years later commit 7783054742ddd807f7b2f7157d6ed81b7fb614eb removed that behavior.

I think commit 729896467e3c77904666019d6cbbda583ae49b95 also may have resulted in poor ccache behavior around credential expiration time, setting a new refresh time on every gss_acquire_cred() call within 30 seconds of cred expiration, even if one is already set.  (If refreshing succeeds the ccache is wiped, so the problem only manifests when we can't refresh the cache.  But it looks like we call kg_cred_time_to_refresh() via maybe_get_initial_cred() on every krb5 gss_acquire_cred() initiator call, even if no keytab is present.)  Even if the krb5_cc_store_cred() calls were semantically correct we would create large FILE ccaches full of deleted config entries.

There's a reasonable argument that refresh_time isn't a good match for krb5_cc_set_config(), given the append-only nature of the most common ccache type.  Unfortunately, I don't think there is an alternative mechanism to prevent AS-REQ storms when we try to refresh a ccache from the keytab and fail.
 
Subject: git commit
From: ghudson@mit.edu

Fix ccache config entry updating

krb5_cc_set_config() originally tried to allow overwriting of existing
config keys, but after commit c0a51fe0c8051e27c6cee4f4f0c705356a715e1e
and 7783054742ddd807f7b2f7157d6ed81b7fb614eb it no longer calls
remove_cred. Add a call back in, as we rely on config entry updating
for refresh_time in the GSS library.

https://github.com/krb5/krb5/commit/fe04743b2b5cae3ed45fb1212a1bed484203d3be
Author: Greg Hudson <ghudson@mit.edu>
Commit: fe04743b2b5cae3ed45fb1212a1bed484203d3be
Branch: master
src/lib/krb5/ccache/ccfns.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)