diff --git a/lib/gssapi/Makefile.am b/lib/gssapi/Makefile.am index fa8c01fbf..0b1c7ff32 100644 --- a/lib/gssapi/Makefile.am +++ b/lib/gssapi/Makefile.am @@ -171,7 +171,6 @@ spnegosrc = \ spnego/accept_sec_context.c \ spnego/compat.c \ spnego/context_stubs.c \ - spnego/cred_stubs.c \ spnego/external.c \ spnego/init_sec_context.c \ spnego/negoex_ctx.c \ diff --git a/lib/gssapi/NTMakefile b/lib/gssapi/NTMakefile index e153a0ce2..185f63c40 100644 --- a/lib/gssapi/NTMakefile +++ b/lib/gssapi/NTMakefile @@ -187,7 +187,6 @@ spnegosrc = \ spnego/accept_sec_context.c \ spnego/compat.c \ spnego/context_stubs.c \ - spnego/cred_stubs.c \ spnego/external.c \ spnego/init_sec_context.c \ spnego/negoex_ctx.c \ @@ -431,7 +430,6 @@ libgssapi_OBJs = \ $(OBJ)\spnego/accept_sec_context.obj \ $(OBJ)\spnego/compat.obj \ $(OBJ)\spnego/context_stubs.obj \ - $(OBJ)\spnego/cred_stubs.obj \ $(OBJ)\spnego/external.obj \ $(OBJ)\spnego/init_sec_context.obj \ $(OBJ)\spnego/negoex_ctx.obj \ diff --git a/lib/gssapi/gssapi_mech.h b/lib/gssapi/gssapi_mech.h index 1199bb6b8..fa027f805 100644 --- a/lib/gssapi/gssapi_mech.h +++ b/lib/gssapi/gssapi_mech.h @@ -485,16 +485,6 @@ _gss_store_cred_into2_t(OM_uint32 *minor_status, gss_cred_usage_t *cred_usage_stored, gss_buffer_set_t *env); -typedef OM_uint32 GSSAPI_CALLCONV -_gss_set_neg_mechs_t(OM_uint32 *minor_status, - gss_cred_id_t cred_handle, - const gss_OID_set mechs); - -typedef OM_uint32 GSSAPI_CALLCONV -_gss_get_neg_mechs_t(OM_uint32 *minor_status, - gss_const_cred_id_t cred_handle, - gss_OID_set *mechs); - typedef OM_uint32 GSSAPI_CALLCONV _gss_query_mechanism_info_t(OM_uint32 *minor_status, gss_const_OID mech_oid, @@ -629,8 +619,6 @@ typedef struct gssapi_mech_interface_desc { _gss_duplicate_cred_t *gm_duplicate_cred; _gss_add_cred_from_t *gm_add_cred_from; _gss_store_cred_into_t *gm_store_cred_into; - _gss_set_neg_mechs_t *gm_set_neg_mechs; - _gss_get_neg_mechs_t *gm_get_neg_mechs; _gss_query_mechanism_info_t *gm_query_mechanism_info; _gss_query_meta_data_t *gm_query_meta_data; _gss_exchange_meta_data_t *gm_exchange_meta_data; diff --git a/lib/gssapi/krb5/external.c b/lib/gssapi/krb5/external.c index 2562ef68a..045d55497 100644 --- a/lib/gssapi/krb5/external.c +++ b/lib/gssapi/krb5/external.c @@ -391,8 +391,6 @@ static gssapi_mech_interface_desc krb5_mech = { _gsskrb5_duplicate_cred, _gsskrb5_add_cred_from, _gsskrb5_store_cred_into, - NULL, /* gm_set_neg_mechs */ - NULL, /* gm_get_neg_mechs */ NULL, /* gm_query_mechanism_info */ NULL, /* gm_query_meta_data */ NULL, /* gm_exchange_meta_data */ diff --git a/lib/gssapi/mech/cred.c b/lib/gssapi/mech/cred.c index aa0b89d8a..1a9ee6143 100644 --- a/lib/gssapi/mech/cred.c +++ b/lib/gssapi/mech/cred.c @@ -42,7 +42,11 @@ release_mech_cred(OM_uint32 *minor, struct _gss_mechanism_cred *mc) { OM_uint32 major; - major = mc->gmc_mech->gm_release_cred(minor, &mc->gmc_cred); + if (mc->gmc_mech->gm_release_cred != NULL) + major = mc->gmc_mech->gm_release_cred(minor, &mc->gmc_cred); + else + major = GSS_S_COMPLETE; + free(mc); return major; diff --git a/lib/gssapi/mech/cred.h b/lib/gssapi/mech/cred.h index d45067110..eed4a824d 100644 --- a/lib/gssapi/mech/cred.h +++ b/lib/gssapi/mech/cred.h @@ -37,6 +37,7 @@ HEIM_TAILQ_HEAD(_gss_mechanism_cred_list, _gss_mechanism_cred); struct _gss_cred { struct _gss_mechanism_cred_list gc_mc; + gss_OID_set gc_neg_mechs; }; struct _gss_cred * diff --git a/lib/gssapi/mech/gss_acquire_cred_from.c b/lib/gssapi/mech/gss_acquire_cred_from.c index d32ebf6cb..bcae629d1 100644 --- a/lib/gssapi/mech/gss_acquire_cred_from.c +++ b/lib/gssapi/mech/gss_acquire_cred_from.c @@ -149,7 +149,7 @@ gss_acquire_cred_from(OM_uint32 *minor_status, struct _gss_cred *cred = NULL; size_t i; OM_uint32 min_time = GSS_C_INDEFINITE; - gss_OID_set mechs; + gss_OID_set mechs = GSS_C_NO_OID_SET; *minor_status = 0; if (output_cred_handle == NULL) @@ -162,28 +162,40 @@ gss_acquire_cred_from(OM_uint32 *minor_status, _gss_load_mech(); - if (desired_mechs) { - int match = 0; + if (desired_mechs != GSS_C_NO_OID_SET) { + int only_mg_cred_mechs = -1; for (i = 0; i < desired_mechs->count; i++) { - gss_test_oid_set_member(minor_status, &desired_mechs->elements[i], - _gss_mech_oids, &match); - if (match) - break; + m = __gss_get_mechanism(&desired_mechs->elements[i]); + if (m != NULL) { + if ((m->gm_flags & GM_USE_MG_CRED) == 0) + only_mg_cred_mechs = 0; + else if (only_mg_cred_mechs == -1) + only_mg_cred_mechs = 1; + } } - if (!match) { + /* + * Now SPNEGO supports GM_USE_MG_CRED it's no longer necessary + * to specifically acquire SPNEGO credentials. If the caller + * did not specify any concrete mechanisms then we will acquire + * credentials for all of them. + */ + if (only_mg_cred_mechs == -1) { *minor_status = 0; major_status = GSS_S_BAD_MECH; goto cleanup; - } - mechs = desired_mechs; + } else if (only_mg_cred_mechs == 0) + mechs = desired_mechs; + else + mechs = _gss_mech_oids; } else mechs = _gss_mech_oids; cred = _gss_mg_alloc_cred(); if (cred == NULL) { *minor_status = ENOMEM; - return GSS_S_FAILURE; + major_status = GSS_S_FAILURE; + goto cleanup; } if (actual_mechs) { @@ -251,6 +263,24 @@ gss_acquire_cred_from(OM_uint32 *minor_status, goto cleanup; } + /* add all GM_USE_MG_CRED mechs such as SPNEGO */ + if (actual_mechs != NULL) { + struct _gss_mech_switch *ms; + + HEIM_TAILQ_FOREACH(ms, &_gss_mechs, gm_link) { + m = &ms->gm_mech; + + if ((m->gm_flags & GM_USE_MG_CRED) == 0) + continue; + + major_status = gss_add_oid_set_member(minor_status, + &m->gm_mech_oid, + actual_mechs); + if (GSS_ERROR(major_status)) + goto cleanup; + } + } + *minor_status = 0; major_status = GSS_S_COMPLETE; diff --git a/lib/gssapi/mech/gss_add_cred_from.c b/lib/gssapi/mech/gss_add_cred_from.c index d304061db..9f761e8a7 100644 --- a/lib/gssapi/mech/gss_add_cred_from.c +++ b/lib/gssapi/mech/gss_add_cred_from.c @@ -88,14 +88,75 @@ _gss_mg_add_mech_cred(OM_uint32 *minor_status, } else major_status = GSS_S_UNAVAILABLE; - if (major_status == GSS_S_COMPLETE && out) + if (major_status == GSS_S_COMPLETE && out) { + heim_assert(new_mc->gmc_cred != GSS_C_NO_CREDENTIAL, + "mechanism gss_add_cred did not return a cred"); *out = new_mc; - else + } else free(new_mc); return major_status; } +static OM_uint32 +add_mech_cred_internal(OM_uint32 *minor_status, + gss_const_name_t desired_name, + gssapi_mech_interface m, + gss_cred_usage_t cred_usage, + OM_uint32 initiator_time_req, + OM_uint32 acceptor_time_req, + gss_const_key_value_set_t cred_store, + struct _gss_cred *mut_cred, + OM_uint32 *initiator_time_rec, + OM_uint32 *acceptor_time_rec) +{ + OM_uint32 major_status; + struct _gss_mechanism_cred *mc; + struct _gss_mechanism_name *mn; + + heim_assert((m->gm_flags & GM_USE_MG_CRED) == 0, + "add_mech_cred_internal must be called with concrete mechanism"); + + if (desired_name != GSS_C_NO_NAME) { + major_status = _gss_find_mn(minor_status, + (struct _gss_name *)desired_name, + &m->gm_mech_oid, &mn); + if (major_status != GSS_S_COMPLETE) + return major_status; + } else + mn = NULL; + + /* + * If we have an existing mechanism credential for mechanism m, then + * add the desired credential to it; otherwise, create a new one and + * add it to mut_cred. + */ + HEIM_TAILQ_FOREACH(mc, &mut_cred->gc_mc, gmc_link) { + if (gss_oid_equal(&m->gm_mech_oid, mc->gmc_mech_oid)) + break; + } + + if (mc) { + major_status = _gss_mg_add_mech_cred(minor_status, m, + mc, mn, cred_usage, + initiator_time_req, acceptor_time_req, + cred_store, NULL, + initiator_time_rec, acceptor_time_rec); + } else { + struct _gss_mechanism_cred *new_mc = NULL; + + major_status = _gss_mg_add_mech_cred(minor_status, m, + NULL, mn, cred_usage, + initiator_time_req, acceptor_time_req, + cred_store, &new_mc, + initiator_time_rec, acceptor_time_rec); + if (major_status == GSS_S_COMPLETE) + HEIM_TAILQ_INSERT_TAIL(&mut_cred->gc_mc, new_mc, gmc_link); + } + + return major_status; +} + GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_add_cred_from(OM_uint32 *minor_status, gss_cred_id_t input_cred_handle, @@ -114,9 +175,6 @@ gss_add_cred_from(OM_uint32 *minor_status, gssapi_mech_interface m; gss_cred_id_t release_cred = GSS_C_NO_CREDENTIAL; struct _gss_cred *mut_cred; - struct _gss_mechanism_cred *mc; - struct _gss_mechanism_cred *new_mc = NULL; - struct _gss_mechanism_name *mn = NULL; OM_uint32 junk; *minor_status = 0; @@ -130,8 +188,7 @@ gss_add_cred_from(OM_uint32 *minor_status, *acceptor_time_rec = 0; if (actual_mechs) *actual_mechs = GSS_C_NO_OID_SET; - if ((m = __gss_get_mechanism(desired_mech)) == NULL || - (m->gm_flags & GM_USE_MG_CRED)) + if ((m = __gss_get_mechanism(desired_mech)) == NULL) return GSS_S_BAD_MECH; if (input_cred_handle == GSS_C_NO_CREDENTIAL && output_cred_handle == NULL) { @@ -162,45 +219,62 @@ gss_add_cred_from(OM_uint32 *minor_status, release_cred = (gss_cred_id_t)mut_cred; } - /* Find an MN, if any */ - if (desired_name) { - major_status = _gss_find_mn(minor_status, - (struct _gss_name *)desired_name, - desired_mech, &mn); - if (major_status != GSS_S_COMPLETE) - goto done; + if (m->gm_flags & GM_USE_MG_CRED) { + struct _gss_mech_switch *ms; + OM_uint32 initiator_time_min = GSS_C_INDEFINITE; + OM_uint32 acceptor_time_min = GSS_C_INDEFINITE; + + major_status = GSS_S_UNAVAILABLE; /* in case of no mechs */ + + if (input_cred_handle == GSS_C_NO_CREDENTIAL) { + HEIM_TAILQ_FOREACH(ms, &_gss_mechs, gm_link) { + m = &ms->gm_mech; /* for _gss_mg_error() */ + + if (m->gm_flags & GM_USE_MG_CRED) + continue; + + major_status = add_mech_cred_internal(minor_status, desired_name, m, + cred_usage, + initiator_time_req, acceptor_time_req, + cred_store, mut_cred, + initiator_time_rec, acceptor_time_rec); + if (major_status != GSS_S_COMPLETE) + continue; + + if (initiator_time_rec && *initiator_time_rec < initiator_time_min) + initiator_time_min = *initiator_time_rec; + if (acceptor_time_rec && *acceptor_time_rec < acceptor_time_min) + acceptor_time_min = *acceptor_time_rec; + } + } else { + OM_uint32 lifetime; + gss_cred_usage_t usage = GSS_C_BOTH; + + major_status = gss_inquire_cred(minor_status, input_cred_handle, + NULL, &lifetime, &usage, NULL); + if (major_status == GSS_S_COMPLETE) { + if (usage == GSS_C_BOTH || usage == GSS_C_INITIATE) + initiator_time_min = lifetime; + if (usage == GSS_C_BOTH || usage == GSS_C_ACCEPT) + acceptor_time_min = lifetime; + } + } + + if (initiator_time_rec) + *initiator_time_rec = initiator_time_min; + if (acceptor_time_rec) + *acceptor_time_rec = acceptor_time_min; + } else { + major_status = add_mech_cred_internal(minor_status, desired_name, m, + cred_usage, + initiator_time_req, acceptor_time_req, + cred_store, mut_cred, + initiator_time_rec, acceptor_time_rec); } - /* - * We go through all the mc attached to the input_cred_handle and check the - * mechanism. If it matches, we call gss_add_cred for that mechanism, - * otherwise we just add a new mc. - */ - HEIM_TAILQ_FOREACH(mc, &mut_cred->gc_mc, gmc_link) { - if (!gss_oid_equal(mc->gmc_mech_oid, desired_mech)) - continue; - major_status = _gss_mg_add_mech_cred(minor_status, m, - mc, mn, cred_usage, - initiator_time_req, acceptor_time_req, - cred_store, NULL, - initiator_time_rec, acceptor_time_rec); - if (major_status != GSS_S_COMPLETE) - _gss_mg_error(m, *minor_status); - goto done; - } + if (major_status != GSS_S_COMPLETE) + _gss_mg_error(m, *minor_status); - major_status = _gss_mg_add_mech_cred(minor_status, m, NULL, mn, cred_usage, - initiator_time_req, acceptor_time_req, - cred_store, &new_mc, - initiator_time_rec, acceptor_time_rec); - if (major_status != GSS_S_COMPLETE) { - _gss_mg_error(m, *minor_status); - goto done; - } - HEIM_TAILQ_INSERT_TAIL(&mut_cred->gc_mc, new_mc, gmc_link); - new_mc = NULL; - -done: /* Lastly, we have to inquire the cred to get the actual_mechs */ if (major_status == GSS_S_COMPLETE && actual_mechs != NULL) { major_status = gss_inquire_cred(minor_status, @@ -213,7 +287,6 @@ done: } else { gss_release_cred(&junk, &release_cred); } - free(new_mc); return major_status; } diff --git a/lib/gssapi/mech/gss_cred.c b/lib/gssapi/mech/gss_cred.c index 94642c587..ee2853222 100644 --- a/lib/gssapi/mech/gss_cred.c +++ b/lib/gssapi/mech/gss_cred.c @@ -34,12 +34,56 @@ #include "mech_locl.h" #include +static OM_uint32 +export_oid_set(OM_uint32 *minor_status, + gss_const_OID mech, + gss_const_OID_set oids, + krb5_storage *sp) +{ + krb5_error_code ret; + krb5_data data; + size_t i, len; + + data.length = mech->length; + data.data = mech->elements; + + ret = krb5_store_data(sp, data); + if (ret) + goto out; + + for (i = 0, len = 0; i < oids->count; i++) + len += 4 + oids->elements[i].length; + + ret = krb5_store_uint32(sp, len); + if (ret) + goto out; + + for (i = 0; i < oids->count; i++) { + data.length = oids->elements[i].length; + data.data = oids->elements[i].elements; + + ret = krb5_store_data(sp, data); + if (ret) + goto out; + } + + ret = krb5_storage_to_data(sp, &data); + if (ret) + goto out; + +out: + *minor_status = ret; + return ret ? GSS_S_FAILURE : GSS_S_COMPLETE; +} + /* * format: any number of: * mech-len: int32 * mech-data: char * (not alligned) * cred-len: int32 * cred-data char * (not alligned) + * + * where neg_mechs is encoded for GSS_SPNEGO_MECHANISM */ GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL @@ -99,6 +143,15 @@ gss_export_cred(OM_uint32 * minor_status, _gss_secure_release_buffer(minor_status, &buffer); } + if (cred->gc_neg_mechs != GSS_C_NO_OID_SET) { + major = export_oid_set(minor_status, GSS_SPNEGO_MECHANISM, + cred->gc_neg_mechs, sp); + if (major != GSS_S_COMPLETE) { + krb5_storage_free(sp); + return major; + } + } + ret = krb5_storage_to_data(sp, &data); krb5_storage_free(sp); if (ret) { @@ -120,6 +173,66 @@ gss_export_cred(OM_uint32 * minor_status, return GSS_S_COMPLETE; } +static OM_uint32 +import_oid_set(OM_uint32 *minor_status, + gss_const_buffer_t token, + gss_OID_set *oids) +{ + OM_uint32 major, junk; + krb5_error_code ret; + krb5_storage *sp = NULL; + + *oids = GSS_C_NO_OID_SET; + + if (token->length == 0) + return GSS_S_COMPLETE; + + major = gss_create_empty_oid_set(minor_status, oids); + if (major != GSS_S_COMPLETE) + goto out; + + sp = krb5_storage_from_readonly_mem(token->value, token->length); + if (sp == NULL) { + *minor_status = ENOMEM; + major = GSS_S_FAILURE; + goto out; + } + + while (1) { + gss_OID_desc oid; + krb5_data data; + + ret = krb5_ret_data(sp, &data); + if (ret == HEIM_ERR_EOF) + break; + else if (ret) { + *minor_status = ret; + major = GSS_S_FAILURE; + goto out; + } + + oid.elements = data.data; + oid.length = (OM_uint32)data.length; + + major = gss_add_oid_set_member(minor_status, &oid, oids); + if (major != GSS_S_COMPLETE) { + krb5_data_free(&data); + goto out; + } + krb5_data_free(&data); + } + + major = GSS_S_COMPLETE; + *minor_status = 0; + +out: + if (major != GSS_S_COMPLETE) + gss_release_oid_set(&junk, oids); + krb5_storage_free(sp); + + return major; +} + GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_import_cred(OM_uint32 * minor_status, gss_buffer_t token, @@ -179,7 +292,8 @@ gss_import_cred(OM_uint32 * minor_status, goto out; } - if (m->gm_import_cred == NULL) { + if (m->gm_import_cred == NULL && + !gss_oid_equal(&m->gm_mech_oid, GSS_SPNEGO_MECHANISM)) { *minor_status = 0; major = GSS_S_BAD_MECH; goto out; @@ -195,6 +309,15 @@ gss_import_cred(OM_uint32 * minor_status, buffer.value = data.data; buffer.length = data.length; + if (gss_oid_equal(&m->gm_mech_oid, GSS_SPNEGO_MECHANISM)) { + major = import_oid_set(minor_status, &buffer, &cred->gc_neg_mechs); + krb5_data_free(&data); + if (major != GSS_S_COMPLETE) + goto out; + else + continue; + } + major = m->gm_import_cred(minor_status, &buffer, &mcred); krb5_data_free(&data); diff --git a/lib/gssapi/mech/gss_get_neg_mechs.c b/lib/gssapi/mech/gss_get_neg_mechs.c index 500db0cc4..cbc378639 100644 --- a/lib/gssapi/mech/gss_get_neg_mechs.c +++ b/lib/gssapi/mech/gss_get_neg_mechs.c @@ -33,81 +33,22 @@ #include "mech_locl.h" GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL -gss_get_neg_mechs (OM_uint32 *minor_status, - gss_const_cred_id_t cred_handle, - gss_OID_set *mechs) +gss_get_neg_mechs(OM_uint32 *minor_status, + gss_const_cred_id_t cred_handle, + gss_OID_set *mechs) { struct _gss_cred *cred = (struct _gss_cred *)cred_handle; - OM_uint32 major, minor; - gss_cred_id_t tmp_cred = GSS_C_NO_CREDENTIAL; - struct _gss_mechanism_cred *mc; if (minor_status == NULL) return GSS_S_CALL_INACCESSIBLE_WRITE; + *minor_status = 0; if (mechs == NULL) return GSS_S_CALL_INACCESSIBLE_WRITE; - *mechs = GSS_C_NO_OID_SET; - _gss_load_mech(); + if (cred->gc_neg_mechs != GSS_C_NO_OID_SET) + return gss_duplicate_oid_set(minor_status, cred->gc_neg_mechs, mechs); - if (cred == NULL) { - major = gss_acquire_cred(minor_status, GSS_C_NO_NAME, GSS_C_INDEFINITE, - GSS_C_NO_OID_SET, GSS_C_BOTH, - &tmp_cred, NULL, NULL); - if (GSS_ERROR(major)) - return major; - - cred = (struct _gss_cred *)tmp_cred; - } - - major = gss_create_empty_oid_set(minor_status, mechs); - if (GSS_ERROR(major)) - goto cleanup; - - major = GSS_S_UNAVAILABLE; - - HEIM_TAILQ_FOREACH(mc, &cred->gc_mc, gmc_link) { - gssapi_mech_interface m; - gss_OID_set mechs2 = GSS_C_NO_OID_SET; - size_t i; - - m = mc->gmc_mech; - if (m == NULL) { - major = GSS_S_BAD_MECH; - goto cleanup; - } - - if (m->gm_get_neg_mechs == NULL) - continue; - - major = m->gm_get_neg_mechs(minor_status, mc->gmc_cred, &mechs2); - if (GSS_ERROR(major)) - goto cleanup; - - if (mechs2 == GSS_C_NO_OID_SET) - continue; - - for (i = 0; i < mechs2->count; i++) { - major = gss_add_oid_set_member(minor_status, &mechs2->elements[i], - mechs); - if (GSS_ERROR(major)) { - gss_release_oid_set(&minor, &mechs2); - goto cleanup; - } - } - - gss_release_oid_set(&minor, &mechs2); - } - -cleanup: - if (tmp_cred) - gss_release_cred(&minor, &tmp_cred); - if (major == GSS_S_COMPLETE && *mechs == GSS_C_NO_OID_SET) - major = GSS_S_NO_CRED; - if (GSS_ERROR(major)) - gss_release_oid_set(&minor, mechs); - - return major; + return GSS_S_UNAVAILABLE; } diff --git a/lib/gssapi/mech/gss_inquire_cred.c b/lib/gssapi/mech/gss_inquire_cred.c index f7408c84c..82cf34c42 100644 --- a/lib/gssapi/mech/gss_inquire_cred.c +++ b/lib/gssapi/mech/gss_inquire_cred.c @@ -99,6 +99,9 @@ gss_inquire_cred(OM_uint32 *minor_status, gss_name_t mc_name = GSS_C_NO_NAME; OM_uint32 mc_lifetime = GSS_C_INDEFINITE; + heim_assert((mc->gmc_mech->gm_flags & GM_USE_MG_CRED) == 0, + "should not have mech creds for GM_USE_MG_CRED mechs"); + if (mc->gmc_mech->gm_inquire_cred == NULL) continue; @@ -137,7 +140,8 @@ gss_inquire_cred(OM_uint32 *minor_status, gss_name_t mc_name; OM_uint32 mc_lifetime; - if (m->gm_mech.gm_inquire_cred == NULL) + if (m->gm_mech.gm_inquire_cred == NULL || + (m->gm_mech.gm_flags & GM_USE_MG_CRED)) continue; major_status = m->gm_mech.gm_inquire_cred(minor_status, @@ -174,6 +178,17 @@ gss_inquire_cred(OM_uint32 *minor_status, } } + if (found && mechanisms) { + /* GM_USE_MG_CRED mechs (SPNEGO) always can be used */ + HEIM_TAILQ_FOREACH(m, &_gss_mechs, gm_link) { + if ((m->gm_mech.gm_flags & GM_USE_MG_CRED) == 0) + continue; + + gss_add_oid_set_member(minor_status, + m->gm_mech_oid, mechanisms); + } + } + if (found == 0 || min_lifetime == 0) { gss_name_t n = (gss_name_t)name; if (n) diff --git a/lib/gssapi/mech/gss_mech_switch.c b/lib/gssapi/mech/gss_mech_switch.c index e4a56f3ce..9085e962f 100644 --- a/lib/gssapi/mech/gss_mech_switch.c +++ b/lib/gssapi/mech/gss_mech_switch.c @@ -417,8 +417,6 @@ _gss_load_mech(void) OPTSYM(duplicate_cred); OPTSYM(add_cred_from); OPTSYM(store_cred_into); - OPTSYM(set_neg_mechs); - OPTSYM(get_neg_mechs); OPTSPISYM(authorize_localname); OPTSPISPISYM(query_mechanism_info); OPTSPISPISYM(query_meta_data); diff --git a/lib/gssapi/mech/gss_set_neg_mechs.c b/lib/gssapi/mech/gss_set_neg_mechs.c index 5402e9656..7c527b2ab 100644 --- a/lib/gssapi/mech/gss_set_neg_mechs.c +++ b/lib/gssapi/mech/gss_set_neg_mechs.c @@ -33,61 +33,28 @@ #include "mech_locl.h" GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL -gss_set_neg_mechs (OM_uint32 *minor_status, - gss_cred_id_t cred_handle, - const gss_OID_set mechs) +gss_set_neg_mechs(OM_uint32 *minor_status, + gss_cred_id_t cred_handle, + const gss_OID_set mechs) { struct _gss_cred *cred = (struct _gss_cred *)cred_handle; - OM_uint32 major; - int found = 0; + OM_uint32 major_status, junk; + gss_OID_set tmp_mechs; if (minor_status == NULL) return GSS_S_CALL_INACCESSIBLE_WRITE; + *minor_status = 0; - if (mechs == GSS_C_NO_OID_SET) + if (cred_handle == GSS_C_NO_CREDENTIAL || mechs == GSS_C_NO_OID_SET) return GSS_S_CALL_INACCESSIBLE_READ; - _gss_load_mech(); + major_status = gss_duplicate_oid_set(minor_status, mechs, &tmp_mechs); + if (major_status != GSS_S_COMPLETE) + return major_status; - major = GSS_S_UNAVAILABLE; + gss_release_oid_set(&junk, &cred->gc_neg_mechs); + cred->gc_neg_mechs = tmp_mechs; - if (cred == NULL) { - struct _gss_mech_switch *m; - - HEIM_TAILQ_FOREACH(m, &_gss_mechs, gm_link) { - if (m->gm_mech.gm_set_neg_mechs == NULL) - continue; - major = m->gm_mech.gm_set_neg_mechs(minor_status, - GSS_C_NO_CREDENTIAL, mechs); - if (major == GSS_S_COMPLETE) - found++; - else - _gss_mg_error(&m->gm_mech, *minor_status); - } - } else { - struct _gss_mechanism_cred *mc; - - HEIM_TAILQ_FOREACH(mc, &cred->gc_mc, gmc_link) { - gssapi_mech_interface m; - - m = mc->gmc_mech; - if (m == NULL) - return GSS_S_BAD_MECH; - if (m->gm_set_neg_mechs == NULL) - continue; - major = m->gm_set_neg_mechs(minor_status, mc->gmc_cred, mechs); - if (major == GSS_S_COMPLETE) - found++; - else - _gss_mg_error(m, *minor_status); - } - } - - if (found) { - *minor_status = 0; - return GSS_S_COMPLETE; - } - - return major; + return GSS_S_COMPLETE; } diff --git a/lib/gssapi/netlogon/external.c b/lib/gssapi/netlogon/external.c index 49a37cb82..7c0f81b1d 100644 --- a/lib/gssapi/netlogon/external.c +++ b/lib/gssapi/netlogon/external.c @@ -100,8 +100,6 @@ static gssapi_mech_interface_desc netlogon_mech = { NULL, /* gm_duplicate_cred */ NULL, /* gm_add_cred_from */ NULL, /* gm_store_cred_into */ - NULL, /* gm_set_neg_mechs */ - NULL, /* gm_get_neg_mechs */ NULL /* gm_compat */ }; diff --git a/lib/gssapi/ntlm/external.c b/lib/gssapi/ntlm/external.c index 02590c96d..ed60c3eae 100644 --- a/lib/gssapi/ntlm/external.c +++ b/lib/gssapi/ntlm/external.c @@ -125,8 +125,6 @@ static gssapi_mech_interface_desc ntlm_mech = { NULL, /* gm_duplicate_cred */ NULL, /* gm_add_cred_from */ NULL, /* gm_store_cred_into */ - NULL, /* gm_set_neg_mechs */ - NULL, /* gm_get_neg_mechs */ NULL, /* gm_query_mechanism_info */ NULL, /* gm_query_meta_data */ NULL, /* gm_exchange_meta_data */ diff --git a/lib/gssapi/spnego/accept_sec_context.c b/lib/gssapi/spnego/accept_sec_context.c index 910d7e2c7..89311a1e7 100644 --- a/lib/gssapi/spnego/accept_sec_context.c +++ b/lib/gssapi/spnego/accept_sec_context.c @@ -601,8 +601,7 @@ acceptor_start gss_buffer_t mech_input_token = GSS_C_NO_BUFFER; gss_buffer_desc mech_output_token; gssspnego_ctx ctx; - int get_mic = 0; - int first_ok = 0; + int get_mic = 0, first_ok = 0, canonical_order; gss_const_OID advertised_mech = GSS_C_NO_OID; memset(&nt, 0, sizeof(nt)); @@ -676,7 +675,8 @@ acceptor_start if (acceptor_cred_handle != GSS_C_NO_CREDENTIAL) ret = _gss_spnego_inquire_cred_mechs(minor_status, acceptor_cred_handle, - &supported_mechs); + &supported_mechs, + &canonical_order); else ret = _gss_spnego_indicate_mechs(minor_status, &supported_mechs); if (ret != GSS_S_COMPLETE) diff --git a/lib/gssapi/spnego/compat.c b/lib/gssapi/spnego/compat.c index 1e09addac..6cfe55266 100644 --- a/lib/gssapi/spnego/compat.c +++ b/lib/gssapi/spnego/compat.c @@ -397,24 +397,21 @@ _gss_spnego_indicate_mechtypelist (OM_uint32 *minor_status, OM_uint32 ret, minor; OM_uint32 first_major = GSS_S_BAD_MECH, first_minor = 0; size_t i; - int added_negoex = FALSE; + int added_negoex = FALSE, canonical_order = FALSE; mechtypelist->len = 0; mechtypelist->val = NULL; if (cred_handle != GSS_C_NO_CREDENTIAL) - ret = _gss_spnego_inquire_cred_mechs(minor_status, - cred_handle, &supported_mechs); + ret = _gss_spnego_inquire_cred_mechs(minor_status, cred_handle, + &supported_mechs, &canonical_order); else ret = _gss_spnego_indicate_mechs(minor_status, &supported_mechs); if (ret != GSS_S_COMPLETE) return ret; - /* - * XXX when gss_set_neg_mechs() obeys application order this should - * apply only to the default mech list - */ - order_mechs_by_flags(supported_mechs, req_flags); + if (!canonical_order) + order_mechs_by_flags(supported_mechs, req_flags); heim_assert(supported_mechs != GSS_C_NO_OID_SET, "NULL mech set returned by SPNEGO inquire/indicate mechs"); @@ -626,40 +623,48 @@ _gss_spnego_indicate_mechs(OM_uint32 *minor_status, OM_uint32 _gss_spnego_inquire_cred_mechs(OM_uint32 *minor_status, gss_const_cred_id_t cred, - gss_OID_set *mechs_p) + gss_OID_set *mechs_p, + int *canonical_order) { OM_uint32 ret, junk; gss_OID_set cred_mechs = GSS_C_NO_OID_SET; - gss_OID_set mechs = GSS_C_NO_OID_SET; + gss_OID_set negotiable_mechs = GSS_C_NO_OID_SET; size_t i; *mechs_p = GSS_C_NO_OID_SET; + *canonical_order = FALSE; heim_assert(cred != GSS_C_NO_CREDENTIAL, "Invalid null credential handle"); - ret = gss_inquire_cred(minor_status, cred, NULL, NULL, NULL, &cred_mechs); + ret = gss_get_neg_mechs(minor_status, cred, &cred_mechs); + if (ret == GSS_S_COMPLETE) { + *canonical_order = TRUE; + } else { + ret = gss_inquire_cred(minor_status, cred, NULL, NULL, NULL, &cred_mechs); + if (ret != GSS_S_COMPLETE) + goto out; + } + + heim_assert(cred_mechs != GSS_C_NO_OID_SET && cred_mechs->count > 0, + "gss_inquire_cred succeeded but returned no mechanisms"); + + ret = _gss_spnego_indicate_mechs(minor_status, &negotiable_mechs); if (ret != GSS_S_COMPLETE) goto out; - heim_assert(cred_mechs != GSS_C_NO_OID_SET, - "gss_inquire_cred succeeded but returned null OID set"); - - ret = _gss_spnego_indicate_mechs(minor_status, &mechs); - if (ret != GSS_S_COMPLETE) - goto out; - - heim_assert(mechs != GSS_C_NO_OID_SET, + heim_assert(negotiable_mechs != GSS_C_NO_OID_SET, "_gss_spnego_indicate_mechs succeeded but returned null OID set"); ret = gss_create_empty_oid_set(minor_status, mechs_p); if (ret != GSS_S_COMPLETE) goto out; + /* Filter credential mechs by negotiable mechs, order by credential mechs */ for (i = 0; i < cred_mechs->count; i++) { gss_OID cred_mech = &cred_mechs->elements[i]; int present = 0; - gss_test_oid_set_member(&junk, cred_mech, mechs, &present); + gss_test_oid_set_member(&junk, cred_mech, negotiable_mechs, &present); if (!present) continue; @@ -672,7 +677,7 @@ out: if (ret != GSS_S_COMPLETE) gss_release_oid_set(&junk, mechs_p); gss_release_oid_set(&junk, &cred_mechs); - gss_release_oid_set(&junk, &mechs); + gss_release_oid_set(&junk, &negotiable_mechs); return ret; } diff --git a/lib/gssapi/spnego/cred_stubs.c b/lib/gssapi/spnego/cred_stubs.c deleted file mode 100644 index 0124c8f44..000000000 --- a/lib/gssapi/spnego/cred_stubs.c +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (c) 2004, 2018, PADL Software Pty Ltd. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. Neither the name of PADL Software nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "spnego_locl.h" -#include - -/* - * For now, just a simple wrapper that avoids recursion. When - * we support gss_{get,set}_neg_mechs() we will need to expose - * more functionality. - */ -OM_uint32 GSSAPI_CALLCONV _gss_spnego_acquire_cred_from -(OM_uint32 *minor_status, - gss_const_name_t desired_name, - OM_uint32 time_req, - const gss_OID_set desired_mechs, - gss_cred_usage_t cred_usage, - gss_const_key_value_set_t cred_store, - gss_cred_id_t * output_cred_handle, - gss_OID_set * actual_mechs, - OM_uint32 * time_rec - ) -{ - OM_uint32 ret, tmp; - gss_OID_set mechs; - - *output_cred_handle = GSS_C_NO_CREDENTIAL; - - ret = _gss_spnego_indicate_mechs(minor_status, &mechs); - if (ret != GSS_S_COMPLETE) - return ret; - - ret = gss_acquire_cred_from(minor_status, desired_name, - time_req, mechs, - cred_usage, cred_store, - output_cred_handle, - actual_mechs, time_rec); - gss_release_oid_set(&tmp, &mechs); - - return ret; -} - -OM_uint32 GSSAPI_CALLCONV _gss_spnego_inquire_cred - (OM_uint32 * minor_status, - gss_const_cred_id_t cred_handle, - gss_name_t * name, - OM_uint32 * lifetime, - gss_cred_usage_t * cred_usage, - gss_OID_set * mechanisms - ) -{ - /* - * A wrapper around the mechglue is required to error out - * where cred_handle == GSS_C_NO_CREDENTIAL, otherwise we - * would infinitely recurse. - */ - if (cred_handle == GSS_C_NO_CREDENTIAL) { - *minor_status = 0; - return GSS_S_NO_CRED; - } - - return gss_inquire_cred(minor_status, cred_handle, name, - lifetime, cred_usage, mechanisms); -} - -OM_uint32 GSSAPI_CALLCONV _gss_spnego_inquire_cred_by_mech ( - OM_uint32 * minor_status, - gss_const_cred_id_t cred_handle, - const gss_OID mech_type, - gss_name_t * name, - OM_uint32 * initiator_lifetime, - OM_uint32 * acceptor_lifetime, - gss_cred_usage_t * cred_usage - ) -{ - /* Similar to _gss_spnego_inquire_cred(), wrapper is required */ - heim_assert(gss_oid_equal(mech_type, GSS_SPNEGO_MECHANISM), - "Mechglue called inquire_cred_by_mech with wrong OID"); - - *minor_status = 0; - return GSS_S_BAD_MECH; -} - -OM_uint32 GSSAPI_CALLCONV -_gss_spnego_set_cred_option (OM_uint32 *minor_status, - gss_cred_id_t *cred_handle, - const gss_OID object, - const gss_buffer_t value) -{ - /* Similar to _gss_spnego_inquire_cred(), wrapper is required */ - if (cred_handle == NULL || *cred_handle == GSS_C_NO_CREDENTIAL) { - *minor_status = 0; - return GSS_S_NO_CRED; - } - - return gss_set_cred_option(minor_status, - cred_handle, - object, - value); -} - - -OM_uint32 GSSAPI_CALLCONV -_gss_spnego_set_neg_mechs (OM_uint32 *minor_status, - gss_cred_id_t cred_handle, - const gss_OID_set mech_list) -{ - OM_uint32 major, minor; - gss_OID_set mechs = GSS_C_NO_OID_SET; - size_t i; - - if (cred_handle != GSS_C_NO_CREDENTIAL) { - major = gss_inquire_cred(minor_status, cred_handle, - NULL, NULL, NULL, &mechs); - if (GSS_ERROR(major)) - return major; - - for (i = 0; i < mechs->count; i++) { - int present; - - major = gss_test_oid_set_member(minor_status, - &mechs->elements[i], - mech_list, &present); - if (GSS_ERROR(major)) - break; - - if (!present) { - major = gss_release_cred_by_mech(minor_status, - cred_handle, - &mechs->elements[i]); - if (GSS_ERROR(major)) - break; - } - } - } else { - /* - * RFC 4178 says that GSS_Set_neg_mechs() on NULL credential sets - * the negotiable mechs for the default credential, but neither - * MIT nor Heimdal support this presently. - */ - major = GSS_S_NO_CRED; - } - - gss_release_oid_set(&minor, &mechs); - - return major; -} - -OM_uint32 GSSAPI_CALLCONV -_gss_spnego_get_neg_mechs (OM_uint32 *minor_status, - gss_const_cred_id_t cred_handle, - gss_OID_set *mech_list) -{ - return gss_inquire_cred(minor_status, cred_handle, - NULL, NULL, NULL, mech_list); -} diff --git a/lib/gssapi/spnego/external.c b/lib/gssapi/spnego/external.c index 9ced91ead..99b3412d8 100644 --- a/lib/gssapi/spnego/external.c +++ b/lib/gssapi/spnego/external.c @@ -88,9 +88,9 @@ static gssapi_mech_interface_desc spnego_mech = { GMI_VERSION, "spnego", {6, rk_UNCONST("\x2b\x06\x01\x05\x05\x02") }, - 0, + GM_USE_MG_CRED, NULL, /* gm_acquire_cred */ - gss_release_cred, + NULL, /* gm_release_cred */ _gss_spnego_init_sec_context, _gss_spnego_accept_sec_context, _gss_spnego_process_context_token, @@ -107,11 +107,11 @@ static gssapi_mech_interface_desc spnego_mech = { _gss_spnego_import_name, _gss_spnego_export_name, _gss_spnego_release_name, - _gss_spnego_inquire_cred, + NULL, /* gm_inquire_cred */ _gss_spnego_inquire_context, _gss_spnego_wrap_size_limit, - gss_add_cred, - _gss_spnego_inquire_cred_by_mech, + NULL, /* gm_add_cred */ + NULL, /* gm_inquire_cred_by_mech */ _gss_spnego_export_sec_context, _gss_spnego_import_sec_context, NULL /* _gss_spnego_inquire_names_for_mech */, @@ -119,17 +119,17 @@ static gssapi_mech_interface_desc spnego_mech = { _gss_spnego_canonicalize_name, _gss_spnego_duplicate_name, _gss_spnego_inquire_sec_context_by_oid, - gss_inquire_cred_by_oid, + NULL, /* gm_inquire_cred_by_oid */ _gss_spnego_set_sec_context_option, - _gss_spnego_set_cred_option, + NULL, /* gm_set_cred_option */ _gss_spnego_pseudo_random, _gss_spnego_wrap_iov, _gss_spnego_unwrap_iov, _gss_spnego_wrap_iov_length, NULL, - gss_export_cred, - gss_import_cred, - _gss_spnego_acquire_cred_from, + NULL, /* gm_export_cred */ + NULL, /* gm_import_cred */ + NULL, /* gm_acquire_cred_from */ NULL, NULL, NULL, @@ -146,11 +146,9 @@ static gssapi_mech_interface_desc spnego_mech = { NULL, /* gm_set_name_attribute */ NULL, /* gm_delete_name_attribute */ NULL, /* gm_export_name_composite */ - gss_duplicate_cred, - gss_add_cred_from, + NULL, /* gm_duplicate_cred */ + NULL, /* gm_add_cred_from */ NULL, /* gm_store_cred_into */ - _gss_spnego_set_neg_mechs, - _gss_spnego_get_neg_mechs, NULL, /* gm_query_mechanism_info */ NULL, /* gm_query_meta_data */ NULL, /* gm_exchange_meta_data */ diff --git a/lib/gssapi/test_context.c b/lib/gssapi/test_context.c index 1462da190..b59bbd83a 100644 --- a/lib/gssapi/test_context.c +++ b/lib/gssapi/test_context.c @@ -627,6 +627,7 @@ main(int argc, char **argv) gss_OID_set mechoids = GSS_C_NO_OID_SET; gss_key_value_element_desc client_cred_elements[2]; gss_key_value_set_desc client_cred_store; + gss_OID_set actual_mechs = GSS_C_NO_OID_SET; setprogname(argv[0]); @@ -756,7 +757,7 @@ main(int argc, char **argv) mechoids, GSS_C_INITIATE, &client_cred, - NULL, + &actual_mechs, NULL); if (GSS_ERROR(maj_stat)) { if (mechoids != GSS_C_NO_OID_SET && mechoids->count == 1) @@ -775,13 +776,41 @@ main(int argc, char **argv) client_cred_store.count ? &client_cred_store : GSS_C_NO_CRED_STORE, &client_cred, - NULL, + &actual_mechs, NULL); if (GSS_ERROR(maj_stat)) errx(1, "gss_acquire_cred: %s", gssapi_err(maj_stat, min_stat, GSS_C_NO_OID)); } + if (verbose_flag) { + size_t i; + + printf("cred mechs:"); + for (i = 0; i < actual_mechs->count; i++) + printf(" %s", oid_to_string(&actual_mechs->elements[i])); + printf("\n"); + } + + if (ei_cred_flag) { + gss_cred_id_t cred2 = GSS_C_NO_CREDENTIAL; + gss_buffer_desc cb; + + maj_stat = gss_export_cred(&min_stat, client_cred, &cb); + if (maj_stat != GSS_S_COMPLETE) + errx(1, "export cred failed: %s", + gssapi_err(maj_stat, min_stat, NULL)); + + maj_stat = gss_import_cred(&min_stat, &cb, &cred2); + if (maj_stat != GSS_S_COMPLETE) + errx(1, "import cred failed: %s", + gssapi_err(maj_stat, min_stat, NULL)); + + gss_release_buffer(&min_stat, &cb); + gss_release_cred(&min_stat, &client_cred); + client_cred = cred2; + } + if (limit_enctype_string) { krb5_error_code ret; @@ -1242,6 +1271,7 @@ main(int argc, char **argv) } gss_release_cred(&min_stat, &client_cred); + gss_release_oid_set(&min_stat, &actual_mechs); empty_release(); krb5_free_context(context);