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