xref: /freebsd/crypto/krb5/src/clients/ksu/main.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * Copyright (c) 1994 by the University of Southern California
4  *
5  * EXPORT OF THIS SOFTWARE from the United States of America may
6  *     require a specific license from the United States Government.
7  *     It is the responsibility of any person or organization contemplating
8  *     export to obtain such a license before exporting.
9  *
10  * WITHIN THAT CONSTRAINT, permission to copy, modify, and distribute
11  *     this software and its documentation in source and binary forms is
12  *     hereby granted, provided that any documentation or other materials
13  *     related to such distribution or use acknowledge that the software
14  *     was developed by the University of Southern California.
15  *
16  * DISCLAIMER OF WARRANTY.  THIS SOFTWARE IS PROVIDED "AS IS".  The
17  *     University of Southern California MAKES NO REPRESENTATIONS OR
18  *     WARRANTIES, EXPRESS OR IMPLIED.  By way of example, but not
19  *     limitation, the University of Southern California MAKES NO
20  *     REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
21  *     PARTICULAR PURPOSE. The University of Southern
22  *     California shall not be held liable for any liability nor for any
23  *     direct, indirect, or consequential damages with respect to any
24  *     claim by the user or distributor of the ksu software.
25  *
26  * KSU was written by:  Ari Medvinsky, ari@isi.edu
27  */
28 
29 #include "ksu.h"
30 #include "adm_proto.h"
31 #include <sys/types.h>
32 #include <sys/wait.h>
33 #include <signal.h>
34 #include <grp.h>
35 
36 /* globals */
37 char * prog_name;
38 int auth_debug =0;
39 char k5login_path[MAXPATHLEN];
40 char k5users_path[MAXPATHLEN];
41 char * gb_err = NULL;
42 int quiet = 0;
43 /***********/
44 
45 #define KS_TEMPORARY_CACHE "MEMORY:_ksu"
46 #define KS_TEMPORARY_PRINC "_ksu/_ksu@_ksu"
47 #define _DEF_CSH "/bin/csh"
48 static int set_env_var (char *, char *);
49 static void sweep_up (krb5_context, krb5_ccache);
50 static char * ontty (void);
51 static krb5_error_code init_ksu_context(krb5_context *);
52 static krb5_error_code set_ccname_env(krb5_context, krb5_ccache);
53 static void print_status( const char *fmt, ...)
54 #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7)
55     __attribute__ ((__format__ (__printf__, 1, 2)))
56 #endif
57     ;
58 static krb5_error_code resolve_target_cache(krb5_context ksu_context,
59                                             krb5_principal princ,
60                                             krb5_ccache *ccache_out,
61                                             krb5_boolean *ccache_reused);
62 
63 /* Note -e and -a options are mutually exclusive */
64 /* insure the proper specification of target user as well as catching
65    ill specified arguments to commands */
66 
67 void
usage(void)68 usage(void)
69 {
70     fprintf(stderr,
71             _("Usage: %s [target user] [-n principal] [-c source cachename] "
72               "[-k] [-r time] [-p|-P] [-f|-F] [-l lifetime] [-zZ] [-q] "
73               "[-e command [args... ] ] [-a [args... ] ]\n"), prog_name);
74 }
75 
76 /* for Ultrix and friends ... */
77 #ifndef MAXHOSTNAMELEN
78 #define MAXHOSTNAMELEN 64
79 #endif
80 
81 /* These are file static so sweep_up can get to them*/
82 static uid_t source_uid, target_uid;
83 
84 int
main(int argc,char ** argv)85 main(int argc, char ** argv)
86 {
87     int hp =0;
88     int some_rest_copy = 0;
89     int all_rest_copy = 0;
90     char *localhostname = NULL;
91     krb5_get_init_creds_opt *options = NULL;
92     int option=0;
93     int statusp=0;
94     krb5_error_code retval = 0;
95     krb5_principal client = NULL, tmp_princ = NULL;
96     krb5_ccache cc_tmp = NULL, cc_target = NULL;
97     krb5_context ksu_context;
98     char * cc_target_tag = NULL;
99     char * target_user = NULL;
100     char * source_user;
101 
102     krb5_ccache cc_source = NULL;
103     const char * cc_source_tag = NULL;
104     char * cmd = NULL, * exec_cmd = NULL;
105     int errflg = 0;
106     krb5_boolean auth_val;
107     krb5_boolean authorization_val = FALSE;
108     int path_passwd = 0;
109     int done =0,i,j;
110     uid_t ruid = getuid ();
111     struct passwd *pwd=NULL,  *target_pwd ;
112     char * shell;
113     char ** params;
114     int keep_target_cache = 0;
115     int child_pid, child_pgrp, ret_pid;
116     int pargc;
117     char ** pargv;
118     krb5_boolean stored = FALSE, cc_reused = FALSE, given_princ = FALSE;
119     krb5_boolean zero_password;
120     krb5_boolean restrict_creds;
121     krb5_deltat lifetime, rlife;
122 
123     if (argc == 0)
124         exit(1);
125 
126     params = (char **) xcalloc (2, sizeof (char *));
127     params[1] = NULL;
128 
129     unsetenv ("KRB5_CONFIG");
130 
131     retval = init_ksu_context(&ksu_context);
132     if (retval) {
133         com_err(argv[0], retval, _("while initializing krb5"));
134         exit(1);
135     }
136 
137     retval = krb5_get_init_creds_opt_alloc(ksu_context, &options);
138     if (retval) {
139         com_err(argv[0], retval, _("while initializing krb5"));
140         exit(1);
141     }
142 
143     if (strrchr(argv[0], '/'))
144         argv[0] = strrchr(argv[0], '/')+1;
145     prog_name = argv[0];
146     if (strlen (prog_name) > 50) {
147         /* this many chars *after* last / ?? */
148         com_err(prog_name, 0,
149                 _("program name too long - quitting to avoid triggering "
150                   "system logging bugs"));
151         exit (1);
152     }
153 
154 
155 #ifndef LOG_NDELAY
156 #define LOG_NDELAY 0
157 #endif
158 
159 #ifndef LOG_AUTH /* 4.2 syslog */
160     openlog(prog_name, LOG_PID|LOG_NDELAY);
161 #else
162     openlog(prog_name, LOG_PID | LOG_NDELAY, LOG_AUTH);
163 #endif /* 4.2 syslog */
164 
165 
166     if (( argc == 1) || (argv[1][0] == '-')){
167         target_user = xstrdup("root");
168         pargc = argc;
169         pargv = argv;
170     } else {
171         target_user = xstrdup(argv[1]);
172         pargc = argc -1;
173 
174         if ((pargv =(char **) calloc(pargc +1,sizeof(char *)))==NULL){
175             com_err(prog_name, errno, _("while allocating memory"));
176             exit(1);
177         }
178 
179         pargv[pargc] = NULL;
180         pargv[0] = argv[0];
181 
182         for(i =1; i< pargc; i ++){
183             pargv[i] = argv[i + 1];
184         }
185     }
186 
187     if (krb5_seteuid (ruid)) {
188         com_err (prog_name, errno, _("while setting euid to source user"));
189         exit (1);
190     }
191     while (!done &&
192            (option = getopt(pargc, pargv,"n:c:r:a:zZDfFpPkql:e:")) != -1) {
193         switch (option) {
194         case 'r':
195             if (strlen (optarg) >= 14)
196                 optarg = "bad-time";
197             retval = krb5_string_to_deltat(optarg, &rlife);
198             if (retval != 0 || rlife == 0) {
199                 fprintf(stderr, _("Bad lifetime value (%s hours?)\n"), optarg);
200                 errflg++;
201             }
202             krb5_get_init_creds_opt_set_renew_life(options, rlife);
203             break;
204         case 'a':
205             /* when integrating this remember to pass in pargc, pargv and
206                take care of params argument */
207             optind --;
208             if (auth_debug){printf("Before get_params optind=%d\n", optind);}
209 
210             if ((retval = get_params( & optind, pargc, pargv, &params))){
211                 com_err(prog_name, retval, _("when gathering parameters"));
212                 errflg++;
213             }
214             if(auth_debug){ printf("After get_params optind=%d\n", optind);}
215             done = 1;
216             break;
217         case 'p':
218             krb5_get_init_creds_opt_set_proxiable(options, 1);
219             break;
220         case 'P':
221             krb5_get_init_creds_opt_set_proxiable(options, 0);
222             break;
223         case 'f':
224             krb5_get_init_creds_opt_set_forwardable(options, 1);
225             break;
226         case 'F':
227             krb5_get_init_creds_opt_set_forwardable(options, 0);
228             break;
229         case 'k':
230             keep_target_cache =1;
231             break;
232         case 'q':
233             quiet =1;
234             break;
235         case 'l':
236             if (strlen (optarg) >= 14)
237                 optarg = "bad-time";
238             retval = krb5_string_to_deltat(optarg, &lifetime);
239             if (retval != 0 || lifetime == 0) {
240                 fprintf(stderr, _("Bad lifetime value (%s hours?)\n"), optarg);
241                 errflg++;
242             }
243             krb5_get_init_creds_opt_set_tkt_life(options, lifetime);
244             break;
245         case 'n':
246             if ((retval = krb5_parse_name(ksu_context, optarg, &client))){
247                 com_err(prog_name, retval, _("when parsing name %s"), optarg);
248                 errflg++;
249             }
250             given_princ = TRUE;
251             break;
252 #ifdef DEBUG
253         case 'D':
254             auth_debug = 1;
255             break;
256 #endif
257         case 'z':
258             some_rest_copy = 1;
259             if(all_rest_copy) {
260                 fprintf(stderr,
261                         _("-z option is mutually exclusive with -Z.\n"));
262                 errflg++;
263             }
264             break;
265         case 'Z':
266             all_rest_copy = 1;
267             if(some_rest_copy) {
268                 fprintf(stderr,
269                         _("-Z option is mutually exclusive with -z.\n"));
270                 errflg++;
271             }
272             break;
273         case 'c':
274             if (cc_source_tag == NULL) {
275                 cc_source_tag = xstrdup(optarg);
276                 if (!ks_ccache_name_is_initialized(ksu_context,
277                                                    cc_source_tag)) {
278                     com_err(prog_name, errno,
279                             _("while looking for credentials cache %s"),
280                             cc_source_tag);
281                     exit(1);
282                 }
283             } else {
284                 fprintf(stderr, _("Only one -c option allowed\n"));
285                 errflg++;
286             }
287             break;
288         case 'e':
289             cmd = xstrdup(optarg);
290             if(auth_debug){printf("Before get_params optind=%d\n", optind);}
291             if ((retval = get_params( & optind, pargc, pargv, &params))){
292                 com_err(prog_name, retval, _("when gathering parameters"));
293                 errflg++;
294             }
295             if(auth_debug){printf("After get_params optind=%d\n", optind);}
296             done = 1;
297 
298             if (auth_debug){
299                 fprintf(stderr,"Command to be executed: %s\n", cmd);
300             }
301             break;
302         case '?':
303         default:
304             errflg++;
305             break;
306         }
307     }
308 
309     if (errflg) {
310         usage();
311         exit(2);
312     }
313 
314     if (optind != pargc ){
315         usage();
316         exit(2);
317     }
318 
319     if (auth_debug){
320         for(j=1; params[j] != NULL; j++){
321             fprintf (stderr,"params[%d]= %s\n", j,params[j]);
322         }
323     }
324 
325     /***********************************/
326     source_user = getlogin(); /*checks for the the login name in /etc/utmp*/
327 
328     /* verify that that the user exists and get his passwd structure */
329 
330     if (source_user == NULL ||(pwd = getpwnam(source_user)) == NULL ||
331         pwd->pw_uid != ruid){
332         pwd = getpwuid(ruid);
333     }
334 
335     if (pwd == NULL) {
336         fprintf(stderr, _("ksu: who are you?\n"));
337         exit(1);
338     }
339     if (pwd->pw_uid != ruid) {
340         fprintf (stderr, _("Your uid doesn't match your passwd entry?!\n"));
341         exit (1);
342     }
343     /* Okay, now we have *some* passwd entry that matches the
344        current real uid.  */
345 
346     /* allocate space and copy the usernamane there */
347     source_user = xstrdup(pwd->pw_name);
348     source_uid = pwd->pw_uid;
349 
350     if (!strcmp(SOURCE_USER_LOGIN, target_user)){
351         target_user = xstrdup (source_user);
352     }
353 
354     if ((target_pwd = getpwnam(target_user)) == NULL){
355         fprintf(stderr, _("ksu: unknown login %s\n"), target_user);
356         exit(1);
357     }
358     target_uid = target_pwd->pw_uid;
359 
360     init_auth_names(target_pwd->pw_dir);
361 
362     /***********************************/
363 
364     if (cc_source_tag == NULL){
365         cc_source_tag = krb5_cc_default_name(ksu_context);
366         if (cc_source_tag == NULL) {
367             fprintf(stderr, _("ksu: failed to get default ccache name\n"));
368             exit(1);
369         }
370     }
371 
372     /* get a handle for the cache */
373     if ((retval = krb5_cc_resolve(ksu_context, cc_source_tag, &cc_source))){
374         com_err(prog_name, retval, _("while getting source cache"));
375         exit(1);
376     }
377 
378     if ((retval = get_best_princ_for_target(ksu_context, source_uid,
379                                             target_uid, source_user,
380                                             target_user, cc_source,
381                                             options, cmd, localhostname,
382                                             &client, &hp))){
383         com_err(prog_name,retval, _("while selecting the best principal"));
384         exit(1);
385     }
386 
387     /* We may be running as either source or target, depending on
388        what happened; become source.*/
389     if ( geteuid() != source_uid) {
390         if (krb5_seteuid(0) || krb5_seteuid(source_uid) ) {
391             com_err(prog_name, errno, _("while returning to source uid after "
392                                         "finding best principal"));
393             exit(1);
394         }
395     }
396 
397     if (auth_debug){
398         if (hp){
399             fprintf(stderr,
400                     "GET_best_princ_for_target result: NOT AUTHORIZED\n");
401         }else{
402             fprintf(stderr,
403                     "GET_best_princ_for_target result-best principal ");
404             plain_dump_principal (ksu_context, client);
405             fprintf(stderr,"\n");
406         }
407     }
408 
409     if (hp){
410         if (gb_err) fprintf(stderr, "%s", gb_err);
411         fprintf(stderr, _("account %s: authorization failed\n"), target_user);
412 
413         if (cmd != NULL) {
414             syslog(LOG_WARNING,
415                    "Account %s: authorization for %s for execution of %s failed",
416                    target_user, source_user, cmd);
417         } else {
418             syslog(LOG_WARNING, "Account %s: authorization of %s failed",
419                    target_user, source_user);
420         }
421 
422         exit(1);
423     }
424 
425     if (auth_debug)
426         fprintf(stderr, " source cache =  %s\n", cc_source_tag);
427 
428     /*
429      * After proper authentication and authorization, populate a cache for the
430      * target user.
431      */
432 
433     /*
434      * We read the set of creds we want to copy from the source ccache as the
435      * source uid, become root for authentication, and then become the target
436      * user to handle authorization and creating the target user's cache.
437      */
438 
439     /* if root ksu's to a regular user, then
440        then only the credentials for that particular user
441        should be copied */
442 
443     restrict_creds = (source_uid == 0) && (target_uid != 0);
444     retval = krb5_parse_name(ksu_context, KS_TEMPORARY_PRINC, &tmp_princ);
445     if (retval) {
446         com_err(prog_name, retval, _("while parsing temporary name"));
447         exit(1);
448     }
449     retval = krb5_cc_resolve(ksu_context, KS_TEMPORARY_CACHE, &cc_tmp);
450     if (retval) {
451         com_err(prog_name, retval, _("while creating temporary cache"));
452         exit(1);
453     }
454     retval = krb5_ccache_copy(ksu_context, cc_source, tmp_princ, cc_tmp,
455                               restrict_creds, client, &stored);
456     if (retval) {
457         com_err(prog_name, retval, _("while copying cache %s to %s"),
458                 krb5_cc_get_name(ksu_context, cc_source), KS_TEMPORARY_CACHE);
459         exit(1);
460     }
461     krb5_cc_close(ksu_context, cc_source);
462 
463     krb5_get_init_creds_opt_set_out_ccache(ksu_context, options, cc_tmp);
464 
465     /* Become root for authentication*/
466 
467     if (krb5_seteuid(0)) {
468         com_err(prog_name, errno, _("while reclaiming root uid"));
469         exit(1);
470     }
471 
472     if ((source_uid == 0) || (target_uid == source_uid)){
473 #ifdef GET_TGT_VIA_PASSWD
474         if (!all_rest_copy && given_princ && client != NULL && !stored) {
475             fprintf(stderr, _("WARNING: Your password may be exposed if you "
476                               "enter it here and are logged\n"));
477             fprintf(stderr, _("         in remotely using an unsecure "
478                               "(non-encrypted) channel.\n"));
479             if (ksu_get_tgt_via_passwd(ksu_context, client, options,
480                                        &zero_password, NULL) == FALSE) {
481 
482                 if (zero_password == FALSE){
483                     fprintf(stderr, _("Goodbye\n"));
484                     exit(1);
485                 }
486 
487                 fprintf(stderr, _("Could not get a tgt for "));
488                 plain_dump_principal (ksu_context, client);
489                 fprintf(stderr, "\n");
490 
491             }
492             stored = TRUE;
493         }
494 #endif /* GET_TGT_VIA_PASSWD */
495     }
496 
497     /* if the user is root or same uid then authentication is not necessary,
498        root gets in automatically */
499 
500     if (source_uid && (source_uid != target_uid)) {
501         char * client_name;
502 
503         auth_val = krb5_auth_check(ksu_context, client, localhostname,
504                                    options, target_user, cc_tmp,
505                                    &path_passwd, target_uid);
506 
507         /* if Kerberos authentication failed then exit */
508         if (auth_val ==FALSE){
509             fprintf(stderr, _("Authentication failed.\n"));
510             syslog(LOG_WARNING, "'%s %s' authentication failed for %s%s",
511                    prog_name,target_user,source_user,ontty());
512             exit(1);
513         }
514         stored = TRUE;
515 
516         if ((retval = krb5_unparse_name(ksu_context, client, &client_name))) {
517             com_err(prog_name, retval, _("When unparsing name"));
518             exit(1);
519         }
520 
521         print_status(_("Authenticated %s\n"), client_name);
522         syslog(LOG_NOTICE,"'%s %s' authenticated %s for %s%s",
523                prog_name,target_user,client_name,
524                source_user,ontty());
525 
526         /* Run authorization as target.*/
527         if (krb5_seteuid(target_uid)) {
528             com_err(prog_name, errno, _("while switching to target for "
529                                         "authorization check"));
530             exit(1);
531         }
532 
533         if ((retval = krb5_authorization(ksu_context, client,target_user,
534                                          cmd, &authorization_val, &exec_cmd))){
535             com_err(prog_name,retval, _("while checking authorization"));
536             krb5_seteuid(0); /*So we have some chance of sweeping up*/
537             exit(1);
538         }
539 
540         if (krb5_seteuid(0)) {
541             com_err(prog_name, errno, _("while switching back from target "
542                                         "after authorization check"));
543             exit(1);
544         }
545         if (authorization_val == TRUE){
546 
547             if (cmd) {
548                 print_status(_("Account %s: authorization for %s for "
549                                "execution of\n"), target_user, client_name);
550                 print_status(_("               %s successful\n"), exec_cmd);
551                 syslog(LOG_NOTICE,
552                        "Account %s: authorization for %s for execution of %s successful",
553                        target_user, client_name, exec_cmd);
554 
555             }else{
556                 print_status(_("Account %s: authorization for %s "
557                                "successful\n"), target_user, client_name);
558                 syslog(LOG_NOTICE,
559                        "Account %s: authorization for %s successful",
560                        target_user, client_name);
561             }
562         }else {
563             if (cmd){
564                 if (exec_cmd){ /* was used to pass back the error msg */
565                     fprintf(stderr, "%s", exec_cmd );
566                     syslog(LOG_WARNING, "%s",exec_cmd);
567                 }
568                 fprintf(stderr, _("Account %s: authorization for %s for "
569                                   "execution of %s failed\n"),
570                         target_user, client_name, cmd );
571                 syslog(LOG_WARNING,
572                        "Account %s: authorization for %s for execution of %s failed",
573                        target_user, client_name, cmd );
574 
575             }else{
576                 fprintf(stderr, _("Account %s: authorization of %s failed\n"),
577                         target_user, client_name);
578                 syslog(LOG_WARNING,
579                        "Account %s: authorization of %s failed",
580                        target_user, client_name);
581 
582             }
583 
584             exit(1);
585         }
586     }
587 
588     if( some_rest_copy){
589         retval = krb5_ccache_filter(ksu_context, cc_tmp, client);
590         if (retval) {
591             com_err(prog_name,retval, _("while calling cc_filter"));
592             exit(1);
593         }
594     }
595 
596     if (all_rest_copy){
597         retval = krb5_cc_initialize(ksu_context, cc_tmp, tmp_princ);
598         if (retval) {
599             com_err(prog_name, retval, _("while erasing target cache"));
600             exit(1);
601         }
602         stored = FALSE;
603     }
604 
605     /* get the shell of the user, this will be the shell used by su */
606     target_pwd = getpwnam(target_user);
607 
608     if (target_pwd->pw_shell)
609         shell = xstrdup(target_pwd->pw_shell);
610     else {
611         shell = _DEF_CSH;  /* default is cshell */
612     }
613 
614 #ifdef HAVE_GETUSERSHELL
615 
616     /* insist that the target login uses a standard shell (root is omitted) */
617 
618     if (!standard_shell(target_pwd->pw_shell) && source_uid) {
619         fprintf(stderr, _("ksu: permission denied (shell).\n"));
620         exit(1);
621     }
622 #endif /* HAVE_GETUSERSHELL */
623 
624     if (target_pwd->pw_uid){
625 
626         if(set_env_var("USER", target_pwd->pw_name)){
627             fprintf(stderr,
628                     _("ksu: couldn't set environment variable USER\n"));
629             exit(1);
630         }
631     }
632 
633     if(set_env_var( "HOME", target_pwd->pw_dir)){
634         fprintf(stderr, _("ksu: couldn't set environment variable HOME\n"));
635         exit(1);
636     }
637 
638     if(set_env_var( "SHELL", shell)){
639         fprintf(stderr, _("ksu: couldn't set environment variable SHELL\n"));
640         exit(1);
641     }
642 
643     /* set permissions */
644     if (setgid(target_pwd->pw_gid) < 0) {
645         perror("ksu: setgid");
646         exit(1);
647     }
648 
649     if (initgroups(target_user, target_pwd->pw_gid)) {
650         fprintf(stderr, _("ksu: initgroups failed.\n"));
651         exit(1);
652     }
653 
654     if ( ! strcmp(target_user, source_user)){
655         print_status(_("Leaving uid as %s (%ld)\n"),
656                      target_user, (long) target_pwd->pw_uid);
657     }else{
658         print_status(_("Changing uid to %s (%ld)\n"),
659                      target_user, (long) target_pwd->pw_uid);
660     }
661 
662 #ifdef  HAVE_SETLUID
663     /*
664      * If we're on a system which keeps track of login uids, then
665      * set the login uid. If this fails this opens up a problem on DEC OSF
666      * with C2 enabled.
667      */
668     if (setluid((uid_t) pwd->pw_uid) < 0) {
669         perror("setluid");
670         exit(1);
671     }
672 #endif  /* HAVE_SETLUID */
673 
674     if (setuid(target_pwd->pw_uid) < 0) {
675         perror("ksu: setuid");
676         exit(1);
677     }
678 
679     retval = resolve_target_cache(ksu_context, client, &cc_target, &cc_reused);
680     if (retval)
681         exit(1);
682     retval = krb5_cc_get_full_name(ksu_context, cc_target, &cc_target_tag);
683     if (retval) {
684         com_err(prog_name, retval, _("while getting name of target ccache"));
685         sweep_up(ksu_context, cc_target);
686         exit(1);
687     }
688     if (auth_debug)
689         fprintf(stderr, " target cache =  %s\n", cc_target_tag);
690     if (cc_reused)
691         keep_target_cache = TRUE;
692 
693     if (stored) {
694         retval = krb5_ccache_copy(ksu_context, cc_tmp, client, cc_target,
695                                   FALSE, client, &stored);
696         if (retval) {
697             com_err(prog_name, retval, _("while copying cache %s to %s"),
698                     KS_TEMPORARY_CACHE, cc_target_tag);
699             exit(1);
700         }
701 
702         if (!ks_ccache_is_initialized(ksu_context, cc_target)) {
703             com_err(prog_name, errno,
704                     _("%s does not have correct permissions for %s, "
705                       "%s aborted"), target_user, cc_target_tag, prog_name);
706             exit(1);
707         }
708     }
709 
710     krb5_free_string(ksu_context, cc_target_tag);
711 
712     /* Set the cc env name to target. */
713     retval = set_ccname_env(ksu_context, cc_target);
714     if (retval != 0) {
715         sweep_up(ksu_context, cc_target);
716         exit(1);
717     }
718 
719     if (cmd){
720         if ((source_uid == 0) || (source_uid == target_uid )){
721             exec_cmd = cmd;
722         }
723 
724         if( !exec_cmd){
725             fprintf(stderr, _("Internal error: command %s did not get "
726                               "resolved\n"), cmd);
727             exit(1);
728         }
729 
730         params[0] = exec_cmd;
731     }
732     else{
733         params[0] = shell;
734     }
735 
736     if (auth_debug){
737         fprintf(stderr, "program to be execed %s\n",params[0]);
738     }
739 
740     if( keep_target_cache ) {
741         execv(params[0], params);
742         com_err(prog_name, errno, _("while trying to execv %s"), params[0]);
743         sweep_up(ksu_context, cc_target);
744         exit(1);
745     }else{
746         statusp = 1;
747         switch ((child_pid = fork())) {
748         default:
749             if (auth_debug){
750                 printf(" The child pid is %ld\n", (long) child_pid);
751                 printf(" The parent pid is %ld\n", (long) getpid());
752             }
753             while ((ret_pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) {
754                 if (WIFSTOPPED(statusp)) {
755                     child_pgrp = tcgetpgrp(1);
756                     kill(getpid(), SIGSTOP);
757                     tcsetpgrp(1, child_pgrp);
758                     kill(child_pid, SIGCONT);
759                     statusp = 1;
760                     continue;
761                 }
762                 break;
763             }
764             if (auth_debug){
765                 printf("The exit status of the child is %d\n", statusp);
766             }
767             if (ret_pid == -1) {
768                 com_err(prog_name, errno, _("while calling waitpid"));
769             }
770             sweep_up(ksu_context, cc_target);
771             exit (WIFEXITED(statusp) ? WEXITSTATUS(statusp) : 1);
772         case -1:
773             com_err(prog_name, errno, _("while trying to fork."));
774             sweep_up(ksu_context, cc_target);
775             exit (1);
776         case 0:
777             execv(params[0], params);
778             com_err(prog_name, errno, _("while trying to execv %s"),
779                     params[0]);
780             exit (1);
781         }
782     }
783 }
784 
785 static krb5_error_code
init_ksu_context(krb5_context * context_out)786 init_ksu_context(krb5_context *context_out)
787 {
788     krb5_error_code retval;
789     const char *env_ccname;
790     krb5_context context;
791 
792     *context_out = NULL;
793 
794     retval = krb5_init_secure_context(&context);
795     if (retval)
796         return retval;
797 
798     /* We want to obey KRB5CCNAME in this context even though this is a setuid
799      * program.  (It will only be used when operating as the real uid.) */
800     env_ccname = getenv(KRB5_ENV_CCNAME);
801     if (env_ccname != NULL) {
802         retval = krb5_cc_set_default_name(context, env_ccname);
803         if (retval) {
804             krb5_free_context(context);
805             return retval;
806         }
807     }
808 
809     *context_out = context;
810     return 0;
811 }
812 
813 /* Set KRB5CCNAME in the environment to point to ccache.  Print an error
814  * message on failure. */
815 static krb5_error_code
set_ccname_env(krb5_context ksu_context,krb5_ccache ccache)816 set_ccname_env(krb5_context ksu_context, krb5_ccache ccache)
817 {
818     krb5_error_code retval;
819     char *ccname;
820 
821     retval = krb5_cc_get_full_name(ksu_context, ccache, &ccname);
822     if (retval) {
823         com_err(prog_name, retval, _("while reading cache name from ccache"));
824         return retval;
825     }
826     if (set_env_var(KRB5_ENV_CCNAME, ccname)) {
827         retval = errno;
828         fprintf(stderr,
829                 _("ksu: couldn't set environment variable %s\n"),
830                 KRB5_ENV_CCNAME);
831     }
832     krb5_free_string(ksu_context, ccname);
833     return retval;
834 }
835 
836 /*
837  * Get the configured default ccache name.  Unset KRB5CCNAME and force a
838  * recomputation so we don't use values for the source user.  Print an error
839  * message on failure.
840  */
841 static krb5_error_code
get_configured_defccname(krb5_context context,char ** target_out)842 get_configured_defccname(krb5_context context, char **target_out)
843 {
844     krb5_error_code retval;
845     const char *defname;
846     char *target = NULL;
847 
848     *target_out = NULL;
849 
850     unsetenv(KRB5_ENV_CCNAME);
851 
852     /* Make sure we don't have a cached value for a different uid. */
853     retval = krb5_cc_set_default_name(context, NULL);
854     if (retval != 0) {
855         com_err(prog_name, retval, _("while resetting target ccache name"));
856         return retval;
857     }
858 
859     defname = krb5_cc_default_name(context);
860     if (defname != NULL) {
861         if (strchr(defname, ':') != NULL) {
862             target = strdup(defname);
863         } else {
864             if (asprintf(&target, "FILE:%s", defname) < 0)
865                 target = NULL;
866         }
867     }
868     if (target == NULL) {
869         com_err(prog_name, ENOMEM, _("while determining target ccache name"));
870         return ENOMEM;
871     }
872     *target_out = target;
873     return 0;
874 }
875 
876 /* Determine where the target user's creds should be stored.  Print an error
877  * message on failure. */
878 static krb5_error_code
resolve_target_cache(krb5_context context,krb5_principal princ,krb5_ccache * ccache_out,krb5_boolean * ccache_reused)879 resolve_target_cache(krb5_context context, krb5_principal princ,
880                      krb5_ccache *ccache_out, krb5_boolean *ccache_reused)
881 {
882     krb5_error_code retval;
883     krb5_boolean switchable, reused = FALSE;
884     krb5_ccache ccache = NULL;
885     char *sep, *ccname = NULL, *sym = NULL, *target;
886 
887     *ccache_out = NULL;
888     *ccache_reused = FALSE;
889 
890     retval = get_configured_defccname(context, &target);
891     if (retval != 0)
892         return retval;
893 
894     /* Check if the configured default name uses a switchable type. */
895     sep = strchr(target, ':');
896     *sep = '\0';
897     switchable = krb5_cc_support_switch(context, target);
898     *sep = ':';
899 
900     if (!switchable) {
901         /* Try to avoid destroying an in-use target ccache by coming up with
902          * the name of a cache that doesn't exist yet. */
903         do {
904             free(ccname);
905             retval = gen_sym(context, &sym);
906             if (retval) {
907                 com_err(prog_name, retval,
908                         _("while generating part of the target ccache name"));
909                 goto cleanup;
910             }
911             if (asprintf(&ccname, "%s.%s", target, sym) < 0) {
912                 retval = ENOMEM;
913                 free(sym);
914                 com_err(prog_name, retval, _("while allocating memory for the "
915                                              "target ccache name"));
916                 goto cleanup;
917             }
918             free(sym);
919         } while (ks_ccache_name_is_initialized(context, ccname));
920         retval = krb5_cc_resolve(context, ccname, &ccache);
921         free(ccname);
922     } else {
923         /* Look for a cache in the collection that we can reuse. */
924         retval = krb5_cc_cache_match(context, princ, &ccache);
925         if (retval == 0) {
926             reused = TRUE;
927         } else {
928             /* There isn't one, so create a new one. */
929             *sep = '\0';
930             retval = krb5_cc_new_unique(context, target, NULL, &ccache);
931             *sep = ':';
932             if (retval) {
933                 com_err(prog_name, retval,
934                         _("while creating new target ccache"));
935                 goto cleanup;
936             }
937             retval = krb5_cc_initialize(context, ccache, princ);
938             if (retval) {
939                 com_err(prog_name, retval,
940                         _("while initializing target cache"));
941                 goto cleanup;
942             }
943         }
944     }
945 
946     *ccache_out = ccache;
947     *ccache_reused = reused;
948 
949 cleanup:
950     free(target);
951     return retval;
952 }
953 
954 #ifdef HAVE_GETUSERSHELL
955 
956 int
standard_shell(char * sh)957 standard_shell(char *sh)
958 {
959     char *cp;
960 
961     while ((cp = getusershell()) != NULL)
962         if (!strcmp(cp, sh))
963             return (1);
964     return (0);
965 }
966 
967 #endif /* HAVE_GETUSERSHELL */
968 
969 static char *
ontty(void)970 ontty(void)
971 {
972     char *p;
973     static char buf[MAXPATHLEN + 5];
974     int result;
975 
976     buf[0] = 0;
977     if ((p = ttyname(STDERR_FILENO))) {
978         result = snprintf(buf, sizeof(buf), " on %s", p);
979         if (SNPRINTF_OVERFLOW(result, sizeof(buf))) {
980             fprintf(stderr, _("terminal name %s too long\n"), p);
981             exit (1);
982         }
983     }
984     return (buf);
985 }
986 
987 static int
set_env_var(char * name,char * value)988 set_env_var(char *name, char *value)
989 {
990     char * env_var_buf;
991 
992     asprintf(&env_var_buf,"%s=%s",name, value);
993     return putenv(env_var_buf);
994 
995 }
996 
997 static void
sweep_up(krb5_context context,krb5_ccache cc)998 sweep_up(krb5_context context, krb5_ccache cc)
999 {
1000     krb5_error_code retval;
1001 
1002     krb5_seteuid(0);
1003     if (krb5_seteuid(target_uid) < 0) {
1004         com_err(prog_name, errno,
1005                 _("while changing to target uid for destroying ccache"));
1006         exit(1);
1007     }
1008 
1009     if (ks_ccache_is_initialized(context, cc)) {
1010         if ((retval = krb5_cc_destroy(context, cc)))
1011             com_err(prog_name, retval, _("while destroying cache"));
1012     }
1013 }
1014 
1015 /*****************************************************************
1016 get_params is to be called for the -a option or -e option to
1017            collect all params passed in for the shell or for
1018            cmd.  An array is returned containing all params.
1019            optindex is incremented accordingly and the first
1020            element in the returned array is reserved for the
1021            name of the command to be executed or the name of the
1022            shell.
1023 *****************************************************************/
1024 
1025 krb5_error_code
get_params(int * optindex,int pargc,char ** pargv,char *** params)1026 get_params(int *optindex, int pargc, char **pargv, char ***params)
1027 {
1028 
1029     int i,j;
1030     char ** ret_params;
1031     int size = pargc - *optindex + 2;
1032 
1033     if ((ret_params = (char **) calloc(size, sizeof (char *)))== NULL ){
1034         return ENOMEM;
1035     }
1036 
1037     for (i = *optindex, j=1; i < pargc; i++,j++){
1038         ret_params[j] = pargv[i];
1039         *optindex = *optindex + 1;
1040     }
1041 
1042     ret_params[size-1] = NULL;
1043     *params = ret_params;
1044     return 0;
1045 }
1046 
1047 static
print_status(const char * fmt,...)1048 void print_status(const char *fmt, ...)
1049 {
1050     va_list ap;
1051     if (! quiet){
1052         va_start(ap, fmt);
1053         vfprintf(stderr, fmt, ap);
1054         va_end(ap);
1055     }
1056 }
1057 
1058 krb5_error_code
ksu_tgtname(krb5_context context,const krb5_data * server,const krb5_data * client,krb5_principal * tgtprinc)1059 ksu_tgtname(krb5_context context, const krb5_data *server,
1060             const krb5_data *client, krb5_principal *tgtprinc)
1061 {
1062     return krb5_build_principal_ext(context, tgtprinc, client->length, client->data,
1063                                     KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
1064                                     server->length, server->data,
1065                                     0);
1066 }
1067