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 * 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 * 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 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 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 * 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 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 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 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_SHA1, 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 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 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 315 test_init(void) 316 { 317 return 0; 318 } 319 320 static krb5_error_code 321 test_cleanup(void) 322 { 323 return 0; 324 } 325 326 static krb5_error_code 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 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 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 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 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) && 411 (flags & KRB5_KDB_FLAG_REFERRAL_OK)) { 412 /* Return a client referral by creating an entry with only the 413 * principal set. */ 414 *entry = ealloc(sizeof(**entry)); 415 (*entry)->princ = princ; 416 princ = NULL; 417 ret = 0; 418 goto cleanup; 419 } else if (flags & KRB5_KDB_FLAG_REFERRAL_OK) { 420 /* Generate a server referral by looking up the TGT for the 421 * canonical name's realm. */ 422 tgtprinc = tgtname(context, &princ->realm, &search_for->realm); 423 krb5_free_principal(context, princ); 424 princ = tgtprinc; 425 426 krb5_free_unparsed_name(context, search_name); 427 check(krb5_unparse_name_flags(context, princ, 428 KRB5_PRINCIPAL_UNPARSE_NO_REALM, 429 &search_name)); 430 ename = search_name; 431 } else { 432 ret = KRB5_KDB_NOENTRY; 433 goto cleanup; 434 } 435 } else { 436 ename = canon; 437 } 438 } else { 439 check(krb5_copy_principal(context, search_for, &princ)); 440 ename = search_name; 441 } 442 443 /* Check that the entry exists. */ 444 set_names(h, "princs", ename, NULL); 445 ret = profile_get_relation_names(h->profile, h->names, &names); 446 if (ret == PROF_NO_RELATION) { 447 ret = KRB5_KDB_NOENTRY; 448 goto cleanup; 449 } 450 profile_free_list(names); 451 452 /* No error exits after this point. */ 453 454 ent = ealloc(sizeof(*ent)); 455 ent->princ = princ; 456 princ = NULL; 457 458 flagstr = get_string(h, "princs", ename, "flags"); 459 if (flagstr != NULL) { 460 check(krb5_flagspec_to_mask(flagstr, &ent->attributes, 461 &ent->attributes)); 462 } 463 free(flagstr); 464 465 ent->max_life = get_duration(h, "princs", ename, "maxlife"); 466 ent->max_renewable_life = get_duration(h, "princs", ename, "maxrenewlife"); 467 ent->expiration = get_time(h, "princs", ename, "expiration"); 468 ent->pw_expiration = get_time(h, "princs", ename, "pwexpiration"); 469 470 /* Leave last_success, last_failed, fail_auth_count zeroed. */ 471 /* Leave e_data empty. */ 472 473 set_names(h, "princs", ename, "keys"); 474 ret = profile_get_values(h->profile, h->names, &key_strings); 475 if (ret != PROF_NO_RELATION) { 476 make_keys(key_strings, ename, &search_for->realm, ent); 477 profile_free_list(key_strings); 478 } 479 480 set_names(h, "princs", ename, "strings"); 481 ret = profile_get_values(h->profile, h->names, &stringattrs); 482 if (ret != PROF_NO_RELATION) { 483 make_strings(stringattrs, ent); 484 profile_free_list(stringattrs); 485 } 486 487 /* We must include mod-princ data or kadm5_get_principal() won't work and 488 * we can't extract keys with kadmin.local. */ 489 check(krb5_dbe_update_mod_princ_data(context, ent, 0, &empty_princ)); 490 491 *entry = ent; 492 ret = 0; 493 494 cleanup: 495 krb5_free_unparsed_name(context, search_name); 496 krb5_free_principal(context, princ); 497 free(canon); 498 return ret; 499 } 500 501 static void 502 lookup_princ_by_cert(krb5_context context, const krb5_data *client_cert, 503 krb5_principal *princ) 504 { 505 krb5_error_code ret; 506 char *cert_princ_name; 507 508 /* The test client sends a principal string instead of a cert. */ 509 cert_princ_name = k5memdup0(client_cert->data, client_cert->length, &ret); 510 check(ret); 511 512 check(krb5_parse_name_flags(context, cert_princ_name, 513 KRB5_PRINCIPAL_PARSE_ENTERPRISE, princ)); 514 free(cert_princ_name); 515 } 516 517 static krb5_error_code 518 test_get_s4u_x509_principal(krb5_context context, const krb5_data *client_cert, 519 krb5_const_principal princ, unsigned int flags, 520 krb5_db_entry **entry) 521 { 522 krb5_error_code ret; 523 krb5_principal cert_princ, canon_princ; 524 testhandle h = context->dal_handle->db_context; 525 krb5_boolean match; 526 char *canon, *princ_name; 527 528 lookup_princ_by_cert(context, client_cert, &cert_princ); 529 530 ret = test_get_principal(context, cert_princ, flags, entry); 531 krb5_free_principal(context, cert_princ); 532 if (ret || (flags & KRB5_KDB_FLAG_REFERRAL_OK)) 533 return ret; 534 535 if (!krb5_realm_compare(context, princ, (*entry)->princ)) 536 abort(); 537 538 if (princ->length == 0 || 539 krb5_principal_compare(context, princ, (*entry)->princ)) 540 return 0; 541 542 match = FALSE; 543 check(krb5_unparse_name_flags(context, princ, 544 KRB5_PRINCIPAL_UNPARSE_NO_REALM, 545 &princ_name)); 546 canon = get_string(h, "alias", princ_name, NULL); 547 krb5_free_unparsed_name(context, princ_name); 548 if (canon != NULL) { 549 check(krb5_parse_name(context, canon, &canon_princ)); 550 match = krb5_principal_compare(context, canon_princ, (*entry)->princ); 551 krb5_free_principal(context, canon_princ); 552 } 553 554 free(canon); 555 return match ? 0 : KRB5KDC_ERR_CLIENT_NAME_MISMATCH; 556 } 557 558 static krb5_error_code 559 test_fetch_master_key(krb5_context context, krb5_principal mname, 560 krb5_keyblock *key_out, krb5_kvno *kvno_out, 561 char *db_args) 562 { 563 memset(key_out, 0, sizeof(*key_out)); 564 *kvno_out = 0; 565 return 0; 566 } 567 568 static krb5_error_code 569 test_fetch_master_key_list(krb5_context context, krb5_principal mname, 570 const krb5_keyblock *key, 571 krb5_keylist_node **mkeys_out) 572 { 573 /* krb5_dbe_get_mkvno() returns an error if we produce NULL, so return an 574 * empty node to make kadm5_get_principal() work. */ 575 *mkeys_out = ealloc(sizeof(**mkeys_out)); 576 return 0; 577 } 578 579 static krb5_error_code 580 test_decrypt_key_data(krb5_context context, const krb5_keyblock *mkey, 581 const krb5_key_data *kd, krb5_keyblock *key_out, 582 krb5_keysalt *salt_out) 583 { 584 key_out->magic = KV5M_KEYBLOCK; 585 key_out->enctype = kd->key_data_type[0]; 586 key_out->length = kd->key_data_length[0]; 587 key_out->contents = ealloc(key_out->length); 588 memcpy(key_out->contents, kd->key_data_contents[0], key_out->length); 589 if (salt_out != NULL) { 590 salt_out->type = (kd->key_data_ver > 1) ? kd->key_data_type[1] : 591 KRB5_KDB_SALTTYPE_NORMAL; 592 salt_out->data = empty_data(); 593 } 594 return 0; 595 } 596 597 static krb5_error_code 598 test_encrypt_key_data(krb5_context context, const krb5_keyblock *mkey, 599 const krb5_keyblock *key, const krb5_keysalt *salt, 600 int kvno, krb5_key_data *kd_out) 601 { 602 memset(kd_out, 0, sizeof(*kd_out)); 603 kd_out->key_data_ver = 2; 604 kd_out->key_data_kvno = kvno; 605 kd_out->key_data_type[0] = key->enctype; 606 kd_out->key_data_length[0] = key->length; 607 kd_out->key_data_contents[0] = ealloc(key->length); 608 memcpy(kd_out->key_data_contents[0], key->contents, key->length); 609 kd_out->key_data_type[1] = (salt != NULL) ? salt->type : 610 KRB5_KDB_SALTTYPE_NORMAL; 611 return 0; 612 } 613 614 static void 615 change_auth_indicators(krb5_context context, krb5_data ***auth_indicators) 616 { 617 krb5_data **inds, d; 618 size_t i; 619 int val; 620 621 /* If we see an auth indicator "dbincrX", replace the whole indicator list 622 * with "dbincr{X+1}". */ 623 inds = *auth_indicators; 624 for (i = 0; inds != NULL && inds[i] != NULL; i++) { 625 if (inds[i]->length == 7 && memcmp(inds[i]->data, "dbincr", 6) == 0) { 626 val = inds[i]->data[6]; 627 k5_free_data_ptr_list(inds); 628 inds = ealloc(2 * sizeof(*inds)); 629 d = string2data("dbincr0"); 630 check(krb5_copy_data(context, &d, &inds[0])); 631 inds[0]->data[6] = val + 1; 632 inds[1] = NULL; 633 *auth_indicators = inds; 634 break; 635 } 636 } 637 } 638 639 static krb5_error_code 640 test_issue_pac(krb5_context context, unsigned int flags, krb5_db_entry *client, 641 krb5_keyblock *replaced_reply_key, krb5_db_entry *server, 642 krb5_db_entry *krb5tgt, krb5_timestamp authtime, 643 krb5_pac old_pac, krb5_pac new_pac, 644 krb5_data ***auth_indicators) 645 { 646 krb5_data data = empty_data(); 647 krb5_boolean found_logon_info = FALSE; 648 krb5_ui_4 *types = NULL; 649 size_t num_buffers = 0, i; 650 651 change_auth_indicators(context, auth_indicators); 652 653 if (old_pac == NULL || 654 (client != NULL && (flags & KRB5_KDB_FLAG_PROTOCOL_TRANSITION))) { 655 /* Generating an initial PAC. */ 656 assert(client != NULL); 657 data = string2data("fake"); 658 check(krb5_pac_add_buffer(context, new_pac, KRB5_PAC_LOGON_INFO, 659 &data)); 660 661 if (replaced_reply_key != NULL) { 662 /* Add a fake PAC_CREDENTIALS_INFO buffer so we can test whether 663 * this parameter was set. */ 664 data = string2data("fake credinfo"); 665 check(krb5_pac_add_buffer(context, new_pac, 666 KRB5_PAC_CREDENTIALS_INFO, &data)); 667 } 668 return 0; 669 } else { 670 /* Field copying - my favorite! */ 671 if (old_pac != NULL) 672 check(krb5_pac_get_types(context, old_pac, &num_buffers, &types)); 673 674 for (i = 0; i < num_buffers; i++) { 675 /* Skip buffer types handled by KDC. */ 676 if (types[i] == KRB5_PAC_SERVER_CHECKSUM || 677 types[i] == KRB5_PAC_PRIVSVR_CHECKSUM || 678 types[i] == KRB5_PAC_TICKET_CHECKSUM || 679 types[i] == KRB5_PAC_CLIENT_INFO || 680 types[i] == KRB5_PAC_DELEGATION_INFO) 681 continue; 682 683 check(krb5_pac_get_buffer(context, old_pac, types[i], &data)); 684 685 if (types[i] == KRB5_PAC_LOGON_INFO) { 686 found_logon_info = TRUE; 687 assert(data_eq_string(data, "fake")); 688 } 689 690 check(krb5_pac_add_buffer(context, new_pac, types[i], &data)); 691 krb5_free_data_contents(context, &data); 692 } 693 694 if (old_pac != NULL) 695 assert(found_logon_info); 696 697 free(types); 698 } 699 700 return 0; 701 } 702 703 static krb5_boolean 704 match_in_table(krb5_context context, const char *table, const char *sprinc, 705 const char *tprinc) 706 { 707 testhandle h = context->dal_handle->db_context; 708 krb5_error_code ret; 709 char **values, **v; 710 krb5_boolean found = FALSE; 711 712 set_names(h, table, sprinc, NULL); 713 ret = profile_get_values(h->profile, h->names, &values); 714 assert(ret == 0 || ret == PROF_NO_RELATION); 715 if (ret) 716 return FALSE; 717 for (v = values; *v != NULL; v++) { 718 if (tprinc == NULL || strcmp(*v, tprinc) == 0) { 719 found = TRUE; 720 break; 721 } 722 } 723 profile_free_list(values); 724 return found; 725 } 726 727 static krb5_error_code 728 test_check_allowed_to_delegate(krb5_context context, 729 krb5_const_principal client, 730 const krb5_db_entry *server, 731 krb5_const_principal proxy) 732 { 733 char *sprinc, *tprinc = NULL; 734 krb5_boolean found = FALSE; 735 736 check(krb5_unparse_name_flags(context, server->princ, 737 KRB5_PRINCIPAL_UNPARSE_NO_REALM, &sprinc)); 738 if (proxy != NULL) { 739 check(krb5_unparse_name_flags(context, proxy, 740 KRB5_PRINCIPAL_UNPARSE_NO_REALM, 741 &tprinc)); 742 } 743 found = match_in_table(context, "delegation", sprinc, tprinc); 744 krb5_free_unparsed_name(context, sprinc); 745 krb5_free_unparsed_name(context, tprinc); 746 return found ? 0 : KRB5KDC_ERR_BADOPTION; 747 } 748 749 static krb5_error_code 750 test_allowed_to_delegate_from(krb5_context context, 751 krb5_const_principal client, 752 krb5_const_principal server, 753 krb5_pac server_pac, 754 const krb5_db_entry *proxy) 755 { 756 char *proxy_princ, *server_princ, *pac_client_princ, *client_princ; 757 krb5_boolean found = FALSE; 758 759 assert(server_pac != NULL); 760 761 check(krb5_unparse_name(context, proxy->princ, &proxy_princ)); 762 check(krb5_unparse_name(context, server, &server_princ)); 763 check(krb5_unparse_name(context, client, &client_princ)); 764 765 check(krb5_pac_get_client_info(context, server_pac, NULL, 766 &pac_client_princ)); 767 768 /* Skip realm portion if not present in PAC. */ 769 assert(strncmp(pac_client_princ, server_princ, 770 strlen(pac_client_princ)) == 0); 771 772 free(pac_client_princ); 773 774 found = match_in_table(context, "rbcd", proxy_princ, server_princ); 775 krb5_free_unparsed_name(context, proxy_princ); 776 krb5_free_unparsed_name(context, server_princ); 777 krb5_free_unparsed_name(context, client_princ); 778 return found ? 0 : KRB5KDC_ERR_BADOPTION; 779 } 780 781 kdb_vftabl PLUGIN_SYMBOL_NAME(krb5_test, kdb_function_table) = { 782 KRB5_KDB_DAL_MAJOR_VERSION, /* major version number */ 783 0, /* minor version number */ 784 test_init, 785 test_cleanup, 786 test_open, 787 test_close, 788 NULL, /* create */ 789 NULL, /* destroy */ 790 NULL, /* get_age */ 791 NULL, /* lock */ 792 NULL, /* unlock */ 793 test_get_principal, 794 NULL, /* put_principal */ 795 NULL, /* delete_principal */ 796 NULL, /* rename_principal */ 797 NULL, /* iterate */ 798 NULL, /* create_policy */ 799 NULL, /* get_policy */ 800 NULL, /* put_policy */ 801 NULL, /* iter_policy */ 802 NULL, /* delete_policy */ 803 test_fetch_master_key, 804 test_fetch_master_key_list, 805 NULL, /* store_master_key_list */ 806 NULL, /* dbe_search_enctype */ 807 NULL, /* change_pwd */ 808 NULL, /* promote_db */ 809 test_decrypt_key_data, 810 test_encrypt_key_data, 811 NULL, /* check_transited_realms */ 812 NULL, /* check_policy_as */ 813 NULL, /* check_policy_tgs */ 814 NULL, /* audit_as_req */ 815 NULL, /* refresh_config */ 816 test_check_allowed_to_delegate, 817 NULL, /* free_principal_e_data */ 818 test_get_s4u_x509_principal, 819 test_allowed_to_delegate_from, 820 test_issue_pac, 821 }; 822