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 static void 44 free_creds_list(krb5_context context, krb5_creds **list) 45 { 46 size_t i; 47 48 if (list == NULL) 49 return; 50 for (i = 0; list[i]; i++) 51 krb5_free_creds(context, list[i]); 52 free(list); 53 } 54 55 void show_credential(krb5_context, krb5_creds *, krb5_ccache); 56 57 /* modifies only the cc_other, the algorithm may look a bit funny, 58 but I had to do it this way, since remove function did not come 59 with k5 beta 3 release. 60 */ 61 62 krb5_error_code 63 krb5_ccache_copy(krb5_context context, krb5_ccache cc_def, 64 krb5_principal target_principal, krb5_ccache cc_target, 65 krb5_boolean restrict_creds, krb5_principal primary_principal, 66 krb5_boolean *stored) 67 { 68 krb5_error_code retval=0; 69 krb5_creds ** cc_def_creds_arr = NULL; 70 krb5_creds ** cc_other_creds_arr = NULL; 71 72 if (ks_ccache_is_initialized(context, cc_def)) { 73 retval = krb5_get_nonexp_tkts(context, cc_def, &cc_def_creds_arr); 74 if (retval) 75 goto cleanup; 76 } 77 78 retval = krb5_cc_initialize(context, cc_target, target_principal); 79 if (retval) 80 goto cleanup; 81 82 if (restrict_creds) { 83 retval = krb5_store_some_creds(context, cc_target, cc_def_creds_arr, 84 cc_other_creds_arr, primary_principal, 85 stored); 86 } else { 87 *stored = krb5_find_princ_in_cred_list(context, cc_def_creds_arr, 88 primary_principal); 89 retval = krb5_store_all_creds(context, cc_target, cc_def_creds_arr, 90 cc_other_creds_arr); 91 } 92 93 cleanup: 94 free_creds_list(context, cc_def_creds_arr); 95 free_creds_list(context, cc_other_creds_arr); 96 return retval; 97 } 98 99 100 krb5_error_code 101 krb5_store_all_creds(krb5_context context, krb5_ccache cc, 102 krb5_creds **creds_def, krb5_creds **creds_other) 103 { 104 105 int i = 0; 106 krb5_error_code retval = 0; 107 krb5_creds ** temp_creds= NULL; 108 109 110 if ((creds_def == NULL) && (creds_other == NULL)) 111 return 0; 112 113 if ((creds_def == NULL) && (creds_other != NULL)) 114 temp_creds = creds_other; 115 116 if ((creds_def != NULL) && (creds_other == NULL)) 117 temp_creds = creds_def; 118 119 120 if (temp_creds){ 121 while(temp_creds[i]){ 122 if ((retval= krb5_cc_store_cred(context, cc, 123 temp_creds[i]))){ 124 return retval; 125 } 126 i++; 127 } 128 } 129 else { /* both arrays have elements in them */ 130 131 return KRB5KRB_ERR_GENERIC; 132 133 /************ while(creds_other[i]){ 134 cmp = FALSE; 135 j = 0; 136 while(creds_def[j]){ 137 cmp = compare_creds(creds_other[i],creds_def[j]); 138 139 if( cmp == TRUE) break; 140 141 j++; 142 } 143 if (cmp == FALSE){ 144 if (retval= krb5_cc_store_cred(context, cc, 145 creds_other[i])){ 146 return retval; 147 } 148 } 149 i ++; 150 } 151 152 i=0; 153 while(creds_def[i]){ 154 if (retval= krb5_cc_store_cred(context, cc, 155 creds_def[i])){ 156 return retval; 157 } 158 i++; 159 } 160 161 **************/ 162 } 163 return 0; 164 } 165 166 krb5_boolean 167 compare_creds(krb5_context context, krb5_creds *cred1, krb5_creds *cred2) 168 { 169 krb5_boolean retval; 170 171 retval = krb5_principal_compare (context, cred1->client, cred2->client); 172 173 if (retval == TRUE) 174 retval = krb5_principal_compare (context, cred1->server, cred2->server); 175 176 return retval; 177 } 178 179 krb5_error_code 180 krb5_get_nonexp_tkts(krb5_context context, krb5_ccache cc, 181 krb5_creds ***creds_array) 182 { 183 184 krb5_creds creds, temp_tktq, temp_tkt; 185 krb5_creds **temp_creds = NULL; 186 krb5_error_code retval=0; 187 krb5_cc_cursor cur; 188 int count = 0; 189 int chunk_count = 1; 190 191 temp_creds = xcalloc(CHUNK, sizeof(*temp_creds)); 192 memset(&temp_tktq, 0, sizeof(temp_tktq)); 193 memset(&temp_tkt, 0, sizeof(temp_tkt)); 194 memset(&creds, 0, sizeof(creds)); 195 196 /* initialize the cursor */ 197 retval = krb5_cc_start_seq_get(context, cc, &cur); 198 if (retval) 199 goto cleanup; 200 201 while (!(retval = krb5_cc_next_cred(context, cc, &cur, &creds))){ 202 203 if (!krb5_is_config_principal(context, creds.server) && 204 (retval = krb5_check_exp(context, creds.times))){ 205 krb5_free_cred_contents(context, &creds); 206 if (retval != KRB5KRB_AP_ERR_TKT_EXPIRED){ 207 goto cleanup; 208 } 209 if (auth_debug){ 210 fprintf(stderr,"krb5_ccache_copy: CREDS EXPIRED:\n"); 211 fputs(" Valid starting Expires Service principal\n",stdout); 212 show_credential(context, &creds, cc); 213 fprintf(stderr,"\n"); 214 } 215 } 216 else { /* these credentials didn't expire */ 217 retval = krb5_copy_creds(context, &creds, &temp_creds[count]); 218 krb5_free_cred_contents(context, &creds); 219 temp_creds[count+1] = NULL; 220 if (retval) 221 goto cleanup; 222 count ++; 223 224 if (count == (chunk_count * CHUNK -1)){ 225 chunk_count ++; 226 227 temp_creds = xrealloc(temp_creds, 228 chunk_count * CHUNK * 229 sizeof(*temp_creds)); 230 } 231 } 232 233 } 234 235 temp_creds[count] = NULL; 236 *creds_array = temp_creds; 237 temp_creds = NULL; 238 239 if (retval == KRB5_CC_END) { 240 retval = krb5_cc_end_seq_get(context, cc, &cur); 241 } 242 243 cleanup: 244 free_creds_list(context, temp_creds); 245 return retval; 246 } 247 248 krb5_error_code 249 krb5_check_exp(krb5_context context, krb5_ticket_times tkt_time) 250 { 251 krb5_error_code retval =0; 252 krb5_timestamp currenttime; 253 254 if ((retval = krb5_timeofday (context, ¤ttime))){ 255 return retval; 256 } 257 if (auth_debug){ 258 fprintf(stderr,"krb5_check_exp: the krb5_clockskew is %d \n", 259 context->clockskew); 260 261 fprintf(stderr,"krb5_check_exp: currenttime - endtime %d \n", 262 ts_delta(currenttime, tkt_time.endtime)); 263 264 } 265 266 if (ts_after(currenttime, ts_incr(tkt_time.endtime, context->clockskew))) { 267 retval = KRB5KRB_AP_ERR_TKT_EXPIRED ; 268 return retval; 269 } 270 271 return 0; 272 } 273 274 char * 275 flags_string(krb5_creds *cred) 276 { 277 static char buf[32]; 278 int i = 0; 279 280 if (cred->ticket_flags & TKT_FLG_FORWARDABLE) 281 buf[i++] = 'F'; 282 if (cred->ticket_flags & TKT_FLG_FORWARDED) 283 buf[i++] = 'f'; 284 if (cred->ticket_flags & TKT_FLG_PROXIABLE) 285 buf[i++] = 'P'; 286 if (cred->ticket_flags & TKT_FLG_PROXY) 287 buf[i++] = 'p'; 288 if (cred->ticket_flags & TKT_FLG_MAY_POSTDATE) 289 buf[i++] = 'D'; 290 if (cred->ticket_flags & TKT_FLG_POSTDATED) 291 buf[i++] = 'd'; 292 if (cred->ticket_flags & TKT_FLG_INVALID) 293 buf[i++] = 'i'; 294 if (cred->ticket_flags & TKT_FLG_RENEWABLE) 295 buf[i++] = 'R'; 296 if (cred->ticket_flags & TKT_FLG_INITIAL) 297 buf[i++] = 'I'; 298 if (cred->ticket_flags & TKT_FLG_HW_AUTH) 299 buf[i++] = 'H'; 300 if (cred->ticket_flags & TKT_FLG_PRE_AUTH) 301 buf[i++] = 'A'; 302 buf[i] = '\0'; 303 return(buf); 304 } 305 306 void 307 printtime(krb5_timestamp ts) 308 { 309 char fmtbuf[18], fill = ' '; 310 311 if (!krb5_timestamp_to_sfstring(ts, fmtbuf, sizeof(fmtbuf), &fill)) 312 printf("%s", fmtbuf); 313 } 314 315 void 316 show_credential(krb5_context context, krb5_creds *cred, krb5_ccache cc) 317 { 318 krb5_error_code retval; 319 char *name = NULL, *sname = NULL, *defname = NULL, *flags; 320 int first = 1; 321 krb5_principal princ = NULL; 322 int show_flags =1; 323 324 retval = krb5_unparse_name(context, cred->client, &name); 325 if (retval) { 326 com_err(prog_name, retval, _("while unparsing client name")); 327 goto cleanup; 328 } 329 retval = krb5_unparse_name(context, cred->server, &sname); 330 if (retval) { 331 com_err(prog_name, retval, _("while unparsing server name")); 332 goto cleanup; 333 } 334 335 if ((retval = krb5_cc_get_principal(context, cc, &princ))) { 336 com_err(prog_name, retval, _("while retrieving principal name")); 337 goto cleanup; 338 } 339 if ((retval = krb5_unparse_name(context, princ, &defname))) { 340 com_err(prog_name, retval, _("while unparsing principal name")); 341 goto cleanup; 342 } 343 344 if (!cred->times.starttime) 345 cred->times.starttime = cred->times.authtime; 346 347 printtime(cred->times.starttime); 348 putchar(' '); putchar(' '); 349 printtime(cred->times.endtime); 350 putchar(' '); putchar(' '); 351 352 printf("%s\n", sname); 353 354 if (strcmp(name, defname)) { 355 printf(_("\tfor client %s"), name); 356 first = 0; 357 } 358 359 if (cred->times.renew_till) { 360 if (first) 361 fputs("\t",stdout); 362 else 363 fputs(", ",stdout); 364 fputs(_("renew until "), stdout); 365 printtime(cred->times.renew_till); 366 } 367 if (show_flags) { 368 flags = flags_string(cred); 369 if (flags && *flags) { 370 if (first) 371 fputs("\t",stdout); 372 else 373 fputs(", ",stdout); 374 printf(_("Flags: %s"), flags); 375 first = 0; 376 } 377 } 378 putchar('\n'); 379 380 cleanup: 381 free(name); 382 free(sname); 383 free(defname); 384 krb5_free_principal(context, princ); 385 } 386 387 /* Create a random string suitable for a filename extension. */ 388 krb5_error_code 389 gen_sym(krb5_context context, char **sym_out) 390 { 391 krb5_error_code retval; 392 char bytes[6], *p, *sym; 393 krb5_data data = make_data(bytes, sizeof(bytes)); 394 395 *sym_out = NULL; 396 retval = krb5_c_random_make_octets(context, &data); 397 if (retval) 398 return retval; 399 sym = k5_base64_encode(data.data, data.length); 400 if (sym == NULL) 401 return ENOMEM; 402 /* Tweak the output alphabet just a bit. */ 403 while ((p = strchr(sym, '/')) != NULL) 404 *p = '_'; 405 while ((p = strchr(sym, '+')) != NULL) 406 *p = '-'; 407 *sym_out = sym; 408 return 0; 409 } 410 411 krb5_error_code 412 krb5_ccache_overwrite(krb5_context context, krb5_ccache ccs, krb5_ccache cct, 413 krb5_principal primary_principal) 414 { 415 krb5_error_code retval=0; 416 krb5_principal defprinc = NULL, princ; 417 krb5_creds ** ccs_creds_arr = NULL; 418 419 if (ks_ccache_is_initialized(context, ccs)) { 420 retval = krb5_get_nonexp_tkts(context, ccs, &ccs_creds_arr); 421 if (retval) 422 goto cleanup; 423 } 424 425 retval = krb5_cc_get_principal(context, cct, &defprinc); 426 princ = (retval == 0) ? defprinc : primary_principal; 427 retval = krb5_cc_initialize(context, cct, princ); 428 if (retval) 429 goto cleanup; 430 431 retval = krb5_store_all_creds(context, cct, ccs_creds_arr, NULL); 432 433 cleanup: 434 free_creds_list(context, ccs_creds_arr); 435 krb5_free_principal(context, defprinc); 436 return retval; 437 } 438 439 krb5_error_code 440 krb5_store_some_creds(krb5_context context, krb5_ccache cc, 441 krb5_creds **creds_def, krb5_creds **creds_other, 442 krb5_principal prst, krb5_boolean *stored) 443 { 444 445 int i = 0; 446 krb5_error_code retval = 0; 447 krb5_creds ** temp_creds= NULL; 448 krb5_boolean temp_stored = FALSE; 449 450 451 if ((creds_def == NULL) && (creds_other == NULL)) 452 return 0; 453 454 if ((creds_def == NULL) && (creds_other != NULL)) 455 temp_creds = creds_other; 456 457 if ((creds_def != NULL) && (creds_other == NULL)) 458 temp_creds = creds_def; 459 460 461 if (temp_creds){ 462 while(temp_creds[i]){ 463 if (krb5_principal_compare(context, 464 temp_creds[i]->client, 465 prst)== TRUE) { 466 467 if ((retval = krb5_cc_store_cred(context, 468 cc,temp_creds[i]))){ 469 return retval; 470 } 471 temp_stored = TRUE; 472 } 473 474 i++; 475 } 476 } 477 else { /* both arrays have elements in them */ 478 return KRB5KRB_ERR_GENERIC; 479 } 480 481 *stored = temp_stored; 482 return 0; 483 } 484 485 krb5_error_code 486 krb5_ccache_filter(krb5_context context, krb5_ccache cc, krb5_principal prst) 487 { 488 489 krb5_error_code retval=0; 490 krb5_principal temp_principal = NULL; 491 krb5_creds ** cc_creds_arr = NULL; 492 const char * cc_name; 493 krb5_boolean stored; 494 495 if (!ks_ccache_is_initialized(context, cc)) 496 return 0; 497 498 if (auth_debug) { 499 cc_name = krb5_cc_get_name(context, cc); 500 fprintf(stderr, "putting cache %s through a filter for -z option\n", 501 cc_name); 502 } 503 504 retval = krb5_get_nonexp_tkts(context, cc, &cc_creds_arr); 505 if (retval) 506 goto cleanup; 507 508 retval = krb5_cc_get_principal(context, cc, &temp_principal); 509 if (retval) 510 goto cleanup; 511 512 retval = krb5_cc_initialize(context, cc, temp_principal); 513 if (retval) 514 goto cleanup; 515 516 retval = krb5_store_some_creds(context, cc, cc_creds_arr, NULL, prst, 517 &stored); 518 519 cleanup: 520 free_creds_list(context, cc_creds_arr); 521 krb5_free_principal(context, temp_principal); 522 return retval; 523 } 524 525 krb5_boolean 526 krb5_find_princ_in_cred_list(krb5_context context, krb5_creds **creds_list, 527 krb5_principal princ) 528 { 529 530 int i = 0; 531 krb5_boolean temp_stored = FALSE; 532 533 if (creds_list){ 534 while(creds_list[i]){ 535 if (krb5_principal_compare(context, 536 creds_list[i]->client, 537 princ)== TRUE){ 538 temp_stored = TRUE; 539 break; 540 } 541 542 i++; 543 } 544 } 545 546 return temp_stored; 547 } 548 549 krb5_error_code 550 krb5_find_princ_in_cache(krb5_context context, krb5_ccache cc, 551 krb5_principal princ, krb5_boolean *found) 552 { 553 krb5_error_code retval = 0; 554 krb5_creds ** creds_list = NULL; 555 556 if (ks_ccache_is_initialized(context, cc)) { 557 retval = krb5_get_nonexp_tkts(context, cc, &creds_list); 558 if (retval) 559 goto cleanup; 560 } 561 562 *found = krb5_find_princ_in_cred_list(context, creds_list, princ); 563 564 cleanup: 565 free_creds_list(context, creds_list); 566 return retval; 567 } 568 569 krb5_boolean 570 ks_ccache_name_is_initialized(krb5_context context, const char *cctag) 571 { 572 krb5_boolean result; 573 krb5_ccache cc; 574 575 if (krb5_cc_resolve(context, cctag, &cc) != 0) 576 return FALSE; 577 result = ks_ccache_is_initialized(context, cc); 578 krb5_cc_close(context, cc); 579 580 return result; 581 } 582 583 krb5_boolean 584 ks_ccache_is_initialized(krb5_context context, krb5_ccache cc) 585 { 586 krb5_principal princ; 587 krb5_error_code retval; 588 589 if (cc == NULL) 590 return FALSE; 591 592 retval = krb5_cc_get_principal(context, cc, &princ); 593 if (retval == 0) 594 krb5_free_principal(context, princ); 595 596 return retval == 0; 597 } 598