22937668 Init auth through keytab as root doesn't work after credentials have expired
authorWill Fiveash <will.fiveash@oracle.com>
Fri, 02 Sep 2016 16:39:26 -0500
changeset 6867 87f7fd05f888
parent 6866 4c1935f5ec9a
child 6868 f1ff679c879b
22937668 Init auth through keytab as root doesn't work after credentials have expired
components/krb5/patches/037-getuid-mod.patch
components/krb5/patches/037-root-defcred.patch
components/krb5/patches/073-root-init-cred-kt.patch
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/krb5/patches/037-getuid-mod.patch	Fri Sep 02 16:39:26 2016 -0500
@@ -0,0 +1,61 @@
+#
+# This patch provides support for programs like gssd to control how
+# expand_userid() determines the UID of the running process so those apps can
+# proxy for other users and thus influence the construction of the default
+# ccache path so the krb code accesses that user's ccache.  This patch in
+# conjuction with the patch that allows root to auto-acquire an init cred via
+# the system keytab also allows a process to influence that fall-back behavior.
+# See Solaris/getuid.c and search for instances of app_krb5_user_uid in ON12.
+# Eventually this patch should be removed when the various ON components that
+# rely on it are modified to remove that reliance.
+#
+# This is Solaris specific behavior that MIT will not take upstream.
+# Patch source: in-house
+#
+
+--- krb5-1.14.3-036/src/include/k5-int.h
++++ krb5-1.14.3-037/src/include/k5-int.h
+@@ -2353,4 +2353,6 @@
+ #define k5_prependmsg krb5_prepend_error_message
+ #define k5_wrapmsg krb5_wrap_error_message
+ 
++uid_t krb5_getuid();
++
+ #endif /* _KRB5_INT_H */
+--- krb5-1.14.3-036/src/lib/krb5/os/Makefile.in
++++ krb5-1.14.3-037/src/lib/krb5/os/Makefile.in
+@@ -24,6 +24,7 @@
+ 	gen_port.o	\
+ 	genaddrs.o	\
+ 	gen_rname.o	\
++	getuid.o	\
+ 	hostaddr.o	\
+ 	hostrealm.o	\
+ 	hostrealm_dns.o \
+@@ -71,6 +72,7 @@
+ 	$(OUTPRE)gen_port.$(OBJEXT)	\
+ 	$(OUTPRE)genaddrs.$(OBJEXT)	\
+ 	$(OUTPRE)gen_rname.$(OBJEXT)	\
++	$(OUTPRE)getuid.$(OBJEXT)	\
+ 	$(OUTPRE)hostaddr.$(OBJEXT)	\
+ 	$(OUTPRE)hostrealm.$(OBJEXT)	\
+ 	$(OUTPRE)hostrealm_dns.$(OBJEXT) \
+@@ -118,6 +120,7 @@
+ 	$(srcdir)/gen_port.c	\
+ 	$(srcdir)/genaddrs.c	\
+ 	$(srcdir)/gen_rname.c	\
++	$(srcdir)/getuid.c	\
+ 	$(srcdir)/hostaddr.c	\
+ 	$(srcdir)/hostrealm.c	\
+ 	$(srcdir)/hostrealm_dns.c \
+--- krb5-1.14.3-036/src/lib/krb5/os/expand_path.c
++++ krb5-1.14.3-037/src/lib/krb5/os/expand_path.c
+@@ -291,7 +291,7 @@
+ expand_userid(krb5_context context, PTYPE param, const char *postfix,
+               char **str)
+ {
+-    if (asprintf(str, "%lu", (unsigned long)getuid()) < 0)
++    if (asprintf(str, "%lu", (unsigned long)krb5_getuid()) < 0)
+         return ENOMEM;
+     return 0;
+ }
--- a/components/krb5/patches/037-root-defcred.patch	Thu Sep 08 13:16:06 2016 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,578 +0,0 @@
-#
-# This patch provides support in kerberos for root acquiring a default
-# cred via either a root or host service principal keys in the keytab if
-# root doesn't have a cred already.
-#
-# This is Solaris specific behavior that MIT will not take upstream.
-# Patch source: in-house
-#
-diff --git a/src/include/k5-int.h b/src/include/k5-int.h
---- a/src/include/k5-int.h
-+++ b/src/include/k5-int.h
-@@ -2353,4 +2353,6 @@ void k5_change_error_message_code(krb5_context ctx, krb5_error_code oldcode,
- #define k5_prependmsg krb5_prepend_error_message
- #define k5_wrapmsg krb5_wrap_error_message
- 
-+uid_t krb5_getuid();
-+
- #endif /* _KRB5_INT_H */
-diff --git a/src/lib/gssapi/krb5/init_sec_context.c b/src/lib/gssapi/krb5/init_sec_context.c
---- a/src/lib/gssapi/krb5/init_sec_context.c
-+++ b/src/lib/gssapi/krb5/init_sec_context.c
-@@ -104,6 +104,11 @@
- #endif
- #include <stdlib.h>
- #include <assert.h>
-+#include <ctype.h>
-+
-+static OM_uint32 get_default_cred(OM_uint32 *, void *, gss_cred_id_t *);
-+krb5_error_code krb5_kt_find_realm(krb5_context, krb5_keytab, krb5_principal,
-+                                   krb5_data *);
- 
- /*
-  * $Id$
-@@ -962,8 +967,11 @@ krb5_gss_init_sec_context_ext(
-         /* verify the credential, or use the default */
-         /*SUPPRESS 29*/
-         if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) {
--            major_status = kg_get_defcred(minor_status, &defcred);
-+            major_status = get_default_cred(minor_status, context,
-+                                            (gss_cred_id_t *)&defcred);
-+
-             if (major_status && GSS_ERROR(major_status)) {
-+                save_error_info(*minor_status, context);
-                 if (*context_handle == GSS_C_NO_CONTEXT)
-                     krb5_free_context(context);
-                 return(major_status);
-@@ -1099,3 +1107,441 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
-                                          time_rec,
-                                          &exts);
- }
-+
-+/* Solaris Kerberos specific routines start */
-+
-+#define ROOT_UID 0
-+#define KRB5_DEFAULT_LIFE 60*60*10
-+
-+extern int
-+safechown(const char *src, uid_t uid, gid_t gid, int mode);
-+
-+/* Solaris Kerberos */
-+static OM_uint32
-+load_root_cred_using_keytab(OM_uint32 *minor_status,
-+                            krb5_context context,
-+                            const char *name,
-+                            krb5_int32 type)
-+{
-+    krb5_creds my_creds;
-+    krb5_principal me = NULL;
-+    krb5_principal server = NULL;
-+    krb5_error_code code;
-+    krb5_ccache ccache = NULL;
-+    krb5_keytab keytab = NULL;
-+    krb5_timestamp now;
-+    krb5_deltat lifetime = KRB5_DEFAULT_LIFE;   /* -l option */
-+    krb5_get_init_creds_opt opt;
-+    krb5_data tgtname = {
-+        0,
-+        KRB5_TGS_NAME_SIZE,
-+        KRB5_TGS_NAME
-+    };
-+    char *svcname = NULL;
-+    size_t kt_size;
-+    krb5_boolean kt_is_file;
-+
-+    if (!name)
-+        return (GSS_S_FAILURE);
-+
-+    memset((char *)&my_creds, 0, sizeof(my_creds));
-+
-+    if (code = krb5_kt_default(context, &keytab)) {
-+        *minor_status = code;
-+        return (GSS_S_FAILURE);
-+    }
-+
-+    if (code = krb5_kt_have_content(context, keytab)) {
-+        *minor_status = code;
-+        return (GSS_S_FAILURE);
-+    }
-+
-+    if (type == KRB5_NT_SRV_HST)
-+    	code = krb5_sname_to_principal(context, NULL, name, type, &me);
-+    else
-+	code = krb5_parse_name(context, name, &me);
-+
-+    if (code == 0 && krb5_is_referral_realm(&me->realm)) {
-+        krb5_data realm;
-+        code = krb5_kt_find_realm(context, keytab, me, &realm);
-+        if (code == 0) {
-+            krb5_free_data_contents(context, &me->realm);
-+            me->realm.length = realm.length;
-+            me->realm.data = realm.data;
-+        } else {
-+            /* Try to set a useful error message */
-+            char *princ = NULL;
-+            krb5_error_code ret;
-+            ret = krb5_unparse_name(context, me, &princ);
-+
-+            krb5_set_error_message(context, code,
-+                                   _("Failed to find realm for %s in keytab"),
-+                                   ret == 0 ? princ : "unknown");
-+            if (princ)
-+                krb5_free_unparsed_name(context, princ);
-+        }
-+    }
-+
-+    if (code) {
-+        (void) krb5_kt_close(context, keytab);
-+        krb5_free_principal(context, me);
-+        *minor_status = code;
-+        return (GSS_S_FAILURE);
-+    }
-+
-+    my_creds.client = me;
-+
-+    if ((code = krb5_build_principal_ext(context, &server,
-+                                        krb5_princ_realm(context, me)->length,
-+                                        krb5_princ_realm(context, me)->data,
-+                                        tgtname.length, tgtname.data,
-+                                        krb5_princ_realm(context, me)->length,
-+                                        krb5_princ_realm(context, me)->data,
-+                                        0))) {
-+        *minor_status = code;
-+        krb5_free_cred_contents(context, &my_creds);
-+        (void) krb5_kt_close(context, keytab);
-+        return (GSS_S_FAILURE);
-+    }
-+
-+    my_creds.server = server;
-+    my_creds.times.starttime = 0;     /* start timer
-+                                       * when request
-+                                       * gets to KDC
-+                                       */
-+    if ((code = krb5_timeofday(context, &now))) {
-+        *minor_status = code;
-+        krb5_free_cred_contents(context, &my_creds);
-+        (void) krb5_kt_close(context, keytab);
-+        return (GSS_S_FAILURE);
-+    }
-+    my_creds.times.endtime = now + lifetime;
-+    my_creds.times.renew_till = 0;
-+
-+    memset(&opt, 0, sizeof (opt));
-+    krb5_get_init_creds_opt_init(&opt);
-+    krb5_get_init_creds_opt_set_tkt_life(&opt, lifetime);
-+
-+    code = krb5_unparse_name(context, server, &svcname);
-+    if (code != 0) {
-+        *minor_status = code;
-+        krb5_free_cred_contents(context, &my_creds);
-+        (void) krb5_kt_close(context, keytab);
-+        return (GSS_S_FAILURE);
-+    }
-+    /*
-+     * Evidently (sigh), on success, krb5_get_init_creds_keytab
-+     * changes the my_creds princ ptrs so we need to free those
-+     * princs (me&server) as well as freeing all of my_creds contents.
-+     */
-+    code = krb5_get_init_creds_keytab(context,
-+                                      &my_creds, me, keytab,
-+                                      0, svcname, &opt);
-+
-+    (void) krb5_kt_close(context, keytab);
-+
-+    if (svcname != NULL) {
-+        free(svcname);
-+        svcname = NULL;
-+    }
-+    if (code) {
-+        *minor_status = code;
-+        krb5_free_cred_contents(context, &my_creds);
-+        return (GSS_S_FAILURE);
-+    }
-+
-+    krb5_free_principal(context, server);
-+    server = NULL;
-+
-+    code = krb5_cc_resolve (context,
-+                            krb5_cc_default_name(context),
-+                            &ccache);
-+    if (code != 0) {
-+        *minor_status = code;
-+        krb5_free_cred_contents(context, &my_creds);
-+        return (GSS_S_FAILURE);
-+    }
-+
-+    code = krb5_cc_initialize (context, ccache, me);
-+    krb5_free_principal(context, me);
-+    me = NULL;
-+    if (code != 0) {
-+        *minor_status = code;
-+        krb5_free_cred_contents(context, &my_creds);
-+        (void) krb5_cc_close(context, ccache);
-+        return (GSS_S_FAILURE);
-+    }
-+
-+    code = krb5_cc_store_cred(context, ccache,
-+                              &my_creds);
-+    krb5_free_cred_contents(context, &my_creds);
-+    (void) krb5_cc_close(context, ccache);
-+
-+    if (code) {
-+        *minor_status = code;
-+        return (GSS_S_FAILURE);
-+    }
-+
-+    return (GSS_S_COMPLETE);
-+}
-+
-+static OM_uint32
-+renew_ccache(OM_uint32 *minor_status, krb5_context context, uid_t uid)
-+{
-+    krb5_principal me;
-+    krb5_principal server;
-+    krb5_creds	creds;
-+    krb5_creds	tmpcreds;
-+    krb5_creds	*out_creds;
-+    krb5_error_code code;
-+    krb5_ccache ccache = NULL;
-+    const char *ccache_name = NULL;
-+    int options = 0;
-+    krb5_data tgtname = {
-+        0,
-+        KRB5_TGS_NAME_SIZE,
-+        KRB5_TGS_NAME
-+    };
-+    gid_t gid = getgid();
-+
-+    memset((char *)&creds, 0, sizeof(creds));
-+    memset((char *)&tmpcreds, 0, sizeof(creds));
-+
-+    if ((code = krb5_cc_default(context, &ccache))) {
-+        *minor_status = code;
-+        return (GSS_S_FAILURE);
-+    }
-+
-+    if ((code = krb5_cc_get_principal(context, ccache, &me)) != 0) {
-+        *minor_status = code;
-+        (void) krb5_cc_close(context, ccache);
-+        return (GSS_S_FAILURE);
-+    }
-+
-+    creds.client = me;
-+
-+    if((code = krb5_build_principal_ext(context, &server,
-+                                        krb5_princ_realm(context, me)->length,
-+                                        krb5_princ_realm(context, me)->data,
-+                                        tgtname.length, tgtname.data,
-+                                        krb5_princ_realm(context, me)->length,
-+                                        krb5_princ_realm(context, me)->data,
-+                                        0))) {
-+        krb5_free_principal(context, me);
-+        (void) krb5_cc_close(context, ccache);
-+        *minor_status = code;
-+        return (GSS_S_FAILURE);
-+    }
-+
-+    creds.server = server;
-+    creds.ticket_flags = TKT_FLG_RENEWABLE;
-+
-+    if ((krb5_cc_retrieve_cred(context, ccache, KRB5_TC_MATCH_FLAGS,
-+                               &creds, &tmpcreds))) {
-+        (void) krb5_cc_close(context, ccache);
-+        return (KDC_ERR_BADOPTION);
-+    }
-+
-+    creds.ticket_flags = 0;
-+    code = krb5_get_credentials_renew(context, options, ccache,
-+                                      &creds, &out_creds);
-+    krb5_free_cred_contents(context, &creds);
-+    krb5_free_cred_contents(context, &tmpcreds);
-+
-+    if (code) {
-+        *minor_status = code;
-+        (void) krb5_cc_close(context, ccache);
-+        return (GSS_S_FAILURE);
-+    }
-+
-+    krb5_free_creds(context, out_creds);
-+
-+    ccache_name = krb5_cc_default_name(context);
-+    if (ccache_name == NULL) {
-+	*minor_status = KRB5_FCC_INTERNAL;
-+        (void) krb5_cc_close(context, ccache);
-+	return (GSS_S_FAILURE);
-+    }
-+
-+    if (strncmp(ccache_name, "FILE:", strlen("FILE:")) == 0) {
-+	ccache_name += strlen("FILE:");
-+    	code = safechown(ccache_name, uid, gid, -1);
-+    	if (code == -1) {
-+	    (void) krb5_cc_destroy(context, ccache);
-+	    *minor_status = code;
-+	    return (GSS_S_FAILURE);
-+	}
-+    }
-+
-+    (void) krb5_cc_close(context, ccache);
-+
-+    return (GSS_S_COMPLETE);
-+}
-+
-+/* Solaris Kerberos */
-+#define	SAM_ACCOUNT_LEN 17 /* 15:hostname + 1:$ + 1:\0 */
-+krb5_error_code
-+get_sam_account_name(char **name)
-+{
-+    char *p, localname[SAM_ACCOUNT_LEN];
-+
-+    if (name == NULL)
-+	return (EINVAL);
-+
-+    if (gethostname(localname, SAM_ACCOUNT_LEN) != 0)
-+	return (errno);
-+
-+    localname[SAM_ACCOUNT_LEN - 2] = '\0';
-+
-+    if ((p = strchr(localname, '.')) != NULL)
-+	*p = '\0';
-+
-+    for (p = localname; *p; p++)
-+	*p = toupper(*p);
-+
-+    (void) strlcat(localname, "$", SAM_ACCOUNT_LEN);
-+
-+    *name = strdup(localname);
-+    if (*name == NULL)
-+	return (ENOMEM);
-+
-+    return (0);
-+}
-+
-+/*
-+ * Solaris Kerberos:
-+ * We enforce a minimum refresh time on the root cred. This avoids problems for
-+ * the higher level communication protocol for having valid creds and
-+ * setting up a valid context, only to have it expire before or while
-+ * it is being used. For non root users we don't care since we do not refresh
-+ * there creds, they get what they can get.
-+ */
-+#define MIN_REFRESH_TIME 300
-+#define MIN_RENEW_TIME 1500
-+
-+/* get_default_cred() must be called with the krb5_mutex lock held */
-+static OM_uint32
-+get_default_cred(OM_uint32 *minor_status, void *ct, gss_cred_id_t *cred_handle)
-+{
-+    krb5_timestamp now;
-+    krb5_gss_cred_id_t cred;
-+    OM_uint32 major;
-+    OM_uint32 mntmp;
-+    /*
-+     * Solaris Kerberos
-+     * Use krb5_getuid() to select the mechanism to obtain the uid.
-+     */
-+    uid_t uid = krb5_getuid();
-+    krb5_context context = (krb5_context)ct;
-+
-+    /* Get the default cred for user */
-+    if (((major = kg_get_defcred(minor_status, cred_handle)) != NULL) &&
-+        GSS_ERROR(major)) {
-+
-+        /* If we're not root we're done */
-+        if (uid != ROOT_UID)
-+            return (major);
-+
-+        /*
-+         * Try and get root's cred in the cache using keytab.
-+         *
-+         * First try "root", then try "host", and finally try the Security
-+         * Account Manager (SAM) for MS AD interop.
-+         */
-+        major = load_root_cred_using_keytab(minor_status,
-+                                            context, "root", KRB5_NT_SRV_HST);
-+
-+        if (major != GSS_S_COMPLETE)
-+            major = load_root_cred_using_keytab(minor_status,
-+                                                context, "host",
-+                                                KRB5_NT_SRV_HST);
-+
-+        if (major != GSS_S_COMPLETE) {
-+            char *name;
-+            krb5_error_code code;
-+
-+            code = get_sam_account_name(&name);
-+            if (code == 0) {
-+                major = load_root_cred_using_keytab(minor_status,
-+                                                    context, name,
-+                                                    KRB5_NT_PRINCIPAL);
-+                free(name);
-+            }
-+        }
-+
-+        if (major != GSS_S_COMPLETE)
-+            return (major);
-+
-+        /* We should have valid tgt now in the cache, so get it. */
-+        major = kg_get_defcred(minor_status, cred_handle);
-+
-+        return (major);
-+    }
-+
-+    /* We've got a gss cred handle that is a kerberos cred handle. */
-+    cred = (krb5_gss_cred_id_t)*cred_handle;
-+
-+    /* If we can't get the time, assume the worst. */
-+    if (krb5_timeofday(context, &now)) {
-+        (void) krb5_gss_release_cred(&mntmp, cred_handle);
-+        return (GSS_S_CREDENTIALS_EXPIRED);
-+    }
-+
-+    /*
-+     * Try and get root's cred in the cache using keytab.
-+     *
-+     * First try "root", then try "host", and finally try the Security
-+     * Account Manager (SAM) for MS AD interop.
-+     */
-+    /* If root's cred has expired re-get it */
-+    if (cred->expire && (cred->expire < now + MIN_REFRESH_TIME) &&
-+        (uid == ROOT_UID)) {
-+        (void) krb5_gss_release_cred(&mntmp, cred_handle);
-+
-+        major = load_root_cred_using_keytab(minor_status,
-+                                            context, "root", KRB5_NT_SRV_HST);
-+
-+        if (major != GSS_S_COMPLETE)
-+            major = load_root_cred_using_keytab(minor_status,
-+                                                context, "host",
-+                                                KRB5_NT_SRV_HST);
-+
-+        if (major != GSS_S_COMPLETE) {
-+            char *name;
-+            krb5_error_code code;
-+
-+            code = get_sam_account_name(&name);
-+            if (code == 0) {
-+                major = load_root_cred_using_keytab(minor_status,
-+                                                    context, name,
-+                                                    KRB5_NT_PRINCIPAL);
-+                free(name);
-+            }
-+        }
-+
-+        if (major != GSS_S_COMPLETE)
-+            return (major);
-+
-+        major = kg_get_defcred(minor_status, cred_handle);
-+        if (major != GSS_S_COMPLETE)
-+            return (major);
-+
-+        /* Any body else is SOL unless we can renew their credential cache */
-+    } else if (cred->expire && (cred->expire < now + MIN_RENEW_TIME) &&
-+               (cred->expire > now)) {
-+        (void) krb5_gss_release_cred(&mntmp, cred_handle);
-+
-+        major = renew_ccache(minor_status, context, uid);
-+        if ((major != GSS_S_COMPLETE) &&
-+            (major != KDC_ERR_BADOPTION))
-+            return (major);
-+
-+        major = kg_get_defcred(minor_status, cred_handle);
-+        if (major != GSS_S_COMPLETE)
-+            return (major);
-+
-+    }
-+
-+    /* Otherwise we got non expired creds */
-+    return (GSS_S_COMPLETE);
-+}
-diff --git a/src/lib/krb5/keytab/Makefile.in b/src/lib/krb5/keytab/Makefile.in
---- a/src/lib/krb5/keytab/Makefile.in
-+++ b/src/lib/krb5/keytab/Makefile.in
-@@ -13,6 +13,7 @@ STLIBOBJS= \
- 	ktremove.o	\
- 	ktfns.o		\
- 	kt_file.o	\
-+	kt_findrealm.o	\
- 	kt_memory.o	\
- 	kt_srvtab.o	\
- 	read_servi.o	\
-@@ -26,6 +27,7 @@ OBJS=	\
- 	$(OUTPRE)ktremove.$(OBJEXT)	\
- 	$(OUTPRE)ktfns.$(OBJEXT)	\
- 	$(OUTPRE)kt_file.$(OBJEXT)	\
-+	$(OUTPRE)kt_findrealm.$(OBJEXT)	\
- 	$(OUTPRE)kt_memory.$(OBJEXT)	\
- 	$(OUTPRE)kt_srvtab.$(OBJEXT)	\
- 	$(OUTPRE)read_servi.$(OBJEXT)	\
-@@ -39,6 +41,7 @@ SRCS=	\
- 	$(srcdir)/ktremove.c	\
- 	$(srcdir)/ktfns.c	\
- 	$(srcdir)/kt_file.c	\
-+	$(srcdir)/kt_findrealm.c	\
- 	$(srcdir)/kt_memory.c	\
- 	$(srcdir)/kt_srvtab.c	\
- 	$(srcdir)/read_servi.c	\
-diff --git a/src/lib/krb5/os/Makefile.in b/src/lib/krb5/os/Makefile.in
---- a/src/lib/krb5/os/Makefile.in
-+++ b/src/lib/krb5/os/Makefile.in
-@@ -24,6 +24,7 @@ STLIBOBJS= \
- 	gen_port.o	\
- 	genaddrs.o	\
- 	gen_rname.o	\
-+	getuid.o	\
- 	hostaddr.o	\
- 	hostrealm.o	\
- 	hostrealm_dns.o \
-@@ -49,6 +50,7 @@ STLIBOBJS= \
- 	read_msg.o	\
- 	read_pwd.o	\
- 	realm_dom.o	\
-+	safechown.o	\
- 	sendto_kdc.o	\
- 	sn2princ.o	\
-         thread_safe.o   \
-@@ -71,6 +73,7 @@ OBJS= \
- 	$(OUTPRE)gen_port.$(OBJEXT)	\
- 	$(OUTPRE)genaddrs.$(OBJEXT)	\
- 	$(OUTPRE)gen_rname.$(OBJEXT)	\
-+	$(OUTPRE)getuid.$(OBJEXT)	\
- 	$(OUTPRE)hostaddr.$(OBJEXT)	\
- 	$(OUTPRE)hostrealm.$(OBJEXT)	\
- 	$(OUTPRE)hostrealm_dns.$(OBJEXT) \
-@@ -96,6 +99,7 @@ OBJS= \
- 	$(OUTPRE)read_msg.$(OBJEXT)	\
- 	$(OUTPRE)read_pwd.$(OBJEXT)	\
- 	$(OUTPRE)realm_dom.$(OBJEXT)	\
-+	$(OUTPRE)safechown.$(OBJEXT)	\
- 	$(OUTPRE)sendto_kdc.$(OBJEXT)	\
- 	$(OUTPRE)sn2princ.$(OBJEXT)	\
-         $(OUTPRE)thread_safe.$(OBJEXT)  \
-@@ -118,6 +122,7 @@ SRCS= \
- 	$(srcdir)/gen_port.c	\
- 	$(srcdir)/genaddrs.c	\
- 	$(srcdir)/gen_rname.c	\
-+	$(srcdir)/getuid.c	\
- 	$(srcdir)/hostaddr.c	\
- 	$(srcdir)/hostrealm.c	\
- 	$(srcdir)/hostrealm_dns.c \
-@@ -143,6 +148,7 @@ SRCS= \
- 	$(srcdir)/read_pwd.c	\
- 	$(srcdir)/realm_dom.c	\
- 	$(srcdir)/port2ip.c	\
-+	$(srcdir)/safechown.c	\
- 	$(srcdir)/sendto_kdc.c	\
- 	$(srcdir)/sn2princ.c	\
-         $(srcdir)/thread_safe.c \
-diff --git a/src/lib/krb5/os/expand_path.c b/src/lib/krb5/os/expand_path.c
---- a/src/lib/krb5/os/expand_path.c
-+++ b/src/lib/krb5/os/expand_path.c
-@@ -291,7 +291,7 @@ static krb5_error_code
- expand_userid(krb5_context context, PTYPE param, const char *postfix,
-               char **str)
- {
--    if (asprintf(str, "%lu", (unsigned long)getuid()) < 0)
-+    if (asprintf(str, "%lu", (unsigned long)krb5_getuid()) < 0)
-         return ENOMEM;
-     return 0;
- }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/krb5/patches/073-root-init-cred-kt.patch	Fri Sep 02 16:39:26 2016 -0500
@@ -0,0 +1,276 @@
+#
+# This patch provides support in kerberos for root acquiring a default cred via
+# either a root, host service principal or sam account name keys in the keytab
+# if root doesn't have a cred already.  Note that if root has a client keytab
+# provisioned then that will be used instead.
+#
+# This is Solaris specific behavior that MIT will not take upstream.
+# Patch source: in-house
+#
+
+--- a/src/lib/gssapi/krb5/acquire_cred.c
++++ b/src/lib/gssapi/krb5/acquire_cred.c
+@@ -77,6 +77,7 @@
+ #else
+ #include <strings.h>
+ #endif
++#include <ctype.h>
+ 
+ #ifdef USE_LEASH
+ #ifdef _WIN64
+@@ -88,6 +89,9 @@
+ static HANDLE hLeashDLL = INVALID_HANDLE_VALUE;
+ #endif
+ 
++/* for solaris root fallback check */
++static char defktname[BUFSIZ];
++
+ #ifndef LEAN_CLIENT
+ k5_mutex_t gssint_krb5_keytab_lock = K5_MUTEX_PARTIAL_INITIALIZER;
+ static char *krb5_gss_keytab = NULL;
+@@ -590,6 +594,151 @@
+     set_refresh_time(context, cred->ccache, refresh);
+ }
+ 
++#define	SAM_ACCOUNT_LEN 17 /* 15:hostname + 1:$ + 1:\0 */
++krb5_error_code
++get_sam_account_name(char **name)
++{
++    char *p, localname[SAM_ACCOUNT_LEN];
++
++    if (name == NULL)
++	return (EINVAL);
++
++    if (gethostname(localname, SAM_ACCOUNT_LEN) != 0)
++	return (errno);
++
++    localname[SAM_ACCOUNT_LEN - 2] = '\0';
++
++    if ((p = strchr(localname, '.')) != NULL)
++	*p = '\0';
++
++    for (p = localname; *p; p++)
++	*p = toupper(*p);
++
++    (void) strlcat(localname, "$", SAM_ACCOUNT_LEN);
++
++    *name = strdup(localname);
++    if (*name == NULL)
++	return (ENOMEM);
++
++    return (0);
++}
++
++krb5_error_code krb5_kt_find_realm(krb5_context, krb5_keytab, krb5_principal,
++                                   krb5_data *);
++
++static krb5_error_code
++get_root_initcred_keytab(krb5_context context,
++                      krb5_creds *kcreds,
++                      krb5_gss_cred_id_rec *gsscred,
++                      const char *name,
++                      krb5_int32 type,
++                      krb5_get_init_creds_opt *opt)
++{
++    krb5_principal client_princ;
++    krb5_error_code code;
++
++    if (type == KRB5_NT_SRV_HST) {
++        code = krb5_sname_to_principal(context, NULL, name, type,
++                                       &client_princ);
++    } else {
++        /* Assuming KRB5_NT_PRINCIPAL */
++        code = krb5_parse_name(context, name, &client_princ);
++    }
++    if (code)
++        return code;
++
++    if (krb5_is_referral_realm(&client_princ->realm)) {
++        krb5_data realm;
++
++        code = krb5_kt_find_realm(context, gsscred->client_keytab,
++                                  client_princ, &realm);
++        if (code == 0) {
++            krb5_free_data_contents(context, &client_princ->realm);
++            client_princ->realm.length = realm.length;
++            client_princ->realm.data = realm.data;
++        } else {
++            /* Try to set a useful error message */
++            char *princ_name = NULL;
++            char kt_name[BUFSIZ];
++
++            (void) krb5_unparse_name(context, client_princ, &princ_name);
++            (void) krb5_kt_get_name(context, gsscred->client_keytab, kt_name,
++                                    BUFSIZ);
++            krb5_set_error_message(context, code,
++                                   _("Failed to find realm for %s in "
++                                     "keytab %s"),
++                                   princ_name != NULL ? princ_name : "unknown",
++                                   kt_name);
++            krb5_free_unparsed_name(context, princ_name);
++        }
++    }
++    if (code)
++        goto cleanup;
++
++    code = krb5_get_init_creds_keytab(context, kcreds, client_princ,
++                                      gsscred->client_keytab, 0, NULL, opt);
++    if (code == 0) {
++        /* set the gsscred name to that of the princ for which an init cred was
++         * acquired. */
++        if (gsscred->name != NULL)
++            (void) kg_release_name(context, &gsscred->name);
++
++        code = kg_init_name(context, client_princ, NULL, NULL, NULL,
++                            KG_INIT_NAME_NO_COPY, &gsscred->name);
++        /* Since KG_INIT_NAME_NO_COPY is set do not free client_princ if
++         * kg_init_name succeeds. */
++        if (code == 0)
++            return 0;
++        else
++            krb5_free_cred_contents(context, kcreds);
++    }
++
++cleanup:
++    krb5_free_principal(context, client_princ);
++    return code;
++}
++
++/*
++ * This implements long time Solaris behavior where processes running as root
++ * will try to acquire an init cred via the default/system keytab.  The root,
++ * host and SAM princs are tried in that order until one succeeds or they all
++ * fail.
++ */
++static krb5_error_code
++root_init_cred_kt_fallback(krb5_context context,
++                          krb5_creds *kcreds,
++                          krb5_gss_cred_id_rec *gsscred,
++                          krb5_get_init_creds_opt *opt)
++{
++    char *sam_name = NULL;
++    krb5_error_code code;
++
++    /* Try the root/<FQDN> service princ in system keytab */
++    code = get_root_initcred_keytab(context, kcreds, gsscred, "root",
++                                    KRB5_NT_SRV_HST, opt);
++    if (code == 0)
++        goto out;
++
++    /* Try the host/<FQDN> service princ in system keytab if the root princ
++     * wasn't found */
++    code = get_root_initcred_keytab(context, kcreds, gsscred, "host",
++                                    KRB5_NT_SRV_HST, opt);
++    if (code == 0)
++        goto out;
++
++    /* Try the SAM account princ in system keytab if the host service princ
++     * wasn't found for MS interop sake */
++    code = get_sam_account_name(&sam_name);
++    if (code)
++        goto out;
++
++    code = get_root_initcred_keytab(context, kcreds, gsscred, sam_name,
++                                    KRB5_NT_PRINCIPAL, opt);
++    free(sam_name);
++out:
++    return code;
++}
++
+ /* Get initial credentials using the supplied password or client keytab. */
+ static krb5_error_code
+ get_initial_cred(krb5_context context, krb5_gss_cred_id_rec *cred)
+@@ -609,8 +758,41 @@
+                                             cred->password, NULL, NULL, 0,
+                                             NULL, opt);
+     } else if (cred->client_keytab != NULL) {
+-        code = krb5_get_init_creds_keytab(context, &creds, cred->name->princ,
+-                                          cred->client_keytab, 0, NULL, opt);
++        if (krb5_getuid() == 0) {
++            char clientktname[BUFSIZ];
++
++            /* assuming we only need to get the default keytab name once */
++            if (defktname[0] == '\0') {
++                code = krb5_kt_default_name(context, defktname,
++                                            sizeof(defktname));
++                if (code)
++                    goto cleanup;
++            }
++
++            code = krb5_kt_get_name(context, cred->client_keytab, clientktname,
++                                    sizeof(clientktname));
++            if (code)
++                goto cleanup;
++
++            /*
++             * If the client keytab name is the same as the system default
++             * keytab and we are root then we need to use the Solaris root
++             * fallback behavior in root_init_cred_kt_fallback().
++             */
++            if (strcmp(defktname, clientktname) == 0) {
++                code = root_init_cred_kt_fallback(context, &creds, cred, opt);
++            } else {
++                code = krb5_get_init_creds_keytab(context, &creds,
++                                                  cred->name->princ,
++                                                  cred->client_keytab, 0, NULL,
++                                                  opt);
++            }
++        } else {
++            code = krb5_get_init_creds_keytab(context, &creds,
++                                              cred->name->princ,
++                                              cred->client_keytab, 0, NULL,
++                                              opt);
++        }
+     } else {
+         code = KRB5_KT_NOTFOUND;
+     }
+@@ -700,6 +882,23 @@
+             krb5_clear_error_message(context);
+             code = 0;
+         }
++        /*
++         * The logic below is involved in providing support for Solaris
++         * behavior where root processes will fall back to acquiring an initial
++         * cred via the system/default keytab.  The idea is that if the
++         * client_keytab could not be resolved or it doesn't exist then set the
++         * client_keytab field to the system/default keytab.
++         */
++        if (krb5_getuid() == 0) {
++            if (cred->client_keytab == NULL ||
++                krb5_kt_have_content(context, cred->client_keytab) != 0) {
++
++                if (cred->client_keytab != NULL)
++                    krb5_kt_close(context, cred->client_keytab);
++
++                code = krb5_kt_default(context, &cred->client_keytab);
++            }
++        }
+     }
+     if (code)
+         goto error;
+--- a/src/lib/krb5/keytab/Makefile.in
++++ b/src/lib/krb5/keytab/Makefile.in
+@@ -13,6 +13,7 @@
+ 	ktremove.o	\
+ 	ktfns.o		\
+ 	kt_file.o	\
++	kt_findrealm.o	\
+ 	kt_memory.o	\
+ 	kt_srvtab.o	\
+ 	read_servi.o	\
+@@ -26,6 +27,7 @@
+ 	$(OUTPRE)ktremove.$(OBJEXT)	\
+ 	$(OUTPRE)ktfns.$(OBJEXT)	\
+ 	$(OUTPRE)kt_file.$(OBJEXT)	\
++	$(OUTPRE)kt_findrealm.$(OBJEXT)	\
+ 	$(OUTPRE)kt_memory.$(OBJEXT)	\
+ 	$(OUTPRE)kt_srvtab.$(OBJEXT)	\
+ 	$(OUTPRE)read_servi.$(OBJEXT)	\
+@@ -39,6 +41,7 @@
+ 	$(srcdir)/ktremove.c	\
+ 	$(srcdir)/ktfns.c	\
+ 	$(srcdir)/kt_file.c	\
++	$(srcdir)/kt_findrealm.c	\
+ 	$(srcdir)/kt_memory.c	\
+ 	$(srcdir)/kt_srvtab.c	\
+ 	$(srcdir)/read_servi.c	\