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 static krb5_error_code
test_get_principal(krb5_context context,krb5_const_principal search_for,unsigned int flags,krb5_db_entry ** entry)364 test_get_principal(krb5_context context, krb5_const_principal search_for,
365                    unsigned int flags, krb5_db_entry **entry)
366 {
367     krb5_error_code ret;
368     krb5_principal princ = NULL, tgtprinc;
369     krb5_principal_data empty_princ = { KV5M_PRINCIPAL };
370     testhandle h = context->dal_handle->db_context;
371     char *search_name = NULL, *canon = NULL, *flagstr;
372     char **names, **key_strings, **stringattrs;
373     const char *ename;
374     krb5_db_entry *ent;
375 
376     *entry = NULL;
377 
378     check(krb5_unparse_name_flags(context, search_for,
379                                   KRB5_PRINCIPAL_UNPARSE_NO_REALM,
380                                   &search_name));
381     canon = get_string(h, "alias", search_name, NULL);
382     if (canon != NULL) {
383         check(krb5_parse_name(context, canon, &princ));
384         if (!krb5_realm_compare(context, search_for, princ)) {
385             /* Out of realm */
386             if ((flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) &&
387                 ((flags & KRB5_KDB_FLAG_CANONICALIZE) ||
388                  search_for->type == KRB5_NT_ENTERPRISE_PRINCIPAL)) {
389                 /* Return a client referral by creating an entry with only the
390                  * principal set. */
391                 *entry = ealloc(sizeof(**entry));
392                 (*entry)->princ = princ;
393                 princ = NULL;
394                 ret = 0;
395                 goto cleanup;
396             } else if (flags & KRB5_KDB_FLAG_CANONICALIZE) {
397                 /* Generate a server referral by looking up the TGT for the
398                  * canonical name's realm. */
399                 tgtprinc = tgtname(context, &princ->realm, &search_for->realm);
400                 krb5_free_principal(context, princ);
401                 princ = tgtprinc;
402 
403                 krb5_free_unparsed_name(context, search_name);
404                 check(krb5_unparse_name_flags(context, princ,
405                                               KRB5_PRINCIPAL_UNPARSE_NO_REALM,
406                                               &search_name));
407                 ename = search_name;
408             } else {
409                 ret = KRB5_KDB_NOENTRY;
410                 goto cleanup;
411             }
412         } else {
413             ename = canon;
414         }
415     } else {
416         check(krb5_copy_principal(context, search_for, &princ));
417         ename = search_name;
418     }
419 
420     /* Check that the entry exists. */
421     set_names(h, "princs", ename, NULL);
422     ret = profile_get_relation_names(h->profile, h->names, &names);
423     if (ret == PROF_NO_RELATION) {
424         ret = KRB5_KDB_NOENTRY;
425         goto cleanup;
426     }
427     profile_free_list(names);
428 
429     /* No error exits after this point. */
430 
431     ent = ealloc(sizeof(*ent));
432     ent->princ = princ;
433     princ = NULL;
434 
435     flagstr = get_string(h, "princs", ename, "flags");
436     if (flagstr != NULL) {
437         check(krb5_flagspec_to_mask(flagstr, &ent->attributes,
438                                     &ent->attributes));
439     }
440     free(flagstr);
441 
442     ent->max_life = get_duration(h, "princs", ename, "maxlife");
443     ent->max_renewable_life = get_duration(h, "princs", ename, "maxrenewlife");
444     ent->expiration = get_time(h, "princs", ename, "expiration");
445     ent->pw_expiration = get_time(h, "princs", ename, "pwexpiration");
446 
447     /* Leave last_success, last_failed, fail_auth_count zeroed. */
448     /* Leave e_data empty. */
449 
450     set_names(h, "princs", ename, "keys");
451     ret = profile_get_values(h->profile, h->names, &key_strings);
452     if (ret != PROF_NO_RELATION) {
453         make_keys(key_strings, ename, &search_for->realm, ent);
454         profile_free_list(key_strings);
455     }
456 
457     set_names(h, "princs", ename, "strings");
458     ret = profile_get_values(h->profile, h->names, &stringattrs);
459     if (ret != PROF_NO_RELATION) {
460         make_strings(stringattrs, ent);
461         profile_free_list(stringattrs);
462     }
463 
464     /* We must include mod-princ data or kadm5_get_principal() won't work and
465      * we can't extract keys with kadmin.local. */
466     check(krb5_dbe_update_mod_princ_data(context, ent, 0, &empty_princ));
467 
468     *entry = ent;
469     ret = 0;
470 
471 cleanup:
472     krb5_free_unparsed_name(context, search_name);
473     krb5_free_principal(context, princ);
474     free(canon);
475     return ret;
476 }
477 
478 static void
lookup_princ_by_cert(krb5_context context,const krb5_data * client_cert,krb5_principal * princ)479 lookup_princ_by_cert(krb5_context context, const krb5_data *client_cert,
480                      krb5_principal *princ)
481 {
482     krb5_error_code ret;
483     char *cert_princ_name;
484 
485     /* The test client sends a principal string instead of a cert. */
486     cert_princ_name = k5memdup0(client_cert->data, client_cert->length, &ret);
487     check(ret);
488 
489     check(krb5_parse_name(context, cert_princ_name, princ));
490     free(cert_princ_name);
491 }
492 
493 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)494 test_get_s4u_x509_principal(krb5_context context, const krb5_data *client_cert,
495                             krb5_const_principal princ, unsigned int flags,
496                             krb5_db_entry **entry)
497 {
498     krb5_error_code ret;
499     krb5_principal cert_princ, canon_princ;
500     testhandle h = context->dal_handle->db_context;
501     krb5_boolean match;
502     char *canon, *princ_name;
503 
504     lookup_princ_by_cert(context, client_cert, &cert_princ);
505 
506     ret = test_get_principal(context, cert_princ, flags, entry);
507     krb5_free_principal(context, cert_princ);
508     if (ret || (flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY))
509         return ret;
510 
511     if (!krb5_realm_compare(context, princ, (*entry)->princ))
512         abort();
513 
514     if (princ->length == 0 ||
515         krb5_principal_compare(context, princ, (*entry)->princ))
516         return 0;
517 
518     match = FALSE;
519     check(krb5_unparse_name_flags(context, princ,
520                                   KRB5_PRINCIPAL_UNPARSE_NO_REALM,
521                                   &princ_name));
522     canon = get_string(h, "alias", princ_name, NULL);
523     krb5_free_unparsed_name(context, princ_name);
524     if (canon != NULL) {
525         check(krb5_parse_name(context, canon, &canon_princ));
526         match = krb5_principal_compare(context, canon_princ, (*entry)->princ);
527         krb5_free_principal(context, canon_princ);
528     }
529 
530     free(canon);
531     return match ? 0 : KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
532 }
533 
534 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)535 test_fetch_master_key(krb5_context context, krb5_principal mname,
536                       krb5_keyblock *key_out, krb5_kvno *kvno_out,
537                       char *db_args)
538 {
539     memset(key_out, 0, sizeof(*key_out));
540     *kvno_out = 0;
541     return 0;
542 }
543 
544 static krb5_error_code
test_fetch_master_key_list(krb5_context context,krb5_principal mname,const krb5_keyblock * key,krb5_keylist_node ** mkeys_out)545 test_fetch_master_key_list(krb5_context context, krb5_principal mname,
546                            const krb5_keyblock *key,
547                            krb5_keylist_node **mkeys_out)
548 {
549     /* krb5_dbe_get_mkvno() returns an error if we produce NULL, so return an
550      * empty node to make kadm5_get_principal() work. */
551     *mkeys_out = ealloc(sizeof(**mkeys_out));
552     return 0;
553 }
554 
555 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)556 test_decrypt_key_data(krb5_context context, const krb5_keyblock *mkey,
557                       const krb5_key_data *kd, krb5_keyblock *key_out,
558                       krb5_keysalt *salt_out)
559 {
560     key_out->magic = KV5M_KEYBLOCK;
561     key_out->enctype = kd->key_data_type[0];
562     key_out->length = kd->key_data_length[0];
563     key_out->contents = ealloc(key_out->length);
564     memcpy(key_out->contents, kd->key_data_contents[0], key_out->length);
565     if (salt_out != NULL) {
566         salt_out->type = (kd->key_data_ver > 1) ? kd->key_data_type[1] :
567             KRB5_KDB_SALTTYPE_NORMAL;
568         salt_out->data = empty_data();
569     }
570     return 0;
571 }
572 
573 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)574 test_encrypt_key_data(krb5_context context, const krb5_keyblock *mkey,
575                       const krb5_keyblock *key, const krb5_keysalt *salt,
576                       int kvno, krb5_key_data *kd_out)
577 {
578     memset(kd_out, 0, sizeof(*kd_out));
579     kd_out->key_data_ver = 2;
580     kd_out->key_data_kvno = kvno;
581     kd_out->key_data_type[0] = key->enctype;
582     kd_out->key_data_length[0] = key->length;
583     kd_out->key_data_contents[0] = ealloc(key->length);
584     memcpy(kd_out->key_data_contents[0], key->contents, key->length);
585     kd_out->key_data_type[1] = (salt != NULL) ? salt->type :
586         KRB5_KDB_SALTTYPE_NORMAL;
587     return 0;
588 }
589 
590 typedef struct {
591     char *pac_princ;
592     struct {
593         char *proxy_target;
594         char *impersonator;
595     } deleg_info;
596     krb5_boolean not_delegated;
597     krb5_pac pac;
598 } pac_info;
599 
600 static void
free_pac_info(krb5_context context,pac_info * info)601 free_pac_info(krb5_context context, pac_info *info)
602 {
603     if (info == NULL)
604         return;
605 
606     free(info->pac_princ);
607     free(info->deleg_info.proxy_target);
608     free(info->deleg_info.impersonator);
609     krb5_pac_free(context, info->pac);
610     free(info);
611 }
612 
613 /*
614  * Create a PAC object with a fake logon-info blob.  Instead of a real
615  * KERB_VALIDATION_INFO structure, store a byte indicating whether the
616  * USER_NOT_DELEGATED bit is set.
617  */
618 static krb5_error_code
create_pac(krb5_context context,krb5_boolean not_delegated,krb5_pac * pac_out)619 create_pac(krb5_context context, krb5_boolean not_delegated, krb5_pac *pac_out)
620 {
621     krb5_data data;
622     krb5_pac pac;
623     char nd;
624 
625     nd = not_delegated ? 1 : 0;
626     data = make_data(&nd, 1);
627     check(krb5_pac_init(context, &pac));
628     check(krb5_pac_add_buffer(context, pac, KRB5_PAC_LOGON_INFO, &data));
629 
630     *pac_out = pac;
631     return 0;
632 }
633 
634 /* Create a fake PAC, setting the USER_NOT_DELEGATED bit if the client DB entry
635  * disallows forwardable tickets. */
636 static krb5_error_code
create_pac_db(krb5_context context,krb5_db_entry * client,krb5_pac * pac_out)637 create_pac_db(krb5_context context, krb5_db_entry *client, krb5_pac *pac_out)
638 {
639     krb5_boolean not_delegated;
640     /* Use disallow_forwardable as delegation_not_allowed attribute */
641     not_delegated = (client->attributes & KRB5_KDB_DISALLOW_FORWARDABLE);
642     return create_pac(context, not_delegated, pac_out);
643 }
644 
645 /* Locate the PAC in tgt_authdata and set *pac_out to its PAC object
646  * representation.  Set it to NULL if no PAC is present. */
647 static void
parse_ticket_pac(krb5_context context,krb5_authdata ** tgt_auth_data,krb5_pac * pac_out)648 parse_ticket_pac(krb5_context context, krb5_authdata **tgt_auth_data,
649                  krb5_pac *pac_out)
650 {
651     krb5_authdata **authdata;
652 
653     *pac_out = NULL;
654 
655     check(krb5_find_authdata(context, tgt_auth_data, NULL,
656                              KRB5_AUTHDATA_WIN2K_PAC, &authdata));
657     if (authdata == NULL)
658         return;
659     assert(authdata[1] == NULL);
660     check(krb5_pac_parse(context, authdata[0]->contents, authdata[0]->length,
661                          pac_out));
662     krb5_free_authdata(context, authdata);
663 }
664 
665 /* Verify the KDC signature against the local TGT key.  tgt_key must be the
666  * decrypted first key data entry of tgt. */
667 static krb5_error_code
verify_kdc_signature(krb5_context context,krb5_pac pac,krb5_keyblock * tgt_key,krb5_db_entry * tgt)668 verify_kdc_signature(krb5_context context, krb5_pac pac,
669                      krb5_keyblock *tgt_key, krb5_db_entry *tgt)
670 {
671     krb5_error_code ret;
672     krb5_key_data *kd;
673     krb5_keyblock old_key;
674     krb5_kvno kvno;
675     int tries;
676 
677     ret = krb5_pac_verify(context, pac, 0, NULL, NULL, tgt_key);
678     if (ret != KRB5KRB_AP_ERR_BAD_INTEGRITY)
679         return ret;
680 
681     kvno = tgt->key_data[0].key_data_kvno - 1;
682 
683     /* There is no kvno in PAC signatures, so try two previous versions. */
684     for (tries = 2; tries > 0 && kvno > 0; tries--, kvno--) {
685         ret = krb5_dbe_find_enctype(context, tgt, -1, -1, kvno, &kd);
686         if (ret)
687             return KRB5KRB_AP_ERR_BAD_INTEGRITY;
688         ret = krb5_dbe_decrypt_key_data(context, NULL, kd, &old_key, NULL);
689         if (ret)
690             return ret;
691         ret = krb5_pac_verify(context, pac, 0, NULL, NULL, &old_key);
692         krb5_free_keyblock_contents(context, &old_key);
693         if (!ret)
694             return 0;
695 
696         /* Try the next lower kvno on the next iteration. */
697         kvno = kd->key_data_kvno - 1;
698     }
699 
700     return KRB5KRB_AP_ERR_BAD_INTEGRITY;
701 }
702 
703 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)704 verify_ticket_pac(krb5_context context, krb5_pac pac, unsigned int flags,
705                   krb5_const_principal client_princ, krb5_boolean check_realm,
706                   krb5_keyblock *server_key, krb5_keyblock *local_tgt_key,
707                   krb5_db_entry *local_tgt, krb5_timestamp authtime)
708 {
709     check(krb5_pac_verify_ext(context, pac, authtime, client_princ, server_key,
710                               NULL, check_realm));
711     if (flags & KRB5_KDB_FLAG_CROSS_REALM)
712         return 0;
713     return verify_kdc_signature(context, pac, local_tgt_key, local_tgt);
714 }
715 
716 static void
get_pac_info(krb5_context context,krb5_authdata ** in_authdata,pac_info ** info_out)717 get_pac_info(krb5_context context, krb5_authdata **in_authdata,
718              pac_info **info_out)
719 {
720     krb5_error_code ret;
721     krb5_pac pac = NULL;
722     krb5_data data;
723     char *sep = NULL;
724     pac_info *info;
725 
726     *info_out = NULL;
727 
728     parse_ticket_pac(context, in_authdata, &pac);
729     if (pac == NULL)
730         return;
731 
732     info = ealloc(sizeof(*info));
733 
734     /* Read the fake logon-info buffer from the PAC and set not_delegated
735      * according to the byte value. */
736     check(krb5_pac_get_client_info(context, pac, NULL, &info->pac_princ));
737     check(krb5_pac_get_buffer(context, pac, KRB5_PAC_LOGON_INFO, &data));
738     assert(data.length == 1);
739     info->not_delegated = *data.data;
740     krb5_free_data_contents(context, &data);
741 
742     ret = krb5_pac_get_buffer(context, pac, KRB5_PAC_DELEGATION_INFO, &data);
743     if (ret && ret != ENOENT)
744         abort();
745     if (!ret) {
746         sep = memchr(data.data, ':', data.length);
747         assert(sep != NULL);
748         info->deleg_info.proxy_target = k5memdup0(data.data, sep - data.data,
749                                                   &ret);
750         check(ret);
751         info->deleg_info.impersonator = k5memdup0(sep + 1, data.length - 1 -
752                                                   (sep - data.data), &ret);
753         check(ret);
754         krb5_free_data_contents(context, &data);
755     }
756 
757     info->pac = pac;
758     *info_out = info;
759 }
760 
761 /* Add a fake delegation-info buffer to pac containing the proxy target and
762  * impersonator from info. */
763 static void
add_delegation_info(krb5_context context,krb5_pac pac,pac_info * info)764 add_delegation_info(krb5_context context, krb5_pac pac, pac_info *info)
765 {
766     krb5_data data;
767     char *str;
768 
769     if (info->deleg_info.proxy_target == NULL)
770         return;
771 
772     if (asprintf(&str, "%s:%s", info->deleg_info.proxy_target,
773                  info->deleg_info.impersonator) < 0)
774         abort();
775     data = string2data(str);
776     check(krb5_pac_add_buffer(context, pac, KRB5_PAC_DELEGATION_INFO, &data));
777     free(str);
778 }
779 
780 /* Set *out to an AD-IF-RELEVANT authdata element containing a PAC authdata
781  * element with contents pac_data. */
782 static void
encode_pac_ad(krb5_context context,krb5_data * pac_data,krb5_authdata ** out)783 encode_pac_ad(krb5_context context, krb5_data *pac_data, krb5_authdata **out)
784 {
785     krb5_authdata pac_ad, *list[2], **ifrel;
786 
787     pac_ad.magic = KV5M_AUTHDATA;
788     pac_ad.ad_type = KRB5_AUTHDATA_WIN2K_PAC;
789     pac_ad.contents = (krb5_octet *)pac_data->data;;
790     pac_ad.length = pac_data->length;
791     list[0] = &pac_ad;
792     list[1] = NULL;
793 
794     check(krb5_encode_authdata_container(context, KRB5_AUTHDATA_IF_RELEVANT,
795                                          list, &ifrel));
796     assert(ifrel[1] == NULL);
797     *out = ifrel[0];
798     free(ifrel);
799 }
800 
801 /* Parse a PAC client-info string into a principal name.  If xrealm_s4u is
802  * true, expect a realm in the string. */
803 static krb5_error_code
parse_pac_princ(krb5_context context,krb5_boolean xrealm_s4u,char * pac_princ,krb5_principal * client_out)804 parse_pac_princ(krb5_context context, krb5_boolean xrealm_s4u, char *pac_princ,
805                 krb5_principal *client_out)
806 {
807     int n_atsigns = 0, flags = 0;
808     char *p = pac_princ;
809 
810     while (*p++) {
811         if (*p == '@')
812             n_atsigns++;
813     }
814     if (xrealm_s4u) {
815         flags |= KRB5_PRINCIPAL_PARSE_REQUIRE_REALM;
816         n_atsigns--;
817     } else {
818         flags |= KRB5_PRINCIPAL_PARSE_NO_REALM;
819     }
820     assert(n_atsigns == 0 || n_atsigns == 1);
821     if (n_atsigns == 1)
822         flags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE;
823     check(krb5_parse_name_flags(context, pac_princ, flags, client_out));
824     (*client_out)->type = KRB5_NT_MS_PRINCIPAL;
825     return 0;
826 }
827 
828 /* Set *ad_out to a fake PAC for testing, or to NULL if it doesn't make sense
829  * to generate a PAC for the request. */
830 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)831 generate_pac(krb5_context context, unsigned int flags,
832              krb5_const_principal client_princ,
833              krb5_const_principal server_princ, krb5_db_entry *client,
834              krb5_db_entry *header_server, krb5_db_entry *local_tgt,
835              krb5_keyblock *server_key, krb5_keyblock *header_key,
836              krb5_keyblock *local_tgt_key, krb5_timestamp authtime,
837              pac_info *info, krb5_authdata **ad_out)
838 {
839     krb5_boolean sign_realm, check_realm;
840     krb5_data pac_data;
841     krb5_pac pac = NULL;
842     krb5_principal pac_princ = NULL;
843 
844     *ad_out = NULL;
845 
846     check_realm = ((flags & KRB5_KDB_FLAGS_S4U) &&
847                    (flags & KRB5_KDB_FLAG_CROSS_REALM));
848     sign_realm = ((flags & KRB5_KDB_FLAGS_S4U) &&
849                   (flags & KRB5_KDB_FLAG_ISSUING_REFERRAL));
850 
851     if (client != NULL &&
852         ((flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) ||
853          (flags & KRB5_KDB_FLAG_PROTOCOL_TRANSITION))) {
854         /* For AS or local-realm S4U2Self, generate an initial PAC. */
855         check(create_pac_db(context, client, &pac));
856     } else if (info == NULL) {
857         /* If there is no input PAC, do not generate one. */
858         assert((flags & KRB5_KDB_FLAGS_S4U) == 0);
859         return;
860     } else {
861         if (IS_TGS_PRINC(server_princ) &&
862             info->deleg_info.proxy_target != NULL) {
863             /* RBCD transitive trust. */
864             assert(flags & KRB5_KDB_FLAG_CROSS_REALM);
865             assert(!(flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION));
866             check(parse_pac_princ(context, TRUE, info->pac_princ, &pac_princ));
867             client_princ = pac_princ;
868             check_realm = TRUE;
869             sign_realm = TRUE;
870         } else if ((flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) &&
871                    !(flags & KRB5_KDB_FLAG_CROSS_REALM)) {
872             /*
873              * Initial RBCD and old constrained delegation requests to
874              * impersonator realm; create delegation info blob.  We cannot
875              * assume that proxy_target is NULL as the evidence ticket could
876              * have been acquired via constrained delegation.
877              */
878             free(info->deleg_info.proxy_target);
879             check(krb5_unparse_name_flags(context, server_princ,
880                                           KRB5_PRINCIPAL_UNPARSE_NO_REALM,
881                                           &info->deleg_info.proxy_target));
882             /* This is supposed to be a list of impersonators, but we currently
883              * only deal with one. */
884             free(info->deleg_info.impersonator);
885             check(krb5_unparse_name(context, header_server->princ,
886                                     &info->deleg_info.impersonator));
887         } else if (flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) {
888             /* Last cross realm RBCD request to proxy realm. */
889             assert(info->deleg_info.proxy_target != NULL);
890         }
891 
892         /* We have already verified the PAC in get_authdata_info, but we should
893          * be able to verify the signatures here as well. */
894         check(verify_ticket_pac(context, info->pac, flags, client_princ,
895                                 check_realm, header_key, local_tgt_key,
896                                 local_tgt, authtime));
897 
898         /* Create a new pac as we may be altering pac principal's realm */
899         check(create_pac(context, info->not_delegated, &pac));
900         add_delegation_info(context, pac, info);
901     }
902     check(krb5_pac_sign_ext(context, pac, authtime, client_princ, server_key,
903                             local_tgt_key, sign_realm, &pac_data));
904     krb5_pac_free(context, pac);
905     krb5_free_principal(context, pac_princ);
906     encode_pac_ad(context, &pac_data, ad_out);
907     krb5_free_data_contents(context, &pac_data);
908 }
909 
910 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)911 test_sign_authdata(krb5_context context, unsigned int flags,
912                    krb5_const_principal client_princ,
913                    krb5_const_principal server_princ, krb5_db_entry *client,
914                    krb5_db_entry *server, krb5_db_entry *header_server,
915                    krb5_db_entry *local_tgt, krb5_keyblock *client_key,
916                    krb5_keyblock *server_key, krb5_keyblock *header_key,
917                    krb5_keyblock *local_tgt_key, krb5_keyblock *session_key,
918                    krb5_timestamp authtime, krb5_authdata **tgt_auth_data,
919                    void *ad_info, krb5_data ***auth_indicators,
920                    krb5_authdata ***signed_auth_data)
921 {
922     krb5_authdata *pac_ad = NULL, *test_ad = NULL, **list;
923     krb5_data **inds, d;
924     int i, val;
925 
926     /* Possibly create a PAC authdata element. */
927     generate_pac(context, flags, client_princ, server_princ, client,
928                  header_server, local_tgt, server_key, header_key,
929                  local_tgt_key, authtime, ad_info, &pac_ad);
930 
931     /* Always create a TEST_AD_TYPE element. */
932     test_ad = ealloc(sizeof(*test_ad));
933     test_ad->magic = KV5M_AUTHDATA;
934     test_ad->ad_type = TEST_AD_TYPE;
935     test_ad->contents = (uint8_t *)estrdup("db-authdata-test");
936     test_ad->length = strlen((char *)test_ad->contents);
937 
938     /* Assemble the authdata into a one-element or two-element list.
939      * The PAC must be the first element. */
940     list = ealloc(3 * sizeof(*list));
941     list[0] = (pac_ad != NULL) ? pac_ad : test_ad;
942     list[1] = (pac_ad != NULL) ? test_ad : NULL;
943     list[2] = NULL;
944     *signed_auth_data = list;
945 
946     /* If we see an auth indicator "dbincrX", replace the whole indicator list
947      * with "dbincr{X+1}". */
948     inds = *auth_indicators;
949     for (i = 0; inds != NULL && inds[i] != NULL; i++) {
950         if (inds[i]->length == 7 && memcmp(inds[i]->data, "dbincr", 6) == 0) {
951             val = inds[i]->data[6];
952             k5_free_data_ptr_list(inds);
953             inds = ealloc(2 * sizeof(*inds));
954             d = string2data("dbincr0");
955             check(krb5_copy_data(context, &d, &inds[0]));
956             inds[0]->data[6] = val + 1;
957             inds[1] = NULL;
958             *auth_indicators = inds;
959             break;
960         }
961     }
962 
963     return 0;
964 }
965 
966 static krb5_boolean
match_in_table(krb5_context context,const char * table,const char * sprinc,const char * tprinc)967 match_in_table(krb5_context context, const char *table, const char *sprinc,
968                const char *tprinc)
969 {
970     testhandle h = context->dal_handle->db_context;
971     krb5_error_code ret;
972     char **values, **v;
973     krb5_boolean found = FALSE;
974 
975     set_names(h, table, sprinc, NULL);
976     ret = profile_get_values(h->profile, h->names, &values);
977     assert(ret == 0 || ret == PROF_NO_RELATION);
978     if (ret)
979         return FALSE;
980     for (v = values; *v != NULL; v++) {
981         if (strcmp(*v, tprinc) == 0) {
982             found = TRUE;
983             break;
984         }
985     }
986     profile_free_list(values);
987     return found;
988 }
989 
990 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)991 test_check_allowed_to_delegate(krb5_context context,
992                                krb5_const_principal client,
993                                const krb5_db_entry *server,
994                                krb5_const_principal proxy)
995 {
996     char *sprinc, *tprinc;
997     krb5_boolean found = FALSE;
998 
999     check(krb5_unparse_name_flags(context, server->princ,
1000                                   KRB5_PRINCIPAL_UNPARSE_NO_REALM, &sprinc));
1001     check(krb5_unparse_name_flags(context, proxy,
1002                                   KRB5_PRINCIPAL_UNPARSE_NO_REALM, &tprinc));
1003     found = match_in_table(context, "delegation", sprinc, tprinc);
1004     krb5_free_unparsed_name(context, sprinc);
1005     krb5_free_unparsed_name(context, tprinc);
1006     return found ? 0 : KRB5KDC_ERR_POLICY;
1007 }
1008 
1009 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)1010 test_allowed_to_delegate_from(krb5_context context,
1011                               krb5_const_principal client,
1012                               krb5_const_principal server,
1013                               void *server_ad_info, const krb5_db_entry *proxy)
1014 {
1015     char *sprinc, *tprinc;
1016     pac_info *info = (pac_info *)server_ad_info;
1017     krb5_boolean found = FALSE;
1018 
1019     check(krb5_unparse_name(context, proxy->princ, &sprinc));
1020     check(krb5_unparse_name(context, server, &tprinc));
1021     assert(strncmp(info->pac_princ, tprinc, strlen(info->pac_princ)) == 0);
1022     found = match_in_table(context, "rbcd", sprinc, tprinc);
1023     krb5_free_unparsed_name(context, sprinc);
1024     krb5_free_unparsed_name(context, tprinc);
1025     return found ? 0 : KRB5KDC_ERR_POLICY;
1026 }
1027 
1028 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)1029 test_get_authdata_info(krb5_context context, unsigned int flags,
1030                        krb5_authdata **in_authdata,
1031                        krb5_const_principal client_princ,
1032                        krb5_const_principal server_princ,
1033                        krb5_keyblock *server_key, krb5_keyblock *krbtgt_key,
1034                        krb5_db_entry *krbtgt, krb5_timestamp authtime,
1035                        void **ad_info_out, krb5_principal *client_out)
1036 {
1037     pac_info *info = NULL;
1038     krb5_boolean rbcd_transitive, xrealm_s4u;
1039     krb5_principal pac_princ = NULL;
1040     char *proxy_name = NULL, *impersonator_name = NULL;
1041 
1042     get_pac_info(context, in_authdata, &info);
1043     if (info == NULL)
1044         return 0;
1045 
1046     /* Transitive RBCD requests are not flagged as constrained delegation */
1047     if (info->not_delegated &&
1048         (info->deleg_info.proxy_target ||
1049          (flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION))) {
1050         free_pac_info(context, info);
1051         return KRB5KDC_ERR_BADOPTION;
1052     }
1053 
1054     rbcd_transitive = IS_TGS_PRINC(server_princ) &&
1055         (flags & KRB5_KDB_FLAG_CROSS_REALM) && info->deleg_info.proxy_target &&
1056         !(flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION);
1057 
1058     xrealm_s4u = rbcd_transitive || ((flags & KRB5_KDB_FLAG_CROSS_REALM) &&
1059                                      (flags & KRB5_KDB_FLAGS_S4U));
1060 
1061     check(parse_pac_princ(context, xrealm_s4u, info->pac_princ, &pac_princ));
1062 
1063     /* Cross-realm and transitive trust RBCD requests */
1064     if (rbcd_transitive || ((flags & KRB5_KDB_FLAG_CROSS_REALM) &&
1065                             (flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION))) {
1066         assert(info->deleg_info.proxy_target != NULL);
1067         assert(info->deleg_info.impersonator != NULL);
1068         /* We must be able to find the impersonator in the delegation info. */
1069         assert(!krb5_principal_compare(context, client_princ, pac_princ));
1070         check(krb5_unparse_name(context, client_princ, &impersonator_name));
1071         assert(strcmp(info->deleg_info.impersonator, impersonator_name) == 0);
1072         krb5_free_unparsed_name(context, impersonator_name);
1073         client_princ = pac_princ;
1074         /* In the non-transitive case we can match the proxy too. */
1075         if (!rbcd_transitive) {
1076             check(krb5_unparse_name_flags(context, server_princ,
1077                                           KRB5_PRINCIPAL_UNPARSE_NO_REALM,
1078                                           &proxy_name));
1079             assert(info->deleg_info.proxy_target != NULL);
1080             assert(strcmp(info->deleg_info.proxy_target, proxy_name) == 0);
1081             krb5_free_unparsed_name(context, proxy_name);
1082         }
1083     }
1084 
1085     check(verify_ticket_pac(context, info->pac, flags, client_princ,
1086                             xrealm_s4u, server_key, krbtgt_key, krbtgt,
1087                             authtime));
1088 
1089     *ad_info_out = info;
1090     if (client_out != NULL)
1091         *client_out = pac_princ;
1092     else
1093         krb5_free_principal(context, pac_princ);
1094 
1095     return 0;
1096 }
1097 
1098 static void
test_free_authdata_info(krb5_context context,void * ad_info)1099 test_free_authdata_info(krb5_context context, void *ad_info)
1100 {
1101     pac_info *info = (pac_info *)ad_info;
1102 
1103     free_pac_info(context, info);
1104 }
1105 
1106 kdb_vftabl PLUGIN_SYMBOL_NAME(krb5_test, kdb_function_table) = {
1107     KRB5_KDB_DAL_MAJOR_VERSION,             /* major version number */
1108     0,                                      /* minor version number */
1109     test_init,
1110     test_cleanup,
1111     test_open,
1112     test_close,
1113     NULL, /* create */
1114     NULL, /* destroy */
1115     NULL, /* get_age */
1116     NULL, /* lock */
1117     NULL, /* unlock */
1118     test_get_principal,
1119     NULL, /* put_principal */
1120     NULL, /* delete_principal */
1121     NULL, /* rename_principal */
1122     NULL, /* iterate */
1123     NULL, /* create_policy */
1124     NULL, /* get_policy */
1125     NULL, /* put_policy */
1126     NULL, /* iter_policy */
1127     NULL, /* delete_policy */
1128     test_fetch_master_key,
1129     test_fetch_master_key_list,
1130     NULL, /* store_master_key_list */
1131     NULL, /* dbe_search_enctype */
1132     NULL, /* change_pwd */
1133     NULL, /* promote_db */
1134     test_decrypt_key_data,
1135     test_encrypt_key_data,
1136     test_sign_authdata,
1137     NULL, /* check_transited_realms */
1138     NULL, /* check_policy_as */
1139     NULL, /* check_policy_tgs */
1140     NULL, /* audit_as_req */
1141     NULL, /* refresh_config */
1142     test_check_allowed_to_delegate,
1143     NULL, /* free_principal_e_data */
1144     test_get_s4u_x509_principal,
1145     test_allowed_to_delegate_from,
1146     test_get_authdata_info,
1147     test_free_authdata_info
1148 };
1149