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 31 #ifdef HAVE_UNISTD_H 32 #include <unistd.h> 33 #endif 34 35 36 /******************************************************************* 37 get_all_princ_from_file - retrieves all principal names 38 from file pointed to by fp. 39 40 *******************************************************************/ 41 static void close_time (int, FILE *, int, FILE *); 42 static krb5_boolean find_str_in_list (char **, char *); 43 44 krb5_error_code get_all_princ_from_file (fp, plist) 45 FILE *fp; 46 char ***plist; 47 { 48 49 krb5_error_code retval; 50 char * line, * fprinc, * lp, ** temp_list = NULL; 51 int count = 0, chunk_count = 1; 52 53 if (!(temp_list = (char **) malloc( CHUNK * sizeof(char *)))) 54 return ENOMEM; 55 56 retval = get_line(fp, &line); 57 if (retval) 58 return retval; 59 60 while (line){ 61 fprinc = get_first_token (line, &lp); 62 63 if (fprinc ){ 64 temp_list[count] = xstrdup(fprinc); 65 count ++; 66 } 67 68 if(count == (chunk_count * CHUNK -1)){ 69 chunk_count ++; 70 if (!(temp_list = (char **) realloc(temp_list, 71 chunk_count * CHUNK * sizeof(char *)))){ 72 return ENOMEM; 73 } 74 } 75 76 77 free (line); 78 retval = get_line(fp, &line); 79 if (retval) 80 return retval; 81 } 82 83 temp_list[count] = NULL; 84 85 *plist = temp_list; 86 return 0; 87 } 88 89 /************************************************************* 90 list_union - combines list1 and list2 into combined_list. 91 the space for list1 and list2 is either freed 92 or used by combined_list. 93 **************************************************************/ 94 95 krb5_error_code list_union(list1, list2, combined_list) 96 char **list1; 97 char **list2; 98 char ***combined_list; 99 { 100 101 unsigned int c1 =0, c2 = 0, i=0, j=0; 102 char ** tlist; 103 104 if (! list1){ 105 *combined_list = list2; 106 return 0; 107 } 108 109 if (! list2){ 110 *combined_list = list1; 111 return 0; 112 } 113 114 while (list1[c1]) c1++; 115 while (list2[c2]) c2++; 116 117 if (!(tlist = (char **) calloc( c1 + c2 + 1, sizeof ( char *)))) 118 return ENOMEM; 119 120 i = 0; 121 while(list1[i]) { 122 tlist[i] = list1[i]; 123 i++; 124 } 125 j = 0; 126 while(list2[j]){ 127 if(find_str_in_list(list1, list2[j])==FALSE){ 128 tlist[i] = list2[j]; 129 i++; 130 } 131 j++; 132 } 133 134 free (list1); 135 free (list2); 136 137 tlist[i]= NULL; 138 139 *combined_list = tlist; 140 return 0; 141 } 142 143 krb5_error_code 144 filter(fp, cmd, k5users_list, k5users_filt_list) 145 FILE *fp; 146 char *cmd; 147 char **k5users_list; 148 char ***k5users_filt_list; 149 { 150 151 krb5_error_code retval =0; 152 krb5_boolean found = FALSE; 153 char * out_cmd = NULL; 154 unsigned int i=0, j=0, found_count = 0, k=0; 155 char ** temp_filt_list; 156 157 *k5users_filt_list = NULL; 158 159 if (! k5users_list){ 160 return 0; 161 } 162 163 while(k5users_list[i]){ 164 165 retval= k5users_lookup(fp, k5users_list[i], cmd, &found, &out_cmd); 166 if (retval) 167 return retval; 168 169 if (found == FALSE){ 170 free (k5users_list[i]); 171 k5users_list[i] = NULL; 172 if (out_cmd) gb_err = out_cmd; 173 } else 174 found_count ++; 175 176 i++; 177 } 178 179 if (! (temp_filt_list = (char **) calloc(found_count +1, sizeof (char*)))) 180 return ENOMEM; 181 182 for(j= 0, k=0; j < i; j++ ) { 183 if (k5users_list[j]){ 184 temp_filt_list[k] = k5users_list[j]; 185 k++; 186 } 187 } 188 189 temp_filt_list[k] = NULL; 190 191 free (k5users_list); 192 193 *k5users_filt_list = temp_filt_list; 194 return 0; 195 } 196 197 krb5_error_code 198 get_authorized_princ_names(luser, cmd, princ_list) 199 const char *luser; 200 char *cmd; 201 char ***princ_list; 202 { 203 204 struct passwd *pwd; 205 int k5login_flag =0; 206 int k5users_flag =0; 207 FILE * login_fp = NULL , * users_fp = NULL; 208 char ** k5login_list = NULL, ** k5users_list = NULL; 209 char ** k5users_filt_list = NULL; 210 char ** combined_list = NULL; 211 struct stat tb; 212 krb5_error_code retval; 213 214 *princ_list = NULL; 215 216 /* no account => no access */ 217 218 if ((pwd = getpwnam(luser)) == NULL) 219 return 0; 220 221 k5login_flag = stat(k5login_path, &tb); 222 k5users_flag = stat(k5users_path, &tb); 223 224 if (!k5login_flag){ 225 if ((login_fp = fopen(k5login_path, "r")) == NULL) 226 return 0; 227 if ( fowner(login_fp, pwd->pw_uid) == FALSE){ 228 close_time(1 /*k5users_flag*/, (FILE *) 0 /*users_fp*/, 229 k5login_flag,login_fp); 230 return 0; 231 } 232 } 233 if (!k5users_flag){ 234 if ((users_fp = fopen(k5users_path, "r")) == NULL) 235 return 0; 236 237 if ( fowner(users_fp, pwd->pw_uid) == FALSE){ 238 close_time(k5users_flag,users_fp, k5login_flag,login_fp); 239 return 0; 240 } 241 242 retval = get_all_princ_from_file (users_fp, &k5users_list); 243 if(retval) { 244 close_time(k5users_flag,users_fp, k5login_flag,login_fp); 245 return retval; 246 } 247 248 rewind(users_fp); 249 250 retval = filter(users_fp,cmd, k5users_list, &k5users_filt_list); 251 if(retval) { 252 close_time(k5users_flag,users_fp, k5login_flag, login_fp); 253 return retval; 254 } 255 } 256 257 if (!k5login_flag){ 258 retval = get_all_princ_from_file (login_fp, &k5login_list); 259 if(retval) { 260 close_time(k5users_flag,users_fp, k5login_flag,login_fp); 261 return retval; 262 } 263 } 264 265 close_time(k5users_flag,users_fp, k5login_flag, login_fp); 266 267 retval = list_union(k5login_list, k5users_filt_list, &combined_list); 268 if (retval){ 269 return retval; 270 } 271 *princ_list = combined_list; 272 return 0; 273 } 274 275 static void close_time(k5users_flag, users_fp, k5login_flag, login_fp) 276 int k5users_flag; 277 FILE *users_fp; 278 int k5login_flag; 279 FILE *login_fp; 280 { 281 282 if (!k5users_flag) fclose(users_fp); 283 if (!k5login_flag) fclose(login_fp); 284 285 } 286 287 static krb5_boolean find_str_in_list(list , elm) 288 char **list; 289 char *elm; 290 { 291 292 int i=0; 293 krb5_boolean found = FALSE; 294 295 if (!list) return found; 296 297 while (list[i] ){ 298 if (!strcmp(list[i], elm)){ 299 found = TRUE; 300 break; 301 } 302 i++; 303 } 304 305 return found; 306 } 307 308 /********************************************************************** 309 returns the principal that is closes to client (can be the the client 310 himself). plist contains 311 a principal list obtained from .k5login and .k5users file. 312 A principal is picked that has the best chance of getting in. 313 314 **********************************************************************/ 315 316 317 krb5_error_code get_closest_principal(context, plist, client, found) 318 krb5_context context; 319 char **plist; 320 krb5_principal *client; 321 krb5_boolean *found; 322 { 323 krb5_error_code retval =0; 324 krb5_principal temp_client, best_client = NULL; 325 int i = 0, j=0, cnelem, pnelem; 326 krb5_boolean got_one; 327 328 *found = FALSE; 329 330 if (! plist ) return 0; 331 332 cnelem = krb5_princ_size(context, *client); 333 334 while(plist[i]){ 335 336 retval = krb5_parse_name(context, plist[i], &temp_client); 337 if (retval) 338 return retval; 339 340 pnelem = krb5_princ_size(context, temp_client); 341 342 if ( cnelem > pnelem){ 343 i++; 344 continue; 345 } 346 347 if (data_eq(*krb5_princ_realm(context, *client), 348 *krb5_princ_realm(context, temp_client))) { 349 350 got_one = TRUE; 351 for(j =0; j < cnelem; j ++){ 352 krb5_data *p1 = 353 krb5_princ_component(context, *client, j); 354 krb5_data *p2 = 355 krb5_princ_component(context, temp_client, j); 356 357 if (!p1 || !p2 || !data_eq(*p1, *p2)) { 358 got_one = FALSE; 359 break; 360 } 361 } 362 if (got_one == TRUE){ 363 if(best_client){ 364 if(krb5_princ_size(context, best_client) > 365 krb5_princ_size(context, temp_client)){ 366 best_client = temp_client; 367 } 368 }else 369 best_client = temp_client; 370 } 371 } 372 i++; 373 } 374 375 if (best_client) { 376 *found = TRUE; 377 *client = best_client; 378 } 379 380 return 0; 381 } 382 383 /**************************************************************** 384 find_either_ticket checks to see whether there is a ticket for the 385 end server or tgt, if neither is there the return FALSE, 386 *****************************************************************/ 387 388 krb5_error_code find_either_ticket (context, cc, client, end_server, found) 389 krb5_context context; 390 krb5_ccache cc; 391 krb5_principal client; 392 krb5_principal end_server; 393 krb5_boolean *found; 394 { 395 396 krb5_principal kdc_server; 397 krb5_error_code retval; 398 krb5_boolean temp_found = FALSE; 399 400 if (ks_ccache_is_initialized(context, cc)) { 401 402 retval = find_ticket(context, cc, client, end_server, &temp_found); 403 if (retval) 404 return retval; 405 406 if (temp_found == FALSE){ 407 retval = ksu_tgtname(context, 408 krb5_princ_realm(context, client), 409 krb5_princ_realm(context, client), 410 &kdc_server); 411 if (retval) 412 return retval; 413 414 retval = find_ticket(context, cc,client, kdc_server, &temp_found); 415 if(retval) 416 return retval; 417 } 418 else if (auth_debug) 419 printf("find_either_ticket: found end server ticket\n"); 420 } 421 422 *found = temp_found; 423 424 return 0; 425 } 426 427 428 krb5_error_code find_ticket (context, cc, client, server, found) 429 krb5_context context; 430 krb5_ccache cc; 431 krb5_principal client; 432 krb5_principal server; 433 krb5_boolean *found; 434 { 435 436 krb5_creds tgt, tgtq; 437 krb5_error_code retval; 438 439 *found = FALSE; 440 441 memset(&tgtq, 0, sizeof(tgtq)); 442 memset(&tgt, 0, sizeof(tgt)); 443 444 retval= krb5_copy_principal(context, client, &tgtq.client); 445 if (retval) 446 return retval; 447 448 retval= krb5_copy_principal(context, server, &tgtq.server); 449 if (retval) 450 return retval ; 451 452 retval = krb5_cc_retrieve_cred(context, cc, KRB5_TC_MATCH_SRV_NAMEONLY | KRB5_TC_SUPPORTED_KTYPES, 453 &tgtq, &tgt); 454 455 if (! retval) retval = krb5_check_exp(context, tgt.times); 456 457 if (retval){ 458 if ((retval != KRB5_CC_NOTFOUND) && 459 (retval != KRB5KRB_AP_ERR_TKT_EXPIRED)){ 460 return retval ; 461 } 462 } else{ 463 *found = TRUE; 464 return 0; 465 } 466 467 free(tgtq.server); 468 free(tgtq.client); 469 470 return 0; 471 } 472 473 474 475 krb5_error_code find_princ_in_list (context, princ, plist, found) 476 krb5_context context; 477 krb5_principal princ; 478 char **plist; 479 krb5_boolean *found; 480 { 481 482 int i=0; 483 char * princname; 484 krb5_error_code retval; 485 486 *found = FALSE; 487 488 if (!plist) return 0; 489 490 retval = krb5_unparse_name(context, princ, &princname); 491 if (retval) 492 return retval; 493 494 while (plist[i] ){ 495 if (!strcmp(plist[i], princname)){ 496 *found = TRUE; 497 break; 498 } 499 i++; 500 } 501 502 return 0; 503 504 } 505 506 typedef struct princ_info { 507 krb5_principal p; 508 krb5_boolean found; 509 }princ_info; 510 511 /********************************************************************** 512 get_best_princ_for_target - 513 514 sets the client name, path_out gets set, if authorization is not possible 515 path_out gets set to ... 516 517 ***********************************************************************/ 518 519 krb5_error_code get_best_princ_for_target(context, source_uid, target_uid, 520 source_user, target_user, 521 cc_source, options, cmd, 522 hostname, client, path_out) 523 krb5_context context; 524 uid_t source_uid; 525 uid_t target_uid; 526 char *source_user; 527 char *target_user; 528 krb5_ccache cc_source; 529 krb5_get_init_creds_opt *options; 530 char *cmd; 531 char *hostname; 532 krb5_principal *client; 533 int *path_out; 534 { 535 536 princ_info princ_trials[10]; 537 krb5_principal cc_def_princ = NULL; 538 krb5_principal temp_client; 539 krb5_principal target_client; 540 krb5_principal source_client; 541 krb5_principal end_server; 542 krb5_error_code retval; 543 char ** aplist =NULL; 544 krb5_boolean found = FALSE; 545 struct stat tb; 546 int count =0; 547 int i; 548 549 *path_out = 0; 550 551 /* -n option was specified client is set we are done */ 552 if (*client != NULL) 553 return 0; 554 555 if (ks_ccache_is_initialized(context, cc_source)) { 556 retval = krb5_cc_get_principal(context, cc_source, &cc_def_princ); 557 if (retval) 558 return retval; 559 } 560 561 retval=krb5_parse_name(context, target_user, &target_client); 562 if (retval) 563 return retval; 564 565 retval=krb5_parse_name(context, source_user, &source_client); 566 if (retval) 567 return retval; 568 569 if (source_uid == 0){ 570 if (target_uid != 0) 571 *client = target_client; /* this will be used to restrict 572 the cache copty */ 573 else { 574 if(cc_def_princ) 575 *client = cc_def_princ; 576 else 577 *client = target_client; 578 } 579 580 if (auth_debug) 581 printf(" GET_best_princ_for_target: via source_uid == 0\n"); 582 583 return 0; 584 } 585 586 /* from here on, the code is for source_uid != 0 */ 587 588 if (source_uid && (source_uid == target_uid)){ 589 if(cc_def_princ) 590 *client = cc_def_princ; 591 else 592 *client = target_client; 593 if (auth_debug) 594 printf("GET_best_princ_for_target: via source_uid == target_uid\n"); 595 return 0; 596 } 597 598 /* Become root, then target for looking at .k5login.*/ 599 if (krb5_seteuid(0) || krb5_seteuid(target_uid) ) { 600 return errno; 601 } 602 603 /* if .k5users and .k5login do not exist */ 604 if (stat(k5login_path, &tb) && stat(k5users_path, &tb) ){ 605 *client = target_client; 606 607 if (cmd) 608 *path_out = NOT_AUTHORIZED; 609 610 if (auth_debug) 611 printf(" GET_best_princ_for_target: via no auth files path\n"); 612 613 return 0; 614 }else{ 615 retval = get_authorized_princ_names(target_user, cmd, &aplist); 616 if (retval) 617 return retval; 618 619 /* .k5users or .k5login exist, but no authorization */ 620 if ((!aplist) || (!aplist[0])) { 621 *path_out = NOT_AUTHORIZED; 622 if (auth_debug) 623 printf("GET_best_princ_for_target: via empty auth files path\n"); 624 return 0; 625 } 626 } 627 628 retval = krb5_sname_to_principal(context, hostname, NULL, 629 KRB5_NT_SRV_HST, &end_server); 630 if (retval) 631 return retval; 632 633 634 /* first see if default principal of the source cache 635 * can get us in, then the target_user@realm, then the 636 * source_user@realm. If all of them fail, try any 637 * other ticket in the cache. */ 638 639 if (cc_def_princ) 640 princ_trials[count ++].p = cc_def_princ; 641 else 642 princ_trials[count ++].p = NULL; 643 644 princ_trials[count ++].p = target_client; 645 princ_trials[count ++].p = source_client; 646 647 for (i= 0; i < count; i ++) 648 princ_trials[i].found = FALSE; 649 650 for (i= 0; i < count; i ++){ 651 if(princ_trials[i].p) { 652 retval= find_princ_in_list(context, princ_trials[i].p, aplist, 653 &found); 654 if (retval) 655 return retval; 656 657 if (found == TRUE){ 658 princ_trials[i].found = TRUE; 659 660 retval = find_either_ticket (context, cc_source, 661 princ_trials[i].p, 662 end_server, &found); 663 if (retval) 664 return retval; 665 if (found == TRUE){ 666 *client = princ_trials[i].p; 667 if (auth_debug) 668 printf("GET_best_princ_for_target: via ticket file, choice #%d\n", i); 669 return 0; 670 } 671 } 672 } 673 } 674 675 /* out of preferred principals, see if there is any ticket that will 676 get us in */ 677 678 i=0; 679 while (aplist[i]){ 680 retval = krb5_parse_name(context, aplist[i], &temp_client); 681 if (retval) 682 return retval; 683 684 retval = find_either_ticket (context, cc_source, temp_client, 685 end_server, &found); 686 if (retval) 687 return retval; 688 689 if (found == TRUE){ 690 if (auth_debug) 691 printf("GET_best_princ_for_target: via ticket file, choice: any ok ticket \n" ); 692 *client = temp_client; 693 return 0; 694 } 695 696 krb5_free_principal(context, temp_client); 697 698 i++; 699 } 700 701 /* no tickets qualified, select a principal, that may be used 702 for password promting */ 703 704 705 for (i=0; i < count; i ++){ 706 if (princ_trials[i].found == TRUE){ 707 *client = princ_trials[i].p; 708 709 if (auth_debug) 710 printf("GET_best_princ_for_target: via prompt passwd list choice #%d \n",i); 711 return 0; 712 } 713 } 714 715 #ifdef PRINC_LOOK_AHEAD 716 for (i=0; i < count; i ++){ 717 if (princ_trials[i].p){ 718 retval=krb5_copy_principal(context, princ_trials[i].p, 719 &temp_client); 720 if(retval) 721 return retval; 722 723 /* get the client name that is the closest 724 to the three princ in trials */ 725 726 retval=get_closest_principal(context, aplist, &temp_client, 727 &found); 728 if(retval) 729 return retval; 730 731 if (found == TRUE){ 732 *client = temp_client; 733 if (auth_debug) 734 printf("GET_best_princ_for_target: via prompt passwd list choice: approximation of princ in trials # %d \n",i); 735 return 0; 736 } 737 krb5_free_principal(context, temp_client); 738 } 739 } 740 741 #endif /* PRINC_LOOK_AHEAD */ 742 743 744 if(auth_debug) 745 printf( "GET_best_princ_for_target: out of luck, can't get appropriate default principal\n"); 746 747 *path_out = NOT_AUTHORIZED; 748 return 0; 749 } 750