1/*
2 * Copyright (c) 2003 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of KTH nor the names of its contributors may be
18 *    used to endorse or promote products derived from this software without
19 *    specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
32
33#include "krb5_locl.h"
34#include <getarg.h>
35#include <err.h>
36
37#ifdef __APPLE_PRIVATE__
38#include <dispatch/dispatch.h>
39#endif
40
41static int debug_flag	= 0;
42static int version_flag = 0;
43static int help_flag	= 0;
44
45#ifdef KRB5_USE_PATH_TOKENS
46#define TEST_CC_NAME "%{TEMP}/krb5-cc-test-foo"
47#else
48#define TEST_CC_NAME "/tmp/krb5-cc-test-foo"
49#endif
50
51static void
52test_default_name(krb5_context context)
53{
54    krb5_error_code ret;
55    const char *p, *test_cc_name = TEST_CC_NAME;
56    char *p1, *p2, *p3;
57
58    p = krb5_cc_default_name(context);
59    if (p == NULL)
60	krb5_errx (context, 1, "krb5_cc_default_name 1 failed");
61    p1 = estrdup(p);
62
63    ret = krb5_cc_set_default_name(context, NULL);
64    if (ret)
65	krb5_errx (context, 1, "krb5_cc_set_default_name failed");
66
67    p = krb5_cc_default_name(context);
68    if (p == NULL)
69	krb5_errx (context, 1, "krb5_cc_default_name 2 failed");
70    p2 = estrdup(p);
71
72    if (strcmp(p1, p2) != 0)
73	krb5_errx (context, 1, "krb5_cc_default_name no longer same");
74
75    ret = krb5_cc_set_default_name(context, test_cc_name);
76    if (ret)
77	krb5_errx (context, 1, "krb5_cc_set_default_name 1 failed");
78
79    p = krb5_cc_default_name(context);
80    if (p == NULL)
81	krb5_errx (context, 1, "krb5_cc_default_name 2 failed");
82    p3 = estrdup(p);
83
84#ifndef KRB5_USE_PATH_TOKENS
85    /* If we are using path tokens, we don't expect the p3 and
86       test_cc_name to match since p3 is going to have expanded
87       tokens. */
88    if (strcmp(p3, test_cc_name) != 0)
89	krb5_errx (context, 1, "krb5_cc_set_default_name 1 failed");
90#endif
91
92    free(p1);
93    free(p2);
94    free(p3);
95}
96
97/*
98 * Check that a closed cc still keeps it data and that it's no longer
99 * there when it's destroyed.
100 */
101
102static void
103test_mcache(krb5_context context)
104{
105    krb5_error_code ret;
106    krb5_ccache id, id2;
107    const char *nc, *tc;
108    char *c;
109    krb5_principal p, p2;
110
111    ret = krb5_parse_name(context, "lha@SU.SE", &p);
112    if (ret)
113	krb5_err(context, 1, ret, "krb5_parse_name");
114
115    ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &id);
116    if (ret)
117	krb5_err(context, 1, ret, "krb5_cc_new_unique");
118
119    ret = krb5_cc_initialize(context, id, p);
120    if (ret)
121	krb5_err(context, 1, ret, "krb5_cc_initialize");
122
123    nc = krb5_cc_get_name(context, id);
124    if (nc == NULL)
125	krb5_errx(context, 1, "krb5_cc_get_name");
126
127    tc = krb5_cc_get_type(context, id);
128    if (tc == NULL)
129	krb5_errx(context, 1, "krb5_cc_get_name");
130
131    if (asprintf(&c, "%s:%s", tc, nc) < 0 || c == NULL)
132	errx(1, "malloc");
133
134    krb5_cc_close(context, id);
135
136    ret = krb5_cc_resolve(context, c, &id2);
137    if (ret)
138	krb5_err(context, 1, ret, "krb5_cc_resolve");
139
140    ret = krb5_cc_get_principal(context, id2, &p2);
141    if (ret)
142	krb5_err(context, 1, ret, "krb5_cc_get_principal");
143
144    if (krb5_principal_compare(context, p, p2) == FALSE)
145	krb5_errx(context, 1, "p != p2");
146
147    krb5_cc_destroy(context, id2);
148    krb5_free_principal(context, p);
149    krb5_free_principal(context, p2);
150
151    ret = krb5_cc_resolve(context, c, &id2);
152    if (ret)
153	krb5_err(context, 1, ret, "krb5_cc_resolve");
154
155    ret = krb5_cc_get_principal(context, id2, &p2);
156    if (ret == 0)
157	krb5_errx(context, 1, "krb5_cc_get_principal");
158
159    krb5_cc_destroy(context, id2);
160    free(c);
161}
162
163/*
164 * Test that init works on a destroyed cc.
165 */
166
167static void
168test_init_vs_destroy(krb5_context context, const char *type)
169{
170    krb5_error_code ret;
171    krb5_ccache id, id2;
172    krb5_principal p, p2;
173    char *n = NULL;
174
175    ret = krb5_parse_name(context, "lha@SU.SE", &p);
176    if (ret)
177	krb5_err(context, 1, ret, "krb5_parse_name");
178
179    ret = krb5_cc_new_unique(context, type, NULL, &id);
180    if (ret)
181	krb5_err(context, 1, ret, "krb5_cc_new_unique: %s", type);
182
183    if (asprintf(&n, "%s:%s",
184		 krb5_cc_get_type(context, id),
185		 krb5_cc_get_name(context, id)) < 0 || n == NULL)
186	errx(1, "malloc");
187
188
189    ret = krb5_cc_resolve(context, n, &id2);
190    free(n);
191    if (ret)
192	krb5_err(context, 1, ret, "krb5_cc_resolve");
193
194    krb5_cc_destroy(context, id);
195
196    ret = krb5_cc_initialize(context, id2, p);
197    if (ret)
198	krb5_err(context, 1, ret, "krb5_cc_initialize");
199
200    ret = krb5_cc_get_principal(context, id2, &p2);
201    if (ret)
202	krb5_err(context, 1, ret, "krb5_cc_get_principal");
203
204    krb5_cc_destroy(context, id2);
205    krb5_free_principal(context, p);
206    krb5_free_principal(context, p2);
207}
208
209static void
210test_cache_remove(krb5_context context, const char *type)
211{
212    krb5_error_code ret;
213    krb5_ccache id;
214    krb5_principal p;
215    krb5_creds cred;
216
217    ret = krb5_parse_name(context, "lha@SU.SE", &p);
218    if (ret)
219	krb5_err(context, 1, ret, "krb5_parse_name");
220
221    ret = krb5_cc_new_unique(context, type, NULL, &id);
222    if (ret)
223	krb5_err(context, 1, ret, "krb5_cc_gen_new: %s", type);
224
225    ret = krb5_cc_initialize(context, id, p);
226    if (ret)
227	krb5_err(context, 1, ret, "krb5_cc_initialize");
228
229    /* */
230    memset(&cred, 0, sizeof(cred));
231    ret = krb5_parse_name(context, "krbtgt/SU.SE@SU.SE", &cred.server);
232    if (ret)
233	krb5_err(context, 1, ret, "krb5_parse_name");
234    ret = krb5_parse_name(context, "lha@SU.SE", &cred.client);
235    if (ret)
236	krb5_err(context, 1, ret, "krb5_parse_name");
237
238    ret = krb5_cc_store_cred(context, id, &cred);
239    if (ret)
240	krb5_err(context, 1, ret, "krb5_cc_store_cred");
241
242    ret = krb5_cc_remove_cred(context, id, 0, &cred);
243    if (ret)
244	krb5_err(context, 1, ret, "krb5_cc_remove_cred");
245
246    ret = krb5_cc_destroy(context, id);
247    if (ret)
248	krb5_err(context, 1, ret, "krb5_cc_destroy");
249
250    krb5_free_principal(context, p);
251    krb5_free_principal(context, cred.server);
252    krb5_free_principal(context, cred.client);
253}
254
255static void
256test_mcc_default(void)
257{
258    krb5_context context;
259    krb5_error_code ret;
260    krb5_ccache id, id2;
261    int i;
262
263    for (i = 0; i < 10; i++) {
264
265	ret = krb5_init_context(&context);
266	if (ret)
267	    krb5_err(context, 1, ret, "krb5_init_context");
268
269	ret = krb5_cc_set_default_name(context, "MEMORY:foo");
270	if (ret)
271	    krb5_err(context, 1, ret, "krb5_cc_set_default_name");
272
273	ret = krb5_cc_default(context, &id);
274	if (ret)
275	    krb5_err(context, 1, ret, "krb5_cc_default");
276
277	ret = krb5_cc_default(context, &id2);
278	if (ret)
279	    krb5_err(context, 1, ret, "krb5_cc_default");
280
281	ret = krb5_cc_close(context, id);
282	if (ret)
283	    krb5_err(context, 1, ret, "krb5_cc_close");
284
285	ret = krb5_cc_close(context, id2);
286	if (ret)
287	    krb5_err(context, 1, ret, "krb5_cc_close");
288
289	krb5_free_context(context);
290    }
291}
292
293struct {
294    char *str;
295    int fail;
296    char *res;
297} cc_names[] = {
298#ifdef KRB5_USE_PATH_TOKENS
299#ifdef _WIN32
300    { "%{APPDATA}", 0 },
301    { "%{COMMON_APPDATA}", 0},
302    { "%{LOCAL_APPDATA}", 0},
303    { "%{SYSTEM}", 0},
304    { "%{WINDOWS}", 0},
305    { "%{USERCONFIG}", 0},
306    { "%{COMMONCONFIG}", 0},
307#else
308    { "%{LIBDIR}", 0},
309    { "%{BINDIR}", 0},
310    { "%{LIBEXEC}", 0},
311    { "%{SBINDIR}", 0},
312#endif
313#if __APPLE__
314    { "%{ApplicationResources}", 1}, /* only for .app's */
315#endif
316    { "%{USERID}", 0},
317    { "%{uid}", 0},
318    { "%{TEMP}", 0},
319#endif
320    { "foo", 0, "foo" },
321    { "foo%}", 0, "foo%}" },
322    { "%{uid}", 0 },
323    { "foo%{null}", 0, "foo" },
324    { "foo%{null}bar", 0, "foobar" },
325    { "%{", 1 },
326    { "%{foo %{", 1 },
327    { "%{{", 1 },
328    { "%{{}", 1 },
329    { "%{nulll}", 1 },
330    { "%{does not exist}", 1 },
331    { "%{}", 1 }
332};
333
334static void
335test_def_cc_name(krb5_context context)
336{
337    krb5_error_code ret;
338    char *str;
339    size_t i;
340
341    for (i = 0; i < sizeof(cc_names)/sizeof(cc_names[0]); i++) {
342	ret = _krb5_expand_default_cc_name(context, cc_names[i].str, &str);
343	if (ret) {
344	    if (cc_names[i].fail == 0)
345		krb5_errx(context, 1, "test %d \"%s\" failed",
346			  (int)i, cc_names[i].str);
347	} else {
348	    if (cc_names[i].fail)
349		krb5_errx(context, 1, "test %d \"%s\" was successful",
350			  (int)i, cc_names[i].str);
351	    if (cc_names[i].res && strcmp(cc_names[i].res, str) != 0)
352		krb5_errx(context, 1, "test %d %s != %s",
353			  (int)i, cc_names[i].res, str);
354	    if (debug_flag)
355		printf("%s => %s\n", cc_names[i].str, str);
356	    free(str);
357	}
358    }
359}
360
361static void
362test_cache_find(krb5_context context, const char *principal, int find)
363{
364    krb5_principal client;
365    krb5_error_code ret;
366    krb5_ccache id = NULL;
367
368    ret = krb5_parse_name(context, principal, &client);
369    if (ret)
370	krb5_err(context, 1, ret, "parse_name for %s failed", principal);
371
372    ret = krb5_cc_cache_match(context, client, &id);
373    if (ret && find)
374	krb5_err(context, 1, ret, "cc_cache_match for %s failed", principal);
375    if (ret == 0 && !find)
376	krb5_err(context, 1, ret, "cc_cache_match for %s found", principal);
377
378    if (id)
379	krb5_cc_close(context, id);
380    krb5_free_principal(context, client);
381}
382
383
384static void
385test_cache_iter(krb5_context context, const char *type, int destroy)
386{
387    krb5_cc_cache_cursor cursor;
388    krb5_error_code ret;
389    krb5_ccache id;
390
391    ret = krb5_cc_cache_get_first (context, type, &cursor);
392    if (ret == KRB5_CC_NOSUPP)
393	return;
394    else if (ret)
395	krb5_err(context, 1, ret, "krb5_cc_cache_get_first(%s)", type);
396
397
398    while ((ret = krb5_cc_cache_next (context, cursor, &id)) == 0) {
399	krb5_principal principal;
400	char *name;
401
402	if (debug_flag)
403	    printf("name: %s\n", krb5_cc_get_name(context, id));
404	ret = krb5_cc_get_principal(context, id, &principal);
405	if (ret == 0) {
406	    ret = krb5_unparse_name(context, principal, &name);
407	    if (ret == 0) {
408		if (debug_flag)
409		    printf("\tprincipal: %s\n", name);
410		free(name);
411	    }
412	    krb5_free_principal(context, principal);
413	}
414	if (destroy)
415	    krb5_cc_destroy(context, id);
416	else
417	    krb5_cc_close(context, id);
418    }
419    if (ret != KRB5_CC_END)
420	krb5_err(context, 1, ret, "krb5_cc_cache_next returned not expected error");
421
422    krb5_cc_cache_end_seq_get(context, cursor);
423}
424
425static void
426test_cache_iter_all(krb5_context context)
427{
428    krb5_cccol_cursor cursor;
429    krb5_error_code ret;
430    krb5_ccache id;
431
432    ret = krb5_cccol_cursor_new (context, &cursor);
433    if (ret)
434	krb5_err(context, 1, ret, "krb5_cccol_cursor_new");
435
436
437    while ((ret = krb5_cccol_cursor_next (context, cursor, &id)) == 0 && id != NULL) {
438	krb5_principal principal;
439	char *name;
440
441	if (debug_flag)
442	    printf("name: %s\n", krb5_cc_get_name(context, id));
443	ret = krb5_cc_get_principal(context, id, &principal);
444	if (ret == 0) {
445	    ret = krb5_unparse_name(context, principal, &name);
446	    if (ret == 0) {
447		if (debug_flag)
448		    printf("\tprincipal: %s\n", name);
449		free(name);
450	    }
451	    krb5_free_principal(context, principal);
452	}
453	krb5_cc_close(context, id);
454    }
455    if (ret != KRB5_CC_END)
456	krb5_err(context, 1, ret, "krb5_cccol_cursor_next returned not expected error");
457
458    krb5_cccol_cursor_free(context, &cursor);
459}
460
461
462static void
463test_copy(krb5_context context, const char *from, const char *to)
464{
465    krb5_ccache fromid, toid;
466    krb5_error_code ret;
467    krb5_principal p, p2;
468
469    ret = krb5_parse_name(context, "lha@SU.SE", &p);
470    if (ret)
471	krb5_err(context, 1, ret, "krb5_parse_name");
472
473    ret = krb5_cc_new_unique(context, from, NULL, &fromid);
474    if (ret)
475	krb5_err(context, 1, ret, "krb5_cc_new_unique: %s", from);
476
477    ret = krb5_cc_initialize(context, fromid, p);
478    if (ret)
479	krb5_err(context, 1, ret, "krb5_cc_initialize");
480
481    ret = krb5_cc_new_unique(context, to, NULL, &toid);
482    if (ret)
483	krb5_err(context, 1, ret, "krb5_cc_gen_new: %s", to);
484
485    ret = krb5_cc_copy_cache(context, fromid, toid);
486    if (ret)
487	krb5_err(context, 1, ret, "krb5_cc_copy_cache");
488
489    ret = krb5_cc_get_principal(context, toid, &p2);
490    if (ret)
491	krb5_err(context, 1, ret, "krb5_cc_get_principal");
492
493    if (krb5_principal_compare(context, p, p2) == FALSE)
494	krb5_errx(context, 1, "p != p2");
495
496    krb5_free_principal(context, p);
497    krb5_free_principal(context, p2);
498
499    krb5_cc_destroy(context, fromid);
500    krb5_cc_destroy(context, toid);
501}
502
503static void
504test_move(krb5_context context, const char *type)
505{
506    const krb5_cc_ops *ops;
507    krb5_ccache fromid, toid;
508    krb5_error_code ret;
509    krb5_principal p, p2;
510
511    ops = krb5_cc_get_prefix_ops(context, type);
512    if (ops == NULL)
513	return;
514
515    ret = krb5_cc_new_unique(context, type, NULL, &fromid);
516    if (ret == KRB5_CC_NOSUPP)
517	return;
518    else if (ret)
519	krb5_err(context, 1, ret, "krb5_cc_new_unique: %s", type);
520
521    ret = krb5_parse_name(context, "lha@SU.SE", &p);
522    if (ret)
523	krb5_err(context, 1, ret, "krb5_parse_name");
524
525    ret = krb5_cc_initialize(context, fromid, p);
526    if (ret)
527	krb5_err(context, 1, ret, "krb5_cc_initialize");
528
529    ret = krb5_cc_new_unique(context, type, NULL, &toid);
530    if (ret)
531	krb5_err(context, 1, ret, "krb5_cc_new_unique");
532
533    ret = krb5_cc_initialize(context, toid, p);
534    if (ret)
535	krb5_err(context, 1, ret, "krb5_cc_initialize");
536
537    ret = krb5_cc_get_principal(context, toid, &p2);
538    if (ret)
539	krb5_err(context, 1, ret, "krb5_cc_get_principal");
540
541    if (krb5_principal_compare(context, p, p2) == FALSE)
542	krb5_errx(context, 1, "p != p2");
543
544    krb5_free_principal(context, p);
545    krb5_free_principal(context, p2);
546
547    krb5_cc_destroy(context, toid);
548    krb5_cc_destroy(context, fromid);
549}
550
551
552static void
553test_prefix_ops(krb5_context context, const char *name, const krb5_cc_ops *ops)
554{
555    const krb5_cc_ops *o;
556
557    o = krb5_cc_get_prefix_ops(context, name);
558    if (o == NULL)
559	krb5_errx(context, 1, "found no match for prefix '%s'", name);
560    if (strcmp(o->prefix, ops->prefix) != 0)
561	krb5_errx(context, 1, "ops for prefix '%s' is not "
562		  "the expected %s != %s", name, o->prefix, ops->prefix);
563}
564
565static void
566test_cc_config(krb5_context context)
567{
568    krb5_error_code ret;
569    krb5_principal p;
570    krb5_ccache id;
571    unsigned int i;
572
573    ret = krb5_cc_new_unique(context, "MEMORY", "bar", &id);
574    if (ret)
575	krb5_err(context, 1, ret, "krb5_cc_new_unique");
576
577    ret = krb5_parse_name(context, "lha@SU.SE", &p);
578    if (ret)
579	krb5_err(context, 1, ret, "krb5_parse_name");
580
581    ret = krb5_cc_initialize(context, id, p);
582    if (ret)
583	krb5_err(context, 1, ret, "krb5_cc_initialize");
584
585    for (i = 0; i < 1000; i++) {
586	krb5_data data, data2;
587	const char *name = "foo";
588	krb5_principal p1 = NULL;
589
590	if (i & 1)
591	    p1 = p;
592
593	data.data = rk_UNCONST(name);
594	data.length = strlen(name);
595
596	ret = krb5_cc_set_config(context, id, p1, "FriendlyName", &data);
597	if (ret)
598	    krb5_errx(context, 1, "krb5_cc_set_config: add");
599
600	ret = krb5_cc_get_config(context, id, p1, "FriendlyName", &data2);
601	if (ret)
602	    krb5_errx(context, 1, "krb5_cc_get_config: first");
603	krb5_data_free(&data2);
604
605	ret = krb5_cc_set_config(context, id, p1, "FriendlyName", &data);
606	if (ret)
607	    krb5_errx(context, 1, "krb5_cc_set_config: add -second");
608
609	ret = krb5_cc_get_config(context, id, p1, "FriendlyName", &data2);
610	if (ret)
611	    krb5_errx(context, 1, "krb5_cc_get_config: second");
612	krb5_data_free(&data2);
613
614	ret = krb5_cc_set_config(context, id, p1, "FriendlyName", NULL);
615	if (ret)
616	    krb5_errx(context, 1, "krb5_cc_set_config: delete");
617
618	ret = krb5_cc_get_config(context, id, p1, "FriendlyName", &data2);
619	if (ret == 0)
620	    krb5_errx(context, 1, "krb5_cc_get_config: non-existant");
621    }
622
623    krb5_cc_destroy(context, id);
624    krb5_free_principal(context, p);
625}
626
627#ifdef HAVE_DISPATCH_DISPATCH_H
628
629static void
630test_threaded(krb5_context context)
631{
632    dispatch_semaphore_t sema;
633    dispatch_queue_t q;
634    dispatch_group_t group;
635    time_t old;
636    const char *type = "API";
637
638    /* clean up old caches first */
639    test_cache_iter(context, type, 1);
640
641    sema = dispatch_semaphore_create(10);
642
643    q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
644
645    old = time(NULL);
646
647    group = dispatch_group_create();
648    if (group == NULL) abort();
649
650    while (time(NULL) - old < 10) {
651	size_t number = 100;
652
653	dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
654
655	if (debug_flag)
656	    printf("time: %d\n", (int)(time(NULL) - old));
657
658	dispatch_group_async(group, q, ^{
659		dispatch_group_t inner = dispatch_group_create();
660		if (inner == NULL) abort();
661
662		dispatch_group_async(inner, q, ^{
663			dispatch_apply(number, q, ^(size_t num) {
664				krb5_context c;
665				krb5_init_context(&c);
666				test_move(c, "API");
667				krb5_free_context(c);
668			    });
669		    });
670		dispatch_group_async(inner, q, ^{
671			dispatch_apply(number, q, ^(size_t num) {
672				krb5_context c;
673				krb5_init_context(&c);
674				test_move(c, "API");
675				krb5_free_context(c);
676			    });
677		    });
678		dispatch_group_async(inner, q, ^{
679			dispatch_apply(number / 10, q, ^(size_t num) {
680				krb5_context c;
681				krb5_init_context(&c);
682				test_cache_iter(c, type, 0);
683				krb5_free_context(c);
684			    });
685		    });
686		dispatch_group_async(inner, q, ^{
687			dispatch_apply(number / 10, q, ^(size_t num) {
688				krb5_context c;
689				krb5_init_context(&c);
690				test_cache_iter_all(c);
691				krb5_free_context(c);
692			    });
693		    });
694
695		dispatch_group_wait(inner, DISPATCH_TIME_FOREVER);
696		dispatch_release(inner);
697		dispatch_semaphore_signal(sema);
698	    });
699    }
700
701    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
702    dispatch_release(group);
703}
704
705#endif
706
707static struct getargs args[] = {
708    {"debug",	'd',	arg_flag,	&debug_flag,
709     "turn on debuggin", NULL },
710    {"version",	0,	arg_flag,	&version_flag,
711     "print version", NULL },
712    {"help",	0,	arg_flag,	&help_flag,
713     NULL, NULL }
714};
715
716static void
717usage (int ret)
718{
719    arg_printusage (args, sizeof(args)/sizeof(*args), NULL, "hostname ...");
720    exit (ret);
721}
722
723int
724main(int argc, char **argv)
725{
726    krb5_context context;
727    krb5_error_code ret;
728    int optidx = 0;
729    krb5_ccache id1, id2;
730
731    setprogname(argv[0]);
732
733    if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
734	usage(1);
735
736    if (help_flag)
737	usage (0);
738
739    if(version_flag){
740	print_version(NULL);
741	exit(0);
742    }
743
744    ret = krb5_init_context(&context);
745    if (ret)
746	errx (1, "krb5_init_context failed: %d", ret);
747
748    test_cache_remove(context, krb5_cc_type_file);
749    test_cache_remove(context, krb5_cc_type_memory);
750#ifdef HAVE_SCC
751    test_cache_remove(context, krb5_cc_type_scc);
752#endif
753
754    test_default_name(context);
755    test_mcache(context);
756    test_init_vs_destroy(context, krb5_cc_type_memory);
757    test_init_vs_destroy(context, krb5_cc_type_file);
758#if 0
759    test_init_vs_destroy(context, krb5_cc_type_api);
760#endif
761#ifdef HAVE_SCC
762    test_init_vs_destroy(context, krb5_cc_type_scc);
763#endif
764    test_mcc_default();
765    test_def_cc_name(context);
766
767    test_cache_iter_all(context);
768
769    test_cache_iter(context, krb5_cc_type_memory, 0);
770    {
771	krb5_principal p;
772	krb5_cc_new_unique(context, krb5_cc_type_memory, "bar", &id1);
773	krb5_cc_new_unique(context, krb5_cc_type_memory, "baz", &id2);
774	krb5_parse_name(context, "lha@SU.SE", &p);
775	krb5_cc_initialize(context, id1, p);
776	krb5_cc_initialize(context, id1, p);
777	krb5_cc_initialize(context, id1, p);
778	krb5_cc_initialize(context, id1, p);
779	krb5_cc_initialize(context, id1, p);
780	krb5_cc_initialize(context, id1, p);
781	krb5_free_principal(context, p);
782    }
783
784    test_cache_find(context, "lha@SU.SE", 1);
785    test_cache_find(context, "hulabundulahotentot@SU.SE", 0);
786
787    test_cache_iter(context, krb5_cc_type_memory, 0);
788    test_cache_iter(context, krb5_cc_type_memory, 1);
789    test_cache_iter(context, krb5_cc_type_memory, 0);
790    test_cache_iter(context, krb5_cc_type_file, 0);
791    test_cache_iter(context, krb5_cc_type_api, 0);
792#ifdef HAVE_SCC
793    test_cache_iter(context, krb5_cc_type_scc, 0);
794    test_cache_iter(context, krb5_cc_type_scc, 1);
795#endif
796#ifdef HAVE_KCC
797    test_cache_iter(context, krb5_cc_type_kcc, 0);
798    test_cache_iter(context, krb5_cc_type_kcc, 1);
799#endif
800
801    test_copy(context, krb5_cc_type_file, krb5_cc_type_file);
802    test_copy(context, krb5_cc_type_memory, krb5_cc_type_memory);
803    test_copy(context, krb5_cc_type_file, krb5_cc_type_memory);
804    test_copy(context, krb5_cc_type_memory, krb5_cc_type_file);
805#ifdef HAVE_SCC
806    test_copy(context, krb5_cc_type_scc, krb5_cc_type_file);
807    test_copy(context, krb5_cc_type_file, krb5_cc_type_scc);
808    test_copy(context, krb5_cc_type_scc, krb5_cc_type_memory);
809    test_copy(context, krb5_cc_type_memory, krb5_cc_type_scc);
810#endif
811#ifdef HAVE_KCC
812    test_copy(context, krb5_cc_type_kcc, krb5_cc_type_file);
813    test_copy(context, krb5_cc_type_file, krb5_cc_type_kcc);
814    test_copy(context, krb5_cc_type_kcc, krb5_cc_type_memory);
815    test_copy(context, krb5_cc_type_memory, krb5_cc_type_kcc);
816#endif
817    test_move(context, krb5_cc_type_file);
818    test_move(context, krb5_cc_type_memory);
819#ifdef HAVE_KCM
820    test_move(context, krb5_cc_type_kcm);
821#endif
822#ifdef HAVE_SCC
823    test_move(context, krb5_cc_type_scc);
824#endif
825
826    test_prefix_ops(context, "FILE:/tmp/foo", &krb5_fcc_ops);
827    test_prefix_ops(context, "FILE", &krb5_fcc_ops);
828    test_prefix_ops(context, "MEMORY", &krb5_mcc_ops);
829    test_prefix_ops(context, "MEMORY:foo", &krb5_mcc_ops);
830    test_prefix_ops(context, "/tmp/kaka", &krb5_fcc_ops);
831#ifdef HAVE_SCC
832    test_prefix_ops(context, "SCC:", &krb5_scc_ops);
833    test_prefix_ops(context, "SCC:foo", &krb5_scc_ops);
834#endif
835#ifdef HAVE_KCC
836    test_prefix_ops(context, "KCC:", &krb5_kcc_ops);
837    test_prefix_ops(context, "KCC:foo", &krb5_kcc_ops);
838#endif
839#ifdef HAVE_XCC
840    test_prefix_ops(context, "XCACHE:", &krb5_xcc_ops);
841    test_prefix_ops(context, "XCACHE:68ADE5C1-C1FF-4088-8AA2-8AF815CDCC5A", &krb5_xcc_ops);
842#endif
843
844    krb5_cc_destroy(context, id1);
845    krb5_cc_destroy(context, id2);
846
847    test_cc_config(context);
848
849#ifdef HAVE_DISPATCH_DISPATCH_H
850    test_threaded(context);
851#endif
852
853    krb5_free_context(context);
854
855    return 0;
856}
857