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
__wrap_krb5_timeofday(krb5_context context,krb5_timestamp * timeret)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
time_return(krb5_timestamp time,krb5_error_code err)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
global_setup(void ** state)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
global_teardown(void ** state)96 global_teardown(void **state)
97 {
98 krb5_free_context(*state);
99 return 0;
100 }
101
102 static int
setup_lookaside(void ** state)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
destroy_lookaside(void ** state)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
test_entry_size_no_response(void ** state)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
test_entry_size_w_response(void ** state)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
test_insert_entry(void ** state)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
test_insert_entry_no_response(void ** state)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
test_insert_entry_multiple(void ** state)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
test_discard_entry(void ** state)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
test_discard_entry_no_response(void ** state)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
test_kdc_remove_lookaside(void ** state)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
test_kdc_remove_lookaside_empty_cache(void ** state)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
test_kdc_remove_lookaside_unknown(void ** state)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
test_kdc_remove_lookaside_multiple(void ** state)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
test_kdc_check_lookaside_hit(void ** state)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
test_kdc_check_lookaside_no_hit(void ** state)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
test_kdc_check_lookaside_empty(void ** state)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
test_kdc_check_lookaside_no_response(void ** state)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
test_kdc_check_lookaside_hit_multiple(void ** state)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
test_kdc_insert_lookaside_single(void ** state)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
test_kdc_insert_lookaside_no_reply(void ** state)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
test_kdc_insert_lookaside_multiple(void ** state)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
test_kdc_insert_lookaside_cache_expire(void ** state)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
main()573 int main()
574 {
575 int ret;
576
577 const struct CMUnitTest replay_tests[] = {
578 /* entry_size tests */
579 replay_unit_test(test_entry_size_no_response),
580 replay_unit_test(test_entry_size_w_response),
581 /* insert_entry tests */
582 replay_unit_test(test_insert_entry),
583 replay_unit_test(test_insert_entry_no_response),
584 replay_unit_test(test_insert_entry_multiple),
585 /* discard_entry tests */
586 replay_unit_test(test_discard_entry),
587 replay_unit_test(test_discard_entry_no_response),
588 /* kdc_remove_lookaside tests */
589 replay_unit_test(test_kdc_remove_lookaside),
590 replay_unit_test(test_kdc_remove_lookaside_empty_cache),
591 replay_unit_test(test_kdc_remove_lookaside_unknown),
592 replay_unit_test(test_kdc_remove_lookaside_multiple),
593 /* kdc_check_lookaside tests */
594 replay_unit_test(test_kdc_check_lookaside_hit),
595 replay_unit_test(test_kdc_check_lookaside_no_hit),
596 replay_unit_test(test_kdc_check_lookaside_empty),
597 replay_unit_test(test_kdc_check_lookaside_no_response),
598 replay_unit_test(test_kdc_check_lookaside_hit_multiple),
599 /* kdc_insert_lookaside tests */
600 replay_unit_test(test_kdc_insert_lookaside_single),
601 replay_unit_test(test_kdc_insert_lookaside_no_reply),
602 replay_unit_test(test_kdc_insert_lookaside_multiple),
603 replay_unit_test(test_kdc_insert_lookaside_cache_expire)
604 };
605
606 ret = cmocka_run_group_tests_name("replay_lookaside", replay_tests,
607 global_setup, global_teardown);
608
609 return ret;
610 }
611
612 #else /* NOCACHE */
613
main()614 int main()
615 {
616 return 0;
617 }
618
619 #endif /* NOCACHE */
620