From 3a456898af626dcab4e1ab0749ca2ccb9ad6162b Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Wed, 30 Oct 2013 21:47:14 -0400 Subject: [PATCH 4/7] Make ksu respect the default_ccache_name setting Move the logic for resolving and initializing a cache that we're copying creds into out of krb5_ccache_copy(), and let the caller deal with it. Add a helper functions to select/resolve an output ccache in the default location for the target user after we've switched to the target user's privileges. If the destination is a collection, take care not to change which subsidiary is its primary, and reuse a subsidiary cache if we can. If the destination is not a collection, append a unique value to its name to make a new ccache. [ghudson@mit.edu: some changes to variable names and comments; move responsibility for getting target ccache name from resolve_target_ccache to main] ticket: 7984 (new) --- src/clients/ksu/ccache.c | 35 +++------ src/clients/ksu/ksu.h | 6 +- src/clients/ksu/main.c | 181 ++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 157 insertions(+), 65 deletions(-) diff --git a/src/clients/ksu/ccache.c b/src/clients/ksu/ccache.c index d0fc389..4693bd4 100644 --- a/src/clients/ksu/ccache.c +++ b/src/clients/ksu/ccache.c @@ -46,59 +46,41 @@ void show_credential(); with k5 beta 3 release. */ -krb5_error_code krb5_ccache_copy (context, cc_def, cc_other_tag, - primary_principal, restrict_creds, - target_principal, cc_out, stored, target_uid) +krb5_error_code krb5_ccache_copy(context, cc_def, target_principal, cc_target, + restrict_creds, primary_principal, stored) /* IN */ krb5_context context; krb5_ccache cc_def; - char *cc_other_tag; - krb5_principal primary_principal; - krb5_boolean restrict_creds; krb5_principal target_principal; - uid_t target_uid; + krb5_ccache cc_target; + krb5_boolean restrict_creds; + krb5_principal primary_principal; /* OUT */ - krb5_ccache *cc_out; krb5_boolean *stored; { int i=0; - krb5_ccache * cc_other; krb5_error_code retval=0; krb5_creds ** cc_def_creds_arr = NULL; krb5_creds ** cc_other_creds_arr = NULL; - cc_other = (krb5_ccache *) xcalloc(1, sizeof (krb5_ccache)); - - if ((retval = krb5_cc_resolve(context, cc_other_tag, cc_other))){ - com_err(prog_name, retval, _("resolving ccache %s"), cc_other_tag); - return retval; - } - if (ks_ccache_is_initialized(context, cc_def)) { if((retval = krb5_get_nonexp_tkts(context,cc_def,&cc_def_creds_arr))){ return retval; } } - if (ks_ccache_name_is_initialized(context, cc_other_tag)) - return EINVAL; - - if (krb5_seteuid(0)||krb5_seteuid(target_uid)) { - return errno; - } - - retval = krb5_cc_initialize(context, *cc_other, target_principal); + retval = krb5_cc_initialize(context, cc_target, target_principal); if (retval) return retval; if (restrict_creds) { - retval = krb5_store_some_creds(context, *cc_other, cc_def_creds_arr, + retval = krb5_store_some_creds(context, cc_target, cc_def_creds_arr, cc_other_creds_arr, primary_principal, stored); } else { *stored = krb5_find_princ_in_cred_list(context, cc_def_creds_arr, primary_principal); - retval = krb5_store_all_creds(context, *cc_other, cc_def_creds_arr, + retval = krb5_store_all_creds(context, cc_target, cc_def_creds_arr, cc_other_creds_arr); } @@ -118,7 +100,6 @@ krb5_error_code krb5_ccache_copy (context, cc_def, cc_other_tag, } } - *cc_out = *cc_other; return retval; } diff --git a/src/clients/ksu/ksu.h b/src/clients/ksu/ksu.h index 08bf01b..fbbf217 100644 --- a/src/clients/ksu/ksu.h +++ b/src/clients/ksu/ksu.h @@ -44,8 +44,6 @@ #define KRB5_DEFAULT_OPTIONS 0 #define KRB5_DEFAULT_TKT_LIFE 60*60*12 /* 12 hours */ -#define KRB5_SECONDARY_CACHE "FILE:/tmp/krb5cc_" - #define KRB5_LOGIN_NAME ".k5login" #define KRB5_USERS_NAME ".k5users" #define USE_DEFAULT_REALM_NAME "." @@ -106,8 +104,8 @@ extern krb5_error_code get_best_principal /* ccache.c */ extern krb5_error_code krb5_ccache_copy -(krb5_context, krb5_ccache, char *, krb5_principal, krb5_boolean, - krb5_principal, krb5_ccache *, krb5_boolean *, uid_t); +(krb5_context, krb5_ccache, krb5_principal, krb5_ccache, + krb5_boolean, krb5_principal, krb5_boolean *); extern krb5_error_code krb5_store_all_creds (krb5_context, krb5_ccache, krb5_creds **, krb5_creds **); diff --git a/src/clients/ksu/main.c b/src/clients/ksu/main.c index d1bb8ca..41a3bf8 100644 --- a/src/clients/ksu/main.c +++ b/src/clients/ksu/main.c @@ -54,6 +54,10 @@ static void print_status( const char *fmt, ...) __attribute__ ((__format__ (__printf__, 1, 2))) #endif ; +static krb5_error_code resolve_target_cache(krb5_context ksu_context, + krb5_principal princ, + krb5_ccache *ccache_out, + krb5_boolean *ccache_reused); /* Note -e and -a options are mutually exclusive */ /* insure the proper specification of target user as well as catching @@ -112,7 +116,7 @@ main (argc, argv) extern char * getpass(), *crypt(); int pargc; char ** pargv; - krb5_boolean stored = FALSE; + krb5_boolean stored = FALSE, cc_reused = FALSE; krb5_principal kdc_server; krb5_boolean zero_password; krb5_boolean restrict_creds; @@ -416,23 +420,8 @@ main (argc, argv) exit(1); } - /* - * Make sure that the new ticket file does not already exist. - * This is run as source_uid because it is reasonable to - * require the source user to have write to where the target - * cache will be created. - */ - cc_target_tag = (char *)xcalloc(KRB5_SEC_BUFFSIZE, sizeof(char)); - do { - snprintf(cc_target_tag, KRB5_SEC_BUFFSIZE, "%s%ld.%d", - KRB5_SECONDARY_CACHE, - (long)target_uid, gen_sym()); - } while (ks_ccache_name_is_initialized(ksu_context, cc_target_tag)); - - if (auth_debug){ + if (auth_debug) fprintf(stderr, " source cache = %s\n", cc_source_tag); - fprintf(stderr, " target cache = %s\n", cc_target_tag); - } /* * After proper authentication and authorization, populate a cache for the @@ -455,14 +444,19 @@ main (argc, argv) com_err(prog_name, retval, _("while parsing temporary name")); exit(1); } - retval = krb5_ccache_copy(ksu_context, cc_source, KS_TEMPORARY_CACHE, - client, restrict_creds, tmp_princ, &cc_tmp, - &stored, 0); + retval = krb5_cc_resolve(ksu_context, KS_TEMPORARY_CACHE, &cc_tmp); + if (retval) { + com_err(prog_name, retval, _("while creating temporary cache")); + exit(1); + } + retval = krb5_ccache_copy(ksu_context, cc_source, tmp_princ, cc_tmp, + restrict_creds, client, &stored); if (retval) { com_err(prog_name, retval, _("while copying cache %s to %s"), krb5_cc_get_name(ksu_context, cc_source), KS_TEMPORARY_CACHE); exit(1); } + krb5_cc_close(ksu_context, cc_source); /* Become root for authentication*/ @@ -686,23 +680,38 @@ main (argc, argv) exit(1); } - retval = krb5_ccache_copy(ksu_context, cc_tmp, cc_target_tag, - client, FALSE, client, &cc_target, &stored, - target_pwd->pw_uid); + retval = resolve_target_cache(ksu_context, client, &cc_target, &cc_reused); + if (retval) + exit(1); + retval = krb5_cc_get_full_name(ksu_context, cc_target, &cc_target_tag); if (retval) { - com_err(prog_name, retval, _("while copying cache %s to %s"), - KS_TEMPORARY_CACHE, cc_target_tag); + com_err(prog_name, retval, _("while getting name of target ccache")); + sweep_up(ksu_context, cc_target); exit(1); } + if (auth_debug) + fprintf(stderr, " target cache = %s\n", cc_target_tag); + if (cc_reused) + keep_target_cache = TRUE; - if (stored && !ks_ccache_is_initialized(ksu_context, cc_target)) { - com_err(prog_name, errno, - _("%s does not have correct permissions for %s, %s aborted"), - target_user, cc_target_tag, prog_name); - exit(1); + if (stored) { + retval = krb5_ccache_copy(ksu_context, cc_tmp, client, cc_target, + FALSE, client, &stored); + if (retval) { + com_err(prog_name, retval, _("while copying cache %s to %s"), + KS_TEMPORARY_CACHE, cc_target_tag); + exit(1); + } + + if (!ks_ccache_is_initialized(ksu_context, cc_target)) { + com_err(prog_name, errno, + _("%s does not have correct permissions for %s, " + "%s aborted"), target_user, cc_target_tag, prog_name); + exit(1); + } } - free(cc_target_tag); + krb5_free_string(ksu_context, cc_target_tag); /* Set the cc env name to target. */ retval = set_ccname_env(ksu_context, cc_target); @@ -711,9 +720,6 @@ main (argc, argv) exit(1); } - if ( cc_source) - krb5_cc_close(ksu_context, cc_source); - if (cmd){ if ((source_uid == 0) || (source_uid == target_uid )){ exec_cmd = cmd; @@ -803,6 +809,113 @@ set_ccname_env(krb5_context ksu_context, krb5_ccache ccache) return retval; } +/* + * Get the configured default ccache name. Unset KRB5CCNAME and force a + * recomputation so we don't use values for the source user. Print an error + * message on failure. + */ +static krb5_error_code +get_configured_defccname(krb5_context context, char **target_out) +{ + krb5_error_code retval; + const char *defname; + char *target; + + *target_out = NULL; + + if (unsetenv(KRB5_ENV_CCNAME) != 0) { + retval = errno; + com_err(prog_name, retval, _("while clearing the value of %s"), + KRB5_ENV_CCNAME); + return retval; + } + + /* Make sure we don't have a cached value for a different uid. */ + retval = krb5_cc_set_default_name(context, NULL); + if (retval != 0) { + com_err(prog_name, retval, _("while resetting target ccache name")); + return retval; + } + + defname = krb5_cc_default_name(context); + target = (defname == NULL) ? NULL : strdup(defname); + if (target == NULL) { + com_err(prog_name, ENOMEM, _("while determining target ccache name")); + return ENOMEM; + } + *target_out = target; + return 0; +} + +/* Determine where the target user's creds should be stored. Print an error + * message on failure. */ +static krb5_error_code +resolve_target_cache(krb5_context context, krb5_principal princ, + krb5_ccache *ccache_out, krb5_boolean *ccache_reused) +{ + krb5_error_code retval; + krb5_boolean switchable, reused = FALSE; + krb5_ccache ccache = NULL; + char *sep, *ccname = NULL, *target; + + *ccache_out = NULL; + *ccache_reused = FALSE; + + retval = get_configured_defccname(context, &target); + if (retval != 0) + return retval; + + /* Check if the configured default name uses a switchable type. */ + sep = strchr(target, ':'); + *sep = '\0'; + switchable = krb5_cc_support_switch(context, target); + *sep = ':'; + + if (!switchable) { + /* Try to avoid destroying an in-use target ccache by coming up with + * the name of a cache that doesn't exist yet. */ + do { + free(ccname); + if (asprintf(&ccname, "%s.%d", target, gen_sym()) < 0) { + retval = ENOMEM; + com_err(prog_name, ENOMEM, + _("while allocating memory for target ccache name")); + goto cleanup; + } + } while (ks_ccache_name_is_initialized(context, ccname)); + retval = krb5_cc_resolve(context, ccname, &ccache); + } else { + /* Look for a cache in the collection that we can reuse. */ + retval = krb5_cc_cache_match(context, princ, &ccache); + if (retval == 0) { + reused = TRUE; + } else { + /* There isn't one, so create a new one. */ + *sep = '\0'; + retval = krb5_cc_new_unique(context, target, NULL, &ccache); + *sep = ':'; + if (retval) { + com_err(prog_name, retval, + _("while creating new target ccache")); + goto cleanup; + } + retval = krb5_cc_initialize(context, ccache, princ); + if (retval) { + com_err(prog_name, retval, + _("while initializing target cache")); + goto cleanup; + } + } + } + + *ccache_out = ccache; + *ccache_reused = reused; + +cleanup: + free(target); + return retval; +} + #ifdef HAVE_GETUSERSHELL int standard_shell(sh) -- 2.0.4