Regarding Issues with Memory Credential Cache
Datar, Ashutosh Anil
ashutosh.datar at hp.com
Tue Aug 19 07:32:11 EDT 2008
I was testing Apache Web Server (which uses mod_auth_kerb) with Kerberos Client 1.6.2 and found some issue with the Memory Cache handling.
According to my understanding, memory cache is arranged as a linked list, each node of which represents a cache and provides a pointer to another linked list (list of credentials), each node representing a credential from that particular cache.
(In source code, these nodes are named as Krb5_mcc_list_node and Krb5_mcc_cursor respectively)
Now, as an already filed ticket (#5895: http://krbdev.mit.edu/rt/Ticket/Display.html?user=guest&pass=guest&id=5895) indicates, we need to place locks in krb5_mcc_initialize () and krb5_mcc_destroy () so that krb5_mcc_free doesn't get called unprotected, causing an application to receive SIGSEGV.
But, as mentioned in the ticket itself, this alone will not ensure the safe access, as a thread can still free a Krb5_mcc_list_node when another is still accessing it. And thus it requires some kind of reference count mechanism which will ensure freeing up Krb5_mcc_list_node happens only when refcount is zero (No one else accessing it). This is already implemented in File Cache handling.
While testing, I could still observe application receiving SIGSEGV at few other places and after analyzing it, following are my observations:
1. Implemented reference count mechanism only ensures the safe access to Krb5_mcc_list_nodes, but when two threads are using same cache (e.g. two threads of same application) then they are actually simultaneously accessing Krb5_mcc_cursors, traversal of which is not completely protected by locks.
2. Though, krb5_mcc_remove_cred () is not supported, a thread can still call krb5_mcc_initialize () while another thread is still using those credentials in krb5_mcc_next_cred (), which doesn't operate under any kind of lock.
3. krb5_mcc_get_principal () also tries to access a Krb5_mcc_list_node data without acquiring any lock on it.
As for point number 2 above, following are possible approaches that I see as solution:
1. When a thread enters krb5_mcc_start_seq_get () to start the traversal of Krb5_mcc_cursor's list, it acquires a lock, which is relinquished in krb5_mcc_end_seq_get () so as to ensure that krb5_mcc_intialize () doesn't free the list when it's being traversed. But, this approach requires locking/unlocking across different functions and I am not very sure if it's the right way or not.
2. To make sure that krb5_mcc_initialize () is called only once for a particular Krb5_mcc_list_node. But there can be situations where an application explicitly wants to refresh(flush) the Cache and here we are restricting it to do so.
Can someone let me know which one is the better option, or if something else can be implemented which will remove both of the mentioned problems?
Also, about the 3rd point in the observations' list, it's required that there is a lock acquired in krb5_mcc_get_principal () before accessing a node so as to avoid accessing an already freed location.
Thanks and Regards,
More information about the krbdev