autofs-5.1.8 - always recreate credential cache From: Ian Collier In recent Kerberos revisions when a TGT expires autofs will fail to renew the ticket. Expired creds are being pulled out of the cache and in that case the patched version clears the cache to remove the expired creds. If the cache is already in use, try to pull out a cred and then if that was successful and the cred is expired, clear the cache. So this fixes the behaviour I was seeing, since that was happening because expired creds were being pulled out of the cache and in that case the patched version clears the cache to remove the expired creds. What sort of race conditions might happen here? - If the function is called very late during the validity of a ticket, it might expire after the decision not to clear the cache. In that case, the behaviour is the same as the unpatched version, but this is highly unlikely because do_kinit is not supposed to happen while there is a valid ticket. - If two or more threads decide to call do_kinit at about the same time: it's protected by a mutex, so one of the calls will happen first; this call will clear the cache and add a new ticket. When the others kick in, the cache won't be cleared because it's only cleared if we can find an expired ticket in the cache and any such ticket was removed when the first do_kinit happened. - If one thread does do_kinit while another thread is trying to do a lookup: if the current ticket is expired then the lookup would have failed anyway; if it's not expired then we won't clear the cache. - If there is both an expired and a valid ticket in the cache: this only happens if two or more do_kinits clashed and stored tickets with different expiration times, and if the current time is between those times. The current bug happens because krb5 cache retrieval is returning the earliest (i.e. expired) ticket. When that's the case then do_kinit will clear the cache because when it tests the cache it will pull the expired cred - and it needs to do this because otherwise all lookups are failing (that's the bug). In a case where krb5 cache retrieval returns the valid ticket, it doesn't matter that the cache is not cleared because any subsequent lookups will use that valid ticket. Signed-off-by: Ian Collier --- CHANGELOG | 1 + modules/cyrus-sasl.c | 53 +++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 4e2d9139..09e1ea43 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -98,6 +98,7 @@ - allow -null map in indirect maps. - fix multi-mount check. - fix let OpenLDAP handle SASL binding. +- always recreate credential cache. 19/10/2021 autofs-5.1.8 - add xdr_exports(). diff --git a/modules/cyrus-sasl.c b/modules/cyrus-sasl.c index e765118e..e742eaf8 100644 --- a/modules/cyrus-sasl.c +++ b/modules/cyrus-sasl.c @@ -661,6 +661,46 @@ sasl_do_kinit(unsigned logopt, struct lookup_context *ctxt) debug(logopt, "Using tgs name %s", tgs_name); memset(&my_creds, 0, sizeof(my_creds)); + + if (krb5cc_in_use++ == 0) { + /* tell the cache what the default principal is */ + ret = krb5_cc_initialize(ctxt->krb5ctxt, + ctxt->krb5_ccache, krb5_client_princ); + + if (ret) { + --krb5cc_in_use; + error(logopt, + "krb5_cc_initialize failed with error %d", ret); + goto out_cleanup_unparse; + } + } + else { + krb5_creds match_creds, out_creds; + time_t now = monotonic_time(NULL); + + /* even if the cache is in use, we will clear it if it + * contains an expired credential for our principal, + * because Kerberos doesn't always work well with caches + * that contain both expired and valid credentials + */ + memset(&match_creds, 0, sizeof match_creds); + match_creds.client = krb5_client_princ; + match_creds.server = tgs_princ; + ret = krb5_cc_retrieve_cred(ctxt->krb5ctxt, ctxt->krb5_ccache, + 0, &match_creds, &out_creds); + if (ret == 0 && (time_t) out_creds.times.endtime < now) { + debug(logopt, + "calling krb5_cc_initialize to clear expired tickets"); + ret = krb5_cc_initialize(ctxt->krb5ctxt, + ctxt->krb5_ccache, krb5_client_princ); + if (ret) + warn(logopt, + "krb5_cc_initialize failed with error %d " + "while trying to clear existing cache", + ret); + } + } + ret = krb5_get_init_creds_keytab(ctxt->krb5ctxt, &my_creds, krb5_client_princ, NULL /*keytab*/, @@ -673,18 +713,7 @@ sasl_do_kinit(unsigned logopt, struct lookup_context *ctxt) goto out_cleanup_unparse; } - if (krb5cc_in_use++ == 0) - /* tell the cache what the default principal is */ - ret = krb5_cc_initialize(ctxt->krb5ctxt, - ctxt->krb5_ccache, krb5_client_princ); - - if (ret) { - error(logopt, - "krb5_cc_initialize failed with error %d", ret); - goto out_cleanup_creds; - } - - /* and store credentials for that principal */ + /* and store credentials for our principal */ ret = krb5_cc_store_cred(ctxt->krb5ctxt, ctxt->krb5_ccache, &my_creds); if (ret) { error(logopt,