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
init_structs(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
init_test_cred(krb5_context context)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
free_test_cred(krb5_context context)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
check_num_entries(krb5_context context,krb5_ccache cache,int expected,unsigned linenum)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
cc_test(krb5_context context,const char * name,krb5_flags flags)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
check_registered(krb5_context context,const char * prefix)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
do_test(krb5_context context,const char * prefix)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
test_misc(krb5_context context)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
test_memory_concurrent(krb5_context context)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 extern const krb5_cc_ops krb5_mcc_ops;
564 extern const krb5_cc_ops krb5_fcc_ops;
565
566 int
main(void)567 main(void)
568 {
569 krb5_context context;
570 krb5_error_code kret;
571
572 if ((kret = krb5_init_context(&context))) {
573 printf("Couldn't initialize krb5 library: %s\n",
574 error_message(kret));
575 exit(1);
576 }
577
578 kret = krb5_cc_register(context, &krb5_mcc_ops,0);
579 if(kret && kret != KRB5_CC_TYPE_EXISTS) {
580 CHECK(kret, "register_mem");
581 }
582
583 kret = krb5_cc_register(context, &krb5_fcc_ops,0);
584 if(kret && kret != KRB5_CC_TYPE_EXISTS) {
585 CHECK(kret, "register_mem");
586 }
587
588 /* Registering a second time tests for error return */
589 kret = krb5_cc_register(context, &krb5_fcc_ops,0);
590 if(kret != KRB5_CC_TYPE_EXISTS) {
591 CHECK(kret, "register_mem");
592 }
593
594 /* Registering with override should work */
595 kret = krb5_cc_register(context, &krb5_fcc_ops,1);
596 CHECK(kret, "register_mem override");
597
598 init_structs();
599
600 test_misc(context);
601 do_test(context, "");
602
603 if (check_registered(context, "KEYRING:process:"))
604 do_test(context, "KEYRING:process:");
605 else
606 printf("Skiping KEYRING: test - unregistered type\n");
607
608 do_test(context, "MEMORY:");
609 do_test(context, "FILE:");
610
611 test_memory_concurrent(context);
612
613 krb5_free_context(context);
614 return 0;
615 }
616