is krb5_cc_initialize() thread safe

Greg Hudson ghudson at mit.edu
Thu Feb 20 18:25:15 EST 2025


On 2/20/25 16:47, Olga Kornievskaia wrote:
> I haven't been able to find in documentation whether or not
> krb5_cc_initialize() is thread safe? From a practical point of view, I
> believe it is not. But a call to "krb5_is_thread_safe() returns true".

krb5_is_thread_safe() just reports whether the library was compiled with 
mutex calls.

krb5_cc_initialize() is thread-safe to the extent that it shouldn't 
exhibit memory errors if called in multiple threads with the same 
krb5_ccache (and different krb5_contexts, as krb5_context is not a 
thread-protected object).  It is inherently not very concurrency-safe, 
whether the concurrency is due to threads or processes, because at the 
end of the call the cache is empty (no TGT) and therefore not usable to 
obtain service tickets.  (Well, "inherently" according to the 
traditional semantics.  Heimdal has adopted a behavior change to 
krb5_cc_initialize() where initializing a cache handle isn't externally 
visible until the first non-config credential is stored.  We didn't go 
that route.)

We did add a way in 1.20 to atomically replace a credentials cache of 
most types: krb5_cc_move() a MEMORY cache onto the target.  This method 
is used automatically by gss_store_cred()/gss_store_cred_into(), and by 
krb5_get_init_creds_*() when an output cache is specified using 
krb5_get_init_creds_opt_set_out_ccache().  The commit that added this 
behavior is:

https://github.com/krb5/krb5/commit/371f09d4bf4ca0c7ba15c5ef909bc35307ed9cc3

> In NFS's gssd implementation, while trying to refresh credentials, the
> code tries to store creds and as part of calls into
> krb5_cc_initialize() to initial credential cache (of either FILE or
> MEMORY type). We've observed that if two threads try to update the
> same cache and thus call into krb5_cc_initialize(), one succeeds and
> the other fails with "Internal credentials cache error". Is that
> unexpected?

In this scenario the two threads a probably using separate krb5_ccache 
handles to perform the initialize and store operations, so libkrb5 
locking the handles doesn't prevent the operations from trying to do 
their work at the same time--the same as if two different processes 
running two different programs were trying to initialize and store to 
FILE caches with the same filename.  I can see one order of 
sub-operations which would generate that error message, right off the 
bat: we start by unlink()ing the file and then opening it with O_EXCL. 
If these operations are interleaved (T1 unlink, T2 unlink, T1 open, T2 
open), the second open() will return an EEXIST error, which is 
translated to KRB5_FCC_INTERNAL.


More information about the krbdev mailing list