changeset 11061:1e27eb9fbdd7

6885980 Need case-insensitive keytab lookups for MS interop 6885387 gsskrb5_extract_authz_data_from_sec_context() fails with service ticket sent by Windows 7 client 6858400 kclient cant join Windows AD domain if hostname is 20 characters or longer 6867203 Solaris acceptors fail in Windows 2000 environment 6868908 Solaris acceptors should have returned KRB5KRB_AP_ERR_MODIFIED for Microsoft interoperability 6867208 Windows client cannot recover from KRB5KRB_AP_ERR_SKEW error
author Shawn Emery <Shawn.Emery@Sun.COM>
date Thu, 12 Nov 2009 17:40:34 -0700
parents 40daac51fc0c
children c5aa655977cc
files usr/src/cmd/krb5/kadmin/kclient/kclient.sh usr/src/cmd/krb5/kadmin/kclient/ksetpw.c usr/src/lib/gss_mechs/mech_krb5/krb5/keytab/kt_file.c usr/src/lib/gss_mechs/mech_krb5/krb5/krb/princ_comp.c usr/src/lib/gss_mechs/mech_krb5/mapfile-vers usr/src/lib/gss_mechs/mech_krb5/mech/accept_sec_context.c usr/src/lib/gss_mechs/mech_krb5/mech/krb5_gss_glue.c usr/src/lib/gss_mechs/mech_spnego/mech/spnego_mech.c
diffstat 8 files changed, 163 insertions(+), 57 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/krb5/kadmin/kclient/kclient.sh	Thu Nov 12 15:28:55 2009 -0800
+++ b/usr/src/cmd/krb5/kadmin/kclient/kclient.sh	Thu Nov 12 17:40:34 2009 -0700
@@ -29,6 +29,7 @@
 # be generated and local host's keytab file setup. The script
 # can also optionally setup the system to do kerberized nfs and
 # bringover a master krb5.conf copy from a specified location.
+#
 
 function cleanup {
 
@@ -1156,6 +1157,8 @@
 
 function join_domain {
 	typeset -u upcase_nodename
+	typeset -l locase_nodename
+	typeset -L15 string15
 	typeset netbios_nodename fqdn
 	
 	container=Computers
@@ -1175,7 +1178,16 @@
 
 	dom=$domain
 	realm=$domain
-	upcase_nodename=$hostname
+
+	if [[ ${#hostname} -gt 15 ]]; then
+		string15=$hostname
+		upcase_nodename=$string15
+		locase_nodename=$string15
+	else
+		upcase_nodename=$hostname
+		locase_nodename=$hostname
+	fi
+
 	netbios_nodename="${upcase_nodename}\$"
 	fqdn=$hostname.$domain
 	upn=host/${fqdn}@${realm}
@@ -1313,9 +1325,10 @@
 		fi
 	fi
 
+	[[ -z $dn ]] && dn="CN=${upcase_nodename},${baseDN}"
 	if $modify_existing; then
 		cat > "$object" <<EOF
-dn: CN=$upcase_nodename,$baseDN
+dn: $dn
 changetype: modify
 replace: userPrincipalName
 userPrincipalName: $upn
@@ -1333,12 +1346,13 @@
 		printf "$(gettext "A machine account already exists; updating it").\n"
 		ldapadd -h "$dc" $ldap_args -f "$object" > /dev/null 2>&1
 		if [[ $? -ne 0 ]]; then
-			printf "$(gettext "Failed to create the AD object via LDAP").\n" >&2
+			printf "$(gettext "Failed to modify the AD object via LDAP").\n" >&2
 			error_message
 		fi
 	else
+		dn="CN=${upcase_nodename},${baseDN}"
 		cat > "$object" <<EOF
-dn: CN=$upcase_nodename,$baseDN
+dn: $dn
 objectClass: computer
 cn: $upcase_nodename
 sAMAccountName: ${netbios_nodename}
@@ -1446,7 +1460,7 @@
 	# encryption type attributes.
 	if [[ $level -gt 2 ]]; then
 		cat > "$object" <<EOF
-dn: CN=$upcase_nodename,$baseDN
+dn: $dn
 changetype: modify
 replace: msDS-SupportedEncryptionTypes
 msDS-SupportedEncryptionTypes: $val
@@ -1469,7 +1483,7 @@
 	# and possibly UseDesOnly (2097152) (see above)
 	#
 	cat > "$object" <<EOF
-dn: CN=$upcase_nodename,$baseDN
+dn: $dn
 changetype: modify
 replace: userAccountControl
 userAccountControl: $userAccountControl
@@ -1491,13 +1505,14 @@
 	rm $new_keytab > /dev/null 2>&1
 
 	cat > "$object" <<EOF
-dn: CN=$upcase_nodename,$baseDN
+dn: $dn
 changetype: modify
 add: servicePrincipalName
 servicePrincipalName: nfs/${fqdn}
 servicePrincipalName: HTTP/${fqdn}
 servicePrincipalName: root/${fqdn}
 servicePrincipalName: cifs/${fqdn}
+servicePrincipalName: host/${upcase_nodename}
 EOF
 	ldapmodify -h "$dc" $ldap_args -f "$object" >/dev/null 2>&1
 	if [[ $? -ne 0 ]]; then
@@ -1507,10 +1522,10 @@
 
 	#
 	# In Windows, unlike MIT based implementations we salt the keys with
-	# the UPN, which is based on the host/fqdn@realm elements, not with the
-	# individual SPN strings.
+	# the UPN, which is based on the host/string15@realm elements, not
+	# with the individual SPN strings.
 	#
-	salt=host/${fqdn}@${realm}
+	salt=host/${locase_nodename}.${domain}@${realm}
 
 	printf "%s" $newpw | $KSETPW -n -s $salt -v $kvno -k "$new_keytab" "${args[@]}" host/${fqdn}@${realm} > /dev/null 2>&1
 	if [[ $? -ne 0 ]]
@@ -1519,16 +1534,6 @@
 		error_message
 	fi
 
-	printf "%s" $newpw | $KSETPW -n -s $salt -v $kvno -k "$new_keytab" "${args[@]}" HOST/${fqdn}@${realm} > /dev/null 2>&1
-	if [[ $? -ne 0 ]]
-	then
-		printf "$(gettext "Failed to set account password").\n" >&2
-		error_message
-	fi
-
-	# Could be setting ${netbios_nodename}@${realm}, but for now no one
-	# is requesting this.
-
 	printf "%s" $newpw | $KSETPW -n -s $salt -v $kvno -k "$new_keytab" "${args[@]}" nfs/${fqdn}@${realm} > /dev/null 2>&1
 	if [[ $? -ne 0 ]]
 	then
@@ -1557,13 +1562,20 @@
 		error_message
 	fi
 
+	printf "%s" $newpw | $KSETPW -n -s $salt -v $kvno -k "$new_keytab" "${args[@]}" ${netbios_nodename}@${realm} > /dev/null 2>&1
+	if [[ $? -ne 0 ]]
+	then
+		printf "$(gettext "Failed to set account password").\n" >&2
+		error_message
+	fi
+
 	doKRB5config
 
 	addDNSRR $dom
 
 	setSMB $dom $dc
 
-	printf -- "\n---------------------------------------------------\n"
+	printf -- "---------------------------------------------------\n"
 	printf "$(gettext "Setup COMPLETE").\n\n"
 
 	kdestroy -q 1>$TMP_FILE 2>&1
--- a/usr/src/cmd/krb5/kadmin/kclient/ksetpw.c	Thu Nov 12 15:28:55 2009 -0800
+++ b/usr/src/cmd/krb5/kadmin/kclient/ksetpw.c	Thu Nov 12 17:40:34 2009 -0700
@@ -381,7 +381,7 @@
 usage()
 {
 	(void) fprintf(stderr, gettext("Usage: %s [-c ccache] [-k keytab] "
-	    "[-e enctype_list] [-n] princ\n"), whoami);
+	    "[-e enctype_list] [-s salt_name] [-n] princ\n"), whoami);
 	(void) fprintf(stderr,
 	    gettext("\t-n\tDon't set the principal's password\n"));
 	(void) fprintf(stderr, gettext("\tenctype_list is a comma or whitespace"
--- a/usr/src/lib/gss_mechs/mech_krb5/krb5/keytab/kt_file.c	Thu Nov 12 15:28:55 2009 -0800
+++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/keytab/kt_file.c	Thu Nov 12 17:40:34 2009 -0700
@@ -1,9 +1,8 @@
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-
 /*
  * lib/krb5/keytab/kt_file.c
  *
@@ -77,6 +76,10 @@
 extern const struct _krb5_kt_ops krb5_ktf_ops;
 extern const struct _krb5_kt_ops krb5_ktf_writable_ops;
 
+extern krb5_boolean KRB5_CALLCONV
+__krb5_principal_compare_case_ins(krb5_context context,
+    krb5_const_principal princ1, krb5_const_principal princ2);
+
 krb5_error_code KRB5_CALLCONV krb5_ktfile_resolve 
 	(krb5_context,
 		   const char *,
@@ -291,7 +294,20 @@
 	/* if the principal isn't the one requested, free new_entry
 	   and continue to the next. */
 
-	if (!krb5_principal_compare(context, principal, new_entry.principal)) {
+	/*
+	 * Solaris Kerberos: MS Interop requires that case insensitive
+	 * comparisons of service and host components are performed for key
+	 * table lookup, etc.  Only called if the private environment variable
+	 * MS_INTEROP is defined.
+	 */
+	if (krb5_getenv("MS_INTEROP")) {
+	  if (!__krb5_principal_compare_case_ins(context, principal,
+	    new_entry.principal)) {
+	    	krb5_kt_free_entry(context, &new_entry);
+	    	continue;
+	  }
+	} else if (!krb5_principal_compare(context, principal,
+	  new_entry.principal)) {
 	    krb5_kt_free_entry(context, &new_entry);
 	    continue;
 	}
--- a/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/princ_comp.c	Thu Nov 12 15:28:55 2009 -0800
+++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/princ_comp.c	Thu Nov 12 17:40:34 2009 -0700
@@ -1,4 +1,3 @@
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
 /*
  * lib/krb5/krb/princ_comp.c
  *
@@ -29,6 +28,11 @@
  * not.
  */
 
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
 #include <k5-int.h>
 
 /*ARGSUSED*/
@@ -68,6 +72,35 @@
     return TRUE;
 }
 
+/*
+ * Solaris Kerberos: MS Interop requires that case insensitive comparisons of
+ * service and host components are performed for key table lookup, etc.  Only
+ * called if the private environment variable MS_INTEROP is defined.
+ */
+krb5_boolean KRB5_CALLCONV
+__krb5_principal_compare_case_ins(krb5_context context,
+    krb5_const_principal princ1, krb5_const_principal princ2)
+{
+    register int i;
+    krb5_int32 nelem;
+
+    nelem = krb5_princ_size(context, princ1);
+    if (nelem != krb5_princ_size(context, princ2))
+	return FALSE;
+
+    if (! krb5_realm_compare(context, princ1, princ2))
+	return FALSE;
+
+    for (i = 0; i < (int) nelem; i++) {
+	register const krb5_data *p1 = krb5_princ_component(context, princ1, i);
+	register const krb5_data *p2 = krb5_princ_component(context, princ2, i);
+	if (p1->length != p2->length ||
+	    strncasecmp(p1->data, p2->data, p1->length))
+	    return FALSE;
+    }
+    return TRUE;
+}
+
 krb5_boolean KRB5_CALLCONV krb5_is_referral_realm(const krb5_data *r)
 {
     /*
--- a/usr/src/lib/gss_mechs/mech_krb5/mapfile-vers	Thu Nov 12 15:28:55 2009 -0800
+++ b/usr/src/lib/gss_mechs/mech_krb5/mapfile-vers	Thu Nov 12 17:40:34 2009 -0700
@@ -219,6 +219,7 @@
 	gss_krb5_copy_ccache;
 	gss_mech_krb5;
 	gss_mech_krb5_old;
+	gss_mech_krb5_wrong;
 	gss_mech_set_krb5;
 	gss_mech_set_krb5_both;
 	gss_mech_set_krb5_old;
--- a/usr/src/lib/gss_mechs/mech_krb5/mech/accept_sec_context.c	Thu Nov 12 15:28:55 2009 -0800
+++ b/usr/src/lib/gss_mechs/mech_krb5/mech/accept_sec_context.c	Thu Nov 12 17:40:34 2009 -0700
@@ -1,7 +1,3 @@
-/*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
- */
 /*
  * Copyright 2000, 2004  by the Massachusetts Institute of Technology.
  * All Rights Reserved.
@@ -74,6 +70,11 @@
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
 #include "k5-int.h"
 #include "gssapiP_krb5.h"
 #ifdef HAVE_MEMORY_H
@@ -381,6 +382,12 @@
 				      input_token->length, 1))) {
        mech_used = gss_mech_krb5;
    } else if ((code == G_WRONG_MECH) &&
+		!(code = g_verify_token_header(gss_mech_krb5_wrong,
+						(uint32_t *)&(ap_req.length),
+						&ptr, KG_TOK_CTX_AP_REQ,
+						input_token->length, 1))) {
+	mech_used = gss_mech_krb5_wrong;
+   } else if ((code == G_WRONG_MECH) &&
 	      !(code = g_verify_token_header(gss_mech_krb5_old,
 					     (uint32_t *)&(ap_req.length),
 					     &ptr, KG_TOK_CTX_AP_REQ,
@@ -1169,6 +1176,37 @@
 	*/
        memset(&krb_error_data, 0, sizeof(krb_error_data));
 
+       /*
+        * Solaris Kerberos: We need to remap error conditions for buggy
+        * Windows clients if the MS_INTEROP env var has been set.
+        */
+       if ((code == KRB5KRB_AP_ERR_BAD_INTEGRITY ||
+	  code == KRB5KRB_AP_ERR_NOKEY || code == KRB5KRB_AP_ERR_BADKEYVER)
+	  && krb5_getenv("MS_INTEROP")) {
+           code = KRB5KRB_AP_ERR_MODIFIED;
+	   major_status = GSS_S_CONTINUE_NEEDED;
+       }
+
+	    /*
+	     * SUNW17PACresync / Solaris Kerberos
+	     * Set e-data to Windows constant.
+	     * (verified by MSFT)
+	     * 
+	     * This facilitates the Windows CIFS client clock skew
+	     * recovery feature.
+	     */
+       if (code == KRB5KRB_AP_ERR_SKEW && krb5_getenv("MS_INTEROP")) {
+	    char *ms_e_data = "\x30\x05\xa1\x03\x02\x01\x02";
+	    int len = strlen(ms_e_data);
+
+	    krb_error_data.e_data.data = malloc(len);
+	    if (krb_error_data.e_data.data) {
+		    (void) memcpy(krb_error_data.e_data.data, ms_e_data, len);
+		    krb_error_data.e_data.length = len;
+	    }
+	    major_status = GSS_S_CONTINUE_NEEDED;
+	}
+
        code -= ERROR_TABLE_BASE_krb5;
        if (code < 0 || code > 128)
 	   code = 60 /* KRB_ERR_GENERIC */;
@@ -1178,25 +1216,6 @@
 				&krb_error_data.susec);
        krb_error_data.server = cred->princ;
 
-       if (code == KRB5KRB_AP_ERR_SKEW) {
-	    /*
-	     * SUNW17PACresync / Solaris Kerberos
-	     * Set e-data to Windows constant.
-	     * (verified by MSFT)
-	     * 
-	     * This facilitates the Windows CIFS client clock skew
-	     * recovery feature.
-	     */
-	    char *ms_e_data = "\x30\x05\xa1\x03\x02\x01\x02";
-	    int len = strlen(ms_e_data);
-
-	    krb_error_data.e_data.data = malloc(len);
-	    if (krb_error_data.e_data.data) {
-		    (void) memcpy(krb_error_data.e_data.data, ms_e_data, len);
-		    krb_error_data.e_data.length = len;
-	    }
-       }
-
        code = krb5_mk_error(context, &krb_error_data, &scratch);
        if (code)
            goto cleanup;
--- a/usr/src/lib/gss_mechs/mech_krb5/mech/krb5_gss_glue.c	Thu Nov 12 15:28:55 2009 -0800
+++ b/usr/src/lib/gss_mechs/mech_krb5/mech/krb5_gss_glue.c	Thu Nov 12 17:40:34 2009 -0700
@@ -1326,8 +1326,13 @@
 gss_mech_initialize(oid)
      const gss_OID oid;
 {
+    /*
+     * Solaris Kerberos: We also want to use the same functions for KRB5 as
+     * we do for the MS KRB5 (krb5_mechanism_wrong).  So both are valid.
+     */
     /* ensure that the requested oid matches our oid */
-    if (oid == NULL || !g_OID_equal(oid, &krb5_mechanism.mech_type)) {
+    if (oid == NULL || (!g_OID_equal(oid, &krb5_mechanism.mech_type) &&
+	!g_OID_equal(oid, &krb5_mechanism_wrong.mech_type))) {
       (void) syslog(LOG_INFO, "krb5mech: gss_mech_initialize: bad oid");
       return (NULL);
     }
@@ -1379,16 +1384,13 @@
         return major_status;
     }
 
-
     /*
      * SUNW17PACresync / Solaris Kerberos
-     * MIT17 expects just 1 but our testing with Win2008 shows
-     * it returns 2.  So we now handle that and rewhack mem mgmt as appro.
+     * MIT17 allows only count==1 which is correct for pre-Win2008 but
+     * our testing with Win2008 shows count==2 and Win7 count==3.
      */
-    if (data_set == GSS_C_NO_BUFFER_SET ||
-        (data_set->count != 1 && data_set->count != 2)) {
+    if ((data_set == GSS_C_NO_BUFFER_SET) || (data_set->count == 0)) {
 	    gss_release_buffer_set(minor_status, &data_set);
-
 	    return GSS_S_FAILURE;
     }
 
--- a/usr/src/lib/gss_mechs/mech_spnego/mech/spnego_mech.c	Thu Nov 12 15:28:55 2009 -0800
+++ b/usr/src/lib/gss_mechs/mech_spnego/mech/spnego_mech.c	Thu Nov 12 17:40:34 2009 -0700
@@ -2598,6 +2598,21 @@
 		    memcmp(mechs->elements[i].elements,
 			spnego_mechanism.mech_type.elements,
 			spnego_mechanism.mech_type.length)) {
+			/*
+			 * Solaris Kerberos: gss_indicate_mechs is stupid as
+			 * it never inferences any of the related OIDs of the
+			 * mechanisms configured, e.g. KRB5_OLD, KRB5_WRONG.
+			 * We add KRB5_WRONG here so that old MS clients can
+			 * negotiate this mechanism, which allows extensions
+			 * in Kerberos (clock skew adjustment, refresh ccache).
+			 */
+			if (is_kerb_mech(&mechs->elements[i])) {
+			    extern gss_OID_desc * const gss_mech_krb5_wrong;
+
+				major_status =
+				  gss_add_oid_set_member(minor_status,
+				  gss_mech_krb5_wrong, rmechs);
+			}
 
 			major_status = gss_add_oid_set_member(minor_status,
 							      &mechs->elements[i],
@@ -3098,10 +3113,18 @@
 	for (i = 0; i < mechset->count; i++) {
 		gss_OID mech_oid = &mechset->elements[i];
 
+		/*
+		 * Solaris Kerberos: MIT compares against MS' wrong OID, but
+		 * we actually want to select it if the client supports, as this
+		 * will enable features on MS clients that allow credential
+		 * refresh on rekeying and caching system times from servers.
+		 */ 
+#if 0
 		/* Accept wrong mechanism OID from MS clients */
 		if (mech_oid->length == gss_mech_krb5_wrong_oid.length &&
 		    memcmp(mech_oid->elements, gss_mech_krb5_wrong_oid.elements, mech_oid->length) == 0)
-			mech_oid = (gss_OID)&gss_mech_krb5_oid;;
+			mech_oid = (gss_OID)&gss_mech_krb5_oid;
+#endif
 
 		gss_test_oid_set_member(minor_status, mech_oid, supported_mechSet, &present);
 		if (!present)