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, ¶ms))){
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, ¶ms))){
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