aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGreg Hudson <ghudson@mit.edu>2010-09-27 03:39:22 +0000
committerGreg Hudson <ghudson@mit.edu>2010-09-27 03:39:22 +0000
commite76d9a48c905e6db8ea9b7af4b843070756effaa (patch)
tree4de2b9144c91046489889161acf8131635faac27
parent19b34d5112cc13214f6a47962be89f199966a449 (diff)
downloadkrb5-e76d9a48c905e6db8ea9b7af4b843070756effaa.zip
krb5-e76d9a48c905e6db8ea9b7af4b843070756effaa.tar.gz
krb5-e76d9a48c905e6db8ea9b7af4b843070756effaa.tar.bz2
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
-rw-r--r--src/lib/gssapi/generic/gssapi_ext.h5
-rw-r--r--src/lib/gssapi/krb5/acquire_cred.c479
-rw-r--r--src/lib/gssapi/krb5/copy_ccache.c5
-rw-r--r--src/lib/gssapi/krb5/gssapiP_krb5.h33
-rw-r--r--src/lib/gssapi/krb5/gssapi_krb5.c24
-rw-r--r--src/lib/gssapi/krb5/gssapi_krb5.hin7
-rw-r--r--src/lib/gssapi/krb5/krb5_gss_glue.c58
-rw-r--r--src/lib/gssapi/krb5/set_allowable_enctypes.c13
-rw-r--r--src/lib/gssapi/libgssapi_krb5.exports4
-rw-r--r--src/lib/gssapi/mechglue/g_set_cred_option.c194
-rw-r--r--src/lib/gssapi/mechglue/mglueP.h2
-rw-r--r--src/lib/gssapi/spnego/gssapiP_spnego.h2
-rw-r--r--src/lib/gssapi/spnego/spnego_mech.c24
-rw-r--r--src/lib/krb5/libkrb5.exports1
-rw-r--r--src/tests/gssapi/t_gssexts.c104
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 8e5c7c9..d68b6cd 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 bceab61..55214ce 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 632c866..efc6807 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 573d315..fc74ff1 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 ba1dbeb..8b074d6 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 ce96454..c4e5a76 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 d2a47ac..eb9dd83 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 cdfc965..de1c5a4 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 5b00fc2..707fe52 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 ff388d9..7bb73ed 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 f219290..da427f4 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 d72c85d..e146508 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 e82e9b5..80789f6 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 57e5c45..413339b 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 959378a..ed14755 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");