diff options
author | Greg Hudson <ghudson@mit.edu> | 2010-09-27 03:39:22 +0000 |
---|---|---|
committer | Greg Hudson <ghudson@mit.edu> | 2010-09-27 03:39:22 +0000 |
commit | e76d9a48c905e6db8ea9b7af4b843070756effaa (patch) | |
tree | 4de2b9144c91046489889161acf8131635faac27 /src | |
parent | 19b34d5112cc13214f6a47962be89f199966a449 (diff) | |
download | krb5-e76d9a48c905e6db8ea9b7af4b843070756effaa.tar.gz krb5-e76d9a48c905e6db8ea9b7af4b843070756effaa.tar.xz krb5-e76d9a48c905e6db8ea9b7af4b843070756effaa.zip |
Add gss_krb5_import_cred
Add gss_krb5_import_cred from Heimdal; allows krb5 creds to be
acquired from a keytab or ccache into a GSSAPI credential without
using global process or thread variables.
Merged from the users/lhoward/import-cred branch.
ticket: 6785
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@24356 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/gssapi/generic/gssapi_ext.h | 5 | ||||
-rw-r--r-- | src/lib/gssapi/krb5/acquire_cred.c | 479 | ||||
-rw-r--r-- | src/lib/gssapi/krb5/copy_ccache.c | 5 | ||||
-rw-r--r-- | src/lib/gssapi/krb5/gssapiP_krb5.h | 33 | ||||
-rw-r--r-- | src/lib/gssapi/krb5/gssapi_krb5.c | 24 | ||||
-rw-r--r-- | src/lib/gssapi/krb5/gssapi_krb5.hin | 7 | ||||
-rw-r--r-- | src/lib/gssapi/krb5/krb5_gss_glue.c | 58 | ||||
-rw-r--r-- | src/lib/gssapi/krb5/set_allowable_enctypes.c | 13 | ||||
-rw-r--r-- | src/lib/gssapi/libgssapi_krb5.exports | 4 | ||||
-rw-r--r-- | src/lib/gssapi/mechglue/g_set_cred_option.c | 194 | ||||
-rw-r--r-- | src/lib/gssapi/mechglue/mglueP.h | 2 | ||||
-rw-r--r-- | src/lib/gssapi/spnego/gssapiP_spnego.h | 2 | ||||
-rw-r--r-- | src/lib/gssapi/spnego/spnego_mech.c | 24 | ||||
-rw-r--r-- | src/lib/krb5/libkrb5.exports | 1 | ||||
-rw-r--r-- | src/tests/gssapi/t_gssexts.c | 104 |
15 files changed, 659 insertions, 296 deletions
diff --git a/src/lib/gssapi/generic/gssapi_ext.h b/src/lib/gssapi/generic/gssapi_ext.h index 8e5c7c9f7..d68b6cd0a 100644 --- a/src/lib/gssapi/generic/gssapi_ext.h +++ b/src/lib/gssapi/generic/gssapi_ext.h @@ -115,10 +115,9 @@ OM_uint32 KRB5_CALLCONV gss_set_sec_context_option const gss_OID /*desired_object*/, const gss_buffer_t /*value*/); -/* XXX do these really belong in this header? */ -OM_uint32 KRB5_CALLCONV gssspi_set_cred_option +OM_uint32 KRB5_CALLCONV gss_set_cred_option (OM_uint32 * /*minor_status*/, - gss_cred_id_t /*cred*/, + gss_cred_id_t * /*cred*/, const gss_OID /*desired_object*/, const gss_buffer_t /*value*/); diff --git a/src/lib/gssapi/krb5/acquire_cred.c b/src/lib/gssapi/krb5/acquire_cred.c index bceab6173..55214ce6a 100644 --- a/src/lib/gssapi/krb5/acquire_cred.c +++ b/src/lib/gssapi/krb5/acquire_cred.c @@ -1,6 +1,6 @@ /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* - * Copyright 2000, 2007, 2008 by the Massachusetts Institute of Technology. + * Copyright 2000, 2007-2010 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may @@ -129,56 +129,55 @@ gss_krb5int_register_acceptor_identity(OM_uint32 *minor_status, } /* get credentials corresponding to a key in the krb5 keytab. - If the default name is requested, return the name in output_name. - If output_name is non-NULL, the caller will use or free it, regardless - of the return value. If successful, set the keytab-specific fields in cred */ static OM_uint32 acquire_accept_cred(krb5_context context, OM_uint32 *minor_status, - krb5_gss_name_t desired_name, - krb5_gss_name_t *output_name, + krb5_principal desired_princ, + krb5_keytab req_keytab, krb5_gss_cred_id_rec *cred) { krb5_error_code code; - krb5_principal princ; krb5_keytab kt; krb5_keytab_entry entry; - *output_name = NULL; - cred->keytab = NULL; + assert(cred->keytab == NULL); - /* open the default keytab */ + if (req_keytab != NULL) { + char ktname[BUFSIZ]; - code = gss_krb5int_initialize_library(); - if (code != 0) { - *minor_status = code; - return GSS_S_FAILURE; - } - code = k5_mutex_lock(&gssint_krb5_keytab_lock); - if (code) { - *minor_status = code; - return GSS_S_FAILURE; - } - if (krb5_gss_keytab != NULL) { - code = krb5_kt_resolve(context, krb5_gss_keytab, &kt); - k5_mutex_unlock(&gssint_krb5_keytab_lock); + /* Duplicate keytab handle */ + code = krb5_kt_get_name(context, req_keytab, ktname, sizeof(ktname)); + if (code) { + *minor_status = code; + return GSS_S_CRED_UNAVAIL; + } + code = krb5_kt_resolve(context, ktname, &kt); } else { - k5_mutex_unlock(&gssint_krb5_keytab_lock); - code = krb5_kt_default(context, &kt); + code = k5_mutex_lock(&gssint_krb5_keytab_lock); + if (code) { + *minor_status = code; + return GSS_S_FAILURE; + } + if (krb5_gss_keytab != NULL) { + code = krb5_kt_resolve(context, krb5_gss_keytab, &kt); + k5_mutex_unlock(&gssint_krb5_keytab_lock); + } else { + k5_mutex_unlock(&gssint_krb5_keytab_lock); + code = krb5_kt_default(context, &kt); + } } - if (code) { *minor_status = code; - return(GSS_S_CRED_UNAVAIL); + return GSS_S_CRED_UNAVAIL; } - if (desired_name != NULL) { - princ = desired_name->princ; - if ((code = krb5_kt_get_entry(context, kt, princ, 0, 0, &entry))) { - (void) krb5_kt_close(context, kt); + if (desired_princ != NULL) { + code = krb5_kt_get_entry(context, kt, desired_princ, 0, 0, &entry); + if (code) { + krb5_kt_close(context, kt); if (code == KRB5_KT_NOTFOUND) { char *errstr = (char *)krb5_get_error_message(context, code); krb5_set_error_message(context, KG_KEYTAB_NOMATCH, "%s", errstr); @@ -186,46 +185,50 @@ acquire_accept_cred(krb5_context context, *minor_status = KG_KEYTAB_NOMATCH; } else *minor_status = code; - return(GSS_S_CRED_UNAVAIL); + return GSS_S_CRED_UNAVAIL; } krb5_kt_free_entry(context, &entry); - /* Open the replay cache for this principal. */ - if ((code = krb5_get_server_rcache(context, - krb5_princ_component(context, princ, 0), - &cred->rcache))) { + assert(cred->name == NULL); + code = kg_init_name(context, desired_princ, NULL, + KG_INIT_NAME_INTERN, &cred->name); + if (code) { *minor_status = code; - return(GSS_S_FAILURE); + return GSS_S_FAILURE; } + /* Open the replay cache for this principal. */ + code = krb5_get_server_rcache(context, + krb5_princ_component(context, desired_princ, 0), + &cred->rcache); + if (code) { + *minor_status = code; + return GSS_S_FAILURE; + } } -/* hooray. we made it */ - cred->keytab = kt; - return(GSS_S_COMPLETE); + return GSS_S_COMPLETE; } #endif /* LEAN_CLIENT */ /* get credentials corresponding to the default credential cache. - If the default name is requested, return the name in output_name. - If output_name is non-NULL, the caller will use or free it, regardless - of the return value. If successful, set the ccache-specific fields in cred. */ static OM_uint32 acquire_init_cred(krb5_context context, OM_uint32 *minor_status, - krb5_gss_name_t desired_name, - krb5_gss_name_t *output_name, + krb5_ccache req_ccache, + krb5_principal desired_princ, gss_buffer_t password, krb5_gss_cred_id_rec *cred) { krb5_error_code code; krb5_ccache ccache; - krb5_principal princ = NULL, tmp_princ; + krb5_principal ccache_princ = NULL, tmp_princ; + krb5_const_principal cred_princ = NULL; krb5_cc_cursor cur; krb5_creds creds; int got_endtime; @@ -237,16 +240,16 @@ acquire_init_cred(krb5_context context, /* load the GSS ccache name into the kg_context */ if (GSS_ERROR(kg_sync_ccache_name(context, minor_status))) - return(GSS_S_FAILURE); + return GSS_S_FAILURE; /* check to see if the caller provided a ccache name if so * we will just use that and not search the cache collection */ if (GSS_ERROR(kg_caller_provided_ccache_name (minor_status, &caller_provided_ccache_name))) { - return(GSS_S_FAILURE); + return GSS_S_FAILURE; } #if defined(USE_KIM) || defined(USE_LEASH) - if (desired_name && !caller_provided_ccache_name) { + if (desired_princ && !caller_provided_ccache_name && !req_ccache) { #if defined(USE_KIM) kim_error err = KIM_NO_ERROR; kim_ccache kimccache = NULL; @@ -255,7 +258,7 @@ acquire_init_cred(krb5_context context, err = kim_identity_create_from_krb5_principal (&identity, context, - desired_name->princ); + desired_princ); if (!err) { err = kim_ccache_create_from_client_identity (&kimccache, identity); @@ -289,7 +292,7 @@ acquire_init_cred(krb5_context context, if (err) { *minor_status = err; - return(GSS_S_CRED_UNAVAIL); + return GSS_S_CRED_UNAVAIL; } #elif defined(USE_LEASH) @@ -303,80 +306,99 @@ acquire_init_cred(krb5_context context, if ( pLeash_AcquireInitialTicketsIfNeeded ) { char ccname[256]=""; - pLeash_AcquireInitialTicketsIfNeeded(context, desired_name->princ, ccname, sizeof(ccname)); + pLeash_AcquireInitialTicketsIfNeeded(context, desired_princ, ccname, sizeof(ccname)); if (!ccname[0]) { *minor_status = KRB5_CC_NOTFOUND; - return(GSS_S_CRED_UNAVAIL); + return GSS_S_CRED_UNAVAIL; } if ((code = krb5_cc_resolve (context, ccname, &ccache))) { *minor_status = code; - return(GSS_S_CRED_UNAVAIL); + return GSS_S_CRED_UNAVAIL; } } else { /* leash dll not available, open the default credential cache */ if ((code = krb5int_cc_default(context, &ccache))) { *minor_status = code; - return(GSS_S_CRED_UNAVAIL); + return GSS_S_CRED_UNAVAIL; } } #endif /* USE_LEASH */ } else #endif /* USE_KIM || USE_LEASH */ { - /* open the default credential cache */ - - if ((code = krb5int_cc_default(context, &ccache))) { + if (req_ccache != NULL) { + /* Duplicate ccache handle */ + code = krb5_cc_dup(context, req_ccache, &ccache); + } else { + /* Open the default credential cache */ + code = krb5int_cc_default(context, &ccache); + } + if (code != 0) { *minor_status = code; - return(GSS_S_CRED_UNAVAIL); + return GSS_S_CRED_UNAVAIL; } } /* turn off OPENCLOSE mode while extensive frobbing is going on */ code = krb5_cc_set_flags(context, ccache, 0); if (code == KRB5_FCC_NOFILE && - password != GSS_C_NO_BUFFER && desired_name != NULL) { + password != GSS_C_NO_BUFFER && desired_princ != NULL) { /* We will get initial creds later. */ - code = krb5_cc_initialize(context, ccache, desired_name->princ); + code = krb5_cc_initialize(context, ccache, desired_princ); if (code == 0) code = krb5_cc_set_flags(context, ccache, 0); } if (code != 0) { - (void)krb5_cc_close(context, ccache); + krb5_cc_close(context, ccache); *minor_status = code; - return(GSS_S_CRED_UNAVAIL); + return GSS_S_CRED_UNAVAIL; } - /* get out the principal name and see if it matches */ - code = krb5_cc_get_principal(context, ccache, &princ); + /* + * Credentials cache principal must match either the acceptor principal + * name or the desired_princ argument (they may be the same). + */ + if (cred->name != NULL && desired_princ == NULL) + desired_princ = cred->name->princ; + + code = krb5_cc_get_principal(context, ccache, &ccache_princ); if (code != 0) { - (void)krb5_cc_close(context, ccache); + krb5_cc_close(context, ccache); *minor_status = code; - return(GSS_S_FAILURE); + return GSS_S_FAILURE; } - if (desired_name != NULL) { - if (!krb5_principal_compare(context, princ, desired_name->princ)) { - (void)krb5_free_principal(context, princ); - (void)krb5_cc_close(context, ccache); + if (desired_princ != NULL) { + if (!krb5_principal_compare(context, ccache_princ, desired_princ)) { + krb5_free_principal(context, ccache_princ); + krb5_cc_close(context, ccache); *minor_status = KG_CCACHE_NOMATCH; - return(GSS_S_CRED_UNAVAIL); + return GSS_S_CRED_UNAVAIL; } - (void)krb5_free_principal(context, princ); - princ = desired_name->princ; - } else { - if ((code = kg_init_name(context, princ, NULL, + } + + /* + * If we are acquiring initiator-only default credentials, then set + * cred->name to the credentials cache principal name. + */ + if (cred->name == NULL) { + if ((code = kg_init_name(context, ccache_princ, NULL, KG_INIT_NAME_NO_COPY | KG_INIT_NAME_INTERN, - output_name))) { - (void)krb5_free_principal(context, princ); - (void)krb5_cc_close(context, ccache); + &cred->name))) { + krb5_free_principal(context, ccache_princ); + krb5_cc_close(context, ccache); *minor_status = code; - return(GSS_S_FAILURE); + return GSS_S_FAILURE; } - /* princ is now owned by output_name, it need not be freed here */ + } else { + krb5_free_principal(context, ccache_princ); } + assert(cred->name->princ != NULL); + cred_princ = cred->name->princ; + if (password != GSS_C_NO_BUFFER) { /* stash the password for later */ password_data.length = password->length; @@ -385,7 +407,7 @@ acquire_init_cred(krb5_context context, code = krb5int_copy_data_contents_add0(context, &password_data, &cred->password); if (code != 0) { - (void)krb5_cc_close(context, ccache); + krb5_cc_close(context, ccache); *minor_status = code; return GSS_S_FAILURE; } @@ -393,7 +415,7 @@ acquire_init_cred(krb5_context context, /* restore the OPENCLOSE flag */ code = krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE); if (code != 0) { - (void)krb5_cc_close(context, ccache); + krb5_cc_close(context, ccache); *minor_status = code; return GSS_S_FAILURE; } @@ -405,9 +427,9 @@ acquire_init_cred(krb5_context context, /* iterate over the ccache, find the tgt */ if ((code = krb5_cc_start_seq_get(context, ccache, &cur))) { - (void)krb5_cc_close(context, ccache); + krb5_cc_close(context, ccache); *minor_status = code; - return(GSS_S_FAILURE); + return GSS_S_FAILURE; } /* this is hairy. If there's a tgt for the principal's local realm @@ -417,16 +439,16 @@ acquire_init_cred(krb5_context context, got_endtime = 0; code = krb5_build_principal_ext(context, &tmp_princ, - krb5_princ_realm(context, princ)->length, - krb5_princ_realm(context, princ)->data, + krb5_princ_realm(context, cred_princ)->length, + krb5_princ_realm(context, cred_princ)->data, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME, - krb5_princ_realm(context, princ)->length, - krb5_princ_realm(context, princ)->data, + krb5_princ_realm(context, cred_princ)->length, + krb5_princ_realm(context, cred_princ)->data, 0); if (code) { - (void)krb5_cc_close(context, ccache); + krb5_cc_close(context, ccache); *minor_status = code; - return(GSS_S_FAILURE); + return GSS_S_FAILURE; } while (!(code = krb5_cc_next_cred(context, ccache, &cur, &creds))) { if (krb5_principal_compare(context, tmp_princ, creds.server)) { @@ -447,27 +469,27 @@ acquire_init_cred(krb5_context context, if (code && code != KRB5_CC_END) { /* this means some error occurred reading the ccache */ - (void)krb5_cc_end_seq_get(context, ccache, &cur); - (void)krb5_cc_close(context, ccache); + krb5_cc_end_seq_get(context, ccache, &cur); + krb5_cc_close(context, ccache); *minor_status = code; - return(GSS_S_FAILURE); + return GSS_S_FAILURE; } else if (! got_endtime) { /* this means the ccache was entirely empty */ - (void)krb5_cc_end_seq_get(context, ccache, &cur); - (void)krb5_cc_close(context, ccache); + krb5_cc_end_seq_get(context, ccache, &cur); + krb5_cc_close(context, ccache); *minor_status = KG_EMPTY_CCACHE; - return(GSS_S_FAILURE); + return GSS_S_FAILURE; } else { /* this means that we found an endtime to use. */ if ((code = krb5_cc_end_seq_get(context, ccache, &cur))) { - (void)krb5_cc_close(context, ccache); + krb5_cc_close(context, ccache); *minor_status = code; - return(GSS_S_FAILURE); + return GSS_S_FAILURE; } if ((code = krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE))) { - (void)krb5_cc_close(context, ccache); + krb5_cc_close(context, ccache); *minor_status = code; - return(GSS_S_FAILURE); + return GSS_S_FAILURE; } } @@ -475,26 +497,37 @@ acquire_init_cred(krb5_context context, cred->ccache = ccache; /* minor_status is set while we are iterating over the ccache */ - return(GSS_S_COMPLETE); + return GSS_S_COMPLETE; } -/*ARGSUSED*/ -static OM_uint32 -acquire_cred(minor_status, desired_name, password, time_req, - cred_usage, output_cred_handle, time_rec, req_iakerb) - OM_uint32 *minor_status; - const gss_name_t desired_name; - const gss_buffer_t password; +struct acquire_cred_args { + gss_name_t desired_name; + gss_buffer_t password; OM_uint32 time_req; + gss_OID_set desired_mechs; gss_cred_usage_t cred_usage; - gss_cred_id_t *output_cred_handle; - OM_uint32 *time_rec; - int req_iakerb; + krb5_keytab keytab; + krb5_ccache ccache; + int iakerb; +}; + +/*ARGSUSED*/ +static OM_uint32 +acquire_cred(OM_uint32 *minor_status, + const struct acquire_cred_args *args, + gss_cred_id_t *output_cred_handle, + OM_uint32 *time_rec) { krb5_context context = NULL; krb5_gss_cred_id_t cred = NULL; OM_uint32 ret; krb5_error_code code = 0; + krb5_principal desired_princ = NULL; + + /* make sure all outputs are valid */ + *output_cred_handle = GSS_C_NO_CREDENTIAL; + if (time_rec) + *time_rec = 0; code = gss_krb5int_initialize_library(); if (code) @@ -504,31 +537,15 @@ acquire_cred(minor_status, desired_name, password, time_req, if (code) goto krb_error_out; - /* make sure all outputs are valid */ - - *output_cred_handle = NULL; - if (time_rec) - *time_rec = 0; - - /* validate the name */ - - /*SUPPRESS 29*/ - if ((desired_name != GSS_C_NO_NAME) && - (! kg_validate_name(desired_name))) { - code = G_VALIDATE_FAILED; - goto krb_error_out; - } - /* create the gss cred structure */ cred = k5alloc(sizeof(krb5_gss_cred_id_rec), &code); - if (code != 0) + if (cred == NULL) goto krb_error_out; - cred->usage = cred_usage; + cred->usage = args->cred_usage; cred->name = NULL; - cred->iakerb_mech = req_iakerb; - cred->default_identity = (desired_name == GSS_C_NO_NAME); - + cred->iakerb_mech = args->iakerb; + cred->default_identity = (args->desired_name == GSS_C_NO_NAME); #ifndef LEAN_CLIENT cred->keytab = NULL; #endif /* LEAN_CLIENT */ @@ -538,59 +555,50 @@ acquire_cred(minor_status, desired_name, password, time_req, if (code) goto krb_error_out; - /* Note that we don't need to lock this GSSAPI credential record - here, because no other thread can gain access to it until we - return it. */ - - if ((cred_usage != GSS_C_INITIATE) && - (cred_usage != GSS_C_ACCEPT) && - (cred_usage != GSS_C_BOTH)) { + switch (args->cred_usage) { + case GSS_C_INITIATE: + case GSS_C_ACCEPT: + case GSS_C_BOTH: + break; + default: ret = GSS_S_FAILURE; *minor_status = (OM_uint32) G_BAD_USAGE; goto error_out; } - /* if requested, acquire credentials for accepting */ - /* this will fill in cred->name if the desired_name is not specified */ + if (args->desired_name != GSS_C_NO_NAME) + desired_princ = ((krb5_gss_name_t)args->desired_name)->princ; + #ifndef LEAN_CLIENT - if ((cred_usage == GSS_C_ACCEPT) || - (cred_usage == GSS_C_BOTH)) - if ((ret = acquire_accept_cred(context, minor_status, - (krb5_gss_name_t)desired_name, - &cred->name, cred)) - != GSS_S_COMPLETE) { + /* + * If requested, acquire credentials for accepting. This will fill + * in cred->name if desired_princ is specified. + */ + if (args->cred_usage == GSS_C_ACCEPT || args->cred_usage == GSS_C_BOTH) { + ret = acquire_accept_cred(context, minor_status, + desired_princ, + args->keytab, cred); + if (ret != GSS_S_COMPLETE) goto error_out; - } + } #endif /* LEAN_CLIENT */ - /* if requested, acquire credentials for initiation */ - /* this will fill in cred->name if it wasn't set above, and - the desired_name is not specified */ - - if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) { - ret = acquire_init_cred(context, minor_status, - cred->name ? - cred->name : (krb5_gss_name_t)desired_name, - &cred->name, password, cred); + /* + * If requested, acquire credentials for initiation. This will fill + * in cred->name if it wasn't set above. + */ + if (args->cred_usage == GSS_C_INITIATE || args->cred_usage == GSS_C_BOTH) { + ret = acquire_init_cred(context, minor_status, args->ccache, + desired_princ, args->password, cred); if (ret != GSS_S_COMPLETE) goto error_out; } - /* if the princ wasn't filled in already, fill it in now */ - - if (!cred->name && (desired_name != GSS_C_NO_NAME)) { - code = kg_duplicate_name(context, - (krb5_gss_name_t)desired_name, - 0, &cred->name); - if (code != 0) - goto krb_error_out; - } + assert(cred->default_identity || cred->name != NULL); /*** at this point, the cred structure has been completely created */ - /* compute time_rec */ - - if (cred_usage == GSS_C_ACCEPT) { + if (args->cred_usage == GSS_C_ACCEPT) { if (time_rec) *time_rec = GSS_C_INDEFINITE; } else { @@ -604,20 +612,16 @@ acquire_cred(minor_status, desired_name, password, time_req, *time_rec = (cred->tgt_expire > now) ? (cred->tgt_expire - now) : 0; } - /* intern the credential handle */ - - if (! kg_save_cred_id((gss_cred_id_t) cred)) { + if (!kg_save_cred_id((gss_cred_id_t)cred)) { ret = GSS_S_FAILURE; goto error_out; } - /* return success */ - *minor_status = 0; *output_cred_handle = (gss_cred_id_t) cred; krb5_free_context(context); - return(GSS_S_COMPLETE); + return GSS_S_COMPLETE; krb_error_out: *minor_status = code; @@ -626,10 +630,10 @@ krb_error_out: error_out: if (cred != NULL) { if (cred->ccache) - (void)krb5_cc_close(context, cred->ccache); + krb5_cc_close(context, cred->ccache); #ifndef LEAN_CLIENT if (cred->keytab) - (void)krb5_kt_close(context, cred->keytab); + krb5_kt_close(context, cred->keytab); #endif /* LEAN_CLIENT */ if (cred->name) kg_release_name(context, 0, &cred->name); @@ -643,7 +647,7 @@ error_out: OM_uint32 gss_krb5int_set_cred_rcache(OM_uint32 *minor_status, - gss_cred_id_t cred_handle, + gss_cred_id_t *cred_handle, const gss_OID desired_oid, const gss_buffer_t value) { @@ -659,10 +663,7 @@ gss_krb5int_set_cred_rcache(OM_uint32 *minor_status, rcache = (krb5_rcache)value->value; - if (cred_handle == GSS_C_NO_CREDENTIAL) - return GSS_S_NO_CRED; - - cred = (krb5_gss_cred_id_t)cred_handle; + cred = (krb5_gss_cred_id_t)*cred_handle; code = krb5_gss_init_context(&context); if (code) { @@ -704,8 +705,21 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req, gss_OID_set *actual_mechs; OM_uint32 *time_rec; { - return acquire_cred(minor_status, desired_name, GSS_C_NO_BUFFER, - time_req, cred_usage, output_cred_handle, time_rec, 0); + struct acquire_cred_args args; + + if (desired_name && !kg_validate_name(desired_name)) { + *minor_status = G_VALIDATE_FAILED; + return GSS_S_FAILURE; + } + + memset(&args, 0, sizeof(args)); + args.desired_name = desired_name; + args.time_req = time_req; + args.desired_mechs = desired_mechs; + args.cred_usage = cred_usage; + args.iakerb = 0; + + return acquire_cred(minor_status, &args, output_cred_handle, time_rec); } OM_uint32 @@ -721,8 +735,21 @@ iakerb_gss_acquire_cred(minor_status, desired_name, time_req, gss_OID_set *actual_mechs; OM_uint32 *time_rec; { - return acquire_cred(minor_status, desired_name, GSS_C_NO_BUFFER, - time_req, cred_usage, output_cred_handle, time_rec, 1); + struct acquire_cred_args args; + + if (desired_name && !kg_validate_name(desired_name)) { + *minor_status = G_VALIDATE_FAILED; + return GSS_S_FAILURE; + } + + memset(&args, 0, sizeof(args)); + args.desired_name = desired_name; + args.time_req = time_req; + args.desired_mechs = desired_mechs; + args.cred_usage = cred_usage; + args.iakerb = 1; + + return acquire_cred(minor_status, &args, output_cred_handle, time_rec); } OM_uint32 @@ -736,8 +763,22 @@ krb5_gss_acquire_cred_with_password(OM_uint32 *minor_status, gss_OID_set *actual_mechs, OM_uint32 *time_rec) { - return acquire_cred(minor_status, desired_name, password, - time_req, cred_usage, output_cred_handle, time_rec, 0); + struct acquire_cred_args args; + + if (desired_name && !kg_validate_name(desired_name)) { + *minor_status = G_VALIDATE_FAILED; + return GSS_S_FAILURE; + } + + memset(&args, 0, sizeof(args)); + args.desired_name = desired_name; + args.password = password; + args.time_req = time_req; + args.desired_mechs = desired_mechs; + args.cred_usage = cred_usage; + args.iakerb = 0; + + return acquire_cred(minor_status, &args, output_cred_handle, time_rec); } OM_uint32 @@ -751,6 +792,64 @@ iakerb_gss_acquire_cred_with_password(OM_uint32 *minor_status, gss_OID_set *actual_mechs, OM_uint32 *time_rec) { - return acquire_cred(minor_status, desired_name, password, - time_req, cred_usage, output_cred_handle, time_rec, 1); + struct acquire_cred_args args; + + if (desired_name && !kg_validate_name(desired_name)) { + *minor_status = G_VALIDATE_FAILED; + return GSS_S_FAILURE; + } + + memset(&args, 0, sizeof(args)); + args.desired_name = desired_name; + args.password = password; + args.time_req = time_req; + args.desired_mechs = desired_mechs; + args.cred_usage = cred_usage; + args.iakerb = 1; + + return acquire_cred(minor_status, &args, output_cred_handle, time_rec); +} + +OM_uint32 +gss_krb5int_import_cred(OM_uint32 *minor_status, + gss_cred_id_t *cred_handle, + const gss_OID desired_oid, + const gss_buffer_t value) +{ + struct krb5_gss_import_cred_req *req; + struct acquire_cred_args args; + krb5_gss_name_rec name; + OM_uint32 time_rec; + + assert(value->length == sizeof(*req)); + + if (value->length != sizeof(*req)) + return GSS_S_FAILURE; + + req = (struct krb5_gss_import_cred_req *)value->value; + + memset(&args, 0, sizeof(args)); + + if (req->keytab_principal) { + memset(&name, 0, sizeof(name)); + name.princ = req->keytab_principal; + args.desired_name = (gss_name_t)&name; + } + + args.ccache = req->id; + args.keytab = req->keytab; + + if (req->id && req->keytab) + args.cred_usage = GSS_C_BOTH; + else if (req->id) + args.cred_usage = GSS_C_INITIATE; + else if (req->keytab) + args.cred_usage = GSS_C_ACCEPT; + else { + *minor_status = EINVAL; + return GSS_S_FAILURE; + } + + return acquire_cred(minor_status, &args, cred_handle, &time_rec); } + diff --git a/src/lib/gssapi/krb5/copy_ccache.c b/src/lib/gssapi/krb5/copy_ccache.c index 632c8664e..efc68073d 100644 --- a/src/lib/gssapi/krb5/copy_ccache.c +++ b/src/lib/gssapi/krb5/copy_ccache.c @@ -3,7 +3,7 @@ OM_uint32 KRB5_CALLCONV gss_krb5int_copy_ccache(OM_uint32 *minor_status, - gss_cred_id_t cred_handle, + gss_cred_id_t *cred_handle, const gss_OID desired_object, const gss_buffer_t value) { @@ -22,8 +22,7 @@ gss_krb5int_copy_ccache(OM_uint32 *minor_status, out_ccache = (krb5_ccache)value->value; /* cred handle will have been validated by gssspi_set_cred_option() */ - - k5creds = (krb5_gss_cred_id_t) cred_handle; + k5creds = (krb5_gss_cred_id_t) *cred_handle; code = k5_mutex_lock(&k5creds->lock); if (code) { *minor_status = code; diff --git a/src/lib/gssapi/krb5/gssapiP_krb5.h b/src/lib/gssapi/krb5/gssapiP_krb5.h index 573d3150c..fc74ff1a1 100644 --- a/src/lib/gssapi/krb5/gssapiP_krb5.h +++ b/src/lib/gssapi/krb5/gssapiP_krb5.h @@ -1007,7 +1007,7 @@ OM_uint32 KRB5_CALLCONV gss_krb5int_get_tkt_flags OM_uint32 KRB5_CALLCONV gss_krb5int_copy_ccache (OM_uint32 *minor_status, - gss_cred_id_t cred_handle, + gss_cred_id_t *cred_handle, const gss_OID desired_oid, const gss_buffer_t value); @@ -1025,6 +1025,12 @@ OM_uint32 KRB5_CALLCONV gss_krb5int_ccache_name const gss_OID, const gss_buffer_t); +#define GSS_KRB5_INQ_SSPI_SESSION_KEY_OID_LENGTH 11 +#define GSS_KRB5_INQ_SSPI_SESSION_KEY_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x05" + +OM_uint32 +gss_krb5int_inq_session_key(OM_uint32 *, const gss_ctx_id_t, const gss_OID, gss_buffer_set_t *); + #define GSS_KRB5_SET_ALLOWABLE_ENCTYPES_OID_LENGTH 11 #define GSS_KRB5_SET_ALLOWABLE_ENCTYPES_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x04" @@ -1033,15 +1039,9 @@ struct krb5_gss_set_allowable_enctypes_req { krb5_enctype *ktypes; }; -#define GSS_KRB5_INQ_SSPI_SESSION_KEY_OID_LENGTH 11 -#define GSS_KRB5_INQ_SSPI_SESSION_KEY_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x05" - -OM_uint32 -gss_krb5int_inq_session_key(OM_uint32 *, const gss_ctx_id_t, const gss_OID, gss_buffer_set_t *); - OM_uint32 KRB5_CALLCONV gss_krb5int_set_allowable_enctypes(OM_uint32 *minor_status, - gss_cred_id_t cred, + gss_cred_id_t *cred, const gss_OID desired_oid, const gss_buffer_t value); @@ -1091,7 +1091,7 @@ gss_krb5int_extract_authz_data_from_sec_context(OM_uint32 *minor_status, #define GSS_KRB5_SET_CRED_RCACHE_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0b" OM_uint32 -gss_krb5int_set_cred_rcache(OM_uint32 *, gss_cred_id_t, const gss_OID, const gss_buffer_t); +gss_krb5int_set_cred_rcache(OM_uint32 *, gss_cred_id_t *, const gss_OID, const gss_buffer_t); #define GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID_LENGTH 11 #define GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0c" @@ -1102,6 +1102,21 @@ gss_krb5int_extract_authtime_from_sec_context(OM_uint32 *, const gss_OID, gss_buffer_set_t *); +#define GSS_KRB5_IMPORT_CRED_OID_LENGTH 11 +#define GSS_KRB5_IMPORT_CRED_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0d" + +struct krb5_gss_import_cred_req { + krb5_ccache id; + krb5_principal keytab_principal; + krb5_keytab keytab; +}; + +OM_uint32 KRB5_CALLCONV +gss_krb5int_import_cred(OM_uint32 *minor_status, + gss_cred_id_t *cred, + const gss_OID desired_oid, + const gss_buffer_t value); + #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 ba1dbebd7..8b074d616 100644 --- a/src/lib/gssapi/krb5/gssapi_krb5.c +++ b/src/lib/gssapi/krb5/gssapi_krb5.c @@ -510,7 +510,7 @@ krb5_gss_set_sec_context_option (OM_uint32 *minor_status, */ static struct { gss_OID_desc oid; - OM_uint32 (*func)(OM_uint32 *, gss_cred_id_t, const gss_OID, const gss_buffer_t); + OM_uint32 (*func)(OM_uint32 *, gss_cred_id_t *, const gss_OID, const gss_buffer_t); } krb5_gssspi_set_cred_option_ops[] = { { {GSS_KRB5_COPY_CCACHE_OID_LENGTH, GSS_KRB5_COPY_CCACHE_OID}, @@ -524,11 +524,15 @@ static struct { {GSS_KRB5_SET_CRED_RCACHE_OID_LENGTH, GSS_KRB5_SET_CRED_RCACHE_OID}, gss_krb5int_set_cred_rcache }, + { + {GSS_KRB5_IMPORT_CRED_OID_LENGTH, GSS_KRB5_IMPORT_CRED_OID}, + gss_krb5int_import_cred + }, }; static OM_uint32 krb5_gssspi_set_cred_option(OM_uint32 *minor_status, - gss_cred_id_t cred_handle, + gss_cred_id_t *cred_handle, const gss_OID desired_object, const gss_buffer_t value) { @@ -538,19 +542,19 @@ krb5_gssspi_set_cred_option(OM_uint32 *minor_status, if (minor_status == NULL) return GSS_S_CALL_INACCESSIBLE_WRITE; - *minor_status = 0; + if (cred_handle == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; - if (cred_handle == GSS_C_NO_CREDENTIAL) { - *minor_status = (OM_uint32)KRB5_NOCREDS_SUPPLIED; - return GSS_S_NO_CRED; - } + *minor_status = 0; if (desired_object == GSS_C_NO_OID) return GSS_S_CALL_INACCESSIBLE_READ; - major_status = krb5_gss_validate_cred(minor_status, cred_handle); - if (GSS_ERROR(major_status)) - return major_status; + if (*cred_handle != GSS_C_NO_CREDENTIAL) { + major_status = krb5_gss_validate_cred(minor_status, *cred_handle); + if (GSS_ERROR(major_status)) + return major_status; + } for (i = 0; i < sizeof(krb5_gssspi_set_cred_option_ops)/ sizeof(krb5_gssspi_set_cred_option_ops[0]); i++) { diff --git a/src/lib/gssapi/krb5/gssapi_krb5.hin b/src/lib/gssapi/krb5/gssapi_krb5.hin index ce96454fe..c4e5a76a5 100644 --- a/src/lib/gssapi/krb5/gssapi_krb5.hin +++ b/src/lib/gssapi/krb5/gssapi_krb5.hin @@ -282,6 +282,13 @@ gss_krb5_set_cred_rcache(OM_uint32 *minor_status, OM_uint32 KRB5_CALLCONV gsskrb5_extract_authtime_from_sec_context(OM_uint32 *, gss_ctx_id_t, krb5_timestamp *); +OM_uint32 KRB5_CALLCONV +gss_krb5_import_cred(OM_uint32 *minor_status, + krb5_ccache id, + krb5_principal keytab_principal, + krb5_keytab keytab, + gss_cred_id_t *cred); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/lib/gssapi/krb5/krb5_gss_glue.c b/src/lib/gssapi/krb5/krb5_gss_glue.c index d2a47acb8..eb9dd83ca 100644 --- a/src/lib/gssapi/krb5/krb5_gss_glue.c +++ b/src/lib/gssapi/krb5/krb5_gss_glue.c @@ -108,10 +108,44 @@ gss_krb5_copy_ccache(OM_uint32 *minor_status, req_buffer.value = out_ccache; req_buffer.length = sizeof(out_ccache); - major_status = gssspi_set_cred_option(minor_status, - cred_handle, - (gss_OID)&req_oid, - &req_buffer); + major_status = gss_set_cred_option(minor_status, + &cred_handle, + (gss_OID)&req_oid, + &req_buffer); + + return major_status; +} + +OM_uint32 KRB5_CALLCONV +gss_krb5_import_cred(OM_uint32 *minor_status, + krb5_ccache id, + krb5_principal keytab_principal, + krb5_keytab keytab, + gss_cred_id_t *cred) +{ + static const gss_OID_desc req_oid = { + GSS_KRB5_IMPORT_CRED_OID_LENGTH, + GSS_KRB5_IMPORT_CRED_OID }; + OM_uint32 major_status; + struct krb5_gss_import_cred_req req; + gss_buffer_desc req_buffer; + + if (cred == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *cred = GSS_C_NO_CREDENTIAL; + + req.id = id; + req.keytab_principal = keytab_principal; + req.keytab = keytab; + + req_buffer.value = &req; + req_buffer.length = sizeof(req); + + major_status = gss_set_cred_option(minor_status, + cred, + (gss_OID)&req_oid, + &req_buffer); return major_status; } @@ -189,10 +223,10 @@ gss_krb5_set_allowable_enctypes(OM_uint32 *minor_status, req_buffer.length = sizeof(req); req_buffer.value = &req; - major_status = gssspi_set_cred_option(minor_status, - cred, - (gss_OID)&req_oid, - &req_buffer); + major_status = gss_set_cred_option(minor_status, + &cred, + (gss_OID)&req_oid, + &req_buffer); return major_status; } @@ -363,10 +397,10 @@ gss_krb5_set_cred_rcache(OM_uint32 *minor_status, req_buffer.length = sizeof(rcache); req_buffer.value = rcache; - major_status = gssspi_set_cred_option(minor_status, - cred, - (gss_OID)&req_oid, - &req_buffer); + major_status = gss_set_cred_option(minor_status, + &cred, + (gss_OID)&req_oid, + &req_buffer); return major_status; } diff --git a/src/lib/gssapi/krb5/set_allowable_enctypes.c b/src/lib/gssapi/krb5/set_allowable_enctypes.c index cdfc96583..de1c5a4db 100644 --- a/src/lib/gssapi/krb5/set_allowable_enctypes.c +++ b/src/lib/gssapi/krb5/set_allowable_enctypes.c @@ -61,7 +61,7 @@ OM_uint32 KRB5_CALLCONV gss_krb5int_set_allowable_enctypes(OM_uint32 *minor_status, - gss_cred_id_t cred_handle, + gss_cred_id_t *cred_handle, const gss_OID desired_oid, const gss_buffer_t value) { @@ -81,16 +81,7 @@ gss_krb5int_set_allowable_enctypes(OM_uint32 *minor_status, req = (struct krb5_gss_set_allowable_enctypes_req *)value->value; /* verify and valildate cred handle */ - if (cred_handle == GSS_C_NO_CREDENTIAL) { - kerr = KRB5_NOCREDS_SUPPLIED; - goto error_out; - } - major_status = krb5_gss_validate_cred(&temp_status, cred_handle); - if (GSS_ERROR(major_status)) { - kerr = temp_status; - goto error_out; - } - cred = (krb5_gss_cred_id_t) cred_handle; + cred = (krb5_gss_cred_id_t) *cred_handle; if (req->ktypes) { for (i = 0; i < req->num_ktypes && req->ktypes[i]; i++) { diff --git a/src/lib/gssapi/libgssapi_krb5.exports b/src/lib/gssapi/libgssapi_krb5.exports index 5b00fc218..707fe52ee 100644 --- a/src/lib/gssapi/libgssapi_krb5.exports +++ b/src/lib/gssapi/libgssapi_krb5.exports @@ -46,8 +46,9 @@ gss_inquire_sec_context_by_oid gss_krb5_ccache_name gss_krb5_copy_ccache gss_krb5_export_lucid_sec_context -gss_krb5_get_tkt_flags gss_krb5_free_lucid_sec_context +gss_krb5_get_tkt_flags +gss_krb5_import_cred gss_krb5_set_allowable_enctypes gss_krb5_set_cred_rcache gss_krb5int_make_seal_token_v3 @@ -99,6 +100,7 @@ gss_wrap_aead gss_wrap_iov gss_wrap_iov_length gss_wrap_size_limit +gss_set_cred_option gssspi_set_cred_option gssspi_mech_invoke krb5_gss_dbg_client_expcreds diff --git a/src/lib/gssapi/mechglue/g_set_cred_option.c b/src/lib/gssapi/mechglue/g_set_cred_option.c index ff388d9e3..7bb73ed0b 100644 --- a/src/lib/gssapi/mechglue/g_set_cred_option.c +++ b/src/lib/gssapi/mechglue/g_set_cred_option.c @@ -1,5 +1,5 @@ /* - * Copyright 2008 by the Massachusetts Institute of Technology. + * Copyright 2008-2010 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may @@ -35,11 +35,92 @@ #include <string.h> #include <time.h> +static OM_uint32 +alloc_union_cred(OM_uint32 *minor_status, + gss_mechanism mech, + gss_cred_id_t mech_cred, + gss_union_cred_t *pcred) +{ + OM_uint32 status; + OM_uint32 temp_minor_status; + gss_union_cred_t cred = NULL; + gss_name_t mech_name = GSS_C_NO_NAME; + + *pcred = NULL; + + if (mech->gss_inquire_cred == NULL) { + status = GSS_S_BAD_MECH; + goto cleanup; + } + + status = GSS_S_FAILURE; + + cred = calloc(1, sizeof(*cred)); + if (cred == NULL) { + *minor_status = ENOMEM; + goto cleanup; + } + + cred->loopback = cred; + cred->count = 1; + + cred->cred_array = calloc(cred->count, sizeof(gss_cred_id_t)); + if (cred->cred_array == NULL) { + *minor_status = ENOMEM; + goto cleanup; + } + cred->cred_array[0] = mech_cred; + + status = generic_gss_copy_oid(minor_status, + &mech->mech_type, + &cred->mechs_array); + if (status != GSS_S_COMPLETE) + goto cleanup; + + cred->auxinfo.creation_time = (OM_uint32)time(NULL); + + status = mech->gss_inquire_cred(minor_status, + mech_cred, + &mech_name, + &cred->auxinfo.time_rec, + &cred->auxinfo.cred_usage, + NULL); + if (status != GSS_S_COMPLETE) + goto cleanup; + + status = mech->gss_display_name(minor_status, + mech_name, + &cred->auxinfo.name, + &cred->auxinfo.name_type); + if (status != GSS_S_COMPLETE) + goto cleanup; + + status = GSS_S_COMPLETE; + *pcred = cred; + +cleanup: + if (status != GSS_S_COMPLETE) + gss_release_cred(&temp_minor_status, (gss_cred_id_t *)&cred); + mech->gss_release_name(&temp_minor_status, &mech_name); + + return status; +} + +/* + * This differs from gssspi_set_cred_option() as shipped in 1.7, in that + * it can return a cred handle. To denote this change we have changed the + * name of the function from gssspi_set_cred_option() to gss_set_cred_option(). + * However, the dlsym() entry point is still gssspi_set_cred_option(). This + * fixes a separate issue, namely that a dynamically loaded mechanism could + * not itself call set_cred_option() without calling its own implementation + * instead of the mechanism glue's. (This is useful where a mechanism wishes + * to export a mechanism-specific API that is a wrapper around this function.) + */ OM_uint32 KRB5_CALLCONV -gssspi_set_cred_option(OM_uint32 *minor_status, - gss_cred_id_t cred_handle, - const gss_OID desired_object, - const gss_buffer_t value) +gss_set_cred_option(OM_uint32 *minor_status, + gss_cred_id_t *cred_handle, + const gss_OID desired_object, + const gss_buffer_t value) { gss_union_cred_t union_cred; gss_mechanism mech; @@ -51,42 +132,95 @@ gssspi_set_cred_option(OM_uint32 *minor_status, if (minor_status == NULL) return GSS_S_CALL_INACCESSIBLE_WRITE; - if (cred_handle == GSS_C_NO_CREDENTIAL) - return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED; + if (cred_handle == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; *minor_status = 0; - union_cred = (gss_union_cred_t) cred_handle; - status = GSS_S_UNAVAILABLE; - for (i = 0; i < union_cred->count; i++) { - mech = gssint_get_mechanism(&union_cred->mechs_array[i]); - if (mech == NULL) { - status = GSS_S_BAD_MECH; - break; - } + if (*cred_handle == GSS_C_NO_CREDENTIAL) { + gss_cred_id_t mech_cred = GSS_C_NO_CREDENTIAL; - if (mech->gssspi_set_cred_option == NULL) { - continue; - } + /* + * We need to give a mechanism the opportunity to allocate a + * credentials handle. Unfortunately this does mean that only + * the default mechanism can allocate a credentials handle. + */ + mech = gssint_get_mechanism(NULL); + if (mech == NULL) + return GSS_S_BAD_MECH; + + if (mech->gssspi_set_cred_option == NULL) + return GSS_S_UNAVAILABLE; - mech_status = (mech->gssspi_set_cred_option)(&mech_minor_status, - union_cred->cred_array[i], - desired_object, - value); - if (mech_status == GSS_S_UNAVAILABLE) { - continue; - } - else { - status = mech_status; - *minor_status = mech_minor_status; - } + status = mech->gssspi_set_cred_option(minor_status, + &mech_cred, + desired_object, + value); if (status != GSS_S_COMPLETE) { map_error(minor_status, mech); - break; + return status; + } + + if (mech_cred != GSS_C_NO_CREDENTIAL) { + status = alloc_union_cred(minor_status, + mech, + mech_cred, + &union_cred); + if (status != GSS_S_COMPLETE) + return status; + *cred_handle = (gss_cred_id_t)union_cred; + } + } else { + union_cred = (gss_union_cred_t)*cred_handle; + + for (i = 0; i < union_cred->count; i++) { + mech = gssint_get_mechanism(&union_cred->mechs_array[i]); + if (mech == NULL) { + status = GSS_S_BAD_MECH; + break; + } + + if (mech->gssspi_set_cred_option == NULL) + continue; + + mech_status = mech->gssspi_set_cred_option(&mech_minor_status, + &union_cred->cred_array[i], + desired_object, + value); + if (mech_status == GSS_S_UNAVAILABLE) + continue; + else { + status = mech_status; + *minor_status = mech_minor_status; + } + if (status != GSS_S_COMPLETE) { + map_error(minor_status, mech); + break; + } } } return status; } + +/* + * Provide this for backward ABI compatibility, but remove it from the + * header. + */ +OM_uint32 KRB5_CALLCONV +gssspi_set_cred_option(OM_uint32 *minor_status, + gss_cred_id_t cred, + const gss_OID desired_object, + const gss_buffer_t value); + +OM_uint32 KRB5_CALLCONV +gssspi_set_cred_option(OM_uint32 *minor_status, + gss_cred_id_t cred, + const gss_OID desired_object, + const gss_buffer_t value) +{ + return gss_set_cred_option(minor_status, &cred, + desired_object, value); +} diff --git a/src/lib/gssapi/mechglue/mglueP.h b/src/lib/gssapi/mechglue/mglueP.h index f21929015..da427f4a6 100644 --- a/src/lib/gssapi/mechglue/mglueP.h +++ b/src/lib/gssapi/mechglue/mglueP.h @@ -396,7 +396,7 @@ typedef struct gss_config { OM_uint32 (*gssspi_set_cred_option) ( OM_uint32 *, /* minor_status */ - gss_cred_id_t, /* cred_handle */ + gss_cred_id_t *, /* cred_handle */ const gss_OID, /* OID */ const gss_buffer_t /* value */ ); diff --git a/src/lib/gssapi/spnego/gssapiP_spnego.h b/src/lib/gssapi/spnego/gssapiP_spnego.h index d72c85da7..e146508c5 100644 --- a/src/lib/gssapi/spnego/gssapiP_spnego.h +++ b/src/lib/gssapi/spnego/gssapiP_spnego.h @@ -361,7 +361,7 @@ OM_uint32 spnego_gss_set_cred_option ( OM_uint32 *minor_status, - gss_cred_id_t cred_handle, + gss_cred_id_t *cred_handle, const gss_OID desired_object, const gss_buffer_t value ); diff --git a/src/lib/gssapi/spnego/spnego_mech.c b/src/lib/gssapi/spnego/spnego_mech.c index e82e9b5b0..80789f643 100644 --- a/src/lib/gssapi/spnego/spnego_mech.c +++ b/src/lib/gssapi/spnego/spnego_mech.c @@ -2247,18 +2247,38 @@ spnego_gss_inquire_cred_by_oid( OM_uint32 spnego_gss_set_cred_option( OM_uint32 *minor_status, - gss_cred_id_t cred_handle, + gss_cred_id_t *cred_handle, const gss_OID desired_object, const gss_buffer_t value) { OM_uint32 ret; + OM_uint32 tmp_minor_status; spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle; gss_cred_id_t mcred; + mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred; + ret = gssspi_set_cred_option(minor_status, - mcred, + &mcred, desired_object, value); + if (ret == GSS_S_COMPLETE && spcred == NULL) { + /* + * If the mechanism allocated a new credential handle, then + * we need to wrap it up in an SPNEGO credential handle. + */ + + spcred = malloc(sizeof(spnego_gss_cred_id_rec)); + if (spcred == NULL) { + gss_release_cred(&tmp_minor_status, &mcred); + *minor_status = ENOMEM; + return (GSS_S_FAILURE); + } + spcred->mcred = mcred; + spcred->neg_mechs = GSS_C_NULL_OID_SET; + *cred_handle = (gss_cred_id_t)spcred; + } + return (ret); } diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports index 57e5c45de..413339b2a 100644 --- a/src/lib/krb5/libkrb5.exports +++ b/src/lib/krb5/libkrb5.exports @@ -180,6 +180,7 @@ krb5_cc_default krb5_cc_default_name krb5_cc_destroy krb5_cc_dfl_ops +krb5_cc_dup krb5_cc_end_seq_get krb5_cc_file_ops krb5_cc_gen_new diff --git a/src/tests/gssapi/t_gssexts.c b/src/tests/gssapi/t_gssexts.c index 959378af5..ed1475596 100644 --- a/src/tests/gssapi/t_gssexts.c +++ b/src/tests/gssapi/t_gssexts.c @@ -288,6 +288,82 @@ initAcceptSecContext(OM_uint32 *minor, return major; } +static OM_uint32 +getDefaultCred(OM_uint32 *minor, + const char *keytab_name, + gss_OID_set mechs, + gss_cred_id_t *impersonator_cred_handle) +{ + OM_uint32 major = GSS_S_FAILURE, tmp_minor; + + if (keytab_name) { + krb5_error_code code; + krb5_context context = NULL; + krb5_keytab keytab = NULL; + krb5_principal keytab_principal = NULL; + krb5_ccache ccache = NULL; + + code = krb5_init_context(&context); + if (code) { + displayStatus("krb5_init_context", major, code); + return major; + } + + code = krb5_kt_resolve(context, keytab_name, &keytab); + if (code) { + displayStatus("krb5_kt_resolve", major, code); + goto out; + } + + code = krb5_cc_default(context, &ccache); + if (code) { + displayStatus("krb5_cc_default", major, code); + goto out; + } + + code = krb5_cc_get_principal(context, ccache, &keytab_principal); + if (code) { + displayStatus("krb5_cc_get_principal", major, code); + goto out; + } + + major = gss_krb5_import_cred(minor, + ccache, + keytab_principal, + keytab, + impersonator_cred_handle); + if (GSS_ERROR(major)) { + displayStatus("gss_krb5_import_cred", major, minor); + goto out; + } + + out: + if (code) + *minor = code; + krb5_free_principal(context, keytab_principal); + krb5_cc_close(context, ccache); + krb5_kt_close(context, keytab); + krb5_free_context(context); + } else { + gss_OID_set actual_mechs = GSS_C_NO_OID_SET; + + 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_acquire_cred", major, minor); + } + (void) gss_release_oid_set(&tmp_minor, &actual_mechs); + } + + return major; +} + int main(int argc, char *argv[]) { OM_uint32 minor, major; @@ -338,34 +414,16 @@ int main(int argc, char *argv[]) target = GSS_C_NO_NAME; } - if (argc > 3) { - major = krb5_gss_register_acceptor_identity(argv[3]); - if (GSS_ERROR(major)) { - displayStatus("krb5_gss_register_acceptor_identity", - major, minor); - goto out; - } - } - mechs.elements = use_spnego ? (gss_OID)&spnego_mech : (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_acquire_cred", major, minor); + major = getDefaultCred(&minor, + argc > 3 ? argv[3] : NULL, + &mechs, + &impersonator_cred_handle); + if (GSS_ERROR(major)) goto out; - } - - (void) gss_release_oid_set(&minor, &actual_mechs); printf("Protocol transition tests follow\n"); printf("-----------------------------------\n\n"); |