diff options
author | Luke Howard <lukeh@padl.com> | 2009-08-21 11:33:44 +0000 |
---|---|---|
committer | Luke Howard <lukeh@padl.com> | 2009-08-21 11:33:44 +0000 |
commit | 19ce682cda48f5c7e356bb12409ed60f6b0813ad (patch) | |
tree | 80e8ceb3ece9db1cacdc99ba3873848db1ce8399 | |
parent | bf5d2e0d3f519f6225bc89d08f33c1b9a9efeffa (diff) | |
download | krb5-19ce682cda48f5c7e356bb12409ed60f6b0813ad.zip krb5-19ce682cda48f5c7e356bb12409ed60f6b0813ad.tar.gz krb5-19ce682cda48f5c7e356bb12409ed60f6b0813ad.tar.bz2 |
Implement gss_{acquire,add}_cred_with_{name,cred} as suggested by Nico Williams
git-svn-id: svn://anonsvn.mit.edu/krb5/users/lhoward/s4u@22555 dc483132-0cff-0310-8789-dd5450dbe970
21 files changed, 2262 insertions, 669 deletions
diff --git a/src/lib/gssapi/generic/gssapi_ext.h b/src/lib/gssapi/generic/gssapi_ext.h index 40f5ab8..de4705d 100644 --- a/src/lib/gssapi/generic/gssapi_ext.h +++ b/src/lib/gssapi/generic/gssapi_ext.h @@ -254,6 +254,67 @@ OM_uint32 KRB5_CALLCONV gss_release_iov_buffer gss_iov_buffer_desc *, /* iov */ int); /* iov_count */ + +/* + * Protocol transition + */ +OM_uint32 KRB5_CALLCONV +gss_acquire_cred_with_name( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t, /* impersonator_cred_handle */ + const gss_name_t, /* desired_name */ + OM_uint32, /* time_req */ + const gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *); /* time_rec */ + +OM_uint32 KRB5_CALLCONV +gss_add_cred_with_name( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t, /* impersonator_cred_handle */ + gss_cred_id_t, /* input_cred_handle */ + const gss_name_t, /* desired_name */ + const gss_OID, /* desired_mech */ + gss_cred_usage_t, /* cred_usage */ + OM_uint32, /* initiator_time_req */ + OM_uint32, /* acceptor_time_req */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *, /* initiator_time_rec */ + OM_uint32 *); /* acceptor_time_rec */ + +/* + * Constrained delegation + */ +OM_uint32 KRB5_CALLCONV +gss_acquire_cred_with_cred( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t, /* impersonator_cred_handle */ + const gss_cred_id_t, /* subject_cred_handle */ + OM_uint32, /* time_req */ + const gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *); /* time_rec */ + +OM_uint32 KRB5_CALLCONV +gss_add_cred_with_cred( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t, /* impersonator_cred_handle */ + gss_cred_id_t, /* input_cred_handle */ + const gss_cred_id_t, /* subject_cred_handle */ + const gss_OID, /* desired_mech */ + gss_cred_usage_t, /* cred_usage */ + OM_uint32, /* initiator_time_req */ + OM_uint32, /* acceptor_time_req */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *, /* initiator_time_rec */ + OM_uint32 *); /* acceptor_time_rec */ + #ifdef __cplusplus } #endif diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c index 25cfd42..7437d5d 100644 --- a/src/lib/gssapi/krb5/accept_sec_context.c +++ b/src/lib/gssapi/krb5/accept_sec_context.c @@ -1,6 +1,6 @@ /* -*- mode: c; indent-tabs-mode: nil -*- */ /* - * Copyright 2000, 2004, 2007-2009 by the Massachusetts Institute of Technology. + * Copyright 2000, 2004, 2007, 2008 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may @@ -113,75 +113,94 @@ #endif #ifndef LEAN_CLIENT -krb5_error_code -krb5_to_gss_cred(krb5_context context, - krb5_creds *creds, - krb5_gss_cred_id_t *out_cred) + +static krb5_error_code +create_constrained_deleg_creds(context, ticket, out_cred) + krb5_context context; + krb5_ticket *ticket; + krb5_gss_cred_id_t *out_cred; { krb5_error_code retval; - krb5_ccache ccache = NULL, ccachep; - krb5_gss_cred_id_t cred = NULL; - - if (out_cred == NULL || *out_cred == NULL) { - retval = krb5_cc_new_unique(context, "MEMORY", NULL, &ccache); - if (retval != 0) - goto cleanup; + krb5_gss_cred_id_t cred; + krb5_ccache ccache; + krb5_creds krb_creds; + krb5_data *data; + + if (out_cred == NULL) + return 0; /* nothing to do */ + + memset(&krb_creds, 0, sizeof(krb_creds)); + krb_creds.client = ticket->enc_part2->client; + krb_creds.server = ticket->server; + krb_creds.keyblock = *(ticket->enc_part2->session); + krb_creds.ticket_flags = ticket->enc_part2->flags; + krb_creds.times = ticket->enc_part2->times; + krb_creds.magic = KV5M_CREDS; + krb_creds.authdata = NULL; + + retval = encode_krb5_ticket(ticket, &data); + if (retval) + return retval; - retval = krb5_cc_initialize(context, ccache, creds->client); - if (retval != 0) - goto cleanup; + krb_creds.ticket = *data; - ccachep = ccache; - } else { - ccachep = (*out_cred)->ccache; + retval = krb5_cc_new_unique(context, "MEMORY", NULL, &ccache); + if (retval) { + krb5_free_data(context, data); + return retval; } - retval = krb5_cc_store_cred(context, ccachep, creds); - if (retval != 0) - goto cleanup; + retval = krb5_cc_initialize(context, ccache, ticket->enc_part2->client); + if (retval) { + krb5_cc_destroy(context, ccache); + krb5_free_data(context, data); + return retval; + } - if (out_cred != NULL && *out_cred == NULL) { - cred = (krb5_gss_cred_id_t)xmalloc(sizeof(*cred)); - if (cred == NULL) { - retval = ENOMEM; - goto cleanup; - } - memset(cred, 0, sizeof(*cred)); + retval = krb5_cc_store_cred(context, ccache, &krb_creds); + if (retval) { + krb5_cc_destroy(context, ccache); + krb5_free_data(context, data); + return retval; + } - retval = k5_mutex_init(&cred->lock); - if (retval != 0) - goto cleanup; + krb5_free_data(context, data); - retval = krb5_copy_principal(context, creds->client, &cred->princ); - if (retval != 0) - goto cleanup; + cred = (krb5_gss_cred_id_t)xmalloc(sizeof(*cred)); + if (cred == NULL) { + krb5_cc_destroy(context, ccache); + return ENOMEM; + } - cred->usage = GSS_C_INITIATE; /* we can't accept with this */ - /* cred->princ already set */ - cred->prerfc_mech = 1; /* this cred will work with all three mechs */ - cred->rfc_mech = 1; - cred->keytab = NULL; /* no keytab associated with this... */ - cred->tgt_expire = creds->times.endtime; /* store the end time */ - cred->ccache = ccache; /* the ccache containing the credential */ - ccache = NULL; /* cred takes ownership so don't destroy */ + memset(cred, 0, sizeof(*cred)); - *out_cred = cred; - cred = NULL; + retval = k5_mutex_init(&cred->lock); + if (retval) { + krb5_cc_destroy(context, ccache); + xfree(cred); + return retval; } -cleanup: - if (ccache != NULL) - krb5_cc_destroy(context, ccache); - if (cred != NULL) { - if (cred->princ != NULL) - krb5_free_principal(context, cred->princ); + retval = krb5_copy_principal(context, ticket->enc_part2->client, &cred->princ); + if (retval) { k5_mutex_destroy(&cred->lock); + krb5_cc_destroy(context, ccache); xfree(cred); + return ENOMEM; } - return retval; -} + cred->usage = GSS_C_INITIATE; /* we can't accept with this */ + /* cred->princ already set */ + cred->prerfc_mech = 1; /* this cred will work with all three mechs */ + cred->rfc_mech = 1; + cred->keytab = NULL; /* no keytab associated with this... */ + cred->tgt_expire = krb_creds.times.endtime; /* store the end time */ + cred->ccache = ccache; /* the ccache containing the credential */ + + *out_cred = cred; + return 0; +} /* Decode, decrypt and store the forwarded creds in the local ccache. */ static krb5_error_code @@ -193,12 +212,11 @@ rd_and_store_for_creds(context, auth_context, inbuf, out_cred) { krb5_creds ** creds = NULL; krb5_error_code retval; + krb5_ccache ccache = NULL; + krb5_gss_cred_id_t cred = NULL; krb5_auth_context new_auth_ctx = NULL; krb5_int32 flags_org; - if (out_cred != NULL) - *out_cred = NULL; - if ((retval = krb5_auth_con_getflags(context, auth_context, &flags_org))) return retval; krb5_auth_con_setflags(context, auth_context, @@ -234,106 +252,80 @@ rd_and_store_for_creds(context, auth_context, inbuf, out_cred) goto cleanup; } - retval = krb5_to_gss_cred(context, creds[0], out_cred); - if (retval) - goto cleanup; - - /* If there were errors, there might have been a memory leak - if (!cred) - if ((retval = krb5_cc_close(context, ccache))) - goto cleanup; - */ -cleanup: - if (creds) - krb5_free_tgt_creds(context, creds); - - if (new_auth_ctx) - krb5_auth_con_free(context, new_auth_ctx); - - krb5_auth_con_setflags(context, auth_context, flags_org); - - return retval; -} - -/* - * Get credentials for constrained delegation. We assume the - * default ccache has a TGT in the acceptor's name. - */ -static krb5_error_code -kg_acquire_s4u2proxy_creds(krb5_context context, - krb5_gss_cred_id_t acceptor_cred, - krb5_ticket *ticket, - krb5_principal *targets, - krb5_gss_cred_id_t *out_cred) -{ - krb5_error_code retval; - krb5_principal princ = NULL; - int i; - - assert(targets != NULL); - assert(targets[0] != NULL); - - if (out_cred != NULL) - *out_cred = NULL; - - if (acceptor_cred->ccache == NULL) { - retval = EINVAL; + if ((retval = krb5_cc_new_unique(context, "MEMORY", NULL, &ccache))) { + ccache = NULL; goto cleanup; } - retval = krb5_cc_get_principal(context, acceptor_cred->ccache, &princ); - if (retval != 0) + if ((retval = krb5_cc_initialize(context, ccache, creds[0]->client))) goto cleanup; - if (krb5_principal_compare(context, princ, ticket->server) == FALSE) { - retval = KG_CCACHE_NOMATCH; + if ((retval = krb5_cc_store_cred(context, ccache, creds[0]))) goto cleanup; - } - for (i = 0; targets[i] != NULL; i++) { - krb5_creds pcreds, *creds; - - memset(&pcreds, 0, sizeof(pcreds)); + /* generate a delegated credential handle */ + if (out_cred) { + /* allocate memory for a cred_t... */ + if (!(cred = + (krb5_gss_cred_id_t) xmalloc(sizeof(krb5_gss_cred_id_rec)))) { + retval = ENOMEM; /* out of memory? */ + goto cleanup; + } - pcreds.client = ticket->enc_part2->client; - pcreds.server = targets[i]; + /* zero it out... */ + memset(cred, 0, sizeof(krb5_gss_cred_id_rec)); - retval = krb5_get_credentials_for_proxy(context, - KRB5_GC_CANONICALIZE | KRB5_GC_NO_STORE, - acceptor_cred->ccache, - &pcreds, - ticket, - &creds); - if (retval != 0) + retval = k5_mutex_init(&cred->lock); + if (retval) { + xfree(cred); + cred = NULL; goto cleanup; + } - retval = krb5_to_gss_cred(context, creds, out_cred); - if (retval != 0) { - krb5_free_creds(context, creds); + /* copy the client principle into it... */ + if ((retval = + krb5_copy_principal(context, creds[0]->client, &(cred->princ)))) { + k5_mutex_destroy(&cred->lock); + retval = ENOMEM; /* out of memory? */ + xfree(cred); /* clean up memory on failure */ + cred = NULL; goto cleanup; } - krb5_free_creds(context, creds); + + cred->usage = GSS_C_INITIATE; /* we can't accept with this */ + /* cred->princ already set */ + cred->prerfc_mech = 1; /* this cred will work with all three mechs */ + cred->rfc_mech = 1; + cred->keytab = NULL; /* no keytab associated with this... */ + cred->tgt_expire = creds[0]->times.endtime; /* store the end time */ + cred->ccache = ccache; /* the ccache containing the credential */ + ccache = NULL; /* cred takes ownership so don't destroy */ } + /* If there were errors, there might have been a memory leak + if (!cred) + if ((retval = krb5_cc_close(context, ccache))) + goto cleanup; + */ cleanup: - if (princ != NULL) - krb5_free_principal(context, princ); - if (retval != 0 && out_cred != NULL && *out_cred != NULL) { - krb5_gss_cred_id_t cred = *out_cred; - - if (cred->princ != NULL) - krb5_free_principal(context, cred->princ); - if (cred->ccache != NULL) - krb5_cc_destroy(context, cred->ccache); - k5_mutex_destroy(&cred->lock); - xfree(cred); + if (creds) + krb5_free_tgt_creds(context, creds); - *out_cred = NULL; - } + if (ccache) + (void)krb5_cc_destroy(context, ccache); + + if (out_cred) + *out_cred = cred; /* return credential */ + + if (new_auth_ctx) + krb5_auth_con_free(context, new_auth_ctx); + + krb5_auth_con_setflags(context, auth_context, flags_org); return retval; } + /* * Performs third leg of DCE authentication */ @@ -491,7 +483,6 @@ kg_accept_krb5(minor_status, context_handle, int no_encap = 0; krb5_flags ap_req_options = 0; krb5_enctype negotiated_etype; - gss_cred_usage_t usage = GSS_C_ACCEPT; code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION); if (code) { @@ -519,22 +510,14 @@ kg_accept_krb5(minor_status, context_handle, if (mech_type) *mech_type = GSS_C_NULL_OID; /* return a bogus cred handle */ - if (delegated_cred_handle) { + if (delegated_cred_handle) *delegated_cred_handle = GSS_C_NO_CREDENTIAL; - if (*context_handle != GSS_C_NO_CONTEXT) { - ctx = (krb5_gss_ctx_id_rec *)*context_handle; - - if (ctx->s4u2proxy_targets != NULL) - usage = GSS_C_BOTH; - } - } - /* handle default cred handle */ if (verifier_cred_handle == GSS_C_NO_CREDENTIAL) { major_status = krb5_gss_acquire_cred(minor_status, GSS_C_NO_NAME, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, - usage, &cred_handle, + GSS_C_ACCEPT, &cred_handle, NULL, NULL); if (major_status != GSS_S_COMPLETE) { code = *minor_status; @@ -554,7 +537,7 @@ kg_accept_krb5(minor_status, context_handle, /* make sure the supplied credentials are valid for accept */ - if ((cred->usage != usage) && + if ((cred->usage != GSS_C_ACCEPT) && (cred->usage != GSS_C_BOTH)) { code = 0; major_status = GSS_S_NO_CRED; @@ -882,47 +865,17 @@ kg_accept_krb5(minor_status, context_handle, goto fail; } - if (*context_handle != GSS_C_NO_CONTEXT) { - ctx = (krb5_gss_ctx_id_rec *)*context_handle; - - assert(ctx->s4u2proxy_targets != NULL); - assert(ctx->mech_used == GSS_C_NO_OID); - - if (delegated_cred_handle != NULL && deleg_cred == NULL) { - code = kg_acquire_s4u2proxy_creds(context, - (krb5_gss_cred_id_t)cred_handle, - ticket, - ctx->s4u2proxy_targets, - &deleg_cred); - if (code) { - major_status = GSS_S_CRED_UNAVAIL; - goto fail; - } - gss_flags |= GSS_C_DELEG_FLAG; - } - } else { - ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(*ctx)); - if (ctx == NULL) { - code = ENOMEM; - major_status = GSS_S_FAILURE; - goto fail; + /* create the ctx struct and start filling it in */ - } - memset(ctx, 0, sizeof(*ctx)); - /* Intern the ctx pointer so that delete_sec_context works */ - if (!kg_save_ctx_id((gss_ctx_id_t) ctx)) { - xfree(ctx); - ctx = NULL; - code = G_VALIDATE_FAILED; - major_status = GSS_S_FAILURE; - goto fail; - } + if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec))) + == NULL) { + code = ENOMEM; + major_status = GSS_S_FAILURE; + goto fail; } + memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec)); ctx->mech_used = (gss_OID) mech_used; - - /* create the ctx struct and start filling it in */ - ctx->auth_context = auth_context; ctx->initiate = 0; ctx->gss_flags = (GSS_C_TRANS_FLAG | @@ -935,7 +888,17 @@ kg_accept_krb5(minor_status, context_handle, ctx->big_endian = bigend; ctx->cred_rcache = cred_rcache; - /* XXX move this into gss_name_t */ + /* Intern the ctx pointer so that delete_sec_context works */ + if (! kg_save_ctx_id((gss_ctx_id_t) ctx)) { + xfree(ctx); + ctx = 0; + + code = G_VALIDATE_FAILED; + major_status = GSS_S_FAILURE; + goto fail; + } + + /* XXX move this into gss_name_t */ if ( (code = krb5_merge_authdata(context, ticket->enc_part2->authorization_data, authdat->authorization_data, @@ -991,6 +954,19 @@ kg_accept_krb5(minor_status, context_handle, ctx->krb_times = ticket->enc_part2->times; /* struct copy */ ctx->krb_flags = ticket->enc_part2->flags; + if (delegated_cred_handle != NULL && deleg_cred == NULL) { + /* + * Now, we always fabricate a delegated credentials handle + * containing the service ticket to ourselves, which can be + * used for S4U2Proxy. + */ + code = create_constrained_deleg_creds(context, ticket, &deleg_cred); + if (code) { + major_status = GSS_S_FAILURE; + goto fail; + } + } + krb5_free_ticket(context, ticket); /* Done with ticket */ { @@ -1180,8 +1156,8 @@ kg_accept_krb5(minor_status, context_handle, if (src_name) *src_name = (gss_name_t) name; - if (delegated_cred_handle && deleg_cred) { - if (!kg_save_cred_id((gss_cred_id_t) deleg_cred)) { + if (delegated_cred_handle) { + if (!kg_save_cred_id((gss_cred_id_t) deleg_cred)) { major_status = GSS_S_FAILURE; code = G_VALIDATE_FAILED; goto fail; @@ -1307,6 +1283,7 @@ done: } return (major_status); } +#endif /* LEAN_CLIENT */ OM_uint32 krb5_gss_accept_sec_context(minor_status, context_handle, @@ -1341,7 +1318,7 @@ krb5_gss_accept_sec_context(minor_status, context_handle, input_chan_bindings, src_name, mech_type, output_token, ret_flags, time_rec, delegated_cred_handle); - } else if (ctx->mech_used != GSS_C_NO_OID) { + } else { *minor_status = EINVAL; save_error_string(EINVAL, "accept_sec_context called with existing context handle"); return GSS_S_FAILURE; @@ -1354,85 +1331,3 @@ krb5_gss_accept_sec_context(minor_status, context_handle, output_token, ret_flags, time_rec, delegated_cred_handle); } - -/* - * This function manages a list of constrained delegation targets in a - * skeletal context, such that when gss_accept_sec_context() is called, - * the returned delegated credentials can be appropriately constructed - * via a S4U2Proxy request to the KDC. - */ -OM_uint32 -gss_krb5int_add_sec_context_delegatee(OM_uint32 *minor_status, - gss_ctx_id_t *context_handle, - const gss_OID desired_object, - gss_buffer_t value) -{ - krb5_gss_ctx_id_rec *ctx = (krb5_gss_ctx_id_rec *)*context_handle; - OM_uint32 minor, major_status = GSS_S_FAILURE; - gss_name_t target = GSS_C_NO_NAME; - krb5_principal *targets; - int i; - - if (ctx != NULL) { - if (ctx->mech_used != GSS_C_NO_OID) { - *minor_status = EINVAL; - save_error_string(EINVAL, "add_sec_context_delegatee called with active context handle"); - goto cleanup; - } - } else { - ctx = (krb5_gss_ctx_id_rec *)xmalloc(sizeof(*ctx)); - if (ctx == NULL) { - *minor_status = ENOMEM; - goto cleanup; - } - memset(ctx, 0, sizeof(*ctx)); - ctx->mech_used = GSS_C_NO_OID; - - if (!kg_save_ctx_id((gss_ctx_id_t) ctx)) { - xfree(ctx); - ctx = NULL; - *minor_status = G_VALIDATE_FAILED; - goto cleanup; - } - - *context_handle = (gss_ctx_id_t)ctx; - } - - major_status = krb5_gss_import_name(minor_status, value, - gss_nt_exported_name, &target); - if (GSS_ERROR(major_status)) - goto cleanup; - - if (ctx->s4u2proxy_targets != NULL) { - for (i = 0; ctx->s4u2proxy_targets[i] != NULL; i++) - ; - } else - i = 0; - - targets = (krb5_principal *)realloc(ctx->s4u2proxy_targets, - (i + 2) * sizeof(krb5_principal)); - if (targets == NULL) { - *minor_status = ENOMEM; - major_status = GSS_S_FAILURE; - goto cleanup; - } - - targets[i] = (krb5_principal)target; - targets[i + 1] = NULL; - - target = NULL; - ctx->s4u2proxy_targets = targets; - -cleanup: - if (target != GSS_C_NO_NAME) - krb5_gss_release_name(&minor, &target); - - if (GSS_ERROR(major_status)) { - krb5_gss_delete_sec_context(&minor, (gss_ctx_id_t *)&ctx, NULL); - *context_handle = GSS_C_NO_CONTEXT; - } - - return major_status; -} -#endif /* !LEAN_CLIENT */ - diff --git a/src/lib/gssapi/krb5/acquire_cred.c b/src/lib/gssapi/krb5/acquire_cred.c index 176d3d0..4427ed7 100644 --- a/src/lib/gssapi/krb5/acquire_cred.c +++ b/src/lib/gssapi/krb5/acquire_cred.c @@ -532,8 +532,8 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req, cred->usage = cred_usage; cred->princ = NULL; - cred->prerfc_mech = req_old; - cred->rfc_mech = req_new; + cred->prerfc_mech = (req_old != 0); + cred->rfc_mech = (req_new != 0); #ifndef LEAN_CLIENT cred->keytab = NULL; diff --git a/src/lib/gssapi/krb5/delete_sec_context.c b/src/lib/gssapi/krb5/delete_sec_context.c index e706555..33e0e31 100644 --- a/src/lib/gssapi/krb5/delete_sec_context.c +++ b/src/lib/gssapi/krb5/delete_sec_context.c @@ -109,13 +109,6 @@ krb5_gss_delete_sec_context(minor_status, context_handle, output_token) if (ctx->authdata) krb5_free_authdata(context, ctx->authdata); - if (ctx->s4u2proxy_targets != NULL) { - int i; - - for (i = 0; ctx->s4u2proxy_targets[i] != NULL; i++) - krb5_gss_release_name(minor_status, (gss_name_t *)&ctx->s4u2proxy_targets[i]); - } - if (ctx->k5_context) krb5_free_context(ctx->k5_context); diff --git a/src/lib/gssapi/krb5/gssapiP_krb5.h b/src/lib/gssapi/krb5/gssapiP_krb5.h index 73c2589..51203f3 100644 --- a/src/lib/gssapi/krb5/gssapiP_krb5.h +++ b/src/lib/gssapi/krb5/gssapiP_krb5.h @@ -162,8 +162,9 @@ typedef struct _krb5_gss_cred_id_rec { /* name/type of credential */ gss_cred_usage_t usage; krb5_principal princ; /* this is not interned as a gss_name_t */ - int prerfc_mech; - int rfc_mech; + unsigned int prerfc_mech : 1; + unsigned int rfc_mech : 1; + unsigned int proxy_cred : 1; /* keytab (accept) data */ krb5_keytab keytab; @@ -217,7 +218,6 @@ typedef struct _krb5_gss_ctx_id_rec { krb5_cksumtype acceptor_subkey_cksumtype; int cred_rcache; /* did we get rcache from creds? */ krb5_authdata **authdata; - krb5_principal *s4u2proxy_targets; /* constrained delegation targets */ } krb5_gss_ctx_id_rec, *krb5_gss_ctx_id_t; extern g_set kg_vdb; @@ -790,6 +790,28 @@ OM_uint32 krb5_gss_validate_cred gss_cred_id_t /* cred */ ); +OM_uint32 krb5_gss_acquire_cred_with_name( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t, /* impersonator_cred_handle */ + const gss_name_t, /* desired_name */ + OM_uint32, /* time_req */ + const gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *); /* time_rec */ + +OM_uint32 krb5_gss_acquire_cred_with_cred( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t, /* impersonator_cred_handle */ + const gss_cred_id_t, /* subject_cred_handle */ + OM_uint32, /* time_req */ + const gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *); /* time_rec */ + OM_uint32 krb5_gss_validate_cred_1(OM_uint32 * /* minor_status */, gss_cred_id_t /* cred_handle */, @@ -927,21 +949,6 @@ gss_krb5int_extract_authtime_from_sec_context(OM_uint32 *, const gss_OID, gss_buffer_set_t *); -#define GSS_KRB5_ADD_SEC_CONTEXT_DELEGATEE_OID_LENGTH 11 -#define GSS_KRB5_ADD_SEC_CONTEXT_DELEGATEE_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0d" - -OM_uint32 -gss_krb5int_add_sec_context_delegatee(OM_uint32 *, gss_ctx_id_t *, const gss_OID, gss_buffer_t); - -#define GSS_KRB5_UNWRAP_CRED_HANDLE_OID_LENGTH 11 -#define GSS_KRB5_UNWRAP_CRED_HANDLE_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0e" - -OM_uint32 -gss_krb5int_unwrap_cred_handle(OM_uint32 *, - gss_cred_id_t, - const gss_OID, - const gss_buffer_t); - #ifdef _GSS_STATIC_LINK int gss_krb5int_lib_init(void); void gss_krb5int_lib_fini(void); diff --git a/src/lib/gssapi/krb5/gssapi_krb5.c b/src/lib/gssapi/krb5/gssapi_krb5.c index 73ad896..4ead32c 100644 --- a/src/lib/gssapi/krb5/gssapi_krb5.c +++ b/src/lib/gssapi/krb5/gssapi_krb5.c @@ -452,10 +452,6 @@ static struct { gss_OID_desc oid; OM_uint32 (*func)(OM_uint32 *, gss_ctx_id_t *, const gss_OID, const gss_buffer_t); } krb5_gss_set_sec_context_option_ops[] = { - { - {GSS_KRB5_ADD_SEC_CONTEXT_DELEGATEE_OID_LENGTH, GSS_KRB5_ADD_SEC_CONTEXT_DELEGATEE_OID}, - gss_krb5int_add_sec_context_delegatee - } }; static OM_uint32 @@ -520,10 +516,6 @@ static struct { {GSS_KRB5_SET_CRED_RCACHE_OID_LENGTH, GSS_KRB5_SET_CRED_RCACHE_OID}, gss_krb5int_set_cred_rcache }, - { - {GSS_KRB5_UNWRAP_CRED_HANDLE_OID_LENGTH, GSS_KRB5_UNWRAP_CRED_HANDLE_OID}, - gss_krb5int_unwrap_cred_handle - }, }; static OM_uint32 @@ -685,6 +677,10 @@ static struct gss_config krb5_mechanism = { krb5_gss_unwrap_iov, krb5_gss_wrap_iov_length, NULL, /* complete_auth_token */ + krb5_gss_acquire_cred_with_name, + NULL, /* krb5_gss_add_cred_with_name */ + krb5_gss_acquire_cred_with_cred, + NULL /* krb5_gss_add_cred_with_cred */ }; diff --git a/src/lib/gssapi/krb5/init_sec_context.c b/src/lib/gssapi/krb5/init_sec_context.c index a5ea299..b58d287 100644 --- a/src/lib/gssapi/krb5/init_sec_context.c +++ b/src/lib/gssapi/krb5/init_sec_context.c @@ -128,25 +128,61 @@ static krb5_error_code get_credentials(context, cred, server, now, krb5_creds **out_creds; { krb5_error_code code; - krb5_creds in_creds; + krb5_creds in_creds, evidence_creds; + krb5_flags flags = 0; + krb5_principal cc_princ = NULL; k5_mutex_assert_locked(&cred->lock); memset(&in_creds, 0, sizeof(krb5_creds)); + memset(&evidence_creds, 0, sizeof(krb5_creds)); in_creds.client = in_creds.server = NULL; - if ((code = krb5_copy_principal(context, cred->princ, &in_creds.client))) + if ((code = krb5_cc_get_principal(context, cred->ccache, &cc_princ))) goto cleanup; - if ((code = krb5_copy_principal(context, server, &in_creds.server))) - goto cleanup; - in_creds.times.endtime = endtime; - in_creds.keyblock.enctype = 0; + if (cred->proxy_cred) { + krb5_creds mcreds; + + flags |= KRB5_GC_CANONICALIZE | + KRB5_GC_NO_STORE | + KRB5_GC_CONSTRAINED_DELEGATION; + + memset(&mcreds, 0, sizeof(mcreds)); + + mcreds.magic = KV5M_CREDS; + mcreds.times.endtime = cred->tgt_expire; + mcreds.server = cc_princ; + mcreds.client = cred->princ; + + code = krb5_cc_retrieve_cred(context, cred->ccache, + KRB5_TC_MATCH_TIMES, &mcreds, + &evidence_creds); + if (code) + goto cleanup; + + in_creds.client = cc_princ; + in_creds.second_ticket = evidence_creds.ticket; + } else { + in_creds.client = cred->princ; + } - code = krb5_get_credentials(context, 0, cred->ccache, + in_creds.server = server; + in_creds.times.endtime = endtime; + + code = krb5_get_credentials(context, flags, cred->ccache, &in_creds, out_creds); if (code) goto cleanup; + if (flags & KRB5_GC_CONSTRAINED_DELEGATION) { + if (!krb5_principal_compare(context, cred->princ, + (*out_creds)->client)) { + /* server did not support constrained delegation */ + code = KRB5_KDCREP_MODIFIED; + goto cleanup; + } + } + /* * Enforce a stricter limit (without timeskew forgiveness at the * boundaries) because accept_sec_context code is also similarly @@ -159,10 +195,10 @@ static krb5_error_code get_credentials(context, cred, server, now, } cleanup: - if (in_creds.client) - krb5_free_principal(context, in_creds.client); - if (in_creds.server) - krb5_free_principal(context, in_creds.server); + if (cc_princ) + krb5_free_principal(context, cc_princ); + krb5_free_cred_contents(context, &evidence_creds); + return code; } struct gss_checksum_data { @@ -844,7 +880,6 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle, } } else { context = ((krb5_gss_ctx_id_rec *)*context_handle)->k5_context; - assert(((krb5_gss_ctx_id_rec *)*context_handle)->s4u2proxy_targets == NULL); } /* set up return values so they can be "freed" successfully */ diff --git a/src/lib/gssapi/krb5/krb5_gss_glue.c b/src/lib/gssapi/krb5/krb5_gss_glue.c index cda2e95..0345501 100644 --- a/src/lib/gssapi/krb5/krb5_gss_glue.c +++ b/src/lib/gssapi/krb5/krb5_gss_glue.c @@ -417,42 +417,3 @@ gsskrb5_extract_authtime_from_sec_context(OM_uint32 *minor_status, return GSS_S_COMPLETE; } -OM_uint32 KRB5_CALLCONV -gss_krb5_add_sec_context_delegatee(OM_uint32 *minor_status, - gss_ctx_id_t *context_handle, - gss_name_t name) -{ - static const gss_OID_desc req_oid = { - GSS_KRB5_ADD_SEC_CONTEXT_DELEGATEE_OID_LENGTH, - GSS_KRB5_ADD_SEC_CONTEXT_DELEGATEE_OID }; - OM_uint32 minor, major_status; - gss_buffer_desc req_buffer; - gss_name_t canon_name; - - if (name == GSS_C_NO_NAME) - return GSS_S_CALL_INACCESSIBLE_READ; - - major_status = gss_canonicalize_name(minor_status, name, - (gss_OID)gss_mech_krb5, - &canon_name); - if (GSS_ERROR(major_status)) - return major_status; - - /* export name to convert from union to mechanism name */ - major_status = gss_export_name(minor_status, canon_name, &req_buffer); - if (GSS_ERROR(major_status)) { - gss_release_name(&minor, &canon_name); - return major_status; - } - - major_status = gss_set_sec_context_option(minor_status, - context_handle, - (gss_OID)&req_oid, - &req_buffer); - - gss_release_buffer(&minor, &req_buffer); - gss_release_name(&minor, &canon_name); - - return major_status; -} - diff --git a/src/lib/gssapi/krb5/s4u_gss_glue.c b/src/lib/gssapi/krb5/s4u_gss_glue.c index 777152d..9b3e253 100644 --- a/src/lib/gssapi/krb5/s4u_gss_glue.c +++ b/src/lib/gssapi/krb5/s4u_gss_glue.c @@ -30,279 +30,550 @@ #endif #include <assert.h> -#ifndef LEAN_CLIENT - -OM_uint32 -gss_krb5int_unwrap_cred_handle(OM_uint32 *minor_status, - gss_cred_id_t cred_handle, - const gss_OID desired_object, - const gss_buffer_t value) +static OM_uint32 +kg_set_desired_mechs(OM_uint32 *minor_status, + const gss_OID_set desired_mechs, + krb5_gss_cred_id_t cred) { - krb5_gss_cred_id_t *mech_cred = (krb5_gss_cred_id_t *)value->value; - - assert(mech_cred != NULL); + unsigned int i; + + if (desired_mechs == GSS_C_NULL_OID_SET) { + cred->prerfc_mech = 1; + cred->rfc_mech = 1; + } else { + cred->prerfc_mech = 0; + cred->rfc_mech = 0; + + for (i = 0; i < desired_mechs->count; i++) { + if (g_OID_equal(gss_mech_krb5_old, &desired_mechs->elements[i])) + cred->prerfc_mech = 1; + else if (g_OID_equal(gss_mech_krb5, &desired_mechs->elements[i])) + cred->rfc_mech = 1; + } - *mech_cred = (krb5_gss_cred_id_t)cred_handle; + if (!cred->prerfc_mech && !cred->rfc_mech) { + *minor_status = 0; + return GSS_S_BAD_MECH; + } + } return GSS_S_COMPLETE; } -/* - * Unwrap a credentials handle to its mechanism specific equivalent - * by calling gssspi_set_cred_option(). - */ static OM_uint32 -kg_unwrap_cred_handle(OM_uint32 *minor_status, - gss_cred_id_t union_cred, - krb5_gss_cred_id_t *mech_cred) +kg_return_mechs(OM_uint32 *minor_status, + krb5_gss_cred_id_t cred, + gss_OID_set *actual_mechs) { - static const gss_OID_desc req_oid = { - GSS_KRB5_UNWRAP_CRED_HANDLE_OID_LENGTH, - GSS_KRB5_UNWRAP_CRED_HANDLE_OID }; - OM_uint32 major_status; - gss_buffer_desc req_buffer; + OM_uint32 major_status, minor; + gss_OID_set mechs; - if (union_cred == GSS_C_NO_CREDENTIAL) { - *mech_cred = NULL; + if (actual_mechs == NULL) return GSS_S_COMPLETE; - } - - req_buffer.value = mech_cred; - req_buffer.length = sizeof(mech_cred); - - major_status = gssspi_set_cred_option(minor_status, - union_cred, - (gss_OID)&req_oid, - &req_buffer); + major_status = generic_gss_create_empty_oid_set(minor_status, &mechs); if (GSS_ERROR(major_status)) return major_status; - major_status = krb5_gss_validate_cred(minor_status, (gss_cred_id_t)*mech_cred); - if (GSS_ERROR(major_status)) - return major_status; + if (cred->prerfc_mech) { + major_status = generic_gss_add_oid_set_member(minor_status, + gss_mech_krb5_old, + &mechs); + if (GSS_ERROR(major_status)) { + generic_gss_release_oid_set(&minor, &mechs); + return major_status; + } + } + if (cred->rfc_mech) { + major_status = generic_gss_add_oid_set_member(minor_status, + gss_mech_krb5, + &mechs); + if (GSS_ERROR(major_status)) { + generic_gss_release_oid_set(&minor, &mechs); + return major_status; + } + } + + *actual_mechs = mechs; return GSS_S_COMPLETE; +} +static int +kg_is_initiator_cred(krb5_gss_cred_id_t cred) +{ + return (cred->usage == GSS_C_INITIATE || cred->usage == GSS_C_BOTH); } -/* - * Unwrap a name to its mechanism specific equivalent by exporting - * and importing it. - */ static OM_uint32 -kg_unwrap_name(OM_uint32 *minor_status, - gss_name_t union_name, - gss_name_t *mech_name) +kg_impersonate(OM_uint32 *minor_status, + const krb5_gss_cred_id_t impersonator_cred, + const krb5_principal user, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + krb5_gss_cred_id_t *output_cred, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec, + krb5_context context) { - OM_uint32 minor, major_status; - gss_name_t canon_name; - gss_buffer_desc buffer; + OM_uint32 major_status; + krb5_error_code code; + krb5_gss_cred_id_t cred = NULL; + krb5_creds in_creds, *out_creds = NULL; - *mech_name = GSS_C_NO_NAME; + memset(&in_creds, 0, sizeof(in_creds)); + memset(&out_creds, 0, sizeof(out_creds)); - major_status = gss_canonicalize_name(minor_status, union_name, - (gss_OID)gss_mech_krb5, &canon_name); + k5_mutex_assert_locked(&impersonator_cred->lock); + + if (!kg_is_initiator_cred(impersonator_cred) || + impersonator_cred->ccache == NULL || + impersonator_cred->princ == NULL) { + *minor_status = (OM_uint32)G_BAD_USAGE; + major_status = GSS_S_FAILURE; + goto cleanup; + } + + cred = (krb5_gss_cred_id_t)xmalloc(sizeof(*cred)); + if (cred == NULL) { + *minor_status = ENOMEM; + major_status = GSS_S_FAILURE; + goto cleanup; + } + memset(cred, 0, sizeof(*cred)); + + code = k5_mutex_init(&cred->lock); + if (code != 0) { + *minor_status = code; + major_status = GSS_S_FAILURE; + goto cleanup; + } + + cred->usage = GSS_C_INITIATE; + + major_status = kg_set_desired_mechs(minor_status, desired_mechs, cred); if (GSS_ERROR(major_status)) - return major_status; + goto cleanup; - major_status = gss_export_name(minor_status, canon_name, &buffer); - if (GSS_ERROR(major_status)) { - gss_release_name(&minor, &canon_name); - return major_status; + code = krb5_copy_principal(context, user, &cred->princ); + if (code != 0) { + *minor_status = code; + major_status = GSS_S_FAILURE; + goto cleanup; + } + + in_creds.client = cred->princ; + in_creds.server = impersonator_cred->princ; + + if (impersonator_cred->req_enctypes != NULL) + in_creds.keyblock.enctype = impersonator_cred->req_enctypes[0]; + + code = krb5_get_credentials_for_user(context, + KRB5_GC_CANONICALIZE | KRB5_GC_NO_STORE, + impersonator_cred->ccache, + &in_creds, + NULL, &out_creds); + if (code != 0) { + *minor_status = code; + major_status = GSS_S_FAILURE; + goto cleanup; + } + + code = krb5_cc_new_unique(context, "MEMORY", NULL, &cred->ccache); + if (code != 0) { + *minor_status = code; + major_status = GSS_S_FAILURE; + goto cleanup; + } + + code = krb5_cc_initialize(context, cred->ccache, cred->princ); + if (code != 0) { + *minor_status = code; + major_status = GSS_S_FAILURE; + goto cleanup; } - gss_release_name(&minor, &canon_name); + code = krb5_cc_store_cred(context, cred->ccache, out_creds); + if (code != 0) { + *minor_status = code; + major_status = GSS_S_FAILURE; + goto cleanup; + } + + cred->tgt_expire = out_creds->times.endtime; - major_status = krb5_gss_import_name(minor_status, &buffer, - gss_nt_exported_name, mech_name); + if (time_rec != NULL) { + krb5_timestamp now; + + code = krb5_timeofday(context, &now); + if (code != 0) { + *minor_status = code; + major_status = GSS_S_FAILURE; + goto cleanup; + } + + *time_rec = cred->tgt_expire - now; + } + + major_status = kg_return_mechs(minor_status, cred, actual_mechs); + if (GSS_ERROR(major_status)) + goto cleanup; + + if (!kg_save_cred_id((gss_cred_id_t)cred)) { + *minor_status = (OM_uint32)G_VALIDATE_FAILED; + major_status = GSS_S_FAILURE; + goto cleanup; + } + + major_status = GSS_S_COMPLETE; + *output_cred = cred; + +cleanup: + if (GSS_ERROR(major_status) && cred != NULL) { + k5_mutex_destroy(&cred->lock); + if (cred->ccache != NULL) + krb5_cc_destroy(context, cred->ccache); + if (cred->princ != NULL) + krb5_free_principal(context, cred->princ); + xfree(cred); + } + + if (out_creds != NULL) + krb5_free_creds(context, out_creds); - gss_release_buffer(&minor, &buffer); return major_status; } -/* - * Acquire S4U2Self creds. - */ -static OM_uint32 -kg_acquire_s4u2self_creds(OM_uint32 *minor_status, - krb5_gss_cred_id_t acceptor_cred, - gss_name_t s4u_name, - OM_uint32 time_req, - krb5_gss_cred_id_t *initiator_cred, - krb5_context context) +OM_uint32 +krb5_gss_acquire_cred_with_name(OM_uint32 *minor_status, + const gss_cred_id_t impersonator_cred_handle, + const gss_name_t desired_name, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) { + OM_uint32 major_status; krb5_error_code code; - krb5_creds in_creds; - krb5_creds *out_creds = NULL; + krb5_gss_cred_id_t cred; + krb5_context context; - memset(&in_creds, 0, sizeof(in_creds)); + if (impersonator_cred_handle == GSS_C_NO_CREDENTIAL) + return GSS_S_CALL_INACCESSIBLE_READ; - in_creds.client = (krb5_principal)s4u_name; - in_creds.server = acceptor_cred->princ; + if (desired_name == GSS_C_NO_NAME) + return GSS_S_CALL_INACCESSIBLE_READ; - if (acceptor_cred->req_enctypes != NULL) - in_creds.keyblock.enctype = acceptor_cred->req_enctypes[0]; + if (output_cred_handle == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; - code = krb5_get_credentials_for_user(context, - KRB5_GC_CANONICALIZE | KRB5_GC_NO_STORE, - acceptor_cred->ccache, &in_creds, - NULL, &out_creds); + if (cred_usage != GSS_C_INITIATE) { + *minor_status = (OM_uint32)G_BAD_USAGE; + return GSS_S_FAILURE; + } + + *output_cred_handle = GSS_C_NO_CREDENTIAL; + if (actual_mechs != NULL) + *actual_mechs = GSS_C_NO_OID_SET; + if (time_rec != NULL) + *time_rec = 0; + + code = krb5_gss_init_context(&context); if (code != 0) { *minor_status = code; - save_error_info(*minor_status, context); return GSS_S_FAILURE; } - code = krb5_to_gss_cred(context, out_creds, initiator_cred); - if (code == 0) { - if (!kg_save_cred_id((gss_cred_id_t)*initiator_cred)) { - code = G_VALIDATE_FAILED; + major_status = krb5_gss_validate_cred_1(minor_status, + impersonator_cred_handle, + context); + if (GSS_ERROR(major_status)) { + krb5_free_context(context); + return major_status; + } - krb5_cc_destroy(context, (*initiator_cred)->ccache); - krb5_free_principal(context, (*initiator_cred)->princ); - k5_mutex_destroy(&(*initiator_cred)->lock); - xfree(*initiator_cred); - *initiator_cred = NULL; - } - } else - save_error_info(code, context); + major_status = kg_impersonate(minor_status, + (krb5_gss_cred_id_t)impersonator_cred_handle, + (krb5_principal)desired_name, + time_req, + desired_mechs, + &cred, + actual_mechs, + time_rec, + context); + + *output_cred_handle = (gss_cred_id_t)cred; + + k5_mutex_unlock(&((krb5_gss_cred_id_t)impersonator_cred_handle)->lock); + krb5_free_context(context); + + return major_status; + +} + +static krb5_error_code +kg_get_evidence_ticket(krb5_context context, + krb5_gss_cred_id_t impersonator_cred, + krb5_gss_cred_id_t subject_cred, + krb5_creds *ncreds) +{ + krb5_creds mcreds; - krb5_free_creds(context, out_creds); + memset(&mcreds, 0, sizeof(mcreds)); - *minor_status = code; + mcreds.magic = KV5M_CREDS; + mcreds.times.endtime = subject_cred->tgt_expire; + mcreds.server = impersonator_cred->princ; + mcreds.client = subject_cred->princ; - return (code != 0) ? GSS_S_FAILURE : GSS_S_COMPLETE; + return krb5_cc_retrieve_cred(context, subject_cred->ccache, + KRB5_TC_MATCH_TIMES, &mcreds, ncreds); } -/* Wrap up S4U2Self in a convenient API that returns a security context */ -OM_uint32 KRB5_CALLCONV -gss_krb5_create_sec_context_for_principal(OM_uint32 *minor_status, - gss_ctx_id_t *context_handle, - gss_cred_id_t verifier_cred_handle, - gss_name_t principal, - OM_uint32 req_flags, - OM_uint32 time_req, - gss_name_t *src_name, - gss_OID *mech_type, - OM_uint32 *ret_flags, - OM_uint32 *time_rec, - gss_cred_id_t *delegated_cred_handle) +static krb5_error_code +kg_duplicate_ccache(krb5_context context, + krb5_gss_cred_id_t impersonator_cred, + krb5_ccache *out_ccache) { - OM_uint32 minor, major_status; - gss_ctx_id_t initiator_ctx = GSS_C_NO_CONTEXT; - gss_name_t canon_name = NULL; - gss_buffer_desc exported_name; - gss_name_t s4u_name = NULL; + krb5_error_code code; + krb5_ccache ccache; + + code = krb5_cc_new_unique(context, "MEMORY", NULL, &ccache); + if (code != 0) + return code; + + code = krb5_cc_initialize(context, ccache, impersonator_cred->princ); + if (code != 0) { + krb5_cc_destroy(context, ccache); + return code; + } + + code = krb5_cc_copy_creds(context, impersonator_cred->ccache, ccache); + if (code != 0) { + krb5_cc_destroy(context, ccache); + return code; + } + + *out_ccache = ccache; + + return 0; +} + +static OM_uint32 +kg_compose_cred(OM_uint32 *minor_status, + krb5_gss_cred_id_t impersonator_cred, + krb5_gss_cred_id_t subject_cred, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + krb5_gss_cred_id_t *output_cred, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec, + krb5_context context) +{ + OM_uint32 major_status; + krb5_error_code code; krb5_gss_cred_id_t cred = NULL; - krb5_gss_cred_id_t s4u_cred = NULL; - gss_buffer_desc input_token, output_token; - krb5_context context = NULL; + krb5_creds evidence_creds; - exported_name.value = NULL; + memset(&evidence_creds, 0, sizeof(evidence_creds)); - input_token.length = 0; - input_token.value = NULL; + k5_mutex_assert_locked(&impersonator_cred->lock); + k5_mutex_assert_locked(&subject_cred->lock); - output_token.length = 0; - output_token.value = NULL; + if (!kg_is_initiator_cred(impersonator_cred) || + impersonator_cred->ccache == NULL || + impersonator_cred->princ == NULL) { + *minor_status = (OM_uint32)G_BAD_USAGE; + major_status = GSS_S_FAILURE; + goto cleanup; + } - if (context_handle == NULL || principal == GSS_C_NO_NAME) { - major_status = GSS_S_CALL_INACCESSIBLE_READ; + if (!kg_is_initiator_cred(subject_cred) || + subject_cred->ccache == NULL || + subject_cred->princ == NULL) { + *minor_status = (OM_uint32)G_BAD_USAGE; + major_status = GSS_S_FAILURE; goto cleanup; } - if (mech_type != NULL) - *mech_type = GSS_C_NO_OID; - if (delegated_cred_handle != NULL) - *delegated_cred_handle = GSS_C_NO_CREDENTIAL; + cred = (krb5_gss_cred_id_t)xmalloc(sizeof(*cred)); + if (cred == NULL) { + *minor_status = ENOMEM; + major_status = GSS_S_FAILURE; + goto cleanup; + } + memset(cred, 0, sizeof(*cred)); - *minor_status = krb5_gss_init_context(&context); - if (*minor_status != 0) { + code = k5_mutex_init(&cred->lock); + if (code != 0) { + *minor_status = code; major_status = GSS_S_FAILURE; goto cleanup; } - major_status = kg_sync_ccache_name(context, minor_status); + cred->usage = GSS_C_INITIATE; + cred->proxy_cred = 1; + + major_status = kg_set_desired_mechs(minor_status, desired_mechs, cred); if (GSS_ERROR(major_status)) goto cleanup; - major_status = kg_unwrap_name(minor_status, principal, &s4u_name); - if (GSS_ERROR(major_status)) + cred->tgt_expire = impersonator_cred->tgt_expire; + + /* The returned credential's subject matches subject_cred */ + code = krb5_copy_principal(context, subject_cred->princ, &cred->princ); + if (code != 0) { + *minor_status = code; + major_status = GSS_S_FAILURE; goto cleanup; + } - major_status = kg_unwrap_cred_handle(minor_status, verifier_cred_handle, - &cred); - if (GSS_ERROR(major_status)) + code = kg_duplicate_ccache(context, impersonator_cred, &cred->ccache); + if (code != 0) { + *minor_status = code; + major_status = GSS_S_FAILURE; goto cleanup; + } - if (cred == NULL) { - major_status = kg_get_defcred(minor_status, (gss_cred_id_t *)&cred); - if (GSS_ERROR(major_status)) - goto cleanup; + code = kg_get_evidence_ticket(context, impersonator_cred, + subject_cred, &evidence_creds); + if (code != 0) { + *minor_status = code; + major_status = GSS_S_FAILURE; + goto cleanup; } - major_status = kg_acquire_s4u2self_creds(minor_status, - cred, - s4u_name, - time_req, - &s4u_cred, - context); - if (GSS_ERROR(major_status)) + code = krb5_cc_store_cred(context, cred->ccache, &evidence_creds); + if (code != 0) { + *minor_status = code; + major_status = GSS_S_FAILURE; goto cleanup; + } + + if (time_rec != NULL) { + krb5_timestamp now; + + code = krb5_timeofday(context, &now); + if (code != 0) { + *minor_status = code; + major_status = GSS_S_FAILURE; + goto cleanup; + } + + *time_rec = cred->tgt_expire - now; + } - req_flags &= ~(GSS_C_MUTUAL_FLAG); - - major_status = kg_new_connection(minor_status, - s4u_cred, - &initiator_ctx, - (gss_name_t)cred->princ, - (gss_OID)gss_mech_krb5, - req_flags, - time_req, - GSS_C_NO_CHANNEL_BINDINGS, - &input_token, - mech_type, - &output_token, - ret_flags, - time_rec, - context, - TRUE); + major_status = kg_return_mechs(minor_status, cred, actual_mechs); if (GSS_ERROR(major_status)) goto cleanup; - major_status = gss_accept_sec_context(minor_status, - context_handle, - verifier_cred_handle, - &output_token, - GSS_C_NO_CHANNEL_BINDINGS, - src_name, - mech_type, - &input_token, - ret_flags, - time_rec, - delegated_cred_handle); - if (major_status != GSS_S_COMPLETE) + if (!kg_save_cred_id((gss_cred_id_t)cred)) { + *minor_status = (OM_uint32)G_VALIDATE_FAILED; + major_status = GSS_S_FAILURE; goto cleanup; + } + + major_status = GSS_S_COMPLETE; + *output_cred = cred; cleanup: - krb5_gss_release_cred(&minor, (gss_cred_id_t *)&cred); - krb5_gss_release_cred(&minor, (gss_cred_id_t *)&s4u_cred); - krb5_gss_delete_sec_context(&minor, &initiator_ctx, NULL); - gss_release_buffer(&minor, &input_token); - gss_release_buffer(&minor, &output_token); - krb5_gss_release_name(&minor, &s4u_name); - gss_release_buffer(&minor, &exported_name); - gss_release_name(&minor, &canon_name); - - if (context != NULL) { - if (GSS_ERROR(major_status) && *minor_status != 0) - save_error_info(*minor_status, context); - krb5_free_context(context); + if (GSS_ERROR(major_status) && cred != NULL) { + k5_mutex_destroy(&cred->lock); + if (cred->ccache != NULL) + krb5_cc_destroy(context, cred->ccache); + if (cred->princ != NULL) + krb5_free_principal(context, cred->princ); + xfree(cred); } + krb5_free_cred_contents(context, &evidence_creds); + return major_status; } -#endif /* !LEAN_CLIENT */ +/* + * Return a composite credential handle including the service's TGT + * (service_cred_handle) and the ticket from the client to the service + * (output_cred_handle). + */ +OM_uint32 +krb5_gss_acquire_cred_with_cred(OM_uint32 *minor_status, + const gss_cred_id_t impersonator_cred_handle, + const gss_cred_id_t subject_cred_handle, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + OM_uint32 major_status; + krb5_error_code code; + krb5_gss_cred_id_t cred; + krb5_context context; + + if (impersonator_cred_handle == GSS_C_NO_CREDENTIAL) + return GSS_S_CALL_INACCESSIBLE_READ; + + if (subject_cred_handle == GSS_C_NO_CREDENTIAL) + return GSS_S_CALL_INACCESSIBLE_READ; + + if (output_cred_handle == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + if (cred_usage != GSS_C_INITIATE) { + *minor_status = (OM_uint32)G_BAD_USAGE; + return GSS_S_FAILURE; + } + + *output_cred_handle = GSS_C_NO_CREDENTIAL; + if (actual_mechs != NULL) + *actual_mechs = GSS_C_NO_OID_SET; + if (time_rec != NULL) + *time_rec = 0; + + code = krb5_gss_init_context(&context); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + + major_status = krb5_gss_validate_cred_1(minor_status, + impersonator_cred_handle, + context); + if (GSS_ERROR(major_status)) { + + krb5_free_context(context); + return major_status; + } + + major_status = krb5_gss_validate_cred_1(minor_status, + subject_cred_handle, + context); + if (GSS_ERROR(major_status)) { + k5_mutex_unlock(&((krb5_gss_cred_id_t)impersonator_cred_handle)->lock); + krb5_free_context(context); + return major_status; + } + + major_status = kg_compose_cred(minor_status, + (krb5_gss_cred_id_t)impersonator_cred_handle, + (krb5_gss_cred_id_t)subject_cred_handle, + time_req, + desired_mechs, + &cred, + actual_mechs, + time_rec, + context); + + *output_cred_handle = (gss_cred_id_t)cred; + + k5_mutex_unlock(&((krb5_gss_cred_id_t)subject_cred_handle)->lock); + k5_mutex_unlock(&((krb5_gss_cred_id_t)impersonator_cred_handle)->lock); + krb5_free_context(context); + + return major_status; +} diff --git a/src/lib/gssapi/krb5/val_cred.c b/src/lib/gssapi/krb5/val_cred.c index dd82d53..43b1f69 100644 --- a/src/lib/gssapi/krb5/val_cred.c +++ b/src/lib/gssapi/krb5/val_cred.c @@ -58,7 +58,8 @@ krb5_gss_validate_cred_1(OM_uint32 *minor_status, gss_cred_id_t cred_handle, *minor_status = code; return(GSS_S_DEFECTIVE_CREDENTIAL); } - if (!krb5_principal_compare(context, princ, cred->princ)) { + if (!cred->proxy_cred && + !krb5_principal_compare(context, princ, cred->princ)) { k5_mutex_unlock(&cred->lock); *minor_status = KG_CCACHE_NOMATCH; return(GSS_S_DEFECTIVE_CREDENTIAL); diff --git a/src/lib/gssapi/libgssapi_krb5.exports b/src/lib/gssapi/libgssapi_krb5.exports index bebea76..002f9cb 100644 --- a/src/lib/gssapi/libgssapi_krb5.exports +++ b/src/lib/gssapi/libgssapi_krb5.exports @@ -9,8 +9,12 @@ GSS_C_NT_USER_NAME GSS_KRB5_NT_PRINCIPAL_NAME gss_accept_sec_context gss_acquire_cred +gss_acquire_cred_with_cred +gss_acquire_cred_with_name gss_add_buffer_set_member gss_add_cred +gss_add_cred_with_cred +gss_add_cred_with_name gss_add_oid_set_member gss_canonicalize_name gss_compare_name @@ -39,7 +43,6 @@ gss_inquire_sec_context_by_oid gss_krb5_add_sec_context_delegatee gss_krb5_ccache_name gss_krb5_copy_ccache -gss_krb5_create_sec_context_for_principal gss_krb5_export_lucid_sec_context gss_krb5_get_tkt_flags gss_krb5_free_lucid_sec_context diff --git a/src/lib/gssapi/mechglue/Makefile.in b/src/lib/gssapi/mechglue/Makefile.in index 927b375..7ffcdfb 100644 --- a/src/lib/gssapi/mechglue/Makefile.in +++ b/src/lib/gssapi/mechglue/Makefile.in @@ -14,6 +14,8 @@ DEFS=-D_GSS_STATIC_LINK=1 SRCS = \ $(srcdir)/g_accept_sec_context.c \ $(srcdir)/g_acquire_cred.c \ + $(srcdir)/g_acquire_cred_with_name.c \ + $(srcdir)/g_acquire_cred_with_cred.c \ $(srcdir)/g_buffer_set.c \ $(srcdir)/g_canon_name.c \ $(srcdir)/g_compare_name.c \ @@ -58,6 +60,8 @@ SRCS = \ OBJS = \ $(OUTPRE)g_accept_sec_context.$(OBJEXT) \ $(OUTPRE)g_acquire_cred.$(OBJEXT) \ + $(OUTPRE)g_acquire_cred_with_name.$(OBJEXT) \ + $(OUTPRE)g_acquire_cred_with_cred.$(OBJEXT) \ $(OUTPRE)g_buffer_set.$(OBJEXT) \ $(OUTPRE)g_canon_name.$(OBJEXT) \ $(OUTPRE)g_compare_name.$(OBJEXT) \ @@ -102,6 +106,8 @@ OBJS = \ STLIBOBJS = \ g_accept_sec_context.o \ g_acquire_cred.o \ + g_acquire_cred_with_name.o \ + g_acquire_cred_with_cred.o \ g_buffer_set.o \ g_canon_name.o \ g_compare_name.o \ diff --git a/src/lib/gssapi/mechglue/g_accept_sec_context.c b/src/lib/gssapi/mechglue/g_accept_sec_context.c index fa703d3..4c232b6 100644 --- a/src/lib/gssapi/mechglue/g_accept_sec_context.c +++ b/src/lib/gssapi/mechglue/g_accept_sec_context.c @@ -246,8 +246,12 @@ gss_cred_id_t * d_cred; } /* Ensure we're returning correct creds format */ - if ((temp_ret_flags & GSS_C_DELEG_FLAG) && - tmp_d_cred != GSS_C_NO_CREDENTIAL) { + /* + * No longer check for GSS_C_DELEG_FLAG, because + * a mechanism may have returned a credential for + * use with gss_acquire_cred_with_cred(). + */ + if (tmp_d_cred != GSS_C_NO_CREDENTIAL) { gss_union_cred_t d_u_cred = NULL; d_u_cred = malloc(sizeof (gss_union_cred_desc)); diff --git a/src/lib/gssapi/mechglue/g_acquire_cred.c b/src/lib/gssapi/mechglue/g_acquire_cred.c index fada9e8..6dfc65f 100644 --- a/src/lib/gssapi/mechglue/g_acquire_cred.c +++ b/src/lib/gssapi/mechglue/g_acquire_cred.c @@ -2,7 +2,7 @@ /* * Copyright 1996 by Sun Microsystems, Inc. - * + * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appears in all copies and @@ -12,7 +12,7 @@ * without specific, written prior permission. Sun Microsystems makes no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. - * + * * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR @@ -35,42 +35,6 @@ #include <errno.h> #include <time.h> -static gss_OID_set -create_actual_mechs(mechs_array, count) - const gss_OID mechs_array; - int count; -{ - gss_OID_set actual_mechs; - int i; - OM_uint32 minor; - - actual_mechs = (gss_OID_set) malloc(sizeof(gss_OID_set_desc)); - if (!actual_mechs) - return NULL; - - actual_mechs->elements = (gss_OID) - malloc(sizeof (gss_OID_desc) * count); - if (!actual_mechs->elements) { - free(actual_mechs); - return NULL; - } - - actual_mechs->count = 0; - - for (i = 0; i < count; i++) { - actual_mechs->elements[i].elements = (void *) - malloc(mechs_array[i].length); - if (actual_mechs->elements[i].elements == NULL) { - (void) gss_release_oid_set(&minor, &actual_mechs); - return (NULL); - } - g_OID_copy(&actual_mechs->elements[i], &mechs_array[i]); - actual_mechs->count++; - } - - return actual_mechs; -} - static OM_uint32 val_acq_cred_args( OM_uint32 *minor_status, @@ -172,7 +136,7 @@ OM_uint32 * time_rec; mech = gssint_get_mechanism(NULL); if (mech == NULL) return (GSS_S_BAD_MECH); - + mechs = &default_OID_set; default_OID_set.count = 1; default_OID_set.elements = &default_OID; @@ -234,12 +198,16 @@ OM_uint32 * time_rec; * setup the actual mechs output parameter */ if (actual_mechs != NULL) { - if ((*actual_mechs = create_actual_mechs(creds->mechs_array, - creds->count)) == NULL) { + gss_OID_set_desc oids; + + oids.count = creds->count; + oids.elements = creds->mechs_array; + + major = generic_gss_copy_oid_set(minor_status, &oids, actual_mechs); + if (GSS_ERROR(major)) { (void) gss_release_cred(minor_status, (gss_cred_id_t *)&creds); - *minor_status = 0; - return (GSS_S_FAILURE); + return (major); } } @@ -312,7 +280,7 @@ OM_uint32 KRB5_CALLCONV gss_add_cred(minor_status, input_cred_handle, desired_name, desired_mech, cred_usage, initiator_time_req, acceptor_time_req, - output_cred_handle, actual_mechs, + output_cred_handle, actual_mechs, initiator_time_rec, acceptor_time_rec) OM_uint32 *minor_status; gss_cred_id_t input_cred_handle; @@ -434,7 +402,7 @@ gss_add_cred(minor_status, input_cred_handle, status = mech->gss_display_name(&temp_minor_status, internal_name, &union_cred->auxinfo.name, &union_cred->auxinfo.name_type); - + if (status != GSS_S_COMPLETE) goto errout; } @@ -475,10 +443,14 @@ gss_add_cred(minor_status, input_cred_handle, g_OID_copy(&new_mechs_array[union_cred->count], &mech->mech_type); - if (actual_mechs) { - *actual_mechs = create_actual_mechs(new_mechs_array, - union_cred->count + 1); - if (*actual_mechs == NULL) { + if (actual_mechs != NULL) { + gss_OID_set_desc oids; + + oids.count = union_cred->count + 1; + oids.elements = new_mechs_array; + + status = generic_gss_copy_oid_set(minor_status, &oids, actual_mechs); + if (GSS_ERROR(status)) { free(new_mechs_array[union_cred->count].elements); goto errout; } diff --git a/src/lib/gssapi/mechglue/g_acquire_cred_with_cred.c b/src/lib/gssapi/mechglue/g_acquire_cred_with_cred.c new file mode 100644 index 0000000..36c6549 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_acquire_cred_with_cred.c @@ -0,0 +1,528 @@ +/* #pragma ident "@(#)g_acquire_cred.c 1.22 04/02/23 SMI" */ + +/* + * Copyright 2009 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. + * + */ +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_acquire_cred_with_cred + */ + +#include "mglueP.h" +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> +#include <errno.h> +#include <time.h> + +static OM_uint32 +val_acq_cred_with_cred_args( + OM_uint32 *minor_status, + const gss_cred_id_t impersonator_cred_handle, + const gss_cred_id_t subject_cred_handle, + OM_uint32 time_req, + gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (output_cred_handle != NULL) + *output_cred_handle = GSS_C_NO_CREDENTIAL; + + if (actual_mechs != NULL) + *actual_mechs = GSS_C_NULL_OID_SET; + + if (time_rec != NULL) + *time_rec = 0; + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (impersonator_cred_handle == GSS_C_NO_CREDENTIAL) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED); + + if (subject_cred_handle == GSS_C_NO_CREDENTIAL) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED); + + if (output_cred_handle == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (cred_usage != GSS_C_ACCEPT + && cred_usage != GSS_C_INITIATE + && cred_usage != GSS_C_BOTH) { + if (minor_status) { + *minor_status = EINVAL; + map_errcode(minor_status); + } + return GSS_S_FAILURE; + } + + return (GSS_S_COMPLETE); +} + + +OM_uint32 KRB5_CALLCONV +gss_acquire_cred_with_cred(OM_uint32 *minor_status, + const gss_cred_id_t impersonator_cred_handle, + const gss_cred_id_t subject_cred_handle, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + OM_uint32 major = GSS_S_FAILURE; + OM_uint32 initTimeOut, acceptTimeOut, outTime = GSS_C_INDEFINITE; + gss_OID_set_desc default_OID_set; + gss_OID_set mechs; + gss_OID_desc default_OID; + gss_mechanism mech; + unsigned int i; + gss_union_cred_t creds; + + major = val_acq_cred_with_cred_args(minor_status, + impersonator_cred_handle, + subject_cred_handle, + time_req, + desired_mechs, + cred_usage, + output_cred_handle, + actual_mechs, + time_rec); + if (major != GSS_S_COMPLETE) + return (major); + + /* Initial value needed below. */ + major = GSS_S_FAILURE; + + /* + * if desired_mechs equals GSS_C_NULL_OID_SET, then pick an + * appropriate default. We use the first mechanism in the + * mechansim list as the default. This set is created with + * statics thus needs not be freed + */ + if(desired_mechs == GSS_C_NULL_OID_SET) { + mech = gssint_get_mechanism(NULL); + if (mech == NULL) + return (GSS_S_BAD_MECH); + + mechs = &default_OID_set; + default_OID_set.count = 1; + default_OID_set.elements = &default_OID; + default_OID.length = mech->mech_type.length; + default_OID.elements = mech->mech_type.elements; + } else + mechs = desired_mechs; + + if (mechs->count == 0) + return (GSS_S_BAD_MECH); + + /* allocate the output credential structure */ + creds = (gss_union_cred_t)malloc(sizeof (gss_union_cred_desc)); + if (creds == NULL) + return (GSS_S_FAILURE); + + /* initialize to 0s */ + (void) memset(creds, 0, sizeof (gss_union_cred_desc)); + creds->loopback = creds; + + /* for each requested mech attempt to obtain a credential */ + for (i = 0; i < mechs->count; i++) { + major = gss_add_cred_with_cred(minor_status, + impersonator_cred_handle, + (gss_cred_id_t)creds, + subject_cred_handle, + &mechs->elements[i], + cred_usage, time_req, time_req, NULL, + NULL, &initTimeOut, &acceptTimeOut); + if (major == GSS_S_COMPLETE) { + /* update the credential's time */ + if (cred_usage == GSS_C_ACCEPT) { + if (outTime > acceptTimeOut) + outTime = acceptTimeOut; + } else if (cred_usage == GSS_C_INITIATE) { + if (outTime > initTimeOut) + outTime = initTimeOut; + } else { + /* + * time_rec is the lesser of the + * init/accept times + */ + if (initTimeOut > acceptTimeOut) + outTime = (outTime > acceptTimeOut) ? + acceptTimeOut : outTime; + else + outTime = (outTime > initTimeOut) ? + initTimeOut : outTime; + } + } + } /* for */ + + /* ensure that we have at least one credential element */ + if (creds->count < 1) { + free(creds); + return (major); + } + + /* + * fill in output parameters + * setup the actual mechs output parameter + */ + if (actual_mechs != NULL) { + gss_OID_set_desc oids; + + oids.count = creds->count; + oids.elements = creds->mechs_array; + + major = generic_gss_copy_oid_set(minor_status, &oids, actual_mechs); + if (GSS_ERROR(major)) { + (void) gss_release_cred(minor_status, + (gss_cred_id_t *)&creds); + return (major); + } + } + + if (time_rec) + *time_rec = outTime; + + + creds->loopback = creds; + *output_cred_handle = (gss_cred_id_t)creds; + return (GSS_S_COMPLETE); +} + +static OM_uint32 +val_add_cred_with_cred_args( + OM_uint32 *minor_status, + gss_cred_id_t impersonator_cred_handle, + gss_cred_id_t input_cred_handle, + gss_cred_id_t subject_cred_handle, + gss_OID desired_mech, + gss_cred_usage_t cred_usage, + OM_uint32 initiator_time_req, + OM_uint32 acceptor_time_req, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *initiator_time_rec, + OM_uint32 *acceptor_time_rec) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (output_cred_handle != NULL) + *output_cred_handle = GSS_C_NO_CREDENTIAL; + + if (actual_mechs != NULL) + *actual_mechs = GSS_C_NO_OID_SET; + + if (acceptor_time_rec != NULL) + *acceptor_time_rec = 0; + + if (initiator_time_rec != NULL) + *initiator_time_rec = 0; + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (impersonator_cred_handle == GSS_C_NO_CREDENTIAL) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED); + + if (subject_cred_handle == GSS_C_NO_CREDENTIAL) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED); + + if (input_cred_handle == GSS_C_NO_CREDENTIAL && + output_cred_handle == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CRED); + + if (cred_usage != GSS_C_ACCEPT + && cred_usage != GSS_C_INITIATE + && cred_usage != GSS_C_BOTH) { + if (minor_status) { + *minor_status = EINVAL; + map_errcode(minor_status); + } + return GSS_S_FAILURE; + } + + return (GSS_S_COMPLETE); +} + + +/* V2 KRB5_CALLCONV */ +OM_uint32 KRB5_CALLCONV +gss_add_cred_with_cred(OM_uint32 *minor_status, + const gss_cred_id_t impersonator_cred_handle, + gss_cred_id_t input_cred_handle, + const gss_cred_id_t subject_cred_handle, + const gss_OID desired_mech, + gss_cred_usage_t cred_usage, + OM_uint32 initiator_time_req, + OM_uint32 acceptor_time_req, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *initiator_time_rec, + OM_uint32 *acceptor_time_rec) +{ + OM_uint32 status, temp_minor_status; + OM_uint32 time_req, time_rec; + gss_union_cred_t new_union_cred, union_cred; + gss_cred_id_t mech_impersonator_cred, mech_subject_cred; + gss_name_t internal_name = GSS_C_NO_NAME; + gss_mechanism mech; + gss_cred_id_t cred = NULL; + gss_OID new_mechs_array = NULL; + gss_cred_id_t * new_cred_array = NULL; + + status = val_add_cred_with_cred_args(minor_status, + impersonator_cred_handle, + input_cred_handle, + subject_cred_handle, + desired_mech, + cred_usage, + initiator_time_req, + acceptor_time_req, + output_cred_handle, + actual_mechs, + initiator_time_rec, + acceptor_time_rec); + if (status != GSS_S_COMPLETE) + return (status); + + mech = gssint_get_mechanism(desired_mech); + if (!mech) + return GSS_S_BAD_MECH; + else if (!mech->gss_acquire_cred) + return (GSS_S_UNAVAILABLE); + + if (input_cred_handle == GSS_C_NO_CREDENTIAL) { + union_cred = malloc(sizeof (gss_union_cred_desc)); + if (union_cred == NULL) + return (GSS_S_FAILURE); + + (void) memset(union_cred, 0, sizeof (gss_union_cred_desc)); + } else { + union_cred = (gss_union_cred_t)input_cred_handle; + if (gssint_get_mechanism_cred(union_cred, desired_mech) != + GSS_C_NO_CREDENTIAL) + return (GSS_S_DUPLICATE_ELEMENT); + } + + mech_impersonator_cred = + gssint_get_mechanism_cred((gss_union_cred_t)impersonator_cred_handle, + desired_mech); + if (mech_impersonator_cred == GSS_C_NO_CREDENTIAL) + return (GSS_S_NO_CRED); + + mech_subject_cred = + gssint_get_mechanism_cred((gss_union_cred_t)subject_cred_handle, + desired_mech); + if (mech_subject_cred == GSS_C_NO_CREDENTIAL) + return (GSS_S_NO_CRED); + + if (cred_usage == GSS_C_ACCEPT) + time_req = acceptor_time_req; + else if (cred_usage == GSS_C_INITIATE) + time_req = initiator_time_req; + else if (cred_usage == GSS_C_BOTH) + time_req = (acceptor_time_req > initiator_time_req) ? + acceptor_time_req : initiator_time_req; + else + time_req = 0; + + status = mech->gss_acquire_cred_with_cred(minor_status, + mech_impersonator_cred, + mech_subject_cred, + time_req, + GSS_C_NULL_OID_SET, + cred_usage, + &cred, + NULL, + &time_rec); + if (status != GSS_S_COMPLETE) { + map_error(minor_status, mech); + goto errout; + } + + /* may need to set credential auxinfo strucutre */ + if (union_cred->auxinfo.creation_time == 0) { + union_cred->auxinfo.creation_time = time(NULL); + union_cred->auxinfo.time_rec = time_rec; + union_cred->auxinfo.cred_usage = cred_usage; + + /* + * we must set the name; if name is not supplied + * we must do inquire cred to get it + */ + if (mech->gss_inquire_cred == NULL || + ((status = mech->gss_inquire_cred(&temp_minor_status, cred, + &internal_name, NULL, NULL, + NULL)) != GSS_S_COMPLETE)) + goto errout; + + if (internal_name != GSS_C_NO_NAME) { + status = mech->gss_display_name(&temp_minor_status, internal_name, + &union_cred->auxinfo.name, + &union_cred->auxinfo.name_type); + + if (status != GSS_S_COMPLETE) + goto errout; + } + } + + /* now add the new credential elements */ + new_mechs_array = (gss_OID) + malloc(sizeof (gss_OID_desc) * (union_cred->count+1)); + + new_cred_array = (gss_cred_id_t *) + malloc(sizeof (gss_cred_id_t) * (union_cred->count+1)); + + if (!new_mechs_array || !new_cred_array) { + status = GSS_S_FAILURE; + goto errout; + } + + if (acceptor_time_rec) + if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) + *acceptor_time_rec = time_rec; + if (initiator_time_rec) + if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) + *initiator_time_rec = time_rec; + + /* + * OK, expand the mechanism array and the credential array + */ + (void) memcpy(new_mechs_array, union_cred->mechs_array, + sizeof (gss_OID_desc) * union_cred->count); + (void) memcpy(new_cred_array, union_cred->cred_array, + sizeof (gss_cred_id_t) * union_cred->count); + + new_cred_array[union_cred->count] = cred; + if ((new_mechs_array[union_cred->count].elements = + malloc(mech->mech_type.length)) == NULL) + goto errout; + + g_OID_copy(&new_mechs_array[union_cred->count], + &mech->mech_type); + + if (actual_mechs != NULL) { + gss_OID_set_desc oids; + + oids.count = union_cred->count + 1; + oids.elements = new_mechs_array; + + status = generic_gss_copy_oid_set(minor_status, &oids, actual_mechs); + if (GSS_ERROR(status)) { + free(new_mechs_array[union_cred->count].elements); + goto errout; + } + } + + if (output_cred_handle == NULL) { + free(union_cred->mechs_array); + free(union_cred->cred_array); + new_union_cred = union_cred; + } else { + new_union_cred = malloc(sizeof (gss_union_cred_desc)); + if (new_union_cred == NULL) { + free(new_mechs_array[union_cred->count].elements); + goto errout; + } + *new_union_cred = *union_cred; + *output_cred_handle = (gss_cred_id_t)new_union_cred; + } + + new_union_cred->mechs_array = new_mechs_array; + new_union_cred->cred_array = new_cred_array; + new_union_cred->count++; + new_union_cred->loopback = new_union_cred; + + /* We're done with the internal name. Free it if we allocated it. */ + + if (internal_name) + (void) gssint_release_internal_name(&temp_minor_status, + &mech->mech_type, + &internal_name); + + return (GSS_S_COMPLETE); + +errout: + if (new_mechs_array) + free(new_mechs_array); + if (new_cred_array) + free(new_cred_array); + + if (cred != NULL && mech->gss_release_cred) + mech->gss_release_cred(&temp_minor_status, &cred); + + if (internal_name) + (void) gssint_release_internal_name(&temp_minor_status, + &mech->mech_type, + &internal_name); + + if (input_cred_handle == GSS_C_NO_CREDENTIAL && union_cred) { + if (union_cred->auxinfo.name.value) + free(union_cred->auxinfo.name.value); + free(union_cred); + } + + return (status); +} diff --git a/src/lib/gssapi/mechglue/g_acquire_cred_with_name.c b/src/lib/gssapi/mechglue/g_acquire_cred_with_name.c new file mode 100644 index 0000000..80e3243 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_acquire_cred_with_name.c @@ -0,0 +1,545 @@ +/* #pragma ident "@(#)g_acquire_cred.c 1.22 04/02/23 SMI" */ + +/* + * Copyright 2009 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. + * + */ +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_acquire_cred_with_name + */ + +#include "mglueP.h" +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> +#include <errno.h> +#include <time.h> + +static OM_uint32 +val_acq_cred_with_name_args( + OM_uint32 *minor_status, + const gss_cred_id_t impersonator_cred_handle, + const gss_name_t desired_name, + OM_uint32 time_req, + gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (output_cred_handle != NULL) + *output_cred_handle = GSS_C_NO_CREDENTIAL; + + if (actual_mechs != NULL) + *actual_mechs = GSS_C_NULL_OID_SET; + + if (time_rec != NULL) + *time_rec = 0; + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (impersonator_cred_handle == GSS_C_NO_CREDENTIAL) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED); + + if (desired_name == GSS_C_NO_NAME) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME); + + if (output_cred_handle == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (cred_usage != GSS_C_ACCEPT + && cred_usage != GSS_C_INITIATE + && cred_usage != GSS_C_BOTH) { + if (minor_status) { + *minor_status = EINVAL; + map_errcode(minor_status); + } + return GSS_S_FAILURE; + } + + return (GSS_S_COMPLETE); +} + + +OM_uint32 KRB5_CALLCONV +gss_acquire_cred_with_name(OM_uint32 *minor_status, + const gss_cred_id_t impersonator_cred_handle, + const gss_name_t desired_name, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + OM_uint32 major = GSS_S_FAILURE; + OM_uint32 initTimeOut, acceptTimeOut, outTime = GSS_C_INDEFINITE; + gss_OID_set_desc default_OID_set; + gss_OID_set mechs; + gss_OID_desc default_OID; + gss_mechanism mech; + unsigned int i; + gss_union_cred_t creds; + + major = val_acq_cred_with_name_args(minor_status, + impersonator_cred_handle, + desired_name, + time_req, + desired_mechs, + cred_usage, + output_cred_handle, + actual_mechs, + time_rec); + if (major != GSS_S_COMPLETE) + return (major); + + /* Initial value needed below. */ + major = GSS_S_FAILURE; + + /* + * if desired_mechs equals GSS_C_NULL_OID_SET, then pick an + * appropriate default. We use the first mechanism in the + * mechansim list as the default. This set is created with + * statics thus needs not be freed + */ + if(desired_mechs == GSS_C_NULL_OID_SET) { + mech = gssint_get_mechanism(NULL); + if (mech == NULL) + return (GSS_S_BAD_MECH); + + mechs = &default_OID_set; + default_OID_set.count = 1; + default_OID_set.elements = &default_OID; + default_OID.length = mech->mech_type.length; + default_OID.elements = mech->mech_type.elements; + } else + mechs = desired_mechs; + + if (mechs->count == 0) + return (GSS_S_BAD_MECH); + + /* allocate the output credential structure */ + creds = (gss_union_cred_t)malloc(sizeof (gss_union_cred_desc)); + if (creds == NULL) + return (GSS_S_FAILURE); + + /* initialize to 0s */ + (void) memset(creds, 0, sizeof (gss_union_cred_desc)); + creds->loopback = creds; + + /* for each requested mech attempt to obtain a credential */ + for (i = 0; i < mechs->count; i++) { + major = gss_add_cred_with_name(minor_status, + impersonator_cred_handle, + (gss_cred_id_t)creds, + desired_name, + &mechs->elements[i], + cred_usage, time_req, time_req, NULL, + NULL, &initTimeOut, &acceptTimeOut); + if (major == GSS_S_COMPLETE) { + /* update the credential's time */ + if (cred_usage == GSS_C_ACCEPT) { + if (outTime > acceptTimeOut) + outTime = acceptTimeOut; + } else if (cred_usage == GSS_C_INITIATE) { + if (outTime > initTimeOut) + outTime = initTimeOut; + } else { + /* + * time_rec is the lesser of the + * init/accept times + */ + if (initTimeOut > acceptTimeOut) + outTime = (outTime > acceptTimeOut) ? + acceptTimeOut : outTime; + else + outTime = (outTime > initTimeOut) ? + initTimeOut : outTime; + } + } + } /* for */ + + /* ensure that we have at least one credential element */ + if (creds->count < 1) { + free(creds); + return (major); + } + + /* + * fill in output parameters + * setup the actual mechs output parameter + */ + if (actual_mechs != NULL) { + gss_OID_set_desc oids; + + oids.count = creds->count; + oids.elements = creds->mechs_array; + + major = generic_gss_copy_oid_set(minor_status, &oids, actual_mechs); + if (GSS_ERROR(major)) { + (void) gss_release_cred(minor_status, + (gss_cred_id_t *)&creds); + return (major); + } + } + + if (time_rec) + *time_rec = outTime; + + + creds->loopback = creds; + *output_cred_handle = (gss_cred_id_t)creds; + return (GSS_S_COMPLETE); +} + +static OM_uint32 +val_add_cred_with_name_args( + OM_uint32 *minor_status, + gss_cred_id_t impersonator_cred_handle, + gss_cred_id_t input_cred_handle, + gss_name_t desired_name, + gss_OID desired_mech, + gss_cred_usage_t cred_usage, + OM_uint32 initiator_time_req, + OM_uint32 acceptor_time_req, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *initiator_time_rec, + OM_uint32 *acceptor_time_rec) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (output_cred_handle != NULL) + *output_cred_handle = GSS_C_NO_CREDENTIAL; + + if (actual_mechs != NULL) + *actual_mechs = GSS_C_NO_OID_SET; + + if (acceptor_time_rec != NULL) + *acceptor_time_rec = 0; + + if (initiator_time_rec != NULL) + *initiator_time_rec = 0; + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (impersonator_cred_handle == GSS_C_NO_CREDENTIAL) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED); + + if (desired_name == GSS_C_NO_NAME) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME); + + if (input_cred_handle == GSS_C_NO_CREDENTIAL && + output_cred_handle == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CRED); + + if (cred_usage != GSS_C_ACCEPT + && cred_usage != GSS_C_INITIATE + && cred_usage != GSS_C_BOTH) { + if (minor_status) { + *minor_status = EINVAL; + map_errcode(minor_status); + } + return GSS_S_FAILURE; + } + + return (GSS_S_COMPLETE); +} + + +/* V2 KRB5_CALLCONV */ +OM_uint32 KRB5_CALLCONV +gss_add_cred_with_name(OM_uint32 *minor_status, + const gss_cred_id_t impersonator_cred_handle, + const gss_cred_id_t input_cred_handle, + const gss_name_t desired_name, + const gss_OID desired_mech, + gss_cred_usage_t cred_usage, + OM_uint32 initiator_time_req, + OM_uint32 acceptor_time_req, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *initiator_time_rec, + OM_uint32 *acceptor_time_rec) +{ + OM_uint32 status, temp_minor_status; + OM_uint32 time_req, time_rec; + gss_union_name_t union_name; + gss_union_cred_t new_union_cred, union_cred; + gss_cred_id_t mech_impersonator_cred; + gss_name_t internal_name = GSS_C_NO_NAME; + gss_name_t allocated_name = GSS_C_NO_NAME; + gss_mechanism mech; + gss_cred_id_t cred = NULL; + gss_OID new_mechs_array = NULL; + gss_cred_id_t * new_cred_array = NULL; + + status = val_add_cred_with_name_args(minor_status, + impersonator_cred_handle, + input_cred_handle, + desired_name, + desired_mech, + cred_usage, + initiator_time_req, + acceptor_time_req, + output_cred_handle, + actual_mechs, + initiator_time_rec, + acceptor_time_rec); + if (status != GSS_S_COMPLETE) + return (status); + + mech = gssint_get_mechanism(desired_mech); + if (!mech) + return GSS_S_BAD_MECH; + else if (!mech->gss_acquire_cred) + return (GSS_S_UNAVAILABLE); + + if (input_cred_handle == GSS_C_NO_CREDENTIAL) { + union_cred = malloc(sizeof (gss_union_cred_desc)); + if (union_cred == NULL) + return (GSS_S_FAILURE); + + (void) memset(union_cred, 0, sizeof (gss_union_cred_desc)); + + /* for default credentials we will use GSS_C_NO_NAME */ + internal_name = GSS_C_NO_NAME; + } else { + union_cred = (gss_union_cred_t)input_cred_handle; + if (gssint_get_mechanism_cred(union_cred, desired_mech) != + GSS_C_NO_CREDENTIAL) + return (GSS_S_DUPLICATE_ELEMENT); + } + + mech_impersonator_cred = + gssint_get_mechanism_cred((gss_union_cred_t)impersonator_cred_handle, + desired_mech); + if (mech_impersonator_cred == GSS_C_NO_CREDENTIAL) + return (GSS_S_NO_CRED); + + /* may need to create a mechanism specific name */ + union_name = (gss_union_name_t)desired_name; + if (union_name->mech_type && + g_OID_equal(union_name->mech_type, + &mech->mech_type)) + internal_name = union_name->mech_name; + else { + if (gssint_import_internal_name(minor_status, + &mech->mech_type, union_name, + &allocated_name) != GSS_S_COMPLETE) + return (GSS_S_BAD_NAME); + internal_name = allocated_name; + } + + if (cred_usage == GSS_C_ACCEPT) + time_req = acceptor_time_req; + else if (cred_usage == GSS_C_INITIATE) + time_req = initiator_time_req; + else if (cred_usage == GSS_C_BOTH) + time_req = (acceptor_time_req > initiator_time_req) ? + acceptor_time_req : initiator_time_req; + else + time_req = 0; + + status = mech->gss_acquire_cred_with_name(minor_status, + mech_impersonator_cred, + internal_name, + time_req, + GSS_C_NULL_OID_SET, + cred_usage, + &cred, + NULL, + &time_rec); + if (status != GSS_S_COMPLETE) { + map_error(minor_status, mech); + goto errout; + } + + /* may need to set credential auxinfo strucutre */ + if (union_cred->auxinfo.creation_time == 0) { + union_cred->auxinfo.creation_time = time(NULL); + union_cred->auxinfo.time_rec = time_rec; + union_cred->auxinfo.cred_usage = cred_usage; + + /* + * we must set the name; if name is not supplied + * we must do inquire cred to get it + */ + if (internal_name == NULL) { + if (mech->gss_inquire_cred == NULL || + ((status = mech->gss_inquire_cred( + &temp_minor_status, cred, + &allocated_name, NULL, NULL, + NULL)) != GSS_S_COMPLETE)) + goto errout; + internal_name = allocated_name; + } + + if (internal_name != GSS_C_NO_NAME) { + status = mech->gss_display_name(&temp_minor_status, internal_name, + &union_cred->auxinfo.name, + &union_cred->auxinfo.name_type); + + if (status != GSS_S_COMPLETE) + goto errout; + } + } + + /* now add the new credential elements */ + new_mechs_array = (gss_OID) + malloc(sizeof (gss_OID_desc) * (union_cred->count+1)); + + new_cred_array = (gss_cred_id_t *) + malloc(sizeof (gss_cred_id_t) * (union_cred->count+1)); + + if (!new_mechs_array || !new_cred_array) { + status = GSS_S_FAILURE; + goto errout; + } + + if (acceptor_time_rec) + if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) + *acceptor_time_rec = time_rec; + if (initiator_time_rec) + if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) + *initiator_time_rec = time_rec; + + /* + * OK, expand the mechanism array and the credential array + */ + (void) memcpy(new_mechs_array, union_cred->mechs_array, + sizeof (gss_OID_desc) * union_cred->count); + (void) memcpy(new_cred_array, union_cred->cred_array, + sizeof (gss_cred_id_t) * union_cred->count); + + new_cred_array[union_cred->count] = cred; + if ((new_mechs_array[union_cred->count].elements = + malloc(mech->mech_type.length)) == NULL) + goto errout; + + g_OID_copy(&new_mechs_array[union_cred->count], + &mech->mech_type); + + if (actual_mechs != NULL) { + gss_OID_set_desc oids; + + oids.count = union_cred->count + 1; + oids.elements = new_mechs_array; + + status = generic_gss_copy_oid_set(minor_status, &oids, actual_mechs); + if (GSS_ERROR(status)) { + free(new_mechs_array[union_cred->count].elements); + goto errout; + } + } + + if (output_cred_handle == NULL) { + free(union_cred->mechs_array); + free(union_cred->cred_array); + new_union_cred = union_cred; + } else { + new_union_cred = malloc(sizeof (gss_union_cred_desc)); + if (new_union_cred == NULL) { + free(new_mechs_array[union_cred->count].elements); + goto errout; + } + *new_union_cred = *union_cred; + *output_cred_handle = (gss_cred_id_t)new_union_cred; + } + + new_union_cred->mechs_array = new_mechs_array; + new_union_cred->cred_array = new_cred_array; + new_union_cred->count++; + new_union_cred->loopback = new_union_cred; + + /* We're done with the internal name. Free it if we allocated it. */ + + if (allocated_name) + (void) gssint_release_internal_name(&temp_minor_status, + &mech->mech_type, + &allocated_name); + + return (GSS_S_COMPLETE); + +errout: + if (new_mechs_array) + free(new_mechs_array); + if (new_cred_array) + free(new_cred_array); + + if (cred != NULL && mech->gss_release_cred) + mech->gss_release_cred(&temp_minor_status, &cred); + + if (allocated_name) + (void) gssint_release_internal_name(&temp_minor_status, + &mech->mech_type, + &allocated_name); + + if (input_cred_handle == GSS_C_NO_CREDENTIAL && union_cred) { + if (union_cred->auxinfo.name.value) + free(union_cred->auxinfo.name.value); + free(union_cred); + } + + return (status); +} diff --git a/src/lib/gssapi/mechglue/g_initialize.c b/src/lib/gssapi/mechglue/g_initialize.c index 85fbe63..415fe15 100644 --- a/src/lib/gssapi/mechglue/g_initialize.c +++ b/src/lib/gssapi/mechglue/g_initialize.c @@ -761,6 +761,11 @@ build_dynamicMech(void *dl, const gss_OID mech_type) GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_unwrap_iov); GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_wrap_iov_length); GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_complete_auth_token); + /* New for 1.8 */ + GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_acquire_cred_with_name); + GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_add_cred_with_name); + GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_acquire_cred_with_cred); + GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_add_cred_with_cred); assert(mech_type != GSS_C_NO_OID); diff --git a/src/lib/gssapi/mechglue/mglueP.h b/src/lib/gssapi/mechglue/mglueP.h index 0016361..90b58c4 100644 --- a/src/lib/gssapi/mechglue/mglueP.h +++ b/src/lib/gssapi/mechglue/mglueP.h @@ -473,6 +473,66 @@ typedef struct gss_config { gss_buffer_t /* input_message_buffer */ ); + /* New for 1.8 */ + + OM_uint32 (*gss_acquire_cred_with_name) + ( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t, /* impersonator_cred_handle */ + const gss_name_t, /* desired_name */ + OM_uint32, /* time_req */ + const gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 * /* time_rec */ + /* */); + + OM_uint32 (*gss_add_cred_with_name) + ( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t, /* impersonator_cred_handle */ + gss_cred_id_t, /* input_cred_handle */ + const gss_name_t, /* desired_name */ + const gss_OID, /* desired_mech */ + gss_cred_usage_t, /* cred_usage */ + OM_uint32, /* initiator_time_req */ + OM_uint32, /* acceptor_time_req */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *, /* initiator_time_rec */ + OM_uint32 * /* acceptor_time_rec */ + /* */); + + OM_uint32 (*gss_acquire_cred_with_cred) + ( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t, /* impersonator_cred_handle */ + const gss_cred_id_t, /* subject_cred_handle */ + OM_uint32, /* time_req */ + const gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 * /* time_rec */ + /* */); + + OM_uint32 (*gss_add_cred_with_cred) + ( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t, /* impersonator_cred_handle */ + gss_cred_id_t, /* input_cred_handle */ + const gss_cred_id_t, /* subject_cred_handle */ + const gss_OID, /* desired_mech */ + gss_cred_usage_t, /* cred_usage */ + OM_uint32, /* initiator_time_req */ + OM_uint32, /* acceptor_time_req */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *, /* initiator_time_rec */ + OM_uint32 * /* acceptor_time_rec */ + /* */); + } *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 e1f3987..3a72022 100644 --- a/src/lib/gssapi/spnego/gssapiP_spnego.h +++ b/src/lib/gssapi/spnego/gssapiP_spnego.h @@ -411,6 +411,30 @@ spnego_gss_complete_auth_token gss_buffer_t input_message_buffer ); +OM_uint32 +spnego_gss_acquire_cred_with_name( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t, /* impersonator_cred_handle */ + const gss_name_t, /* desired_name */ + OM_uint32, /* time_req */ + const gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *); /* time_rec */ + +OM_uint32 +spnego_gss_acquire_cred_with_cred( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t, /* impersonator_cred_handle */ + const gss_cred_id_t, /* subject_cred_handle */ + OM_uint32, /* time_req */ + const gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *); /* time_rec */ + #ifdef __cplusplus } #endif diff --git a/src/lib/gssapi/spnego/spnego_mech.c b/src/lib/gssapi/spnego/spnego_mech.c index ae9ffa7..e6d52f6 100644 --- a/src/lib/gssapi/spnego/spnego_mech.c +++ b/src/lib/gssapi/spnego/spnego_mech.c @@ -257,7 +257,11 @@ static struct gss_config spnego_mechanism = spnego_gss_wrap_iov, spnego_gss_unwrap_iov, spnego_gss_wrap_iov_length, - spnego_gss_complete_auth_token + spnego_gss_complete_auth_token, + spnego_gss_acquire_cred_with_name, + NULL, /* gss_add_cred_with_name */ + spnego_gss_acquire_cred_with_cred, + NULL, /* gss_add_cred_with_cred */ }; #ifdef _GSS_STATIC_LINK @@ -2210,6 +2214,99 @@ spnego_gss_complete_auth_token( return (ret); } +OM_uint32 +spnego_gss_acquire_cred_with_name(OM_uint32 *minor_status, + const gss_cred_id_t impersonator_cred_handle, + gss_name_t desired_name, + OM_uint32 time_req, + gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + OM_uint32 status; + gss_OID_set amechs = GSS_C_NULL_OID_SET; + + dsyslog("Entering spnego_gss_acquire_cred_with_name\n"); + + if (actual_mechs) + *actual_mechs = NULL; + + if (time_rec) + *time_rec = 0; + + if (desired_mechs == GSS_C_NO_OID_SET) { + status = gss_inquire_cred(minor_status, + impersonator_cred_handle, + NULL, NULL, + NULL, &amechs); + if (status != GSS_S_COMPLETE) + return status; + + desired_mechs = amechs; + } + + status = gss_acquire_cred_with_name(minor_status, + impersonator_cred_handle, + desired_name, time_req, + desired_mechs, cred_usage, + output_cred_handle, actual_mechs, + time_rec); + + if (amechs != GSS_C_NULL_OID_SET) + (void) gss_release_oid_set(minor_status, &amechs); + + dsyslog("Leaving spnego_gss_acquire_cred_with_name\n"); + return (status); +} + +OM_uint32 +spnego_gss_acquire_cred_with_cred(OM_uint32 *minor_status, + const gss_cred_id_t impersonator_cred_handle, + const gss_cred_id_t subject_cred_handle, + OM_uint32 time_req, + gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + OM_uint32 status; + gss_OID_set amechs = GSS_C_NULL_OID_SET; + dsyslog("Entering spnego_gss_acquire_cred_with_cred\n"); + + if (actual_mechs) + *actual_mechs = NULL; + + if (time_rec) + *time_rec = 0; + + if (desired_mechs == GSS_C_NO_OID_SET) { + status = gss_inquire_cred(minor_status, + impersonator_cred_handle, + NULL, NULL, + NULL, &amechs); + if (status != GSS_S_COMPLETE) + return status; + + desired_mechs = amechs; + } + + status = gss_acquire_cred_with_cred(minor_status, + impersonator_cred_handle, + subject_cred_handle, time_req, + desired_mechs, cred_usage, + output_cred_handle, actual_mechs, + time_rec); + + if (amechs != GSS_C_NO_OID_SET) + (void) gss_release_oid_set(minor_status, &amechs); + + dsyslog("Leaving spnego_gss_acquire_cred_with_cred\n"); + return (status); +} + /* * We will release everything but the ctx_handle so that it * can be passed back to init/accept context. This routine should diff --git a/src/tests/gssapi/t_s4u.c b/src/tests/gssapi/t_s4u.c index 0396c05..6c9f477 100644 --- a/src/tests/gssapi/t_s4u.c +++ b/src/tests/gssapi/t_s4u.c @@ -110,18 +110,156 @@ displayCanonName(OM_uint32 *minor, gss_name_t name, char *tag) return GSS_S_COMPLETE; } +static OM_uint32 +initAcceptSecContext(OM_uint32 *minor, + gss_cred_id_t claimant_cred_handle, + gss_cred_id_t verifier_cred_handle, + gss_cred_id_t *deleg_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; + gss_name_t target_name = GSS_C_NO_NAME; + OM_uint32 time_rec; + + token.value = NULL; + token.length = 0; + + tmp.value = NULL; + tmp.length = 0; + + *deleg_cred_handle = GSS_C_NO_CREDENTIAL; + + major = gss_inquire_cred(minor, verifier_cred_handle, + &target_name, NULL, NULL, NULL); + if (GSS_ERROR(major)) { + displayStatus("gss_inquire_cred", major, minor); + return major; + } + + displayCanonName(minor, target_name, "Target name"); + + major = gss_init_sec_context(minor, + claimant_cred_handle, + &initiator_context, + target_name, + (gss_OID)gss_mech_krb5, + 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, + NULL, + &tmp, + NULL, + &time_rec, + deleg_cred_handle); + + if (GSS_ERROR(major)) + displayStatus("gss_accept_sec_context", major, minor); + else + displayCanonName(minor, source_name, "Source name"); + + (void) gss_delete_sec_context(minor, &acceptor_context, NULL); + (void) gss_release_buffer(minor, &token); + (void) gss_release_buffer(minor, &tmp); + + return major; +} + +static OM_uint32 +constrainedDelegate(OM_uint32 *minor, + gss_OID_set desired_mechs, + gss_name_t target, + gss_cred_id_t delegated_cred_handle, + gss_cred_id_t verifier_cred_handle) +{ + OM_uint32 major, tmp; + gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; + gss_ctx_id_t initiator_context = GSS_C_NO_CONTEXT; + gss_name_t cred_name = GSS_C_NO_NAME; + OM_uint32 time_rec, lifetime; + gss_cred_usage_t usage; + gss_buffer_desc token; + + printf("Constrained delegation tests follow\n"); + printf("-----------------------------------\n\n"); + + major = gss_acquire_cred_with_cred(minor, + verifier_cred_handle, + delegated_cred_handle, + GSS_C_INDEFINITE, + desired_mechs, + GSS_C_INITIATE, + &cred, + NULL, + &time_rec); + if (GSS_ERROR(major)) { + displayStatus("gss_acquire_cred_with_cred", major, minor); + return major; + } + + if (gss_inquire_cred(minor, delegated_cred_handle, &cred_name, + &lifetime, &usage, NULL) == GSS_S_COMPLETE) + displayCanonName(minor, cred_name, "Proxy cred name"); + gss_release_name(minor, &cred_name); + + printf("\n"); + major = gss_init_sec_context(minor, + cred, + &initiator_context, + target, + (gss_OID)gss_mech_krb5, + 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 (GSS_ERROR(major)) + displayStatus("gss_init_sec_context", major, minor); + + (void) gss_release_buffer(&tmp, &token); + (void) gss_delete_sec_context(&tmp, &initiator_context, NULL); + (void) gss_release_cred(&tmp, &cred); + + return major; +} int main(int argc, char *argv[]) { OM_uint32 minor, major; - gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT; - gss_cred_id_t verifier_cred_handle = GSS_C_NO_CREDENTIAL; - gss_name_t principal = GSS_C_NO_NAME, target = GSS_C_NO_NAME; - gss_name_t src_name = GSS_C_NO_NAME; + gss_cred_id_t impersonator_cred_handle = GSS_C_NO_CREDENTIAL; + gss_cred_id_t user_cred_handle = GSS_C_NO_CREDENTIAL; gss_cred_id_t delegated_cred_handle = GSS_C_NO_CREDENTIAL; + gss_name_t user = GSS_C_NO_NAME, target = GSS_C_NO_NAME; + gss_OID_set_desc mechs; + gss_OID_set actual_mechs = GSS_C_NO_OID_SET; gss_buffer_desc buf; - gss_OID mech_type; - OM_uint32 ret_flags, time_ret; if (argc < 2 || argc > 4) { fprintf(stderr, "Usage: %s [user] [proxy-target] [keytab]\n", argv[0]); @@ -132,7 +270,9 @@ int main(int argc, char *argv[]) buf.value = argv[1]; buf.length = strlen((char *)buf.value); - major = gss_import_name(&minor, &buf, (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME, &principal); + major = gss_import_name(&minor, &buf, + (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME, + &user); if (GSS_ERROR(major)) { displayStatus("gss_import_name(user)", major, minor); goto out; @@ -142,17 +282,13 @@ int main(int argc, char *argv[]) buf.value = argv[2]; buf.length = strlen((char *)buf.value); - major = gss_import_name(&minor, &buf, (gss_OID)GSS_C_NT_HOSTBASED_SERVICE, &target); + major = gss_import_name(&minor, &buf, + (gss_OID)GSS_C_NT_HOSTBASED_SERVICE, + &target); if (GSS_ERROR(major)) { displayStatus("gss_import_name(target)", major, minor); goto out; } - - major = gss_krb5_add_sec_context_delegatee(&minor, &context_handle, target); - if (GSS_ERROR(major)) { - displayStatus("gss_krb5_add_sec_context_delegatee", major, minor); - goto out; - } } else { target = GSS_C_NO_NAME; } @@ -165,74 +301,67 @@ int main(int argc, char *argv[]) } } - buf.value = NULL; - - major = gss_krb5_create_sec_context_for_principal(&minor, - &context_handle, - verifier_cred_handle, - principal, - GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG, - 0, - &src_name, - &mech_type, - &ret_flags, - &time_ret, - &delegated_cred_handle); + mechs.elements = (gss_OID)gss_mech_krb5; + mechs.count = 1; + + /* get default cred */ + major = gss_acquire_cred(&minor, + GSS_C_NO_NAME, + GSS_C_INDEFINITE, + &mechs, + GSS_C_BOTH, + &impersonator_cred_handle, + &actual_mechs, + NULL); if (GSS_ERROR(major)) { - displayStatus("gss_krb5_create_sec_context_for_principal", major, minor); + displayStatus("gss_acquire_cred(impersonator_cred)", major, minor); goto out; } - displayCanonName(&minor, src_name, "User name"); - if (target != GSS_C_NO_NAME) - displayCanonName(&minor, target, "Target name"); - - if (delegated_cred_handle != GSS_C_NO_CREDENTIAL) { - gss_name_t cred_name = GSS_C_NO_NAME; - OM_uint32 lifetime; - gss_cred_usage_t usage; - - buf.value = NULL; + (void) gss_release_oid_set(&minor, &actual_mechs); + + printf("Protocol transition tests follow\n"); + printf("-----------------------------------\n\n"); + + /* get S4U2Self cred */ + major = gss_acquire_cred_with_name(&minor, + impersonator_cred_handle, + user, + GSS_C_INDEFINITE, + &mechs, + GSS_C_INITIATE, + &user_cred_handle, + &actual_mechs, + NULL); + if (GSS_ERROR(major)) { + displayStatus("gss_acquire_cred(user_cred)", major, minor); + goto out; + } - if (gss_inquire_cred(&minor, delegated_cred_handle, &cred_name, - &lifetime, &usage, NULL) == GSS_S_COMPLETE) - displayCanonName(&minor, cred_name, "Cred name"); - gss_release_name(&minor, &cred_name); + major = initAcceptSecContext(&minor, + user_cred_handle, + impersonator_cred_handle, + &delegated_cred_handle); + if (GSS_ERROR(major)) + goto out; - printf("\n"); + printf("\n"); - gss_delete_sec_context(&minor, &context_handle, NULL); - context_handle = NULL; - - major = gss_init_sec_context(&minor, - delegated_cred_handle, - &context_handle, - target, - (gss_OID)gss_mech_krb5, - GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG, - 0, - GSS_C_NO_CHANNEL_BINDINGS, - GSS_C_NO_BUFFER, - &mech_type, - &buf, - &ret_flags, - &time_ret); - if (GSS_ERROR(major)) { - displayStatus("gss_init_sec_context(delegated_cred_handle)", major, minor); - } - printf("gss_init_sec_context with delegated credentials succeeded\n"); - gss_release_buffer(&minor, &buf); + if (delegated_cred_handle != GSS_C_NO_CREDENTIAL) { + major = constrainedDelegate(&minor, &mechs, target, + delegated_cred_handle, + impersonator_cred_handle); } else if (target != GSS_C_NO_NAME) { fprintf(stderr, "Warning: no delegated credentials handle returned\n"); } - out: - gss_release_name(&minor, &principal); - gss_release_name(&minor, &target); - gss_release_name(&minor, &src_name); - gss_delete_sec_context(&minor, &context_handle, NULL); - gss_release_cred(&minor, &delegated_cred_handle); + (void) gss_release_name(&minor, &user); + (void) gss_release_name(&minor, &target); + (void) gss_release_cred(&minor, &delegated_cred_handle); + (void) gss_release_cred(&minor, &impersonator_cred_handle); + (void) gss_release_cred(&minor, &user_cred_handle); + (void) gss_release_oid_set(&minor, &actual_mechs); return GSS_ERROR(major) ? 1 : 0; } |