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 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 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 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 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 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 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 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 * 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 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 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 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 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 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