diff options
-rw-r--r-- | src/lib/gssapi/generic/gssapi.hin | 6 | ||||
-rw-r--r-- | src/lib/gssapi/krb5/gssapi_krb5.c | 1 | ||||
-rw-r--r-- | src/lib/gssapi/libgssapi_krb5.exports | 1 | ||||
-rw-r--r-- | src/lib/gssapi/mechglue/Makefile.in | 3 | ||||
-rw-r--r-- | src/lib/gssapi/mechglue/g_initialize.c | 2 | ||||
-rw-r--r-- | src/lib/gssapi/mechglue/g_set_neg_mechs.c | 77 | ||||
-rw-r--r-- | src/lib/gssapi/mechglue/mglueP.h | 6 | ||||
-rw-r--r-- | src/lib/gssapi/spnego/gssapiP_spnego.h | 14 | ||||
-rw-r--r-- | src/lib/gssapi/spnego/spnego_mech.c | 272 | ||||
-rw-r--r-- | src/tests/gssapi/Makefile.in | 6 | ||||
-rw-r--r-- | src/tests/gssapi/t_spnego.c | 265 |
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; +} |