Subject: | Credential cache API does not support atomic reinitialization |
If a group of processes are using a credential cache for client
operations which require service tickets, and another process
reinitializes the cache, there is a brief window of time where the
former group of processes will fail because the cache is empty.
This failure window occurs because the ccache API does not provide any
way to initialize a cache and place credentials in it atomically.
There is a workaround for FILE ccaches on Unix-like systems where you
create a ccache in the same directory and rename it into place, but it
violates the ccache abstraction barrier.
Here are several candidate solutions along with reasons why some of them
might not work:
1. Provide a krb5_cc_init_with_cred() which is defined to atomically
reinitialize the ccache and place one specified credential in it,
presumably a TGT. This would be easy for a caller to use, but would
still leave open a window where the TGT is present but config entries
(such as "FAST is available") are not. So it's not a complete solution.
2. Define krb5_cc_lock() and krb5_cc_unlock() to lock the ccache across
processes (currently it just locks a mutex in the handle for most
types), and provide an API to initialize a ccache with the lock held.
This might be difficult to implement for some ccache types, such as
KEYRING.
3. Provide a way for krb5_cc_new_unique() to make a ccache within the
same atomic-replacement domain as an existing ccache, perhaps via the
currently-unused hint parameter, and then make krb5_cc_move() try to use
atomic replacement when possible.
4. Define krb5_cc_move() to be atomic in all cases (or perhaps only when
the source ccache type is MEMORY, although it shouldn't really matter),
whether the implementation uses locks or atomic replacement.
There are some related credential cache concurrency issues, such as the
behavior of iteration and the IAKERB state machine across cache
reinitialization. Those are separate issues; this ticket is only about
supporting atomic reinitialization in the API.
operations which require service tickets, and another process
reinitializes the cache, there is a brief window of time where the
former group of processes will fail because the cache is empty.
This failure window occurs because the ccache API does not provide any
way to initialize a cache and place credentials in it atomically.
There is a workaround for FILE ccaches on Unix-like systems where you
create a ccache in the same directory and rename it into place, but it
violates the ccache abstraction barrier.
Here are several candidate solutions along with reasons why some of them
might not work:
1. Provide a krb5_cc_init_with_cred() which is defined to atomically
reinitialize the ccache and place one specified credential in it,
presumably a TGT. This would be easy for a caller to use, but would
still leave open a window where the TGT is present but config entries
(such as "FAST is available") are not. So it's not a complete solution.
2. Define krb5_cc_lock() and krb5_cc_unlock() to lock the ccache across
processes (currently it just locks a mutex in the handle for most
types), and provide an API to initialize a ccache with the lock held.
This might be difficult to implement for some ccache types, such as
KEYRING.
3. Provide a way for krb5_cc_new_unique() to make a ccache within the
same atomic-replacement domain as an existing ccache, perhaps via the
currently-unused hint parameter, and then make krb5_cc_move() try to use
atomic replacement when possible.
4. Define krb5_cc_move() to be atomic in all cases (or perhaps only when
the source ccache type is MEMORY, although it shouldn't really matter),
whether the implementation uses locks or atomic replacement.
There are some related credential cache concurrency issues, such as the
behavior of iteration and the IAKERB state machine across cache
reinitialization. Those are separate issues; this ticket is only about
supporting atomic reinitialization in the API.