aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lib/gssapi/generic/gssapi.hin6
-rw-r--r--src/lib/gssapi/krb5/gssapi_krb5.c1
-rw-r--r--src/lib/gssapi/libgssapi_krb5.exports1
-rw-r--r--src/lib/gssapi/mechglue/Makefile.in3
-rw-r--r--src/lib/gssapi/mechglue/g_initialize.c2
-rw-r--r--src/lib/gssapi/mechglue/g_set_neg_mechs.c77
-rw-r--r--src/lib/gssapi/mechglue/mglueP.h6
-rw-r--r--src/lib/gssapi/spnego/gssapiP_spnego.h14
-rw-r--r--src/lib/gssapi/spnego/spnego_mech.c272
-rw-r--r--src/tests/gssapi/Makefile.in6
-rw-r--r--src/tests/gssapi/t_spnego.c265
11 files changed, 570 insertions, 83 deletions
diff --git a/src/lib/gssapi/generic/gssapi.hin b/src/lib/gssapi/generic/gssapi.hin
index e60d04d..fb82e3c 100644
--- a/src/lib/gssapi/generic/gssapi.hin
+++ b/src/lib/gssapi/generic/gssapi.hin
@@ -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
diff --git a/src/lib/gssapi/krb5/gssapi_krb5.c b/src/lib/gssapi/krb5/gssapi_krb5.c
index b68bc9a..9e5ba76 100644
--- a/src/lib/gssapi/krb5/gssapi_krb5.c
+++ b/src/lib/gssapi/krb5/gssapi_krb5.c
@@ -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 */
};
diff --git a/src/lib/gssapi/libgssapi_krb5.exports b/src/lib/gssapi/libgssapi_krb5.exports
index e7f7b36..e038f50 100644
--- a/src/lib/gssapi/libgssapi_krb5.exports
+++ b/src/lib/gssapi/libgssapi_krb5.exports
@@ -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
diff --git a/src/lib/gssapi/mechglue/Makefile.in b/src/lib/gssapi/mechglue/Makefile.in
index 0858a4a..f435711 100644
--- a/src/lib/gssapi/mechglue/Makefile.in
+++ b/src/lib/gssapi/mechglue/Makefile.in
@@ -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 \
diff --git a/src/lib/gssapi/mechglue/g_initialize.c b/src/lib/gssapi/mechglue/g_initialize.c
index e01d174..70c2203 100644
--- a/src/lib/gssapi/mechglue/g_initialize.c
+++ b/src/lib/gssapi/mechglue/g_initialize.c
@@ -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
index 0000000..1675376
--- /dev/null
+++ b/src/lib/gssapi/mechglue/g_set_neg_mechs.c
@@ -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;
+}
diff --git a/src/lib/gssapi/mechglue/mglueP.h b/src/lib/gssapi/mechglue/mglueP.h
index 517ca48..3769caf 100644
--- a/src/lib/gssapi/mechglue/mglueP.h
+++ b/src/lib/gssapi/mechglue/mglueP.h
@@ -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 */
diff --git a/src/lib/gssapi/spnego/gssapiP_spnego.h b/src/lib/gssapi/spnego/gssapiP_spnego.h
index 4bfe863..a1cd2b4 100644
--- a/src/lib/gssapi/spnego/gssapiP_spnego.h
+++ b/src/lib/gssapi/spnego/gssapiP_spnego.h
@@ -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
diff --git a/src/lib/gssapi/spnego/spnego_mech.c b/src/lib/gssapi/spnego/spnego_mech.c
index 669b343..60fadd5 100644
--- a/src/lib/gssapi/spnego/spnego_mech.c
+++ b/src/lib/gssapi/spnego/spnego_mech.c
@@ -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 */
/*
diff --git a/src/tests/gssapi/Makefile.in b/src/tests/gssapi/Makefile.in
index 98020d4..6a15414 100644
--- a/src/tests/gssapi/Makefile.in
+++ b/src/tests/gssapi/Makefile.in
@@ -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
index 0000000..e37b5ad
--- /dev/null
+++ b/src/tests/gssapi/t_spnego.c
@@ -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;
+}