From 079eed2cf749702f75ddc385cf943fbab931f9d8 Mon Sep 17 00:00:00 2001 From: Greg Hudson Date: Wed, 10 Feb 2010 23:44:18 +0000 Subject: [PATCH] Implement gss_set_neg_mechs 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 target_version: 1.8 tags: pullup git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@23715 dc483132-0cff-0310-8789-dd5450dbe970 --- src/lib/gssapi/generic/gssapi.hin | 6 + src/lib/gssapi/krb5/gssapi_krb5.c | 1 + src/lib/gssapi/libgssapi_krb5.exports | 1 + src/lib/gssapi/mechglue/Makefile.in | 3 + src/lib/gssapi/mechglue/g_initialize.c | 2 + src/lib/gssapi/mechglue/g_set_neg_mechs.c | 77 ++++++ src/lib/gssapi/mechglue/mglueP.h | 6 + src/lib/gssapi/spnego/gssapiP_spnego.h | 14 ++ src/lib/gssapi/spnego/spnego_mech.c | 272 +++++++++++++++------- src/tests/gssapi/Makefile.in | 6 +- src/tests/gssapi/t_spnego.c | 265 +++++++++++++++++++++ 11 files changed, 570 insertions(+), 83 deletions(-) create mode 100644 src/lib/gssapi/mechglue/g_set_neg_mechs.c create mode 100644 src/tests/gssapi/t_spnego.c diff --git a/src/lib/gssapi/generic/gssapi.hin b/src/lib/gssapi/generic/gssapi.hin index e60d04d87..fb82e3c4f 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 b68bc9a73..9e5ba76d9 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 e7f7b3609..e038f5076 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 0858a4a98..f43571145 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 e01d17474..70c220391 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 000000000..167537675 --- /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 517ca481b..3769caf87 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 4bfe863f9..a1cd2b4a6 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 669b343d9..60fadd5e5 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 98020d449..6a154140e 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 000000000..e37b5adfe --- /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 +#include +#include + +#include + +/* + * 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; +} -- 2.26.2