1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* plugins/kdb/test/kdb_test.c - Test KDB module */
3 /*
4 * Copyright (C) 2015 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 /*
34 * This is a read-only KDB module intended to help test KDC behavior which
35 * cannot be exercised with the DB2 module. Responses are read from the
36 * dbmodules subsection according to this example:
37 *
38 * [dbmodules]
39 * test = {
40 * alias = {
41 * aliasname = canonname
42 * # For cross-realm aliases, only the realm part will
43 * # matter to the client.
44 * aliasname = @FOREIGN_REALM
45 * enterprise@PRINC = @FOREIGN_REALM
46 * }
47 * princs = {
48 * krbtgt/KRBTEST.COM = {
49 * flags = +preauth +ok-to-auth-as-delegate
50 * maxlife = 1d
51 * maxrenewlife = 7d
52 * expiration = 14d # relative to current time
53 * pwexpiration = 1h
54 * # Initial number is kvno; defaults to 1.
55 * keys = 3 aes256-cts aes128-cts:normal
56 * keys = 2 rc4-hmac
57 * strings = key1:value1
58 * strings = key2:value2
59 * }
60 * }
61 * delegation = {
62 * # Traditional constrained delegation; target_service
63 * # must be in the same realm.
64 * intermediate_service = target_service
65 * }
66 * rbcd = {
67 * # Resource-based constrained delegation;
68 * # intermediate_service may be in a different realm.
69 * target_service = intermediate_service
70 * }
71 * }
72 *
73 * Key values are generated using a hash of the kvno, enctype, salt type,
74 * principal name, and lookup realm. This module does not use master key
75 * encryption, so it serves as a partial test of the DAL's ability to avoid
76 * that.
77 *
78 * Inbound cross-realm TGT entries are currently implicit; they will use the
79 * same configuration and key enctypes as the local krbtgt principal, although
80 * they will use different keys (because the lookup realm is hashed in).
81 * Outgoing cross-realm TGT entries must be added explicitly
82 * (krbtgt/OTHER_REALM).
83 */
84
85 #include "k5-int.h"
86 #include "kdb5.h"
87 #include "adm_proto.h"
88 #include <ctype.h>
89
90 #define TEST_AD_TYPE -456
91
92 #define IS_TGS_PRINC(p) ((p)->length == 2 && \
93 data_eq_string((p)->data[0], KRB5_TGS_NAME))
94
95 typedef struct {
96 void *profile;
97 char *section;
98 const char *names[6];
99 } *testhandle;
100
101 static void *
ealloc(size_t sz)102 ealloc(size_t sz)
103 {
104 void *p = calloc(sz, 1);
105
106 if (p == NULL)
107 abort();
108 return p;
109 }
110
111 static char *
estrdup(const char * s)112 estrdup(const char *s)
113 {
114 char *copy = strdup(s);
115
116 if (copy == NULL)
117 abort();
118 return copy;
119 }
120
121 static void
check(krb5_error_code code)122 check(krb5_error_code code)
123 {
124 if (code != 0)
125 abort();
126 }
127
128 /* Set up for a profile query using h->names. Look up s1 -> s2 -> s3 (some of
129 * which may be NULL) within this database's dbmodules section. */
130 static void
set_names(testhandle h,const char * s1,const char * s2,const char * s3)131 set_names(testhandle h, const char *s1, const char *s2, const char *s3)
132 {
133 h->names[0] = KDB_MODULE_SECTION;
134 h->names[1] = h->section;
135 h->names[2] = s1;
136 h->names[3] = s2;
137 h->names[4] = s3;
138 h->names[5] = NULL;
139 }
140
141 /* Look up a string within this database's dbmodules section. */
142 static char *
get_string(testhandle h,const char * s1,const char * s2,const char * s3)143 get_string(testhandle h, const char *s1, const char *s2, const char *s3)
144 {
145 krb5_error_code ret;
146 char **values, *val;
147
148 set_names(h, s1, s2, s3);
149 ret = profile_get_values(h->profile, h->names, &values);
150 if (ret == PROF_NO_RELATION)
151 return NULL;
152 if (ret)
153 abort();
154 val = estrdup(values[0]);
155 profile_free_list(values);
156 return val;
157 }
158
159 /* Look up a duration within this database's dbmodules section. */
160 static krb5_deltat
get_duration(testhandle h,const char * s1,const char * s2,const char * s3)161 get_duration(testhandle h, const char *s1, const char *s2, const char *s3)
162 {
163 char *strval = get_string(h, s1, s2, s3);
164 krb5_deltat val;
165
166 if (strval == NULL)
167 return 0;
168 check(krb5_string_to_deltat(strval, &val));
169 free(strval);
170 return val;
171 }
172
173 /* Look up an absolute time within this database's dbmodules section. The time
174 * is expressed in the profile as an interval relative to the current time. */
175 static krb5_timestamp
get_time(testhandle h,const char * s1,const char * s2,const char * s3)176 get_time(testhandle h, const char *s1, const char *s2, const char *s3)
177 {
178 char *strval = get_string(h, s1, s2, s3);
179 krb5_deltat val;
180
181 if (strval == NULL)
182 return 0;
183 check(krb5_string_to_deltat(strval, &val));
184 free(strval);
185 return val + time(NULL);
186 }
187
188 /* Initialize kb_out with a key of type etype, using a hash of kvno, etype,
189 * salttype, and princstr for the key bytes. */
190 static void
make_keyblock(krb5_kvno kvno,krb5_enctype etype,int32_t salttype,const char * princstr,const krb5_data * realm,krb5_keyblock * kb_out)191 make_keyblock(krb5_kvno kvno, krb5_enctype etype, int32_t salttype,
192 const char *princstr, const krb5_data *realm,
193 krb5_keyblock *kb_out)
194 {
195 size_t keybytes, keylength, pos, n;
196 char *hashstr;
197 krb5_data d, rndin;
198 krb5_checksum cksum;
199
200 check(krb5_c_keylengths(NULL, etype, &keybytes, &keylength));
201 alloc_data(&rndin, keybytes);
202
203 /* Hash the kvno, enctype, salt type, and principal name together. */
204 if (asprintf(&hashstr, "%d %d %d %s %.*s", (int)kvno, (int)etype,
205 (int)salttype, princstr, (int)realm->length, realm->data) < 0)
206 abort();
207 d = string2data(hashstr);
208 check(krb5_c_make_checksum(NULL, CKSUMTYPE_NIST_SHA, NULL, 0, &d, &cksum));
209
210 /* Make the appropriate number of input bytes from the hash result. */
211 for (pos = 0; pos < keybytes; pos += n) {
212 n = (cksum.length < keybytes - pos) ? cksum.length : keybytes - pos;
213 memcpy(rndin.data + pos, cksum.contents, n);
214 }
215
216 kb_out->enctype = etype;
217 kb_out->length = keylength;
218 kb_out->contents = ealloc(keylength);
219 check(krb5_c_random_to_key(NULL, etype, &rndin, kb_out));
220 free(cksum.contents);
221 free(rndin.data);
222 free(hashstr);
223 }
224
225 /* Return key data for the given key/salt tuple strings, using hashes of the
226 * enctypes, salts, and princstr for the key contents. */
227 static void
make_keys(char ** strings,const char * princstr,const krb5_data * realm,krb5_db_entry * ent)228 make_keys(char **strings, const char *princstr, const krb5_data *realm,
229 krb5_db_entry *ent)
230 {
231 krb5_key_data *key_data, *kd;
232 krb5_keyblock kb;
233 int32_t *ks_list_sizes, nstrings, nkeys, i, j;
234 krb5_key_salt_tuple **ks_lists, *ks;
235 krb5_kvno *kvnos;
236 char *s;
237
238 for (nstrings = 0; strings[nstrings] != NULL; nstrings++);
239 ks_lists = ealloc(nstrings * sizeof(*ks_lists));
240 ks_list_sizes = ealloc(nstrings * sizeof(*ks_list_sizes));
241 kvnos = ealloc(nstrings * sizeof(*kvnos));
242
243 /* Convert each string into a key/salt tuple list and count the total
244 * number of key data structures needed. */
245 nkeys = 0;
246 for (i = 0; i < nstrings; i++) {
247 s = strings[i];
248 /* Read a leading kvno if present; otherwise assume kvno 1. */
249 if (isdigit(*s)) {
250 kvnos[i] = strtol(s, &s, 10);
251 while (isspace(*s))
252 s++;
253 } else {
254 kvnos[i] = 1;
255 }
256 check(krb5_string_to_keysalts(s, NULL, NULL, FALSE, &ks_lists[i],
257 &ks_list_sizes[i]));
258 nkeys += ks_list_sizes[i];
259 }
260
261 /* Turn each key/salt tuple into a key data entry. */
262 kd = key_data = ealloc(nkeys * sizeof(*kd));
263 for (i = 0; i < nstrings; i++) {
264 ks = ks_lists[i];
265 for (j = 0; j < ks_list_sizes[i]; j++) {
266 make_keyblock(kvnos[i], ks[j].ks_enctype, ks[j].ks_salttype,
267 princstr, realm, &kb);
268 kd->key_data_ver = 2;
269 kd->key_data_kvno = kvnos[i];
270 kd->key_data_type[0] = ks[j].ks_enctype;
271 kd->key_data_length[0] = kb.length;
272 kd->key_data_contents[0] = kb.contents;
273 kd->key_data_type[1] = ks[j].ks_salttype;
274 kd++;
275 }
276 }
277
278 for (i = 0; i < nstrings; i++)
279 free(ks_lists[i]);
280 free(ks_lists);
281 free(ks_list_sizes);
282 free(kvnos);
283 ent->key_data = key_data;
284 ent->n_key_data = nkeys;
285 }
286
287 static void
make_strings(char ** stringattrs,krb5_db_entry * ent)288 make_strings(char **stringattrs, krb5_db_entry *ent)
289 {
290 struct k5buf buf;
291 char **p;
292 const char *str, *sep;
293 krb5_tl_data *tl;
294
295 k5_buf_init_dynamic(&buf);
296 for (p = stringattrs; *p != NULL; p++) {
297 str = *p;
298 sep = strchr(str, ':');
299 assert(sep != NULL);
300 k5_buf_add_len(&buf, str, sep - str);
301 k5_buf_add_len(&buf, "\0", 1);
302 k5_buf_add_len(&buf, sep + 1, strlen(sep + 1) + 1);
303 }
304 assert(buf.data != NULL);
305
306 tl = ealloc(sizeof(*ent->tl_data));
307 tl->tl_data_next = NULL;
308 tl->tl_data_type = KRB5_TL_STRING_ATTRS;
309 tl->tl_data_length = buf.len;
310 tl->tl_data_contents = buf.data;
311 ent->tl_data = tl;
312 }
313
314 static krb5_error_code
test_init()315 test_init()
316 {
317 return 0;
318 }
319
320 static krb5_error_code
test_cleanup()321 test_cleanup()
322 {
323 return 0;
324 }
325
326 static krb5_error_code
test_open(krb5_context context,char * conf_section,char ** db_args,int mode)327 test_open(krb5_context context, char *conf_section, char **db_args, int mode)
328 {
329 testhandle h;
330
331 h = ealloc(sizeof(*h));
332 h->profile = context->profile;
333 h->section = estrdup(conf_section);
334 context->dal_handle->db_context = h;
335 return 0;
336 }
337
338 static krb5_error_code
test_close(krb5_context context)339 test_close(krb5_context context)
340 {
341 testhandle h = context->dal_handle->db_context;
342
343 free(h->section);
344 free(h);
345 return 0;
346 }
347
348 /* Return the principal name krbtgt/tgs_realm@our_realm. */
349 static krb5_principal
tgtname(krb5_context context,const krb5_data * tgs_realm,const krb5_data * our_realm)350 tgtname(krb5_context context, const krb5_data *tgs_realm,
351 const krb5_data *our_realm)
352 {
353 krb5_principal princ;
354
355 check(krb5_build_principal_ext(context, &princ,
356 our_realm->length, our_realm->data,
357 KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
358 tgs_realm->length, tgs_realm->data, 0));
359 princ->type = KRB5_NT_SRV_INST;
360 return princ;
361 }
362
363 /* Return true if search_for is within context's default realm or is an
364 * incoming cross-realm TGS name. */
365 static krb5_boolean
request_for_us(krb5_context context,krb5_const_principal search_for)366 request_for_us(krb5_context context, krb5_const_principal search_for)
367 {
368 char *defrealm;
369 krb5_data realm;
370 krb5_boolean for_us;
371 krb5_principal local_tgs;
372
373 check(krb5_get_default_realm(context, &defrealm));
374 realm = string2data(defrealm);
375 local_tgs = tgtname(context, &realm, &realm);
376 krb5_free_default_realm(context, defrealm);
377
378 for_us = krb5_realm_compare(context, local_tgs, search_for) ||
379 krb5_principal_compare_any_realm(context, local_tgs, search_for);
380 krb5_free_principal(context, local_tgs);
381 return for_us;
382 }
383
384 static krb5_error_code
test_get_principal(krb5_context context,krb5_const_principal search_for,unsigned int flags,krb5_db_entry ** entry)385 test_get_principal(krb5_context context, krb5_const_principal search_for,
386 unsigned int flags, krb5_db_entry **entry)
387 {
388 krb5_error_code ret;
389 krb5_principal princ = NULL, tgtprinc;
390 krb5_principal_data empty_princ = { KV5M_PRINCIPAL };
391 testhandle h = context->dal_handle->db_context;
392 char *search_name = NULL, *canon = NULL, *flagstr;
393 char **names, **key_strings, **stringattrs;
394 const char *ename;
395 krb5_db_entry *ent;
396
397 *entry = NULL;
398
399 if (!request_for_us(context, search_for))
400 return KRB5_KDB_NOENTRY;
401
402 check(krb5_unparse_name_flags(context, search_for,
403 KRB5_PRINCIPAL_UNPARSE_NO_REALM,
404 &search_name));
405 canon = get_string(h, "alias", search_name, NULL);
406 if (canon != NULL) {
407 check(krb5_parse_name(context, canon, &princ));
408 if (!krb5_realm_compare(context, search_for, princ)) {
409 /* Out of realm */
410 if ((flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) &&
411 ((flags & KRB5_KDB_FLAG_CANONICALIZE) ||
412 search_for->type == KRB5_NT_ENTERPRISE_PRINCIPAL)) {
413 /* Return a client referral by creating an entry with only the
414 * principal set. */
415 *entry = ealloc(sizeof(**entry));
416 (*entry)->princ = princ;
417 princ = NULL;
418 ret = 0;
419 goto cleanup;
420 } else if (flags & KRB5_KDB_FLAG_CANONICALIZE) {
421 /* Generate a server referral by looking up the TGT for the
422 * canonical name's realm. */
423 tgtprinc = tgtname(context, &princ->realm, &search_for->realm);
424 krb5_free_principal(context, princ);
425 princ = tgtprinc;
426
427 krb5_free_unparsed_name(context, search_name);
428 check(krb5_unparse_name_flags(context, princ,
429 KRB5_PRINCIPAL_UNPARSE_NO_REALM,
430 &search_name));
431 ename = search_name;
432 } else {
433 ret = KRB5_KDB_NOENTRY;
434 goto cleanup;
435 }
436 } else {
437 ename = canon;
438 }
439 } else {
440 check(krb5_copy_principal(context, search_for, &princ));
441 ename = search_name;
442 }
443
444 /* Check that the entry exists. */
445 set_names(h, "princs", ename, NULL);
446 ret = profile_get_relation_names(h->profile, h->names, &names);
447 if (ret == PROF_NO_RELATION) {
448 ret = KRB5_KDB_NOENTRY;
449 goto cleanup;
450 }
451 profile_free_list(names);
452
453 /* No error exits after this point. */
454
455 ent = ealloc(sizeof(*ent));
456 ent->princ = princ;
457 princ = NULL;
458
459 flagstr = get_string(h, "princs", ename, "flags");
460 if (flagstr != NULL) {
461 check(krb5_flagspec_to_mask(flagstr, &ent->attributes,
462 &ent->attributes));
463 }
464 free(flagstr);
465
466 ent->max_life = get_duration(h, "princs", ename, "maxlife");
467 ent->max_renewable_life = get_duration(h, "princs", ename, "maxrenewlife");
468 ent->expiration = get_time(h, "princs", ename, "expiration");
469 ent->pw_expiration = get_time(h, "princs", ename, "pwexpiration");
470
471 /* Leave last_success, last_failed, fail_auth_count zeroed. */
472 /* Leave e_data empty. */
473
474 set_names(h, "princs", ename, "keys");
475 ret = profile_get_values(h->profile, h->names, &key_strings);
476 if (ret != PROF_NO_RELATION) {
477 make_keys(key_strings, ename, &search_for->realm, ent);
478 profile_free_list(key_strings);
479 }
480
481 set_names(h, "princs", ename, "strings");
482 ret = profile_get_values(h->profile, h->names, &stringattrs);
483 if (ret != PROF_NO_RELATION) {
484 make_strings(stringattrs, ent);
485 profile_free_list(stringattrs);
486 }
487
488 /* We must include mod-princ data or kadm5_get_principal() won't work and
489 * we can't extract keys with kadmin.local. */
490 check(krb5_dbe_update_mod_princ_data(context, ent, 0, &empty_princ));
491
492 *entry = ent;
493 ret = 0;
494
495 cleanup:
496 krb5_free_unparsed_name(context, search_name);
497 krb5_free_principal(context, princ);
498 free(canon);
499 return ret;
500 }
501
502 static void
lookup_princ_by_cert(krb5_context context,const krb5_data * client_cert,krb5_principal * princ)503 lookup_princ_by_cert(krb5_context context, const krb5_data *client_cert,
504 krb5_principal *princ)
505 {
506 krb5_error_code ret;
507 char *cert_princ_name;
508
509 /* The test client sends a principal string instead of a cert. */
510 cert_princ_name = k5memdup0(client_cert->data, client_cert->length, &ret);
511 check(ret);
512
513 check(krb5_parse_name_flags(context, cert_princ_name,
514 KRB5_PRINCIPAL_PARSE_ENTERPRISE, princ));
515 free(cert_princ_name);
516 }
517
518 static krb5_error_code
test_get_s4u_x509_principal(krb5_context context,const krb5_data * client_cert,krb5_const_principal princ,unsigned int flags,krb5_db_entry ** entry)519 test_get_s4u_x509_principal(krb5_context context, const krb5_data *client_cert,
520 krb5_const_principal princ, unsigned int flags,
521 krb5_db_entry **entry)
522 {
523 krb5_error_code ret;
524 krb5_principal cert_princ, canon_princ;
525 testhandle h = context->dal_handle->db_context;
526 krb5_boolean match;
527 char *canon, *princ_name;
528
529 lookup_princ_by_cert(context, client_cert, &cert_princ);
530
531 ret = test_get_principal(context, cert_princ, flags, entry);
532 krb5_free_principal(context, cert_princ);
533 if (ret || (flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY))
534 return ret;
535
536 if (!krb5_realm_compare(context, princ, (*entry)->princ))
537 abort();
538
539 if (princ->length == 0 ||
540 krb5_principal_compare(context, princ, (*entry)->princ))
541 return 0;
542
543 match = FALSE;
544 check(krb5_unparse_name_flags(context, princ,
545 KRB5_PRINCIPAL_UNPARSE_NO_REALM,
546 &princ_name));
547 canon = get_string(h, "alias", princ_name, NULL);
548 krb5_free_unparsed_name(context, princ_name);
549 if (canon != NULL) {
550 check(krb5_parse_name(context, canon, &canon_princ));
551 match = krb5_principal_compare(context, canon_princ, (*entry)->princ);
552 krb5_free_principal(context, canon_princ);
553 }
554
555 free(canon);
556 return match ? 0 : KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
557 }
558
559 static krb5_error_code
test_fetch_master_key(krb5_context context,krb5_principal mname,krb5_keyblock * key_out,krb5_kvno * kvno_out,char * db_args)560 test_fetch_master_key(krb5_context context, krb5_principal mname,
561 krb5_keyblock *key_out, krb5_kvno *kvno_out,
562 char *db_args)
563 {
564 memset(key_out, 0, sizeof(*key_out));
565 *kvno_out = 0;
566 return 0;
567 }
568
569 static krb5_error_code
test_fetch_master_key_list(krb5_context context,krb5_principal mname,const krb5_keyblock * key,krb5_keylist_node ** mkeys_out)570 test_fetch_master_key_list(krb5_context context, krb5_principal mname,
571 const krb5_keyblock *key,
572 krb5_keylist_node **mkeys_out)
573 {
574 /* krb5_dbe_get_mkvno() returns an error if we produce NULL, so return an
575 * empty node to make kadm5_get_principal() work. */
576 *mkeys_out = ealloc(sizeof(**mkeys_out));
577 return 0;
578 }
579
580 static krb5_error_code
test_decrypt_key_data(krb5_context context,const krb5_keyblock * mkey,const krb5_key_data * kd,krb5_keyblock * key_out,krb5_keysalt * salt_out)581 test_decrypt_key_data(krb5_context context, const krb5_keyblock *mkey,
582 const krb5_key_data *kd, krb5_keyblock *key_out,
583 krb5_keysalt *salt_out)
584 {
585 key_out->magic = KV5M_KEYBLOCK;
586 key_out->enctype = kd->key_data_type[0];
587 key_out->length = kd->key_data_length[0];
588 key_out->contents = ealloc(key_out->length);
589 memcpy(key_out->contents, kd->key_data_contents[0], key_out->length);
590 if (salt_out != NULL) {
591 salt_out->type = (kd->key_data_ver > 1) ? kd->key_data_type[1] :
592 KRB5_KDB_SALTTYPE_NORMAL;
593 salt_out->data = empty_data();
594 }
595 return 0;
596 }
597
598 static krb5_error_code
test_encrypt_key_data(krb5_context context,const krb5_keyblock * mkey,const krb5_keyblock * key,const krb5_keysalt * salt,int kvno,krb5_key_data * kd_out)599 test_encrypt_key_data(krb5_context context, const krb5_keyblock *mkey,
600 const krb5_keyblock *key, const krb5_keysalt *salt,
601 int kvno, krb5_key_data *kd_out)
602 {
603 memset(kd_out, 0, sizeof(*kd_out));
604 kd_out->key_data_ver = 2;
605 kd_out->key_data_kvno = kvno;
606 kd_out->key_data_type[0] = key->enctype;
607 kd_out->key_data_length[0] = key->length;
608 kd_out->key_data_contents[0] = ealloc(key->length);
609 memcpy(kd_out->key_data_contents[0], key->contents, key->length);
610 kd_out->key_data_type[1] = (salt != NULL) ? salt->type :
611 KRB5_KDB_SALTTYPE_NORMAL;
612 return 0;
613 }
614
615 typedef struct {
616 char *pac_princ;
617 struct {
618 char *proxy_target;
619 char *impersonator;
620 } deleg_info;
621 krb5_boolean not_delegated;
622 krb5_pac pac;
623 } pac_info;
624
625 static void
free_pac_info(krb5_context context,pac_info * info)626 free_pac_info(krb5_context context, pac_info *info)
627 {
628 if (info == NULL)
629 return;
630
631 free(info->pac_princ);
632 free(info->deleg_info.proxy_target);
633 free(info->deleg_info.impersonator);
634 krb5_pac_free(context, info->pac);
635 free(info);
636 }
637
638 /*
639 * Create a PAC object with a fake logon-info blob. Instead of a real
640 * KERB_VALIDATION_INFO structure, store a byte indicating whether the
641 * USER_NOT_DELEGATED bit is set.
642 */
643 static krb5_error_code
create_pac(krb5_context context,krb5_boolean not_delegated,krb5_pac * pac_out)644 create_pac(krb5_context context, krb5_boolean not_delegated, krb5_pac *pac_out)
645 {
646 krb5_data data;
647 krb5_pac pac;
648 char nd;
649
650 nd = not_delegated ? 1 : 0;
651 data = make_data(&nd, 1);
652 check(krb5_pac_init(context, &pac));
653 check(krb5_pac_add_buffer(context, pac, KRB5_PAC_LOGON_INFO, &data));
654
655 *pac_out = pac;
656 return 0;
657 }
658
659 /* Create a fake PAC, setting the USER_NOT_DELEGATED bit if the client DB entry
660 * disallows forwardable tickets. */
661 static krb5_error_code
create_pac_db(krb5_context context,krb5_db_entry * client,krb5_pac * pac_out)662 create_pac_db(krb5_context context, krb5_db_entry *client, krb5_pac *pac_out)
663 {
664 krb5_boolean not_delegated;
665 /* Use disallow_forwardable as delegation_not_allowed attribute */
666 not_delegated = (client->attributes & KRB5_KDB_DISALLOW_FORWARDABLE);
667 return create_pac(context, not_delegated, pac_out);
668 }
669
670 /* Locate the PAC in tgt_authdata and set *pac_out to its PAC object
671 * representation. Set it to NULL if no PAC is present. */
672 static void
parse_ticket_pac(krb5_context context,krb5_authdata ** tgt_auth_data,krb5_pac * pac_out)673 parse_ticket_pac(krb5_context context, krb5_authdata **tgt_auth_data,
674 krb5_pac *pac_out)
675 {
676 krb5_authdata **authdata;
677
678 *pac_out = NULL;
679
680 check(krb5_find_authdata(context, tgt_auth_data, NULL,
681 KRB5_AUTHDATA_WIN2K_PAC, &authdata));
682 if (authdata == NULL)
683 return;
684 assert(authdata[1] == NULL);
685 check(krb5_pac_parse(context, authdata[0]->contents, authdata[0]->length,
686 pac_out));
687 krb5_free_authdata(context, authdata);
688 }
689
690 /* Verify the KDC signature against the local TGT key. tgt_key must be the
691 * decrypted first key data entry of tgt. */
692 static krb5_error_code
verify_kdc_signature(krb5_context context,krb5_pac pac,krb5_keyblock * tgt_key,krb5_db_entry * tgt)693 verify_kdc_signature(krb5_context context, krb5_pac pac,
694 krb5_keyblock *tgt_key, krb5_db_entry *tgt)
695 {
696 krb5_error_code ret;
697 krb5_key_data *kd;
698 krb5_keyblock old_key;
699 krb5_kvno kvno;
700 int tries;
701
702 ret = krb5_pac_verify(context, pac, 0, NULL, NULL, tgt_key);
703 if (ret != KRB5KRB_AP_ERR_BAD_INTEGRITY)
704 return ret;
705
706 kvno = tgt->key_data[0].key_data_kvno - 1;
707
708 /* There is no kvno in PAC signatures, so try two previous versions. */
709 for (tries = 2; tries > 0 && kvno > 0; tries--, kvno--) {
710 ret = krb5_dbe_find_enctype(context, tgt, -1, -1, kvno, &kd);
711 if (ret)
712 return KRB5KRB_AP_ERR_BAD_INTEGRITY;
713 ret = krb5_dbe_decrypt_key_data(context, NULL, kd, &old_key, NULL);
714 if (ret)
715 return ret;
716 ret = krb5_pac_verify(context, pac, 0, NULL, NULL, &old_key);
717 krb5_free_keyblock_contents(context, &old_key);
718 if (!ret)
719 return 0;
720
721 /* Try the next lower kvno on the next iteration. */
722 kvno = kd->key_data_kvno - 1;
723 }
724
725 return KRB5KRB_AP_ERR_BAD_INTEGRITY;
726 }
727
728 static krb5_error_code
verify_ticket_pac(krb5_context context,krb5_pac pac,unsigned int flags,krb5_const_principal client_princ,krb5_boolean check_realm,krb5_keyblock * server_key,krb5_keyblock * local_tgt_key,krb5_db_entry * local_tgt,krb5_timestamp authtime)729 verify_ticket_pac(krb5_context context, krb5_pac pac, unsigned int flags,
730 krb5_const_principal client_princ, krb5_boolean check_realm,
731 krb5_keyblock *server_key, krb5_keyblock *local_tgt_key,
732 krb5_db_entry *local_tgt, krb5_timestamp authtime)
733 {
734 check(krb5_pac_verify_ext(context, pac, authtime, client_princ, server_key,
735 NULL, check_realm));
736 if (flags & KRB5_KDB_FLAG_CROSS_REALM)
737 return 0;
738 return verify_kdc_signature(context, pac, local_tgt_key, local_tgt);
739 }
740
741 static void
get_pac_info(krb5_context context,krb5_authdata ** in_authdata,pac_info ** info_out)742 get_pac_info(krb5_context context, krb5_authdata **in_authdata,
743 pac_info **info_out)
744 {
745 krb5_error_code ret;
746 krb5_pac pac = NULL;
747 krb5_data data;
748 char *sep = NULL;
749 pac_info *info;
750
751 *info_out = NULL;
752
753 parse_ticket_pac(context, in_authdata, &pac);
754 if (pac == NULL)
755 return;
756
757 info = ealloc(sizeof(*info));
758
759 /* Read the fake logon-info buffer from the PAC and set not_delegated
760 * according to the byte value. */
761 check(krb5_pac_get_client_info(context, pac, NULL, &info->pac_princ));
762 check(krb5_pac_get_buffer(context, pac, KRB5_PAC_LOGON_INFO, &data));
763 assert(data.length == 1);
764 info->not_delegated = *data.data;
765 krb5_free_data_contents(context, &data);
766
767 ret = krb5_pac_get_buffer(context, pac, KRB5_PAC_DELEGATION_INFO, &data);
768 if (ret && ret != ENOENT)
769 abort();
770 if (!ret) {
771 sep = memchr(data.data, ':', data.length);
772 assert(sep != NULL);
773 info->deleg_info.proxy_target = k5memdup0(data.data, sep - data.data,
774 &ret);
775 check(ret);
776 info->deleg_info.impersonator = k5memdup0(sep + 1, data.length - 1 -
777 (sep - data.data), &ret);
778 check(ret);
779 krb5_free_data_contents(context, &data);
780 }
781
782 info->pac = pac;
783 *info_out = info;
784 }
785
786 /* Add a fake delegation-info buffer to pac containing the proxy target and
787 * impersonator from info. */
788 static void
add_delegation_info(krb5_context context,krb5_pac pac,pac_info * info)789 add_delegation_info(krb5_context context, krb5_pac pac, pac_info *info)
790 {
791 krb5_data data;
792 char *str;
793
794 if (info->deleg_info.proxy_target == NULL)
795 return;
796
797 if (asprintf(&str, "%s:%s", info->deleg_info.proxy_target,
798 info->deleg_info.impersonator) < 0)
799 abort();
800 data = string2data(str);
801 check(krb5_pac_add_buffer(context, pac, KRB5_PAC_DELEGATION_INFO, &data));
802 free(str);
803 }
804
805 /* Set *out to an AD-IF-RELEVANT authdata element containing a PAC authdata
806 * element with contents pac_data. */
807 static void
encode_pac_ad(krb5_context context,krb5_data * pac_data,krb5_authdata ** out)808 encode_pac_ad(krb5_context context, krb5_data *pac_data, krb5_authdata **out)
809 {
810 krb5_authdata pac_ad, *list[2], **ifrel;
811
812 pac_ad.magic = KV5M_AUTHDATA;
813 pac_ad.ad_type = KRB5_AUTHDATA_WIN2K_PAC;
814 pac_ad.contents = (krb5_octet *)pac_data->data;;
815 pac_ad.length = pac_data->length;
816 list[0] = &pac_ad;
817 list[1] = NULL;
818
819 check(krb5_encode_authdata_container(context, KRB5_AUTHDATA_IF_RELEVANT,
820 list, &ifrel));
821 assert(ifrel[1] == NULL);
822 *out = ifrel[0];
823 free(ifrel);
824 }
825
826 /* Parse a PAC client-info string into a principal name. If xrealm_s4u is
827 * true, expect a realm in the string. */
828 static krb5_error_code
parse_pac_princ(krb5_context context,krb5_boolean xrealm_s4u,char * pac_princ,krb5_principal * client_out)829 parse_pac_princ(krb5_context context, krb5_boolean xrealm_s4u, char *pac_princ,
830 krb5_principal *client_out)
831 {
832 int n_atsigns = 0, flags = 0;
833 char *p = pac_princ;
834
835 while (*p++) {
836 if (*p == '@')
837 n_atsigns++;
838 }
839 if (xrealm_s4u) {
840 flags |= KRB5_PRINCIPAL_PARSE_REQUIRE_REALM;
841 n_atsigns--;
842 } else {
843 flags |= KRB5_PRINCIPAL_PARSE_NO_REALM;
844 }
845 assert(n_atsigns == 0 || n_atsigns == 1);
846 if (n_atsigns == 1)
847 flags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE;
848 check(krb5_parse_name_flags(context, pac_princ, flags, client_out));
849 (*client_out)->type = KRB5_NT_MS_PRINCIPAL;
850 return 0;
851 }
852
853 /* Set *ad_out to a fake PAC for testing, or to NULL if it doesn't make sense
854 * to generate a PAC for the request. */
855 static void
generate_pac(krb5_context context,unsigned int flags,krb5_const_principal client_princ,krb5_const_principal server_princ,krb5_db_entry * client,krb5_db_entry * header_server,krb5_db_entry * local_tgt,krb5_keyblock * server_key,krb5_keyblock * header_key,krb5_keyblock * local_tgt_key,krb5_timestamp authtime,pac_info * info,krb5_authdata ** ad_out)856 generate_pac(krb5_context context, unsigned int flags,
857 krb5_const_principal client_princ,
858 krb5_const_principal server_princ, krb5_db_entry *client,
859 krb5_db_entry *header_server, krb5_db_entry *local_tgt,
860 krb5_keyblock *server_key, krb5_keyblock *header_key,
861 krb5_keyblock *local_tgt_key, krb5_timestamp authtime,
862 pac_info *info, krb5_authdata **ad_out)
863 {
864 krb5_boolean sign_realm, check_realm;
865 krb5_data pac_data;
866 krb5_pac pac = NULL;
867 krb5_principal pac_princ = NULL;
868
869 *ad_out = NULL;
870
871 check_realm = ((flags & KRB5_KDB_FLAGS_S4U) &&
872 (flags & KRB5_KDB_FLAG_CROSS_REALM));
873 sign_realm = ((flags & KRB5_KDB_FLAGS_S4U) &&
874 (flags & KRB5_KDB_FLAG_ISSUING_REFERRAL));
875
876 if (client != NULL &&
877 ((flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) ||
878 (flags & KRB5_KDB_FLAG_PROTOCOL_TRANSITION))) {
879 /* For AS or local-realm S4U2Self, generate an initial PAC. */
880 check(create_pac_db(context, client, &pac));
881 } else if (info == NULL) {
882 /* If there is no input PAC, do not generate one. */
883 assert((flags & KRB5_KDB_FLAGS_S4U) == 0);
884 return;
885 } else {
886 if (IS_TGS_PRINC(server_princ) &&
887 info->deleg_info.proxy_target != NULL) {
888 /* RBCD transitive trust. */
889 assert(flags & KRB5_KDB_FLAG_CROSS_REALM);
890 assert(!(flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION));
891 check(parse_pac_princ(context, TRUE, info->pac_princ, &pac_princ));
892 client_princ = pac_princ;
893 check_realm = TRUE;
894 sign_realm = TRUE;
895 } else if ((flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) &&
896 !(flags & KRB5_KDB_FLAG_CROSS_REALM)) {
897 /*
898 * Initial RBCD and old constrained delegation requests to
899 * impersonator realm; create delegation info blob. We cannot
900 * assume that proxy_target is NULL as the evidence ticket could
901 * have been acquired via constrained delegation.
902 */
903 free(info->deleg_info.proxy_target);
904 check(krb5_unparse_name_flags(context, server_princ,
905 KRB5_PRINCIPAL_UNPARSE_NO_REALM,
906 &info->deleg_info.proxy_target));
907 /* This is supposed to be a list of impersonators, but we currently
908 * only deal with one. */
909 free(info->deleg_info.impersonator);
910 check(krb5_unparse_name(context, header_server->princ,
911 &info->deleg_info.impersonator));
912 } else if (flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) {
913 /* Last cross realm RBCD request to proxy realm. */
914 assert(info->deleg_info.proxy_target != NULL);
915 }
916
917 /* We have already verified the PAC in get_authdata_info, but we should
918 * be able to verify the signatures here as well. */
919 check(verify_ticket_pac(context, info->pac, flags, client_princ,
920 check_realm, header_key, local_tgt_key,
921 local_tgt, authtime));
922
923 /* Create a new pac as we may be altering pac principal's realm */
924 check(create_pac(context, info->not_delegated, &pac));
925 add_delegation_info(context, pac, info);
926 }
927 check(krb5_pac_sign_ext(context, pac, authtime, client_princ, server_key,
928 local_tgt_key, sign_realm, &pac_data));
929 krb5_pac_free(context, pac);
930 krb5_free_principal(context, pac_princ);
931 encode_pac_ad(context, &pac_data, ad_out);
932 krb5_free_data_contents(context, &pac_data);
933 }
934
935 static krb5_error_code
test_sign_authdata(krb5_context context,unsigned int flags,krb5_const_principal client_princ,krb5_const_principal server_princ,krb5_db_entry * client,krb5_db_entry * server,krb5_db_entry * header_server,krb5_db_entry * local_tgt,krb5_keyblock * client_key,krb5_keyblock * server_key,krb5_keyblock * header_key,krb5_keyblock * local_tgt_key,krb5_keyblock * session_key,krb5_timestamp authtime,krb5_authdata ** tgt_auth_data,void * ad_info,krb5_data *** auth_indicators,krb5_authdata *** signed_auth_data)936 test_sign_authdata(krb5_context context, unsigned int flags,
937 krb5_const_principal client_princ,
938 krb5_const_principal server_princ, krb5_db_entry *client,
939 krb5_db_entry *server, krb5_db_entry *header_server,
940 krb5_db_entry *local_tgt, krb5_keyblock *client_key,
941 krb5_keyblock *server_key, krb5_keyblock *header_key,
942 krb5_keyblock *local_tgt_key, krb5_keyblock *session_key,
943 krb5_timestamp authtime, krb5_authdata **tgt_auth_data,
944 void *ad_info, krb5_data ***auth_indicators,
945 krb5_authdata ***signed_auth_data)
946 {
947 krb5_authdata *pac_ad = NULL, *test_ad = NULL, **list;
948 krb5_data **inds, d;
949 int i, val;
950
951 /* Possibly create a PAC authdata element. */
952 generate_pac(context, flags, client_princ, server_princ, client,
953 header_server, local_tgt, server_key, header_key,
954 local_tgt_key, authtime, ad_info, &pac_ad);
955
956 /* Always create a TEST_AD_TYPE element. */
957 test_ad = ealloc(sizeof(*test_ad));
958 test_ad->magic = KV5M_AUTHDATA;
959 test_ad->ad_type = TEST_AD_TYPE;
960 test_ad->contents = (uint8_t *)estrdup("db-authdata-test");
961 test_ad->length = strlen((char *)test_ad->contents);
962
963 /* Assemble the authdata into a one-element or two-element list.
964 * The PAC must be the first element. */
965 list = ealloc(3 * sizeof(*list));
966 list[0] = (pac_ad != NULL) ? pac_ad : test_ad;
967 list[1] = (pac_ad != NULL) ? test_ad : NULL;
968 list[2] = NULL;
969 *signed_auth_data = list;
970
971 /* If we see an auth indicator "dbincrX", replace the whole indicator list
972 * with "dbincr{X+1}". */
973 inds = *auth_indicators;
974 for (i = 0; inds != NULL && inds[i] != NULL; i++) {
975 if (inds[i]->length == 7 && memcmp(inds[i]->data, "dbincr", 6) == 0) {
976 val = inds[i]->data[6];
977 k5_free_data_ptr_list(inds);
978 inds = ealloc(2 * sizeof(*inds));
979 d = string2data("dbincr0");
980 check(krb5_copy_data(context, &d, &inds[0]));
981 inds[0]->data[6] = val + 1;
982 inds[1] = NULL;
983 *auth_indicators = inds;
984 break;
985 }
986 }
987
988 return 0;
989 }
990
991 static krb5_boolean
match_in_table(krb5_context context,const char * table,const char * sprinc,const char * tprinc)992 match_in_table(krb5_context context, const char *table, const char *sprinc,
993 const char *tprinc)
994 {
995 testhandle h = context->dal_handle->db_context;
996 krb5_error_code ret;
997 char **values, **v;
998 krb5_boolean found = FALSE;
999
1000 set_names(h, table, sprinc, NULL);
1001 ret = profile_get_values(h->profile, h->names, &values);
1002 assert(ret == 0 || ret == PROF_NO_RELATION);
1003 if (ret)
1004 return FALSE;
1005 for (v = values; *v != NULL; v++) {
1006 if (strcmp(*v, tprinc) == 0) {
1007 found = TRUE;
1008 break;
1009 }
1010 }
1011 profile_free_list(values);
1012 return found;
1013 }
1014
1015 static krb5_error_code
test_check_allowed_to_delegate(krb5_context context,krb5_const_principal client,const krb5_db_entry * server,krb5_const_principal proxy)1016 test_check_allowed_to_delegate(krb5_context context,
1017 krb5_const_principal client,
1018 const krb5_db_entry *server,
1019 krb5_const_principal proxy)
1020 {
1021 char *sprinc, *tprinc;
1022 krb5_boolean found = FALSE;
1023
1024 check(krb5_unparse_name_flags(context, server->princ,
1025 KRB5_PRINCIPAL_UNPARSE_NO_REALM, &sprinc));
1026 check(krb5_unparse_name_flags(context, proxy,
1027 KRB5_PRINCIPAL_UNPARSE_NO_REALM, &tprinc));
1028 found = match_in_table(context, "delegation", sprinc, tprinc);
1029 krb5_free_unparsed_name(context, sprinc);
1030 krb5_free_unparsed_name(context, tprinc);
1031 return found ? 0 : KRB5KDC_ERR_POLICY;
1032 }
1033
1034 static krb5_error_code
test_allowed_to_delegate_from(krb5_context context,krb5_const_principal client,krb5_const_principal server,void * server_ad_info,const krb5_db_entry * proxy)1035 test_allowed_to_delegate_from(krb5_context context,
1036 krb5_const_principal client,
1037 krb5_const_principal server,
1038 void *server_ad_info, const krb5_db_entry *proxy)
1039 {
1040 char *sprinc, *tprinc;
1041 pac_info *info = (pac_info *)server_ad_info;
1042 krb5_boolean found = FALSE;
1043
1044 check(krb5_unparse_name(context, proxy->princ, &sprinc));
1045 check(krb5_unparse_name(context, server, &tprinc));
1046 assert(strncmp(info->pac_princ, tprinc, strlen(info->pac_princ)) == 0);
1047 found = match_in_table(context, "rbcd", sprinc, tprinc);
1048 krb5_free_unparsed_name(context, sprinc);
1049 krb5_free_unparsed_name(context, tprinc);
1050 return found ? 0 : KRB5KDC_ERR_POLICY;
1051 }
1052
1053 static krb5_error_code
test_get_authdata_info(krb5_context context,unsigned int flags,krb5_authdata ** in_authdata,krb5_const_principal client_princ,krb5_const_principal server_princ,krb5_keyblock * server_key,krb5_keyblock * krbtgt_key,krb5_db_entry * krbtgt,krb5_timestamp authtime,void ** ad_info_out,krb5_principal * client_out)1054 test_get_authdata_info(krb5_context context, unsigned int flags,
1055 krb5_authdata **in_authdata,
1056 krb5_const_principal client_princ,
1057 krb5_const_principal server_princ,
1058 krb5_keyblock *server_key, krb5_keyblock *krbtgt_key,
1059 krb5_db_entry *krbtgt, krb5_timestamp authtime,
1060 void **ad_info_out, krb5_principal *client_out)
1061 {
1062 pac_info *info = NULL;
1063 krb5_boolean rbcd_transitive, xrealm_s4u;
1064 krb5_principal pac_princ = NULL;
1065 char *proxy_name = NULL, *impersonator_name = NULL;
1066
1067 get_pac_info(context, in_authdata, &info);
1068 if (info == NULL)
1069 return 0;
1070
1071 /* Transitive RBCD requests are not flagged as constrained delegation */
1072 if (info->not_delegated &&
1073 (info->deleg_info.proxy_target ||
1074 (flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION))) {
1075 free_pac_info(context, info);
1076 return KRB5KDC_ERR_BADOPTION;
1077 }
1078
1079 rbcd_transitive = IS_TGS_PRINC(server_princ) &&
1080 (flags & KRB5_KDB_FLAG_CROSS_REALM) && info->deleg_info.proxy_target &&
1081 !(flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION);
1082
1083 xrealm_s4u = rbcd_transitive || ((flags & KRB5_KDB_FLAG_CROSS_REALM) &&
1084 (flags & KRB5_KDB_FLAGS_S4U));
1085
1086 check(parse_pac_princ(context, xrealm_s4u, info->pac_princ, &pac_princ));
1087
1088 /* Cross-realm and transitive trust RBCD requests */
1089 if (rbcd_transitive || ((flags & KRB5_KDB_FLAG_CROSS_REALM) &&
1090 (flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION))) {
1091 assert(info->deleg_info.proxy_target != NULL);
1092 assert(info->deleg_info.impersonator != NULL);
1093 /* We must be able to find the impersonator in the delegation info. */
1094 assert(!krb5_principal_compare(context, client_princ, pac_princ));
1095 check(krb5_unparse_name(context, client_princ, &impersonator_name));
1096 assert(strcmp(info->deleg_info.impersonator, impersonator_name) == 0);
1097 krb5_free_unparsed_name(context, impersonator_name);
1098 client_princ = pac_princ;
1099 /* In the non-transitive case we can match the proxy too. */
1100 if (!rbcd_transitive) {
1101 check(krb5_unparse_name_flags(context, server_princ,
1102 KRB5_PRINCIPAL_UNPARSE_NO_REALM,
1103 &proxy_name));
1104 assert(info->deleg_info.proxy_target != NULL);
1105 assert(strcmp(info->deleg_info.proxy_target, proxy_name) == 0);
1106 krb5_free_unparsed_name(context, proxy_name);
1107 }
1108 }
1109
1110 check(verify_ticket_pac(context, info->pac, flags, client_princ,
1111 xrealm_s4u, server_key, krbtgt_key, krbtgt,
1112 authtime));
1113
1114 *ad_info_out = info;
1115 if (client_out != NULL)
1116 *client_out = pac_princ;
1117 else
1118 krb5_free_principal(context, pac_princ);
1119
1120 return 0;
1121 }
1122
1123 static void
test_free_authdata_info(krb5_context context,void * ad_info)1124 test_free_authdata_info(krb5_context context, void *ad_info)
1125 {
1126 pac_info *info = (pac_info *)ad_info;
1127
1128 free_pac_info(context, info);
1129 }
1130
1131 kdb_vftabl PLUGIN_SYMBOL_NAME(krb5_test, kdb_function_table) = {
1132 KRB5_KDB_DAL_MAJOR_VERSION, /* major version number */
1133 0, /* minor version number */
1134 test_init,
1135 test_cleanup,
1136 test_open,
1137 test_close,
1138 NULL, /* create */
1139 NULL, /* destroy */
1140 NULL, /* get_age */
1141 NULL, /* lock */
1142 NULL, /* unlock */
1143 test_get_principal,
1144 NULL, /* put_principal */
1145 NULL, /* delete_principal */
1146 NULL, /* rename_principal */
1147 NULL, /* iterate */
1148 NULL, /* create_policy */
1149 NULL, /* get_policy */
1150 NULL, /* put_policy */
1151 NULL, /* iter_policy */
1152 NULL, /* delete_policy */
1153 test_fetch_master_key,
1154 test_fetch_master_key_list,
1155 NULL, /* store_master_key_list */
1156 NULL, /* dbe_search_enctype */
1157 NULL, /* change_pwd */
1158 NULL, /* promote_db */
1159 test_decrypt_key_data,
1160 test_encrypt_key_data,
1161 test_sign_authdata,
1162 NULL, /* check_transited_realms */
1163 NULL, /* check_policy_as */
1164 NULL, /* check_policy_tgs */
1165 NULL, /* audit_as_req */
1166 NULL, /* refresh_config */
1167 test_check_allowed_to_delegate,
1168 NULL, /* free_principal_e_data */
1169 test_get_s4u_x509_principal,
1170 test_allowed_to_delegate_from,
1171 test_get_authdata_info,
1172 test_free_authdata_info
1173 };
1174