1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* lib/krb5/ccache/t_cc.c */ 3 /* 4 * Copyright 2000 by the Massachusetts Institute of Technology. 5 * All Rights Reserved. 6 * 7 * Export of this software from the United States of America may 8 * require a specific license from the United States Government. 9 * It is the responsibility of any person or organization contemplating 10 * export to obtain such a license before exporting. 11 * 12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 13 * distribute this software and its documentation for any purpose and 14 * without fee is hereby granted, provided that the above copyright 15 * notice appear in all copies and that both that copyright notice and 16 * this permission notice appear in supporting documentation, and that 17 * the name of M.I.T. not be used in advertising or publicity pertaining 18 * to distribution of the software without specific, written prior 19 * permission. Furthermore if you modify this software you must label 20 * your software as modified software and not distribute it in such a 21 * fashion that it might be confused with the original M.I.T. software. 22 * M.I.T. makes no representations about the suitability of 23 * this software for any purpose. It is provided "as is" without express 24 * or implied warranty. 25 */ 26 27 #include "k5-int.h" 28 #include "cc-int.h" 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include "autoconf.h" 32 #ifdef HAVE_UNISTD_H 33 #include <unistd.h> 34 #endif 35 #include "com_err.h" 36 37 #define KRB5_OK 0 38 39 krb5_creds test_creds, test_creds2; 40 41 int debug=0; 42 43 static void 44 init_structs(void) 45 { 46 static int add=0x12345; 47 48 static krb5_address addr; 49 50 static krb5_address *addrs[] = { 51 &addr, 52 0, 53 }; 54 55 addr.magic = KV5M_ADDRESS; 56 addr.addrtype = ADDRTYPE_INET; 57 addr.length = 4; 58 addr.contents = (krb5_octet *) &add; 59 60 test_creds.magic = KV5M_CREDS; 61 test_creds.client = NULL; 62 test_creds.server = NULL; 63 64 test_creds.keyblock.magic = KV5M_KEYBLOCK; 65 test_creds.keyblock.contents = 0; 66 test_creds.keyblock.enctype = 1; 67 test_creds.keyblock.length = 1; 68 test_creds.keyblock.contents = (unsigned char *) "1"; 69 test_creds.times.authtime = 1111; 70 test_creds.times.starttime = 2222; 71 test_creds.times.endtime = 3333; 72 test_creds.times.renew_till = 4444; 73 test_creds.is_skey = 1; 74 test_creds.ticket_flags = 5555; 75 test_creds.addresses = addrs; 76 77 #define SET_TICKET(ent, str) {ent.magic = KV5M_DATA; ent.length = sizeof(str); ent.data = str;} 78 SET_TICKET(test_creds.ticket, "This is ticket 1"); 79 SET_TICKET(test_creds.second_ticket, "This is ticket 2"); 80 test_creds.authdata = NULL; 81 } 82 83 static krb5_error_code 84 init_test_cred(krb5_context context) 85 { 86 krb5_error_code kret; 87 unsigned int i; 88 krb5_authdata *a; 89 #define REALM "REALM" 90 kret = krb5_build_principal(context, &test_creds.client, sizeof(REALM), REALM, 91 "client-comp1", "client-comp2", NULL); 92 if(kret) 93 return kret; 94 95 kret = krb5_build_principal(context, &test_creds.server, sizeof(REALM), REALM, 96 "server-comp1", "server-comp2", NULL); 97 if(kret) { 98 krb5_free_principal(context, test_creds.client); 99 test_creds.client = 0; 100 goto cleanup; 101 } 102 103 test_creds.authdata = malloc (3 * sizeof(krb5_authdata *)); 104 if (!test_creds.authdata) { 105 kret = ENOMEM; 106 goto cleanup; 107 } 108 109 for (i = 0 ; i <= 2 ; i++) { 110 test_creds.authdata[i] = 0; 111 } 112 a = (krb5_authdata *) malloc(sizeof(krb5_authdata)); 113 if(!a) { 114 kret = ENOMEM; 115 goto cleanup; 116 } 117 a->magic = KV5M_AUTHDATA; 118 a->ad_type = KRB5_AUTHDATA_IF_RELEVANT; 119 a->contents = (krb5_octet * ) malloc(1); 120 if(!a->contents) { 121 free(a); 122 kret = ENOMEM; 123 goto cleanup; 124 } 125 a->contents[0]=5; 126 a->length = 1; 127 test_creds.authdata[0] = a; 128 129 a = (krb5_authdata *) malloc(sizeof(krb5_authdata)); 130 if(!a) { 131 kret = ENOMEM; 132 goto cleanup; 133 } 134 a->magic = KV5M_AUTHDATA; 135 a->ad_type = KRB5_AUTHDATA_KDC_ISSUED; 136 a->contents = (krb5_octet * ) malloc(2); 137 if(!a->contents) { 138 free(a); 139 kret = ENOMEM; 140 goto cleanup; 141 } 142 a->contents[0]=4; 143 a->contents[1]=6; 144 a->length = 2; 145 test_creds.authdata[1] = a; 146 147 memcpy(&test_creds2, &test_creds, sizeof(test_creds)); 148 kret = krb5_build_principal(context, &test_creds2.server, sizeof(REALM), 149 REALM, "server-comp1", "server-comp3", NULL); 150 151 cleanup: 152 if(kret) { 153 if (test_creds.client) { 154 krb5_free_principal(context, test_creds.client); 155 test_creds.client = 0; 156 } 157 if (test_creds.server) { 158 krb5_free_principal(context, test_creds.server); 159 test_creds.server = 0; 160 161 } 162 if (test_creds.authdata) { 163 krb5_free_authdata(context, test_creds.authdata); 164 test_creds.authdata = 0; 165 } 166 } 167 168 return kret; 169 } 170 171 static void 172 free_test_cred(krb5_context context) 173 { 174 krb5_free_principal(context, test_creds.client); 175 176 krb5_free_principal(context, test_creds.server); 177 krb5_free_principal(context, test_creds2.server); 178 179 if(test_creds.authdata) { 180 krb5_free_authdata(context, test_creds.authdata); 181 test_creds.authdata = 0; 182 } 183 } 184 185 #define CHECK(kret,msg) \ 186 if (kret != KRB5_OK) { \ 187 com_err(msg, kret, ""); \ 188 fflush(stderr); \ 189 exit(1); \ 190 } else if(debug) printf("%s went ok\n", msg); 191 192 #define CHECK_STR(str,msg) \ 193 if (str == 0) { \ 194 com_err(msg, kret, ""); \ 195 exit(1); \ 196 } else if(debug) printf("%s went ok\n", msg); 197 198 #define CHECK_BOOL(expr,errstr,msg) \ 199 if (expr) { \ 200 fprintf(stderr, "%s %s\n", msg, errstr); \ 201 exit(1); \ 202 } else if(debug) printf("%s went ok\n", msg); 203 204 #define CHECK_FAIL(experr, kret, msg) \ 205 if (experr != kret) { CHECK(kret, msg);} 206 207 static void 208 check_num_entries(krb5_context context, krb5_ccache cache, int expected, 209 unsigned linenum) 210 { 211 krb5_error_code ret; 212 krb5_cc_cursor cursor; 213 krb5_creds creds; 214 int count = 0; 215 216 ret = krb5_cc_start_seq_get(context, cache, &cursor); 217 if (ret != 0) { 218 com_err("", ret, "(on line %d) - krb5_cc_start_seq_get", linenum); 219 fflush(stderr); 220 exit(1); 221 } 222 223 while (1) { 224 ret = krb5_cc_next_cred(context, cache, &cursor, &creds); 225 if (ret) 226 break; 227 228 count++; 229 krb5_free_cred_contents(context, &creds); 230 } 231 krb5_cc_end_seq_get(context, cache, &cursor); 232 if (ret != KRB5_CC_END) { 233 CHECK(ret, "counting entries in ccache"); 234 } 235 236 if (count != expected) { 237 com_err("", KRB5_FCC_INTERNAL, 238 "(on line %d) - count didn't match (expected %d, got %d)", 239 linenum, expected, count); 240 fflush(stderr); 241 exit(1); 242 } 243 } 244 245 static void 246 cc_test(krb5_context context, const char *name, krb5_flags flags) 247 { 248 krb5_ccache id, id2; 249 krb5_creds creds; 250 krb5_error_code kret; 251 krb5_cc_cursor cursor; 252 krb5_principal tmp; 253 krb5_flags matchflags = KRB5_TC_MATCH_IS_SKEY; 254 255 const char *c_name; 256 char newcache[300]; 257 char *save_type; 258 259 kret = init_test_cred(context); 260 CHECK(kret, "init_creds"); 261 262 kret = krb5_cc_resolve(context, name, &id); 263 CHECK(kret, "resolve"); 264 kret = krb5_cc_initialize(context, id, test_creds.client); 265 CHECK(kret, "initialize"); 266 267 c_name = krb5_cc_get_name(context, id); 268 CHECK_STR(c_name, "get_name"); 269 270 c_name = krb5_cc_get_type(context, id); 271 CHECK_STR(c_name, "get_type"); 272 save_type=strdup(c_name); 273 CHECK_STR(save_type, "copying type"); 274 275 kret = krb5_cc_store_cred(context, id, &test_creds); 276 CHECK(kret, "store"); 277 278 kret = krb5_cc_get_principal(context, id, &tmp); 279 CHECK(kret, "get_principal"); 280 281 CHECK_BOOL(krb5_realm_compare(context, tmp, test_creds.client) != TRUE, 282 "realms do not match", "realm_compare"); 283 284 285 CHECK_BOOL(krb5_principal_compare(context, tmp, test_creds.client) != TRUE, 286 "principals do not match", "principal_compare"); 287 288 krb5_free_principal(context, tmp); 289 290 kret = krb5_cc_set_flags (context, id, flags); 291 CHECK(kret, "set_flags"); 292 293 kret = krb5_cc_start_seq_get(context, id, &cursor); 294 CHECK(kret, "start_seq_get"); 295 kret = 0; 296 while (kret != KRB5_CC_END) { 297 if(debug) printf("Calling next_cred\n"); 298 kret = krb5_cc_next_cred(context, id, &cursor, &creds); 299 if(kret == KRB5_CC_END) { 300 if(debug) printf("next_cred: ok at end\n"); 301 } 302 else { 303 CHECK(kret, "next_cred"); 304 krb5_free_cred_contents(context, &creds); 305 } 306 307 } 308 kret = krb5_cc_end_seq_get(context, id, &cursor); 309 CHECK(kret, "end_seq_get"); 310 311 kret = krb5_cc_close(context, id); 312 CHECK(kret, "close"); 313 314 315 /* ------------------------------------------------- */ 316 kret = krb5_cc_resolve(context, name, &id); 317 CHECK(kret, "resolve2"); 318 319 { 320 /* Copy the cache test*/ 321 snprintf(newcache, sizeof(newcache), "%s.new", name); 322 kret = krb5_cc_resolve(context, newcache, &id2); 323 CHECK(kret, "resolve of new cache"); 324 325 /* This should fail as the new creds are not initialized */ 326 kret = krb5_cc_copy_creds(context, id, id2); 327 CHECK_FAIL(KRB5_FCC_NOFILE, kret, "copy_creds"); 328 329 kret = krb5_cc_initialize(context, id2, test_creds.client); 330 CHECK(kret, "initialize of id2"); 331 332 kret = krb5_cc_copy_creds(context, id, id2); 333 CHECK(kret, "copy_creds"); 334 335 kret = krb5_cc_destroy(context, id2); 336 CHECK(kret, "destroy new cache"); 337 } 338 339 /* Destroy the first cache */ 340 kret = krb5_cc_destroy(context, id); 341 CHECK(kret, "destroy"); 342 343 /* ----------------------------------------------------- */ 344 /* Tests the generate new code */ 345 kret = krb5_cc_new_unique(context, save_type, 346 NULL, &id2); 347 CHECK(kret, "new_unique"); 348 349 kret = krb5_cc_initialize(context, id2, test_creds.client); 350 CHECK(kret, "initialize"); 351 352 kret = krb5_cc_store_cred(context, id2, &test_creds); 353 CHECK(kret, "store"); 354 355 kret = krb5_cc_destroy(context, id2); 356 CHECK(kret, "destroy id2"); 357 358 /* ----------------------------------------------------- */ 359 /* Test credential removal */ 360 kret = krb5_cc_resolve(context, name, &id); 361 CHECK(kret, "resolving for remove"); 362 363 kret = krb5_cc_initialize(context, id, test_creds.client); 364 CHECK(kret, "initialize for remove"); 365 check_num_entries(context, id, 0, __LINE__); 366 367 kret = krb5_cc_store_cred(context, id, &test_creds); 368 CHECK(kret, "store for remove (first pass)"); 369 check_num_entries(context, id, 1, __LINE__); /* 1 */ 370 371 kret = krb5_cc_remove_cred(context, id, matchflags, &test_creds); 372 CHECK(kret, "removing credential (first pass)"); 373 check_num_entries(context, id, 0, __LINE__); /* empty */ 374 375 kret = krb5_cc_store_cred(context, id, &test_creds); 376 CHECK(kret, "first store for remove (second pass)"); 377 check_num_entries(context, id, 1, __LINE__); /* 1 */ 378 379 kret = krb5_cc_store_cred(context, id, &test_creds2); 380 CHECK(kret, "second store for remove (second pass)"); 381 check_num_entries(context, id, 2, __LINE__); /* 1, 2 */ 382 383 kret = krb5_cc_remove_cred(context, id, matchflags, &test_creds2); 384 CHECK(kret, "first remove (second pass)"); 385 check_num_entries(context, id, 1, __LINE__); /* 1 */ 386 387 kret = krb5_cc_store_cred(context, id, &test_creds2); 388 CHECK(kret, "third store for remove (second pass)"); 389 check_num_entries(context, id, 2, __LINE__); /* 1, 2 */ 390 391 kret = krb5_cc_remove_cred(context, id, matchflags, &test_creds); 392 CHECK(kret, "second remove (second pass)"); 393 check_num_entries(context, id, 1, __LINE__); /* 2 */ 394 395 kret = krb5_cc_remove_cred(context, id, matchflags, &test_creds2); 396 CHECK(kret, "third remove (second pass)"); 397 check_num_entries(context, id, 0, __LINE__); /* empty */ 398 399 kret = krb5_cc_destroy(context, id); 400 CHECK(kret, "destruction for remove"); 401 402 /* Test removal with iteration. */ 403 kret = krb5_cc_resolve(context, name, &id); 404 CHECK(kret, "resolving for remove-iter"); 405 406 kret = krb5_cc_initialize(context, id, test_creds.client); 407 CHECK(kret, "initialize for remove-iter"); 408 409 kret = krb5_cc_store_cred(context, id, &test_creds); 410 CHECK(kret, "first store for remove-iter"); 411 412 kret = krb5_cc_store_cred(context, id, &test_creds2); 413 CHECK(kret, "second store for remove-iter"); 414 415 kret = krb5_cc_start_seq_get(context, id, &cursor); 416 CHECK(kret, "start_seq_get for remove-iter"); 417 418 kret = krb5_cc_remove_cred(context, id, matchflags, &test_creds); 419 CHECK(kret, "remove for remove-iter"); 420 421 while (1) { 422 /* The removed credential may or may not be present in the cache - 423 * either behavior is technically correct. */ 424 kret = krb5_cc_next_cred(context, id, &cursor, &creds); 425 if (kret == KRB5_CC_END) 426 break; 427 CHECK(kret, "next_cred for remove-iter: %s"); 428 429 CHECK(creds.times.endtime == 0, "no-lifetime cred"); 430 431 krb5_free_cred_contents(context, &creds); 432 } 433 434 kret = krb5_cc_end_seq_get(context, id, &cursor); 435 CHECK(kret, "end_seq_get for remove-iter"); 436 437 kret = krb5_cc_destroy(context, id); 438 CHECK(kret, "destruction for remove-iter"); 439 440 free(save_type); 441 free_test_cred(context); 442 } 443 444 /* 445 * Checks if a credential type is registered with the library 446 */ 447 static int 448 check_registered(krb5_context context, const char *prefix) 449 { 450 char name[300]; 451 krb5_error_code kret; 452 krb5_ccache id; 453 454 snprintf(name, sizeof(name), "%s/tmp/cctest.%ld", prefix, (long) getpid()); 455 456 kret = krb5_cc_resolve(context, name, &id); 457 if(kret != KRB5_OK) { 458 if(kret == KRB5_CC_UNKNOWN_TYPE) 459 return 0; 460 com_err("Checking on credential type", kret, "%s", prefix); 461 fflush(stderr); 462 return 0; 463 } 464 465 kret = krb5_cc_close(context, id); 466 if(kret != KRB5_OK) { 467 com_err("Checking on credential type - closing", kret, "%s", prefix); 468 fflush(stderr); 469 } 470 471 return 1; 472 } 473 474 475 static void 476 do_test(krb5_context context, const char *prefix) 477 { 478 char name[300]; 479 480 snprintf(name, sizeof(name), "%s/tmp/cctest.%ld", prefix, (long) getpid()); 481 printf("Starting test on %s\n", name); 482 cc_test (context, name, 0); 483 cc_test (context, name, !0); 484 printf("Test on %s passed\n", name); 485 } 486 487 static void 488 test_misc(krb5_context context) 489 { 490 /* Tests for certain error returns */ 491 krb5_error_code kret; 492 krb5_ccache id; 493 const krb5_cc_ops *ops_save; 494 495 fprintf(stderr, "Testing miscellaneous error conditions\n"); 496 497 kret = krb5_cc_resolve(context, "unknown_method_ep:/tmp/name", &id); 498 if (kret != KRB5_CC_UNKNOWN_TYPE) { 499 CHECK(kret, "resolve unknown type"); 500 } 501 502 /* Test for not specifying a cache type with no defaults */ 503 ops_save = krb5_cc_dfl_ops; 504 krb5_cc_dfl_ops = 0; 505 506 kret = krb5_cc_resolve(context, "/tmp/e", &id); 507 if (kret != KRB5_CC_BADNAME) { 508 CHECK(kret, "resolve no builtin type"); 509 } 510 511 krb5_cc_dfl_ops = ops_save; 512 513 } 514 515 /* 516 * Regression tests for #8202. Because memory ccaches share objects between 517 * different handles to the same cache and between iterators and caches, 518 * historically there have been some bugs when those objects are released. 519 */ 520 static void 521 test_memory_concurrent(krb5_context context) 522 { 523 krb5_error_code kret; 524 krb5_ccache id1, id2; 525 krb5_cc_cursor cursor; 526 krb5_creds creds; 527 528 /* Create two handles to the same memory ccache and destroy them. */ 529 kret = krb5_cc_resolve(context, "MEMORY:x", &id1); 530 CHECK(kret, "resolve 1"); 531 kret = krb5_cc_resolve(context, "MEMORY:x", &id2); 532 CHECK(kret, "resolve 2"); 533 kret = krb5_cc_destroy(context, id1); 534 CHECK(kret, "destroy 1"); 535 kret = krb5_cc_destroy(context, id2); 536 CHECK(kret, "destroy 2"); 537 538 kret = init_test_cred(context); 539 CHECK(kret, "init_creds"); 540 541 /* Reinitialize the cache after creating an iterator for it, and verify 542 * that the iterator ends gracefully. */ 543 kret = krb5_cc_resolve(context, "MEMORY:x", &id1); 544 CHECK(kret, "resolve"); 545 kret = krb5_cc_initialize(context, id1, test_creds.client); 546 CHECK(kret, "initialize"); 547 kret = krb5_cc_store_cred(context, id1, &test_creds); 548 CHECK(kret, "store"); 549 kret = krb5_cc_start_seq_get(context, id1, &cursor); 550 CHECK(kret, "start_seq_get"); 551 kret = krb5_cc_initialize(context, id1, test_creds.client); 552 CHECK(kret, "initialize again"); 553 kret = krb5_cc_next_cred(context, id1, &cursor, &creds); 554 CHECK_BOOL(kret != KRB5_CC_END, "iterator should end", "next_cred"); 555 kret = krb5_cc_end_seq_get(context, id1, &cursor); 556 CHECK(kret, "end_seq_get"); 557 kret = krb5_cc_destroy(context, id1); 558 CHECK(kret, "destroy"); 559 560 free_test_cred(context); 561 } 562 563 /* Check that order is preserved during iteration. Not all cache types have 564 * this property. */ 565 static void 566 test_order(krb5_context context, const char *name) 567 { 568 krb5_error_code kret; 569 krb5_ccache id; 570 krb5_cc_cursor cursor; 571 krb5_creds creds; 572 573 kret = init_test_cred(context); 574 CHECK(kret, "init_creds"); 575 576 kret = krb5_cc_resolve(context, name, &id); 577 CHECK(kret, "resolve"); 578 kret = krb5_cc_initialize(context, id, test_creds.client); 579 CHECK(kret, "initialize"); 580 kret = krb5_cc_store_cred(context, id, &test_creds); 581 CHECK(kret, "store 1"); 582 kret = krb5_cc_store_cred(context, id, &test_creds2); 583 CHECK(kret, "store 2"); 584 585 kret = krb5_cc_start_seq_get(context, id, &cursor); 586 CHECK(kret, "start_seq_get"); 587 kret = krb5_cc_next_cred(context, id, &cursor, &creds); 588 CHECK(kret, "next_cred 1"); 589 CHECK_BOOL(krb5_principal_compare(context, creds.server, 590 test_creds.server) != TRUE, 591 "first cred does not match", "principal_compare"); 592 krb5_free_cred_contents(context, &creds); 593 594 kret = krb5_cc_next_cred(context, id, &cursor, &creds); 595 CHECK(kret, "next_cred 2"); 596 CHECK_BOOL(krb5_principal_compare(context, creds.server, 597 test_creds2.server) != TRUE, 598 "second cred does not match", "principal_compare"); 599 krb5_free_cred_contents(context, &creds); 600 601 krb5_cc_end_seq_get(context, id, &cursor); 602 krb5_cc_close(context, id); 603 free_test_cred(context); 604 } 605 606 extern const krb5_cc_ops krb5_mcc_ops; 607 extern const krb5_cc_ops krb5_fcc_ops; 608 609 int 610 main(void) 611 { 612 krb5_context context; 613 krb5_error_code kret; 614 615 if ((kret = krb5_init_context(&context))) { 616 printf("Couldn't initialize krb5 library: %s\n", 617 error_message(kret)); 618 exit(1); 619 } 620 621 kret = krb5_cc_register(context, &krb5_mcc_ops,0); 622 if(kret && kret != KRB5_CC_TYPE_EXISTS) { 623 CHECK(kret, "register_mem"); 624 } 625 626 kret = krb5_cc_register(context, &krb5_fcc_ops,0); 627 if(kret && kret != KRB5_CC_TYPE_EXISTS) { 628 CHECK(kret, "register_mem"); 629 } 630 631 /* Registering a second time tests for error return */ 632 kret = krb5_cc_register(context, &krb5_fcc_ops,0); 633 if(kret != KRB5_CC_TYPE_EXISTS) { 634 CHECK(kret, "register_mem"); 635 } 636 637 /* Registering with override should work */ 638 kret = krb5_cc_register(context, &krb5_fcc_ops,1); 639 CHECK(kret, "register_mem override"); 640 641 init_structs(); 642 643 test_misc(context); 644 do_test(context, ""); 645 646 if (check_registered(context, "KEYRING:process:")) 647 do_test(context, "KEYRING:process:"); 648 else 649 printf("Skipping KEYRING: test - unregistered type\n"); 650 651 do_test(context, "MEMORY:"); 652 do_test(context, "FILE:"); 653 654 test_memory_concurrent(context); 655 656 test_order(context, "MEMORY:order"); 657 658 krb5_free_context(context); 659 return 0; 660 } 661