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