Better support for "non-standard" GSS mechs
authorNicolas Williams <nico@twosigma.com>
Fri, 17 Apr 2020 03:53:22 +0000 (22:53 -0500)
committerNico Williams <nico@cryptonector.com>
Fri, 17 Apr 2020 19:37:39 +0000 (14:37 -0500)
If an initial security context token doesn't have a standard header per
RFC2743 then try all mechanisms until one succeeds or all fail.

We still try to guess NTLMSSP, raw Kerberos, and SPNEGO, from tasting
the initial security context token.

lib/gssapi/krb5/accept_sec_context.c
lib/gssapi/mech/gss_accept_sec_context.c
lib/gssapi/ntlm/accept_sec_context.c
lib/gssapi/ntlm/init_sec_context.c

index 9100c71b971e45ee54baaf5ddd63b6a32ed5a23c..b5aa5f1f8e36b22983bcfaabbb672a88d30c1d79 100644 (file)
@@ -373,6 +373,13 @@ gsskrb5_acceptor_start(OM_uint32 * minor_status,
                                GSS_KRB5_MECHANISM);
 
     if (ret) {
+       /* Could be a raw AP-REQ (check for APPLICATION tag) */
+       if (input_token_buffer->length == 0 ||
+           ((const uint8_t *)input_token_buffer->value)[0] != 0x6E) {
+           *minor_status = ASN1_MISPLACED_FIELD;
+           return GSS_S_DEFECTIVE_TOKEN;
+       }
+
        /* Assume that there is no OID wrapping. */
        indata.length   = input_token_buffer->length;
        indata.data     = input_token_buffer->value;
index 69de22f53a26608a3b121ad1bd5ea9d4b12856be..2189dbe3be131624e621ab37de163cb0246beeac 100644 (file)
@@ -93,36 +93,22 @@ parse_header(const gss_buffer_t input_token, gss_OID *mech_oid)
        return GSS_S_COMPLETE;
 }
 
-static OM_uint32
-choose_mech(const gss_buffer_t input, gss_OID *mech_oid)
+static gss_OID
+choose_mech(const gss_buffer_t input)
 {
        OM_uint32 status;
+        gss_OID mech_oid = GSS_C_NO_OID;
 
        /*
         * First try to parse the gssapi token header and see if it's a
         * correct header, use that in the first hand.
         */
 
-       status = parse_header(input, mech_oid);
-       if (status == GSS_S_COMPLETE)
-           return GSS_S_COMPLETE;
-
-       /*
-        * Lets guess what mech is really is, callback function to mech ??
-        */
+       status = parse_header(input, &mech_oid);
+       if (status == GSS_S_COMPLETE && mech_oid != GSS_C_NO_OID)
+           return mech_oid;
 
-       if (input->length > 8 &&
-           memcmp((const char *)input->value, "NTLMSSP\x00", 8) == 0)
-       {
-               *mech_oid = &__gss_ntlm_mechanism_oid_desc;
-               return GSS_S_COMPLETE;
-       } else if (input->length != 0 &&
-                  ((const char *)input->value)[0] == 0x6E)
-       {
-               /* Could be a raw AP-REQ (check for APPLICATION tag) */
-               *mech_oid = &__gss_krb5_mechanism_oid_desc;
-               return GSS_S_COMPLETE;
-       } else if (input->length == 0) {
+       if (input->length == 0) {
                /*
                 * There is the a wierd mode of SPNEGO (in CIFS and
                 * SASL GSS-SPENGO where the first token is zero
@@ -132,13 +118,12 @@ choose_mech(const gss_buffer_t input, gss_OID *mech_oid)
                 * http://msdn.microsoft.com/en-us/library/cc213114.aspx
                 * "NegTokenInit2 Variation for Server-Initiation"
                 */
-               *mech_oid = &__gss_spnego_mechanism_oid_desc;
-               return GSS_S_COMPLETE;
+               return &__gss_spnego_mechanism_oid_desc;
        }
 
        _gss_mg_log(10, "Don't have client request mech");
 
-       return status;
+       return mech_oid;
 }
 
 
@@ -156,14 +141,19 @@ gss_accept_sec_context(OM_uint32 *minor_status,
     gss_cred_id_t *delegated_cred_handle)
 {
        OM_uint32 major_status, mech_ret_flags, junk;
-       gssapi_mech_interface m;
+       gssapi_mech_interface m = NULL;
        struct _gss_context *ctx = (struct _gss_context *) *context_handle;
        struct _gss_cred *cred = (struct _gss_cred *) acceptor_cred_handle;
        struct _gss_mechanism_cred *mc;
+        gss_buffer_desc defective_token_error;
        gss_const_cred_id_t acceptor_mc;
        gss_cred_id_t delegated_mc = GSS_C_NO_CREDENTIAL;
        gss_name_t src_mn = GSS_C_NO_NAME;
        gss_OID mech_ret_type = GSS_C_NO_OID;
+        int initial = !!(*context_handle == GSS_C_NO_CONTEXT);
+
+        defective_token_error.length = 0;
+        defective_token_error.value = NULL;
 
        *minor_status = 0;
        if (src_name)
@@ -182,34 +172,125 @@ gss_accept_sec_context(OM_uint32 *minor_status,
         * If this is the first call (*context_handle is NULL), we must
         * parse the input token to figure out the mechanism to use.
         */
-       if (*context_handle == GSS_C_NO_CONTEXT) {
+       if (initial) {
                gss_OID mech_oid;
 
-               major_status = choose_mech(input_token, &mech_oid);
-               if (major_status != GSS_S_COMPLETE)
-                       return major_status;
+               mech_oid = choose_mech(input_token);
 
-               /*
-                * Now that we have a mechanism, we can find the
-                * implementation.
-                */
+                /*
+                 * If mech_oid == GSS_C_NO_OID then the mech is non-standard
+                 * and we have to try all mechs (that we have a cred element
+                 * for, if we have a cred).
+                 */
                ctx = malloc(sizeof(struct _gss_context));
                if (!ctx) {
                        *minor_status = ENOMEM;
                        return (GSS_S_DEFECTIVE_TOKEN);
                }
                memset(ctx, 0, sizeof(struct _gss_context));
-               m = ctx->gc_mech = __gss_get_mechanism(mech_oid);
-               if (!m) {
-                       free(ctx);
-                       _gss_mg_log(10, "mechanism client used is unknown");
-                       return (GSS_S_BAD_MECH);
-               }
+                if (mech_oid != GSS_C_NO_OID) {
+                        m = ctx->gc_mech = __gss_get_mechanism(mech_oid);
+                        if (!m) {
+                                free(ctx);
+                                _gss_mg_log(10, "mechanism client used is unknown");
+                                return (GSS_S_BAD_MECH);
+                        }
+                }
                *context_handle = (gss_ctx_id_t) ctx;
        } else {
                m = ctx->gc_mech;
        }
 
+        if (initial && !m && acceptor_cred_handle == GSS_C_NO_CREDENTIAL) {
+                /*
+                 * No header, not a standard mechanism.  Try all the mechanisms
+                 * (because default credential).
+                 */
+                struct _gss_mech_switch *ms;
+
+                _gss_load_mech();
+                acceptor_mc = GSS_C_NO_CREDENTIAL;
+                HEIM_TAILQ_FOREACH(ms, &_gss_mechs, gm_link) {
+                        m = &ms->gm_mech;
+                        mech_ret_flags = 0;
+                        major_status = m->gm_accept_sec_context(minor_status,
+                            &ctx->gc_ctx,
+                            acceptor_mc,
+                            input_token,
+                            input_chan_bindings,
+                            &src_mn,
+                            &mech_ret_type,
+                            output_token,
+                            &mech_ret_flags,
+                            time_rec,
+                            &delegated_mc);
+                        if (major_status == GSS_S_DEFECTIVE_TOKEN) {
+                                /*
+                                 * Try to retain and output one error token for
+                                 * GSS_S_DEFECTIVE_TOKEN.  The first one.
+                                 */
+                                if (output_token->length &&
+                                    defective_token_error.length == 0) {
+                                    defective_token_error = *output_token;
+                                    output_token->length = 0;
+                                    output_token->value = NULL;
+                                }
+                                gss_release_buffer(&junk, output_token);
+                                continue;
+                        }
+                        gss_release_buffer(&junk, &defective_token_error);
+                        ctx->gc_mech = m;
+                        goto got_one;
+                }
+                m = NULL;
+                acceptor_mc = GSS_C_NO_CREDENTIAL;
+        } else if (initial && !m) {
+                /*
+                 * No header, not a standard mechanism.  Try all the mechanisms
+                 * that we have a credential element for if we have a
+                 * non-default credential.
+                 */
+               HEIM_TAILQ_FOREACH(mc, &cred->gc_mc, gmc_link) {
+                        m = mc->gmc_mech;
+                        acceptor_mc = (m->gm_flags & GM_USE_MG_CRED) ?
+                            acceptor_cred_handle : mc->gmc_cred;
+                        mech_ret_flags = 0;
+                        major_status = m->gm_accept_sec_context(minor_status,
+                            &ctx->gc_ctx,
+                            acceptor_mc,
+                            input_token,
+                            input_chan_bindings,
+                            &src_mn,
+                            &mech_ret_type,
+                            output_token,
+                            &mech_ret_flags,
+                            time_rec,
+                            &delegated_mc);
+                        if (major_status == GSS_S_DEFECTIVE_TOKEN) {
+                                if (output_token->length &&
+                                    defective_token_error.length == 0) {
+                                    defective_token_error = *output_token;
+                                    output_token->length = 0;
+                                    output_token->value = NULL;
+                                }
+                                gss_release_buffer(&junk, output_token);
+                                continue;
+                        }
+                        gss_release_buffer(&junk, &defective_token_error);
+                        ctx->gc_mech = m;
+                        goto got_one;
+                }
+                m = NULL;
+                acceptor_mc = GSS_C_NO_CREDENTIAL;
+        }
+
+        if (m == NULL) {
+                _gss_mg_log(10, "No mechanism accepted the non-standard initial security context token");
+                *output_token = defective_token_error;
+                free(ctx);
+                return GSS_S_BAD_MECH;
+        }
+
        if (m->gm_flags & GM_USE_MG_CRED) {
                acceptor_mc = acceptor_cred_handle;
        } else if (cred) {
@@ -242,6 +323,8 @@ gss_accept_sec_context(OM_uint32 *minor_status,
            &mech_ret_flags,
            time_rec,
            &delegated_mc);
+
+got_one:
        if (major_status != GSS_S_COMPLETE &&
            major_status != GSS_S_CONTINUE_NEEDED)
        {
index b169f788dc02785a509a7e9f5a6710ebfb12bad2..d6300006b21009aa295bb069cfdcde7234541470 100644 (file)
@@ -139,7 +139,7 @@ _gss_ntlm_accept_sec_context
        if (ret) {
            _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
            *minor_status = ret;
-           return GSS_S_FAILURE;
+           return GSS_S_DEFECTIVE_TOKEN;
        }
 
        if ((type1.flags & NTLM_NEG_UNICODE) == 0) {
@@ -195,7 +195,7 @@ _gss_ntlm_accept_sec_context
        if (ret) {
            _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
            *minor_status = ret;
-           return GSS_S_FAILURE;
+           return GSS_S_DEFECTIVE_TOKEN;
        }
 
        maj_stat = (*ctx->server->nsi_type3)(minor_status,
index 954057e2a5bc753294f09e909f752a2a450b5d4c..a0c4134b31f193b577bf715959461cf33c9569d4 100644 (file)
@@ -347,7 +347,7 @@ _gss_ntlm_init_sec_context
        if (ret) {
            _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
            *minor_status = ret;
-           return GSS_S_FAILURE;
+           return GSS_S_DEFECTIVE_TOKEN;
        }
 
        ctx->flags = type2.flags;
@@ -437,7 +437,7 @@ _gss_ntlm_init_sec_context
                _gss_ntlm_delete_sec_context(minor_status,
                                             context_handle, NULL);
                *minor_status = ret;
-               return GSS_S_FAILURE;
+               return GSS_S_DEFECTIVE_TOKEN;
            }
 
            if (ti.domainname && strcmp(ti.domainname, name->domain) != 0) {