133 lines
4.9 KiB
Diff
133 lines
4.9 KiB
Diff
autofs-5.1.8 - always recreate credential cache
|
|
|
|
From: Ian Collier <imc@cs.ox.ac.uk>
|
|
|
|
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 <imc@cs.ox.ac.uk>
|
|
---
|
|
CHANGELOG | 1
|
|
modules/cyrus-sasl.c | 53 +++++++++++++++++++++++++++++++++++++++------------
|
|
2 files changed, 42 insertions(+), 12 deletions(-)
|
|
|
|
--- autofs-5.1.4.orig/CHANGELOG
|
|
+++ autofs-5.1.4/CHANGELOG
|
|
@@ -163,6 +163,7 @@
|
|
- add flags argument to amd do_program_mount().
|
|
- fix deadlock in master_notify_submount().
|
|
- fix ldap sasl reconnect problem.
|
|
+- always recreate credential cache.
|
|
|
|
xx/xx/2018 autofs-5.1.5
|
|
- fix flag file permission.
|
|
--- autofs-5.1.4.orig/modules/cyrus-sasl.c
|
|
+++ autofs-5.1.4/modules/cyrus-sasl.c
|
|
@@ -509,6 +509,46 @@ sasl_do_kinit(unsigned logopt, struct lo
|
|
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*/,
|
|
@@ -521,18 +561,7 @@ sasl_do_kinit(unsigned logopt, struct lo
|
|
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,
|