xref: /freebsd/crypto/krb5/src/lib/krb5/keytab/t_keytab.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/keytab/t_keytab.c - Tests for keytab interface */
3 /*
4  * Copyright (C) 2007 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 "autoconf.h"
29 #include <stdio.h>
30 #include <errno.h>
31 #if HAVE_UNISTD_H
32 #include <unistd.h>
33 #endif
34 #include <string.h>
35 
36 
37 int debug=0;
38 
39 extern const krb5_kt_ops krb5_ktf_writable_ops;
40 
41 #define KRB5_OK 0
42 
43 #define CHECK_ERR(kret,err,msg)                         \
44     if (kret != err) {                                  \
45         com_err(msg, kret, "");                         \
46         fflush(stderr);                                 \
47         exit(1);                                        \
48     } else if(debug) printf("%s went ok\n", msg);
49 
50 #define CHECK(kret,msg) CHECK_ERR(kret, 0, msg)
51 
52 #define CHECK_STR(str,msg)                              \
53     if (str == 0) {                                     \
54         com_err(msg, kret, "");                         \
55         exit(1);                                        \
56     } else if(debug) printf("%s went ok\n", msg);
57 
58 static void
test_misc(krb5_context context)59 test_misc(krb5_context context)
60 {
61     /* Tests for certain error returns */
62     krb5_error_code       kret;
63     krb5_keytab ktid;
64     char defname[BUFSIZ];
65     char *name;
66 
67     fprintf(stderr, "Testing miscellaneous error conditions\n");
68 
69     kret = krb5_kt_resolve(context, "unknown_method_ep:/tmp/name", &ktid);
70     CHECK_ERR(kret, KRB5_KT_UNKNOWN_TYPE, "resolve unknown type");
71 
72     /* Test length limits on krb5_kt_default_name */
73     kret = krb5_kt_default_name(context, defname, sizeof(defname));
74     CHECK(kret, "krb5_kt_default_name error");
75 
76     /* Now allocate space - without the null... */
77     name = malloc(strlen(defname));
78     if(!name) {
79         fprintf(stderr, "Out of memory in testing\n");
80         exit(1);
81     }
82     kret = krb5_kt_default_name(context, name, strlen(defname));
83     free(name);
84     CHECK_ERR(kret, KRB5_CONFIG_NOTENUFSPACE, "krb5_kt_default_name limited");
85 }
86 
87 static void
kt_test(krb5_context context,const char * name)88 kt_test(krb5_context context, const char *name)
89 {
90     krb5_error_code kret;
91     krb5_keytab kt;
92     const char *type;
93     char buf[BUFSIZ];
94     char *p;
95     krb5_keytab_entry kent, kent2;
96     krb5_principal princ;
97     krb5_kt_cursor cursor, cursor2;
98     int cnt;
99     krb5_enctype e1 = ENCTYPE_AES128_CTS_HMAC_SHA256_128,
100         e2 = ENCTYPE_AES256_CTS_HMAC_SHA384_192;
101 
102     kret = krb5_kt_resolve(context, name, &kt);
103     CHECK(kret, "resolve");
104 
105     type = krb5_kt_get_type(context, kt);
106     CHECK_STR(type, "getting kt type");
107     printf("  Type is: %s\n", type);
108 
109     kret = krb5_kt_get_name(context, kt, buf, sizeof(buf));
110     CHECK(kret, "get_name");
111     printf("  Name is: %s\n", buf);
112 
113     /* Check that length checks fail */
114     /* The buffer is allocated too small - to allow for valgrind test of
115        overflows
116     */
117     p = malloc(strlen(buf));
118     kret = krb5_kt_get_name(context, kt, p, 1);
119     CHECK_ERR(kret, KRB5_KT_NAME_TOOLONG, "get_name - size 1");
120 
121 
122     kret = krb5_kt_get_name(context, kt, p, strlen(buf));
123     CHECK_ERR(kret, KRB5_KT_NAME_TOOLONG, "get_name");
124     free(p);
125 
126     /* Try to lookup unknown principal - when keytab does not exist*/
127     kret = krb5_parse_name(context, "test/test2@TEST.MIT.EDU", &princ);
128     CHECK(kret, "parsing principal");
129 
130     /* This will return ENOENT for FILE because the file doesn't exist,
131      * so accept that or KRB5_KT_NOTFOUND. */
132     kret = krb5_kt_get_entry(context, kt, princ, 0, 0, &kent);
133     if (kret != ENOENT) {
134         CHECK_ERR(kret, KRB5_KT_NOTFOUND, "Getting non-existent entry");
135     }
136 
137     kret = krb5_kt_have_content(context, kt);
138     CHECK_ERR(kret, KRB5_KT_NOTFOUND, "Checking for keytab content (empty)");
139 
140 
141     /* ===================   Add entries to keytab ================= */
142     /*
143      * Add the following for this principal
144      * enctype e1, kvno 1, key = "1"
145      * enctype e2, kvno 1, key = "1"
146      * enctype e1, kvno 2, key = "2"
147      */
148     memset(&kent, 0, sizeof(kent));
149     kent.magic = KV5M_KEYTAB_ENTRY;
150     kent.principal = princ;
151     kent.timestamp = 327689;
152     kent.vno = 1;
153     kent.key.magic = KV5M_KEYBLOCK;
154     kent.key.enctype = e1;
155     kent.key.length = 1;
156     kent.key.contents = (krb5_octet *) "1";
157 
158 
159     kret = krb5_kt_add_entry(context, kt, &kent);
160     CHECK(kret, "Adding initial entry");
161 
162     kent.key.enctype = e2;
163     kret = krb5_kt_add_entry(context, kt, &kent);
164     CHECK(kret, "Adding second entry");
165 
166     kent.key.enctype = e1;
167     kent.vno = 2;
168     kent.key.contents = (krb5_octet *) "2";
169     kret = krb5_kt_add_entry(context, kt, &kent);
170     CHECK(kret, "Adding third entry");
171 
172     /* Free memory */
173     krb5_free_principal(context, princ);
174 
175     /* ==============   Test iterating over contents of keytab ========= */
176 
177     kret = krb5_kt_have_content(context, kt);
178     CHECK(kret, "Checking for keytab content (full)");
179 
180     kret = krb5_kt_start_seq_get(context, kt, &cursor);
181     CHECK(kret, "Start sequence get");
182 
183 
184     memset(&kent, 0, sizeof(kent));
185     cnt = 0;
186     while((kret = krb5_kt_next_entry(context, kt, &kent, &cursor)) == 0) {
187         if(((kent.vno != 1) && (kent.vno != 2)) ||
188            ((kent.key.enctype != e1) && (kent.key.enctype != e2)) ||
189            (kent.key.length != 1) ||
190            (kent.key.contents[0] != kent.vno +'0')) {
191             fprintf(stderr, "Error in read contents\n");
192             exit(1);
193         }
194 
195         if((kent.magic != KV5M_KEYTAB_ENTRY) ||
196            (kent.key.magic != KV5M_KEYBLOCK)) {
197             fprintf(stderr, "Magic number in sequence not proper\n");
198             exit(1);
199         }
200 
201         cnt++;
202         krb5_free_keytab_entry_contents(context, &kent);
203     }
204     CHECK_ERR(kret, KRB5_KT_END, "getting next entry");
205 
206     if(cnt != 3) {
207         fprintf(stderr, "Mismatch in number of entries in keytab");
208     }
209 
210     kret = krb5_kt_end_seq_get(context, kt, &cursor);
211     CHECK(kret, "End sequence get");
212 
213 
214     /* ==========================   get_entry tests ============== */
215 
216     /* Try to lookup unknown principal  - now that keytab exists*/
217     kret = krb5_parse_name(context, "test3/test2@TEST.MIT.EDU", &princ);
218     CHECK(kret, "parsing principal");
219 
220 
221     kret = krb5_kt_get_entry(context, kt, princ, 0, 0, &kent);
222     CHECK_ERR(kret, KRB5_KT_NOTFOUND, "Getting nonexistent entry");
223 
224     krb5_free_principal(context, princ);
225 
226     /* Try to lookup known principal */
227     kret = krb5_parse_name(context, "test/test2@TEST.MIT.EDU", &princ);
228     CHECK(kret, "parsing principal");
229 
230     kret = krb5_kt_get_entry(context, kt, princ, 0, 0, &kent);
231     CHECK(kret, "looking up principal");
232 
233     /* Ensure a valid answer  - we did not specify an enctype or kvno */
234     if (!krb5_principal_compare(context, princ, kent.principal) ||
235         ((kent.vno != 1) && (kent.vno != 2)) ||
236         ((kent.key.enctype != e1) && (kent.key.enctype != e2)) ||
237         (kent.key.length != 1) ||
238         (kent.key.contents[0] != kent.vno +'0')) {
239         fprintf(stderr, "Retrieved principal does not check\n");
240         exit(1);
241     }
242 
243     krb5_free_keytab_entry_contents(context, &kent);
244 
245     /* Try to lookup a specific enctype - but unspecified kvno - should give
246      * max kvno
247      */
248     kret = krb5_kt_get_entry(context, kt, princ, 0, e1, &kent);
249     CHECK(kret, "looking up principal");
250 
251     /* Ensure a valid answer  - we did specified an enctype */
252     if (!krb5_principal_compare(context, princ, kent.principal) ||
253         (kent.vno != 2) || (kent.key.enctype != e1) ||
254         (kent.key.length != 1) ||
255         (kent.key.contents[0] != kent.vno +'0')) {
256         fprintf(stderr, "Retrieved principal does not check\n");
257 
258         exit(1);
259 
260     }
261 
262     krb5_free_keytab_entry_contents(context, &kent);
263 
264     /* Try to lookup unspecified enctype, but a specified kvno */
265 
266     kret = krb5_kt_get_entry(context, kt, princ, 2, 0, &kent);
267     CHECK(kret, "looking up principal");
268 
269     /* Ensure a valid answer  - we did not specify a kvno */
270     if (!krb5_principal_compare(context, princ, kent.principal) ||
271         (kent.vno != 2) || (kent.key.enctype != e1) ||
272         (kent.key.length != 1) ||
273         (kent.key.contents[0] != kent.vno +'0')) {
274         fprintf(stderr, "Retrieved principal does not check\n");
275 
276         exit(1);
277 
278     }
279 
280     krb5_free_keytab_entry_contents(context, &kent);
281 
282 
283 
284     /* Try to lookup specified enctype and kvno */
285 
286     kret = krb5_kt_get_entry(context, kt, princ, 1, e1, &kent);
287     CHECK(kret, "looking up principal");
288 
289     if (!krb5_principal_compare(context, princ, kent.principal) ||
290         (kent.vno != 1) || (kent.key.enctype != e1) ||
291         (kent.key.length != 1) ||
292         (kent.key.contents[0] != kent.vno +'0')) {
293         fprintf(stderr, "Retrieved principal does not check\n");
294 
295         exit(1);
296 
297     }
298 
299     krb5_free_keytab_entry_contents(context, &kent);
300 
301 
302     /* Try lookup with active iterators.  */
303     kret = krb5_kt_start_seq_get(context, kt, &cursor);
304     CHECK(kret, "Start sequence get(2)");
305     kret = krb5_kt_start_seq_get(context, kt, &cursor2);
306     CHECK(kret, "Start sequence get(3)");
307     kret = krb5_kt_next_entry(context, kt, &kent, &cursor);
308     CHECK(kret, "getting next entry(2)");
309     krb5_free_keytab_entry_contents(context, &kent);
310     kret = krb5_kt_next_entry(context, kt, &kent, &cursor);
311     CHECK(kret, "getting next entry(3)");
312     kret = krb5_kt_next_entry(context, kt, &kent2, &cursor2);
313     CHECK(kret, "getting next entry(4)");
314     krb5_free_keytab_entry_contents(context, &kent2);
315     kret = krb5_kt_get_entry(context, kt, kent.principal, 0, 0, &kent2);
316     CHECK(kret, "looking up principal(2)");
317     krb5_free_keytab_entry_contents(context, &kent2);
318     kret = krb5_kt_next_entry(context, kt, &kent2, &cursor2);
319     CHECK(kret, "getting next entry(5)");
320     if (!krb5_principal_compare(context, kent.principal, kent2.principal)) {
321         fprintf(stderr, "iterators not in sync\n");
322         exit(1);
323     }
324     krb5_free_keytab_entry_contents(context, &kent);
325     krb5_free_keytab_entry_contents(context, &kent2);
326     kret = krb5_kt_next_entry(context, kt, &kent, &cursor);
327     CHECK(kret, "getting next entry(6)");
328     kret = krb5_kt_next_entry(context, kt, &kent2, &cursor2);
329     CHECK(kret, "getting next entry(7)");
330     krb5_free_keytab_entry_contents(context, &kent);
331     krb5_free_keytab_entry_contents(context, &kent2);
332     kret = krb5_kt_end_seq_get(context, kt, &cursor);
333     CHECK(kret, "ending sequence get(1)");
334     kret = krb5_kt_end_seq_get(context, kt, &cursor2);
335     CHECK(kret, "ending sequence get(2)");
336 
337     /* Try to lookup specified enctype and kvno  - that does not exist*/
338 
339     kret = krb5_kt_get_entry(context, kt, princ, 3, e1, &kent);
340     CHECK_ERR(kret, KRB5_KT_KVNONOTFOUND,
341               "looking up specific principal, kvno, enctype");
342 
343     krb5_free_principal(context, princ);
344 
345 
346     /* =========================   krb5_kt_remove_entry =========== */
347     /* Lookup the keytab entry w/ 2 kvno - and delete version 2 -
348        ensure gone */
349     kret = krb5_parse_name(context, "test/test2@TEST.MIT.EDU", &princ);
350     CHECK(kret, "parsing principal");
351 
352     kret = krb5_kt_get_entry(context, kt, princ, 0, e1, &kent);
353     CHECK(kret, "looking up principal");
354 
355     /* Ensure a valid answer  - we are looking for max(kvno) and enc=e1 */
356     if (!krb5_principal_compare(context, princ, kent.principal) ||
357         (kent.vno != 2) || (kent.key.enctype != e1) ||
358         (kent.key.length != 1) ||
359         (kent.key.contents[0] != kent.vno +'0')) {
360         fprintf(stderr, "Retrieved principal does not check\n");
361 
362         exit(1);
363 
364     }
365 
366     /* Delete it */
367     kret = krb5_kt_remove_entry(context, kt, &kent);
368     CHECK(kret, "Removing entry");
369 
370     krb5_free_keytab_entry_contents(context, &kent);
371     /* And ensure gone */
372 
373     kret = krb5_kt_get_entry(context, kt, princ, 0, e1, &kent);
374     CHECK(kret, "looking up principal");
375 
376     /* Ensure a valid answer - kvno should now be 1 - we deleted 2 */
377     if (!krb5_principal_compare(context, princ, kent.principal) ||
378         (kent.vno != 1) || (kent.key.enctype != e1) ||
379         (kent.key.length != 1) ||
380         (kent.key.contents[0] != kent.vno +'0')) {
381         fprintf(stderr, "Delete principal check failed\n");
382 
383         exit(1);
384 
385     }
386     krb5_free_keytab_entry_contents(context, &kent);
387 
388     krb5_free_principal(context, princ);
389 
390     /* =======================  Finally close =======================  */
391 
392     kret = krb5_kt_close(context, kt);
393     CHECK(kret, "close");
394 
395 }
396 
397 static void
do_test(krb5_context context,const char * prefix,krb5_boolean delete)398 do_test(krb5_context context, const char *prefix, krb5_boolean delete)
399 {
400     char *name, *filename;
401 
402     if (asprintf(&filename, "/tmp/kttest.%ld", (long) getpid()) < 0) {
403         perror("asprintf");
404         exit(1);
405     }
406     if (asprintf(&name, "%s%s", prefix, filename) < 0) {
407         perror("asprintf");
408         exit(1);
409     }
410     printf("Starting test on %s\n", name);
411     kt_test(context, name);
412     printf("Test on %s passed\n", name);
413     if(delete)
414         unlink(filename);
415     free(filename);
416     free(name);
417 
418 }
419 
420 int
main(void)421 main(void)
422 {
423     krb5_context context;
424     krb5_error_code kret;
425 
426 
427     if ((kret = krb5_init_context(&context))) {
428         printf("Couldn't initialize krb5 library: %s\n",
429                error_message(kret));
430         exit(1);
431     }
432 
433     /* All keytab types are registered by default -- test for
434        redundant error */
435     kret = krb5_kt_register(context, &krb5_ktf_writable_ops);
436     CHECK_ERR(kret, KRB5_KT_TYPE_EXISTS, "register ktf_writable");
437 
438     test_misc(context);
439     do_test(context, "WRFILE:", FALSE);
440     do_test(context, "MEMORY:", TRUE);
441 
442     krb5_free_context(context);
443     return 0;
444 
445 }
446