Changeset 740 for vendor/current/source4/kdc/hdb-samba4.c
- Timestamp:
- Nov 14, 2012, 12:59:34 PM (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
vendor/current/source4/kdc/hdb-samba4.c
r414 r740 34 34 35 35 #include "includes.h" 36 #include "system/time.h" 37 #include "../libds/common/flags.h" 38 #include "lib/ldb/include/ldb.h" 39 #include "lib/ldb/include/ldb_errors.h" 40 #include "librpc/gen_ndr/netlogon.h" 41 #include "libcli/security/security.h" 42 #include "auth/auth.h" 43 #include "auth/credentials/credentials.h" 44 #include "auth/auth_sam.h" 45 #include "../lib/util/util_ldb.h" 46 #include "dsdb/samdb/samdb.h" 47 #include "librpc/ndr/libndr.h" 48 #include "librpc/gen_ndr/ndr_drsblobs.h" 49 #include "librpc/gen_ndr/lsa.h" 50 #include "libcli/auth/libcli_auth.h" 51 #include "param/param.h" 52 #include "events/events.h" 53 #include "kdc/kdc.h" 54 #include "../lib/crypto/md4.h" 55 56 enum hdb_samba4_ent_type 57 { HDB_SAMBA4_ENT_TYPE_CLIENT, HDB_SAMBA4_ENT_TYPE_SERVER, 58 HDB_SAMBA4_ENT_TYPE_KRBTGT, HDB_SAMBA4_ENT_TYPE_TRUST, HDB_SAMBA4_ENT_TYPE_ANY }; 59 60 enum trust_direction { 61 UNKNOWN = 0, 62 INBOUND = LSA_TRUST_DIRECTION_INBOUND, 63 OUTBOUND = LSA_TRUST_DIRECTION_OUTBOUND 64 }; 65 66 static const char *trust_attrs[] = { 67 "trustPartner", 68 "trustAuthIncoming", 69 "trustAuthOutgoing", 70 "whenCreated", 71 "msDS-SupportedEncryptionTypes", 72 "trustAttributes", 73 "trustDirection", 74 "trustType", 75 NULL 76 }; 77 78 static KerberosTime ldb_msg_find_krb5time_ldap_time(struct ldb_message *msg, const char *attr, KerberosTime default_val) 79 { 80 const char *tmp; 81 const char *gentime; 82 struct tm tm; 83 84 gentime = ldb_msg_find_attr_as_string(msg, attr, NULL); 85 if (!gentime) 86 return default_val; 87 88 tmp = strptime(gentime, "%Y%m%d%H%M%SZ", &tm); 89 if (tmp == NULL) { 90 return default_val; 91 } 92 93 return timegm(&tm); 94 } 95 96 static HDBFlags uf2HDBFlags(krb5_context context, int userAccountControl, enum hdb_samba4_ent_type ent_type) 97 { 98 HDBFlags flags = int2HDBFlags(0); 99 100 /* we don't allow kadmin deletes */ 101 flags.immutable = 1; 102 103 /* mark the principal as invalid to start with */ 104 flags.invalid = 1; 105 106 flags.renewable = 1; 107 108 /* All accounts are servers, but this may be disabled again in the caller */ 109 flags.server = 1; 110 111 /* Account types - clear the invalid bit if it turns out to be valid */ 112 if (userAccountControl & UF_NORMAL_ACCOUNT) { 113 if (ent_type == HDB_SAMBA4_ENT_TYPE_CLIENT || ent_type == HDB_SAMBA4_ENT_TYPE_ANY) { 114 flags.client = 1; 115 } 116 flags.invalid = 0; 117 } 118 119 if (userAccountControl & UF_INTERDOMAIN_TRUST_ACCOUNT) { 120 if (ent_type == HDB_SAMBA4_ENT_TYPE_CLIENT || ent_type == HDB_SAMBA4_ENT_TYPE_ANY) { 121 flags.client = 1; 122 } 123 flags.invalid = 0; 124 } 125 if (userAccountControl & UF_WORKSTATION_TRUST_ACCOUNT) { 126 if (ent_type == HDB_SAMBA4_ENT_TYPE_CLIENT || ent_type == HDB_SAMBA4_ENT_TYPE_ANY) { 127 flags.client = 1; 128 } 129 flags.invalid = 0; 130 } 131 if (userAccountControl & UF_SERVER_TRUST_ACCOUNT) { 132 if (ent_type == HDB_SAMBA4_ENT_TYPE_CLIENT || ent_type == HDB_SAMBA4_ENT_TYPE_ANY) { 133 flags.client = 1; 134 } 135 flags.invalid = 0; 136 } 137 138 /* Not permitted to act as a client if disabled */ 139 if (userAccountControl & UF_ACCOUNTDISABLE) { 140 flags.client = 0; 141 } 142 if (userAccountControl & UF_LOCKOUT) { 143 flags.invalid = 1; 144 } 145 /* 146 if (userAccountControl & UF_PASSWORD_NOTREQD) { 147 flags.invalid = 1; 148 } 149 */ 150 /* 151 UF_PASSWORD_CANT_CHANGE and UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED are irrelevent 152 */ 153 if (userAccountControl & UF_TEMP_DUPLICATE_ACCOUNT) { 154 flags.invalid = 1; 155 } 156 157 /* UF_DONT_EXPIRE_PASSWD and UF_USE_DES_KEY_ONLY handled in hdb_samba4_message2entry() */ 158 159 /* 160 if (userAccountControl & UF_MNS_LOGON_ACCOUNT) { 161 flags.invalid = 1; 162 } 163 */ 164 if (userAccountControl & UF_SMARTCARD_REQUIRED) { 165 flags.require_hwauth = 1; 166 } 167 if (userAccountControl & UF_TRUSTED_FOR_DELEGATION) { 168 flags.ok_as_delegate = 1; 169 } 170 if (!(userAccountControl & UF_NOT_DELEGATED)) { 171 flags.forwardable = 1; 172 flags.proxiable = 1; 173 } 174 175 if (userAccountControl & UF_DONT_REQUIRE_PREAUTH) { 176 flags.require_preauth = 0; 177 } else { 178 flags.require_preauth = 1; 179 180 } 181 return flags; 182 } 183 184 static int hdb_samba4_destructor(struct hdb_samba4_private *p) 185 { 186 hdb_entry_ex *entry_ex = p->entry_ex; 187 free_hdb_entry(&entry_ex->entry); 188 return 0; 189 } 190 191 static void hdb_samba4_free_entry(krb5_context context, hdb_entry_ex *entry_ex) 192 { 193 talloc_free(entry_ex->ctx); 194 } 195 196 static krb5_error_code hdb_samba4_message2entry_keys(krb5_context context, 197 struct smb_iconv_convenience *iconv_convenience, 198 TALLOC_CTX *mem_ctx, 199 struct ldb_message *msg, 200 unsigned int userAccountControl, 201 hdb_entry_ex *entry_ex) 202 { 203 krb5_error_code ret = 0; 204 enum ndr_err_code ndr_err; 205 struct samr_Password *hash; 206 const struct ldb_val *sc_val; 207 struct supplementalCredentialsBlob scb; 208 struct supplementalCredentialsPackage *scpk = NULL; 209 bool newer_keys = false; 210 struct package_PrimaryKerberosBlob _pkb; 211 struct package_PrimaryKerberosCtr3 *pkb3 = NULL; 212 struct package_PrimaryKerberosCtr4 *pkb4 = NULL; 213 uint32_t i; 214 uint32_t allocated_keys = 0; 215 216 entry_ex->entry.keys.val = NULL; 217 entry_ex->entry.keys.len = 0; 218 219 entry_ex->entry.kvno = ldb_msg_find_attr_as_int(msg, "msDS-KeyVersionNumber", 0); 220 221 /* Get keys from the db */ 222 223 hash = samdb_result_hash(mem_ctx, msg, "unicodePwd"); 224 sc_val = ldb_msg_find_ldb_val(msg, "supplementalCredentials"); 225 226 /* unicodePwd for enctype 0x17 (23) if present */ 227 if (hash) { 228 allocated_keys++; 229 } 230 231 /* supplementalCredentials if present */ 232 if (sc_val) { 233 ndr_err = ndr_pull_struct_blob_all(sc_val, mem_ctx, iconv_convenience, &scb, 234 (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob); 235 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { 236 dump_data(0, sc_val->data, sc_val->length); 237 ret = EINVAL; 238 goto out; 239 } 240 241 if (scb.sub.signature != SUPPLEMENTAL_CREDENTIALS_SIGNATURE) { 242 NDR_PRINT_DEBUG(supplementalCredentialsBlob, &scb); 243 ret = EINVAL; 244 goto out; 245 } 246 247 for (i=0; i < scb.sub.num_packages; i++) { 248 if (strcmp("Primary:Kerberos-Newer-Keys", scb.sub.packages[i].name) == 0) { 249 scpk = &scb.sub.packages[i]; 250 if (!scpk->data || !scpk->data[0]) { 251 scpk = NULL; 252 continue; 253 } 254 newer_keys = true; 255 break; 256 } else if (strcmp("Primary:Kerberos", scb.sub.packages[i].name) == 0) { 257 scpk = &scb.sub.packages[i]; 258 if (!scpk->data || !scpk->data[0]) { 259 scpk = NULL; 260 } 261 /* 262 * we don't break here in hope to find 263 * a Kerberos-Newer-Keys package 264 */ 265 } 266 } 267 } 268 /* 269 * Primary:Kerberos-Newer-Keys or Primary:Kerberos element 270 * of supplementalCredentials 271 */ 272 if (scpk) { 273 DATA_BLOB blob; 274 275 blob = strhex_to_data_blob(mem_ctx, scpk->data); 276 if (!blob.data) { 277 ret = ENOMEM; 278 goto out; 279 } 280 281 /* we cannot use ndr_pull_struct_blob_all() here, as w2k and w2k3 add padding bytes */ 282 ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, iconv_convenience, &_pkb, 283 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob); 284 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { 285 ret = EINVAL; 286 krb5_set_error_message(context, ret, "hdb_samba4_message2entry_keys: could not parse package_PrimaryKerberosBlob"); 287 krb5_warnx(context, "hdb_samba4_message2entry_keys: could not parse package_PrimaryKerberosBlob"); 288 goto out; 289 } 290 291 if (newer_keys && _pkb.version != 4) { 292 ret = EINVAL; 293 krb5_set_error_message(context, ret, "hdb_samba4_message2entry_keys: Primary:Kerberos-Newer-Keys not version 4"); 294 krb5_warnx(context, "hdb_samba4_message2entry_keys: Primary:Kerberos-Newer-Keys not version 4"); 295 goto out; 296 } 297 298 if (!newer_keys && _pkb.version != 3) { 299 ret = EINVAL; 300 krb5_set_error_message(context, ret, "hdb_samba4_message2entry_keys: could not parse Primary:Kerberos not version 3"); 301 krb5_warnx(context, "hdb_samba4_message2entry_keys: could not parse Primary:Kerberos not version 3"); 302 goto out; 303 } 304 305 if (_pkb.version == 4) { 306 pkb4 = &_pkb.ctr.ctr4; 307 allocated_keys += pkb4->num_keys; 308 } else if (_pkb.version == 3) { 309 pkb3 = &_pkb.ctr.ctr3; 310 allocated_keys += pkb3->num_keys; 311 } 312 } 313 314 if (allocated_keys == 0) { 315 /* oh, no password. Apparently (comment in 316 * hdb-ldap.c) this violates the ASN.1, but this 317 * allows an entry with no keys (yet). */ 318 return 0; 319 } 320 321 /* allocate space to decode into */ 322 entry_ex->entry.keys.len = 0; 323 entry_ex->entry.keys.val = calloc(allocated_keys, sizeof(Key)); 324 if (entry_ex->entry.keys.val == NULL) { 325 ret = ENOMEM; 326 goto out; 327 } 328 329 if (hash && !(userAccountControl & UF_USE_DES_KEY_ONLY)) { 330 Key key; 331 332 key.mkvno = 0; 333 key.salt = NULL; /* No salt for this enc type */ 334 335 ret = krb5_keyblock_init(context, 336 ENCTYPE_ARCFOUR_HMAC, 337 hash->hash, sizeof(hash->hash), 338 &key.key); 339 if (ret) { 340 goto out; 341 } 342 343 entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key; 344 entry_ex->entry.keys.len++; 345 } 346 347 if (pkb4) { 348 for (i=0; i < pkb4->num_keys; i++) { 349 bool use = true; 350 Key key; 351 352 if (!pkb4->keys[i].value) continue; 353 354 if (userAccountControl & UF_USE_DES_KEY_ONLY) { 355 switch (pkb4->keys[i].keytype) { 356 case ENCTYPE_DES_CBC_CRC: 357 case ENCTYPE_DES_CBC_MD5: 358 break; 359 default: 360 use = false; 361 break; 362 } 363 } 364 365 if (!use) continue; 366 367 key.mkvno = 0; 368 key.salt = NULL; 369 370 if (pkb4->salt.string) { 371 DATA_BLOB salt; 372 373 salt = data_blob_string_const(pkb4->salt.string); 374 375 key.salt = calloc(1, sizeof(*key.salt)); 376 if (key.salt == NULL) { 377 ret = ENOMEM; 378 goto out; 379 } 380 381 key.salt->type = hdb_pw_salt; 382 383 ret = krb5_data_copy(&key.salt->salt, salt.data, salt.length); 384 if (ret) { 385 free(key.salt); 386 key.salt = NULL; 387 goto out; 388 } 389 } 390 391 /* TODO: maybe pass the iteration_count somehow... */ 392 393 ret = krb5_keyblock_init(context, 394 pkb4->keys[i].keytype, 395 pkb4->keys[i].value->data, 396 pkb4->keys[i].value->length, 397 &key.key); 398 if (ret == KRB5_PROG_ETYPE_NOSUPP) { 399 DEBUG(2,("Unsupported keytype ignored - type %u\n", 400 pkb4->keys[i].keytype)); 401 ret = 0; 402 continue; 403 } 404 if (ret) { 405 if (key.salt) { 406 free_Salt(key.salt); 407 free(key.salt); 408 key.salt = NULL; 409 } 410 goto out; 411 } 412 413 entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key; 414 entry_ex->entry.keys.len++; 415 } 416 } else if (pkb3) { 417 for (i=0; i < pkb3->num_keys; i++) { 418 bool use = true; 419 Key key; 420 421 if (!pkb3->keys[i].value) continue; 422 423 if (userAccountControl & UF_USE_DES_KEY_ONLY) { 424 switch (pkb3->keys[i].keytype) { 425 case ENCTYPE_DES_CBC_CRC: 426 case ENCTYPE_DES_CBC_MD5: 427 break; 428 default: 429 use = false; 430 break; 431 } 432 } 433 434 if (!use) continue; 435 436 key.mkvno = 0; 437 key.salt = NULL; 438 439 if (pkb3->salt.string) { 440 DATA_BLOB salt; 441 442 salt = data_blob_string_const(pkb3->salt.string); 443 444 key.salt = calloc(1, sizeof(*key.salt)); 445 if (key.salt == NULL) { 446 ret = ENOMEM; 447 goto out; 448 } 449 450 key.salt->type = hdb_pw_salt; 451 452 ret = krb5_data_copy(&key.salt->salt, salt.data, salt.length); 453 if (ret) { 454 free(key.salt); 455 key.salt = NULL; 456 goto out; 457 } 458 } 459 460 ret = krb5_keyblock_init(context, 461 pkb3->keys[i].keytype, 462 pkb3->keys[i].value->data, 463 pkb3->keys[i].value->length, 464 &key.key); 465 if (ret) { 466 if (key.salt) { 467 free_Salt(key.salt); 468 free(key.salt); 469 key.salt = NULL; 470 } 471 goto out; 472 } 473 474 entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key; 475 entry_ex->entry.keys.len++; 476 } 477 } 478 479 out: 480 if (ret != 0) { 481 entry_ex->entry.keys.len = 0; 482 } 483 if (entry_ex->entry.keys.len == 0 && entry_ex->entry.keys.val) { 484 free(entry_ex->entry.keys.val); 485 entry_ex->entry.keys.val = NULL; 486 } 487 return ret; 488 } 489 490 /* 491 * Construct an hdb_entry from a directory entry. 492 */ 493 static krb5_error_code hdb_samba4_message2entry(krb5_context context, HDB *db, 494 struct loadparm_context *lp_ctx, 495 TALLOC_CTX *mem_ctx, krb5_const_principal principal, 496 enum hdb_samba4_ent_type ent_type, 497 struct ldb_dn *realm_dn, 498 struct ldb_message *msg, 499 hdb_entry_ex *entry_ex) 500 { 501 unsigned int userAccountControl; 502 int i; 503 krb5_error_code ret = 0; 504 krb5_boolean is_computer = FALSE; 505 char *realm = strupper_talloc(mem_ctx, lp_realm(lp_ctx)); 506 507 struct hdb_samba4_private *p; 508 NTTIME acct_expiry; 509 NTSTATUS status; 510 511 uint32_t rid; 512 struct ldb_message_element *objectclasses; 513 struct ldb_val computer_val; 514 const char *samAccountName = ldb_msg_find_attr_as_string(msg, "samAccountName", NULL); 515 computer_val.data = discard_const_p(uint8_t,"computer"); 516 computer_val.length = strlen((const char *)computer_val.data); 517 518 if (!samAccountName) { 519 ret = ENOENT; 520 krb5_set_error_message(context, ret, "hdb_samba4_message2entry: no samAccountName present"); 521 goto out; 522 } 523 524 objectclasses = ldb_msg_find_element(msg, "objectClass"); 525 526 if (objectclasses && ldb_msg_find_val(objectclasses, &computer_val)) { 527 is_computer = TRUE; 528 } 529 530 memset(entry_ex, 0, sizeof(*entry_ex)); 531 532 if (!realm) { 533 ret = ENOMEM; 534 krb5_set_error_message(context, ret, "talloc_strdup: out of memory"); 535 goto out; 536 } 537 538 p = talloc(mem_ctx, struct hdb_samba4_private); 539 if (!p) { 540 ret = ENOMEM; 541 goto out; 542 } 543 544 p->entry_ex = entry_ex; 545 p->iconv_convenience = lp_iconv_convenience(lp_ctx); 546 p->lp_ctx = lp_ctx; 547 p->realm_dn = talloc_reference(p, realm_dn); 548 if (!p->realm_dn) { 549 ret = ENOMEM; 550 goto out; 551 } 552 553 talloc_set_destructor(p, hdb_samba4_destructor); 554 555 entry_ex->ctx = p; 556 entry_ex->free_entry = hdb_samba4_free_entry; 557 558 userAccountControl = ldb_msg_find_attr_as_uint(msg, "userAccountControl", 0); 559 560 561 entry_ex->entry.principal = malloc(sizeof(*(entry_ex->entry.principal))); 562 if (ent_type == HDB_SAMBA4_ENT_TYPE_ANY && principal == NULL) { 563 krb5_make_principal(context, &entry_ex->entry.principal, realm, samAccountName, NULL); 564 } else { 565 ret = copy_Principal(principal, entry_ex->entry.principal); 566 if (ret) { 567 krb5_clear_error_message(context); 568 goto out; 569 } 570 571 /* While we have copied the client principal, tests 572 * show that Win2k3 returns the 'corrected' realm, not 573 * the client-specified realm. This code attempts to 574 * replace the client principal's realm with the one 575 * we determine from our records */ 576 577 /* this has to be with malloc() */ 578 krb5_principal_set_realm(context, entry_ex->entry.principal, realm); 579 } 580 581 /* First try and figure out the flags based on the userAccountControl */ 582 entry_ex->entry.flags = uf2HDBFlags(context, userAccountControl, ent_type); 583 584 /* Windows 2008 seems to enforce this (very sensible) rule by 585 * default - don't allow offline attacks on a user's password 586 * by asking for a ticket to them as a service (encrypted with 587 * their probably patheticly insecure password) */ 588 589 if (entry_ex->entry.flags.server 590 && lp_parm_bool(lp_ctx, NULL, "kdc", "require spn for service", true)) { 591 if (!is_computer && !ldb_msg_find_attr_as_string(msg, "servicePrincipalName", NULL)) { 592 entry_ex->entry.flags.server = 0; 593 } 594 } 595 596 { 597 /* These (created_by, modified_by) parts of the entry are not relevant for Samba4's use 598 * of the Heimdal KDC. They are stored in a the traditional 599 * DB for audit purposes, and still form part of the structure 600 * we must return */ 601 602 /* use 'whenCreated' */ 603 entry_ex->entry.created_by.time = ldb_msg_find_krb5time_ldap_time(msg, "whenCreated", 0); 604 /* use '???' */ 605 entry_ex->entry.created_by.principal = NULL; 606 607 entry_ex->entry.modified_by = (Event *) malloc(sizeof(Event)); 608 if (entry_ex->entry.modified_by == NULL) { 609 ret = ENOMEM; 610 krb5_set_error_message(context, ret, "malloc: out of memory"); 611 goto out; 612 } 613 614 /* use 'whenChanged' */ 615 entry_ex->entry.modified_by->time = ldb_msg_find_krb5time_ldap_time(msg, "whenChanged", 0); 616 /* use '???' */ 617 entry_ex->entry.modified_by->principal = NULL; 618 } 619 620 621 /* The lack of password controls etc applies to krbtgt by 622 * virtue of being that particular RID */ 623 status = dom_sid_split_rid(NULL, samdb_result_dom_sid(mem_ctx, msg, "objectSid"), NULL, &rid); 624 625 if (!NT_STATUS_IS_OK(status)) { 626 ret = EINVAL; 627 goto out; 628 } 629 630 if (rid == DOMAIN_RID_KRBTGT) { 631 entry_ex->entry.valid_end = NULL; 632 entry_ex->entry.pw_end = NULL; 633 634 entry_ex->entry.flags.invalid = 0; 635 entry_ex->entry.flags.server = 1; 636 637 /* Don't mark all requests for the krbtgt/realm as 638 * 'change password', as otherwise we could get into 639 * trouble, and not enforce the password expirty. 640 * Instead, only do it when request is for the kpasswd service */ 641 if (ent_type == HDB_SAMBA4_ENT_TYPE_SERVER 642 && principal->name.name_string.len == 2 643 && (strcmp(principal->name.name_string.val[0], "kadmin") == 0) 644 && (strcmp(principal->name.name_string.val[1], "changepw") == 0) 645 && lp_is_my_domain_or_realm(lp_ctx, principal->realm)) { 646 entry_ex->entry.flags.change_pw = 1; 647 } 648 entry_ex->entry.flags.client = 0; 649 entry_ex->entry.flags.forwardable = 1; 650 entry_ex->entry.flags.ok_as_delegate = 1; 651 } else if (entry_ex->entry.flags.server && ent_type == HDB_SAMBA4_ENT_TYPE_SERVER) { 652 /* The account/password expiry only applies when the account is used as a 653 * client (ie password login), not when used as a server */ 654 655 /* Make very well sure we don't use this for a client, 656 * it could bypass the password restrictions */ 657 entry_ex->entry.flags.client = 0; 658 659 entry_ex->entry.valid_end = NULL; 660 entry_ex->entry.pw_end = NULL; 661 662 } else { 663 NTTIME must_change_time 664 = samdb_result_force_password_change((struct ldb_context *)db->hdb_db, mem_ctx, 665 realm_dn, msg); 666 if (must_change_time == 0x7FFFFFFFFFFFFFFFULL) { 667 entry_ex->entry.pw_end = NULL; 668 } else { 669 entry_ex->entry.pw_end = malloc(sizeof(*entry_ex->entry.pw_end)); 670 if (entry_ex->entry.pw_end == NULL) { 671 ret = ENOMEM; 672 goto out; 673 } 674 *entry_ex->entry.pw_end = nt_time_to_unix(must_change_time); 675 } 676 677 acct_expiry = samdb_result_account_expires(msg); 678 if (acct_expiry == 0x7FFFFFFFFFFFFFFFULL) { 679 entry_ex->entry.valid_end = NULL; 680 } else { 681 entry_ex->entry.valid_end = malloc(sizeof(*entry_ex->entry.valid_end)); 682 if (entry_ex->entry.valid_end == NULL) { 683 ret = ENOMEM; 684 goto out; 685 } 686 *entry_ex->entry.valid_end = nt_time_to_unix(acct_expiry); 687 } 688 } 689 690 entry_ex->entry.valid_start = NULL; 691 692 entry_ex->entry.max_life = NULL; 693 694 entry_ex->entry.max_renew = NULL; 695 696 entry_ex->entry.generation = NULL; 697 698 /* Get keys from the db */ 699 ret = hdb_samba4_message2entry_keys(context, p->iconv_convenience, p, msg, userAccountControl, entry_ex); 700 if (ret) { 701 /* Could be bougus data in the entry, or out of memory */ 702 goto out; 703 } 704 705 entry_ex->entry.etypes = malloc(sizeof(*(entry_ex->entry.etypes))); 706 if (entry_ex->entry.etypes == NULL) { 707 krb5_clear_error_message(context); 708 ret = ENOMEM; 709 goto out; 710 } 711 entry_ex->entry.etypes->len = entry_ex->entry.keys.len; 712 entry_ex->entry.etypes->val = calloc(entry_ex->entry.etypes->len, sizeof(int)); 713 if (entry_ex->entry.etypes->val == NULL) { 714 krb5_clear_error_message(context); 715 ret = ENOMEM; 716 goto out; 717 } 718 for (i=0; i < entry_ex->entry.etypes->len; i++) { 719 entry_ex->entry.etypes->val[i] = entry_ex->entry.keys.val[i].key.keytype; 720 } 721 722 723 p->msg = talloc_steal(p, msg); 724 p->samdb = (struct ldb_context *)db->hdb_db; 725 726 out: 727 if (ret != 0) { 728 /* This doesn't free ent itself, that is for the eventual caller to do */ 729 hdb_free_entry(context, entry_ex); 730 } else { 731 talloc_steal(db, entry_ex->ctx); 732 } 733 734 return ret; 735 } 736 737 /* 738 * Construct an hdb_entry from a directory entry. 739 */ 740 static krb5_error_code hdb_samba4_trust_message2entry(krb5_context context, HDB *db, 741 struct loadparm_context *lp_ctx, 742 TALLOC_CTX *mem_ctx, krb5_const_principal principal, 743 enum trust_direction direction, 744 struct ldb_dn *realm_dn, 745 struct ldb_message *msg, 746 hdb_entry_ex *entry_ex) 747 { 748 749 const char *dnsdomain; 750 char *realm; 751 DATA_BLOB password_utf16; 752 struct samr_Password password_hash; 753 const struct ldb_val *password_val; 754 struct trustAuthInOutBlob password_blob; 755 struct hdb_samba4_private *p; 756 757 enum ndr_err_code ndr_err; 758 int i, ret, trust_direction_flags; 759 760 p = talloc(mem_ctx, struct hdb_samba4_private); 761 if (!p) { 762 ret = ENOMEM; 763 goto out; 764 } 765 766 p->entry_ex = entry_ex; 767 p->iconv_convenience = lp_iconv_convenience(lp_ctx); 768 p->lp_ctx = lp_ctx; 769 p->realm_dn = realm_dn; 770 771 talloc_set_destructor(p, hdb_samba4_destructor); 772 773 entry_ex->ctx = p; 774 entry_ex->free_entry = hdb_samba4_free_entry; 775 776 /* use 'whenCreated' */ 777 entry_ex->entry.created_by.time = ldb_msg_find_krb5time_ldap_time(msg, "whenCreated", 0); 778 /* use '???' */ 779 entry_ex->entry.created_by.principal = NULL; 780 781 entry_ex->entry.valid_start = NULL; 782 783 trust_direction_flags = ldb_msg_find_attr_as_int(msg, "trustDirection", 0); 784 785 if (direction == INBOUND) { 786 realm = strupper_talloc(mem_ctx, lp_realm(lp_ctx)); 787 password_val = ldb_msg_find_ldb_val(msg, "trustAuthIncoming"); 788 789 } else { /* OUTBOUND */ 790 dnsdomain = ldb_msg_find_attr_as_string(msg, "trustPartner", NULL); 791 realm = strupper_talloc(mem_ctx, dnsdomain); 792 password_val = ldb_msg_find_ldb_val(msg, "trustAuthOutgoing"); 793 } 794 795 if (!password_val || !(trust_direction_flags & direction)) { 796 ret = ENOENT; 797 goto out; 798 } 799 800 ndr_err = ndr_pull_struct_blob(password_val, mem_ctx, p->iconv_convenience, &password_blob, 801 (ndr_pull_flags_fn_t)ndr_pull_trustAuthInOutBlob); 802 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { 803 ret = EINVAL; 804 goto out; 805 } 806 807 entry_ex->entry.kvno = -1; 808 for (i=0; i < password_blob.count; i++) { 809 if (password_blob.current->array[i].AuthType == TRUST_AUTH_TYPE_VERSION) { 810 entry_ex->entry.kvno = password_blob.current->array[i].AuthInfo.version.version; 811 } 812 } 813 814 for (i=0; i < password_blob.count; i++) { 815 if (password_blob.current->array[i].AuthType == TRUST_AUTH_TYPE_CLEAR) { 816 password_utf16 = data_blob_const(password_blob.current->array[i].AuthInfo.clear.password, 817 password_blob.current->array[i].AuthInfo.clear.size); 818 /* In the future, generate all sorts of 819 * hashes, but for now we can't safely convert 820 * the random strings windows uses into 821 * utf8 */ 822 823 /* but as it is utf16 already, we can get the NT password/arcfour-hmac-md5 key */ 824 mdfour(password_hash.hash, password_utf16.data, password_utf16.length); 825 break; 826 } else if (password_blob.current->array[i].AuthType == TRUST_AUTH_TYPE_NT4OWF) { 827 password_hash = password_blob.current->array[i].AuthInfo.nt4owf.password; 828 break; 829 } 830 } 831 entry_ex->entry.keys.len = 0; 832 entry_ex->entry.keys.val = NULL; 833 834 if (i < password_blob.count) { 835 Key key; 836 /* Must have found a cleartext or MD4 password */ 837 entry_ex->entry.keys.val = calloc(1, sizeof(Key)); 838 839 key.mkvno = 0; 840 key.salt = NULL; /* No salt for this enc type */ 841 842 if (entry_ex->entry.keys.val == NULL) { 843 ret = ENOMEM; 844 goto out; 845 } 846 847 ret = krb5_keyblock_init(context, 848 ENCTYPE_ARCFOUR_HMAC, 849 password_hash.hash, sizeof(password_hash.hash), 850 &key.key); 851 852 entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key; 853 entry_ex->entry.keys.len++; 854 } 855 856 entry_ex->entry.principal = malloc(sizeof(*(entry_ex->entry.principal))); 857 858 ret = copy_Principal(principal, entry_ex->entry.principal); 859 if (ret) { 860 krb5_clear_error_message(context); 861 goto out; 862 } 863 864 /* While we have copied the client principal, tests 865 * show that Win2k3 returns the 'corrected' realm, not 866 * the client-specified realm. This code attempts to 867 * replace the client principal's realm with the one 868 * we determine from our records */ 869 870 krb5_principal_set_realm(context, entry_ex->entry.principal, realm); 871 entry_ex->entry.flags = int2HDBFlags(0); 872 entry_ex->entry.flags.immutable = 1; 873 entry_ex->entry.flags.invalid = 0; 874 entry_ex->entry.flags.server = 1; 875 entry_ex->entry.flags.require_preauth = 1; 876 877 entry_ex->entry.pw_end = NULL; 878 879 entry_ex->entry.max_life = NULL; 880 881 entry_ex->entry.max_renew = NULL; 882 883 entry_ex->entry.generation = NULL; 884 885 entry_ex->entry.etypes = malloc(sizeof(*(entry_ex->entry.etypes))); 886 if (entry_ex->entry.etypes == NULL) { 887 krb5_clear_error_message(context); 888 ret = ENOMEM; 889 goto out; 890 } 891 entry_ex->entry.etypes->len = entry_ex->entry.keys.len; 892 entry_ex->entry.etypes->val = calloc(entry_ex->entry.etypes->len, sizeof(int)); 893 if (entry_ex->entry.etypes->val == NULL) { 894 krb5_clear_error_message(context); 895 ret = ENOMEM; 896 goto out; 897 } 898 for (i=0; i < entry_ex->entry.etypes->len; i++) { 899 entry_ex->entry.etypes->val[i] = entry_ex->entry.keys.val[i].key.keytype; 900 } 901 902 903 p->msg = talloc_steal(p, msg); 904 p->samdb = (struct ldb_context *)db->hdb_db; 905 906 out: 907 if (ret != 0) { 908 /* This doesn't free ent itself, that is for the eventual caller to do */ 909 hdb_free_entry(context, entry_ex); 910 } else { 911 talloc_steal(db, entry_ex->ctx); 912 } 913 914 return ret; 915 916 } 917 918 static krb5_error_code hdb_samba4_lookup_trust(krb5_context context, struct ldb_context *ldb_ctx, 919 TALLOC_CTX *mem_ctx, 920 const char *realm, 921 struct ldb_dn *realm_dn, 922 struct ldb_message **pmsg) 923 { 924 int lret; 925 krb5_error_code ret; 926 char *filter = NULL; 927 const char * const *attrs = trust_attrs; 928 929 struct ldb_result *res = NULL; 930 filter = talloc_asprintf(mem_ctx, "(&(objectClass=trustedDomain)(|(flatname=%s)(trustPartner=%s)))", realm, realm); 931 932 if (!filter) { 933 ret = ENOMEM; 934 krb5_set_error_message(context, ret, "talloc_asprintf: out of memory"); 935 return ret; 936 } 937 938 lret = ldb_search(ldb_ctx, mem_ctx, &res, 939 ldb_get_default_basedn(ldb_ctx), 940 LDB_SCOPE_SUBTREE, attrs, "%s", filter); 941 if (lret != LDB_SUCCESS) { 942 DEBUG(3, ("Failed to search for %s: %s\n", filter, ldb_errstring(ldb_ctx))); 943 return HDB_ERR_NOENTRY; 944 } else if (res->count == 0 || res->count > 1) { 945 DEBUG(3, ("Failed find a single entry for %s: got %d\n", filter, res->count)); 946 talloc_free(res); 947 return HDB_ERR_NOENTRY; 948 } 949 talloc_steal(mem_ctx, res->msgs); 950 *pmsg = res->msgs[0]; 951 talloc_free(res); 952 return 0; 953 } 36 #include "kdc/kdc-glue.h" 37 #include "kdc/db-glue.h" 954 38 955 39 static krb5_error_code hdb_samba4_open(krb5_context context, HDB *db, int flags, mode_t mode) … … 960 44 krb5_set_error_message(context, ret, "hdb_samba4_open: use of a master key incompatible with LDB\n"); 961 45 return ret; 962 } 46 } 963 47 964 48 return 0; … … 985 69 } 986 70 987 static krb5_error_code hdb_samba4_lookup_client(krb5_context context, HDB *db,988 struct loadparm_context *lp_ctx,989 TALLOC_CTX *mem_ctx,990 krb5_const_principal principal,991 const char **attrs,992 struct ldb_dn **realm_dn,993 struct ldb_message **msg) {994 NTSTATUS nt_status;995 char *principal_string;996 krb5_error_code ret;997 998 ret = krb5_unparse_name(context, principal, &principal_string);999 1000 if (ret != 0) {1001 return ret;1002 }1003 1004 nt_status = sam_get_results_principal((struct ldb_context *)db->hdb_db,1005 mem_ctx, principal_string, attrs,1006 realm_dn, msg);1007 free(principal_string);1008 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER)) {1009 return HDB_ERR_NOENTRY;1010 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_MEMORY)) {1011 return ENOMEM;1012 } else if (!NT_STATUS_IS_OK(nt_status)) {1013 return EINVAL;1014 }1015 1016 return ret;1017 }1018 1019 static krb5_error_code hdb_samba4_fetch_client(krb5_context context, HDB *db,1020 struct loadparm_context *lp_ctx,1021 TALLOC_CTX *mem_ctx,1022 krb5_const_principal principal,1023 unsigned flags,1024 hdb_entry_ex *entry_ex) {1025 struct ldb_dn *realm_dn;1026 krb5_error_code ret;1027 struct ldb_message *msg = NULL;1028 1029 ret = hdb_samba4_lookup_client(context, db, lp_ctx,1030 mem_ctx, principal, user_attrs,1031 &realm_dn, &msg);1032 if (ret != 0) {1033 return ret;1034 }1035 1036 ret = hdb_samba4_message2entry(context, db, lp_ctx, mem_ctx,1037 principal, HDB_SAMBA4_ENT_TYPE_CLIENT,1038 realm_dn, msg, entry_ex);1039 return ret;1040 }1041 1042 static krb5_error_code hdb_samba4_fetch_krbtgt(krb5_context context, HDB *db,1043 struct loadparm_context *lp_ctx,1044 TALLOC_CTX *mem_ctx,1045 krb5_const_principal principal,1046 unsigned flags,1047 hdb_entry_ex *entry_ex)1048 {1049 krb5_error_code ret;1050 struct ldb_message *msg = NULL;1051 struct ldb_dn *realm_dn = ldb_get_default_basedn(db->hdb_db);1052 const char *realm;1053 1054 krb5_principal alloc_principal = NULL;1055 if (principal->name.name_string.len != 21056 || (strcmp(principal->name.name_string.val[0], KRB5_TGS_NAME) != 0)) {1057 /* Not a krbtgt */1058 return HDB_ERR_NOENTRY;1059 }1060 1061 /* krbtgt case. Either us or a trusted realm */1062 1063 if (lp_is_my_domain_or_realm(lp_ctx, principal->realm)1064 && lp_is_my_domain_or_realm(lp_ctx, principal->name.name_string.val[1])) {1065 /* us */1066 /* Cludge, cludge cludge. If the realm part of krbtgt/realm,1067 * is in our db, then direct the caller at our primary1068 * krbtgt */1069 1070 int lret;1071 char *realm_fixed;1072 1073 lret = gendb_search_single_extended_dn(db->hdb_db, mem_ctx,1074 realm_dn, LDB_SCOPE_SUBTREE,1075 &msg, krbtgt_attrs,1076 "(&(objectClass=user)(samAccountName=krbtgt))");1077 if (lret == LDB_ERR_NO_SUCH_OBJECT) {1078 krb5_warnx(context, "hdb_samba4_fetch: could not find own KRBTGT in DB!");1079 krb5_set_error_message(context, HDB_ERR_NOENTRY, "hdb_samba4_fetch: could not find own KRBTGT in DB!");1080 return HDB_ERR_NOENTRY;1081 } else if (lret != LDB_SUCCESS) {1082 krb5_warnx(context, "hdb_samba4_fetch: could not find own KRBTGT in DB: %s", ldb_errstring(db->hdb_db));1083 krb5_set_error_message(context, HDB_ERR_NOENTRY, "hdb_samba4_fetch: could not find own KRBTGT in DB: %s", ldb_errstring(db->hdb_db));1084 return HDB_ERR_NOENTRY;1085 }1086 1087 realm_fixed = strupper_talloc(mem_ctx, lp_realm(lp_ctx));1088 if (!realm_fixed) {1089 ret = ENOMEM;1090 krb5_set_error_message(context, ret, "strupper_talloc: out of memory");1091 return ret;1092 }1093 1094 ret = krb5_copy_principal(context, principal, &alloc_principal);1095 if (ret) {1096 return ret;1097 }1098 1099 free(alloc_principal->name.name_string.val[1]);1100 alloc_principal->name.name_string.val[1] = strdup(realm_fixed);1101 talloc_free(realm_fixed);1102 if (!alloc_principal->name.name_string.val[1]) {1103 ret = ENOMEM;1104 krb5_set_error_message(context, ret, "hdb_samba4_fetch: strdup() failed!");1105 return ret;1106 }1107 principal = alloc_principal;1108 1109 ret = hdb_samba4_message2entry(context, db, lp_ctx, mem_ctx,1110 principal, HDB_SAMBA4_ENT_TYPE_KRBTGT,1111 realm_dn, msg, entry_ex);1112 if (ret != 0) {1113 krb5_warnx(context, "hdb_samba4_fetch: self krbtgt message2entry failed");1114 }1115 return ret;1116 1117 } else {1118 enum trust_direction direction = UNKNOWN;1119 1120 /* Either an inbound or outbound trust */1121 1122 if (strcasecmp(lp_realm(lp_ctx), principal->realm) == 0) {1123 /* look for inbound trust */1124 direction = INBOUND;1125 realm = principal->name.name_string.val[1];1126 }1127 1128 if (strcasecmp(lp_realm(lp_ctx), principal->name.name_string.val[1]) == 0) {1129 /* look for outbound trust */1130 direction = OUTBOUND;1131 realm = principal->realm;1132 }1133 1134 /* Trusted domains are under CN=system */1135 1136 ret = hdb_samba4_lookup_trust(context, (struct ldb_context *)db->hdb_db,1137 mem_ctx,1138 realm, realm_dn, &msg);1139 1140 if (ret != 0) {1141 krb5_warnx(context, "hdb_samba4_fetch: could not find principal in DB");1142 krb5_set_error_message(context, ret, "hdb_samba4_fetch: could not find principal in DB");1143 return ret;1144 }1145 1146 ret = hdb_samba4_trust_message2entry(context, db, lp_ctx, mem_ctx,1147 principal, direction,1148 realm_dn, msg, entry_ex);1149 if (ret != 0) {1150 krb5_warnx(context, "hdb_samba4_fetch: trust_message2entry failed");1151 }1152 return ret;1153 1154 1155 /* we should lookup trusted domains */1156 return HDB_ERR_NOENTRY;1157 }1158 1159 }1160 1161 static krb5_error_code hdb_samba4_lookup_server(krb5_context context, HDB *db,1162 struct loadparm_context *lp_ctx,1163 TALLOC_CTX *mem_ctx,1164 krb5_const_principal principal,1165 const char **attrs,1166 struct ldb_dn **realm_dn,1167 struct ldb_message **msg)1168 {1169 krb5_error_code ret;1170 const char *realm;1171 if (principal->name.name_string.len >= 2) {1172 /* 'normal server' case */1173 int ldb_ret;1174 NTSTATUS nt_status;1175 struct ldb_dn *user_dn;1176 char *principal_string;1177 1178 ret = krb5_unparse_name_flags(context, principal,1179 KRB5_PRINCIPAL_UNPARSE_NO_REALM,1180 &principal_string);1181 if (ret != 0) {1182 return ret;1183 }1184 1185 /* At this point we may find the host is known to be1186 * in a different realm, so we should generate a1187 * referral instead */1188 nt_status = crack_service_principal_name((struct ldb_context *)db->hdb_db,1189 mem_ctx, principal_string,1190 &user_dn, realm_dn);1191 free(principal_string);1192 1193 if (!NT_STATUS_IS_OK(nt_status)) {1194 return HDB_ERR_NOENTRY;1195 }1196 1197 ldb_ret = gendb_search_single_extended_dn((struct ldb_context *)db->hdb_db,1198 mem_ctx,1199 user_dn, LDB_SCOPE_BASE,1200 msg, attrs,1201 "(objectClass=*)");1202 if (ldb_ret != LDB_SUCCESS) {1203 return HDB_ERR_NOENTRY;1204 }1205 1206 } else {1207 int lret;1208 char *filter = NULL;1209 char *short_princ;1210 /* server as client principal case, but we must not lookup userPrincipalNames */1211 *realm_dn = ldb_get_default_basedn(db->hdb_db);1212 realm = krb5_principal_get_realm(context, principal);1213 1214 /* TODO: Check if it is our realm, otherwise give referall */1215 1216 ret = krb5_unparse_name_flags(context, principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM, &short_princ);1217 1218 if (ret != 0) {1219 krb5_set_error_message(context, ret, "hdb_samba4_lookup_principal: could not parse principal");1220 krb5_warnx(context, "hdb_samba4_lookup_principal: could not parse principal");1221 return ret;1222 }1223 1224 lret = gendb_search_single_extended_dn(db->hdb_db, mem_ctx,1225 *realm_dn, LDB_SCOPE_SUBTREE,1226 msg, attrs, "(&(objectClass=user)(samAccountName=%s))",1227 ldb_binary_encode_string(mem_ctx, short_princ));1228 free(short_princ);1229 if (lret == LDB_ERR_NO_SUCH_OBJECT) {1230 DEBUG(3, ("Failed find a entry for %s\n", filter));1231 return HDB_ERR_NOENTRY;1232 }1233 if (lret != LDB_SUCCESS) {1234 DEBUG(3, ("Failed single search for for %s - %s\n",1235 filter, ldb_errstring(db->hdb_db)));1236 return HDB_ERR_NOENTRY;1237 }1238 }1239 1240 return 0;1241 }1242 1243 static krb5_error_code hdb_samba4_fetch_server(krb5_context context, HDB *db,1244 struct loadparm_context *lp_ctx,1245 TALLOC_CTX *mem_ctx,1246 krb5_const_principal principal,1247 unsigned flags,1248 hdb_entry_ex *entry_ex)1249 {1250 krb5_error_code ret;1251 struct ldb_dn *realm_dn;1252 struct ldb_message *msg;1253 1254 ret = hdb_samba4_lookup_server(context, db, lp_ctx, mem_ctx, principal,1255 server_attrs, &realm_dn, &msg);1256 if (ret != 0) {1257 return ret;1258 }1259 1260 ret = hdb_samba4_message2entry(context, db, lp_ctx, mem_ctx,1261 principal, HDB_SAMBA4_ENT_TYPE_SERVER,1262 realm_dn, msg, entry_ex);1263 if (ret != 0) {1264 krb5_warnx(context, "hdb_samba4_fetch: message2entry failed");1265 }1266 1267 return ret;1268 }1269 1270 static krb5_error_code hdb_samba4_fetch(krb5_context context, HDB *db,1271 krb5_const_principal principal,1272 unsigned flags,1273 hdb_entry_ex *entry_ex)1274 {1275 krb5_error_code ret = HDB_ERR_NOENTRY;1276 TALLOC_CTX *mem_ctx = talloc_named(db, 0, "hdb_samba4_fetch context");1277 struct loadparm_context *lp_ctx = talloc_get_type(ldb_get_opaque(db->hdb_db, "loadparm"), struct loadparm_context);1278 1279 if (!mem_ctx) {1280 ret = ENOMEM;1281 krb5_set_error_message(context, ret, "hdb_samba4_fetch: talloc_named() failed!");1282 return ret;1283 }1284 1285 if (flags & HDB_F_GET_CLIENT) {1286 ret = hdb_samba4_fetch_client(context, db, lp_ctx, mem_ctx, principal, flags, entry_ex);1287 if (ret != HDB_ERR_NOENTRY) goto done;1288 }1289 if (flags & HDB_F_GET_SERVER) {1290 /* krbtgt fits into this situation for trusted realms, and for resolving different versions of our own realm name */1291 ret = hdb_samba4_fetch_krbtgt(context, db, lp_ctx, mem_ctx, principal, flags, entry_ex);1292 if (ret != HDB_ERR_NOENTRY) goto done;1293 1294 /* We return 'no entry' if it does not start with krbtgt/, so move to the common case quickly */1295 ret = hdb_samba4_fetch_server(context, db, lp_ctx, mem_ctx, principal, flags, entry_ex);1296 if (ret != HDB_ERR_NOENTRY) goto done;1297 }1298 if (flags & HDB_F_GET_KRBTGT) {1299 ret = hdb_samba4_fetch_krbtgt(context, db, lp_ctx, mem_ctx, principal, flags, entry_ex);1300 if (ret != HDB_ERR_NOENTRY) goto done;1301 }1302 1303 done:1304 talloc_free(mem_ctx);1305 return ret;1306 }1307 1308 71 static krb5_error_code hdb_samba4_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) 1309 72 { … … 1316 79 } 1317 80 1318 struct hdb_samba4_seq { 1319 struct ldb_context *ctx; 1320 struct loadparm_context *lp_ctx; 1321 int index; 1322 int count; 1323 struct ldb_message **msgs; 1324 struct ldb_dn *realm_dn; 1325 }; 1326 1327 static krb5_error_code hdb_samba4_seq(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) 1328 { 1329 krb5_error_code ret; 1330 struct hdb_samba4_seq *priv = (struct hdb_samba4_seq *)db->hdb_dbc; 1331 TALLOC_CTX *mem_ctx; 1332 hdb_entry_ex entry_ex; 1333 memset(&entry_ex, '\0', sizeof(entry_ex)); 1334 1335 if (!priv) { 1336 return HDB_ERR_NOENTRY; 1337 } 1338 1339 mem_ctx = talloc_named(priv, 0, "hdb_samba4_seq context"); 1340 1341 if (!mem_ctx) { 1342 ret = ENOMEM; 1343 krb5_set_error_message(context, ret, "hdb_samba4_seq: talloc_named() failed!"); 1344 return ret; 1345 } 1346 1347 if (priv->index < priv->count) { 1348 ret = hdb_samba4_message2entry(context, db, priv->lp_ctx, 1349 mem_ctx, 1350 NULL, HDB_SAMBA4_ENT_TYPE_ANY, 1351 priv->realm_dn, priv->msgs[priv->index++], entry); 1352 } else { 1353 ret = HDB_ERR_NOENTRY; 1354 } 1355 1356 if (ret != 0) { 1357 db->hdb_dbc = NULL; 1358 } else { 1359 talloc_free(mem_ctx); 1360 } 1361 1362 return ret; 81 static krb5_error_code hdb_samba4_fetch_kvno(krb5_context context, HDB *db, 82 krb5_const_principal principal, 83 unsigned flags, 84 krb5_kvno kvno, 85 hdb_entry_ex *entry_ex) 86 { 87 struct samba_kdc_db_context *kdc_db_ctx; 88 89 kdc_db_ctx = talloc_get_type_abort(db->hdb_db, 90 struct samba_kdc_db_context); 91 92 return samba_kdc_fetch(context, kdc_db_ctx, principal, flags, kvno, entry_ex); 1363 93 } 1364 94 … … 1366 96 hdb_entry_ex *entry) 1367 97 { 1368 struct ldb_context *ldb_ctx = (struct ldb_context *)db->hdb_db; 1369 struct loadparm_context *lp_ctx = talloc_get_type(ldb_get_opaque(ldb_ctx, "loadparm"), 1370 struct loadparm_context); 1371 struct hdb_samba4_seq *priv = (struct hdb_samba4_seq *)db->hdb_dbc; 1372 char *realm; 1373 struct ldb_result *res = NULL; 1374 krb5_error_code ret; 1375 TALLOC_CTX *mem_ctx; 1376 int lret; 1377 1378 if (priv) { 1379 talloc_free(priv); 1380 db->hdb_dbc = NULL; 1381 } 1382 1383 priv = (struct hdb_samba4_seq *) talloc(db, struct hdb_samba4_seq); 1384 if (!priv) { 1385 ret = ENOMEM; 1386 krb5_set_error_message(context, ret, "talloc: out of memory"); 1387 return ret; 1388 } 1389 1390 priv->ctx = ldb_ctx; 1391 priv->lp_ctx = lp_ctx; 1392 priv->index = 0; 1393 priv->msgs = NULL; 1394 priv->realm_dn = ldb_get_default_basedn(ldb_ctx); 1395 priv->count = 0; 1396 1397 mem_ctx = talloc_named(priv, 0, "hdb_samba4_firstkey context"); 1398 1399 if (!mem_ctx) { 1400 ret = ENOMEM; 1401 krb5_set_error_message(context, ret, "hdb_samba4_firstkey: talloc_named() failed!"); 1402 return ret; 1403 } 1404 1405 ret = krb5_get_default_realm(context, &realm); 1406 if (ret != 0) { 1407 talloc_free(priv); 1408 return ret; 1409 } 1410 1411 lret = ldb_search(ldb_ctx, priv, &res, 1412 priv->realm_dn, LDB_SCOPE_SUBTREE, user_attrs, 1413 "(objectClass=user)"); 1414 1415 if (lret != LDB_SUCCESS) { 1416 talloc_free(priv); 1417 return HDB_ERR_NOENTRY; 1418 } 1419 1420 priv->count = res->count; 1421 priv->msgs = talloc_steal(priv, res->msgs); 1422 talloc_free(res); 1423 1424 db->hdb_dbc = priv; 1425 1426 ret = hdb_samba4_seq(context, db, flags, entry); 1427 1428 if (ret != 0) { 1429 talloc_free(priv); 1430 db->hdb_dbc = NULL; 1431 } else { 1432 talloc_free(mem_ctx); 1433 } 1434 return ret; 98 struct samba_kdc_db_context *kdc_db_ctx; 99 100 kdc_db_ctx = talloc_get_type_abort(db->hdb_db, 101 struct samba_kdc_db_context); 102 103 return samba_kdc_firstkey(context, kdc_db_ctx, entry); 1435 104 } 1436 105 … … 1438 107 hdb_entry_ex *entry) 1439 108 { 1440 return hdb_samba4_seq(context, db, flags, entry); 109 struct samba_kdc_db_context *kdc_db_ctx; 110 111 kdc_db_ctx = talloc_get_type_abort(db->hdb_db, 112 struct samba_kdc_db_context); 113 114 return samba_kdc_nextkey(context, kdc_db_ctx, entry); 1441 115 } 1442 116 … … 1447 121 } 1448 122 1449 1450 /* Check if a given entry may delegate to this target principal 1451 * 1452 * This is currently a very nasty hack - allowing only delegation to itself. 1453 */ 1454 krb5_error_code hdb_samba4_check_constrained_delegation(krb5_context context, HDB *db, 1455 hdb_entry_ex *entry, 1456 krb5_const_principal target_principal) 1457 { 1458 struct ldb_context *ldb_ctx = (struct ldb_context *)db->hdb_db; 1459 struct loadparm_context *lp_ctx = talloc_get_type(ldb_get_opaque(ldb_ctx, "loadparm"), 1460 struct loadparm_context); 1461 krb5_error_code ret; 1462 krb5_principal enterprise_prinicpal = NULL; 1463 struct ldb_dn *realm_dn; 1464 struct ldb_message *msg; 1465 struct dom_sid *orig_sid; 1466 struct dom_sid *target_sid; 1467 struct hdb_samba4_private *p = talloc_get_type(entry->ctx, struct hdb_samba4_private); 1468 const char *delegation_check_attrs[] = { 1469 "objectSid", NULL 1470 }; 1471 1472 TALLOC_CTX *mem_ctx = talloc_named(db, 0, "hdb_samba4_check_constrained_delegation"); 1473 1474 if (!mem_ctx) { 1475 ret = ENOMEM; 1476 krb5_set_error_message(context, ret, "hdb_samba4_fetch: talloc_named() failed!"); 1477 return ret; 1478 } 1479 1480 if (target_principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) { 1481 /* Need to reparse the enterprise principal to find the real target */ 1482 if (target_principal->name.name_string.len != 1) { 1483 ret = KRB5_PARSE_MALFORMED; 1484 krb5_set_error_message(context, ret, "hdb_samba4_check_constrained_delegation: request for delegation to enterprise principal with wrong (%d) number of components", 1485 target_principal->name.name_string.len); 1486 talloc_free(mem_ctx); 1487 return ret; 1488 } 1489 ret = krb5_parse_name(context, target_principal->name.name_string.val[0], 1490 &enterprise_prinicpal); 1491 if (ret) { 1492 talloc_free(mem_ctx); 1493 return ret; 1494 } 1495 target_principal = enterprise_prinicpal; 1496 } 1497 1498 ret = hdb_samba4_lookup_server(context, db, lp_ctx, mem_ctx, target_principal, 1499 delegation_check_attrs, &realm_dn, &msg); 1500 1501 krb5_free_principal(context, enterprise_prinicpal); 1502 1503 if (ret != 0) { 1504 talloc_free(mem_ctx); 1505 return ret; 1506 } 1507 1508 orig_sid = samdb_result_dom_sid(mem_ctx, p->msg, "objectSid"); 1509 target_sid = samdb_result_dom_sid(mem_ctx, msg, "objectSid"); 1510 1511 /* Allow delegation to the same principal, even if by a different 1512 * name. The easy and safe way to prove this is by SID 1513 * comparison */ 1514 if (!(orig_sid && target_sid && dom_sid_equal(orig_sid, target_sid))) { 1515 talloc_free(mem_ctx); 1516 return KRB5KDC_ERR_BADOPTION; 1517 } 1518 1519 talloc_free(mem_ctx); 1520 return ret; 1521 } 1522 1523 /* Certificates printed by a the Certificate Authority might have a 1524 * slightly different form of the user principal name to that in the 1525 * database. Allow a mismatch where they both refer to the same 1526 * SID */ 1527 1528 krb5_error_code hdb_samba4_check_pkinit_ms_upn_match(krb5_context context, HDB *db, 1529 hdb_entry_ex *entry, 1530 krb5_const_principal certificate_principal) 1531 { 1532 struct ldb_context *ldb_ctx = (struct ldb_context *)db->hdb_db; 1533 struct loadparm_context *lp_ctx = talloc_get_type(ldb_get_opaque(ldb_ctx, "loadparm"), 1534 struct loadparm_context); 1535 krb5_error_code ret; 1536 struct ldb_dn *realm_dn; 1537 struct ldb_message *msg; 1538 struct dom_sid *orig_sid; 1539 struct dom_sid *target_sid; 1540 struct hdb_samba4_private *p = talloc_get_type(entry->ctx, struct hdb_samba4_private); 1541 const char *ms_upn_check_attrs[] = { 1542 "objectSid", NULL 1543 }; 1544 1545 TALLOC_CTX *mem_ctx = talloc_named(db, 0, "hdb_samba4_check_constrained_delegation"); 1546 1547 if (!mem_ctx) { 1548 ret = ENOMEM; 1549 krb5_set_error_message(context, ret, "hdb_samba4_fetch: talloc_named() failed!"); 1550 return ret; 1551 } 1552 1553 ret = hdb_samba4_lookup_client(context, db, lp_ctx, 1554 mem_ctx, certificate_principal, 1555 ms_upn_check_attrs, &realm_dn, &msg); 1556 1557 if (ret != 0) { 1558 talloc_free(mem_ctx); 1559 return ret; 1560 } 1561 1562 orig_sid = samdb_result_dom_sid(mem_ctx, p->msg, "objectSid"); 1563 target_sid = samdb_result_dom_sid(mem_ctx, msg, "objectSid"); 1564 1565 /* Consider these to be the same principal, even if by a different 1566 * name. The easy and safe way to prove this is by SID 1567 * comparison */ 1568 if (!(orig_sid && target_sid && dom_sid_equal(orig_sid, target_sid))) { 1569 talloc_free(mem_ctx); 1570 return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH; 1571 } 1572 1573 talloc_free(mem_ctx); 1574 return ret; 1575 } 1576 1577 /* This interface is to be called by the KDC and libnet_keytab_dump, which is expecting Samba 1578 * calling conventions. It is also called by a wrapper 1579 * (hdb_samba4_create) from the kpasswdd -> krb5 -> keytab_hdb -> hdb 1580 * code */ 1581 1582 NTSTATUS hdb_samba4_create_kdc(TALLOC_CTX *mem_ctx, 1583 struct tevent_context *ev_ctx, 1584 struct loadparm_context *lp_ctx, 1585 krb5_context context, struct HDB **db) 1586 { 123 static krb5_error_code 124 hdb_samba4_check_identical_client_and_server(krb5_context context, HDB *db, 125 hdb_entry_ex *entry, 126 krb5_const_principal target_principal) 127 { 128 struct samba_kdc_db_context *kdc_db_ctx; 129 130 kdc_db_ctx = talloc_get_type_abort(db->hdb_db, 131 struct samba_kdc_db_context); 132 133 return samba_kdc_check_identical_client_and_server(context, kdc_db_ctx, 134 entry, 135 target_principal); 136 } 137 138 static krb5_error_code 139 hdb_samba4_check_pkinit_ms_upn_match(krb5_context context, HDB *db, 140 hdb_entry_ex *entry, 141 krb5_const_principal certificate_principal) 142 { 143 struct samba_kdc_db_context *kdc_db_ctx; 144 145 kdc_db_ctx = talloc_get_type_abort(db->hdb_db, 146 struct samba_kdc_db_context); 147 148 return samba_kdc_check_pkinit_ms_upn_match(context, kdc_db_ctx, 149 entry, 150 certificate_principal); 151 } 152 153 /* This interface is to be called by the KDC and libnet_keytab_dump, 154 * which is expecting Samba calling conventions. 155 * It is also called by a wrapper (hdb_samba4_create) from the 156 * kpasswdd -> krb5 -> keytab_hdb -> hdb code */ 157 158 NTSTATUS hdb_samba4_create_kdc(struct samba_kdc_base_context *base_ctx, 159 krb5_context context, struct HDB **db) 160 { 161 struct samba_kdc_db_context *kdc_db_ctx; 1587 162 NTSTATUS nt_status; 1588 struct auth_session_info *session_info; 1589 *db = talloc( mem_ctx, HDB);163 164 *db = talloc(base_ctx, HDB); 1590 165 if (!*db) { 1591 166 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); … … 1597 172 (*db)->hdb_capability_flags = 0; 1598 173 1599 nt_status = auth_system_session_info(*db, lp_ctx, &session_info);174 nt_status = samba_kdc_setup_db_ctx(*db, base_ctx, &kdc_db_ctx); 1600 175 if (!NT_STATUS_IS_OK(nt_status)) { 176 talloc_free(*db); 1601 177 return nt_status; 1602 178 } 1603 1604 /* The idea here is very simple. Using Kerberos to 1605 * authenticate the KDC to the LDAP server is higly likely to 1606 * be circular. 1607 * 1608 * In future we may set this up to use EXERNAL and SSL 1609 * certificates, for now it will almost certainly be NTLMSSP 1610 */ 1611 1612 cli_credentials_set_kerberos_state(session_info->credentials, 1613 CRED_DONT_USE_KERBEROS); 1614 1615 /* Setup the link to LDB */ 1616 (*db)->hdb_db = samdb_connect(*db, ev_ctx, lp_ctx, session_info); 1617 if ((*db)->hdb_db == NULL) { 1618 DEBUG(1, ("hdb_samba4_create: Cannot open samdb for KDC backend!")); 1619 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; 1620 } 179 (*db)->hdb_db = kdc_db_ctx; 1621 180 1622 181 (*db)->hdb_dbc = NULL; 1623 182 (*db)->hdb_open = hdb_samba4_open; 1624 183 (*db)->hdb_close = hdb_samba4_close; 1625 (*db)->hdb_fetch = hdb_samba4_fetch;184 (*db)->hdb_fetch_kvno = hdb_samba4_fetch_kvno; 1626 185 (*db)->hdb_store = hdb_samba4_store; 1627 186 (*db)->hdb_remove = hdb_samba4_remove; … … 1639 198 1640 199 (*db)->hdb_auth_status = NULL; 1641 (*db)->hdb_check_constrained_delegation = hdb_samba4_check_ constrained_delegation;200 (*db)->hdb_check_constrained_delegation = hdb_samba4_check_identical_client_and_server; 1642 201 (*db)->hdb_check_pkinit_ms_upn_match = hdb_samba4_check_pkinit_ms_upn_match; 202 (*db)->hdb_check_s4u2self = hdb_samba4_check_identical_client_and_server; 1643 203 1644 204 return NT_STATUS_OK; … … 1649 209 NTSTATUS nt_status; 1650 210 void *ptr; 1651 struct hdb_samba4_context *hdb_samba4_context; 211 struct samba_kdc_base_context *base_ctx; 212 1652 213 if (sscanf(arg, "&%p", &ptr) != 1) { 1653 214 return EINVAL; 1654 215 } 1655 hdb_samba4_context = talloc_get_type_abort(ptr, struct hdb_samba4_context);216 base_ctx = talloc_get_type_abort(ptr, struct samba_kdc_base_context); 1656 217 /* The global kdc_mem_ctx and kdc_lp_ctx, Disgusting, ugly hack, but it means one less private hook */ 1657 nt_status = hdb_samba4_create_kdc(hdb_samba4_context, hdb_samba4_context->ev_ctx, hdb_samba4_context->lp_ctx, 1658 context, db); 218 nt_status = hdb_samba4_create_kdc(base_ctx, context, db); 1659 219 1660 220 if (NT_STATUS_IS_OK(nt_status)) { … … 1673 233 struct hdb_method hdb_samba4 = { 1674 234 .interface_version = HDB_INTERFACE_VERSION, 1675 .prefix = "samba4", 235 .prefix = "samba4", 1676 236 .create = hdb_samba4_create 1677 237 };
Note:
See TracChangeset
for help on using the changeset viewer.