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