Blob Blame History Raw
Adjusted to apply to 1.11.5.

commit 8255613476d4c1583a5e810b50444f188fde871f
Author: Greg Hudson <ghudson@mit.edu>
Date:   Mon Feb 3 21:11:34 2014 -0500

    Properly reflect MS krb5 mech in SPNEGO acceptor
    
    r25590 changed negotiate_mech() to return an alias into the acceptor's
    mech set, with the unfortunate side effect of transforming the
    erroneous Microsoft krb5 mech OID into the correct krb5 mech OID,
    meaning that we answer with a different OID than the requested one.
    Return an alias into the initiator's mech set instead, and store that
    in mech_set field the SPNEGO context.  The acceptor code only uses
    mech_set to hold the allocated storage pointed into by internal_mech,
    so this change is safe.
    
    ticket: 7858
    target_version: 1.12.2
    tags: pullup

diff --git a/src/lib/gssapi/spnego/spnego_mech.c b/src/lib/gssapi/spnego/spnego_mech.c
index 7e4bf90..7529c74 100644
--- a/src/lib/gssapi/spnego/spnego_mech.c
+++ b/src/lib/gssapi/spnego/spnego_mech.c
@@ -1388,8 +1388,8 @@ acc_ctx_new(OM_uint32 *minor_status,
 		*return_token = NO_TOKEN_SEND;
 		goto cleanup;
 	}
-	sc->mech_set = supported_mechSet;
-	supported_mechSet = GSS_C_NO_OID_SET;
+	sc->mech_set = mechTypes;
+	mechTypes = GSS_C_NO_OID_SET;
 	sc->internal_mech = mech_wanted;
 	sc->DER_mechTypes = der_mechTypes;
 	der_mechTypes.length = 0;
@@ -3538,7 +3538,7 @@ put_negResult(unsigned char **buf_out, OM_uint32 negResult,
  * is set to ACCEPT_INCOMPLETE if it's the first mech, REQUEST_MIC if
  * it's not the first mech, otherwise we return NULL and negResult
  * is set to REJECT. The returned pointer is an alias into
- * supported->elements and should not be freed.
+ * received->elements and should not be freed.
  *
  * NOTE: There is currently no way to specify a preference order of
  * mechanisms supported by the acceptor.
@@ -3560,7 +3560,7 @@ negotiate_mech(gss_OID_set supported, gss_OID_set received,
 			if (g_OID_equal(mech_oid, &supported->elements[j])) {
 				*negResult = (i == 0) ? ACCEPT_INCOMPLETE :
 					REQUEST_MIC;
-				return &supported->elements[j];
+				return &received->elements[i];
 			}
 		}
 	}

commit 53cfb8327c452bd72a8e915338fb5ec838079cd3
Author: Greg Hudson <ghudson@mit.edu>
Date:   Mon Feb 3 20:59:54 2014 -0500

    Test SPNEGO acceptor response to MS krb5 mech OID
    
    In t_spnego.c, add code to make a SPNEGO request with the erroneous
    Microsoft OID value and examine the response to make sure that it uses
    the same OID value as the request did.  The token and tmp variables
    were unused, so rename them to itok and atok for the purpose of the
    new test code.
    
    ticket: 7858
    target_version: 1.12.2
    tags: pullup

diff --git a/src/tests/gssapi/t_spnego.c b/src/tests/gssapi/t_spnego.c
index cbf720b..ca05848 100644
--- a/src/tests/gssapi/t_spnego.c
+++ b/src/tests/gssapi/t_spnego.c
@@ -27,9 +27,15 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <assert.h>
 
 #include "common.h"
 
+static gss_OID_desc mech_krb5_wrong = {
+    9, "\052\206\110\202\367\022\001\002\002"
+};
+gss_OID_set_desc mechset_krb5_wrong = { 1, &mech_krb5_wrong };
+
 /*
  * Test program for SPNEGO and gss_set_neg_mechs
  *
@@ -44,13 +50,16 @@ main(int argc, char *argv[])
 {
     OM_uint32 minor, major;
     gss_cred_id_t verifier_cred_handle = GSS_C_NO_CREDENTIAL;
+    gss_cred_id_t initiator_cred_handle = GSS_C_NO_CREDENTIAL;
     gss_OID_set actual_mechs = GSS_C_NO_OID_SET;
     gss_buffer_desc token = GSS_C_EMPTY_BUFFER, tmp = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc itok = GSS_C_EMPTY_BUFFER, atok = GSS_C_EMPTY_BUFFER;
     gss_ctx_id_t initiator_context = GSS_C_NO_CONTEXT;
     gss_ctx_id_t acceptor_context = GSS_C_NO_CONTEXT;
     gss_name_t target_name, source_name = GSS_C_NO_NAME;
     OM_uint32 time_rec;
     gss_OID mech = GSS_C_NO_OID;
+    const unsigned char *atok_oid;
 
     if (argc < 2 || argc > 3) {
         fprintf(stderr, "Usage: %s target_name [keytab]\n", argv[0]);
@@ -83,10 +91,59 @@ main(int argc, char *argv[])
 
     (void)gss_delete_sec_context(&minor, &acceptor_context, NULL);
     (void)gss_release_name(&minor, &source_name);
-    (void)gss_release_name(&minor, &target_name);
-    (void)gss_release_buffer(&minor, &token);
-    (void)gss_release_buffer(&minor, &tmp);
     (void)gss_release_cred(&minor, &verifier_cred_handle);
     (void)gss_release_oid_set(&minor, &actual_mechs);
+
+    /*
+     * Test that the SPNEGO acceptor code properly reflects back the erroneous
+     * Microsoft mech OID in the supportedMech field of the NegTokenResp
+     * message.  Our initiator code doesn't care (it treats all variants of the
+     * krb5 mech as equivalent when comparing the supportedMech response to its
+     * first-choice mech), so we have to look directly at the DER encoding of
+     * the response token.  If we don't request mutual authentication, the
+     * SPNEGO reply will contain no underlying mech token, so the encoding of
+     * the correct NegotiationToken response is completely predictable:
+     *
+     *   A1 14 (choice 1, length 20, meaning negTokenResp)
+     *     30 12 (sequence, length 18)
+     *       A0 03 (context tag 0, length 3)
+     *         0A 01 00 (enumerated value 0, meaning accept-completed)
+     *       A1 0B (context tag 1, length 11)
+     *         06 09 (object identifier, length 9)
+     *            2A 86 48 82 F7 12 01 02 02 (the erroneous krb5 OID)
+     *
+     * So we can just compare the length to 22 and the nine bytes at offset 13
+     * to the expected OID.
+     */
+    major = gss_acquire_cred(&minor, GSS_C_NO_NAME, GSS_C_INDEFINITE,
+                             &mechset_spnego, GSS_C_INITIATE,
+                             &initiator_cred_handle, NULL, NULL);
+    check_gsserr("gss_acquire_cred(2)", major, minor);
+    major = gss_set_neg_mechs(&minor, initiator_cred_handle,
+                              &mechset_krb5_wrong);
+    check_gsserr("gss_set_neg_mechs(2)", major, minor);
+    major = gss_init_sec_context(&minor, initiator_cred_handle,
+                                 &initiator_context, target_name, &mech_spnego,
+                                 GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG,
+                                 GSS_C_INDEFINITE,
+                                 GSS_C_NO_CHANNEL_BINDINGS, &atok, NULL, &itok,
+                                 NULL, NULL);
+    check_gsserr("gss_init_sec_context", major, minor);
+    assert(major == GSS_S_CONTINUE_NEEDED);
+    major = gss_accept_sec_context(&minor, &acceptor_context,
+                                   GSS_C_NO_CREDENTIAL, &itok,
+                                   GSS_C_NO_CHANNEL_BINDINGS, NULL,
+                                   NULL, &atok, NULL, NULL, NULL);
+    assert(atok.length == 22);
+    atok_oid = (unsigned char *)atok.value + 13;
+    assert(memcmp(atok_oid, mech_krb5_wrong.elements, 9) == 0);
+    check_gsserr("gss_accept_sec_context", major, minor);
+
+    (void)gss_delete_sec_context(&minor, &initiator_context, NULL);
+    (void)gss_delete_sec_context(&minor, &acceptor_context, NULL);
+    (void)gss_release_cred(&minor, &initiator_cred_handle);
+    (void)gss_release_name(&minor, &target_name);
+    (void)gss_release_buffer(&minor, &itok);
+    (void)gss_release_buffer(&minor, &atok);
     return 0;
 }