1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* kdc/t_replay.c - tests for replay.c */ 3 /* 4 * Copyright (C) 2016 by the Massachusetts Institute of Technology. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 14 * * Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in 16 * the documentation and/or other materials provided with the 17 * distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 * OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 /* 34 * Unit tests for the lookaside cache in replay.c 35 */ 36 37 #ifndef NOCACHE 38 39 #include "k5-cmocka.h" 40 41 /* For wrapping functions */ 42 #include "k5-int.h" 43 #include "krb5.h" 44 45 /* 46 * Wrapper functions 47 */ 48 49 static krb5_error_code 50 __wrap_krb5_timeofday(krb5_context context, krb5_timestamp *timeret) 51 { 52 *timeret = (krb5_timestamp)mock(); 53 return (krb5_error_code)mock(); 54 } 55 56 #define krb5_timeofday __wrap_krb5_timeofday 57 58 #include "replay.c" 59 60 #undef krb5_timeofday 61 62 #define SEED 0x6F03A219 63 #define replay_unit_test(fn) \ 64 cmocka_unit_test_setup_teardown(fn, setup_lookaside, destroy_lookaside) 65 66 /* 67 * Helper functions 68 */ 69 70 static void 71 time_return(krb5_timestamp time, krb5_error_code err) 72 { 73 will_return(__wrap_krb5_timeofday, time); 74 will_return(__wrap_krb5_timeofday, err); 75 } 76 77 /* 78 * setup/teardown functions 79 */ 80 81 static int 82 global_setup(void **state) 83 { 84 krb5_error_code ret; 85 krb5_context context = NULL; 86 87 ret = krb5_init_context(&context); 88 if (ret) 89 return ret; 90 91 *state = context; 92 return 0; 93 } 94 95 static int 96 global_teardown(void **state) 97 { 98 krb5_free_context(*state); 99 return 0; 100 } 101 102 static int 103 setup_lookaside(void **state) 104 { 105 krb5_error_code ret; 106 krb5_context context = *state; 107 108 ret = kdc_init_lookaside(context); 109 if (ret) 110 return ret; 111 112 /* Ensure some vars are all set to initial values */ 113 hits = 0; 114 calls = 0; 115 max_hits_per_entry = 0; 116 num_entries = 0; 117 total_size = 0; 118 119 return 0; 120 } 121 122 static int 123 destroy_lookaside(void **state) 124 { 125 kdc_free_lookaside(*state); 126 return 0; 127 } 128 129 /* 130 * entry_size tests 131 */ 132 133 static void 134 test_entry_size_no_response(void **state) 135 { 136 size_t result; 137 const krb5_data req = string2data("I'm a test request"); 138 139 result = entry_size(&req, NULL); 140 assert_int_equal(result, sizeof(struct entry) + 18); 141 } 142 143 static void 144 test_entry_size_w_response(void **state) 145 { 146 size_t result; 147 const krb5_data req = string2data("I'm a test request"); 148 const krb5_data rep = string2data("I'm a test response"); 149 150 result = entry_size(&req, &rep); 151 assert_int_equal(result, sizeof(struct entry) + 18 + 19); 152 } 153 154 /* 155 * insert_entry tests 156 */ 157 158 static void 159 test_insert_entry(void **state) 160 { 161 struct entry *e; 162 krb5_context context = *state; 163 krb5_data req = string2data("I'm a test request"); 164 krb5_data rep = string2data("I'm a test response"); 165 166 e = insert_entry(context, &req, &rep, 15); 167 168 assert_ptr_equal(k5_hashtab_get(hash_table, req.data, req.length), e); 169 assert_ptr_equal(K5_TAILQ_FIRST(&expiration_queue), e); 170 assert_true(data_eq(e->req_packet, req)); 171 assert_true(data_eq(e->reply_packet, rep)); 172 assert_int_equal(e->timein, 15); 173 } 174 175 static void 176 test_insert_entry_no_response(void **state) 177 { 178 struct entry *e; 179 krb5_context context = *state; 180 krb5_data req = string2data("I'm a test request"); 181 182 e = insert_entry(context, &req, NULL, 10); 183 184 assert_ptr_equal(k5_hashtab_get(hash_table, req.data, req.length), e); 185 assert_ptr_equal(K5_TAILQ_FIRST(&expiration_queue), e); 186 assert_true(data_eq(e->req_packet, req)); 187 assert_int_equal(e->reply_packet.length, 0); 188 assert_int_equal(e->timein, 10); 189 } 190 191 static void 192 test_insert_entry_multiple(void **state) 193 { 194 struct entry *e1, *e2; 195 krb5_context context = *state; 196 krb5_data req1 = string2data("I'm a test request"); 197 krb5_data rep1 = string2data("I'm a test response"); 198 krb5_data req2 = string2data("I'm a different test request"); 199 200 e1 = insert_entry(context, &req1, &rep1, 20); 201 202 assert_ptr_equal(k5_hashtab_get(hash_table, req1.data, req1.length), e1); 203 assert_ptr_equal(K5_TAILQ_FIRST(&expiration_queue), e1); 204 assert_true(data_eq(e1->req_packet, req1)); 205 assert_true(data_eq(e1->reply_packet, rep1)); 206 assert_int_equal(e1->timein, 20); 207 208 e2 = insert_entry(context, &req2, NULL, 30); 209 210 assert_ptr_equal(k5_hashtab_get(hash_table, req2.data, req2.length), e2); 211 assert_ptr_equal(K5_TAILQ_LAST(&expiration_queue,entry_queue), e2); 212 assert_true(data_eq(e2->req_packet, req2)); 213 assert_int_equal(e2->reply_packet.length, 0); 214 assert_int_equal(e2->timein, 30); 215 } 216 217 /* 218 * discard_entry tests 219 */ 220 221 static void 222 test_discard_entry(void **state) 223 { 224 struct entry *e; 225 krb5_context context = *state; 226 krb5_data req = string2data("I'm a test request"); 227 krb5_data rep = string2data("I'm a test response"); 228 229 e = insert_entry(context, &req, &rep, 0); 230 discard_entry(context, e); 231 232 assert_null(k5_hashtab_get(hash_table, req.data, req.length)); 233 assert_int_equal(num_entries, 0); 234 assert_int_equal(total_size, 0); 235 } 236 237 static void 238 test_discard_entry_no_response(void **state) 239 { 240 struct entry *e; 241 krb5_context context = *state; 242 krb5_data req = string2data("I'm a test request"); 243 244 e = insert_entry(context, &req, NULL, 0); 245 discard_entry(context, e); 246 247 assert_null(k5_hashtab_get(hash_table, req.data, req.length)); 248 assert_int_equal(num_entries, 0); 249 assert_int_equal(total_size, 0); 250 } 251 252 /* 253 * kdc_remove_lookaside tests 254 */ 255 256 static void 257 test_kdc_remove_lookaside(void **state) 258 { 259 krb5_context context = *state; 260 krb5_data req = string2data("I'm a test request"); 261 krb5_data rep = string2data("I'm a test response"); 262 263 insert_entry(context, &req, &rep, 0); 264 kdc_remove_lookaside(context, &req); 265 266 assert_null(k5_hashtab_get(hash_table, req.data, req.length)); 267 assert_int_equal(num_entries, 0); 268 assert_int_equal(total_size, 0); 269 } 270 271 static void 272 test_kdc_remove_lookaside_empty_cache(void **state) 273 { 274 krb5_context context = *state; 275 krb5_data req = string2data("I'm a test request"); 276 277 assert_int_equal(num_entries, 0); 278 kdc_remove_lookaside(context, &req); 279 280 assert_int_equal(num_entries, 0); 281 assert_int_equal(total_size, 0); 282 } 283 284 static void 285 test_kdc_remove_lookaside_unknown(void **state) 286 { 287 struct entry *e; 288 krb5_context context = *state; 289 krb5_data req1 = string2data("I'm a test request"); 290 krb5_data rep1 = string2data("I'm a test response"); 291 krb5_data req2 = string2data("I'm a different test request"); 292 293 e = insert_entry(context, &req1, &rep1, 0); 294 kdc_remove_lookaside(context, &req2); 295 296 assert_ptr_equal(k5_hashtab_get(hash_table, req1.data, req1.length), e); 297 assert_int_equal(num_entries, 1); 298 assert_int_equal(total_size, entry_size(&req1, &rep1)); 299 } 300 301 static void 302 test_kdc_remove_lookaside_multiple(void **state) 303 { 304 struct entry *e1; 305 krb5_context context = *state; 306 krb5_data req1 = string2data("I'm a test request"); 307 krb5_data rep1 = string2data("I'm a test response"); 308 krb5_data req2 = string2data("I'm a different test request"); 309 310 e1 = insert_entry(context, &req1, &rep1, 0); 311 insert_entry(context, &req2, NULL, 0); 312 313 kdc_remove_lookaside(context, &req2); 314 315 assert_null(k5_hashtab_get(hash_table, req2.data, req2.length)); 316 assert_ptr_equal(k5_hashtab_get(hash_table, req1.data, req1.length), e1); 317 assert_int_equal(num_entries, 1); 318 assert_int_equal(total_size, entry_size(&req1, &rep1)); 319 320 kdc_remove_lookaside(context, &req1); 321 322 assert_null(k5_hashtab_get(hash_table, req1.data, req1.length)); 323 assert_int_equal(num_entries, 0); 324 assert_int_equal(total_size, 0); 325 } 326 327 /* 328 * kdc_check_lookaside tests 329 */ 330 331 static void 332 test_kdc_check_lookaside_hit(void **state) 333 { 334 struct entry *e; 335 krb5_boolean result; 336 krb5_data *result_data; 337 krb5_context context = *state; 338 krb5_data req = string2data("I'm a test request"); 339 krb5_data rep = string2data("I'm a test response"); 340 341 e = insert_entry(context, &req, &rep, 0); 342 343 result = kdc_check_lookaside(context, &req, &result_data); 344 345 assert_true(result); 346 assert_true(data_eq(rep, *result_data)); 347 assert_int_equal(hits, 1); 348 assert_int_equal(e->num_hits, 1); 349 350 krb5_free_data(context, result_data); 351 } 352 353 static void 354 test_kdc_check_lookaside_no_hit(void **state) 355 { 356 krb5_boolean result; 357 krb5_data *result_data; 358 krb5_context context = *state; 359 krb5_data req = string2data("I'm a test request"); 360 361 result = kdc_check_lookaside(context, &req, &result_data); 362 363 assert_false(result); 364 assert_null(result_data); 365 assert_int_equal(hits, 0); 366 } 367 368 static void 369 test_kdc_check_lookaside_empty(void **state) 370 { 371 krb5_boolean result; 372 krb5_data *result_data; 373 krb5_context context = *state; 374 krb5_data req = string2data("I'm a test request"); 375 376 /* Set result_data so we can verify that it is reset to NULL. */ 377 result_data = &req; 378 result = kdc_check_lookaside(context, &req, &result_data); 379 380 assert_false(result); 381 assert_null(result_data); 382 assert_int_equal(hits, 0); 383 } 384 385 static void 386 test_kdc_check_lookaside_no_response(void **state) 387 { 388 struct entry *e; 389 krb5_boolean result; 390 krb5_data *result_data; 391 krb5_context context = *state; 392 krb5_data req = string2data("I'm a test request"); 393 394 e = insert_entry(context, &req, NULL, 0); 395 396 /* Set result_data so we can verify that it is reset to NULL. */ 397 result_data = &req; 398 result = kdc_check_lookaside(context, &req, &result_data); 399 400 assert_true(result); 401 assert_null(result_data); 402 assert_int_equal(hits, 1); 403 assert_int_equal(e->num_hits, 1); 404 } 405 406 static void 407 test_kdc_check_lookaside_hit_multiple(void **state) 408 { 409 struct entry *e1, *e2; 410 krb5_boolean result; 411 krb5_data *result_data; 412 krb5_context context = *state; 413 krb5_data req1 = string2data("I'm a test request"); 414 krb5_data rep1 = string2data("I'm a test response"); 415 krb5_data req2 = string2data("I'm a different test request"); 416 417 e1 = insert_entry(context, &req1, &rep1, 0); 418 e2 = insert_entry(context, &req2, NULL, 0); 419 420 result = kdc_check_lookaside(context, &req1, &result_data); 421 422 assert_true(result); 423 assert_true(data_eq(rep1, *result_data)); 424 assert_int_equal(hits, 1); 425 assert_int_equal(e1->num_hits, 1); 426 assert_int_equal(e2->num_hits, 0); 427 428 krb5_free_data(context, result_data); 429 430 /* Set result_data so we can verify that it is reset to NULL. */ 431 result_data = &req1; 432 result = kdc_check_lookaside(context, &req2, &result_data); 433 434 assert_true(result); 435 assert_null(result_data); 436 assert_int_equal(hits, 2); 437 assert_int_equal(e1->num_hits, 1); 438 assert_int_equal(e2->num_hits, 1); 439 } 440 441 /* 442 * kdc_insert_lookaside tests 443 */ 444 445 static void 446 test_kdc_insert_lookaside_single(void **state) 447 { 448 krb5_context context = *state; 449 krb5_data req = string2data("I'm a test request"); 450 krb5_data rep = string2data("I'm a test response"); 451 struct entry *hash_ent, *exp_ent; 452 453 time_return(0, 0); 454 kdc_insert_lookaside(context, &req, &rep); 455 456 hash_ent = k5_hashtab_get(hash_table, req.data, req.length); 457 assert_non_null(hash_ent); 458 assert_true(data_eq(hash_ent->req_packet, req)); 459 assert_true(data_eq(hash_ent->reply_packet, rep)); 460 exp_ent = K5_TAILQ_FIRST(&expiration_queue); 461 assert_true(data_eq(exp_ent->req_packet, req)); 462 assert_true(data_eq(exp_ent->reply_packet, rep)); 463 assert_int_equal(num_entries, 1); 464 assert_int_equal(total_size, entry_size(&req, &rep)); 465 } 466 467 static void 468 test_kdc_insert_lookaside_no_reply(void **state) 469 { 470 krb5_context context = *state; 471 krb5_data req = string2data("I'm a test request"); 472 struct entry *hash_ent, *exp_ent; 473 474 time_return(0, 0); 475 kdc_insert_lookaside(context, &req, NULL); 476 477 hash_ent = k5_hashtab_get(hash_table, req.data, req.length); 478 assert_non_null(hash_ent); 479 assert_true(data_eq(hash_ent->req_packet, req)); 480 assert_int_equal(hash_ent->reply_packet.length, 0); 481 exp_ent = K5_TAILQ_FIRST(&expiration_queue); 482 assert_true(data_eq(exp_ent->req_packet, req)); 483 assert_int_equal(exp_ent->reply_packet.length, 0); 484 assert_int_equal(num_entries, 1); 485 assert_int_equal(total_size, entry_size(&req, NULL)); 486 } 487 488 static void 489 test_kdc_insert_lookaside_multiple(void **state) 490 { 491 krb5_context context = *state; 492 krb5_data req1 = string2data("I'm a test request"); 493 krb5_data rep1 = string2data("I'm a test response"); 494 size_t e1_size = entry_size(&req1, &rep1); 495 krb5_data req2 = string2data("I'm a different test request"); 496 size_t e2_size = entry_size(&req2, NULL); 497 struct entry *hash1_ent, *hash2_ent, *exp_first, *exp_last; 498 499 time_return(0, 0); 500 kdc_insert_lookaside(context, &req1, &rep1); 501 502 hash1_ent = k5_hashtab_get(hash_table, req1.data, req1.length); 503 assert_non_null(hash1_ent); 504 assert_true(data_eq(hash1_ent->req_packet, req1)); 505 assert_true(data_eq(hash1_ent->reply_packet, rep1)); 506 exp_first = K5_TAILQ_FIRST(&expiration_queue); 507 assert_true(data_eq(exp_first->req_packet, req1)); 508 assert_true(data_eq(exp_first->reply_packet, rep1)); 509 assert_int_equal(num_entries, 1); 510 assert_int_equal(total_size, e1_size); 511 512 time_return(0, 0); 513 kdc_insert_lookaside(context, &req2, NULL); 514 515 hash2_ent = k5_hashtab_get(hash_table, req2.data, req2.length); 516 assert_non_null(hash2_ent); 517 assert_true(data_eq(hash2_ent->req_packet, req2)); 518 assert_int_equal(hash2_ent->reply_packet.length, 0); 519 exp_last = K5_TAILQ_LAST(&expiration_queue, entry_queue); 520 assert_true(data_eq(exp_last->req_packet, req2)); 521 assert_int_equal(exp_last->reply_packet.length, 0); 522 assert_int_equal(num_entries, 2); 523 assert_int_equal(total_size, e1_size + e2_size); 524 } 525 526 static void 527 test_kdc_insert_lookaside_cache_expire(void **state) 528 { 529 struct entry *e; 530 krb5_context context = *state; 531 krb5_data req1 = string2data("I'm a test request"); 532 krb5_data rep1 = string2data("I'm a test response"); 533 size_t e1_size = entry_size(&req1, &rep1); 534 krb5_data req2 = string2data("I'm a different test request"); 535 size_t e2_size = entry_size(&req2, NULL); 536 struct entry *hash1_ent, *hash2_ent, *exp_ent; 537 538 time_return(0, 0); 539 kdc_insert_lookaside(context, &req1, &rep1); 540 541 hash1_ent = k5_hashtab_get(hash_table, req1.data, req1.length); 542 assert_non_null(hash1_ent); 543 assert_true(data_eq(hash1_ent->req_packet, req1)); 544 assert_true(data_eq(hash1_ent->reply_packet, rep1)); 545 exp_ent = K5_TAILQ_FIRST(&expiration_queue); 546 assert_true(data_eq(exp_ent->req_packet, req1)); 547 assert_true(data_eq(exp_ent->reply_packet, rep1)); 548 assert_int_equal(num_entries, 1); 549 assert_int_equal(total_size, e1_size); 550 551 /* Increase hits on entry */ 552 e = k5_hashtab_get(hash_table, req1.data, req1.length); 553 assert_non_null(e); 554 e->num_hits = 5; 555 556 time_return(STALE_TIME + 1, 0); 557 kdc_insert_lookaside(context, &req2, NULL); 558 559 assert_null(k5_hashtab_get(hash_table, req1.data, req1.length)); 560 assert_int_equal(max_hits_per_entry, 5); 561 562 hash2_ent = k5_hashtab_get(hash_table, req2.data, req2.length); 563 assert_non_null(hash2_ent); 564 assert_true(data_eq(hash2_ent->req_packet, req2)); 565 assert_int_equal(hash2_ent-> reply_packet.length, 0); 566 exp_ent = K5_TAILQ_FIRST(&expiration_queue); 567 assert_true(data_eq(exp_ent->req_packet, req2)); 568 assert_int_equal(exp_ent->reply_packet.length, 0); 569 assert_int_equal(num_entries, 1); 570 assert_int_equal(total_size, e2_size); 571 } 572 573 int 574 main(void) 575 { 576 int ret; 577 578 const struct CMUnitTest replay_tests[] = { 579 /* entry_size tests */ 580 replay_unit_test(test_entry_size_no_response), 581 replay_unit_test(test_entry_size_w_response), 582 /* insert_entry tests */ 583 replay_unit_test(test_insert_entry), 584 replay_unit_test(test_insert_entry_no_response), 585 replay_unit_test(test_insert_entry_multiple), 586 /* discard_entry tests */ 587 replay_unit_test(test_discard_entry), 588 replay_unit_test(test_discard_entry_no_response), 589 /* kdc_remove_lookaside tests */ 590 replay_unit_test(test_kdc_remove_lookaside), 591 replay_unit_test(test_kdc_remove_lookaside_empty_cache), 592 replay_unit_test(test_kdc_remove_lookaside_unknown), 593 replay_unit_test(test_kdc_remove_lookaside_multiple), 594 /* kdc_check_lookaside tests */ 595 replay_unit_test(test_kdc_check_lookaside_hit), 596 replay_unit_test(test_kdc_check_lookaside_no_hit), 597 replay_unit_test(test_kdc_check_lookaside_empty), 598 replay_unit_test(test_kdc_check_lookaside_no_response), 599 replay_unit_test(test_kdc_check_lookaside_hit_multiple), 600 /* kdc_insert_lookaside tests */ 601 replay_unit_test(test_kdc_insert_lookaside_single), 602 replay_unit_test(test_kdc_insert_lookaside_no_reply), 603 replay_unit_test(test_kdc_insert_lookaside_multiple), 604 replay_unit_test(test_kdc_insert_lookaside_cache_expire) 605 }; 606 607 ret = cmocka_run_group_tests_name("replay_lookaside", replay_tests, 608 global_setup, global_teardown); 609 610 return ret; 611 } 612 613 #else /* NOCACHE */ 614 615 int 616 main(void) 617 { 618 return 0; 619 } 620 621 #endif /* NOCACHE */ 622