pull up r23715 from trunk
authorTom Yu <tlyu@mit.edu>
Fri, 12 Feb 2010 20:28:47 +0000 (20:28 +0000)
committerTom Yu <tlyu@mit.edu>
Fri, 12 Feb 2010 20:28:47 +0000 (20:28 +0000)
 ------------------------------------------------------------------------
 r23715 | ghudson | 2010-02-10 18:44:18 -0500 (Wed, 10 Feb 2010) | 14 lines

 ticket: 6658
 subject: Implement gss_set_neg_mechs
 target_version: 1.8
 tags: pullup

 Implement gss_set_neg_mechs in SPNEGO by intersecting the provided
 mech set with the mechanisms available in the union credential.  As
 we now need space to hold the mech set, the SPNEGO credential is now
 a structure and not just a mechglue credential.

 t_spnego.c is a test program which exercises the new logic.  Like the
 other GSSAPI tests, it is not run as part of "make check" at this
 time.

ticket: 6658
version_fixed: 1.8
status: resolved

git-svn-id: svn://anonsvn.mit.edu/krb5/branches/krb5-1-8@23720 dc483132-0cff-0310-8789-dd5450dbe970

src/lib/gssapi/generic/gssapi.hin
src/lib/gssapi/krb5/gssapi_krb5.c
src/lib/gssapi/libgssapi_krb5.exports
src/lib/gssapi/mechglue/Makefile.in
src/lib/gssapi/mechglue/g_initialize.c
src/lib/gssapi/mechglue/g_set_neg_mechs.c [new file with mode: 0644]
src/lib/gssapi/mechglue/mglueP.h
src/lib/gssapi/spnego/gssapiP_spnego.h
src/lib/gssapi/spnego/spnego_mech.c
src/tests/gssapi/Makefile.in
src/tests/gssapi/t_spnego.c [new file with mode: 0644]

index e60d04d87297b31ec1fa51b1b4ab166e46d7477c..fb82e3c4f5131e84315ab533bdc600fc3a7be184 100644 (file)
@@ -805,6 +805,12 @@ gss_store_cred(
     gss_OID_set *,      /* elements_stored */
     gss_cred_usage_t *);/* cred_usage_stored */
 
+OM_uint32 KRB5_CALLCONV
+gss_set_neg_mechs(
+    OM_uint32 *,        /* minor_status */
+    gss_cred_id_t,      /* cred_handle */
+    const gss_OID_set); /* mech_set */
+
 #if TARGET_OS_MAC
 #    pragma pack(pop)
 #endif
index b68bc9a737f6057eee4adf466f775f7fc140950a..9e5ba76d9f8fc2f5756705794c493299d36e8eb1 100644 (file)
@@ -694,6 +694,7 @@ static struct gss_config krb5_mechanism = {
     krb5_gss_map_name_to_any,
     krb5_gss_release_any_name_mapping,
     krb5_gss_pseudo_random,
+    NULL,               /* set_neg_mechs */
 };
 
 
index e7f7b3609d9bcd866972761b137581ee924a6430..e038f507693ed1351712ebd869460e2eb63ec848 100644 (file)
@@ -80,6 +80,7 @@ gss_release_oid
 gss_release_oid_set
 gss_seal
 gss_set_name_attribute
+gss_set_neg_mechs
 gss_set_sec_context_option
 gss_sign
 gss_store_cred
index 0858a4a989fede5c6e44adf3dacf4fe96d4e85bc..f43571145aadf48a500892c30cf277fe60323ba8 100644 (file)
@@ -54,6 +54,7 @@ SRCS = \
        $(srcdir)/g_set_context_option.c \
        $(srcdir)/g_set_cred_option.c \
        $(srcdir)/g_set_name_attr.c \
+       $(srcdir)/g_set_neg_mechs.c \
        $(srcdir)/g_sign.c \
        $(srcdir)/g_store_cred.c \
        $(srcdir)/g_unseal.c \
@@ -108,6 +109,7 @@ OBJS = \
        $(OUTPRE)g_set_context_option.$(OBJEXT) \
        $(OUTPRE)g_set_cred_option.$(OBJEXT) \
        $(OUTPRE)g_set_name_attr.$(OBJEXT) \
+       $(OUTPRE)g_set_neg_mechs.$(OBJEXT) \
        $(OUTPRE)g_sign.$(OBJEXT) \
        $(OUTPRE)g_store_cred.$(OBJEXT) \
        $(OUTPRE)g_unseal.$(OBJEXT) \
@@ -162,6 +164,7 @@ STLIBOBJS = \
        g_set_context_option.o \
        g_set_cred_option.o \
        g_set_name_attr.o \
+       g_set_neg_mechs.o \
        g_sign.o \
        g_store_cred.o \
        g_unseal.o \
index e01d174742cd8a27108361363a4a87776a53c732..70c2203912912d7c0ac30515d932e95e4703bb33 100644 (file)
@@ -775,6 +775,8 @@ build_dynamicMech(void *dl, const gss_OID mech_type)
        GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_release_any_name_mapping);
         /* RFC 4401 (introduced in 1.8) */
        GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_pseudo_random);
+       /* RFC 4178 (introduced in 1.8; gss_get_neg_mechs not implemented) */
+       GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_set_neg_mechs);
 
        assert(mech_type != GSS_C_NO_OID);
 
diff --git a/src/lib/gssapi/mechglue/g_set_neg_mechs.c b/src/lib/gssapi/mechglue/g_set_neg_mechs.c
new file mode 100644 (file)
index 0000000..1675376
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * lib/gssapi/mechglue/g_set_neg_mechs.c
+ *
+ * Copyright (C) 2010 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ *
+ * Glue routine for gss_set_neg_mechs.
+ */
+
+#include "mglueP.h"
+
+OM_uint32 KRB5_CALLCONV
+gss_set_neg_mechs(OM_uint32 *minor_status,
+                 gss_cred_id_t cred_handle,
+                 const gss_OID_set mech_set)
+{
+    gss_union_cred_t   union_cred;
+    gss_mechanism      mech;
+    int                        i, avail;
+    OM_uint32          status;
+
+    if (minor_status == NULL)
+       return GSS_S_CALL_INACCESSIBLE_WRITE;
+
+    if (cred_handle == GSS_C_NO_CREDENTIAL)
+       return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED;
+
+    *minor_status = 0;
+
+    union_cred = (gss_union_cred_t) cred_handle;
+
+    avail = 0;
+    status = GSS_S_COMPLETE;
+    for (i = 0; i < union_cred->count; i++) {
+       mech = gssint_get_mechanism(&union_cred->mechs_array[i]);
+       if (mech == NULL) {
+           status = GSS_S_BAD_MECH;
+           break;
+       }
+
+       if (mech->gss_set_neg_mechs == NULL)
+           continue;
+
+       avail = 1;
+       status = (mech->gss_set_neg_mechs)(minor_status,
+                                          union_cred->cred_array[i],
+                                          mech_set);
+       if (status != GSS_S_COMPLETE) {
+           map_error(minor_status, mech);
+           break;
+       }
+    }
+
+    if (status == GSS_S_COMPLETE && !avail)
+       return GSS_S_UNAVAILABLE;
+    return status;
+}
index 517ca481b99c245b853f9b055a380381786a8bcd..3769caf8768b1373e8fc1203ff1c8a83744ba131 100644 (file)
@@ -583,6 +583,12 @@ typedef struct gss_config {
             gss_buffer_t                /* prf_out */
         /* */);
 
+       OM_uint32       (*gss_set_neg_mechs)
+       (
+           OM_uint32 *,                /* minor_status */
+           gss_cred_id_t,              /* cred_handle */
+           const gss_OID_set           /* mech_set */
+       /* */);
 } *gss_mechanism;
 
 /* This structure MUST NOT be used by any code outside libgss */
index 4bfe863f9958a85aef18ae3e8374a6e731651fbb..a1cd2b4a63fff58b04743a30ac8c212fd1933980 100644 (file)
@@ -82,6 +82,12 @@ typedef struct {
        gss_name_t      mech_name;
 } spnego_name_desc, *spnego_name_t;
 
+/* Structure for credential */
+typedef struct {
+       gss_cred_id_t mcred;    /* mechglue union of obtainable creds */
+       gss_OID_set neg_mechs;  /* app-specified list of allowable mechs */
+} spnego_gss_cred_id_rec, *spnego_gss_cred_id_t;
+
 /* Structure for context handle */
 typedef struct {
        OM_uint32       magic_num;
@@ -539,6 +545,14 @@ spnego_gss_pseudo_random
        gss_buffer_t prf_out
 );
 
+OM_uint32
+spnego_gss_set_neg_mechs
+(
+       OM_uint32 *minor_status,
+       gss_cred_id_t cred_handle,
+       const gss_OID_set mech_list
+);
+
 #ifdef __cplusplus
 }
 #endif
index 669b343d978b69e622d9f64ad833e39197f6430b..60fadd5e5ba84d6fd22096a455747c09f5abcc2d 100644 (file)
@@ -103,6 +103,8 @@ static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);
 static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);
 static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t,
        gss_cred_usage_t, gss_cred_id_t *, gss_OID_set *);
+static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
+                                     gss_cred_usage_t, gss_OID_set *);
 static void release_spnego_ctx(spnego_gss_ctx_id_t *);
 static void check_spnego_options(spnego_gss_ctx_id_t);
 static spnego_gss_ctx_id_t create_spnego_ctx(void);
@@ -119,7 +121,7 @@ handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
           gss_buffer_t *, OM_uint32 *, send_token_flag *);
 
 static OM_uint32
-init_ctx_new(OM_uint32 *, gss_cred_id_t, gss_ctx_id_t *,
+init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
             gss_OID_set *, send_token_flag *);
 static OM_uint32
 init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
@@ -134,14 +136,14 @@ init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
                  gss_OID, gss_buffer_t *, gss_buffer_t *,
                  OM_uint32 *, send_token_flag *);
 static OM_uint32
-init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, gss_cred_id_t,
+init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
                   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
                   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
                   OM_uint32 *, send_token_flag *);
 
 static OM_uint32
 acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
-           gss_cred_id_t, gss_buffer_t *,
+           spnego_gss_cred_id_t, gss_buffer_t *,
            gss_buffer_t *, OM_uint32 *, send_token_flag *);
 static OM_uint32
 acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
@@ -151,7 +153,7 @@ static OM_uint32
 acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
                OM_uint32 *, send_token_flag *);
 static OM_uint32
-acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, gss_cred_id_t,
+acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
                 gss_buffer_t, gss_OID *, gss_buffer_t,
                 OM_uint32 *, OM_uint32 *, gss_cred_id_t *,
                 OM_uint32 *, send_token_flag *);
@@ -195,10 +197,10 @@ static const gss_OID_set_desc spnego_oidsets[] = {
 };
 const gss_OID_set_desc * const gss_mech_set_spnego = spnego_oidsets+0;
 
-static int make_NegHints(OM_uint32 *, gss_cred_id_t, gss_buffer_t *);
+static int make_NegHints(OM_uint32 *, spnego_gss_cred_id_t, gss_buffer_t *);
 static int put_neg_hints(unsigned char **, gss_buffer_t, unsigned int);
 static OM_uint32
-acc_ctx_hints(OM_uint32 *, gss_ctx_id_t *, gss_cred_id_t,
+acc_ctx_hints(OM_uint32 *, gss_ctx_id_t *, spnego_gss_cred_id_t,
              gss_buffer_t *, OM_uint32 *, send_token_flag *);
 
 /*
@@ -269,6 +271,7 @@ static struct gss_config spnego_mechanism =
        spnego_gss_map_name_to_any,
        spnego_gss_release_any_name_mapping,
        spnego_gss_pseudo_random,
+       spnego_gss_set_neg_mechs,
 };
 
 #ifdef _GSS_STATIC_LINK
@@ -323,6 +326,8 @@ spnego_gss_acquire_cred(OM_uint32 *minor_status,
 {
        OM_uint32 status;
        gss_OID_set amechs;
+       gss_cred_id_t mcred = NULL;
+       spnego_gss_cred_id_t spcred = NULL;
        dsyslog("Entering spnego_gss_acquire_cred\n");
 
        if (actual_mechs)
@@ -331,6 +336,13 @@ spnego_gss_acquire_cred(OM_uint32 *minor_status,
        if (time_rec)
                *time_rec = 0;
 
+       spcred = malloc(sizeof(spnego_gss_cred_id_rec));
+       if (spcred == NULL) {
+               *minor_status = ENOMEM;
+               return (GSS_S_FAILURE);
+       }
+       spcred->neg_mechs = GSS_C_NULL_OID_SET;
+
        /*
         * If the user did not specify a list of mechs,
         * use get_available_mechs to collect a list of
@@ -339,7 +351,7 @@ spnego_gss_acquire_cred(OM_uint32 *minor_status,
        if (desired_mechs == GSS_C_NULL_OID_SET) {
                status = get_available_mechs(minor_status,
                                desired_name, cred_usage,
-                               output_cred_handle, &amechs);
+                               &mcred, &amechs);
        } else {
                /*
                 * The caller gave a specific list of mechanisms,
@@ -350,8 +362,7 @@ spnego_gss_acquire_cred(OM_uint32 *minor_status,
                status = gss_acquire_cred(minor_status,
                                desired_name, time_req,
                                desired_mechs, cred_usage,
-                               output_cred_handle, &amechs,
-                               time_rec);
+                               &mcred, &amechs, time_rec);
        }
 
        if (actual_mechs && amechs != GSS_C_NULL_OID_SET) {
@@ -359,6 +370,14 @@ spnego_gss_acquire_cred(OM_uint32 *minor_status,
        }
        (void) gss_release_oid_set(minor_status, &amechs);
 
+       if (status == GSS_S_COMPLETE) {
+               spcred->mcred = mcred;
+               *output_cred_handle = (gss_cred_id_t)spcred;
+       } else {
+               free(spcred);
+               *output_cred_handle = GSS_C_NO_CREDENTIAL;
+       }
+
        dsyslog("Leaving spnego_gss_acquire_cred\n");
        return (status);
 }
@@ -368,7 +387,7 @@ OM_uint32
 spnego_gss_release_cred(OM_uint32 *minor_status,
                        gss_cred_id_t *cred_handle)
 {
-       OM_uint32 status;
+       spnego_gss_cred_id_t spcred = NULL;
 
        dsyslog("Entering spnego_gss_release_cred\n");
 
@@ -380,10 +399,14 @@ spnego_gss_release_cred(OM_uint32 *minor_status,
        if (*cred_handle == GSS_C_NO_CREDENTIAL)
                return (GSS_S_COMPLETE);
 
-       status = gss_release_cred(minor_status, cred_handle);
+       spcred = (spnego_gss_cred_id_t)*cred_handle;
+       *cred_handle = GSS_C_NO_CREDENTIAL;
+       gss_release_oid_set(minor_status, &spcred->neg_mechs);
+       gss_release_cred(minor_status, &spcred->mcred);
+       free(spcred);
 
        dsyslog("Leaving spnego_gss_release_cred\n");
-       return (status);
+       return (GSS_S_COMPLETE);
 }
 
 static void
@@ -540,28 +563,17 @@ process_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
  */
 static OM_uint32
 init_ctx_new(OM_uint32 *minor_status,
-            gss_cred_id_t cred,
+            spnego_gss_cred_id_t spcred,
             gss_ctx_id_t *ctx,
             gss_OID_set *mechSet,
             send_token_flag *tokflag)
 {
        OM_uint32 ret, tmpmin;
-       gss_cred_id_t creds = GSS_C_NO_CREDENTIAL;
        spnego_gss_ctx_id_t sc = NULL;
 
        /* determine negotiation mech set */
-       if (cred == GSS_C_NO_CREDENTIAL) {
-               ret = get_available_mechs(minor_status, GSS_C_NO_NAME,
-                                         GSS_C_INITIATE, &creds, mechSet);
-               gss_release_cred(&tmpmin, &creds);
-       } else {
-               /*
-                * Use the list of mechs included in the cred that we
-                * were given.
-                */
-               ret = gss_inquire_cred(minor_status, cred,
-                                      NULL, NULL, NULL, mechSet);
-       }
+       ret = get_negotiable_mechs(minor_status, spcred, GSS_C_INITIATE,
+                                  mechSet);
        if (ret != GSS_S_COMPLETE)
                return ret;
 
@@ -795,7 +807,7 @@ init_ctx_reselect(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
 static OM_uint32
 init_ctx_call_init(OM_uint32 *minor_status,
                   spnego_gss_ctx_id_t sc,
-                  gss_cred_id_t claimant_cred_handle,
+                  spnego_gss_cred_id_t spcred,
                   gss_name_t target_name,
                   OM_uint32 req_flags,
                   OM_uint32 time_req,
@@ -808,9 +820,11 @@ init_ctx_call_init(OM_uint32 *minor_status,
                   send_token_flag *send_token)
 {
        OM_uint32 ret;
+       gss_cred_id_t mcred;
 
+       mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
        ret = gss_init_sec_context(minor_status,
-                                  claimant_cred_handle,
+                                  mcred,
                                   &sc->ctx_handle,
                                   target_name,
                                   sc->internal_mech,
@@ -887,6 +901,7 @@ spnego_gss_init_sec_context(
        gss_buffer_t mechtok_in, mechListMIC_in, mechListMIC_out;
        gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
        gss_OID_set mechSet = GSS_C_NO_OID_SET;
+       spnego_gss_cred_id_t spcred = NULL;
        spnego_gss_ctx_id_t spnego_ctx = NULL;
 
        dsyslog("Entering init_sec_context\n");
@@ -908,8 +923,9 @@ spnego_gss_init_sec_context(
        if (actual_mech != NULL)
                *actual_mech = GSS_C_NO_OID;
 
+       spcred = (spnego_gss_cred_id_t)claimant_cred_handle;
        if (*context_handle == GSS_C_NO_CONTEXT) {
-               ret = init_ctx_new(minor_status, claimant_cred_handle,
+               ret = init_ctx_new(minor_status, spcred,
                                   context_handle, &mechSet, &send_token);
                if (ret != GSS_S_CONTINUE_NEEDED) {
                        goto cleanup;
@@ -925,8 +941,7 @@ spnego_gss_init_sec_context(
        spnego_ctx = (spnego_gss_ctx_id_t)*context_handle;
        if (!spnego_ctx->mech_complete) {
                ret = init_ctx_call_init(
-                       minor_status, spnego_ctx,
-                       claimant_cred_handle,
+                       minor_status, spnego_ctx, spcred,
                        target_name, req_flags,
                        time_req, mechtok_in,
                        actual_mech, &mechtok_out,
@@ -1045,7 +1060,7 @@ put_neg_hints(unsigned char **buf_out, gss_buffer_t input_token,
 
 static int
 make_NegHints(OM_uint32 *minor_status,
-             gss_cred_id_t cred, gss_buffer_t *outbuf)
+             spnego_gss_cred_id_t spcred, gss_buffer_t *outbuf)
 {
        gss_buffer_desc hintNameBuf;
        gss_name_t hintName = GSS_C_NO_NAME;
@@ -1061,9 +1076,9 @@ make_NegHints(OM_uint32 *minor_status,
 
        *outbuf = GSS_C_NO_BUFFER;
 
-       if (cred != GSS_C_NO_CREDENTIAL) {
+       if (spcred != NULL) {
                major_status = gss_inquire_cred(minor_status,
-                                               cred,
+                                               spcred->mcred,
                                                &hintName,
                                                NULL,
                                                NULL,
@@ -1188,7 +1203,7 @@ errout:
 static OM_uint32
 acc_ctx_hints(OM_uint32 *minor_status,
              gss_ctx_id_t *ctx,
-             gss_cred_id_t cred,
+             spnego_gss_cred_id_t spcred,
              gss_buffer_t *mechListMIC,
              OM_uint32 *negState,
              send_token_flag *return_token)
@@ -1206,24 +1221,14 @@ acc_ctx_hints(OM_uint32 *minor_status,
        *ctx = GSS_C_NO_CONTEXT;
        ret = GSS_S_DEFECTIVE_TOKEN;
 
-       if (cred != GSS_C_NO_CREDENTIAL) {
-               ret = gss_inquire_cred(minor_status, cred, NULL, NULL,
-                                      NULL, &supported_mechSet);
-               if (ret != GSS_S_COMPLETE) {
-                       *return_token = NO_TOKEN_SEND;
-                       goto cleanup;
-               }
-       } else {
-               ret = get_available_mechs(minor_status, GSS_C_NO_NAME,
-                                         GSS_C_ACCEPT, NULL,
-                                         &supported_mechSet);
-               if (ret != GSS_S_COMPLETE) {
-                       *return_token = NO_TOKEN_SEND;
-                       goto cleanup;
-               }
+       ret = get_negotiable_mechs(minor_status, spcred, GSS_C_ACCEPT,
+                                  &supported_mechSet);
+       if (ret != GSS_S_COMPLETE) {
+               *return_token = NO_TOKEN_SEND;
+               goto cleanup;
        }
 
-       ret = make_NegHints(minor_status, cred, mechListMIC);
+       ret = make_NegHints(minor_status, spcred, mechListMIC);
        if (ret != GSS_S_COMPLETE) {
                *return_token = NO_TOKEN_SEND;
                goto cleanup;
@@ -1268,7 +1273,7 @@ static OM_uint32
 acc_ctx_new(OM_uint32 *minor_status,
            gss_buffer_t buf,
            gss_ctx_id_t *ctx,
-           gss_cred_id_t cred,
+           spnego_gss_cred_id_t spcred,
            gss_buffer_t *mechToken,
            gss_buffer_t *mechListMIC,
            OM_uint32 *negState,
@@ -1297,21 +1302,11 @@ acc_ctx_new(OM_uint32 *minor_status,
        if (ret != GSS_S_COMPLETE) {
                goto cleanup;
        }
-       if (cred != GSS_C_NO_CREDENTIAL) {
-               ret = gss_inquire_cred(minor_status, cred, NULL, NULL,
-                                      NULL, &supported_mechSet);
-               if (ret != GSS_S_COMPLETE) {
-                       *return_token = NO_TOKEN_SEND;
-                       goto cleanup;
-               }
-       } else {
-               ret = get_available_mechs(minor_status, GSS_C_NO_NAME,
-                                         GSS_C_ACCEPT, NULL,
-                                         &supported_mechSet);
-               if (ret != GSS_S_COMPLETE) {
-                       *return_token = NO_TOKEN_SEND;
-                       goto cleanup;
-               }
+       ret = get_negotiable_mechs(minor_status, spcred, GSS_C_ACCEPT,
+                                  &supported_mechSet);
+       if (ret != GSS_S_COMPLETE) {
+               *return_token = NO_TOKEN_SEND;
+               goto cleanup;
        }
        /*
         * Select the best match between the list of mechs
@@ -1484,7 +1479,7 @@ cleanup:
  */
 static OM_uint32
 acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
-                gss_cred_id_t cred, gss_buffer_t mechtok_in,
+                spnego_gss_cred_id_t spcred, gss_buffer_t mechtok_in,
                 gss_OID *mech_type, gss_buffer_t mechtok_out,
                 OM_uint32 *ret_flags, OM_uint32 *time_rec,
                 gss_cred_id_t *delegated_cred_handle,
@@ -1492,6 +1487,7 @@ acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
 {
        OM_uint32 ret;
        gss_OID_desc mechoid;
+       gss_cred_id_t mcred;
 
        if (sc->ctx_handle == GSS_C_NO_CONTEXT) {
                /*
@@ -1508,9 +1504,10 @@ acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
                        return ret;
        }
 
+       mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
        ret = gss_accept_sec_context(minor_status,
                                     &sc->ctx_handle,
-                                    cred,
+                                    mcred,
                                     mechtok_in,
                                     GSS_C_NO_CHANNEL_BINDINGS,
                                     &sc->internal_name,
@@ -1571,6 +1568,7 @@ spnego_gss_accept_sec_context(
        gss_buffer_t mechtok_in, mic_in, mic_out;
        gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
        spnego_gss_ctx_id_t sc = NULL;
+       spnego_gss_cred_id_t spcred = NULL;
        OM_uint32 mechstat = GSS_S_FAILURE;
        int sendTokenInit = 0;
 
@@ -1592,6 +1590,7 @@ spnego_gss_accept_sec_context(
                return GSS_S_CALL_INACCESSIBLE_READ;
 
        sc = (spnego_gss_ctx_id_t)*context_handle;
+       spcred = (spnego_gss_cred_id_t)verifier_cred_handle;
        if (sc == NULL || sc->internal_mech == GSS_C_NO_OID) {
                if (src_name != NULL)
                        *src_name = GSS_C_NO_NAME;
@@ -1606,8 +1605,7 @@ spnego_gss_accept_sec_context(
                if (input_token->length == 0) {
                        sendTokenInit = 1;
                        ret = acc_ctx_hints(minor_status,
-                                           context_handle,
-                                           verifier_cred_handle,
+                                           context_handle, spcred,
                                            &mic_out,
                                            &negState,
                                            &return_token);
@@ -1617,7 +1615,7 @@ spnego_gss_accept_sec_context(
                } else {
                        /* Can set negState to REQUEST_MIC */
                        ret = acc_ctx_new(minor_status, input_token,
-                                         context_handle, verifier_cred_handle,
+                                         context_handle, spcred,
                                          &mechtok_in, &mic_in,
                                          &negState, &return_token);
                        if (ret != GSS_S_COMPLETE)
@@ -1643,9 +1641,8 @@ spnego_gss_accept_sec_context(
         */
        mechstat = GSS_S_FAILURE;
        if (negState != REQUEST_MIC && mechtok_in != GSS_C_NO_BUFFER) {
-               ret = acc_ctx_call_acc(minor_status, sc,
-                                      verifier_cred_handle, mechtok_in,
-                                      mech_type, &mechtok_out,
+               ret = acc_ctx_call_acc(minor_status, sc, spcred,
+                                      mechtok_in, mech_type, &mechtok_out,
                                       ret_flags, time_rec,
                                       delegated_cred_handle,
                                       &negState, &return_token);
@@ -1808,6 +1805,7 @@ spnego_gss_inquire_cred(
                        gss_OID_set *mechanisms)
 {
        OM_uint32 status;
+       spnego_gss_cred_id_t spcred = NULL;
        gss_cred_id_t creds = GSS_C_NO_CREDENTIAL;
        OM_uint32 tmp_minor_status;
        OM_uint32 initiator_lifetime, acceptor_lifetime;
@@ -1819,7 +1817,8 @@ spnego_gss_inquire_cred(
         * supplied we call gss_inquire_cred_by_mech() on the
         * first non-SPNEGO mechanism.
         */
-       if (cred_handle == GSS_C_NO_CREDENTIAL) {
+       spcred = (spnego_gss_cred_id_t)cred_handle;
+       if (spcred == NULL) {
                status = get_available_mechs(minor_status,
                        GSS_C_NO_NAME,
                        GSS_C_BOTH,
@@ -1858,7 +1857,7 @@ spnego_gss_inquire_cred(
 
                gss_release_cred(&tmp_minor_status, &creds);
        } else {
-               status = gss_inquire_cred(minor_status, cred_handle,
+               status = gss_inquire_cred(minor_status, spcred->mcred,
                                          name, lifetime,
                                          cred_usage, mechanisms);
        }
@@ -2179,8 +2178,11 @@ spnego_gss_inquire_cred_by_oid(
                gss_buffer_set_t *data_set)
 {
        OM_uint32 ret;
+       spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
+       gss_cred_id_t mcred;
+       mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
        ret = gss_inquire_cred_by_oid(minor_status,
-                               cred_handle,
+                               mcred,
                                desired_object,
                                data_set);
        return (ret);
@@ -2194,8 +2196,11 @@ spnego_gss_set_cred_option(
                const gss_buffer_t value)
 {
        OM_uint32 ret;
+       spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
+       gss_cred_id_t mcred;
+       mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
        ret = gssspi_set_cred_option(minor_status,
-                                    cred_handle,
+                                    mcred,
                                     desired_object,
                                     value);
        return (ret);
@@ -2344,6 +2349,8 @@ spnego_gss_acquire_cred_impersonate_name(OM_uint32 *minor_status,
 {
        OM_uint32 status;
        gss_OID_set amechs = GSS_C_NULL_OID_SET;
+       spnego_gss_cred_id_t imp_spcred = NULL, out_spcred = NULL;
+       gss_cred_id_t mcred;
 
        dsyslog("Entering spnego_gss_acquire_cred_impersonate_name\n");
 
@@ -2364,16 +2371,27 @@ spnego_gss_acquire_cred_impersonate_name(OM_uint32 *minor_status,
                desired_mechs = amechs;
        }
 
+       imp_spcred = (spnego_gss_cred_id_t)impersonator_cred_handle;
        status = gss_acquire_cred_impersonate_name(minor_status,
-                       impersonator_cred_handle,
+                       imp_spcred ? imp_spcred->mcred : GSS_C_NO_CREDENTIAL,
                        desired_name, time_req,
                        desired_mechs, cred_usage,
-                       output_cred_handle, actual_mechs,
+                       &mcred, actual_mechs,
                        time_rec);
 
        if (amechs != GSS_C_NULL_OID_SET)
                (void) gss_release_oid_set(minor_status, &amechs);
 
+       out_spcred = malloc(sizeof(spnego_gss_cred_id_rec));
+       if (out_spcred == NULL) {
+               gss_release_cred(minor_status, &mcred);
+               *minor_status = ENOMEM;
+               return (GSS_S_FAILURE);
+       }
+       out_spcred->mcred = mcred;
+       out_spcred->neg_mechs = GSS_C_NULL_OID_SET;
+       *output_cred_handle = (gss_cred_id_t)out_spcred;
+
        dsyslog("Leaving spnego_gss_acquire_cred_impersonate_name\n");
        return (status);
 }
@@ -2519,6 +2537,21 @@ spnego_gss_pseudo_random(OM_uint32 *minor_status,
         return (ret);
 }
 
+OM_uint32
+spnego_gss_set_neg_mechs(OM_uint32 *minor_status,
+                        gss_cred_id_t cred_handle,
+                        const gss_OID_set mech_list)
+{
+       OM_uint32 ret;
+       spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
+
+       /* Store mech_list in spcred for use in negotiation logic. */
+       gss_release_oid_set(minor_status, &spcred->neg_mechs);
+       ret = generic_gss_copy_oid_set(minor_status, mech_list,
+                                      &spcred->neg_mechs);
+       return (ret);
+}
+
 /*
  * We will release everything but the ctx_handle so that it
  * can be passed back to init/accept context. This routine should
@@ -2632,6 +2665,83 @@ get_available_mechs(OM_uint32 *minor_status,
        return (major_status);
 }
 
+/*
+ * Return a list of mechanisms we are willing to negotiate for a credential,
+ * taking into account the mech set provided with gss_set_neg_mechs if it
+ * exists.
+ */
+static OM_uint32
+get_negotiable_mechs(OM_uint32 *minor_status, spnego_gss_cred_id_t spcred,
+                    gss_cred_usage_t usage, gss_OID_set *rmechs)
+{
+       OM_uint32 ret, tmpmin;
+       gss_cred_id_t creds = GSS_C_NO_CREDENTIAL, *credptr;
+       gss_OID_set cred_mechs = GSS_C_NULL_OID_SET;
+       gss_OID_set intersect_mechs = GSS_C_NULL_OID_SET;
+       unsigned int i, j;
+
+       if (spcred == NULL) {
+               /*
+                * The default credentials were supplied.  Return a list of all
+                * available mechs except SPNEGO.  When initiating, trim this
+                * list to mechs we can acquire credentials for.
+                */
+               credptr = (usage == GSS_C_INITIATE) ? &creds : NULL;
+               ret = get_available_mechs(minor_status, GSS_C_NO_NAME, usage,
+                                         credptr, rmechs);
+               gss_release_cred(&tmpmin, &creds);
+               return (ret);
+       }
+
+       /* Get the list of mechs in the mechglue cred. */
+       ret = gss_inquire_cred(minor_status, spcred->mcred, NULL, NULL, NULL,
+                              &cred_mechs);
+       if (ret != GSS_S_COMPLETE)
+               return (ret);
+
+       if (spcred->neg_mechs == GSS_C_NULL_OID_SET) {
+               /* gss_set_neg_mechs was never called; return cred_mechs. */
+               *rmechs = cred_mechs;
+               *minor_status = 0;
+               return (GSS_S_COMPLETE);
+       }
+
+       /* Compute the intersection of cred_mechs and spcred->neg_mechs,
+        * preserving the order in spcred->neg_mechs. */
+       ret = gss_create_empty_oid_set(minor_status, &intersect_mechs);
+       if (ret != GSS_S_COMPLETE) {
+               gss_release_oid_set(&tmpmin, &cred_mechs);
+               return (ret);
+       }
+
+       for (i = 0; i < spcred->neg_mechs->count; i++) {
+               for (j = 0; j < cred_mechs->count; j++) {
+                       if (!g_OID_equal(&spcred->neg_mechs->elements[i],
+                                        &cred_mechs->elements[j]))
+                               break;
+               }
+               if (j == cred_mechs->count)
+                       continue;
+               ret = gss_add_oid_set_member(minor_status,
+                                            &spcred->neg_mechs->elements[i],
+                                            &intersect_mechs);
+               if (ret != GSS_S_COMPLETE)
+                       break;
+       }
+
+       gss_release_oid_set(&tmpmin, &cred_mechs);
+       if (intersect_mechs->count == 0 || ret != GSS_S_COMPLETE) {
+               gss_release_oid_set(&tmpmin, &intersect_mechs);
+               *minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
+               map_errcode(minor_status);
+               return (GSS_S_FAILURE);
+       }
+
+       *rmechs = intersect_mechs;
+       *minor_status = 0;
+       return (GSS_S_COMPLETE);
+}
+
 /* following are token creation and reading routines */
 
 /*
index 98020d4490dc34572c4f3199c4dcbc57dafec78d..6a154140e6be8aeb39db5e86b2fb1fbb031766c3 100644 (file)
@@ -8,7 +8,7 @@ SRCS= $(srcdir)/t_imp_name.c $(srcdir)/t_s4u.c $(srcdir)/t_namingexts.c $(srcdir
 
 OBJS= t_imp_name.o t_s4u.o t_namingexts.o t_gssexts.o
 
-all:: t_imp_name t_s4u t_namingexts t_gssexts
+all:: t_imp_name t_s4u t_namingexts t_gssexts t_spnego
 
 t_imp_name: t_imp_name.o $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS)
        $(CC_LINK) -o t_imp_name t_imp_name.o $(GSS_LIBS) $(KRB5_BASE_LIBS)
@@ -18,7 +18,9 @@ t_namingexts: t_namingexts.o $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS)
        $(CC_LINK) -o t_namingexts t_namingexts.o $(GSS_LIBS) $(KRB5_BASE_LIBS)
 t_gssexts: t_gssexts.o $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS)
        $(CC_LINK) -o t_gssexts t_gssexts.o $(GSS_LIBS) $(KRB5_BASE_LIBS)
+t_spnego: t_spnego.o $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS)
+       $(CC_LINK) -o t_spnego t_spnego.o $(GSS_LIBS) $(KRB5_BASE_LIBS)
 
 clean::
-       $(RM) t_imp_name t_s4u t_namingexts t_gssexts
+       $(RM) t_imp_name t_s4u t_namingexts t_gssexts t_spnego
 
diff --git a/src/tests/gssapi/t_spnego.c b/src/tests/gssapi/t_spnego.c
new file mode 100644 (file)
index 0000000..e37b5ad
--- /dev/null
@@ -0,0 +1,265 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2010  by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gssapi/gssapi_krb5.h>
+
+/*
+ * Test program for SPNEGO and gss_set_neg_mechs
+ *
+ * Example usage:
+ *
+ * kinit testuser
+ * ./t_spnego host/test.host@REALM testhost.keytab
+ */
+
+static gss_OID_desc spnego_mech = { 6, "\053\006\001\005\005\002" };
+
+static void displayStatus_1(m, code, type)
+     char *m;
+     OM_uint32 code;
+     int type;
+{
+     OM_uint32 maj_stat, min_stat;
+     gss_buffer_desc msg;
+     OM_uint32 msg_ctx;
+
+     msg_ctx = 0;
+     while (1) {
+          maj_stat = gss_display_status(&min_stat, code,
+                                       type, GSS_C_NULL_OID,
+                                       &msg_ctx, &msg);
+          fprintf(stderr, "%s: %s\n", m, (char *)msg.value);
+          (void) gss_release_buffer(&min_stat, &msg);
+
+          if (!msg_ctx)
+               break;
+     }
+}
+
+static void displayStatus(msg, maj_stat, min_stat)
+     char *msg;
+     OM_uint32 maj_stat;
+     OM_uint32 min_stat;
+{
+     displayStatus_1(msg, maj_stat, GSS_C_GSS_CODE);
+     displayStatus_1(msg, min_stat, GSS_C_MECH_CODE);
+}
+
+static OM_uint32
+displayCanonName(OM_uint32 *minor, gss_name_t name, char *tag)
+{
+    gss_name_t canon;
+    OM_uint32 major, tmp_minor;
+    gss_buffer_desc buf;
+
+    major = gss_canonicalize_name(minor, name,
+                                  (gss_OID)gss_mech_krb5, &canon);
+    if (GSS_ERROR(major)) {
+        displayStatus("gss_canonicalize_name", major, *minor);
+        return major;
+    }
+
+    major = gss_display_name(minor, canon, &buf, NULL);
+    if (GSS_ERROR(major)) {
+        displayStatus("gss_display_name", major, *minor);
+        gss_release_name(&tmp_minor, &canon);
+        return major;
+    }
+
+    printf("%s:\t%s\n", tag, (char *)buf.value);
+
+    gss_release_buffer(&tmp_minor, &buf);
+    gss_release_name(&tmp_minor, &canon);
+
+    return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+displayOID(OM_uint32 *minor, gss_OID oid, char *tag)
+{
+    OM_uint32 major, tmp_minor;
+    gss_buffer_desc buf;
+
+    major = gss_oid_to_str(minor, oid, &buf);
+    if (GSS_ERROR(major)) {
+        displayStatus("gss_oid_to_str", major, *minor);
+        return major;
+    }
+
+    printf("%s:\t%s\n", tag, (char *)buf.value);
+
+    gss_release_buffer(&tmp_minor, &buf);
+
+    return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+initAcceptSecContext(OM_uint32 *minor,
+                    gss_name_t target_name,
+                     gss_cred_id_t verifier_cred_handle)
+{
+    OM_uint32 major;
+    gss_buffer_desc token, tmp;
+    gss_ctx_id_t initiator_context = GSS_C_NO_CONTEXT;
+    gss_ctx_id_t acceptor_context = GSS_C_NO_CONTEXT;
+    gss_name_t source_name = GSS_C_NO_NAME;
+    OM_uint32 time_rec;
+    gss_OID mech = GSS_C_NO_OID;
+
+    token.value = NULL;
+    token.length = 0;
+
+    tmp.value = NULL;
+    tmp.length = 0;
+
+    major = gss_init_sec_context(minor,
+                                 GSS_C_NO_CREDENTIAL,
+                                 &initiator_context,
+                                 target_name,
+                                 &spnego_mech,
+                                 GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG,
+                                 GSS_C_INDEFINITE,
+                                 GSS_C_NO_CHANNEL_BINDINGS,
+                                 GSS_C_NO_BUFFER,
+                                 NULL,
+                                 &token,
+                                 NULL,
+                                 &time_rec);
+
+    if (target_name != GSS_C_NO_NAME)
+        (void) gss_release_name(minor, &target_name);
+
+    if (GSS_ERROR(major)) {
+        displayStatus("gss_init_sec_context", major, *minor);
+        return major;
+    }
+
+    (void) gss_delete_sec_context(minor, &initiator_context, NULL);
+
+    major = gss_accept_sec_context(minor,
+                                   &acceptor_context,
+                                   verifier_cred_handle,
+                                   &token,
+                                   GSS_C_NO_CHANNEL_BINDINGS,
+                                   &source_name,
+                                   &mech,
+                                   &tmp,
+                                   NULL,
+                                   &time_rec,
+                                   NULL);
+
+    if (GSS_ERROR(major))
+        displayStatus("gss_accept_sec_context", major, *minor);
+    else {
+        displayCanonName(minor, source_name, "Source name");
+        displayOID(minor, mech, "Source mech");
+    }
+
+    (void) gss_release_name(minor, &source_name);
+    (void) gss_delete_sec_context(minor, &acceptor_context, NULL);
+    (void) gss_release_buffer(minor, &token);
+    (void) gss_release_buffer(minor, &tmp);
+    (void) gss_release_oid(minor, &mech);
+
+    return major;
+}
+
+int main(int argc, char *argv[])
+{
+    OM_uint32 minor, major;
+    gss_cred_id_t verifier_cred_handle = GSS_C_NO_CREDENTIAL;
+    gss_OID_set_desc mechs;
+    gss_OID_set actual_mechs = GSS_C_NO_OID_SET;
+    gss_buffer_desc buf;
+    gss_name_t target_name;
+
+    if (argc < 2 || argc > 3) {
+        fprintf(stderr, "Usage: %s target_name [keytab]\n", argv[0]);
+        exit(1);
+    }
+
+    buf.value = argv[1];
+    buf.length = strlen((char *)buf.value);
+    major = gss_import_name(&minor, &buf,
+                            (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME,
+                            &target_name);
+    if (GSS_ERROR(major)) {
+        displayStatus("gss_import_name(target_name)", major, minor);
+        goto out;
+    }
+
+    if (argc > 2) {
+        major = krb5_gss_register_acceptor_identity(argv[2]);
+        if (GSS_ERROR(major)) {
+            displayStatus("krb5_gss_register_acceptor_identity",
+                          major, minor);
+            goto out;
+        }
+    }
+
+    mechs.elements = &spnego_mech;
+    mechs.count = 1;
+
+    /* get default acceptor cred */
+    major = gss_acquire_cred(&minor,
+                             GSS_C_NO_NAME,
+                             GSS_C_INDEFINITE,
+                             &mechs,
+                             GSS_C_ACCEPT,
+                             &verifier_cred_handle,
+                             &actual_mechs,
+                             NULL);
+    if (GSS_ERROR(major)) {
+        displayStatus("gss_acquire_cred", major, minor);
+        goto out;
+    }
+
+    /* Restrict the acceptor to krb5, to exercise the neg_mechs logic. */
+    mechs.elements = (gss_OID)gss_mech_krb5;
+    mechs.count = 1;
+    major = gss_set_neg_mechs(&minor, verifier_cred_handle, &mechs);
+    if (GSS_ERROR(major)) {
+        displayStatus("gss_set_neg_mechs", major, minor);
+        goto out;
+    }
+
+    major = initAcceptSecContext(&minor, target_name, verifier_cred_handle);
+    if (GSS_ERROR(major))
+        goto out;
+
+    printf("\n");
+
+out:
+    (void) gss_release_cred(&minor, &verifier_cred_handle);
+    (void) gss_release_oid_set(&minor, &actual_mechs);
+    (void) gss_release_name(&minor, &target_name);
+
+    return GSS_ERROR(major) ? 1 : 0;
+}