2 Unix SMB/CIFS implementation.
4 Database Glue between Samba and the KDC
6 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2009
7 Copyright (C) Simo Sorce <idra@samba.org> 2010
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "libcli/security/security.h"
26 #include "auth/auth.h"
27 #include "auth/auth_sam.h"
28 #include "dsdb/samdb/samdb.h"
29 #include "dsdb/common/util.h"
30 #include "librpc/gen_ndr/ndr_drsblobs.h"
31 #include "param/param.h"
32 #include "../lib/crypto/md4.h"
33 #include "system/kerberos.h"
34 #include "auth/kerberos/kerberos.h"
36 #include "kdc/samba_kdc.h"
37 #include "kdc/kdc-glue.h"
38 #include "kdc/kdc-policy.h"
39 #include "kdc/db-glue.h"
41 #define SAMBA_KVNO_GET_KRBTGT(kvno) \
42 ((uint16_t)(((uint32_t)kvno) >> 16))
44 #define SAMBA_KVNO_AND_KRBTGT(kvno, krbtgt) \
45 ((krb5_kvno)((((uint32_t)kvno) & 0xFFFF) | \
46 ((((uint32_t)krbtgt) << 16) & 0xFFFF0000)))
48 enum samba_kdc_ent_type
49 { SAMBA_KDC_ENT_TYPE_CLIENT, SAMBA_KDC_ENT_TYPE_SERVER,
50 SAMBA_KDC_ENT_TYPE_KRBTGT, SAMBA_KDC_ENT_TYPE_TRUST, SAMBA_KDC_ENT_TYPE_ANY };
52 enum trust_direction {
54 INBOUND = LSA_TRUST_DIRECTION_INBOUND,
55 OUTBOUND = LSA_TRUST_DIRECTION_OUTBOUND
58 static const char *trust_attrs[] = {
63 "msDS-SupportedEncryptionTypes",
70 static KerberosTime ldb_msg_find_krb5time_ldap_time(struct ldb_message *msg, const char *attr, KerberosTime default_val)
76 gentime = ldb_msg_find_attr_as_string(msg, attr, NULL);
80 tmp = strptime(gentime, "%Y%m%d%H%M%SZ", &tm);
88 static HDBFlags uf2HDBFlags(krb5_context context, uint32_t userAccountControl, enum samba_kdc_ent_type ent_type)
90 HDBFlags flags = int2HDBFlags(0);
92 /* we don't allow kadmin deletes */
95 /* mark the principal as invalid to start with */
100 /* All accounts are servers, but this may be disabled again in the caller */
103 /* Account types - clear the invalid bit if it turns out to be valid */
104 if (userAccountControl & UF_NORMAL_ACCOUNT) {
105 if (ent_type == SAMBA_KDC_ENT_TYPE_CLIENT || ent_type == SAMBA_KDC_ENT_TYPE_ANY) {
111 if (userAccountControl & UF_INTERDOMAIN_TRUST_ACCOUNT) {
112 if (ent_type == SAMBA_KDC_ENT_TYPE_CLIENT || ent_type == SAMBA_KDC_ENT_TYPE_ANY) {
117 if (userAccountControl & UF_WORKSTATION_TRUST_ACCOUNT) {
118 if (ent_type == SAMBA_KDC_ENT_TYPE_CLIENT || ent_type == SAMBA_KDC_ENT_TYPE_ANY) {
123 if (userAccountControl & UF_SERVER_TRUST_ACCOUNT) {
124 if (ent_type == SAMBA_KDC_ENT_TYPE_CLIENT || ent_type == SAMBA_KDC_ENT_TYPE_ANY) {
130 /* Not permitted to act as a client if disabled */
131 if (userAccountControl & UF_ACCOUNTDISABLE) {
134 if (userAccountControl & UF_LOCKOUT) {
138 if (userAccountControl & UF_PASSWORD_NOTREQD) {
143 UF_PASSWORD_CANT_CHANGE and UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED are irrelevent
145 if (userAccountControl & UF_TEMP_DUPLICATE_ACCOUNT) {
149 /* UF_DONT_EXPIRE_PASSWD and UF_USE_DES_KEY_ONLY handled in samba_kdc_message2entry() */
152 if (userAccountControl & UF_MNS_LOGON_ACCOUNT) {
156 if (userAccountControl & UF_SMARTCARD_REQUIRED) {
157 flags.require_hwauth = 1;
159 if (userAccountControl & UF_TRUSTED_FOR_DELEGATION) {
160 flags.ok_as_delegate = 1;
162 if (userAccountControl & UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION) {
164 * this is confusing...
166 * UF_TRUSTED_FOR_DELEGATION
171 * UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION
172 * => trusted_for_delegation
174 flags.trusted_for_delegation = 1;
176 if (!(userAccountControl & UF_NOT_DELEGATED)) {
177 flags.forwardable = 1;
181 if (userAccountControl & UF_DONT_REQUIRE_PREAUTH) {
182 flags.require_preauth = 0;
184 flags.require_preauth = 1;
190 static int samba_kdc_entry_destructor(struct samba_kdc_entry *p)
192 hdb_entry_ex *entry_ex = p->entry_ex;
193 free_hdb_entry(&entry_ex->entry);
197 static void samba_kdc_free_entry(krb5_context context, hdb_entry_ex *entry_ex)
199 /* this function is called only from hdb_free_entry().
200 * Make sure we neutralize the destructor or we will
201 * get a double free later when hdb_free_entry() will
202 * try to call free_hdb_entry() */
203 talloc_set_destructor(entry_ex->ctx, NULL);
205 /* now proceed to free the talloc part */
206 talloc_free(entry_ex->ctx);
209 static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
210 struct samba_kdc_db_context *kdc_db_ctx,
212 struct ldb_message *msg,
215 uint32_t userAccountControl,
216 enum samba_kdc_ent_type ent_type,
217 hdb_entry_ex *entry_ex)
219 krb5_error_code ret = 0;
220 enum ndr_err_code ndr_err;
221 struct samr_Password *hash;
222 const struct ldb_val *sc_val;
223 struct supplementalCredentialsBlob scb;
224 struct supplementalCredentialsPackage *scpk = NULL;
225 bool newer_keys = false;
226 struct package_PrimaryKerberosBlob _pkb;
227 struct package_PrimaryKerberosCtr3 *pkb3 = NULL;
228 struct package_PrimaryKerberosCtr4 *pkb4 = NULL;
230 uint16_t allocated_keys = 0;
231 int rodc_krbtgt_number = 0;
233 uint32_t supported_enctypes
234 = ldb_msg_find_attr_as_uint(msg,
235 "msDS-SupportedEncryptionTypes",
238 if (rid == DOMAIN_RID_KRBTGT || is_rodc) {
239 /* KDCs (and KDCs on RODCs) use AES */
240 supported_enctypes |= ENC_HMAC_SHA1_96_AES128 | ENC_HMAC_SHA1_96_AES256;
241 } else if (userAccountControl & (UF_PARTIAL_SECRETS_ACCOUNT|UF_SERVER_TRUST_ACCOUNT)) {
242 /* DCs and RODCs comptuer accounts use AES */
243 supported_enctypes |= ENC_HMAC_SHA1_96_AES128 | ENC_HMAC_SHA1_96_AES256;
244 } else if (ent_type == SAMBA_KDC_ENT_TYPE_CLIENT ||
245 (ent_type == SAMBA_KDC_ENT_TYPE_ANY)) {
246 /* for AS-REQ the client chooses the enc types it
247 * supports, and this will vary between computers a
250 * likewise for 'any' return as much as is supported,
251 * to export into a keytab */
252 supported_enctypes = ENC_ALL_TYPES;
255 /* If UF_USE_DES_KEY_ONLY has been set, then don't allow use of the newer enc types */
256 if (userAccountControl & UF_USE_DES_KEY_ONLY) {
257 supported_enctypes = ENC_CRC32|ENC_RSA_MD5;
259 /* Otherwise, add in the default enc types */
260 supported_enctypes |= ENC_CRC32 | ENC_RSA_MD5 | ENC_RC4_HMAC_MD5;
263 /* Is this the krbtgt or a RODC krbtgt */
265 rodc_krbtgt_number = ldb_msg_find_attr_as_int(msg, "msDS-SecondaryKrbTgtNumber", -1);
267 if (rodc_krbtgt_number == -1) {
272 entry_ex->entry.keys.val = NULL;
273 entry_ex->entry.keys.len = 0;
275 kvno = ldb_msg_find_attr_as_int(msg, "msDS-KeyVersionNumber", 0);
277 kvno = SAMBA_KVNO_AND_KRBTGT(kvno, rodc_krbtgt_number);
279 entry_ex->entry.kvno = kvno;
281 /* Get keys from the db */
283 hash = samdb_result_hash(mem_ctx, msg, "unicodePwd");
284 sc_val = ldb_msg_find_ldb_val(msg, "supplementalCredentials");
286 /* unicodePwd for enctype 0x17 (23) if present */
291 /* supplementalCredentials if present */
293 ndr_err = ndr_pull_struct_blob_all(sc_val, mem_ctx, &scb,
294 (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
295 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
296 dump_data(0, sc_val->data, sc_val->length);
301 if (scb.sub.signature != SUPPLEMENTAL_CREDENTIALS_SIGNATURE) {
302 NDR_PRINT_DEBUG(supplementalCredentialsBlob, &scb);
307 for (i=0; i < scb.sub.num_packages; i++) {
308 if (strcmp("Primary:Kerberos-Newer-Keys", scb.sub.packages[i].name) == 0) {
309 scpk = &scb.sub.packages[i];
310 if (!scpk->data || !scpk->data[0]) {
316 } else if (strcmp("Primary:Kerberos", scb.sub.packages[i].name) == 0) {
317 scpk = &scb.sub.packages[i];
318 if (!scpk->data || !scpk->data[0]) {
322 * we don't break here in hope to find
323 * a Kerberos-Newer-Keys package
329 * Primary:Kerberos-Newer-Keys or Primary:Kerberos element
330 * of supplementalCredentials
335 blob = strhex_to_data_blob(mem_ctx, scpk->data);
341 /* we cannot use ndr_pull_struct_blob_all() here, as w2k and w2k3 add padding bytes */
342 ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &_pkb,
343 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
344 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
346 krb5_set_error_message(context, ret, "samba_kdc_message2entry_keys: could not parse package_PrimaryKerberosBlob");
347 krb5_warnx(context, "samba_kdc_message2entry_keys: could not parse package_PrimaryKerberosBlob");
351 if (newer_keys && _pkb.version != 4) {
353 krb5_set_error_message(context, ret, "samba_kdc_message2entry_keys: Primary:Kerberos-Newer-Keys not version 4");
354 krb5_warnx(context, "samba_kdc_message2entry_keys: Primary:Kerberos-Newer-Keys not version 4");
358 if (!newer_keys && _pkb.version != 3) {
360 krb5_set_error_message(context, ret, "samba_kdc_message2entry_keys: could not parse Primary:Kerberos not version 3");
361 krb5_warnx(context, "samba_kdc_message2entry_keys: could not parse Primary:Kerberos not version 3");
365 if (_pkb.version == 4) {
366 pkb4 = &_pkb.ctr.ctr4;
367 allocated_keys += pkb4->num_keys;
368 } else if (_pkb.version == 3) {
369 pkb3 = &_pkb.ctr.ctr3;
370 allocated_keys += pkb3->num_keys;
374 if (allocated_keys == 0) {
375 if (kdc_db_ctx->rodc) {
376 /* We are on an RODC, but don't have keys for this account. Signal this to the caller */
377 return HDB_ERR_NOT_FOUND_HERE;
380 /* oh, no password. Apparently (comment in
381 * hdb-ldap.c) this violates the ASN.1, but this
382 * allows an entry with no keys (yet). */
386 /* allocate space to decode into */
387 entry_ex->entry.keys.len = 0;
388 entry_ex->entry.keys.val = calloc(allocated_keys, sizeof(Key));
389 if (entry_ex->entry.keys.val == NULL) {
394 if (hash && (supported_enctypes & ENC_RC4_HMAC_MD5)) {
398 key.salt = NULL; /* No salt for this enc type */
400 ret = krb5_keyblock_init(context,
401 ENCTYPE_ARCFOUR_HMAC,
402 hash->hash, sizeof(hash->hash),
408 entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key;
409 entry_ex->entry.keys.len++;
413 for (i=0; i < pkb4->num_keys; i++) {
416 if (!pkb4->keys[i].value) continue;
418 if (!(kerberos_enctype_to_bitmap(pkb4->keys[i].keytype) & supported_enctypes)) {
425 if (pkb4->salt.string) {
428 salt = data_blob_string_const(pkb4->salt.string);
430 key.salt = calloc(1, sizeof(*key.salt));
431 if (key.salt == NULL) {
436 key.salt->type = hdb_pw_salt;
438 ret = krb5_data_copy(&key.salt->salt, salt.data, salt.length);
446 /* TODO: maybe pass the iteration_count somehow... */
448 ret = krb5_keyblock_init(context,
449 pkb4->keys[i].keytype,
450 pkb4->keys[i].value->data,
451 pkb4->keys[i].value->length,
453 if (ret == KRB5_PROG_ETYPE_NOSUPP) {
454 DEBUG(2,("Unsupported keytype ignored - type %u\n",
455 pkb4->keys[i].keytype));
468 entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key;
469 entry_ex->entry.keys.len++;
472 for (i=0; i < pkb3->num_keys; i++) {
475 if (!pkb3->keys[i].value) continue;
477 if (!(kerberos_enctype_to_bitmap(pkb3->keys[i].keytype) & supported_enctypes)) {
484 if (pkb3->salt.string) {
487 salt = data_blob_string_const(pkb3->salt.string);
489 key.salt = calloc(1, sizeof(*key.salt));
490 if (key.salt == NULL) {
495 key.salt->type = hdb_pw_salt;
497 ret = krb5_data_copy(&key.salt->salt, salt.data, salt.length);
505 ret = krb5_keyblock_init(context,
506 pkb3->keys[i].keytype,
507 pkb3->keys[i].value->data,
508 pkb3->keys[i].value->length,
519 entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key;
520 entry_ex->entry.keys.len++;
526 entry_ex->entry.keys.len = 0;
528 if (entry_ex->entry.keys.len == 0 && entry_ex->entry.keys.val) {
529 free(entry_ex->entry.keys.val);
530 entry_ex->entry.keys.val = NULL;
536 * Construct an hdb_entry from a directory entry.
538 static krb5_error_code samba_kdc_message2entry(krb5_context context,
539 struct samba_kdc_db_context *kdc_db_ctx,
540 TALLOC_CTX *mem_ctx, krb5_const_principal principal,
541 enum samba_kdc_ent_type ent_type,
543 struct ldb_dn *realm_dn,
544 struct ldb_message *msg,
545 hdb_entry_ex *entry_ex)
547 struct loadparm_context *lp_ctx = kdc_db_ctx->lp_ctx;
548 uint32_t userAccountControl;
550 krb5_error_code ret = 0;
551 krb5_boolean is_computer = FALSE;
553 struct samba_kdc_entry *p;
558 bool is_rodc = false;
559 struct ldb_message_element *objectclasses;
560 struct ldb_val computer_val;
561 const char *samAccountName = ldb_msg_find_attr_as_string(msg, "samAccountName", NULL);
562 computer_val.data = discard_const_p(uint8_t,"computer");
563 computer_val.length = strlen((const char *)computer_val.data);
565 if (ldb_msg_find_element(msg, "msDS-SecondaryKrbTgtNumber")) {
569 if (!samAccountName) {
571 krb5_set_error_message(context, ret, "samba_kdc_message2entry: no samAccountName present");
575 objectclasses = ldb_msg_find_element(msg, "objectClass");
577 if (objectclasses && ldb_msg_find_val(objectclasses, &computer_val)) {
581 memset(entry_ex, 0, sizeof(*entry_ex));
583 p = talloc(mem_ctx, struct samba_kdc_entry);
589 p->kdc_db_ctx = kdc_db_ctx;
590 p->entry_ex = entry_ex;
591 p->realm_dn = talloc_reference(p, realm_dn);
597 talloc_set_destructor(p, samba_kdc_entry_destructor);
599 /* make sure we do not have bogus data in there */
600 memset(&entry_ex->entry, 0, sizeof(hdb_entry));
603 entry_ex->free_entry = samba_kdc_free_entry;
605 userAccountControl = ldb_msg_find_attr_as_uint(msg, "userAccountControl", 0);
608 entry_ex->entry.principal = malloc(sizeof(*(entry_ex->entry.principal)));
609 if (ent_type == SAMBA_KDC_ENT_TYPE_ANY && principal == NULL) {
610 krb5_make_principal(context, &entry_ex->entry.principal, lpcfg_realm(lp_ctx), samAccountName, NULL);
612 ret = copy_Principal(principal, entry_ex->entry.principal);
614 krb5_clear_error_message(context);
618 /* While we have copied the client principal, tests
619 * show that Win2k3 returns the 'corrected' realm, not
620 * the client-specified realm. This code attempts to
621 * replace the client principal's realm with the one
622 * we determine from our records */
624 /* this has to be with malloc() */
625 krb5_principal_set_realm(context, entry_ex->entry.principal, lpcfg_realm(lp_ctx));
628 /* First try and figure out the flags based on the userAccountControl */
629 entry_ex->entry.flags = uf2HDBFlags(context, userAccountControl, ent_type);
631 /* Windows 2008 seems to enforce this (very sensible) rule by
632 * default - don't allow offline attacks on a user's password
633 * by asking for a ticket to them as a service (encrypted with
634 * their probably patheticly insecure password) */
636 if (entry_ex->entry.flags.server
637 && lpcfg_parm_bool(lp_ctx, NULL, "kdc", "require spn for service", true)) {
638 if (!is_computer && !ldb_msg_find_attr_as_string(msg, "servicePrincipalName", NULL)) {
639 entry_ex->entry.flags.server = 0;
643 if (flags & HDB_F_ADMIN_DATA) {
644 /* These (created_by, modified_by) parts of the entry are not relevant for Samba4's use
645 * of the Heimdal KDC. They are stored in a the traditional
646 * DB for audit purposes, and still form part of the structure
649 /* use 'whenCreated' */
650 entry_ex->entry.created_by.time = ldb_msg_find_krb5time_ldap_time(msg, "whenCreated", 0);
651 /* use 'kadmin' for now (needed by mit_samba) */
652 krb5_make_principal(context,
653 &entry_ex->entry.created_by.principal,
654 lpcfg_realm(lp_ctx), "kadmin", NULL);
656 entry_ex->entry.modified_by = (Event *) malloc(sizeof(Event));
657 if (entry_ex->entry.modified_by == NULL) {
659 krb5_set_error_message(context, ret, "malloc: out of memory");
663 /* use 'whenChanged' */
664 entry_ex->entry.modified_by->time = ldb_msg_find_krb5time_ldap_time(msg, "whenChanged", 0);
665 /* use 'kadmin' for now (needed by mit_samba) */
666 krb5_make_principal(context,
667 &entry_ex->entry.modified_by->principal,
668 lpcfg_realm(lp_ctx), "kadmin", NULL);
672 /* The lack of password controls etc applies to krbtgt by
673 * virtue of being that particular RID */
674 status = dom_sid_split_rid(NULL, samdb_result_dom_sid(mem_ctx, msg, "objectSid"), NULL, &rid);
676 if (!NT_STATUS_IS_OK(status)) {
681 if (rid == DOMAIN_RID_KRBTGT) {
682 entry_ex->entry.valid_end = NULL;
683 entry_ex->entry.pw_end = NULL;
685 entry_ex->entry.flags.invalid = 0;
686 entry_ex->entry.flags.server = 1;
688 /* Don't mark all requests for the krbtgt/realm as
689 * 'change password', as otherwise we could get into
690 * trouble, and not enforce the password expirty.
691 * Instead, only do it when request is for the kpasswd service */
692 if (ent_type == SAMBA_KDC_ENT_TYPE_SERVER
693 && principal->name.name_string.len == 2
694 && (strcmp(principal->name.name_string.val[0], "kadmin") == 0)
695 && (strcmp(principal->name.name_string.val[1], "changepw") == 0)
696 && lpcfg_is_my_domain_or_realm(lp_ctx, principal->realm)) {
697 entry_ex->entry.flags.change_pw = 1;
699 entry_ex->entry.flags.client = 0;
700 entry_ex->entry.flags.forwardable = 1;
701 entry_ex->entry.flags.ok_as_delegate = 1;
702 } else if (is_rodc) {
703 /* The RODC krbtgt account is like the main krbtgt,
704 * but it does not have a changepw or kadmin
707 entry_ex->entry.valid_end = NULL;
708 entry_ex->entry.pw_end = NULL;
710 /* Also don't allow the RODC krbtgt to be a client (it should not be needed) */
711 entry_ex->entry.flags.client = 0;
712 entry_ex->entry.flags.invalid = 0;
713 entry_ex->entry.flags.server = 1;
715 entry_ex->entry.flags.client = 0;
716 entry_ex->entry.flags.forwardable = 1;
717 entry_ex->entry.flags.ok_as_delegate = 0;
718 } else if (entry_ex->entry.flags.server && ent_type == SAMBA_KDC_ENT_TYPE_SERVER) {
719 /* The account/password expiry only applies when the account is used as a
720 * client (ie password login), not when used as a server */
722 /* Make very well sure we don't use this for a client,
723 * it could bypass the password restrictions */
724 entry_ex->entry.flags.client = 0;
726 entry_ex->entry.valid_end = NULL;
727 entry_ex->entry.pw_end = NULL;
730 NTTIME must_change_time
731 = samdb_result_force_password_change(kdc_db_ctx->samdb, mem_ctx,
733 if (must_change_time == 0x7FFFFFFFFFFFFFFFULL) {
734 entry_ex->entry.pw_end = NULL;
736 entry_ex->entry.pw_end = malloc(sizeof(*entry_ex->entry.pw_end));
737 if (entry_ex->entry.pw_end == NULL) {
741 *entry_ex->entry.pw_end = nt_time_to_unix(must_change_time);
744 acct_expiry = samdb_result_account_expires(msg);
745 if (acct_expiry == 0x7FFFFFFFFFFFFFFFULL) {
746 entry_ex->entry.valid_end = NULL;
748 entry_ex->entry.valid_end = malloc(sizeof(*entry_ex->entry.valid_end));
749 if (entry_ex->entry.valid_end == NULL) {
753 *entry_ex->entry.valid_end = nt_time_to_unix(acct_expiry);
757 entry_ex->entry.valid_start = NULL;
759 entry_ex->entry.max_life = malloc(sizeof(*entry_ex->entry.max_life));
760 if (entry_ex->entry.max_life == NULL) {
765 if (ent_type == SAMBA_KDC_ENT_TYPE_SERVER) {
766 *entry_ex->entry.max_life = nt_time_to_unix(kdc_db_ctx->policy.service_tkt_lifetime);
767 } else if (ent_type == SAMBA_KDC_ENT_TYPE_KRBTGT || ent_type == SAMBA_KDC_ENT_TYPE_CLIENT) {
768 *entry_ex->entry.max_life = nt_time_to_unix(kdc_db_ctx->policy.user_tkt_lifetime);
770 *entry_ex->entry.max_life = MIN(nt_time_to_unix(kdc_db_ctx->policy.service_tkt_lifetime),
771 nt_time_to_unix(kdc_db_ctx->policy.user_tkt_lifetime));
774 entry_ex->entry.max_renew = malloc(sizeof(*entry_ex->entry.max_life));
775 if (entry_ex->entry.max_renew == NULL) {
780 *entry_ex->entry.max_renew = nt_time_to_unix(kdc_db_ctx->policy.user_tkt_renewaltime);
782 entry_ex->entry.generation = NULL;
784 /* Get keys from the db */
785 ret = samba_kdc_message2entry_keys(context, kdc_db_ctx, p, msg,
786 rid, is_rodc, userAccountControl,
789 /* Could be bougus data in the entry, or out of memory */
793 entry_ex->entry.etypes = malloc(sizeof(*(entry_ex->entry.etypes)));
794 if (entry_ex->entry.etypes == NULL) {
795 krb5_clear_error_message(context);
799 entry_ex->entry.etypes->len = entry_ex->entry.keys.len;
800 entry_ex->entry.etypes->val = calloc(entry_ex->entry.etypes->len, sizeof(int));
801 if (entry_ex->entry.etypes->val == NULL) {
802 krb5_clear_error_message(context);
806 for (i=0; i < entry_ex->entry.etypes->len; i++) {
807 entry_ex->entry.etypes->val[i] = entry_ex->entry.keys.val[i].key.keytype;
811 p->msg = talloc_steal(p, msg);
815 /* This doesn't free ent itself, that is for the eventual caller to do */
816 hdb_free_entry(context, entry_ex);
818 talloc_steal(kdc_db_ctx, entry_ex->ctx);
825 * Construct an hdb_entry from a directory entry.
827 static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
828 struct samba_kdc_db_context *kdc_db_ctx,
829 TALLOC_CTX *mem_ctx, krb5_const_principal principal,
830 enum trust_direction direction,
831 struct ldb_dn *realm_dn,
832 struct ldb_message *msg,
833 hdb_entry_ex *entry_ex)
835 struct loadparm_context *lp_ctx = kdc_db_ctx->lp_ctx;
836 const char *dnsdomain;
837 const char *realm = lpcfg_realm(lp_ctx);
838 DATA_BLOB password_utf16;
839 struct samr_Password password_hash;
840 const struct ldb_val *password_val;
841 struct trustAuthInOutBlob password_blob;
842 struct samba_kdc_entry *p;
844 enum ndr_err_code ndr_err;
845 int ret, trust_direction_flags;
848 p = talloc(mem_ctx, struct samba_kdc_entry);
854 p->kdc_db_ctx = kdc_db_ctx;
855 p->entry_ex = entry_ex;
856 p->realm_dn = realm_dn;
858 talloc_set_destructor(p, samba_kdc_entry_destructor);
860 /* make sure we do not have bogus data in there */
861 memset(&entry_ex->entry, 0, sizeof(hdb_entry));
864 entry_ex->free_entry = samba_kdc_free_entry;
866 /* use 'whenCreated' */
867 entry_ex->entry.created_by.time = ldb_msg_find_krb5time_ldap_time(msg, "whenCreated", 0);
868 /* use 'kadmin' for now (needed by mit_samba) */
869 krb5_make_principal(context,
870 &entry_ex->entry.created_by.principal,
871 realm, "kadmin", NULL);
873 entry_ex->entry.valid_start = NULL;
875 trust_direction_flags = ldb_msg_find_attr_as_int(msg, "trustDirection", 0);
877 if (direction == INBOUND) {
878 password_val = ldb_msg_find_ldb_val(msg, "trustAuthIncoming");
880 } else { /* OUTBOUND */
881 dnsdomain = ldb_msg_find_attr_as_string(msg, "trustPartner", NULL);
883 realm = strupper_talloc(mem_ctx, dnsdomain);
884 password_val = ldb_msg_find_ldb_val(msg, "trustAuthOutgoing");
887 if (!password_val || !(trust_direction_flags & direction)) {
892 ndr_err = ndr_pull_struct_blob(password_val, mem_ctx, &password_blob,
893 (ndr_pull_flags_fn_t)ndr_pull_trustAuthInOutBlob);
894 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
899 entry_ex->entry.kvno = 0;
901 we usually don't have a TRUST_AUTH_TYPE_VERSION field, as
902 windows doesn't create one, so we rely on the fact that both
903 windows and Samba don't actually check the kvno and instead
904 just check against the latest password blob. If we do have a
905 TRUST_AUTH_TYPE_VERSION field then we do use it, otherwise
908 for (i=0; i < password_blob.count; i++) {
909 if (password_blob.current.array[i].AuthType == TRUST_AUTH_TYPE_VERSION) {
910 entry_ex->entry.kvno = password_blob.current.array[i].AuthInfo.version.version;
914 for (i=0; i < password_blob.count; i++) {
915 if (password_blob.current.array[i].AuthType == TRUST_AUTH_TYPE_CLEAR) {
916 password_utf16 = data_blob_const(password_blob.current.array[i].AuthInfo.clear.password,
917 password_blob.current.array[i].AuthInfo.clear.size);
918 /* In the future, generate all sorts of
919 * hashes, but for now we can't safely convert
920 * the random strings windows uses into
923 /* but as it is utf16 already, we can get the NT password/arcfour-hmac-md5 key */
924 mdfour(password_hash.hash, password_utf16.data, password_utf16.length);
926 } else if (password_blob.current.array[i].AuthType == TRUST_AUTH_TYPE_NT4OWF) {
927 password_hash = password_blob.current.array[i].AuthInfo.nt4owf.password;
932 if (i < password_blob.count) {
934 /* Must have found a cleartext or MD4 password */
935 entry_ex->entry.keys.val = calloc(1, sizeof(Key));
938 key.salt = NULL; /* No salt for this enc type */
940 if (entry_ex->entry.keys.val == NULL) {
945 ret = krb5_keyblock_init(context,
946 ENCTYPE_ARCFOUR_HMAC,
947 password_hash.hash, sizeof(password_hash.hash),
950 entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key;
951 entry_ex->entry.keys.len++;
954 entry_ex->entry.principal = malloc(sizeof(*(entry_ex->entry.principal)));
956 ret = copy_Principal(principal, entry_ex->entry.principal);
958 krb5_clear_error_message(context);
962 /* While we have copied the client principal, tests
963 * show that Win2k3 returns the 'corrected' realm, not
964 * the client-specified realm. This code attempts to
965 * replace the client principal's realm with the one
966 * we determine from our records */
968 krb5_principal_set_realm(context, entry_ex->entry.principal, realm);
969 entry_ex->entry.flags = int2HDBFlags(0);
970 entry_ex->entry.flags.immutable = 1;
971 entry_ex->entry.flags.invalid = 0;
972 entry_ex->entry.flags.server = 1;
973 entry_ex->entry.flags.require_preauth = 1;
975 entry_ex->entry.pw_end = NULL;
977 entry_ex->entry.max_life = NULL;
979 entry_ex->entry.max_renew = NULL;
981 entry_ex->entry.generation = NULL;
983 entry_ex->entry.etypes = malloc(sizeof(*(entry_ex->entry.etypes)));
984 if (entry_ex->entry.etypes == NULL) {
985 krb5_clear_error_message(context);
989 entry_ex->entry.etypes->len = entry_ex->entry.keys.len;
990 entry_ex->entry.etypes->val = calloc(entry_ex->entry.etypes->len, sizeof(int));
991 if (entry_ex->entry.etypes->val == NULL) {
992 krb5_clear_error_message(context);
996 for (i=0; i < entry_ex->entry.etypes->len; i++) {
997 entry_ex->entry.etypes->val[i] = entry_ex->entry.keys.val[i].key.keytype;
1001 p->msg = talloc_steal(p, msg);
1005 /* This doesn't free ent itself, that is for the eventual caller to do */
1006 hdb_free_entry(context, entry_ex);
1008 talloc_steal(kdc_db_ctx, entry_ex->ctx);
1015 static krb5_error_code samba_kdc_lookup_trust(krb5_context context, struct ldb_context *ldb_ctx,
1016 TALLOC_CTX *mem_ctx,
1018 struct ldb_dn *realm_dn,
1019 struct ldb_message **pmsg)
1022 krb5_error_code ret;
1023 char *filter = NULL;
1024 const char * const *attrs = trust_attrs;
1026 struct ldb_result *res = NULL;
1027 char *realm_encoded = ldb_binary_encode_string(mem_ctx, realm);
1028 if (!realm_encoded) {
1031 krb5_set_error_message(context, ret, "talloc_asprintf: out of memory");
1035 filter = talloc_asprintf(mem_ctx, "(&(objectClass=trustedDomain)(|(flatname=%s)(trustPartner=%s)))",
1036 realm_encoded, realm_encoded);
1039 talloc_free(realm_encoded);
1041 krb5_set_error_message(context, ret, "talloc_asprintf: out of memory");
1045 lret = ldb_search(ldb_ctx, mem_ctx, &res,
1046 ldb_get_default_basedn(ldb_ctx),
1047 LDB_SCOPE_SUBTREE, attrs, "%s", filter);
1048 if (lret != LDB_SUCCESS) {
1049 DEBUG(3, ("Failed to search for %s: %s\n", filter, ldb_errstring(ldb_ctx)));
1050 return HDB_ERR_NOENTRY;
1051 } else if (res->count == 0 || res->count > 1) {
1052 DEBUG(3, ("Failed find a single entry for %s: got %d\n", filter, res->count));
1054 return HDB_ERR_NOENTRY;
1056 talloc_steal(mem_ctx, res->msgs);
1057 *pmsg = res->msgs[0];
1062 static krb5_error_code samba_kdc_lookup_client(krb5_context context,
1063 struct samba_kdc_db_context *kdc_db_ctx,
1064 TALLOC_CTX *mem_ctx,
1065 krb5_const_principal principal,
1067 struct ldb_dn **realm_dn,
1068 struct ldb_message **msg) {
1070 char *principal_string;
1071 krb5_error_code ret;
1073 ret = krb5_unparse_name(context, principal, &principal_string);
1079 nt_status = sam_get_results_principal(kdc_db_ctx->samdb,
1080 mem_ctx, principal_string, attrs,
1082 free(principal_string);
1083 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER)) {
1084 return HDB_ERR_NOENTRY;
1085 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_MEMORY)) {
1087 } else if (!NT_STATUS_IS_OK(nt_status)) {
1094 static krb5_error_code samba_kdc_fetch_client(krb5_context context,
1095 struct samba_kdc_db_context *kdc_db_ctx,
1096 TALLOC_CTX *mem_ctx,
1097 krb5_const_principal principal,
1099 hdb_entry_ex *entry_ex) {
1100 struct ldb_dn *realm_dn;
1101 krb5_error_code ret;
1102 struct ldb_message *msg = NULL;
1104 ret = samba_kdc_lookup_client(context, kdc_db_ctx,
1105 mem_ctx, principal, user_attrs,
1111 ret = samba_kdc_message2entry(context, kdc_db_ctx, mem_ctx,
1112 principal, SAMBA_KDC_ENT_TYPE_CLIENT,
1114 realm_dn, msg, entry_ex);
1118 static krb5_error_code samba_kdc_fetch_krbtgt(krb5_context context,
1119 struct samba_kdc_db_context *kdc_db_ctx,
1120 TALLOC_CTX *mem_ctx,
1121 krb5_const_principal principal,
1123 uint32_t krbtgt_number,
1124 hdb_entry_ex *entry_ex)
1126 struct loadparm_context *lp_ctx = kdc_db_ctx->lp_ctx;
1127 krb5_error_code ret;
1128 struct ldb_message *msg = NULL;
1129 struct ldb_dn *realm_dn = ldb_get_default_basedn(kdc_db_ctx->samdb);
1131 krb5_principal alloc_principal = NULL;
1132 if (principal->name.name_string.len != 2
1133 || (strcmp(principal->name.name_string.val[0], KRB5_TGS_NAME) != 0)) {
1135 return HDB_ERR_NOENTRY;
1138 /* krbtgt case. Either us or a trusted realm */
1140 if (lpcfg_is_my_domain_or_realm(lp_ctx, principal->realm)
1141 && lpcfg_is_my_domain_or_realm(lp_ctx, principal->name.name_string.val[1])) {
1142 /* us, or someone quite like us */
1143 /* Cludge, cludge cludge. If the realm part of krbtgt/realm,
1144 * is in our db, then direct the caller at our primary
1149 if (krbtgt_number == kdc_db_ctx->my_krbtgt_number) {
1150 lret = dsdb_search_one(kdc_db_ctx->samdb, mem_ctx,
1151 &msg, kdc_db_ctx->krbtgt_dn, LDB_SCOPE_BASE,
1153 "(objectClass=user)");
1155 /* We need to look up an RODC krbtgt (perhaps
1156 * ours, if we are an RODC, perhaps another
1157 * RODC if we are a read-write DC */
1158 lret = dsdb_search_one(kdc_db_ctx->samdb, mem_ctx,
1159 &msg, realm_dn, LDB_SCOPE_SUBTREE,
1161 DSDB_SEARCH_SHOW_EXTENDED_DN,
1162 "(&(objectClass=user)(msDS-SecondaryKrbTgtNumber=%u))", (unsigned)(krbtgt_number));
1165 if (lret == LDB_ERR_NO_SUCH_OBJECT) {
1166 krb5_warnx(context, "samba_kdc_fetch: could not find KRBTGT number %u in DB!",
1167 (unsigned)(krbtgt_number));
1168 krb5_set_error_message(context, HDB_ERR_NOENTRY,
1169 "samba_kdc_fetch: could not find KRBTGT number %u in DB!",
1170 (unsigned)(krbtgt_number));
1171 return HDB_ERR_NOENTRY;
1172 } else if (lret != LDB_SUCCESS) {
1173 krb5_warnx(context, "samba_kdc_fetch: could not find KRBTGT number %u in DB!",
1174 (unsigned)(krbtgt_number));
1175 krb5_set_error_message(context, HDB_ERR_NOENTRY,
1176 "samba_kdc_fetch: could not find KRBTGT number %u in DB!",
1177 (unsigned)(krbtgt_number));
1178 return HDB_ERR_NOENTRY;
1182 * Windows seems to canonicalize the principal
1183 * in a TGS REP even if the client did not specify
1184 * the canonicalize flag.
1186 if (flags & (HDB_F_CANON|HDB_F_FOR_TGS_REQ)) {
1187 ret = krb5_copy_principal(context, principal, &alloc_principal);
1192 /* When requested to do so, ensure that the
1193 * both realm values in the principal are set
1194 * to the upper case, canonical realm */
1195 free(alloc_principal->name.name_string.val[1]);
1196 alloc_principal->name.name_string.val[1] = strdup(lpcfg_realm(lp_ctx));
1197 if (!alloc_principal->name.name_string.val[1]) {
1199 krb5_set_error_message(context, ret, "samba_kdc_fetch: strdup() failed!");
1202 principal = alloc_principal;
1205 ret = samba_kdc_message2entry(context, kdc_db_ctx, mem_ctx,
1206 principal, SAMBA_KDC_ENT_TYPE_KRBTGT,
1207 flags, realm_dn, msg, entry_ex);
1208 if (alloc_principal) {
1209 /* This is again copied in the message2entry call */
1210 krb5_free_principal(context, alloc_principal);
1213 krb5_warnx(context, "samba_kdc_fetch: self krbtgt message2entry failed");
1218 enum trust_direction direction = UNKNOWN;
1219 const char *realm = NULL;
1221 /* Either an inbound or outbound trust */
1223 if (strcasecmp(lpcfg_realm(lp_ctx), principal->realm) == 0) {
1224 /* look for inbound trust */
1225 direction = INBOUND;
1226 realm = principal->name.name_string.val[1];
1227 } else if (strcasecmp(lpcfg_realm(lp_ctx), principal->name.name_string.val[1]) == 0) {
1228 /* look for outbound trust */
1229 direction = OUTBOUND;
1230 realm = principal->realm;
1232 krb5_warnx(context, "samba_kdc_fetch: not our realm for trusts ('%s', '%s')",
1233 principal->realm, principal->name.name_string.val[1]);
1234 krb5_set_error_message(context, HDB_ERR_NOENTRY, "samba_kdc_fetch: not our realm for trusts ('%s', '%s')",
1235 principal->realm, principal->name.name_string.val[1]);
1236 return HDB_ERR_NOENTRY;
1239 /* Trusted domains are under CN=system */
1241 ret = samba_kdc_lookup_trust(context, kdc_db_ctx->samdb,
1243 realm, realm_dn, &msg);
1246 krb5_warnx(context, "samba_kdc_fetch: could not find principal in DB");
1247 krb5_set_error_message(context, ret, "samba_kdc_fetch: could not find principal in DB");
1251 ret = samba_kdc_trust_message2entry(context, kdc_db_ctx, mem_ctx,
1252 principal, direction,
1253 realm_dn, msg, entry_ex);
1255 krb5_warnx(context, "samba_kdc_fetch: trust_message2entry failed");
1262 static krb5_error_code samba_kdc_lookup_server(krb5_context context,
1263 struct samba_kdc_db_context *kdc_db_ctx,
1264 TALLOC_CTX *mem_ctx,
1265 krb5_const_principal principal,
1267 struct ldb_dn **realm_dn,
1268 struct ldb_message **msg)
1270 krb5_error_code ret;
1271 if (principal->name.name_string.len >= 2) {
1272 /* 'normal server' case */
1275 struct ldb_dn *user_dn;
1276 char *principal_string;
1278 ret = krb5_unparse_name_flags(context, principal,
1279 KRB5_PRINCIPAL_UNPARSE_NO_REALM,
1285 /* At this point we may find the host is known to be
1286 * in a different realm, so we should generate a
1287 * referral instead */
1288 nt_status = crack_service_principal_name(kdc_db_ctx->samdb,
1289 mem_ctx, principal_string,
1290 &user_dn, realm_dn);
1291 free(principal_string);
1293 if (!NT_STATUS_IS_OK(nt_status)) {
1294 return HDB_ERR_NOENTRY;
1297 ldb_ret = dsdb_search_one(kdc_db_ctx->samdb,
1299 msg, user_dn, LDB_SCOPE_BASE,
1301 DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
1303 if (ldb_ret != LDB_SUCCESS) {
1304 return HDB_ERR_NOENTRY;
1309 char *filter = NULL;
1312 /* server as client principal case, but we must not lookup userPrincipalNames */
1313 *realm_dn = ldb_get_default_basedn(kdc_db_ctx->samdb);
1314 realm = krb5_principal_get_realm(context, principal);
1316 /* TODO: Check if it is our realm, otherwise give referral */
1318 ret = krb5_unparse_name_flags(context, principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM, &short_princ);
1321 krb5_set_error_message(context, ret, "samba_kdc_lookup_principal: could not parse principal");
1322 krb5_warnx(context, "samba_kdc_lookup_principal: could not parse principal");
1326 lret = dsdb_search_one(kdc_db_ctx->samdb, mem_ctx, msg,
1327 *realm_dn, LDB_SCOPE_SUBTREE,
1329 DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
1330 "(&(objectClass=user)(samAccountName=%s))",
1331 ldb_binary_encode_string(mem_ctx, short_princ));
1333 if (lret == LDB_ERR_NO_SUCH_OBJECT) {
1334 DEBUG(3, ("Failed find a entry for %s\n", filter));
1335 return HDB_ERR_NOENTRY;
1337 if (lret != LDB_SUCCESS) {
1338 DEBUG(3, ("Failed single search for for %s - %s\n",
1339 filter, ldb_errstring(kdc_db_ctx->samdb)));
1340 return HDB_ERR_NOENTRY;
1347 static krb5_error_code samba_kdc_fetch_server(krb5_context context,
1348 struct samba_kdc_db_context *kdc_db_ctx,
1349 TALLOC_CTX *mem_ctx,
1350 krb5_const_principal principal,
1352 hdb_entry_ex *entry_ex)
1354 krb5_error_code ret;
1355 struct ldb_dn *realm_dn;
1356 struct ldb_message *msg;
1358 ret = samba_kdc_lookup_server(context, kdc_db_ctx, mem_ctx, principal,
1359 server_attrs, &realm_dn, &msg);
1364 ret = samba_kdc_message2entry(context, kdc_db_ctx, mem_ctx,
1365 principal, SAMBA_KDC_ENT_TYPE_SERVER,
1367 realm_dn, msg, entry_ex);
1369 krb5_warnx(context, "samba_kdc_fetch: message2entry failed");
1375 krb5_error_code samba_kdc_fetch(krb5_context context,
1376 struct samba_kdc_db_context *kdc_db_ctx,
1377 krb5_const_principal principal,
1380 hdb_entry_ex *entry_ex)
1382 krb5_error_code ret = HDB_ERR_NOENTRY;
1383 TALLOC_CTX *mem_ctx;
1384 unsigned int krbtgt_number;
1385 if (flags & HDB_F_KVNO_SPECIFIED) {
1386 krbtgt_number = SAMBA_KVNO_GET_KRBTGT(kvno);
1387 if (kdc_db_ctx->rodc) {
1388 if (krbtgt_number != kdc_db_ctx->my_krbtgt_number) {
1389 return HDB_ERR_NOT_FOUND_HERE;
1393 krbtgt_number = kdc_db_ctx->my_krbtgt_number;
1396 mem_ctx = talloc_named(kdc_db_ctx, 0, "samba_kdc_fetch context");
1399 krb5_set_error_message(context, ret, "samba_kdc_fetch: talloc_named() failed!");
1403 if (flags & HDB_F_GET_CLIENT) {
1404 ret = samba_kdc_fetch_client(context, kdc_db_ctx, mem_ctx, principal, flags, entry_ex);
1405 if (ret != HDB_ERR_NOENTRY) goto done;
1407 if (flags & HDB_F_GET_SERVER) {
1408 /* krbtgt fits into this situation for trusted realms, and for resolving different versions of our own realm name */
1409 ret = samba_kdc_fetch_krbtgt(context, kdc_db_ctx, mem_ctx, principal, flags, krbtgt_number, entry_ex);
1410 if (ret != HDB_ERR_NOENTRY) goto done;
1412 /* We return 'no entry' if it does not start with krbtgt/, so move to the common case quickly */
1413 ret = samba_kdc_fetch_server(context, kdc_db_ctx, mem_ctx, principal, flags, entry_ex);
1414 if (ret != HDB_ERR_NOENTRY) goto done;
1416 if (flags & HDB_F_GET_KRBTGT) {
1417 ret = samba_kdc_fetch_krbtgt(context, kdc_db_ctx, mem_ctx, principal, flags, krbtgt_number, entry_ex);
1418 if (ret != HDB_ERR_NOENTRY) goto done;
1422 talloc_free(mem_ctx);
1426 struct samba_kdc_seq {
1429 struct ldb_message **msgs;
1430 struct ldb_dn *realm_dn;
1433 static krb5_error_code samba_kdc_seq(krb5_context context,
1434 struct samba_kdc_db_context *kdc_db_ctx,
1435 hdb_entry_ex *entry)
1437 krb5_error_code ret;
1438 struct samba_kdc_seq *priv = kdc_db_ctx->seq_ctx;
1439 TALLOC_CTX *mem_ctx;
1440 hdb_entry_ex entry_ex;
1441 memset(&entry_ex, '\0', sizeof(entry_ex));
1444 return HDB_ERR_NOENTRY;
1447 mem_ctx = talloc_named(priv, 0, "samba_kdc_seq context");
1451 krb5_set_error_message(context, ret, "samba_kdc_seq: talloc_named() failed!");
1455 if (priv->index < priv->count) {
1456 ret = samba_kdc_message2entry(context, kdc_db_ctx, mem_ctx,
1457 NULL, SAMBA_KDC_ENT_TYPE_ANY,
1458 HDB_F_ADMIN_DATA|HDB_F_GET_ANY,
1459 priv->realm_dn, priv->msgs[priv->index++], entry);
1461 ret = HDB_ERR_NOENTRY;
1466 kdc_db_ctx->seq_ctx = NULL;
1468 talloc_free(mem_ctx);
1474 krb5_error_code samba_kdc_firstkey(krb5_context context,
1475 struct samba_kdc_db_context *kdc_db_ctx,
1476 hdb_entry_ex *entry)
1478 struct ldb_context *ldb_ctx = kdc_db_ctx->samdb;
1479 struct samba_kdc_seq *priv = kdc_db_ctx->seq_ctx;
1481 struct ldb_result *res = NULL;
1482 krb5_error_code ret;
1483 TALLOC_CTX *mem_ctx;
1488 kdc_db_ctx->seq_ctx = NULL;
1491 priv = (struct samba_kdc_seq *) talloc(kdc_db_ctx, struct samba_kdc_seq);
1494 krb5_set_error_message(context, ret, "talloc: out of memory");
1500 priv->realm_dn = ldb_get_default_basedn(ldb_ctx);
1503 mem_ctx = talloc_named(priv, 0, "samba_kdc_firstkey context");
1507 krb5_set_error_message(context, ret, "samba_kdc_firstkey: talloc_named() failed!");
1511 ret = krb5_get_default_realm(context, &realm);
1517 lret = ldb_search(ldb_ctx, priv, &res,
1518 priv->realm_dn, LDB_SCOPE_SUBTREE, user_attrs,
1519 "(objectClass=user)");
1521 if (lret != LDB_SUCCESS) {
1523 return HDB_ERR_NOENTRY;
1526 priv->count = res->count;
1527 priv->msgs = talloc_steal(priv, res->msgs);
1530 kdc_db_ctx->seq_ctx = priv;
1532 ret = samba_kdc_seq(context, kdc_db_ctx, entry);
1536 kdc_db_ctx->seq_ctx = NULL;
1538 talloc_free(mem_ctx);
1543 krb5_error_code samba_kdc_nextkey(krb5_context context,
1544 struct samba_kdc_db_context *kdc_db_ctx,
1545 hdb_entry_ex *entry)
1547 return samba_kdc_seq(context, kdc_db_ctx, entry);
1550 /* Check if a given entry may delegate or do s4u2self to this target principal
1552 * This is currently a very nasty hack - allowing only delegation to itself.
1555 samba_kdc_check_s4u2self(krb5_context context,
1556 struct samba_kdc_db_context *kdc_db_ctx,
1557 hdb_entry_ex *entry,
1558 krb5_const_principal target_principal)
1560 krb5_error_code ret;
1561 krb5_principal enterprise_prinicpal = NULL;
1562 struct ldb_dn *realm_dn;
1563 struct ldb_message *msg;
1564 struct dom_sid *orig_sid;
1565 struct dom_sid *target_sid;
1566 struct samba_kdc_entry *p = talloc_get_type(entry->ctx, struct samba_kdc_entry);
1567 const char *delegation_check_attrs[] = {
1571 TALLOC_CTX *mem_ctx = talloc_named(kdc_db_ctx, 0, "samba_kdc_check_s4u2self");
1575 krb5_set_error_message(context, ret, "samba_kdc_check_s4u2self: talloc_named() failed!");
1579 if (target_principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) {
1580 /* Need to reparse the enterprise principal to find the real target */
1581 if (target_principal->name.name_string.len != 1) {
1582 ret = KRB5_PARSE_MALFORMED;
1583 krb5_set_error_message(context, ret, "samba_kdc_check_s4u2self: request for delegation to enterprise principal with wrong (%d) number of components",
1584 target_principal->name.name_string.len);
1585 talloc_free(mem_ctx);
1588 ret = krb5_parse_name(context, target_principal->name.name_string.val[0],
1589 &enterprise_prinicpal);
1591 talloc_free(mem_ctx);
1594 target_principal = enterprise_prinicpal;
1597 ret = samba_kdc_lookup_server(context, kdc_db_ctx, mem_ctx, target_principal,
1598 delegation_check_attrs, &realm_dn, &msg);
1600 krb5_free_principal(context, enterprise_prinicpal);
1603 talloc_free(mem_ctx);
1607 orig_sid = samdb_result_dom_sid(mem_ctx, p->msg, "objectSid");
1608 target_sid = samdb_result_dom_sid(mem_ctx, msg, "objectSid");
1610 /* Allow delegation to the same principal, even if by a different
1611 * name. The easy and safe way to prove this is by SID
1613 if (!(orig_sid && target_sid && dom_sid_equal(orig_sid, target_sid))) {
1614 talloc_free(mem_ctx);
1615 return KRB5KDC_ERR_BADOPTION;
1618 talloc_free(mem_ctx);
1622 /* Certificates printed by a the Certificate Authority might have a
1623 * slightly different form of the user principal name to that in the
1624 * database. Allow a mismatch where they both refer to the same
1628 samba_kdc_check_pkinit_ms_upn_match(krb5_context context,
1629 struct samba_kdc_db_context *kdc_db_ctx,
1630 hdb_entry_ex *entry,
1631 krb5_const_principal certificate_principal)
1633 krb5_error_code ret;
1634 struct ldb_dn *realm_dn;
1635 struct ldb_message *msg;
1636 struct dom_sid *orig_sid;
1637 struct dom_sid *target_sid;
1638 struct samba_kdc_entry *p = talloc_get_type(entry->ctx, struct samba_kdc_entry);
1639 const char *ms_upn_check_attrs[] = {
1643 TALLOC_CTX *mem_ctx = talloc_named(kdc_db_ctx, 0, "samba_kdc_check_pkinit_ms_upn_match");
1647 krb5_set_error_message(context, ret, "samba_kdc_fetch: talloc_named() failed!");
1651 ret = samba_kdc_lookup_client(context, kdc_db_ctx,
1652 mem_ctx, certificate_principal,
1653 ms_upn_check_attrs, &realm_dn, &msg);
1656 talloc_free(mem_ctx);
1660 orig_sid = samdb_result_dom_sid(mem_ctx, p->msg, "objectSid");
1661 target_sid = samdb_result_dom_sid(mem_ctx, msg, "objectSid");
1663 /* Consider these to be the same principal, even if by a different
1664 * name. The easy and safe way to prove this is by SID
1666 if (!(orig_sid && target_sid && dom_sid_equal(orig_sid, target_sid))) {
1667 talloc_free(mem_ctx);
1668 return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1671 talloc_free(mem_ctx);
1676 * Check if a given entry may delegate to this target principal
1680 samba_kdc_check_s4u2proxy(krb5_context context,
1681 struct samba_kdc_db_context *kdc_db_ctx,
1682 hdb_entry_ex *entry,
1683 krb5_const_principal target_principal)
1685 krb5_error_code ret;
1687 const char *client_dn = NULL;
1688 const char *target_principal_name = NULL;
1689 struct ldb_message_element *el;
1693 struct samba_kdc_entry *p = talloc_get_type(entry->ctx, struct samba_kdc_entry);
1695 TALLOC_CTX *mem_ctx = talloc_named(kdc_db_ctx, 0, "samba_kdc_check_s4u2proxy");
1699 krb5_set_error_message(context, ret,
1700 "samba_kdc_check_s4u2proxy:"
1701 " talloc_named() failed!");
1705 client_dn = ldb_dn_get_linearized(p->msg->dn);
1711 krb5_set_error_message(context, ret,
1712 "samba_kdc_check_s4u2proxy:"
1713 " ldb_dn_get_linearized() failed!");
1718 * The main heimdal code already checked that the target_principal
1719 * belongs to the same realm as the client.
1721 * So we just need the principal without the realm,
1722 * as that is what is configured in the "msDS-AllowedToDelegateTo"
1725 ret = krb5_unparse_name_flags(context, target_principal,
1726 KRB5_PRINCIPAL_UNPARSE_NO_REALM, &tmp);
1728 talloc_free(mem_ctx);
1729 krb5_set_error_message(context, ret,
1730 "samba_kdc_check_s4u2proxy:"
1731 " krb5_unparse_name() failed!");
1734 DEBUG(10,("samba_kdc_check_s4u2proxy: client[%s] for target[%s]\n",
1737 target_principal_name = talloc_strdup(mem_ctx, tmp);
1739 if (target_principal_name == NULL) {
1741 krb5_set_error_message(context, ret,
1742 "samba_kdc_check_s4u2proxy:"
1743 " talloc_strdup() failed!");
1747 el = ldb_msg_find_element(p->msg, "msDS-AllowedToDelegateTo");
1752 val = data_blob_string_const(target_principal_name);
1754 for (i=0; i<el->num_values; i++) {
1755 struct ldb_val *val1 = &val;
1756 struct ldb_val *val2 = &el->values[i];
1759 if (val1->length != val2->length) {
1763 cmp = strncasecmp((const char *)val1->data,
1764 (const char *)val2->data,
1778 DEBUG(10,("samba_kdc_check_s4u2proxy: client[%s] allowed target[%s]\n",
1780 talloc_free(mem_ctx);
1784 krb5_set_error_message(context, ret,
1785 "samba_kdc_check_s4u2proxy: client[%s] "
1786 "not allowed for delegation to target[%s]",
1788 target_principal_name);
1789 talloc_free(mem_ctx);
1790 return KRB5KDC_ERR_BADOPTION;
1793 NTSTATUS samba_kdc_setup_db_ctx(TALLOC_CTX *mem_ctx, struct samba_kdc_base_context *base_ctx,
1794 struct samba_kdc_db_context **kdc_db_ctx_out)
1797 struct ldb_message *msg;
1798 struct auth_session_info *session_info;
1799 struct samba_kdc_db_context *kdc_db_ctx;
1800 /* The idea here is very simple. Using Kerberos to
1801 * authenticate the KDC to the LDAP server is higly likely to
1804 * In future we may set this up to use EXERNAL and SSL
1805 * certificates, for now it will almost certainly be NTLMSSP_SET_USERNAME
1808 kdc_db_ctx = talloc_zero(mem_ctx, struct samba_kdc_db_context);
1809 if (kdc_db_ctx == NULL) {
1810 return NT_STATUS_NO_MEMORY;
1812 kdc_db_ctx->ev_ctx = base_ctx->ev_ctx;
1813 kdc_db_ctx->lp_ctx = base_ctx->lp_ctx;
1815 kdc_get_policy(base_ctx->lp_ctx, NULL, &kdc_db_ctx->policy);
1817 session_info = system_session(kdc_db_ctx->lp_ctx);
1818 if (session_info == NULL) {
1819 return NT_STATUS_INTERNAL_ERROR;
1822 /* Setup the link to LDB */
1823 kdc_db_ctx->samdb = samdb_connect(kdc_db_ctx, base_ctx->ev_ctx,
1824 base_ctx->lp_ctx, session_info, 0);
1825 if (kdc_db_ctx->samdb == NULL) {
1826 DEBUG(1, ("hdb_samba4_create: Cannot open samdb for KDC backend!"));
1827 talloc_free(kdc_db_ctx);
1828 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
1831 /* Find out our own krbtgt kvno */
1832 ldb_ret = samdb_rodc(kdc_db_ctx->samdb, &kdc_db_ctx->rodc);
1833 if (ldb_ret != LDB_SUCCESS) {
1834 DEBUG(1, ("hdb_samba4_create: Cannot determine if we are an RODC in KDC backend: %s\n",
1835 ldb_errstring(kdc_db_ctx->samdb)));
1836 talloc_free(kdc_db_ctx);
1837 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
1839 if (kdc_db_ctx->rodc) {
1840 int my_krbtgt_number;
1841 const char *secondary_keytab[] = { "msDS-SecondaryKrbTgtNumber", NULL };
1842 struct ldb_dn *account_dn;
1843 struct ldb_dn *server_dn = samdb_server_dn(kdc_db_ctx->samdb, kdc_db_ctx);
1845 DEBUG(1, ("hdb_samba4_create: Cannot determine server DN in KDC backend: %s\n",
1846 ldb_errstring(kdc_db_ctx->samdb)));
1847 talloc_free(kdc_db_ctx);
1848 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
1851 ldb_ret = samdb_reference_dn(kdc_db_ctx->samdb, kdc_db_ctx, server_dn,
1852 "serverReference", &account_dn);
1853 if (ldb_ret != LDB_SUCCESS) {
1854 DEBUG(1, ("hdb_samba4_create: Cannot determine server account in KDC backend: %s\n",
1855 ldb_errstring(kdc_db_ctx->samdb)));
1856 talloc_free(kdc_db_ctx);
1857 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
1860 ldb_ret = samdb_reference_dn(kdc_db_ctx->samdb, kdc_db_ctx, account_dn,
1861 "msDS-KrbTgtLink", &kdc_db_ctx->krbtgt_dn);
1862 talloc_free(account_dn);
1863 if (ldb_ret != LDB_SUCCESS) {
1864 DEBUG(1, ("hdb_samba4_create: Cannot determine RODC krbtgt account in KDC backend: %s\n",
1865 ldb_errstring(kdc_db_ctx->samdb)));
1866 talloc_free(kdc_db_ctx);
1867 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
1870 ldb_ret = dsdb_search_one(kdc_db_ctx->samdb, kdc_db_ctx,
1871 &msg, kdc_db_ctx->krbtgt_dn, LDB_SCOPE_BASE,
1874 "(&(objectClass=user)(msDS-SecondaryKrbTgtNumber=*))");
1875 if (ldb_ret != LDB_SUCCESS) {
1876 DEBUG(1, ("hdb_samba4_create: Cannot read krbtgt account %s in KDC backend to get msDS-SecondaryKrbTgtNumber: %s: %s\n",
1877 ldb_dn_get_linearized(kdc_db_ctx->krbtgt_dn),
1878 ldb_errstring(kdc_db_ctx->samdb),
1879 ldb_strerror(ldb_ret)));
1880 talloc_free(kdc_db_ctx);
1881 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
1883 my_krbtgt_number = ldb_msg_find_attr_as_int(msg, "msDS-SecondaryKrbTgtNumber", -1);
1884 if (my_krbtgt_number == -1) {
1885 DEBUG(1, ("hdb_samba4_create: Cannot read msDS-SecondaryKrbTgtNumber from krbtgt account %s in KDC backend: got %d\n",
1886 ldb_dn_get_linearized(kdc_db_ctx->krbtgt_dn),
1888 talloc_free(kdc_db_ctx);
1889 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
1891 kdc_db_ctx->my_krbtgt_number = my_krbtgt_number;
1894 kdc_db_ctx->my_krbtgt_number = 0;
1895 ldb_ret = dsdb_search_one(kdc_db_ctx->samdb, kdc_db_ctx,
1897 ldb_get_default_basedn(kdc_db_ctx->samdb),
1901 "(&(objectClass=user)(samAccountName=krbtgt))");
1903 if (ldb_ret != LDB_SUCCESS) {
1904 DEBUG(1, ("samba_kdc_fetch: could not find own KRBTGT in DB: %s\n", ldb_errstring(kdc_db_ctx->samdb)));
1905 talloc_free(kdc_db_ctx);
1906 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
1908 kdc_db_ctx->krbtgt_dn = talloc_steal(kdc_db_ctx, msg->dn);
1909 kdc_db_ctx->my_krbtgt_number = 0;
1912 *kdc_db_ctx_out = kdc_db_ctx;
1913 return NT_STATUS_OK;