Path: blob/main/crypto/krb5/src/clients/ksu/main.c
34889 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/*2* Copyright (c) 1994 by the University of Southern California3*4* EXPORT OF THIS SOFTWARE from the United States of America may5* require a specific license from the United States Government.6* It is the responsibility of any person or organization contemplating7* export to obtain such a license before exporting.8*9* WITHIN THAT CONSTRAINT, permission to copy, modify, and distribute10* this software and its documentation in source and binary forms is11* hereby granted, provided that any documentation or other materials12* related to such distribution or use acknowledge that the software13* was developed by the University of Southern California.14*15* DISCLAIMER OF WARRANTY. THIS SOFTWARE IS PROVIDED "AS IS". The16* University of Southern California MAKES NO REPRESENTATIONS OR17* WARRANTIES, EXPRESS OR IMPLIED. By way of example, but not18* limitation, the University of Southern California MAKES NO19* REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY20* PARTICULAR PURPOSE. The University of Southern21* California shall not be held liable for any liability nor for any22* direct, indirect, or consequential damages with respect to any23* claim by the user or distributor of the ksu software.24*25* KSU was written by: Ari Medvinsky, [email protected]26*/2728#include "ksu.h"29#include "adm_proto.h"30#include <sys/types.h>31#include <sys/wait.h>32#include <signal.h>33#include <grp.h>3435/* globals */36char * prog_name;37int auth_debug =0;38char k5login_path[MAXPATHLEN];39char k5users_path[MAXPATHLEN];40char * gb_err = NULL;41int quiet = 0;42/***********/4344#define KS_TEMPORARY_CACHE "MEMORY:_ksu"45#define KS_TEMPORARY_PRINC "_ksu/_ksu@_ksu"46#define _DEF_CSH "/bin/csh"47static int set_env_var (char *, char *);48static void sweep_up (krb5_context, krb5_ccache);49static char * ontty (void);50static krb5_error_code init_ksu_context(krb5_context *);51static krb5_error_code set_ccname_env(krb5_context, krb5_ccache);52static void print_status( const char *fmt, ...)53#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7)54__attribute__ ((__format__ (__printf__, 1, 2)))55#endif56;57static krb5_error_code resolve_target_cache(krb5_context ksu_context,58krb5_principal princ,59krb5_ccache *ccache_out,60krb5_boolean *ccache_reused);6162/* Note -e and -a options are mutually exclusive */63/* insure the proper specification of target user as well as catching64ill specified arguments to commands */6566void67usage(void)68{69fprintf(stderr,70_("Usage: %s [target user] [-n principal] [-c source cachename] "71"[-k] [-r time] [-p|-P] [-f|-F] [-l lifetime] [-zZ] [-q] "72"[-e command [args... ] ] [-a [args... ] ]\n"), prog_name);73}7475/* for Ultrix and friends ... */76#ifndef MAXHOSTNAMELEN77#define MAXHOSTNAMELEN 6478#endif7980/* These are file static so sweep_up can get to them*/81static uid_t source_uid, target_uid;8283int84main(int argc, char ** argv)85{86int hp =0;87int some_rest_copy = 0;88int all_rest_copy = 0;89char *localhostname = NULL;90krb5_get_init_creds_opt *options = NULL;91int option=0;92int statusp=0;93krb5_error_code retval = 0;94krb5_principal client = NULL, tmp_princ = NULL;95krb5_ccache cc_tmp = NULL, cc_target = NULL;96krb5_context ksu_context;97char * cc_target_tag = NULL;98char * target_user = NULL;99char * source_user;100101krb5_ccache cc_source = NULL;102const char * cc_source_tag = NULL;103char * cmd = NULL, * exec_cmd = NULL;104int errflg = 0;105krb5_boolean auth_val;106krb5_boolean authorization_val = FALSE;107int path_passwd = 0;108int done =0,i,j;109uid_t ruid = getuid ();110struct passwd *pwd=NULL, *target_pwd ;111char * shell;112char ** params;113int keep_target_cache = 0;114int child_pid, child_pgrp, ret_pid;115int pargc;116char ** pargv;117krb5_boolean stored = FALSE, cc_reused = FALSE, given_princ = FALSE;118krb5_boolean zero_password;119krb5_boolean restrict_creds;120krb5_deltat lifetime, rlife;121122if (argc == 0)123exit(1);124125params = (char **) xcalloc (2, sizeof (char *));126params[1] = NULL;127128unsetenv ("KRB5_CONFIG");129130retval = init_ksu_context(&ksu_context);131if (retval) {132com_err(argv[0], retval, _("while initializing krb5"));133exit(1);134}135136retval = krb5_get_init_creds_opt_alloc(ksu_context, &options);137if (retval) {138com_err(argv[0], retval, _("while initializing krb5"));139exit(1);140}141142if (strrchr(argv[0], '/'))143argv[0] = strrchr(argv[0], '/')+1;144prog_name = argv[0];145if (strlen (prog_name) > 50) {146/* this many chars *after* last / ?? */147com_err(prog_name, 0,148_("program name too long - quitting to avoid triggering "149"system logging bugs"));150exit (1);151}152153154#ifndef LOG_NDELAY155#define LOG_NDELAY 0156#endif157158#ifndef LOG_AUTH /* 4.2 syslog */159openlog(prog_name, LOG_PID|LOG_NDELAY);160#else161openlog(prog_name, LOG_PID | LOG_NDELAY, LOG_AUTH);162#endif /* 4.2 syslog */163164165if (( argc == 1) || (argv[1][0] == '-')){166target_user = xstrdup("root");167pargc = argc;168pargv = argv;169} else {170target_user = xstrdup(argv[1]);171pargc = argc -1;172173if ((pargv =(char **) calloc(pargc +1,sizeof(char *)))==NULL){174com_err(prog_name, errno, _("while allocating memory"));175exit(1);176}177178pargv[pargc] = NULL;179pargv[0] = argv[0];180181for(i =1; i< pargc; i ++){182pargv[i] = argv[i + 1];183}184}185186if (krb5_seteuid (ruid)) {187com_err (prog_name, errno, _("while setting euid to source user"));188exit (1);189}190while (!done &&191(option = getopt(pargc, pargv,"n:c:r:a:zZDfFpPkql:e:")) != -1) {192switch (option) {193case 'r':194if (strlen (optarg) >= 14)195optarg = "bad-time";196retval = krb5_string_to_deltat(optarg, &rlife);197if (retval != 0 || rlife == 0) {198fprintf(stderr, _("Bad lifetime value (%s hours?)\n"), optarg);199errflg++;200}201krb5_get_init_creds_opt_set_renew_life(options, rlife);202break;203case 'a':204/* when integrating this remember to pass in pargc, pargv and205take care of params argument */206optind --;207if (auth_debug){printf("Before get_params optind=%d\n", optind);}208209if ((retval = get_params( & optind, pargc, pargv, ¶ms))){210com_err(prog_name, retval, _("when gathering parameters"));211errflg++;212}213if(auth_debug){ printf("After get_params optind=%d\n", optind);}214done = 1;215break;216case 'p':217krb5_get_init_creds_opt_set_proxiable(options, 1);218break;219case 'P':220krb5_get_init_creds_opt_set_proxiable(options, 0);221break;222case 'f':223krb5_get_init_creds_opt_set_forwardable(options, 1);224break;225case 'F':226krb5_get_init_creds_opt_set_forwardable(options, 0);227break;228case 'k':229keep_target_cache =1;230break;231case 'q':232quiet =1;233break;234case 'l':235if (strlen (optarg) >= 14)236optarg = "bad-time";237retval = krb5_string_to_deltat(optarg, &lifetime);238if (retval != 0 || lifetime == 0) {239fprintf(stderr, _("Bad lifetime value (%s hours?)\n"), optarg);240errflg++;241}242krb5_get_init_creds_opt_set_tkt_life(options, lifetime);243break;244case 'n':245if ((retval = krb5_parse_name(ksu_context, optarg, &client))){246com_err(prog_name, retval, _("when parsing name %s"), optarg);247errflg++;248}249given_princ = TRUE;250break;251#ifdef DEBUG252case 'D':253auth_debug = 1;254break;255#endif256case 'z':257some_rest_copy = 1;258if(all_rest_copy) {259fprintf(stderr,260_("-z option is mutually exclusive with -Z.\n"));261errflg++;262}263break;264case 'Z':265all_rest_copy = 1;266if(some_rest_copy) {267fprintf(stderr,268_("-Z option is mutually exclusive with -z.\n"));269errflg++;270}271break;272case 'c':273if (cc_source_tag == NULL) {274cc_source_tag = xstrdup(optarg);275if (!ks_ccache_name_is_initialized(ksu_context,276cc_source_tag)) {277com_err(prog_name, errno,278_("while looking for credentials cache %s"),279cc_source_tag);280exit(1);281}282} else {283fprintf(stderr, _("Only one -c option allowed\n"));284errflg++;285}286break;287case 'e':288cmd = xstrdup(optarg);289if(auth_debug){printf("Before get_params optind=%d\n", optind);}290if ((retval = get_params( & optind, pargc, pargv, ¶ms))){291com_err(prog_name, retval, _("when gathering parameters"));292errflg++;293}294if(auth_debug){printf("After get_params optind=%d\n", optind);}295done = 1;296297if (auth_debug){298fprintf(stderr,"Command to be executed: %s\n", cmd);299}300break;301case '?':302default:303errflg++;304break;305}306}307308if (errflg) {309usage();310exit(2);311}312313if (optind != pargc ){314usage();315exit(2);316}317318if (auth_debug){319for(j=1; params[j] != NULL; j++){320fprintf (stderr,"params[%d]= %s\n", j,params[j]);321}322}323324/***********************************/325source_user = getlogin(); /*checks for the the login name in /etc/utmp*/326327/* verify that that the user exists and get his passwd structure */328329if (source_user == NULL ||(pwd = getpwnam(source_user)) == NULL ||330pwd->pw_uid != ruid){331pwd = getpwuid(ruid);332}333334if (pwd == NULL) {335fprintf(stderr, _("ksu: who are you?\n"));336exit(1);337}338if (pwd->pw_uid != ruid) {339fprintf (stderr, _("Your uid doesn't match your passwd entry?!\n"));340exit (1);341}342/* Okay, now we have *some* passwd entry that matches the343current real uid. */344345/* allocate space and copy the usernamane there */346source_user = xstrdup(pwd->pw_name);347source_uid = pwd->pw_uid;348349if (!strcmp(SOURCE_USER_LOGIN, target_user)){350target_user = xstrdup (source_user);351}352353if ((target_pwd = getpwnam(target_user)) == NULL){354fprintf(stderr, _("ksu: unknown login %s\n"), target_user);355exit(1);356}357target_uid = target_pwd->pw_uid;358359init_auth_names(target_pwd->pw_dir);360361/***********************************/362363if (cc_source_tag == NULL){364cc_source_tag = krb5_cc_default_name(ksu_context);365if (cc_source_tag == NULL) {366fprintf(stderr, _("ksu: failed to get default ccache name\n"));367exit(1);368}369}370371/* get a handle for the cache */372if ((retval = krb5_cc_resolve(ksu_context, cc_source_tag, &cc_source))){373com_err(prog_name, retval, _("while getting source cache"));374exit(1);375}376377if ((retval = get_best_princ_for_target(ksu_context, source_uid,378target_uid, source_user,379target_user, cc_source,380options, cmd, localhostname,381&client, &hp))){382com_err(prog_name,retval, _("while selecting the best principal"));383exit(1);384}385386/* We may be running as either source or target, depending on387what happened; become source.*/388if ( geteuid() != source_uid) {389if (krb5_seteuid(0) || krb5_seteuid(source_uid) ) {390com_err(prog_name, errno, _("while returning to source uid after "391"finding best principal"));392exit(1);393}394}395396if (auth_debug){397if (hp){398fprintf(stderr,399"GET_best_princ_for_target result: NOT AUTHORIZED\n");400}else{401fprintf(stderr,402"GET_best_princ_for_target result-best principal ");403plain_dump_principal (ksu_context, client);404fprintf(stderr,"\n");405}406}407408if (hp){409if (gb_err) fprintf(stderr, "%s", gb_err);410fprintf(stderr, _("account %s: authorization failed\n"), target_user);411412if (cmd != NULL) {413syslog(LOG_WARNING,414"Account %s: authorization for %s for execution of %s failed",415target_user, source_user, cmd);416} else {417syslog(LOG_WARNING, "Account %s: authorization of %s failed",418target_user, source_user);419}420421exit(1);422}423424if (auth_debug)425fprintf(stderr, " source cache = %s\n", cc_source_tag);426427/*428* After proper authentication and authorization, populate a cache for the429* target user.430*/431432/*433* We read the set of creds we want to copy from the source ccache as the434* source uid, become root for authentication, and then become the target435* user to handle authorization and creating the target user's cache.436*/437438/* if root ksu's to a regular user, then439then only the credentials for that particular user440should be copied */441442restrict_creds = (source_uid == 0) && (target_uid != 0);443retval = krb5_parse_name(ksu_context, KS_TEMPORARY_PRINC, &tmp_princ);444if (retval) {445com_err(prog_name, retval, _("while parsing temporary name"));446exit(1);447}448retval = krb5_cc_resolve(ksu_context, KS_TEMPORARY_CACHE, &cc_tmp);449if (retval) {450com_err(prog_name, retval, _("while creating temporary cache"));451exit(1);452}453retval = krb5_ccache_copy(ksu_context, cc_source, tmp_princ, cc_tmp,454restrict_creds, client, &stored);455if (retval) {456com_err(prog_name, retval, _("while copying cache %s to %s"),457krb5_cc_get_name(ksu_context, cc_source), KS_TEMPORARY_CACHE);458exit(1);459}460krb5_cc_close(ksu_context, cc_source);461462krb5_get_init_creds_opt_set_out_ccache(ksu_context, options, cc_tmp);463464/* Become root for authentication*/465466if (krb5_seteuid(0)) {467com_err(prog_name, errno, _("while reclaiming root uid"));468exit(1);469}470471if ((source_uid == 0) || (target_uid == source_uid)){472#ifdef GET_TGT_VIA_PASSWD473if (!all_rest_copy && given_princ && client != NULL && !stored) {474fprintf(stderr, _("WARNING: Your password may be exposed if you "475"enter it here and are logged\n"));476fprintf(stderr, _(" in remotely using an unsecure "477"(non-encrypted) channel.\n"));478if (ksu_get_tgt_via_passwd(ksu_context, client, options,479&zero_password, NULL) == FALSE) {480481if (zero_password == FALSE){482fprintf(stderr, _("Goodbye\n"));483exit(1);484}485486fprintf(stderr, _("Could not get a tgt for "));487plain_dump_principal (ksu_context, client);488fprintf(stderr, "\n");489490}491stored = TRUE;492}493#endif /* GET_TGT_VIA_PASSWD */494}495496/* if the user is root or same uid then authentication is not necessary,497root gets in automatically */498499if (source_uid && (source_uid != target_uid)) {500char * client_name;501502auth_val = krb5_auth_check(ksu_context, client, localhostname,503options, target_user, cc_tmp,504&path_passwd, target_uid);505506/* if Kerberos authentication failed then exit */507if (auth_val ==FALSE){508fprintf(stderr, _("Authentication failed.\n"));509syslog(LOG_WARNING, "'%s %s' authentication failed for %s%s",510prog_name,target_user,source_user,ontty());511exit(1);512}513stored = TRUE;514515if ((retval = krb5_unparse_name(ksu_context, client, &client_name))) {516com_err(prog_name, retval, _("When unparsing name"));517exit(1);518}519520print_status(_("Authenticated %s\n"), client_name);521syslog(LOG_NOTICE,"'%s %s' authenticated %s for %s%s",522prog_name,target_user,client_name,523source_user,ontty());524525/* Run authorization as target.*/526if (krb5_seteuid(target_uid)) {527com_err(prog_name, errno, _("while switching to target for "528"authorization check"));529exit(1);530}531532if ((retval = krb5_authorization(ksu_context, client,target_user,533cmd, &authorization_val, &exec_cmd))){534com_err(prog_name,retval, _("while checking authorization"));535krb5_seteuid(0); /*So we have some chance of sweeping up*/536exit(1);537}538539if (krb5_seteuid(0)) {540com_err(prog_name, errno, _("while switching back from target "541"after authorization check"));542exit(1);543}544if (authorization_val == TRUE){545546if (cmd) {547print_status(_("Account %s: authorization for %s for "548"execution of\n"), target_user, client_name);549print_status(_(" %s successful\n"), exec_cmd);550syslog(LOG_NOTICE,551"Account %s: authorization for %s for execution of %s successful",552target_user, client_name, exec_cmd);553554}else{555print_status(_("Account %s: authorization for %s "556"successful\n"), target_user, client_name);557syslog(LOG_NOTICE,558"Account %s: authorization for %s successful",559target_user, client_name);560}561}else {562if (cmd){563if (exec_cmd){ /* was used to pass back the error msg */564fprintf(stderr, "%s", exec_cmd );565syslog(LOG_WARNING, "%s",exec_cmd);566}567fprintf(stderr, _("Account %s: authorization for %s for "568"execution of %s failed\n"),569target_user, client_name, cmd );570syslog(LOG_WARNING,571"Account %s: authorization for %s for execution of %s failed",572target_user, client_name, cmd );573574}else{575fprintf(stderr, _("Account %s: authorization of %s failed\n"),576target_user, client_name);577syslog(LOG_WARNING,578"Account %s: authorization of %s failed",579target_user, client_name);580581}582583exit(1);584}585}586587if( some_rest_copy){588retval = krb5_ccache_filter(ksu_context, cc_tmp, client);589if (retval) {590com_err(prog_name,retval, _("while calling cc_filter"));591exit(1);592}593}594595if (all_rest_copy){596retval = krb5_cc_initialize(ksu_context, cc_tmp, tmp_princ);597if (retval) {598com_err(prog_name, retval, _("while erasing target cache"));599exit(1);600}601stored = FALSE;602}603604/* get the shell of the user, this will be the shell used by su */605target_pwd = getpwnam(target_user);606607if (target_pwd->pw_shell)608shell = xstrdup(target_pwd->pw_shell);609else {610shell = _DEF_CSH; /* default is cshell */611}612613#ifdef HAVE_GETUSERSHELL614615/* insist that the target login uses a standard shell (root is omitted) */616617if (!standard_shell(target_pwd->pw_shell) && source_uid) {618fprintf(stderr, _("ksu: permission denied (shell).\n"));619exit(1);620}621#endif /* HAVE_GETUSERSHELL */622623if (target_pwd->pw_uid){624625if(set_env_var("USER", target_pwd->pw_name)){626fprintf(stderr,627_("ksu: couldn't set environment variable USER\n"));628exit(1);629}630}631632if(set_env_var( "HOME", target_pwd->pw_dir)){633fprintf(stderr, _("ksu: couldn't set environment variable HOME\n"));634exit(1);635}636637if(set_env_var( "SHELL", shell)){638fprintf(stderr, _("ksu: couldn't set environment variable SHELL\n"));639exit(1);640}641642/* set permissions */643if (setgid(target_pwd->pw_gid) < 0) {644perror("ksu: setgid");645exit(1);646}647648if (initgroups(target_user, target_pwd->pw_gid)) {649fprintf(stderr, _("ksu: initgroups failed.\n"));650exit(1);651}652653if ( ! strcmp(target_user, source_user)){654print_status(_("Leaving uid as %s (%ld)\n"),655target_user, (long) target_pwd->pw_uid);656}else{657print_status(_("Changing uid to %s (%ld)\n"),658target_user, (long) target_pwd->pw_uid);659}660661#ifdef HAVE_SETLUID662/*663* If we're on a system which keeps track of login uids, then664* set the login uid. If this fails this opens up a problem on DEC OSF665* with C2 enabled.666*/667if (setluid((uid_t) pwd->pw_uid) < 0) {668perror("setluid");669exit(1);670}671#endif /* HAVE_SETLUID */672673if (setuid(target_pwd->pw_uid) < 0) {674perror("ksu: setuid");675exit(1);676}677678retval = resolve_target_cache(ksu_context, client, &cc_target, &cc_reused);679if (retval)680exit(1);681retval = krb5_cc_get_full_name(ksu_context, cc_target, &cc_target_tag);682if (retval) {683com_err(prog_name, retval, _("while getting name of target ccache"));684sweep_up(ksu_context, cc_target);685exit(1);686}687if (auth_debug)688fprintf(stderr, " target cache = %s\n", cc_target_tag);689if (cc_reused)690keep_target_cache = TRUE;691692if (stored) {693retval = krb5_ccache_copy(ksu_context, cc_tmp, client, cc_target,694FALSE, client, &stored);695if (retval) {696com_err(prog_name, retval, _("while copying cache %s to %s"),697KS_TEMPORARY_CACHE, cc_target_tag);698exit(1);699}700701if (!ks_ccache_is_initialized(ksu_context, cc_target)) {702com_err(prog_name, errno,703_("%s does not have correct permissions for %s, "704"%s aborted"), target_user, cc_target_tag, prog_name);705exit(1);706}707}708709krb5_free_string(ksu_context, cc_target_tag);710711/* Set the cc env name to target. */712retval = set_ccname_env(ksu_context, cc_target);713if (retval != 0) {714sweep_up(ksu_context, cc_target);715exit(1);716}717718if (cmd){719if ((source_uid == 0) || (source_uid == target_uid )){720exec_cmd = cmd;721}722723if( !exec_cmd){724fprintf(stderr, _("Internal error: command %s did not get "725"resolved\n"), cmd);726exit(1);727}728729params[0] = exec_cmd;730}731else{732params[0] = shell;733}734735if (auth_debug){736fprintf(stderr, "program to be execed %s\n",params[0]);737}738739if( keep_target_cache ) {740execv(params[0], params);741com_err(prog_name, errno, _("while trying to execv %s"), params[0]);742sweep_up(ksu_context, cc_target);743exit(1);744}else{745statusp = 1;746switch ((child_pid = fork())) {747default:748if (auth_debug){749printf(" The child pid is %ld\n", (long) child_pid);750printf(" The parent pid is %ld\n", (long) getpid());751}752while ((ret_pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) {753if (WIFSTOPPED(statusp)) {754child_pgrp = tcgetpgrp(1);755kill(getpid(), SIGSTOP);756tcsetpgrp(1, child_pgrp);757kill(child_pid, SIGCONT);758statusp = 1;759continue;760}761break;762}763if (auth_debug){764printf("The exit status of the child is %d\n", statusp);765}766if (ret_pid == -1) {767com_err(prog_name, errno, _("while calling waitpid"));768}769sweep_up(ksu_context, cc_target);770exit (WIFEXITED(statusp) ? WEXITSTATUS(statusp) : 1);771case -1:772com_err(prog_name, errno, _("while trying to fork."));773sweep_up(ksu_context, cc_target);774exit (1);775case 0:776execv(params[0], params);777com_err(prog_name, errno, _("while trying to execv %s"),778params[0]);779exit (1);780}781}782}783784static krb5_error_code785init_ksu_context(krb5_context *context_out)786{787krb5_error_code retval;788const char *env_ccname;789krb5_context context;790791*context_out = NULL;792793retval = krb5_init_secure_context(&context);794if (retval)795return retval;796797/* We want to obey KRB5CCNAME in this context even though this is a setuid798* program. (It will only be used when operating as the real uid.) */799env_ccname = getenv(KRB5_ENV_CCNAME);800if (env_ccname != NULL) {801retval = krb5_cc_set_default_name(context, env_ccname);802if (retval) {803krb5_free_context(context);804return retval;805}806}807808*context_out = context;809return 0;810}811812/* Set KRB5CCNAME in the environment to point to ccache. Print an error813* message on failure. */814static krb5_error_code815set_ccname_env(krb5_context ksu_context, krb5_ccache ccache)816{817krb5_error_code retval;818char *ccname;819820retval = krb5_cc_get_full_name(ksu_context, ccache, &ccname);821if (retval) {822com_err(prog_name, retval, _("while reading cache name from ccache"));823return retval;824}825if (set_env_var(KRB5_ENV_CCNAME, ccname)) {826retval = errno;827fprintf(stderr,828_("ksu: couldn't set environment variable %s\n"),829KRB5_ENV_CCNAME);830}831krb5_free_string(ksu_context, ccname);832return retval;833}834835/*836* Get the configured default ccache name. Unset KRB5CCNAME and force a837* recomputation so we don't use values for the source user. Print an error838* message on failure.839*/840static krb5_error_code841get_configured_defccname(krb5_context context, char **target_out)842{843krb5_error_code retval;844const char *defname;845char *target = NULL;846847*target_out = NULL;848849unsetenv(KRB5_ENV_CCNAME);850851/* Make sure we don't have a cached value for a different uid. */852retval = krb5_cc_set_default_name(context, NULL);853if (retval != 0) {854com_err(prog_name, retval, _("while resetting target ccache name"));855return retval;856}857858defname = krb5_cc_default_name(context);859if (defname != NULL) {860if (strchr(defname, ':') != NULL) {861target = strdup(defname);862} else {863if (asprintf(&target, "FILE:%s", defname) < 0)864target = NULL;865}866}867if (target == NULL) {868com_err(prog_name, ENOMEM, _("while determining target ccache name"));869return ENOMEM;870}871*target_out = target;872return 0;873}874875/* Determine where the target user's creds should be stored. Print an error876* message on failure. */877static krb5_error_code878resolve_target_cache(krb5_context context, krb5_principal princ,879krb5_ccache *ccache_out, krb5_boolean *ccache_reused)880{881krb5_error_code retval;882krb5_boolean switchable, reused = FALSE;883krb5_ccache ccache = NULL;884char *sep, *ccname = NULL, *sym = NULL, *target;885886*ccache_out = NULL;887*ccache_reused = FALSE;888889retval = get_configured_defccname(context, &target);890if (retval != 0)891return retval;892893/* Check if the configured default name uses a switchable type. */894sep = strchr(target, ':');895*sep = '\0';896switchable = krb5_cc_support_switch(context, target);897*sep = ':';898899if (!switchable) {900/* Try to avoid destroying an in-use target ccache by coming up with901* the name of a cache that doesn't exist yet. */902do {903free(ccname);904retval = gen_sym(context, &sym);905if (retval) {906com_err(prog_name, retval,907_("while generating part of the target ccache name"));908goto cleanup;909}910if (asprintf(&ccname, "%s.%s", target, sym) < 0) {911retval = ENOMEM;912free(sym);913com_err(prog_name, retval, _("while allocating memory for the "914"target ccache name"));915goto cleanup;916}917free(sym);918} while (ks_ccache_name_is_initialized(context, ccname));919retval = krb5_cc_resolve(context, ccname, &ccache);920free(ccname);921} else {922/* Look for a cache in the collection that we can reuse. */923retval = krb5_cc_cache_match(context, princ, &ccache);924if (retval == 0) {925reused = TRUE;926} else {927/* There isn't one, so create a new one. */928*sep = '\0';929retval = krb5_cc_new_unique(context, target, NULL, &ccache);930*sep = ':';931if (retval) {932com_err(prog_name, retval,933_("while creating new target ccache"));934goto cleanup;935}936retval = krb5_cc_initialize(context, ccache, princ);937if (retval) {938com_err(prog_name, retval,939_("while initializing target cache"));940goto cleanup;941}942}943}944945*ccache_out = ccache;946*ccache_reused = reused;947948cleanup:949free(target);950return retval;951}952953#ifdef HAVE_GETUSERSHELL954955int956standard_shell(char *sh)957{958char *cp;959960while ((cp = getusershell()) != NULL)961if (!strcmp(cp, sh))962return (1);963return (0);964}965966#endif /* HAVE_GETUSERSHELL */967968static char *969ontty(void)970{971char *p;972static char buf[MAXPATHLEN + 5];973int result;974975buf[0] = 0;976if ((p = ttyname(STDERR_FILENO))) {977result = snprintf(buf, sizeof(buf), " on %s", p);978if (SNPRINTF_OVERFLOW(result, sizeof(buf))) {979fprintf(stderr, _("terminal name %s too long\n"), p);980exit (1);981}982}983return (buf);984}985986static int987set_env_var(char *name, char *value)988{989char * env_var_buf;990991asprintf(&env_var_buf,"%s=%s",name, value);992return putenv(env_var_buf);993994}995996static void997sweep_up(krb5_context context, krb5_ccache cc)998{999krb5_error_code retval;10001001krb5_seteuid(0);1002if (krb5_seteuid(target_uid) < 0) {1003com_err(prog_name, errno,1004_("while changing to target uid for destroying ccache"));1005exit(1);1006}10071008if (ks_ccache_is_initialized(context, cc)) {1009if ((retval = krb5_cc_destroy(context, cc)))1010com_err(prog_name, retval, _("while destroying cache"));1011}1012}10131014/*****************************************************************1015get_params is to be called for the -a option or -e option to1016collect all params passed in for the shell or for1017cmd. An array is returned containing all params.1018optindex is incremented accordingly and the first1019element in the returned array is reserved for the1020name of the command to be executed or the name of the1021shell.1022*****************************************************************/10231024krb5_error_code1025get_params(int *optindex, int pargc, char **pargv, char ***params)1026{10271028int i,j;1029char ** ret_params;1030int size = pargc - *optindex + 2;10311032if ((ret_params = (char **) calloc(size, sizeof (char *)))== NULL ){1033return ENOMEM;1034}10351036for (i = *optindex, j=1; i < pargc; i++,j++){1037ret_params[j] = pargv[i];1038*optindex = *optindex + 1;1039}10401041ret_params[size-1] = NULL;1042*params = ret_params;1043return 0;1044}10451046static1047void print_status(const char *fmt, ...)1048{1049va_list ap;1050if (! quiet){1051va_start(ap, fmt);1052vfprintf(stderr, fmt, ap);1053va_end(ap);1054}1055}10561057krb5_error_code1058ksu_tgtname(krb5_context context, const krb5_data *server,1059const krb5_data *client, krb5_principal *tgtprinc)1060{1061return krb5_build_principal_ext(context, tgtprinc, client->length, client->data,1062KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,1063server->length, server->data,10640);1065}106610671068