xref: /freebsd/crypto/krb5/src/lib/krb5/ccache/t_cccol.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/ccache/t_cccol.py - Test ccache collection via API */
3 /*
4  * Copyright (C) 2013 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 #include <krb5.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <assert.h>
38 
39 static krb5_context ctx;
40 
41 /* Check that code is 0.  Display an error message first if it is not. */
42 static void
check(krb5_error_code code)43 check(krb5_error_code code)
44 {
45     const char *errmsg;
46 
47     if (code != 0) {
48         errmsg = krb5_get_error_message(ctx, code);
49         fprintf(stderr, "%s\n", errmsg);
50         krb5_free_error_message(ctx, errmsg);
51     }
52     assert(code == 0);
53 }
54 
55 /* Construct a list of the names of each credential cache in the collection. */
56 static void
get_collection_names(char *** list_out,size_t * count_out)57 get_collection_names(char ***list_out, size_t *count_out)
58 {
59     krb5_cccol_cursor cursor;
60     krb5_ccache cache;
61     char **list = NULL;
62     size_t count = 0;
63     char *name;
64 
65     check(krb5_cccol_cursor_new(ctx, &cursor));
66     while (1) {
67         check(krb5_cccol_cursor_next(ctx, cursor, &cache));
68         if (cache == NULL)
69             break;
70         check(krb5_cc_get_full_name(ctx, cache, &name));
71         krb5_cc_close(ctx, cache);
72         list = realloc(list, (count + 1) * sizeof(*list));
73         assert(list != NULL);
74         list[count++] = name;
75     }
76     krb5_cccol_cursor_free(ctx, &cursor);
77     *list_out = list;
78     *count_out = count;
79 }
80 
81 /* Return true if list contains name. */
82 static krb5_boolean
in_list(char ** list,size_t count,const char * name)83 in_list(char **list, size_t count, const char *name)
84 {
85     size_t i;
86 
87     for (i = 0; i < count; i++) {
88         if (strcmp(list[i], name) == 0)
89             return TRUE;
90     }
91     return FALSE;
92 }
93 
94 /* Release the memory for a list of credential cache names. */
95 static void
free_list(char ** list,size_t count)96 free_list(char **list, size_t count)
97 {
98     size_t i;
99 
100     for (i = 0; i < count; i++)
101         krb5_free_string(ctx, list[i]);
102     free(list);
103 }
104 
105 /*
106  * Check that the cache names within the current collection begin with first
107  * (unless first is NULL), that the other elements match the remaining
108  * arguments in some order.  others must be the number of additional cache
109  * names.
110  */
111 static void
check_collection(const char * first,size_t others,...)112 check_collection(const char *first, size_t others, ...)
113 {
114     va_list ap;
115     char **list;
116     size_t count, i;
117     const char *name;
118 
119     get_collection_names(&list, &count);
120     if (first != NULL) {
121         assert(strcmp(first, list[0]) == 0);
122         assert(count == others + 1);
123     } else {
124         assert(count == others);
125     }
126     va_start(ap, others);
127     for (i = 0; i < others; i++) {
128         name = va_arg(ap, const char *);
129         assert(in_list(list, count, name));
130     }
131     va_end(ap);
132     free_list(list, count);
133 }
134 
135 /* Check that the name of cache matches expected_name. */
136 static void
check_name(krb5_ccache cache,const char * expected_name)137 check_name(krb5_ccache cache, const char *expected_name)
138 {
139     char *name;
140 
141     check(krb5_cc_get_full_name(ctx, cache, &name));
142     assert(strcmp(name, expected_name) == 0);
143     krb5_free_string(ctx, name);
144 }
145 
146 /* Check that when collection_name is resolved, the resulting cache's name
147  * matches expected_name. */
148 static void
check_primary_name(const char * collection_name,const char * expected_name)149 check_primary_name(const char *collection_name, const char *expected_name)
150 {
151     krb5_ccache cache;
152 
153     check(krb5_cc_resolve(ctx, collection_name, &cache));
154     check_name(cache, expected_name);
155     krb5_cc_close(ctx, cache);
156 }
157 
158 /* Check that when name is resolved, the resulting cache's principal matches
159  * expected_princ, or has no principal if expected_princ is NULL. */
160 static void
check_princ(const char * name,krb5_principal expected_princ)161 check_princ(const char *name, krb5_principal expected_princ)
162 {
163     krb5_ccache cache;
164     krb5_principal princ;
165 
166     check(krb5_cc_resolve(ctx, name, &cache));
167     if (expected_princ != NULL) {
168         check(krb5_cc_get_principal(ctx, cache, &princ));
169         assert(krb5_principal_compare(ctx, princ, expected_princ));
170         krb5_free_principal(ctx, princ);
171     } else {
172         assert(krb5_cc_get_principal(ctx, cache, &princ) != 0);
173     }
174     krb5_cc_close(ctx, cache);
175 }
176 
177 /* Check that krb5_cc_cache_match on princ returns a cache whose name matches
178  * expected_name, or that the match fails if expected_name is NULL. */
179 static void
check_match(krb5_principal princ,const char * expected_name)180 check_match(krb5_principal princ, const char *expected_name)
181 {
182     krb5_ccache cache;
183 
184     if (expected_name != NULL) {
185         check(krb5_cc_cache_match(ctx, princ, &cache));
186         check_name(cache, expected_name);
187         krb5_cc_close(ctx, cache);
188     } else {
189         assert(krb5_cc_cache_match(ctx, princ, &cache) != 0);
190     }
191 }
192 
193 int
main(int argc,char ** argv)194 main(int argc, char **argv)
195 {
196     krb5_ccache ccinitial, ccu1, ccu2;
197     krb5_principal princ1, princ2, princ3;
198     const char *collection_name, *typename;
199     char *initial_primary_name, *unique1_name, *unique2_name;
200 
201     /*
202      * Get the collection name from the command line.  This is a ccache name
203      * with collection semantics, like DIR:/path/to/directory.  This test
204      * program assumes that the collection is empty to start with.
205      */
206     assert(argc == 2);
207     collection_name = argv[1];
208 
209     /*
210      * Set the default ccache for the context to be the collection name, so the
211      * library can find the collection.
212      */
213     check(krb5_init_context(&ctx));
214     check(krb5_cc_set_default_name(ctx, collection_name));
215 
216     /*
217      * Resolve the collection name.  Since the collection is empty, this should
218      * generate a subsidiary name of an uninitialized cache.  Getting the name
219      * of the resulting cache should give us the subsidiary name, not the
220      * collection name.  This resulting subsidiary name should be consistent if
221      * we resolve the collection name again, and the collection should still be
222      * empty since we haven't initialized the cache.
223      */
224     check(krb5_cc_resolve(ctx, collection_name, &ccinitial));
225     check(krb5_cc_get_full_name(ctx, ccinitial, &initial_primary_name));
226     assert(strcmp(initial_primary_name, collection_name) != 0);
227     check_primary_name(collection_name, initial_primary_name);
228     check_collection(NULL, 0);
229     check_princ(collection_name, NULL);
230     check_princ(initial_primary_name, NULL);
231 
232     /*
233      * Before initializing the primary ccache, generate and initialize two
234      * unique caches of the collection's type.  Check that the cache names
235      * resolve to the generated caches and appear in the collection.  (They
236      * might appear before being initialized; that's not currently considered
237      * important).  The primary cache for the collection should remain as the
238      * uninitialized cache from the previous step.
239      */
240     typename = krb5_cc_get_type(ctx, ccinitial);
241     check(krb5_cc_new_unique(ctx, typename, NULL, &ccu1));
242     check(krb5_cc_get_full_name(ctx, ccu1, &unique1_name));
243     check(krb5_parse_name(ctx, "princ1@X", &princ1));
244     check(krb5_cc_initialize(ctx, ccu1, princ1));
245     check_princ(unique1_name, princ1);
246     check_match(princ1, unique1_name);
247     check_collection(NULL, 1, unique1_name);
248     check(krb5_cc_new_unique(ctx, typename, NULL, &ccu2));
249     check(krb5_cc_get_full_name(ctx, ccu2, &unique2_name));
250     check(krb5_parse_name(ctx, "princ2@X", &princ2));
251     check(krb5_cc_initialize(ctx, ccu2, princ2));
252     check_princ(unique2_name, princ2);
253     check_match(princ1, unique1_name);
254     check_match(princ2, unique2_name);
255     check_collection(NULL, 2, unique1_name, unique2_name);
256     assert(strcmp(unique1_name, initial_primary_name) != 0);
257     assert(strcmp(unique1_name, collection_name) != 0);
258     assert(strcmp(unique2_name, initial_primary_name) != 0);
259     assert(strcmp(unique2_name, collection_name) != 0);
260     assert(strcmp(unique2_name, unique1_name) != 0);
261     check_primary_name(collection_name, initial_primary_name);
262 
263     /*
264      * Initialize the initial primary cache.  Make sure it didn't change names,
265      * that the previously retrieved name and the collection name both resolve
266      * to the initialized cache, and that it now appears first in the
267      * collection.
268      */
269     check(krb5_parse_name(ctx, "princ3@X", &princ3));
270     check(krb5_cc_initialize(ctx, ccinitial, princ3));
271     check_name(ccinitial, initial_primary_name);
272     check_princ(initial_primary_name, princ3);
273     check_princ(collection_name, princ3);
274     check_match(princ3, initial_primary_name);
275     check_collection(initial_primary_name, 2, unique1_name, unique2_name);
276 
277     /*
278      * Switch the primary cache to each cache we have open.  One each switch,
279      * check the primary name, check that the collection resolves to the
280      * expected cache, and check that the new primary name appears first in the
281      * collection.
282      */
283     check(krb5_cc_switch(ctx, ccu1));
284     check_primary_name(collection_name, unique1_name);
285     check_princ(collection_name, princ1);
286     check_collection(unique1_name, 2, initial_primary_name, unique2_name);
287     check(krb5_cc_switch(ctx, ccu2));
288     check_primary_name(collection_name, unique2_name);
289     check_princ(collection_name, princ2);
290     check_collection(unique2_name, 2, initial_primary_name, unique1_name);
291     check(krb5_cc_switch(ctx, ccinitial));
292     check_primary_name(collection_name, initial_primary_name);
293     check_princ(collection_name, princ3);
294     check_collection(initial_primary_name, 2, unique1_name, unique2_name);
295 
296     /*
297      * Temporarily set the context default ccache to a subsidiary name, and
298      * check that iterating over the collection yields that subsidiary cache
299      * and no others.
300      */
301     check(krb5_cc_set_default_name(ctx, unique1_name));
302     check_collection(unique1_name, 0);
303     check(krb5_cc_set_default_name(ctx, collection_name));
304 
305     /*
306      * Destroy the primary cache.  Make sure this causes both the initial
307      * primary name and the collection name to resolve to an uninitialized
308      * cache.  Make sure the primary name doesn't change and doesn't appear in
309      * the collection any more.
310      */
311     check(krb5_cc_destroy(ctx, ccinitial));
312     check_princ(initial_primary_name, NULL);
313     check_princ(collection_name, NULL);
314     check_primary_name(collection_name, initial_primary_name);
315     check_match(princ1, unique1_name);
316     check_match(princ2, unique2_name);
317     check_match(princ3, NULL);
318     check_collection(NULL, 2, unique1_name, unique2_name);
319 
320     /*
321      * Switch to the first unique cache after destroying the primary cache.
322      * Check that the collection name resolves to this cache and that the new
323      * primary name appears first in the collection.
324      */
325     check(krb5_cc_switch(ctx, ccu1));
326     check_primary_name(collection_name, unique1_name);
327     check_princ(collection_name, princ1);
328     check_collection(unique1_name, 1, unique2_name);
329 
330     /*
331      * Destroy the second unique cache (which is not the current primary),
332      * check that it is on longer initialized, and check that it no longer
333      * appears in the collection.  Check that destroying the non-primary cache
334      * doesn't affect the primary name.
335      */
336     check(krb5_cc_destroy(ctx, ccu2));
337     check_princ(unique2_name, NULL);
338     check_match(princ2, NULL);
339     check_collection(unique1_name, 0);
340     check_primary_name(collection_name, unique1_name);
341     check_match(princ1, unique1_name);
342     check_princ(collection_name, princ1);
343 
344     /*
345      * Destroy the first unique cache.  Check that the collection is empty and
346      * still has the same primary name.
347      */
348     check(krb5_cc_destroy(ctx, ccu1));
349     check_princ(unique1_name, NULL);
350     check_princ(collection_name, NULL);
351     check_primary_name(collection_name, unique1_name);
352     check_match(princ1, NULL);
353     check_collection(NULL, 0);
354 
355     krb5_free_string(ctx, initial_primary_name);
356     krb5_free_string(ctx, unique1_name);
357     krb5_free_string(ctx, unique2_name);
358     krb5_free_principal(ctx, princ1);
359     krb5_free_principal(ctx, princ2);
360     krb5_free_principal(ctx, princ3);
361     krb5_free_context(ctx);
362     return 0;
363 }
364