diff -r 4c1935f5ec9a -r 87f7fd05f888 components/krb5/patches/037-root-defcred.patch --- 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 - #include -+#include -+ -+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; - }