-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);