#
# This patch allows for better interop with MS Windows clients accessing Solaris
# SMB services. It fixes a few memory leaks and double frees found during SMB
# stress testing. The CRs in order:
#
# 15580724 SUNBT6868908 Solaris acceptors should have returned KRB5KRB_AP_...
# 20416772 spnego_gss_accept_sec_context issue with incorrect KRB OID
# 16005842 Should retry SMB authentication upgrade to account for network...
# 15579598 SUNBT6867208 Windows client cannot recover from KRB5KRB_AP_ERR_SKEW..
#
# Note: MIT tickets will subsequently be filed, but the solution may differ from
# what we currently offer in Solaris, because they may want the changes as the
# default behavior therefore removing the dependency on the MS_INTEROP
# environment variable.
# Patch source: in-house
#
--- a/src/lib/gssapi/krb5/accept_sec_context.c
+++ b/src/lib/gssapi/krb5/accept_sec_context.c
@@ -454,8 +454,6 @@ kg_accept_krb5(minor_status, context_handle,
const gss_OID_desc *mech_used = NULL;
OM_uint32 major_status = GSS_S_FAILURE;
OM_uint32 tmp_minor_status;
- krb5_error krb_error_data;
- krb5_data scratch;
gss_cred_id_t defcred = GSS_C_NO_CREDENTIAL;
krb5_gss_cred_id_t deleg_cred = NULL;
krb5int_access kaccess;
@@ -1214,6 +1212,8 @@ fail:
major_status == GSS_S_CONTINUE_NEEDED)) {
unsigned int tmsglen;
int toktype;
+ krb5_error krb_error_data;
+ krb5_data scratch;
/*
* The client is expecting a response, so we can send an
@@ -1221,6 +1221,31 @@ fail:
*/
memset(&krb_error_data, 0, sizeof(krb_error_data));
+ /*
+ * 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)
+ && getenv("MS_INTEROP")) {
+ code = KRB5KRB_AP_ERR_MODIFIED;
+ major_status = GSS_S_CONTINUE_NEEDED;
+ }
+
+ /*
+ * 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 && getenv("MS_INTEROP")) {
+ /* Note that free() must not be called on
+ * krb_error_data.e_data.data */
+ krb_error_data.e_data.data = "\x30\x05\xa1\x03\x02\x01\x02";
+ krb_error_data.e_data.length = 7;
+ major_status = GSS_S_CONTINUE_NEEDED;
+ }
+
code -= ERROR_TABLE_BASE_krb5;
if (code < 0 || code > KRB_ERR_MAX)
code = 60 /* KRB_ERR_GENERIC */;
--- a/src/lib/gssapi/spnego/spnego_mech.c
+++ b/src/lib/gssapi/spnego/spnego_mech.c
@@ -180,6 +180,13 @@ get_negTokenResp(OM_uint32 *, unsigned char *, unsigned int,
static int
is_kerb_mech(gss_OID oid);
+/* encoded OID octet string for NTLMSSP security mechanism */
+#define GSS_MECH_NTLMSSP_OID_LENGTH 10
+#define GSS_MECH_NTLMSSP_OID "\053\006\001\004\001\202\067\002\002\012"
+static gss_OID_desc ntlmssp_oid = {
+ GSS_MECH_NTLMSSP_OID_LENGTH, GSS_MECH_NTLMSSP_OID
+};
+
/* SPNEGO oid structure */
static const gss_OID_desc spnego_oids[] = {
{SPNEGO_OID_LENGTH, SPNEGO_OID},
@@ -1325,6 +1332,7 @@ acc_ctx_new(OM_uint32 *minor_status,
gss_buffer_desc der_mechTypes;
gss_OID mech_wanted;
spnego_gss_ctx_id_t sc = NULL;
+ unsigned int i;
ret = GSS_S_DEFECTIVE_TOKEN;
der_mechTypes.length = 0;
@@ -1347,6 +1355,26 @@ acc_ctx_new(OM_uint32 *minor_status,
*return_token = NO_TOKEN_SEND;
goto cleanup;
}
+
+ /*
+ * We add KRB5_WRONG here so that old MS clients can negotiate this
+ * mechanism, which allows extensions in Kerberos (clock skew
+ * adjustment, refresh ccache).
+ */
+ for (i = 0; i < supported_mechSet->count; i++) {
+ if (is_kerb_mech(&supported_mechSet->elements[i])) {
+ extern gss_OID_desc * const gss_mech_krb5_wrong;
+ ret = gss_add_oid_set_member(minor_status,
+ gss_mech_krb5_wrong,
+ &supported_mechSet);
+ if (ret != GSS_S_COMPLETE) {
+ *return_token = NO_TOKEN_SEND;
+ goto cleanup;
+ }
+ break;
+ }
+ }
+
/*
* Select the best match between the list of mechs
* that the initiator requested and the list that
@@ -3084,6 +3112,7 @@ get_available_mechs(OM_uint32 *minor_status,
gss_OID_set mechs, goodmechs;
gss_OID_set_desc except_attrs;
gss_OID_desc attr_oids[2];
+ char *msinterop = getenv("MS_INTEROP");
attr_oids[0] = *GSS_C_MA_DEPRECATED;
attr_oids[1] = *GSS_C_MA_NOT_DFLT_MECH;
@@ -3105,6 +3134,15 @@ get_available_mechs(OM_uint32 *minor_status,
return (major_status);
}
+ /*
+ * If the required keytab entries for Kerberized SMB service are
+ * missing due to an SMB authentication upgrade failure, SMB daemon
+ * will set MS_INTEROP environmment variable to 1 to ensure only
+ * NTLMSSP security mech is used for negotiation.
+ */
+ if ((msinterop != NULL) && (!strcmp(msinterop, "1")))
+ goto ntlmssp;
+
for (i = 0; i < mechs->count && major_status == GSS_S_COMPLETE; i++) {
if ((mechs->elements[i].length
!= spnego_mechanism.mech_type.length) ||
@@ -3120,6 +3158,25 @@ get_available_mechs(OM_uint32 *minor_status,
}
}
+ntlmssp:
+ /*
+ * Add NTLMSSP OID to the mech OID set only if MS_INTEROP env var has
+ * been set to:
+ * - "1" (NTLMSSP only) or
+ * - "2" (both Krb5 and NTLMSSP)
+ *
+ * This is a requirement until NTLMSSP is implemented as a GSS-API
+ * plugin.
+ */
+ if ((msinterop != NULL) &&
+ (!strcmp(msinterop, "1") || !strcmp(msinterop, "2"))) {
+ major_status = gss_add_oid_set_member(minor_status,
+ &ntlmssp_oid, rmechs);
+
+ if (major_status == GSS_S_COMPLETE)
+ found++;
+ }
+
/*
* If the caller wanted a list of creds returned,
* trim the list of mechanisms down to only those
@@ -3695,9 +3752,17 @@ negotiate_mech(gss_OID_set supported, gss_OID_set received,
for (i = 0; i < received->count; i++) {
gss_OID mech_oid = &received->elements[i];
+ /*
+ * 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 (g_OID_equal(mech_oid, &gss_mech_krb5_wrong_oid))
mech_oid = (gss_OID)&gss_mech_krb5_oid;
+#endif
for (j = 0; j < supported->count; j++) {
if (g_OID_equal(mech_oid, &supported->elements[j])) {