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