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