From: Greg Hudson Date: Mon, 5 Sep 2011 16:35:40 +0000 (+0000) Subject: Make gss-krb5 use cache collection X-Git-Tag: krb5-1.10-alpha1~215 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=1cd2821c19b2b95e39d5fc2f451a035585a40fa5;p=krb5.git Make gss-krb5 use cache collection For default credentials, defer ccache resolution until we need the information. If this happens in init_sec_context when we have the target principal in hand, use krb5_cc_select() to pick a cache. If the target principal is not known, use the default cache. For credentials with a specified principal, use krb5_cc_cache_match() to find the cache. If no cache is found and a password is specified, create a new cache within the collection to hold the new credentials, if the default cache type supports a collection. ticket: 6958 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@25160 dc483132-0cff-0310-8789-dd5450dbe970 --- diff --git a/src/lib/gssapi/krb5/acquire_cred.c b/src/lib/gssapi/krb5/acquire_cred.c index 50c8ef4ad..c815b3590 100644 --- a/src/lib/gssapi/krb5/acquire_cred.c +++ b/src/lib/gssapi/krb5/acquire_cred.c @@ -368,12 +368,25 @@ prep_ccache(krb5_context context, krb5_gss_cred_id_rec *cred, krb5_error_code code; krb5_principal ccache_princ; krb5_data password_data = make_data(password->value, password->length); + krb5_boolean eq; + const char *cctype; + krb5_ccache newcache = NULL; /* Check the ccache principal or initialize a new cache. */ code = krb5_cc_get_principal(context, ccache, &ccache_princ); if (code == 0) { - if (!krb5_principal_compare(context, ccache_princ, desired_princ)) - return KG_CCACHE_NOMATCH; + eq = krb5_principal_compare(context, ccache_princ, desired_princ); + krb5_free_principal(context, ccache_princ); + if (!eq) { + cctype = krb5_cc_get_type(context, ccache); + if (krb5_cc_support_switch(context, cctype)) { + /* Make a new ccache within the collection. */ + code = krb5_cc_new_unique(context, cctype, NULL, &newcache); + if (code) + return code; + } else + return KG_CCACHE_NOMATCH; + } } else if (code == KRB5_FCC_NOFILE) { /* Cache file does not exist; create and initialize one. */ code = krb5_cc_initialize(context, ccache, desired_princ); @@ -396,7 +409,11 @@ prep_ccache(krb5_context context, krb5_gss_cred_id_rec *cred, if (code) return code; - cred->ccache = ccache; + if (newcache) { + krb5_cc_close(context, ccache); + cred->ccache = newcache; + } else + cred->ccache = ccache; return 0; } @@ -523,7 +540,13 @@ acquire_init_cred(krb5_context context, #elif defined(USE_LEASH) code = get_ccache_leash(context, desired_princ, &ccache); #else - code = 0; + code = krb5_cc_cache_match(context, desired_princ, &ccache); + if (code == KRB5_CC_NOTFOUND && password != GSS_C_NO_BUFFER) { + /* Grab the default ccache for now; if it's not empty, prep_ccache + * will create a new one of the default type or error out. */ + krb5_clear_error_message(context); + code = krb5_cc_default(context, &ccache); + } #endif } else code = 0; @@ -531,25 +554,26 @@ acquire_init_cred(krb5_context context, *minor_status = code; return GSS_S_CRED_UNAVAIL; } - if (ccache == NULL) { - code = krb5int_cc_default(context, &ccache); + + if (ccache != NULL) { + if (password != GSS_C_NO_BUFFER && desired_princ != NULL) + code = prep_ccache(context, cred, ccache, desired_princ, password); + else + code = scan_ccache(context, cred, ccache, desired_princ); if (code != 0) { + krb5_cc_close(context, ccache); *minor_status = code; return GSS_S_CRED_UNAVAIL; } + cred->ccache = ccache; } - if (password != GSS_C_NO_BUFFER && desired_princ != NULL) - code = prep_ccache(context, cred, ccache, desired_princ, password); - else - code = scan_ccache(context, cred, ccache, desired_princ); - if (code != 0) { - krb5_cc_close(context, ccache); - *minor_status = code; - return GSS_S_CRED_UNAVAIL; - } + /* + * If the caller specified no ccache and no desired principal, leave + * cred->ccache and cred->name NULL. They will be resolved later by + * kg_cred_resolve(), possibly using the target principal name. + */ - cred->ccache = ccache; *minor_status = 0; return GSS_S_COMPLETE; } @@ -692,6 +716,67 @@ error_out: return ret; } +/* + * Resolve the name and ccache for an initiator credential if it has not yet + * been done. If specified, use the target name to pick an appropriate ccache + * within the collection. Validates cred_handle and leaves it locked on + * success. + */ +OM_uint32 +kg_cred_resolve(OM_uint32 *minor_status, krb5_context context, + gss_cred_id_t cred_handle, gss_name_t target_name) +{ + OM_uint32 maj; + krb5_error_code code; + krb5_gss_cred_id_t cred = (krb5_gss_cred_id_t)cred_handle; + krb5_gss_name_t tname = (krb5_gss_name_t)target_name; + krb5_ccache ccache = NULL; + krb5_principal client_princ = NULL; + + *minor_status = 0; + + maj = krb5_gss_validate_cred_1(minor_status, cred_handle, context); + if (maj != 0) + return maj; + k5_mutex_assert_locked(&cred->lock); + + if (cred->ccache != NULL || cred->usage == GSS_C_ACCEPT) + return GSS_S_COMPLETE; + + /* Pick a credential cache. */ + if (tname != NULL) { + code = krb5_cc_select(context, tname->princ, &ccache, &client_princ); + if (code && code != KRB5_CC_NOTFOUND) + goto kerr; + } + if (ccache == NULL) { + /* + * Ideally we would get credentials for client_princ if it is set. At + * the moment, we just get the default ccache (obtaining credentials if + * the platform supports it) and check it against client_princ below. + */ + code = krb5int_cc_default(context, &ccache); + if (code) + goto kerr; + } + + code = scan_ccache(context, cred, ccache, client_princ); + if (code) { + krb5_cc_close(context, ccache); + goto kerr; + } + + krb5_free_principal(context, client_princ); + return GSS_S_COMPLETE; + +kerr: + krb5_free_principal(context, client_princ); + k5_mutex_unlock(&cred->lock); + save_error_info(code, context); + *minor_status = code; + return GSS_S_CRED_UNAVAIL; +} + OM_uint32 gss_krb5int_set_cred_rcache(OM_uint32 *minor_status, gss_cred_id_t *cred_handle, diff --git a/src/lib/gssapi/krb5/gssapiP_krb5.h b/src/lib/gssapi/krb5/gssapiP_krb5.h index f7aab88b8..08155e820 100644 --- a/src/lib/gssapi/krb5/gssapiP_krb5.h +++ b/src/lib/gssapi/krb5/gssapiP_krb5.h @@ -478,6 +478,10 @@ krb5_to_gss_cred(krb5_context context, krb5_creds *creds, krb5_gss_cred_id_t *out_cred); +OM_uint32 +kg_cred_resolve(OM_uint32 *minor_status, krb5_context context, + gss_cred_id_t cred_handle, gss_name_t target_name); + /** declarations of internal name mechanism functions **/ OM_uint32 KRB5_CALLCONV krb5_gss_acquire_cred diff --git a/src/lib/gssapi/krb5/iakerb.c b/src/lib/gssapi/krb5/iakerb.c index e0aede9fb..84a96ac1b 100644 --- a/src/lib/gssapi/krb5/iakerb.c +++ b/src/lib/gssapi/krb5/iakerb.c @@ -914,16 +914,7 @@ iakerb_gss_init_sec_context(OM_uint32 *minor_status, kname = (krb5_gss_name_t)target_name; - if (claimant_cred_handle != GSS_C_NO_CREDENTIAL) { - major_status = krb5_gss_validate_cred_1(minor_status, - claimant_cred_handle, - ctx->k5c); - if (GSS_ERROR(major_status)) - goto cleanup; - - cred_locked = TRUE; - kcred = (krb5_gss_cred_id_t)claimant_cred_handle; - } else { + if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) { major_status = krb5_gss_acquire_cred(minor_status, NULL, GSS_C_INDEFINITE, GSS_C_NULL_OID_SET, @@ -931,9 +922,16 @@ iakerb_gss_init_sec_context(OM_uint32 *minor_status, &defcred, NULL, NULL); if (GSS_ERROR(major_status)) goto cleanup; - kcred = (krb5_gss_cred_id_t)defcred; + claimant_cred_handle = defcred; } + major_status = kg_cred_resolve(minor_status, ctx->k5c, + claimant_cred_handle, target_name); + if (GSS_ERROR(major_status)) + goto cleanup; + cred_locked = TRUE; + kcred = (krb5_gss_cred_id_t)claimant_cred_handle; + major_status = GSS_S_FAILURE; if (initialContextToken) { diff --git a/src/lib/gssapi/krb5/init_sec_context.c b/src/lib/gssapi/krb5/init_sec_context.c index 0133bf084..d62822e2d 100644 --- a/src/lib/gssapi/krb5/init_sec_context.c +++ b/src/lib/gssapi/krb5/init_sec_context.c @@ -930,6 +930,7 @@ krb5_gss_init_sec_context_ext( krb5_gss_ctx_ext_t exts) { krb5_context context; + gss_cred_id_t defcred = GSS_C_NO_CREDENTIAL; krb5_gss_cred_id_t cred; krb5_error_code kerr; OM_uint32 major_status; @@ -961,30 +962,25 @@ krb5_gss_init_sec_context_ext( /* verify the credential, or use the default */ /*SUPPRESS 29*/ if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) { - major_status = kg_get_defcred(minor_status, (gss_cred_id_t *)&cred); + major_status = kg_get_defcred(minor_status, &defcred); if (major_status && GSS_ERROR(major_status)) { if (*context_handle == GSS_C_NO_CONTEXT) krb5_free_context(context); return(major_status); } - } else { - major_status = krb5_gss_validate_cred(minor_status, claimant_cred_handle); - if (GSS_ERROR(major_status)) { - save_error_info(*minor_status, context); - if (*context_handle == GSS_C_NO_CONTEXT) - krb5_free_context(context); - return(major_status); - } - cred = (krb5_gss_cred_id_t) claimant_cred_handle; + claimant_cred_handle = defcred; } - kerr = k5_mutex_lock(&cred->lock); - if (kerr) { - if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) - krb5_gss_release_cred(minor_status, (gss_cred_id_t *)&cred); - krb5_free_context(context); - *minor_status = kerr; - return GSS_S_FAILURE; + + major_status = kg_cred_resolve(minor_status, context, claimant_cred_handle, + target_name); + if (GSS_ERROR(major_status)) { + save_error_info(*minor_status, context); + krb5_gss_release_cred(&tmp_min_stat, &defcred); + if (*context_handle == GSS_C_NO_CONTEXT) + krb5_free_context(context); + return(major_status); } + cred = (krb5_gss_cred_id_t)claimant_cred_handle; /* verify the mech_type */ @@ -998,8 +994,7 @@ krb5_gss_init_sec_context_ext( mech_type = (gss_OID) gss_mech_iakerb; } else { k5_mutex_unlock(&cred->lock); - if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) - krb5_gss_release_cred(minor_status, (gss_cred_id_t *)&cred); + krb5_gss_release_cred(minor_status, &defcred); *minor_status = 0; if (*context_handle == GSS_C_NO_CONTEXT) krb5_free_context(context); @@ -1036,9 +1031,7 @@ krb5_gss_init_sec_context_ext( too. */ } - if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) - krb5_gss_release_cred(&tmp_min_stat, (gss_cred_id_t *)&cred); - + krb5_gss_release_cred(&tmp_min_stat, &defcred); return(major_status); } diff --git a/src/lib/gssapi/krb5/inq_cred.c b/src/lib/gssapi/krb5/inq_cred.c index cc24e7a90..f523a545c 100644 --- a/src/lib/gssapi/krb5/inq_cred.c +++ b/src/lib/gssapi/krb5/inq_cred.c @@ -83,14 +83,14 @@ krb5_gss_inquire_cred(minor_status, cred_handle, name, lifetime_ret, gss_OID_set *mechanisms; { krb5_context context; - krb5_gss_cred_id_t cred; + krb5_gss_cred_id_t defcred = GSS_C_NO_CREDENTIAL, cred; krb5_error_code code; krb5_timestamp now; krb5_deltat lifetime; krb5_gss_name_t ret_name; krb5_principal princ; gss_OID_set mechs; - OM_uint32 ret; + OM_uint32 major, tmpmin, ret; ret = GSS_S_FAILURE; ret_name = NULL; @@ -104,39 +104,31 @@ krb5_gss_inquire_cred(minor_status, cred_handle, name, lifetime_ret, if (name) *name = NULL; if (mechanisms) *mechanisms = NULL; + if ((code = krb5_timeofday(context, &now))) { + *minor_status = code; + ret = GSS_S_FAILURE; + goto fail; + } + /* check for default credential */ /*SUPPRESS 29*/ if (cred_handle == GSS_C_NO_CREDENTIAL) { - OM_uint32 major; - - if ((major = kg_get_defcred(minor_status, (gss_cred_id_t *)&cred)) && - GSS_ERROR(major)) { - krb5_free_context(context); - return(major); - } - } else { - OM_uint32 major; - - major = krb5_gss_validate_cred(minor_status, cred_handle); + major = kg_get_defcred(minor_status, &defcred); if (GSS_ERROR(major)) { krb5_free_context(context); return(major); } - cred = (krb5_gss_cred_id_t) cred_handle; + cred_handle = defcred; } - if ((code = krb5_timeofday(context, &now))) { - *minor_status = code; - ret = GSS_S_FAILURE; - goto fail; + major = krb5_gss_validate_cred(minor_status, cred_handle); + if (GSS_ERROR(major)) { + krb5_gss_release_cred(minor_status, &defcred); + krb5_free_context(context); + return(major); } + cred = (krb5_gss_cred_id_t)cred_handle; - code = k5_mutex_lock(&cred->lock); - if (code != 0) { - *minor_status = code; - ret = GSS_S_FAILURE; - goto fail; - } if (cred->tgt_expire > 0) { if ((lifetime = cred->tgt_expire - now) < 0) lifetime = 0; @@ -161,7 +153,6 @@ krb5_gss_inquire_cred(minor_status, cred_handle, name, lifetime_ret, code = 0; } if (code) { - k5_mutex_unlock(&cred->lock); *minor_status = code; save_error_info(*minor_status, context); ret = GSS_S_FAILURE; @@ -178,7 +169,6 @@ krb5_gss_inquire_cred(minor_status, cred_handle, name, lifetime_ret, GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status, gss_mech_krb5, &mechs))) { - k5_mutex_unlock(&cred->lock); if (ret_name) kg_release_name(context, &ret_name); /* *minor_status set above */ @@ -210,11 +200,8 @@ krb5_gss_inquire_cred(minor_status, cred_handle, name, lifetime_ret, *minor_status = 0; return((lifetime == 0)?GSS_S_CREDENTIALS_EXPIRED:GSS_S_COMPLETE); fail: - if (cred_handle == GSS_C_NO_CREDENTIAL) { - OM_uint32 tmp_min_stat; - - krb5_gss_release_cred(&tmp_min_stat, (gss_cred_id_t *)&cred); - } + k5_mutex_unlock(&cred->lock); + krb5_gss_release_cred(&tmpmin, &defcred); krb5_free_context(context); return ret; } diff --git a/src/lib/gssapi/krb5/s4u_gss_glue.c b/src/lib/gssapi/krb5/s4u_gss_glue.c index 70fbfbce6..4ac2ce301 100644 --- a/src/lib/gssapi/krb5/s4u_gss_glue.c +++ b/src/lib/gssapi/krb5/s4u_gss_glue.c @@ -144,9 +144,8 @@ krb5_gss_acquire_cred_impersonate_name(OM_uint32 *minor_status, return GSS_S_FAILURE; } - major_status = krb5_gss_validate_cred_1(minor_status, - impersonator_cred_handle, - context); + major_status = kg_cred_resolve(minor_status, context, + impersonator_cred_handle, NULL); if (GSS_ERROR(major_status)) { krb5_free_context(context); return major_status; diff --git a/src/tests/gssapi/Makefile.in b/src/tests/gssapi/Makefile.in index 2bf390106..5b6765b59 100644 --- a/src/tests/gssapi/Makefile.in +++ b/src/tests/gssapi/Makefile.in @@ -4,21 +4,24 @@ DEFINES = -DUSE_AUTOCONF_H PROG_LIBPATH=-L$(TOPLIBD) PROG_RPATH=$(KRB5_LIBDIR) -SRCS= $(srcdir)/t_accname.c $(srcdir)/t_imp_cred.c $(srcdir)/t_imp_name.c \ - $(srcdir)/t_s4u.c $(srcdir)/t_namingexts.c $(srcdir)/t_gssexts.c \ - $(srcdir)/t_saslname.c +SRCS= $(srcdir)/t_accname.c $(srcdir)/t_ccselect.c $(srcdir)/t_imp_cred.c \ + $(srcdir)/t_imp_name.c $(srcdir)/t_s4u.c $(srcdir)/t_namingexts.c \ + $(srcdir)/t_gssexts.c $(srcdir)/t_saslname.c -OBJS= t_accname.o t_imp_cred.o t_imp_name.o t_s4u.o t_namingexts.o \ - t_gssexts.o t_spnego.o t_saslname.o +OBJS= t_accname.o t_ccselect.o t_imp_cred.o t_imp_name.o t_s4u.o \ + t_namingexts.o t_gssexts.o t_spnego.o t_saslname.o -all:: t_accname t_imp_cred t_imp_name t_s4u t_namingexts t_gssexts t_spnego \ - t_saslname +all:: t_accname t_ccselect t_imp_cred t_imp_name t_s4u t_namingexts t_gssexts \ + t_spnego t_saslname -check-pytests:: t_accname t_imp_cred t_spnego +check-pytests:: t_accname t_ccselect t_imp_cred t_spnego $(RUNPYTEST) $(srcdir)/t_gssapi.py $(PYTESTFLAGS) + $(RUNPYTEST) $(srcdir)/t_ccselect.py $(PYTESTFLAGS) t_accname: t_accname.o $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS) $(CC_LINK) -o t_accname t_accname.o $(GSS_LIBS) $(KRB5_BASE_LIBS) +t_ccselect: t_ccselect.o $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS) + $(CC_LINK) -o t_ccselect t_ccselect.o $(GSS_LIBS) $(KRB5_BASE_LIBS) t_imp_cred: t_imp_cred.o $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS) $(CC_LINK) -o t_imp_cred t_imp_cred.o $(GSS_LIBS) $(KRB5_BASE_LIBS) t_imp_name: t_imp_name.o $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS) diff --git a/src/tests/gssapi/deps b/src/tests/gssapi/deps index 8838e240d..a87f6e0e0 100644 --- a/src/tests/gssapi/deps +++ b/src/tests/gssapi/deps @@ -5,6 +5,10 @@ $(OUTPRE)t_accname.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \ $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \ t_accname.c +$(OUTPRE)t_ccselect.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \ + $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \ + t_ccselect.c $(OUTPRE)t_imp_cred.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ $(BUILDTOP)/include/gssapi/gssapi_krb5.h $(BUILDTOP)/include/krb5/krb5.h \ diff --git a/src/tests/gssapi/t_ccselect.c b/src/tests/gssapi/t_ccselect.c new file mode 100644 index 000000000..8acf045ba --- /dev/null +++ b/src/tests/gssapi/t_ccselect.c @@ -0,0 +1,170 @@ +/* -*- mode: c; indent-tabs-mode: nil -*- */ +/* tests/gssapi/t_ccselect.c - Test program for GSSAPI cred selection */ +/* + * Copyright 2011 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include +#include +#include + +#include + +/* + * Test program for client credential selection, intended to be run from a + * Python test script. Performs a one-token + * gss_init_sec_context/gss_accept_sec_context exchange, optionally with a + * specified principal as the initiator name, a specified principal name as + * target name, the default acceptor cred. If the exchange is successful, + * prints the initiator name as seen by the acceptor. If any call is + * unsuccessful, displays an error message. Exits with status 0 if all + * operations are successful, or 1 if not. + * + * Usage: ./t_ccselect [targetprinc|gss:service@host] [initiatorprinc|-] + */ + +static void +display_status_1(const char *m, OM_uint32 code, int type) +{ + OM_uint32 maj_stat, min_stat; + gss_buffer_desc msg; + OM_uint32 msg_ctx; + + msg_ctx = 0; + while (1) { + maj_stat = gss_display_status(&min_stat, code, + type, GSS_C_NULL_OID, + &msg_ctx, &msg); + fprintf(stderr, "%s: %s\n", m, (char *)msg.value); + (void) gss_release_buffer(&min_stat, &msg); + + if (!msg_ctx) + break; + } +} + +static void +gsserr(const char *msg, OM_uint32 maj_stat, OM_uint32 min_stat) +{ + display_status_1(msg, maj_stat, GSS_C_GSS_CODE); + display_status_1(msg, min_stat, GSS_C_MECH_CODE); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + OM_uint32 minor, major; + gss_cred_id_t initiator_cred; + gss_buffer_desc buf; + gss_name_t target_name, initiator_name = GSS_C_NO_NAME; + gss_name_t real_initiator_name; + gss_buffer_desc token, tmp, namebuf; + gss_ctx_id_t initiator_context = GSS_C_NO_CONTEXT; + gss_ctx_id_t acceptor_context = GSS_C_NO_CONTEXT; + + if (argc < 2 || argc > 3) { + fprintf(stderr, "Usage: %s targetprinc [initiatorprinc|-]\n", argv[0]); + return 1; + } + + /* Import the target name. */ + if (strncmp(argv[1], "gss:", 4) == 0) { + /* Import as host-based service. */ + buf.value = argv[1] + 4; + buf.length = strlen((char *)buf.value); + major = gss_import_name(&minor, &buf, + (gss_OID)GSS_C_NT_HOSTBASED_SERVICE, + &target_name); + } else { + /* Import as krb5 principal name. */ + buf.value = argv[1]; + buf.length = strlen((char *)buf.value); + major = gss_import_name(&minor, &buf, + (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME, + &target_name); + } + if (GSS_ERROR(major)) + gsserr("gss_import_name(target_name)", major, minor); + + /* Import the initiator name as a krb5 principal and get creds, maybe. */ + if (argc >= 3) { + if (strcmp(argv[2], "-") != 0) { + buf.value = argv[2]; + buf.length = strlen((char *)buf.value); + major = gss_import_name(&minor, &buf, + (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME, + &initiator_name); + if (GSS_ERROR(major)) + gsserr("gss_import_name(initiator_name)", major, minor); + } + + /* Get acceptor cred. */ + major = gss_acquire_cred(&minor, initiator_name, GSS_C_INDEFINITE, + GSS_C_NO_OID_SET, GSS_C_INITIATE, + &initiator_cred, NULL, NULL); + if (GSS_ERROR(major)) + gsserr("gss_acquire_cred", major, minor); + } + + + /* Create krb5 initiator context and get the first token. */ + token.value = NULL; + token.length = 0; + major = gss_init_sec_context(&minor, initiator_cred, &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, NULL); + if (GSS_ERROR(major)) + gsserr("gss_init_sec_context", major, minor); + + /* Pass the token to gss_accept_sec_context. */ + tmp.value = NULL; + tmp.length = 0; + major = gss_accept_sec_context(&minor, &acceptor_context, + GSS_C_NO_CREDENTIAL, &token, + GSS_C_NO_CHANNEL_BINDINGS, + &real_initiator_name, NULL, &tmp, + NULL, NULL, NULL); + if (major != GSS_S_COMPLETE) + gsserr("gss_accept_sec_context", major, minor); + + namebuf.value = NULL; + namebuf.length = 0; + major = gss_display_name(&minor, real_initiator_name, &namebuf, NULL); + if (GSS_ERROR(major)) + gsserr("gss_display_name(initiator)", major, minor); + printf("%.*s\n", (int)namebuf.length, (char *)namebuf.value); + + (void)gss_release_name(&minor, &target_name); + (void)gss_release_name(&minor, &initiator_name); + (void)gss_release_name(&minor, &real_initiator_name); + (void)gss_release_cred(&minor, &initiator_cred); + (void)gss_delete_sec_context(&minor, &initiator_context, NULL); + (void)gss_delete_sec_context(&minor, &acceptor_context, NULL); + (void)gss_release_buffer(&minor, &token); + (void)gss_release_buffer(&minor, &tmp); + (void)gss_release_buffer(&minor, &namebuf); + return 0; +} diff --git a/src/tests/gssapi/t_ccselect.py b/src/tests/gssapi/t_ccselect.py new file mode 100644 index 000000000..2722873fc --- /dev/null +++ b/src/tests/gssapi/t_ccselect.py @@ -0,0 +1,122 @@ +# Copyright (C) 2011 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. + +#!/usr/bin/python +from k5test import * + +# Create two independent realms (no cross-realm TGTs). +r1 = K5Realm(start_kadmind=False, create_user=False) +r2 = K5Realm(start_kadmind=False, create_user=False, realm='KRBTEST2.COM', + testdir=os.path.join(r1.testdir, 'r2'), portbase=62000) + +# gsserver specifies the target as a GSS name. The resulting +# principal will have the host-based type, but the realm won't be +# known before the client cache is selected (since k5test realms have +# no domain-realm mapping by default). +gssserver = 'gss:host@' + hostname + +# refserver specifies the target as a principal in the referral realm. +# The principal won't be treated as a host principal by the +# .k5identity rules since it has unknown type. +refserver = 'host/' + hostname + '@' + +# Make each realm's keytab contain entries for both realm's servers. +#r1.run_as_client(['/bin/sh', '-c', '(echo rkt %s; echo wkt %s) | %s' % +# (r2.keytab, r1.keytab, ktutil)]) +#r1.run_as_client(['/bin/sh', '-c', '(echo rkt %s; echo wkt %s) | %s' % +# (r1.keytab, r2.keytab, ktutil)]) + +# Make a directory collection and use it for client commands in both realms. +ccdir = os.path.join(r1.testdir, 'cc') +ccname = 'DIR:' + ccdir +os.mkdir(ccdir) +r1.env_client['KRB5CCNAME'] = ccname +r2.env_client['KRB5CCNAME'] = ccname + +# Use .k5identity from testdir and not from the tester's homedir. +r1.env_client['HOME'] = r1.testdir +r2.env_client['HOME'] = r1.testdir + +# Create two users in r1 and one in r2. +alice='alice@KRBTEST.COM' +bob='bob@KRBTEST.COM' +zaphod='zaphod@KRBTEST2.COM' +r1.addprinc(alice, password('alice')) +r1.addprinc(bob, password('bob')) +r2.addprinc(zaphod, password('zaphod')) + +# Get tickets for one user in each realm (zaphod will be primary). +r1.kinit(alice, password('alice')) +r2.kinit(zaphod, password('zaphod')) + +# Check that we can find a cache for a specified client principal. +output = r1.run_as_client(['./t_ccselect', r1.host_princ, alice]) +if output != (alice + '\n'): + fail('alice not chosen when specified') +output = r2.run_as_client(['./t_ccselect', r2.host_princ, zaphod]) +if output != (zaphod + '\n'): + fail('zaphod not chosen when specified') + +# Check that we can guess a cache based on the service realm. +output = r1.run_as_client(['./t_ccselect', r1.host_princ]) +if output != (alice + '\n'): + fail('alice not chosen as default initiator cred for server in r1') +output = r1.run_as_client(['./t_ccselect', r1.host_princ, '-']) +if output != (alice + '\n'): + fail('alice not chosen as default initiator name for server in r1') +output = r2.run_as_client(['./t_ccselect', r2.host_princ]) +if output != (zaphod + '\n'): + fail('zaphod not chosen as default initiator cred for server in r1') +output = r2.run_as_client(['./t_ccselect', r2.host_princ, '-']) +if output != (zaphod + '\n'): + fail('zaphod not chosen as default initiator name for server in r1') + +# Check that primary cache is used if server realm is unknown. +output = r2.run_as_client(['./t_ccselect', gssserver]) +if output != (zaphod + '\n'): + fail('zaphod not chosen via primary cache for unknown server realm') +r1.run_as_client(['./t_ccselect', gssserver], expected_code=1) + +# Get a second cred in r1 (bob will be primary). +r1.kinit(bob, password('bob')) + +# Try some cache selections using .k5identity. +k5id = open(os.path.join(r1.testdir, '.k5identity'), 'w') +k5id.write('%s realm=%s\n' % (alice, r1.realm)) +k5id.write('%s service=ho*t host=%s\n' % (zaphod, hostname)) +k5id.write('noprinc service=bogus') +k5id.close() +output = r1.run_as_client(['./t_ccselect', r1.host_princ]) +if output != (alice + '\n'): + fail('alice not chosen via .k5identity realm line.') +output = r2.run_as_client(['./t_ccselect', gssserver]) +if output != (zaphod + '\n'): + fail('zaphod not chosen via .k5identity service/host line.') +output = r1.run_as_client(['./t_ccselect', refserver]) +if output != (bob + '\n'): + fail('bob not chosen via primary cache when no .k5identity line matches.') +output = r1.run_as_client(['./t_ccselect', 'gss:bogus@' + hostname], + expected_code=1) +if 'does not match desired' not in output: + fail('Expected error not seen when k5identity selects bad principal.') + +success('GSSAPI credential selection tests.')