--- /dev/null
+diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c
+index ccadb07..c1339b6 100644
+--- a/utils/gssd/gssd.c
++++ b/utils/gssd/gssd.c
+@@ -60,6 +60,7 @@ char keytabfile[PATH_MAX] = GSSD_DEFAULT_KEYTAB_FILE;
+ char ccachedir[PATH_MAX] = GSSD_DEFAULT_CRED_DIR;
+ char *ccachesearch[GSSD_MAX_CCACHE_SEARCH + 1];
+ int use_memcache = 0;
++int use_kcmcache = 0;
+ int root_uses_machine_creds = 1;
+ unsigned int context_timeout = 0;
+ char *preferred_realm = NULL;
+@@ -85,7 +86,7 @@ sig_hup(int signal)
+ static void
+ usage(char *progname)
+ {
+- fprintf(stderr, "usage: %s [-f] [-M] [-n] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir] [-t timeout] [-R preferred realm]\n",
++ fprintf(stderr, "usage: %s [-f] [-K] [-M] [-n] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir] [-t timeout] [-R preferred realm]\n",
+ progname);
+ exit(1);
+ }
+@@ -102,7 +103,7 @@ main(int argc, char *argv[])
+ char *progname;
+
+ memset(ccachesearch, 0, sizeof(ccachesearch));
+- while ((opt = getopt(argc, argv, "fvrmnMp:k:d:t:R:")) != -1) {
++ while ((opt = getopt(argc, argv, "fvrmnMKp:k:d:t:R:")) != -1) {
+ switch (opt) {
+ case 'f':
+ fg = 1;
+@@ -113,6 +114,9 @@ main(int argc, char *argv[])
+ case 'M':
+ use_memcache = 1;
+ break;
++ case 'K':
++ use_kcmcache = 1;
++ break;
+ case 'n':
+ root_uses_machine_creds = 0;
+ break;
+diff --git a/utils/gssd/gssd.h b/utils/gssd/gssd.h
+index b1b5793..82c4406 100644
+--- a/utils/gssd/gssd.h
++++ b/utils/gssd/gssd.h
+@@ -63,6 +63,7 @@ extern char pipefs_dir[PATH_MAX];
+ extern char keytabfile[PATH_MAX];
+ extern char *ccachesearch[];
+ extern int use_memcache;
++extern int use_kcmcache;
+ extern int root_uses_machine_creds;
+ extern unsigned int context_timeout;
+ extern char *preferred_realm;
+diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
+index f071600..503cac2 100644
+--- a/utils/gssd/krb5_util.c
++++ b/utils/gssd/krb5_util.c
+@@ -125,6 +125,7 @@
+ #include "err_util.h"
+ #include "gss_util.h"
+ #include "krb5_util.h"
++#include "xcommon.h"
+
+ /* Global list of principals/cache file names for machine credentials */
+ struct gssd_k5_kt_princ *gssd_k5_kt_princ_list = NULL;
+@@ -299,6 +300,115 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d)
+ return err;
+ }
+
++#ifdef HAVE_HEIMDAL
++static int
++gssd_find_existing_krb5_ccache_kcm(uid_t uid, char **cc)
++{
++ krb5_context context;
++ krb5_cc_cache_cursor cursor;
++ krb5_ccache id;
++ char *best_match_name = NULL;
++ krb5_timestamp best_match_mtime, mtime;
++ char *ccname;
++ uid_t ccuid;
++ int i;
++ int found = 0;
++ char buf[1030];
++ char *princname = NULL;
++ char *realm = NULL;
++ int score, best_match_score = 0, err = -EACCES;
++
++ *cc = NULL;
++ if (krb5_init_context(&context))
++ return err;
++
++ if (krb5_cc_cache_get_first(context, "KCM", &cursor))
++ return err;
++
++ while (krb5_cc_cache_next(context, cursor, &id) == 0) {
++ ccname = xstrdup(krb5_cc_get_name(context, id));
++ if (ccname == NULL) {
++ printerr(0, "Error getting CC name\n");
++ continue;
++ }
++ for (i=0,ccuid=0; ccname[i] && isdigit(ccname[i]); i++) {
++ ccuid = ccuid*10 + (ccname[i] - '0');
++ }
++ if (i == 0) {
++ printerr(3, "CC '%s' not available due to"
++ " non-standard name\n",
++ ccname);
++ continue;
++ }
++ /* Only pick caches owned by the user (uid) */
++ if (ccuid != uid) {
++ printerr(3, "CC '%s' owned by %u, not %u\n",
++ ccname, ccuid, uid);
++ continue;
++ }
++ snprintf(buf, sizeof(buf), "KCM:%s", ccname);
++ if (!query_krb5_ccache(buf, &princname, &realm)) {
++ printerr(3, "CC '%s' is expired or corrupt\n",
++ ccname);
++ err = -EKEYEXPIRED;
++ continue;
++ }
++ krb5_cc_last_change_time(context, id, &mtime);
++
++ score = 0;
++ if (preferred_realm && strcmp(realm, preferred_realm) == 0)
++ score++;
++
++ printerr(3, "CC '%s'(%s@%s) passed all checks and"
++ " has mtime of %u\n",
++ ccname, princname, realm, mtime);
++ /*
++ * if more than one match is found, return the most
++ * recent (the one with the latest mtime), and
++ * don't free the dirent
++ */
++ if (!found) {
++ best_match_name = ccname;
++ best_match_mtime = mtime;
++ best_match_score = score;
++ found++;
++ } else {
++ /*
++ * If current score is higher than best match
++ * score, we use the current match. Otherwise,
++ * if the current match has an mtime later
++ * than the one we are looking at, then use
++ * the current match. Otherwise, we still
++ * have the best match.
++ */
++ if (best_match_score < score ||
++ (best_match_score == score &&
++ mtime > best_match_mtime)) {
++ free(best_match_name);
++ best_match_name = ccname;
++ best_match_mtime = mtime;
++ best_match_score = score;
++ } else {
++ free(ccname);
++ }
++ printerr(3, "CC '%s' is our current best match "
++ "with mtime of %u\n",
++ best_match_name, best_match_mtime);
++ }
++ free(princname);
++ free(realm);
++ }
++ krb5_cc_cache_end_seq_get(context, cursor);
++ krb5_free_context(context);
++ if (found) {
++ *cc = best_match_name;
++ return 0;
++ }
++
++ return err;
++}
++#endif
++
+ /*
+ * Obtain credentials via a key in the keytab given
+ * a keytab handle and a gssd_k5_kt_princ structure.
+@@ -1002,12 +1112,26 @@ gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername, char *dirname)
+ printerr(2, "getting credentials for client with uid %u for "
+ "server %s\n", uid, servername);
+ memset(buf, 0, sizeof(buf));
+- err = gssd_find_existing_krb5_ccache(uid, dirname, &d);
+- if (err)
+- return err;
++#ifdef HAVE_HEIMDAL
++ if (use_kcmcache) {
++ char *s;
++ err = gssd_find_existing_krb5_ccache_kcm(uid, &s);
++ if (err)
++ return err;
++
++ snprintf(buf, sizeof(buf), "KCM:%s", s);
++ free(s);
++ } else {
++#endif
++ err = gssd_find_existing_krb5_ccache(uid, dirname, &d);
++ if (err)
++ return err;
+
+- snprintf(buf, sizeof(buf), "FILE:%s/%s", dirname, d->d_name);
+- free(d);
++ snprintf(buf, sizeof(buf), "FILE:%s/%s", dirname, d->d_name);
++ free(d);
++#ifdef HAVE_HEIMDAL
++ }
++#endif
+
+ printerr(2, "using %s as credentials cache for client with "
+ "uid %u for server %s\n", buf, uid, servername);