Fix spelling
[heimdal.git] / lib / hdb / test_namespace.c
blobf9b4cdbdde89a927b4b4d94887eaf42335879e47
1 /*
2 * Copyright (c) 2020 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
35 * This program implements an ephemeral, memory-based HDB backend, stores into
36 * it just one HDB entry -one for a namespace- then checks that virtual
37 * principals are returned below that namespace by hdb_fetch_kvno(), and that
38 * the logic for automatic key rotation of virtual principals is correct.
41 #include "hdb_locl.h"
42 #include <hex.h>
44 static KeyRotation krs[2];
45 static const char *base_pw[2] = { "Testing123...", "Tested123..." };
47 typedef struct {
48 HDB hdb; /* generic members */
50 * Make this dict a global, add a mutex lock around it, and a .finit and/or
51 * atexit() handler to free it, and we'd have a first-class MEMORY HDB.
53 * What would a first-class MEMORY HDB be good for though, besides testing?
55 * However, we could move this dict into `HDB' and then have _hdb_store()
56 * and friends support it as a cache for frequently-used & seldom-changing
57 * entries, such as: K/M, namespaces, and krbtgt principals. That would
58 * speed up lookups, especially for backends with poor reader-writer
59 * concurrency (DB, LMDB) and LDAP. Such entries could be cached for a
60 * minute or three at a time.
62 heim_dict_t dict;
63 } TEST_HDB;
65 struct hdb_called {
66 int create;
67 int init;
68 int fini;
71 static krb5_error_code
72 TDB_close(krb5_context context, HDB *db)
74 return 0;
77 static krb5_error_code
78 TDB_destroy(krb5_context context, HDB *db)
80 TEST_HDB *tdb = (void *)db;
82 heim_release(tdb->dict);
83 free(tdb->hdb.hdb_name);
84 free(tdb);
85 return 0;
88 static krb5_error_code
89 TDB_set_sync(krb5_context context, HDB *db, int on)
91 return 0;
94 static krb5_error_code
95 TDB_lock(krb5_context context, HDB *db, int operation)
98 return 0;
101 static krb5_error_code
102 TDB_unlock(krb5_context context, HDB *db)
105 return 0;
108 static krb5_error_code
109 TDB_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry *entry)
111 /* XXX Implement */
112 /* Tricky thing: heim_dict_iterate_f() is inconvenient here */
113 /* We need this to check that virtual principals aren't created */
114 return 0;
117 static krb5_error_code
118 TDB_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry *entry)
120 /* XXX Implement */
121 /* Tricky thing: heim_dict_iterate_f() is inconvenient here */
122 /* We need this to check that virtual principals aren't created */
123 return 0;
126 static krb5_error_code
127 TDB_rename(krb5_context context, HDB *db, const char *new_name)
129 return EEXIST;
132 static krb5_error_code
133 TDB__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply)
135 krb5_error_code ret = 0;
136 TEST_HDB *tdb = (void *)db;
137 heim_object_t k, v = NULL;
139 if ((k = heim_data_create(key.data, key.length)) == NULL)
140 ret = krb5_enomem(context);
141 if (ret == 0 && (v = heim_dict_get_value(tdb->dict, k)) == NULL)
142 ret = HDB_ERR_NOENTRY;
143 if (ret == 0)
144 ret = krb5_data_copy(reply, heim_data_get_ptr(v), heim_data_get_length(v));
145 heim_release(k);
146 return ret;
149 static krb5_error_code
150 TDB__put(krb5_context context, HDB *db, int rplc, krb5_data kd, krb5_data vd)
152 krb5_error_code ret = 0;
153 TEST_HDB *tdb = (void *)db;
154 heim_object_t k = NULL;
155 heim_object_t v = NULL;
157 if ((k = heim_data_create(kd.data, kd.length)) == NULL ||
158 (v = heim_data_create(vd.data, vd.length)) == NULL)
159 ret = krb5_enomem(context);
160 if (ret == 0 && !rplc && heim_dict_get_value(tdb->dict, k) != NULL)
161 ret = HDB_ERR_EXISTS;
162 if (ret == 0 && heim_dict_set_value(tdb->dict, k, v))
163 ret = krb5_enomem(context);
164 heim_release(k);
165 heim_release(v);
166 return ret;
169 static krb5_error_code
170 TDB__del(krb5_context context, HDB *db, krb5_data key)
172 krb5_error_code ret = 0;
173 TEST_HDB *tdb = (void *)db;
174 heim_object_t k;
176 if ((k = heim_data_create(key.data, key.length)) == NULL)
177 ret = krb5_enomem(context);
178 if (ret == 0 && heim_dict_get_value(tdb->dict, k) == NULL)
179 ret = HDB_ERR_NOENTRY;
180 if (ret == 0)
181 heim_dict_delete_key(tdb->dict, k);
182 heim_release(k);
183 return ret;
186 static krb5_error_code
187 TDB_open(krb5_context context, HDB *db, int flags, mode_t mode)
189 return 0;
192 static krb5_error_code
193 hdb_test_create(krb5_context context, struct HDB **db, const char *arg)
195 TEST_HDB *tdb;
197 if ((tdb = calloc(1, sizeof(tdb[0]))) == NULL ||
198 (tdb->hdb.hdb_name = strdup(arg)) == NULL ||
199 (tdb->dict = heim_dict_create(10)) == NULL) {
200 if (tdb)
201 free(tdb->hdb.hdb_name);
202 free(tdb);
203 return krb5_enomem(context);
206 tdb->hdb.hdb_db = NULL;
207 tdb->hdb.hdb_master_key_set = 0;
208 tdb->hdb.hdb_openp = 0;
209 tdb->hdb.hdb_capability_flags = HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL;
210 tdb->hdb.hdb_open = TDB_open;
211 tdb->hdb.hdb_close = TDB_close;
212 tdb->hdb.hdb_fetch_kvno = _hdb_fetch_kvno;
213 tdb->hdb.hdb_store = _hdb_store;
214 tdb->hdb.hdb_remove = _hdb_remove;
215 tdb->hdb.hdb_firstkey = TDB_firstkey;
216 tdb->hdb.hdb_nextkey= TDB_nextkey;
217 tdb->hdb.hdb_lock = TDB_lock;
218 tdb->hdb.hdb_unlock = TDB_unlock;
219 tdb->hdb.hdb_rename = TDB_rename;
220 tdb->hdb.hdb__get = TDB__get;
221 tdb->hdb.hdb__put = TDB__put;
222 tdb->hdb.hdb__del = TDB__del;
223 tdb->hdb.hdb_destroy = TDB_destroy;
224 tdb->hdb.hdb_set_sync = TDB_set_sync;
225 *db = &tdb->hdb;
227 return 0;
230 static krb5_error_code
231 hdb_test_init(krb5_context context, void **ctx)
233 *ctx = NULL;
234 return 0;
237 static void hdb_test_fini(void *ctx)
241 struct hdb_method hdb_test =
243 #ifdef WIN32
244 /* Not c99 */
245 HDB_INTERFACE_VERSION,
246 hdb_test_init,
247 hdb_test_fini,
248 1 /*is_file_based*/, 1 /*can_taste*/,
249 "test",
250 hdb_test_create
251 #else
252 .minor_version = HDB_INTERFACE_VERSION,
253 .init = hdb_test_init,
254 .fini = hdb_test_fini,
255 .is_file_based = 1,
256 .can_taste = 1,
257 .prefix = "test",
258 .create = hdb_test_create
259 #endif
262 static krb5_error_code
263 make_base_key(krb5_context context,
264 krb5_const_principal p,
265 const char *pw,
266 krb5_keyblock *k)
268 return krb5_string_to_key(context, KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
269 pw, p, k);
272 static krb5_error_code
273 tderive_key(krb5_context context,
274 const char *p,
275 KeyRotation *kr,
276 int toffset,
277 krb5_keyblock *base,
278 krb5int32 etype,
279 krb5_keyblock *k,
280 uint32_t *kvno,
281 time_t *set_time)
283 krb5_error_code ret = 0;
284 krb5_crypto crypto = NULL;
285 EncryptionKey intermediate;
286 krb5_data pad, out;
287 size_t len;
288 int n;
290 n = toffset / kr->period;
291 *set_time = kr->epoch + kr->period * n;
292 *kvno = kr->base_kvno + n;
294 out.data = 0;
295 out.length = 0;
297 /* Derive intermediate key */
298 pad.data = (void *)(uintptr_t)p;
299 pad.length = strlen(p);
300 ret = krb5_enctype_keysize(context, base->keytype, &len);
301 if (ret == 0)
302 ret = krb5_crypto_init(context, base, 0, &crypto);
303 if (ret == 0)
304 ret = krb5_crypto_prfplus(context, crypto, &pad, len, &out);
305 if (crypto)
306 krb5_crypto_destroy(context, crypto);
307 crypto = NULL;
308 if (ret == 0)
309 ret = krb5_random_to_key(context, etype, out.data, out.length,
310 &intermediate);
311 krb5_data_free(&out);
313 /* Derive final key */
314 pad.data = kvno;
315 pad.length = sizeof(*kvno);
316 if (ret == 0)
317 ret = krb5_enctype_keysize(context, etype, &len);
318 if (ret == 0)
319 ret = krb5_crypto_init(context, &intermediate, 0, &crypto);
320 if (ret == 0) {
321 *kvno = htonl(*kvno);
322 ret = krb5_crypto_prfplus(context, crypto, &pad, len, &out);
323 *kvno = ntohl(*kvno);
325 if (crypto)
326 krb5_crypto_destroy(context, crypto);
327 if (ret == 0)
328 ret = krb5_random_to_key(context, etype, out.data, out.length, k);
329 krb5_data_free(&out);
331 free_EncryptionKey(&intermediate);
332 return ret;
335 /* Create a namespace principal */
336 static void
337 make_namespace(krb5_context context, HDB *db, const char *name)
339 krb5_error_code ret = 0;
340 hdb_entry e;
341 Key k;
343 memset(&k, 0, sizeof(k));
344 k.mkvno = 0;
345 k.salt = 0;
347 /* Setup the HDB entry */
348 memset(&e, 0, sizeof(e));
349 e.created_by.time = krs[0].epoch;
350 e.valid_start = e.valid_end = e.pw_end = 0;
351 e.generation = 0;
352 e.flags = int2HDBFlags(0);
353 e.flags.server = e.flags.client = 1;
354 e.flags.virtual = 1;
356 /* Setup etypes */
357 if (ret == 0 &&
358 (e.etypes = malloc(sizeof(*e.etypes))) == NULL)
359 ret = krb5_enomem(context);
360 if (ret == 0)
361 e.etypes->len = 3;
362 if (ret == 0 &&
363 (e.etypes->val = calloc(e.etypes->len,
364 sizeof(e.etypes->val[0]))) == NULL)
365 ret = krb5_enomem(context);
366 if (ret == 0) {
367 e.etypes->val[0] = KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128;
368 e.etypes->val[1] = KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192;
369 e.etypes->val[2] = KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96;
372 /* Setup max_life and max_renew */
373 if (ret == 0 &&
374 (e.max_life = malloc(sizeof(*e.max_life))) == NULL)
375 ret = krb5_enomem(context);
376 if (ret == 0 &&
377 (e.max_renew = malloc(sizeof(*e.max_renew))) == NULL)
378 ret = krb5_enomem(context);
379 if (ret == 0)
380 /* Make it long, so we see the clamped max */
381 *e.max_renew = 2 * ((*e.max_life = 15 * 24 * 3600));
383 /* Setup principal name and created_by */
384 if (ret == 0)
385 ret = krb5_parse_name(context, name, &e.principal);
386 if (ret == 0)
387 ret = krb5_parse_name(context, "admin@BAR.EXAMPLE",
388 &e.created_by.principal);
390 /* Make base keys for first epoch */
391 if (ret == 0)
392 ret = make_base_key(context, e.principal, base_pw[0], &k.key);
393 if (ret == 0)
394 add_Keys(&e.keys, &k);
395 if (ret == 0)
396 ret = hdb_entry_set_pw_change_time(context, &e, krs[0].epoch);
397 free_Key(&k);
398 e.kvno = krs[0].base_key_kvno;
400 /* Move them to history */
401 if (ret == 0)
402 ret = hdb_add_current_keys_to_history(context, &e);
403 free_Keys(&e.keys);
405 /* Make base keys for second epoch */
406 if (ret == 0)
407 ret = make_base_key(context, e.principal, base_pw[1], &k.key);
408 if (ret == 0)
409 add_Keys(&e.keys, &k);
410 e.kvno = krs[1].base_key_kvno;
411 if (ret == 0)
412 ret = hdb_entry_set_pw_change_time(context, &e, krs[1].epoch);
414 /* Add the key rotation metadata */
415 if (ret == 0)
416 ret = hdb_entry_add_key_rotation(context, &e, 0, &krs[0]);
417 if (ret == 0)
418 ret = hdb_entry_add_key_rotation(context, &e, 0, &krs[1]);
420 if (ret == 0)
421 ret = db->hdb_store(context, db, 0, &e);
422 if (ret)
423 krb5_err(context, 1, ret, "failed to setup a namespace principal");
424 free_Key(&k);
425 hdb_free_entry(context, db, &e);
428 #define WK_PREFIX "WELLKNOWN/" HDB_WK_NAMESPACE "/"
430 static const char *expected[] = {
431 WK_PREFIX "_/bar.example@BAR.EXAMPLE",
432 "HTTP/bar.example@BAR.EXAMPLE",
433 "HTTP/foo.bar.example@BAR.EXAMPLE",
434 "host/foo.bar.example@BAR.EXAMPLE",
435 "HTTP/blah.foo.bar.example@BAR.EXAMPLE",
437 static const char *unexpected[] = {
438 WK_PREFIX "_/no.example@BAZ.EXAMPLE",
439 "HTTP/no.example@BAR.EXAMPLE",
440 "HTTP/foo.no.example@BAR.EXAMPLE",
441 "HTTP/blah.foo.no.example@BAR.EXAMPLE",
445 * We'll fetch as many entries as we have principal names in `expected[]', for
446 * as many KeyRotation periods as we have (between 1 and 3), and for up to 5
447 * different time offsets in each period.
449 #define NUM_OFFSETS 5
450 static hdb_entry e[
451 (sizeof(expected) / sizeof(expected[0])) *
452 (sizeof(krs) / sizeof(krs[0])) *
453 NUM_OFFSETS
456 static int
457 hist_key_compar(const void *va, const void *vb)
459 const hdb_keyset *a = va;
460 const hdb_keyset *b = vb;
462 return a->kvno - b->kvno;
466 * Fetch keys for some decent time in the given kr.
468 * `kr' is an index into the global `krs[]'.
469 * `t' is a number 0..4 inclusive that identifies a time period relative to the
470 * epoch of `krs[kr]' (see code below).
472 static void
473 fetch_entries(krb5_context context,
474 HDB *db,
475 size_t kr,
476 size_t t,
477 int must_fail)
479 krb5_error_code ret = 0;
480 krb5_principal p = NULL;
481 krb5_keyblock base_key, dk;
482 hdb_entry *ep;
483 hdb_entry no;
484 size_t i, b;
485 int toffset = 0;
487 memset(&base_key, 0, sizeof(base_key));
489 /* Work out offset of first entry in `e[]' */
490 assert(kr < sizeof(krs) / sizeof(krs[0]));
491 assert(t < NUM_OFFSETS);
492 b = (kr * NUM_OFFSETS + t) * (sizeof(expected) / sizeof(expected[0]));
493 assert(b < sizeof(e) / sizeof(e[0]));
494 assert(sizeof(e) / sizeof(e[0]) - b >=
495 (sizeof(expected) / sizeof(expected[0])));
497 switch (t) {
498 case 0: toffset = 1; break; /* epoch + 1s */
499 case 1: toffset = 1 + (krs[kr].period >> 1); break; /* epoch + period/2 */
500 case 2: toffset = 1 + (krs[kr].period >> 2); break; /* epoch + period/4 */
501 case 3: toffset = 1 + (krs[kr].period >> 3); break; /* epoch + period/8 */
502 case 4: toffset = 1 - (krs[kr].period >> 3); break; /* epoch - period/8 */
505 for (i = 0; ret == 0 && i < sizeof(expected) / sizeof(expected[0]); i++) {
506 ep = &e[b + i];
507 memset(ep, 0, sizeof(*ep));
508 if (ret == 0)
509 ret = krb5_parse_name(context, expected[i], &p);
510 if (ret == 0 && i == 0) {
511 if (toffset < 0 && kr)
512 ret = make_base_key(context, p, base_pw[kr - 1], &base_key);
513 else
514 ret = make_base_key(context, p, base_pw[kr], &base_key);
516 if (ret == 0)
517 ret = hdb_fetch_kvno(context, db, p,
518 HDB_F_DECRYPT | HDB_F_ALL_KVNOS,
519 krs[kr].epoch + toffset, 0, 0, ep);
520 if (i && must_fail && ret == 0)
521 krb5_errx(context, 1,
522 "virtual principal that shouldn't exist does");
523 if (kr == 0 && toffset < 0 && ret == HDB_ERR_NOENTRY)
524 continue;
525 if (kr == 0 && toffset < 0) {
527 * Virtual principals don't exist before their earliest key
528 * rotation epoch's start time.
530 if (i == 0) {
531 if (ret)
532 krb5_errx(context, 1,
533 "namespace principal does not exist before its time");
534 } else if (i != 0) {
535 if (ret == 0)
536 krb5_errx(context, 1,
537 "virtual principal exists before its time");
538 if (ret != HDB_ERR_NOENTRY)
539 krb5_errx(context, 1, "wrong error code");
540 ret = 0;
542 } else {
543 if (ret == 0 &&
544 !krb5_principal_compare(context, p, ep->principal))
545 krb5_errx(context, 1, "wrong principal in fetched entry");
549 HDB_Ext_KeySet *hist_keys;
550 HDB_extension *ext;
551 ext = hdb_find_extension(ep,
552 choice_HDB_extension_data_hist_keys);
553 if (ext) {
554 /* Sort key history by kvno, why not */
555 hist_keys = &ext->data.u.hist_keys;
556 qsort(hist_keys->val, hist_keys->len,
557 sizeof(hist_keys->val[0]), hist_key_compar);
561 krb5_free_principal(context, p);
563 if (ret && must_fail) {
564 free_EncryptionKey(&base_key);
565 return;
567 if (ret)
568 krb5_err(context, 1, ret, "virtual principal test failed");
570 for (i = 0; i < sizeof(unexpected) / sizeof(unexpected[0]); i++) {
571 memset(&no, 0, sizeof(no));
572 if (ret == 0)
573 ret = krb5_parse_name(context, unexpected[i], &p);
574 if (ret == 0)
575 ret = hdb_fetch_kvno(context, db, p, HDB_F_DECRYPT,
576 krs[kr].epoch + toffset, 0, 0, &no);
577 if (ret == 0)
578 krb5_errx(context, 1, "bogus principal exists, wat");
579 krb5_free_principal(context, p);
580 ret = 0;
583 if (kr == 0 && toffset < 0)
584 return;
587 * XXX
589 * Add check that derived keys are a) different, b) as expected, using a
590 * set of test vectors or else by computing the expected keys here with
591 * code that's not shared with lib/hdb/common.c.
593 * Add check that we get expected past and/or future keys, not just current
594 * keys.
596 for (i = 1; ret == 0 && i < sizeof(expected) / sizeof(expected[0]); i++) {
597 uint32_t kvno;
598 time_t set_time, chg_time;
600 ep = &e[b + i];
601 if (toffset > 0) {
602 ret = tderive_key(context, expected[i], &krs[kr], toffset,
603 &base_key, base_key.keytype, &dk, &kvno, &set_time);
604 } else /* XXX */{
605 /* XXX */
606 assert(kr);
607 ret = tderive_key(context, expected[i], &krs[kr - 1],
608 krs[kr].epoch - krs[kr - 1].epoch + toffset,
609 &base_key, base_key.keytype, &dk, &kvno, &set_time);
611 if (ret)
612 krb5_err(context, 1, ret, "deriving keys for comparison");
614 if (kvno != ep->kvno)
615 krb5_errx(context, 1, "kvno mismatch (%u != %u)", kvno, ep->kvno);
616 (void) hdb_entry_get_pw_change_time(ep, &chg_time);
617 if (set_time != chg_time)
618 krb5_errx(context, 1, "key change time mismatch");
619 if (ep->keys.len == 0)
620 krb5_errx(context, 1, "no keys!");
621 if (ep->keys.val[0].key.keytype != dk.keytype)
622 krb5_errx(context, 1, "enctype mismatch!");
623 if (ep->keys.val[0].key.keyvalue.length !=
624 dk.keyvalue.length)
625 krb5_errx(context, 1, "key length mismatch!");
626 if (memcmp(ep->keys.val[0].key.keyvalue.data,
627 dk.keyvalue.data, dk.keyvalue.length) != 0)
628 krb5_errx(context, 1, "key mismatch!");
629 if (memcmp(ep->keys.val[0].key.keyvalue.data,
630 e[b + i - 1].keys.val[0].key.keyvalue.data,
631 dk.keyvalue.length) == 0)
632 krb5_errx(context, 1, "different virtual principals have the same keys!");
633 /* XXX Add check that we have the expected number of history keys */
634 free_EncryptionKey(&dk);
636 free_EncryptionKey(&base_key);
639 static void
640 check_kvnos(krb5_context context)
642 HDB_Ext_KeySet keysets;
643 size_t i, k, m, p; /* iterator indices */
645 keysets.len = 0;
646 keysets.val = 0;
648 /* For every principal name */
649 for (i = 0; i < sizeof(expected)/sizeof(expected[0]); i++) {
650 free_HDB_Ext_KeySet(&keysets);
652 /* For every entry we've fetched for it */
653 for (k = 0; k < sizeof(e)/sizeof(e[0]); k++) {
654 HDB_Ext_KeySet *hist_keys;
655 HDB_extension *ext;
656 hdb_entry *ep;
657 int match = 0;
659 if ((k % NUM_OFFSETS) != i)
660 continue;
662 ep = &e[k];
663 if (ep->principal == NULL)
664 continue; /* Didn't fetch this one */
667 * Check that the current keys for it match what we've seen already
668 * or else add them to `keysets'.
670 for (m = 0; m < keysets.len; m++) {
671 if (ep->kvno == keysets.val[m].kvno) {
672 /* Check the key is the same */
673 if (ep->keys.val[0].key.keytype !=
674 keysets.val[m].keys.val[0].key.keytype ||
675 ep->keys.val[0].key.keyvalue.length !=
676 keysets.val[m].keys.val[0].key.keyvalue.length ||
677 memcmp(ep->keys.val[0].key.keyvalue.data,
678 keysets.val[m].keys.val[0].key.keyvalue.data,
679 ep->keys.val[0].key.keyvalue.length) != 0)
680 krb5_errx(context, 1,
681 "key mismatch for same princ & kvno");
682 match = 1;
685 if (m == keysets.len) {
686 hdb_keyset ks;
688 ks.kvno = ep->kvno;
689 ks.keys = ep->keys;
690 ks.set_time = 0;
691 if (add_HDB_Ext_KeySet(&keysets, &ks))
692 krb5_err(context, 1, ENOMEM, "out of memory");
693 match = 1;
695 if (match)
696 continue;
698 /* For all non-current keysets, repeat the above */
699 ext = hdb_find_extension(ep,
700 choice_HDB_extension_data_hist_keys);
701 if (!ext)
702 continue;
703 hist_keys = &ext->data.u.hist_keys;
704 for (p = 0; p < hist_keys->len; p++) {
705 for (m = 0; m < keysets.len; m++) {
706 if (keysets.val[m].kvno == hist_keys->val[p].kvno)
707 if (ep->keys.val[0].key.keytype !=
708 keysets.val[m].keys.val[0].key.keytype ||
709 ep->keys.val[0].key.keyvalue.length !=
710 keysets.val[m].keys.val[0].key.keyvalue.length ||
711 memcmp(ep->keys.val[0].key.keyvalue.data,
712 keysets.val[m].keys.val[0].key.keyvalue.data,
713 ep->keys.val[0].key.keyvalue.length) != 0)
714 krb5_errx(context, 1,
715 "key mismatch for same princ & kvno");
717 if (m == keysets.len) {
718 hdb_keyset ks;
719 ks.kvno = ep->kvno;
720 ks.keys = ep->keys;
721 ks.set_time = 0;
722 if (add_HDB_Ext_KeySet(&keysets, &ks))
723 krb5_err(context, 1, ENOMEM, "out of memory");
728 free_HDB_Ext_KeySet(&keysets);
731 static void
732 print_em(krb5_context context)
734 HDB_Ext_KeySet *hist_keys;
735 HDB_extension *ext;
736 size_t i, p;
738 for (i = 0; i < sizeof(e)/sizeof(e[0]); i++) {
739 const char *name = expected[i % (sizeof(expected)/sizeof(expected[0]))];
740 char *x;
742 if (0 == i % (sizeof(expected)/sizeof(expected[0])))
743 continue;
744 if (e[i].principal == NULL)
745 continue;
746 hex_encode(e[i].keys.val[0].key.keyvalue.data,
747 e[i].keys.val[0].key.keyvalue.length, &x);
748 printf("%s %u %s\n", x, e[i].kvno, name);
749 free(x);
751 ext = hdb_find_extension(&e[i], choice_HDB_extension_data_hist_keys);
752 if (!ext)
753 continue;
754 hist_keys = &ext->data.u.hist_keys;
755 for (p = 0; p < hist_keys->len; p++) {
756 hex_encode(hist_keys->val[p].keys.val[0].key.keyvalue.data,
757 hist_keys->val[p].keys.val[0].key.keyvalue.length, &x);
758 printf("%s %u %s\n", x, hist_keys->val[p].kvno, name);
759 free(x);
764 #if 0
765 static void
766 check_expected_kvnos(krb5_context context)
768 HDB_Ext_KeySet *hist_keys;
769 HDB_extension *ext;
770 size_t i, k, m, p;
772 for (i = 0; i < sizeof(expected)/sizeof(expected[0]); i++) {
773 for (k = 0; k < sizeof(krs)/sizeof(krs[0]); k++) {
774 hdb_entry *ep = &e[k * sizeof(expected)/sizeof(expected[0]) + i];
776 if (ep->principal == NULL)
777 continue;
778 for (m = 0; m < NUM_OFFSETS; m++) {
779 ext = hdb_find_extension(ep,
780 choice_HDB_extension_data_hist_keys);
781 if (!ext)
782 continue;
783 hist_keys = &ext->data.u.hist_keys;
784 for (p = 0; p < hist_keys->len; p++) {
785 fprintf(stderr, "%s at %lu, %lu: history kvno %u\n",
786 expected[i], k, m, hist_keys->val[p].kvno);
789 fprintf(stderr, "%s at %lu: kvno %u\n", expected[i], k,
790 ep->kvno);
794 #endif
796 #define SOME_TIME 1596318329
797 #define SOME_BASE_KVNO 150
798 #define SOME_EPOCH (SOME_TIME - (7 * 24 * 3600) - (SOME_TIME % (7 * 24 * 3600)))
799 #define SOME_PERIOD 3600
801 #define CONF \
802 "[hdb]\n" \
803 "\tenable_virtual_hostbased_princs = true\n" \
804 "\tvirtual_hostbased_princ_mindots = 1\n" \
805 "\tvirtual_hostbased_princ_maxdots = 3\n" \
808 main(int argc, char **argv)
810 krb5_error_code ret;
811 krb5_context context;
812 size_t i;
813 HDB *db = NULL;
815 setprogname(argv[0]);
816 memset(e, 0, sizeof(e));
817 ret = krb5_init_context(&context);
818 if (ret == 0)
819 ret = krb5_set_config(context, CONF);
820 if (ret == 0)
821 ret = krb5_plugin_register(context, PLUGIN_TYPE_DATA, "hdb_test_interface",
822 &hdb_test);
823 if (ret == 0)
824 ret = hdb_create(context, &db, "test:mem");
825 if (ret)
826 krb5_err(context, 1, ret, "failed to setup HDB driver and test");
828 assert(db->enable_virtual_hostbased_princs);
829 assert(db->virtual_hostbased_princ_ndots == 1);
830 assert(db->virtual_hostbased_princ_maxdots == 3);
832 /* Setup key rotation metadata in a convenient way */
834 * FIXME Reorder these two KRs to match how we store them to avoid
835 * confusion. #0 should be future-most, #1 should past-post.
837 krs[0].flags = krs[1].flags = int2KeyRotationFlags(0);
838 krs[0].epoch = SOME_EPOCH - 20 * 24 * 3600;
839 krs[0].period = SOME_PERIOD >> 1;
840 krs[0].base_kvno = 150;
841 krs[0].base_key_kvno = 1;
842 krs[1].epoch = SOME_TIME;
843 krs[1].period = SOME_PERIOD;
844 krs[1].base_kvno = krs[0].base_kvno + 1 + (krs[1].epoch + (krs[0].period - 1) - krs[0].epoch) / krs[0].period;
845 krs[1].base_key_kvno = 2;
848 HDB_Ext_KeyRotation existing_krs, new_krs;
849 KeyRotation ordered_krs[2];
851 ordered_krs[0] = krs[1];
852 ordered_krs[1] = krs[0];
853 existing_krs.len = 0;
854 existing_krs.val = 0;
855 new_krs.len = 1;
856 new_krs.val = &ordered_krs[1];
857 if ((ret = hdb_validate_key_rotations(context, NULL, &new_krs)) ||
858 (ret = hdb_validate_key_rotations(context, &existing_krs,
859 &new_krs)))
860 krb5_err(context, 1, ret, "Valid KeyRotation thought invalid");
861 new_krs.len = 1;
862 new_krs.val = &ordered_krs[0];
863 if ((ret = hdb_validate_key_rotations(context, NULL, &new_krs)) ||
864 (ret = hdb_validate_key_rotations(context, &existing_krs,
865 &new_krs)))
866 krb5_err(context, 1, ret, "Valid KeyRotation thought invalid");
867 new_krs.len = 2;
868 new_krs.val = &ordered_krs[0];
869 if ((ret = hdb_validate_key_rotations(context, NULL, &new_krs)) ||
870 (ret = hdb_validate_key_rotations(context, &existing_krs,
871 &new_krs)))
872 krb5_err(context, 1, ret, "Valid KeyRotation thought invalid");
873 existing_krs.len = 1;
874 existing_krs.val = &ordered_krs[1];
875 if ((ret = hdb_validate_key_rotations(context, &existing_krs,
876 &new_krs)))
877 krb5_err(context, 1, ret, "Valid KeyRotation thought invalid");
878 existing_krs.len = 2;
879 existing_krs.val = &ordered_krs[0];
880 if ((ret = hdb_validate_key_rotations(context, &existing_krs,
881 &new_krs)))
882 krb5_err(context, 1, ret, "Valid KeyRotation thought invalid");
884 new_krs.len = 2;
885 new_krs.val = &krs[0];
886 if ((ret = hdb_validate_key_rotations(context, &existing_krs,
887 &new_krs)) == 0)
888 krb5_errx(context, 1, "Invalid KeyRotation thought valid");
891 make_namespace(context, db, WK_PREFIX "_/bar.example@BAR.EXAMPLE");
893 fetch_entries(context, db, 1, 0, 0);
894 fetch_entries(context, db, 1, 1, 0);
895 fetch_entries(context, db, 1, 2, 0);
896 fetch_entries(context, db, 1, 3, 0);
897 fetch_entries(context, db, 1, 4, 0); /* Just before newest KR */
899 fetch_entries(context, db, 0, 0, 0);
900 fetch_entries(context, db, 0, 1, 0);
901 fetch_entries(context, db, 0, 2, 0);
902 fetch_entries(context, db, 0, 3, 0);
903 fetch_entries(context, db, 0, 4, 1); /* Must fail: just before 1st KR */
906 * Check that for every virtual principal in `expected[]', all the keysets
907 * with the same kvno, in all the entries fetched for different times,
908 * match.
910 check_kvnos(context);
912 #if 0
914 * Check that for every virtual principal in `expected[]' we have the
915 * expected key history.
917 check_expected_kvnos(context);
918 #endif
921 * XXX Add various tests here, checking `e[]':
923 * - Extract all {principal, kvno, key} for all keys, current and
924 * otherwise, then sort by {key, kvno, principal}, then check that the
925 * only time we have matching keys is when the kvno and principal also
926 * match.
929 print_em(context);
932 * XXX Test adding a third KR, a 4th KR, dropping KRs...
935 /* Cleanup */
936 for (i = 0; ret == 0 && i < sizeof(e) / sizeof(e[0]); i++)
937 hdb_free_entry(context, db, &e[i]);
938 db->hdb_destroy(context, db);
939 krb5_free_context(context);
940 return 0;