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 "k5-base64.h" 31 #include "adm_proto.h" 32 #include <sys/types.h> 33 #include <sys/stat.h> 34 35 /****************************************************************** 36 krb5_cache_copy 37 38 gets rid of any expired tickets in the secondary cache, 39 copies the default cache into the secondary cache, 40 41 ************************************************************************/ 42 43 void show_credential(); 44 45 /* modifies only the cc_other, the algorithm may look a bit funny, 46 but I had to do it this way, since remove function did not come 47 with k5 beta 3 release. 48 */ 49 50 krb5_error_code krb5_ccache_copy(context, cc_def, target_principal, cc_target, 51 restrict_creds, primary_principal, stored) 52 /* IN */ 53 krb5_context context; 54 krb5_ccache cc_def; 55 krb5_principal target_principal; 56 krb5_ccache cc_target; 57 krb5_boolean restrict_creds; 58 krb5_principal primary_principal; 59 /* OUT */ 60 krb5_boolean *stored; 61 { 62 int i=0; 63 krb5_error_code retval=0; 64 krb5_creds ** cc_def_creds_arr = NULL; 65 krb5_creds ** cc_other_creds_arr = NULL; 66 67 if (ks_ccache_is_initialized(context, cc_def)) { 68 if((retval = krb5_get_nonexp_tkts(context,cc_def,&cc_def_creds_arr))){ 69 return retval; 70 } 71 } 72 73 retval = krb5_cc_initialize(context, cc_target, target_principal); 74 if (retval) 75 return retval; 76 77 if (restrict_creds) { 78 retval = krb5_store_some_creds(context, cc_target, cc_def_creds_arr, 79 cc_other_creds_arr, primary_principal, 80 stored); 81 } else { 82 *stored = krb5_find_princ_in_cred_list(context, cc_def_creds_arr, 83 primary_principal); 84 retval = krb5_store_all_creds(context, cc_target, cc_def_creds_arr, 85 cc_other_creds_arr); 86 } 87 88 if (cc_def_creds_arr){ 89 while (cc_def_creds_arr[i]){ 90 krb5_free_creds(context, cc_def_creds_arr[i]); 91 i++; 92 } 93 } 94 95 i=0; 96 97 if(cc_other_creds_arr){ 98 while (cc_other_creds_arr[i]){ 99 krb5_free_creds(context, cc_other_creds_arr[i]); 100 i++; 101 } 102 } 103 104 return retval; 105 } 106 107 108 krb5_error_code krb5_store_all_creds(context, cc, creds_def, creds_other) 109 krb5_context context; 110 krb5_ccache cc; 111 krb5_creds **creds_def; 112 krb5_creds **creds_other; 113 { 114 115 int i = 0; 116 krb5_error_code retval = 0; 117 krb5_creds ** temp_creds= NULL; 118 119 120 if ((creds_def == NULL) && (creds_other == NULL)) 121 return 0; 122 123 if ((creds_def == NULL) && (creds_other != NULL)) 124 temp_creds = creds_other; 125 126 if ((creds_def != NULL) && (creds_other == NULL)) 127 temp_creds = creds_def; 128 129 130 if (temp_creds){ 131 while(temp_creds[i]){ 132 if ((retval= krb5_cc_store_cred(context, cc, 133 temp_creds[i]))){ 134 return retval; 135 } 136 i++; 137 } 138 } 139 else { /* both arrays have elements in them */ 140 141 return KRB5KRB_ERR_GENERIC; 142 143 /************ while(creds_other[i]){ 144 cmp = FALSE; 145 j = 0; 146 while(creds_def[j]){ 147 cmp = compare_creds(creds_other[i],creds_def[j]); 148 149 if( cmp == TRUE) break; 150 151 j++; 152 } 153 if (cmp == FALSE){ 154 if (retval= krb5_cc_store_cred(context, cc, 155 creds_other[i])){ 156 return retval; 157 } 158 } 159 i ++; 160 } 161 162 i=0; 163 while(creds_def[i]){ 164 if (retval= krb5_cc_store_cred(context, cc, 165 creds_def[i])){ 166 return retval; 167 } 168 i++; 169 } 170 171 **************/ 172 } 173 return 0; 174 } 175 176 krb5_boolean compare_creds(context, cred1, cred2) 177 krb5_context context; 178 krb5_creds *cred1; 179 krb5_creds *cred2; 180 { 181 krb5_boolean retval; 182 183 retval = krb5_principal_compare (context, cred1->client, cred2->client); 184 185 if (retval == TRUE) 186 retval = krb5_principal_compare (context, cred1->server, cred2->server); 187 188 return retval; 189 } 190 191 192 193 194 krb5_error_code krb5_get_nonexp_tkts(context, cc, creds_array) 195 krb5_context context; 196 krb5_ccache cc; 197 krb5_creds ***creds_array; 198 { 199 200 krb5_creds creds, temp_tktq, temp_tkt; 201 krb5_creds **temp_creds; 202 krb5_error_code retval=0; 203 krb5_cc_cursor cur; 204 int count = 0; 205 int chunk_count = 1; 206 207 if ( ! ( temp_creds = (krb5_creds **) malloc( CHUNK * sizeof(krb5_creds *)))){ 208 return ENOMEM; 209 } 210 211 212 memset(&temp_tktq, 0, sizeof(temp_tktq)); 213 memset(&temp_tkt, 0, sizeof(temp_tkt)); 214 memset(&creds, 0, sizeof(creds)); 215 216 /* initialize the cursor */ 217 if ((retval = krb5_cc_start_seq_get(context, cc, &cur))) { 218 return retval; 219 } 220 221 while (!(retval = krb5_cc_next_cred(context, cc, &cur, &creds))){ 222 223 if (!krb5_is_config_principal(context, creds.server) && 224 (retval = krb5_check_exp(context, creds.times))){ 225 if (retval != KRB5KRB_AP_ERR_TKT_EXPIRED){ 226 return retval; 227 } 228 if (auth_debug){ 229 fprintf(stderr,"krb5_ccache_copy: CREDS EXPIRED:\n"); 230 fputs(" Valid starting Expires Service principal\n",stdout); 231 show_credential(context, &creds, cc); 232 fprintf(stderr,"\n"); 233 } 234 } 235 else { /* these credentials didn't expire */ 236 237 if ((retval = krb5_copy_creds(context, &creds, 238 &temp_creds[count]))){ 239 return retval; 240 } 241 count ++; 242 243 if (count == (chunk_count * CHUNK -1)){ 244 chunk_count ++; 245 if (!(temp_creds = (krb5_creds **) realloc(temp_creds, 246 chunk_count * CHUNK * sizeof(krb5_creds *)))){ 247 return ENOMEM; 248 } 249 } 250 } 251 252 } 253 254 temp_creds[count] = NULL; 255 *creds_array = temp_creds; 256 257 if (retval == KRB5_CC_END) { 258 retval = krb5_cc_end_seq_get(context, cc, &cur); 259 } 260 261 return retval; 262 263 } 264 265 266 krb5_error_code krb5_check_exp(context, tkt_time) 267 krb5_context context; 268 krb5_ticket_times tkt_time; 269 { 270 krb5_error_code retval =0; 271 krb5_timestamp currenttime; 272 273 if ((retval = krb5_timeofday (context, ¤ttime))){ 274 return retval; 275 } 276 if (auth_debug){ 277 fprintf(stderr,"krb5_check_exp: the krb5_clockskew is %d \n", 278 context->clockskew); 279 280 fprintf(stderr,"krb5_check_exp: currenttime - endtime %d \n", 281 ts_delta(currenttime, tkt_time.endtime)); 282 283 } 284 285 if (ts_after(currenttime, ts_incr(tkt_time.endtime, context->clockskew))) { 286 retval = KRB5KRB_AP_ERR_TKT_EXPIRED ; 287 return retval; 288 } 289 290 return 0; 291 } 292 293 294 char *flags_string(cred) 295 krb5_creds *cred; 296 { 297 static char buf[32]; 298 int i = 0; 299 300 if (cred->ticket_flags & TKT_FLG_FORWARDABLE) 301 buf[i++] = 'F'; 302 if (cred->ticket_flags & TKT_FLG_FORWARDED) 303 buf[i++] = 'f'; 304 if (cred->ticket_flags & TKT_FLG_PROXIABLE) 305 buf[i++] = 'P'; 306 if (cred->ticket_flags & TKT_FLG_PROXY) 307 buf[i++] = 'p'; 308 if (cred->ticket_flags & TKT_FLG_MAY_POSTDATE) 309 buf[i++] = 'D'; 310 if (cred->ticket_flags & TKT_FLG_POSTDATED) 311 buf[i++] = 'd'; 312 if (cred->ticket_flags & TKT_FLG_INVALID) 313 buf[i++] = 'i'; 314 if (cred->ticket_flags & TKT_FLG_RENEWABLE) 315 buf[i++] = 'R'; 316 if (cred->ticket_flags & TKT_FLG_INITIAL) 317 buf[i++] = 'I'; 318 if (cred->ticket_flags & TKT_FLG_HW_AUTH) 319 buf[i++] = 'H'; 320 if (cred->ticket_flags & TKT_FLG_PRE_AUTH) 321 buf[i++] = 'A'; 322 buf[i] = '\0'; 323 return(buf); 324 } 325 326 void printtime(krb5_timestamp ts) 327 { 328 char fmtbuf[18], fill = ' '; 329 330 if (!krb5_timestamp_to_sfstring(ts, fmtbuf, sizeof(fmtbuf), &fill)) 331 printf("%s", fmtbuf); 332 } 333 334 335 krb5_error_code 336 krb5_get_login_princ(luser, princ_list) 337 const char *luser; 338 char ***princ_list; 339 { 340 struct stat sbuf; 341 struct passwd *pwd; 342 char pbuf[MAXPATHLEN]; 343 FILE *fp; 344 char * linebuf; 345 char *newline; 346 int gobble, result; 347 char ** buf_out; 348 struct stat st_temp; 349 int count = 0, chunk_count = 1; 350 351 /* no account => no access */ 352 353 if ((pwd = getpwnam(luser)) == NULL) { 354 return 0; 355 } 356 result = snprintf(pbuf, sizeof(pbuf), "%s/.k5login", pwd->pw_dir); 357 if (SNPRINTF_OVERFLOW(result, sizeof(pbuf))) { 358 fprintf(stderr, _("home directory path for %s too long\n"), luser); 359 exit (1); 360 } 361 362 if (stat(pbuf, &st_temp)) { /* not accessible */ 363 return 0; 364 } 365 366 367 /* open ~/.k5login */ 368 if ((fp = fopen(pbuf, "r")) == NULL) { 369 return 0; 370 } 371 /* 372 * For security reasons, the .k5login file must be owned either by 373 * the user himself, or by root. Otherwise, don't grant access. 374 */ 375 if (fstat(fileno(fp), &sbuf)) { 376 fclose(fp); 377 return 0; 378 } 379 if ((sbuf.st_uid != pwd->pw_uid) && sbuf.st_uid) { 380 fclose(fp); 381 return 0; 382 } 383 384 /* check each line */ 385 386 387 if( !(linebuf = (char *) calloc (BUFSIZ, sizeof(char)))) return ENOMEM; 388 389 if (!(buf_out = (char **) malloc( CHUNK * sizeof(char *)))) return ENOMEM; 390 391 while ( fgets(linebuf, BUFSIZ, fp) != NULL) { 392 /* null-terminate the input string */ 393 linebuf[BUFSIZ-1] = '\0'; 394 newline = NULL; 395 /* nuke the newline if it exists */ 396 if ((newline = strchr(linebuf, '\n'))) 397 *newline = '\0'; 398 399 buf_out[count] = linebuf; 400 count ++; 401 402 if (count == (chunk_count * CHUNK -1)){ 403 chunk_count ++; 404 if (!(buf_out = (char **) realloc(buf_out, 405 chunk_count * CHUNK * sizeof(char *)))){ 406 return ENOMEM; 407 } 408 } 409 410 /* clean up the rest of the line if necessary */ 411 if (!newline) 412 while (((gobble = getc(fp)) != EOF) && gobble != '\n'); 413 414 if( !(linebuf = (char *) calloc (BUFSIZ, sizeof(char)))) return ENOMEM; 415 } 416 417 buf_out[count] = NULL; 418 *princ_list = buf_out; 419 fclose(fp); 420 return 0; 421 } 422 423 424 425 void 426 show_credential(context, cred, cc) 427 krb5_context context; 428 krb5_creds *cred; 429 krb5_ccache cc; 430 { 431 krb5_error_code retval; 432 char *name, *sname, *flags; 433 int first = 1; 434 krb5_principal princ; 435 char * defname; 436 int show_flags =1; 437 438 retval = krb5_unparse_name(context, cred->client, &name); 439 if (retval) { 440 com_err(prog_name, retval, _("while unparsing client name")); 441 return; 442 } 443 retval = krb5_unparse_name(context, cred->server, &sname); 444 if (retval) { 445 com_err(prog_name, retval, _("while unparsing server name")); 446 free(name); 447 return; 448 } 449 450 if ((retval = krb5_cc_get_principal(context, cc, &princ))) { 451 com_err(prog_name, retval, _("while retrieving principal name")); 452 return; 453 } 454 if ((retval = krb5_unparse_name(context, princ, &defname))) { 455 com_err(prog_name, retval, _("while unparsing principal name")); 456 return; 457 } 458 459 if (!cred->times.starttime) 460 cred->times.starttime = cred->times.authtime; 461 462 printtime(cred->times.starttime); 463 putchar(' '); putchar(' '); 464 printtime(cred->times.endtime); 465 putchar(' '); putchar(' '); 466 467 printf("%s\n", sname); 468 469 if (strcmp(name, defname)) { 470 printf(_("\tfor client %s"), name); 471 first = 0; 472 } 473 474 if (cred->times.renew_till) { 475 if (first) 476 fputs("\t",stdout); 477 else 478 fputs(", ",stdout); 479 fputs(_("renew until "), stdout); 480 printtime(cred->times.renew_till); 481 } 482 if (show_flags) { 483 flags = flags_string(cred); 484 if (flags && *flags) { 485 if (first) 486 fputs("\t",stdout); 487 else 488 fputs(", ",stdout); 489 printf(_("Flags: %s"), flags); 490 first = 0; 491 } 492 } 493 putchar('\n'); 494 free(name); 495 free(sname); 496 } 497 498 /* Create a random string suitable for a filename extension. */ 499 krb5_error_code 500 gen_sym(krb5_context context, char **sym_out) 501 { 502 krb5_error_code retval; 503 char bytes[6], *p, *sym; 504 krb5_data data = make_data(bytes, sizeof(bytes)); 505 506 *sym_out = NULL; 507 retval = krb5_c_random_make_octets(context, &data); 508 if (retval) 509 return retval; 510 sym = k5_base64_encode(data.data, data.length); 511 if (sym == NULL) 512 return ENOMEM; 513 /* Tweak the output alphabet just a bit. */ 514 while ((p = strchr(sym, '/')) != NULL) 515 *p = '_'; 516 while ((p = strchr(sym, '+')) != NULL) 517 *p = '-'; 518 *sym_out = sym; 519 return 0; 520 } 521 522 krb5_error_code krb5_ccache_overwrite(context, ccs, cct, primary_principal) 523 krb5_context context; 524 krb5_ccache ccs; 525 krb5_ccache cct; 526 krb5_principal primary_principal; 527 { 528 krb5_error_code retval=0; 529 krb5_principal temp_principal; 530 krb5_creds ** ccs_creds_arr = NULL; 531 int i=0; 532 533 if (ks_ccache_is_initialized(context, ccs)) { 534 if ((retval = krb5_get_nonexp_tkts(context, ccs, &ccs_creds_arr))){ 535 return retval; 536 } 537 } 538 539 if (ks_ccache_is_initialized(context, cct)) { 540 if ((retval = krb5_cc_get_principal(context, cct, &temp_principal))){ 541 return retval; 542 } 543 }else{ 544 temp_principal = primary_principal; 545 } 546 547 if ((retval = krb5_cc_initialize(context, cct, temp_principal))){ 548 return retval; 549 } 550 551 retval = krb5_store_all_creds(context, cct, ccs_creds_arr, NULL); 552 553 if (ccs_creds_arr){ 554 while (ccs_creds_arr[i]){ 555 krb5_free_creds(context, ccs_creds_arr[i]); 556 i++; 557 } 558 } 559 560 return retval; 561 } 562 563 krb5_error_code krb5_store_some_creds(context, cc, creds_def, creds_other, prst, 564 stored) 565 krb5_context context; 566 krb5_ccache cc; 567 krb5_creds **creds_def; 568 krb5_creds **creds_other; 569 krb5_principal prst; 570 krb5_boolean *stored; 571 { 572 573 int i = 0; 574 krb5_error_code retval = 0; 575 krb5_creds ** temp_creds= NULL; 576 krb5_boolean temp_stored = FALSE; 577 578 579 if ((creds_def == NULL) && (creds_other == NULL)) 580 return 0; 581 582 if ((creds_def == NULL) && (creds_other != NULL)) 583 temp_creds = creds_other; 584 585 if ((creds_def != NULL) && (creds_other == NULL)) 586 temp_creds = creds_def; 587 588 589 if (temp_creds){ 590 while(temp_creds[i]){ 591 if (krb5_principal_compare(context, 592 temp_creds[i]->client, 593 prst)== TRUE) { 594 595 if ((retval = krb5_cc_store_cred(context, 596 cc,temp_creds[i]))){ 597 return retval; 598 } 599 temp_stored = TRUE; 600 } 601 602 i++; 603 } 604 } 605 else { /* both arrays have elements in them */ 606 return KRB5KRB_ERR_GENERIC; 607 } 608 609 *stored = temp_stored; 610 return 0; 611 } 612 613 krb5_error_code krb5_ccache_filter (context, cc, prst) 614 krb5_context context; 615 krb5_ccache cc; 616 krb5_principal prst; 617 { 618 619 int i=0; 620 krb5_error_code retval=0; 621 krb5_principal temp_principal; 622 krb5_creds ** cc_creds_arr = NULL; 623 const char * cc_name; 624 krb5_boolean stored; 625 626 cc_name = krb5_cc_get_name(context, cc); 627 628 if (ks_ccache_is_initialized(context, cc)) { 629 if (auth_debug) { 630 fprintf(stderr,"putting cache %s through a filter for -z option\n", cc_name); 631 } 632 633 if ((retval = krb5_get_nonexp_tkts(context, cc, &cc_creds_arr))){ 634 return retval; 635 } 636 637 if ((retval = krb5_cc_get_principal(context, cc, &temp_principal))){ 638 return retval; 639 } 640 641 if ((retval = krb5_cc_initialize(context, cc, temp_principal))){ 642 return retval; 643 } 644 645 if ((retval = krb5_store_some_creds(context, cc, cc_creds_arr, 646 NULL, prst, &stored))){ 647 return retval; 648 } 649 650 if (cc_creds_arr){ 651 while (cc_creds_arr[i]){ 652 krb5_free_creds(context, cc_creds_arr[i]); 653 i++; 654 } 655 } 656 } 657 return 0; 658 } 659 660 krb5_boolean krb5_find_princ_in_cred_list (context, creds_list, princ) 661 krb5_context context; 662 krb5_creds **creds_list; 663 krb5_principal princ; 664 { 665 666 int i = 0; 667 krb5_boolean temp_stored = FALSE; 668 669 if (creds_list){ 670 while(creds_list[i]){ 671 if (krb5_principal_compare(context, 672 creds_list[i]->client, 673 princ)== TRUE){ 674 temp_stored = TRUE; 675 break; 676 } 677 678 i++; 679 } 680 } 681 682 return temp_stored; 683 } 684 685 krb5_error_code krb5_find_princ_in_cache (context, cc, princ, found) 686 krb5_context context; 687 krb5_ccache cc; 688 krb5_principal princ; 689 krb5_boolean *found; 690 { 691 krb5_error_code retval; 692 krb5_creds ** creds_list = NULL; 693 694 if (ks_ccache_is_initialized(context, cc)) { 695 if ((retval = krb5_get_nonexp_tkts(context, cc, &creds_list))){ 696 return retval; 697 } 698 } 699 700 *found = krb5_find_princ_in_cred_list(context, creds_list, princ); 701 return 0; 702 } 703 704 krb5_boolean 705 ks_ccache_name_is_initialized(krb5_context context, const char *cctag) 706 { 707 krb5_boolean result; 708 krb5_ccache cc; 709 710 if (krb5_cc_resolve(context, cctag, &cc) != 0) 711 return FALSE; 712 result = ks_ccache_is_initialized(context, cc); 713 krb5_cc_close(context, cc); 714 715 return result; 716 } 717 718 krb5_boolean 719 ks_ccache_is_initialized(krb5_context context, krb5_ccache cc) 720 { 721 krb5_principal princ; 722 krb5_error_code retval; 723 724 if (cc == NULL) 725 return FALSE; 726 727 retval = krb5_cc_get_principal(context, cc, &princ); 728 if (retval == 0) 729 krb5_free_principal(context, princ); 730 731 return retval == 0; 732 } 733