]> git.samba.org - obnox/samba/samba-obnox.git/blobdiff - source4/kdc/db-glue.c
s4:kdc: add aes key support for trusted domains
[obnox/samba/samba-obnox.git] / source4 / kdc / db-glue.c
index eaa97e3a1d7c9589acd6329e25ec2db2e52b67b6..caeb1b2effeb3d87d78312af015ba125412369c7 100644 (file)
 */
 
 #include "includes.h"
-#include "system/time.h"
-#include "../libds/common/flags.h"
-#include "lib/ldb/include/ldb.h"
-#include "librpc/gen_ndr/netlogon.h"
 #include "libcli/security/security.h"
 #include "auth/auth.h"
-#include "auth/credentials/credentials.h"
 #include "auth/auth_sam.h"
 #include "dsdb/samdb/samdb.h"
 #include "dsdb/common/util.h"
-#include "librpc/ndr/libndr.h"
 #include "librpc/gen_ndr/ndr_drsblobs.h"
-#include "librpc/gen_ndr/lsa.h"
-#include "libcli/auth/libcli_auth.h"
 #include "param/param.h"
 #include "../lib/crypto/md4.h"
 #include "system/kerberos.h"
 #include "auth/kerberos/kerberos.h"
 #include <hdb.h>
 #include "kdc/samba_kdc.h"
+#include "kdc/kdc-glue.h"
 #include "kdc/db-glue.h"
 
+#define SAMBA_KVNO_GET_KRBTGT(kvno) \
+       ((uint16_t)(((uint32_t)kvno) >> 16))
+
+#define SAMBA_KVNO_AND_KRBTGT(kvno, krbtgt) \
+       ((krb5_kvno)((((uint32_t)kvno) & 0xFFFF) | \
+        ((((uint32_t)krbtgt) << 16) & 0xFFFF0000)))
+
 enum samba_kdc_ent_type
 { SAMBA_KDC_ENT_TYPE_CLIENT, SAMBA_KDC_ENT_TYPE_SERVER,
   SAMBA_KDC_ENT_TYPE_KRBTGT, SAMBA_KDC_ENT_TYPE_TRUST, SAMBA_KDC_ENT_TYPE_ANY };
@@ -66,6 +66,7 @@ static const char *trust_attrs[] = {
        NULL
 };
 
+
 static KerberosTime ldb_msg_find_krb5time_ldap_time(struct ldb_message *msg, const char *attr, KerberosTime default_val)
 {
     const char *tmp;
@@ -131,7 +132,7 @@ static HDBFlags uf2HDBFlags(krb5_context context, uint32_t userAccountControl, e
                flags.client = 0;
        }
        if (userAccountControl & UF_LOCKOUT) {
-               flags.invalid = 1;
+               flags.locked_out = 1;
        }
 /*
        if (userAccountControl & UF_PASSWORD_NOTREQD) {
@@ -158,6 +159,20 @@ static HDBFlags uf2HDBFlags(krb5_context context, uint32_t userAccountControl, e
        if (userAccountControl & UF_TRUSTED_FOR_DELEGATION) {
                flags.ok_as_delegate = 1;
        }
+       if (userAccountControl & UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION) {
+               /*
+                * this is confusing...
+                *
+                * UF_TRUSTED_FOR_DELEGATION
+                * => ok_as_delegate
+                *
+                * and
+                *
+                * UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION
+                * => trusted_for_delegation
+                */
+               flags.trusted_for_delegation = 1;
+       }
        if (!(userAccountControl & UF_NOT_DELEGATED)) {
                flags.forwardable = 1;
                flags.proxiable = 1;
@@ -214,35 +229,35 @@ static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
        uint16_t i;
        uint16_t allocated_keys = 0;
        int rodc_krbtgt_number = 0;
-       uint32_t supported_enctypes;
+       int kvno = 0;
+       uint32_t supported_enctypes
+               = ldb_msg_find_attr_as_uint(msg,
+                                           "msDS-SupportedEncryptionTypes",
+                                           0);
 
        if (rid == DOMAIN_RID_KRBTGT || is_rodc) {
-               /* KDCs (and KDCs on RODCs) use AES, but not DES */
-               supported_enctypes = ENC_ALL_TYPES;
-               supported_enctypes &= ~(ENC_CRC32|ENC_RSA_MD5);
+               /* KDCs (and KDCs on RODCs) use AES */
+               supported_enctypes |= ENC_HMAC_SHA1_96_AES128 | ENC_HMAC_SHA1_96_AES256;
        } else if (userAccountControl & (UF_PARTIAL_SECRETS_ACCOUNT|UF_SERVER_TRUST_ACCOUNT)) {
                /* DCs and RODCs comptuer accounts use AES */
-               supported_enctypes = ENC_ALL_TYPES;
+               supported_enctypes |= ENC_HMAC_SHA1_96_AES128 | ENC_HMAC_SHA1_96_AES256;
        } else if (ent_type == SAMBA_KDC_ENT_TYPE_CLIENT ||
                   (ent_type == SAMBA_KDC_ENT_TYPE_ANY)) {
                /* for AS-REQ the client chooses the enc types it
                 * supports, and this will vary between computers a
-                * user logs in from.  However, some accounts may be
-                * banned from using DES, so allow the default to be
-                * overridden
+                * user logs in from.
                 *
                 * likewise for 'any' return as much as is supported,
                 * to export into a keytab */
-               supported_enctypes = ldb_msg_find_attr_as_uint(msg, "msDS-SupportedEncryptionTypes",
-                                                              ENC_ALL_TYPES);
+               supported_enctypes = ENC_ALL_TYPES;
+       }
+
+       /* If UF_USE_DES_KEY_ONLY has been set, then don't allow use of the newer enc types */
+       if (userAccountControl & UF_USE_DES_KEY_ONLY) {
+               supported_enctypes = ENC_CRC32|ENC_RSA_MD5;
        } else {
-               /* However, if this is a TGS-REQ, then lock it down to
-                * a reasonable guess as to what the server can decode
-                * - we must use whatever is in
-                * "msDS-SupportedEncryptionTypes", or the 'old' set
-                * of keys (ie, what Windows 2000 supported) */
-               supported_enctypes = ldb_msg_find_attr_as_uint(msg, "msDS-SupportedEncryptionTypes",
-                                                              ENC_CRC32 | ENC_RSA_MD5 | ENC_RC4_HMAC_MD5);
+               /* Otherwise, add in the default enc types */
+               supported_enctypes |= ENC_CRC32 | ENC_RSA_MD5 | ENC_RC4_HMAC_MD5;
        }
 
        /* Is this the krbtgt or a RODC krbtgt */
@@ -254,34 +269,14 @@ static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
                }
        }
 
-
-       /* If UF_USE_DES_KEY_ONLY has been set, then don't allow use of the newer enc types */
-       if (userAccountControl & UF_USE_DES_KEY_ONLY) {
-               /* However, this still won't allow use of DES, if we
-                * were told not to by msDS-SupportedEncTypes */
-               supported_enctypes &= ENC_CRC32|ENC_RSA_MD5;
-       } else {
-               switch (ent_type) {
-               case SAMBA_KDC_ENT_TYPE_KRBTGT:
-               case SAMBA_KDC_ENT_TYPE_TRUST:
-                       /* Unless a very special effort it made,
-                        * disallow trust tickets to be DES encrypted,
-                        * it's just too dangerous */
-                       supported_enctypes &= ~(ENC_CRC32|ENC_RSA_MD5);
-                       break;
-               default:
-                       break;
-                       /* No further restrictions */
-               }
-       }
-
        entry_ex->entry.keys.val = NULL;
        entry_ex->entry.keys.len = 0;
 
-       entry_ex->entry.kvno = ldb_msg_find_attr_as_int(msg, "msDS-KeyVersionNumber", 0);
+       kvno = ldb_msg_find_attr_as_int(msg, "msDS-KeyVersionNumber", 0);
        if (is_rodc) {
-               entry_ex->entry.kvno |= (rodc_krbtgt_number << 16);
+               kvno = SAMBA_KVNO_AND_KRBTGT(kvno, rodc_krbtgt_number);
        }
+       entry_ex->entry.kvno = kvno;
 
        /* Get keys from the db */
 
@@ -541,19 +536,20 @@ out:
  * Construct an hdb_entry from a directory entry.
  */
 static krb5_error_code samba_kdc_message2entry(krb5_context context,
-                                        struct samba_kdc_db_context *kdc_db_ctx,
-                                        TALLOC_CTX *mem_ctx, krb5_const_principal principal,
-                                        enum samba_kdc_ent_type ent_type,
-                                        struct ldb_dn *realm_dn,
-                                        struct ldb_message *msg,
-                                        hdb_entry_ex *entry_ex)
+                                              struct samba_kdc_db_context *kdc_db_ctx,
+                                              TALLOC_CTX *mem_ctx, krb5_const_principal principal,
+                                              enum samba_kdc_ent_type ent_type,
+                                              unsigned flags,
+                                              struct ldb_dn *realm_dn,
+                                              struct ldb_message *msg,
+                                              hdb_entry_ex *entry_ex)
 {
        struct loadparm_context *lp_ctx = kdc_db_ctx->lp_ctx;
        uint32_t userAccountControl;
+       uint32_t msDS_User_Account_Control_Computed;
        unsigned int i;
        krb5_error_code ret = 0;
        krb5_boolean is_computer = FALSE;
-       char *realm = strupper_talloc(mem_ctx, lpcfg_realm(lp_ctx));
 
        struct samba_kdc_entry *p;
        NTTIME acct_expiry;
@@ -585,12 +581,6 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
 
        memset(entry_ex, 0, sizeof(*entry_ex));
 
-       if (!realm) {
-               ret = ENOMEM;
-               krb5_set_error_message(context, ret, "talloc_strdup: out of memory");
-               goto out;
-       }
-
        p = talloc(mem_ctx, struct samba_kdc_entry);
        if (!p) {
                ret = ENOMEM;
@@ -615,10 +605,29 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
 
        userAccountControl = ldb_msg_find_attr_as_uint(msg, "userAccountControl", 0);
 
+       msDS_User_Account_Control_Computed
+               = ldb_msg_find_attr_as_uint(msg,
+                                           "msDS-User-Account-Control-Computed",
+                                           UF_ACCOUNTDISABLE);
+
+       /*
+        * This brings in the lockout flag, block the account if not
+        * found.  We need the weird UF_ACCOUNTDISABLE check because
+        * we do not want to fail open if the value is not returned,
+        * but 0 is a valid value (all OK)
+        */
+       if (msDS_User_Account_Control_Computed == UF_ACCOUNTDISABLE) {
+               ret = EINVAL;
+               krb5_set_error_message(context, ret, "samba_kdc_message2entry: "
+                               "no msDS-User-Account-Control-Computed present");
+               goto out;
+       } else {
+               userAccountControl |= msDS_User_Account_Control_Computed;
+       }
 
        entry_ex->entry.principal = malloc(sizeof(*(entry_ex->entry.principal)));
        if (ent_type == SAMBA_KDC_ENT_TYPE_ANY && principal == NULL) {
-               krb5_make_principal(context, &entry_ex->entry.principal, realm, samAccountName, NULL);
+               krb5_make_principal(context, &entry_ex->entry.principal, lpcfg_realm(lp_ctx), samAccountName, NULL);
        } else {
                ret = copy_Principal(principal, entry_ex->entry.principal);
                if (ret) {
@@ -633,7 +642,7 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
                 * we determine from our records */
 
                /* this has to be with malloc() */
-               krb5_principal_set_realm(context, entry_ex->entry.principal, realm);
+               krb5_principal_set_realm(context, entry_ex->entry.principal, lpcfg_realm(lp_ctx));
        }
 
        /* First try and figure out the flags based on the userAccountControl */
@@ -651,7 +660,7 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
                }
        }
 
-       {
+       if (flags & HDB_F_ADMIN_DATA) {
                /* These (created_by, modified_by) parts of the entry are not relevant for Samba4's use
                 * of the Heimdal KDC.  They are stored in a the traditional
                 * DB for audit purposes, and still form part of the structure
@@ -662,7 +671,7 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
                /* use 'kadmin' for now (needed by mit_samba) */
                krb5_make_principal(context,
                                    &entry_ex->entry.created_by.principal,
-                                   realm, "kadmin", NULL);
+                                   lpcfg_realm(lp_ctx), "kadmin", NULL);
 
                entry_ex->entry.modified_by = (Event *) malloc(sizeof(Event));
                if (entry_ex->entry.modified_by == NULL) {
@@ -676,7 +685,7 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
                /* use 'kadmin' for now (needed by mit_samba) */
                krb5_make_principal(context,
                                    &entry_ex->entry.modified_by->principal,
-                                   realm, "kadmin", NULL);
+                                   lpcfg_realm(lp_ctx), "kadmin", NULL);
        }
 
 
@@ -767,9 +776,28 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
 
        entry_ex->entry.valid_start = NULL;
 
-       entry_ex->entry.max_life = NULL;
+       entry_ex->entry.max_life = malloc(sizeof(*entry_ex->entry.max_life));
+       if (entry_ex->entry.max_life == NULL) {
+               ret = ENOMEM;
+               goto out;
+       }
 
-       entry_ex->entry.max_renew = NULL;
+       if (ent_type == SAMBA_KDC_ENT_TYPE_SERVER) {
+               *entry_ex->entry.max_life = kdc_db_ctx->policy.svc_tkt_lifetime;
+       } else if (ent_type == SAMBA_KDC_ENT_TYPE_KRBTGT || ent_type == SAMBA_KDC_ENT_TYPE_CLIENT) {
+               *entry_ex->entry.max_life = kdc_db_ctx->policy.usr_tkt_lifetime;
+       } else {
+               *entry_ex->entry.max_life = MIN(kdc_db_ctx->policy.svc_tkt_lifetime,
+                                               kdc_db_ctx->policy.usr_tkt_lifetime);
+       }
+
+       entry_ex->entry.max_renew = malloc(sizeof(*entry_ex->entry.max_life));
+       if (entry_ex->entry.max_renew == NULL) {
+               ret = ENOMEM;
+               goto out;
+       }
+
+       *entry_ex->entry.max_renew = kdc_db_ctx->policy.renewal_lifetime;
 
        entry_ex->entry.generation = NULL;
 
@@ -815,27 +843,42 @@ out:
 
 /*
  * Construct an hdb_entry from a directory entry.
+ * The kvno is what the remote client asked for
  */
 static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
                                               struct samba_kdc_db_context *kdc_db_ctx,
                                               TALLOC_CTX *mem_ctx, krb5_const_principal principal,
                                               enum trust_direction direction,
                                               struct ldb_dn *realm_dn,
+                                              unsigned flags,
+                                              uint32_t kvno,
                                               struct ldb_message *msg,
                                               hdb_entry_ex *entry_ex)
 {
        struct loadparm_context *lp_ctx = kdc_db_ctx->lp_ctx;
        const char *dnsdomain;
-       char *realm = strupper_talloc(mem_ctx, lpcfg_realm(lp_ctx));
-       DATA_BLOB password_utf16;
-       struct samr_Password password_hash;
+       const char *realm = lpcfg_realm(lp_ctx);
+       DATA_BLOB password_utf16 = data_blob_null;
+       DATA_BLOB password_utf8 = data_blob_null;
+       struct samr_Password _password_hash;
+       const struct samr_Password *password_hash = NULL;
        const struct ldb_val *password_val;
        struct trustAuthInOutBlob password_blob;
        struct samba_kdc_entry *p;
-
+       bool use_previous;
+       uint32_t current_kvno;
+       uint32_t num_keys = 0;
        enum ndr_err_code ndr_err;
        int ret, trust_direction_flags;
        unsigned int i;
+       struct AuthenticationInformationArray *auth_array;
+       uint32_t supported_enctypes = ENCTYPE_ARCFOUR_HMAC;
+
+       if (dsdb_functional_level(kdc_db_ctx->samdb) >= DS_DOMAIN_FUNCTION_2008) {
+               supported_enctypes = ldb_msg_find_attr_as_uint(msg,
+                                       "msDS-SupportedEncryptionTypes",
+                                       supported_enctypes);
+       }
 
        p = talloc(mem_ctx, struct samba_kdc_entry);
        if (!p) {
@@ -862,6 +905,29 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
                            &entry_ex->entry.created_by.principal,
                            realm, "kadmin", NULL);
 
+       entry_ex->entry.principal = malloc(sizeof(*(entry_ex->entry.principal)));
+       if (entry_ex->entry.principal == NULL) {
+               krb5_clear_error_message(context);
+               ret = ENOMEM;
+               goto out;
+       }
+
+       ret = copy_Principal(principal, entry_ex->entry.principal);
+       if (ret) {
+               krb5_clear_error_message(context);
+               goto out;
+       }
+
+       /*
+        * While we have copied the client principal, tests
+        * show that Win2k3 returns the 'corrected' realm, not
+        * the client-specified realm.  This code attempts to
+        * replace the client principal's realm with the one
+        * we determine from our records
+        */
+
+       krb5_principal_set_realm(context, entry_ex->entry.principal, realm);
+
        entry_ex->entry.valid_start = NULL;
 
        trust_direction_flags = ldb_msg_find_attr_as_int(msg, "trustDirection", 0);
@@ -872,85 +938,202 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
        } else { /* OUTBOUND */
                dnsdomain = ldb_msg_find_attr_as_string(msg, "trustPartner", NULL);
                /* replace realm */
-               talloc_free(realm);
                realm = strupper_talloc(mem_ctx, dnsdomain);
                password_val = ldb_msg_find_ldb_val(msg, "trustAuthOutgoing");
        }
 
        if (!password_val || !(trust_direction_flags & direction)) {
-               ret = ENOENT;
+               krb5_clear_error_message(context);
+               ret = HDB_ERR_NOENTRY;
                goto out;
        }
 
        ndr_err = ndr_pull_struct_blob(password_val, mem_ctx, &password_blob,
                                           (ndr_pull_flags_fn_t)ndr_pull_trustAuthInOutBlob);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               krb5_clear_error_message(context);
                ret = EINVAL;
                goto out;
        }
 
-       entry_ex->entry.kvno = -1;
+
+       /* we need to work out if we are going to use the current or
+        * the previous password hash.
+        * We base this on the kvno the client passes in. If the kvno
+        * passed in is equal to the current kvno in our database then
+        * we use the current structure. If it is the current kvno-1,
+        * then we use the previous substrucure.
+        */
+
+       /* first work out the current kvno */
+       current_kvno = 0;
        for (i=0; i < password_blob.count; i++) {
                if (password_blob.current.array[i].AuthType == TRUST_AUTH_TYPE_VERSION) {
-                       entry_ex->entry.kvno = password_blob.current.array[i].AuthInfo.version.version;
+                       current_kvno = password_blob.current.array[i].AuthInfo.version.version;
                }
        }
 
-       for (i=0; i < password_blob.count; i++) {
-               if (password_blob.current.array[i].AuthType == TRUST_AUTH_TYPE_CLEAR) {
-                       password_utf16 = data_blob_const(password_blob.current.array[i].AuthInfo.clear.password,
-                                                        password_blob.current.array[i].AuthInfo.clear.size);
-                       /* In the future, generate all sorts of
-                        * hashes, but for now we can't safely convert
-                        * the random strings windows uses into
-                        * utf8 */
-
-                       /* but as it is utf16 already, we can get the NT password/arcfour-hmac-md5 key */
-                       mdfour(password_hash.hash, password_utf16.data, password_utf16.length);
-                       break;
-               } else if (password_blob.current.array[i].AuthType == TRUST_AUTH_TYPE_NT4OWF) {
-                       password_hash = password_blob.current.array[i].AuthInfo.nt4owf.password;
+       /* work out whether we will use the previous or current
+          password */
+       if (password_blob.previous.count == 0) {
+               /* there is no previous password */
+               use_previous = false;
+       } else if (!(flags & HDB_F_KVNO_SPECIFIED) ||
+           kvno == current_kvno) {
+               use_previous = false;
+       } else if ((kvno+1 == current_kvno) ||
+                  (kvno == 255 && current_kvno == 0)) {
+               use_previous = true;
+       } else {
+               DEBUG(1,(__location__ ": Request for unknown kvno %u - current kvno is %u\n",
+                        kvno, current_kvno));
+               krb5_clear_error_message(context);
+               ret = HDB_ERR_NOENTRY;
+               goto out;
+       }
+
+       if (use_previous) {
+               auth_array = &password_blob.previous;
+       } else {
+               auth_array = &password_blob.current;
+       }
+
+       /* use the kvno the client specified, if available */
+       if (flags & HDB_F_KVNO_SPECIFIED) {
+               entry_ex->entry.kvno = kvno;
+       } else {
+               entry_ex->entry.kvno = current_kvno;
+       }
+
+       for (i=0; i < auth_array->count; i++) {
+               if (auth_array->array[i].AuthType == TRUST_AUTH_TYPE_CLEAR) {
+                       bool ok;
+
+                       password_utf16 = data_blob_const(auth_array->array[i].AuthInfo.clear.password,
+                                                        auth_array->array[i].AuthInfo.clear.size);
+                       if (password_utf16.length == 0) {
+                               break;
+                       }
+
+                       if (supported_enctypes & ENCTYPE_ARCFOUR_HMAC) {
+                               mdfour(_password_hash.hash, password_utf16.data, password_utf16.length);
+                               if (password_hash == NULL) {
+                                       num_keys += 1;
+                               }
+                               password_hash = &_password_hash;
+                       }
+
+                       if (!(supported_enctypes & (ENC_HMAC_SHA1_96_AES128|ENC_HMAC_SHA1_96_AES256))) {
+                               break;
+                       }
+
+                       ok = convert_string_talloc(mem_ctx,
+                                                  CH_UTF16MUNGED, CH_UTF8,
+                                                  password_utf16.data,
+                                                  password_utf16.length,
+                                                  (void *)&password_utf8.data,
+                                                  &password_utf8.length);
+                       if (!ok) {
+                               krb5_clear_error_message(context);
+                               ret = ENOMEM;
+                               goto out;
+                       }
+
+                       if (supported_enctypes & ENC_HMAC_SHA1_96_AES128) {
+                               num_keys += 1;
+                       }
+                       if (supported_enctypes & ENC_HMAC_SHA1_96_AES256) {
+                               num_keys += 1;
+                       }
                        break;
+               } else if (auth_array->array[i].AuthType == TRUST_AUTH_TYPE_NT4OWF) {
+                       if (supported_enctypes & ENCTYPE_ARCFOUR_HMAC) {
+                               password_hash = &auth_array->array[i].AuthInfo.nt4owf.password;
+                               num_keys += 1;
+                       }
                }
        }
 
-       if (i < password_blob.count) {
-               Key key;
-               /* Must have found a cleartext or MD4 password */
-               entry_ex->entry.keys.val = calloc(1, sizeof(Key));
+       /* Must have found a cleartext or MD4 password */
+       if (num_keys == 0) {
+               DEBUG(1,(__location__ ": no usable key found\n"));
+               krb5_clear_error_message(context);
+               ret = HDB_ERR_NOENTRY;
+               goto out;
+       }
 
-               key.mkvno = 0;
-               key.salt = NULL; /* No salt for this enc type */
+       entry_ex->entry.keys.val = calloc(num_keys, sizeof(Key));
+       if (entry_ex->entry.keys.val == NULL) {
+               krb5_clear_error_message(context);
+               ret = ENOMEM;
+               goto out;
+       }
 
-               if (entry_ex->entry.keys.val == NULL) {
-                       ret = ENOMEM;
+       if (password_utf8.length != 0) {
+               Key key = {};
+               krb5_const_principal salt_principal = principal;
+               krb5_salt salt;
+               krb5_data cleartext_data;
+
+               cleartext_data.data = password_utf8.data;
+               cleartext_data.length = password_utf8.length;
+
+               ret = krb5_get_pw_salt(context,
+                                      salt_principal,
+                                      &salt);
+               if (ret != 0) {
                        goto out;
                }
 
+               if (supported_enctypes & ENCTYPE_AES256_CTS_HMAC_SHA1_96) {
+                       ret = krb5_string_to_key_data_salt(context,
+                                                          ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+                                                          cleartext_data,
+                                                          salt,
+                                                          &key.key);
+                       if (ret != 0) {
+                               krb5_free_salt(context, salt);
+                               goto out;
+                       }
+
+                       entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key;
+                       entry_ex->entry.keys.len++;
+               }
+
+               if (supported_enctypes & ENCTYPE_AES128_CTS_HMAC_SHA1_96) {
+                       ret = krb5_string_to_key_data_salt(context,
+                                                          ENCTYPE_AES128_CTS_HMAC_SHA1_96,
+                                                          cleartext_data,
+                                                          salt,
+                                                          &key.key);
+                       if (ret != 0) {
+                               krb5_free_salt(context, salt);
+                               goto out;
+                       }
+
+                       entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key;
+                       entry_ex->entry.keys.len++;
+               }
+
+               krb5_free_salt(context, salt);
+       }
+
+       if (password_hash != NULL) {
+               Key key = {};
+
                ret = krb5_keyblock_init(context,
                                         ENCTYPE_ARCFOUR_HMAC,
-                                        password_hash.hash, sizeof(password_hash.hash),
+                                        password_hash->hash,
+                                        sizeof(password_hash->hash),
                                         &key.key);
+               if (ret != 0) {
+                       goto out;
+               }
 
                entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key;
                entry_ex->entry.keys.len++;
        }
 
-       entry_ex->entry.principal = malloc(sizeof(*(entry_ex->entry.principal)));
-
-       ret = copy_Principal(principal, entry_ex->entry.principal);
-       if (ret) {
-               krb5_clear_error_message(context);
-               goto out;
-       }
-
-       /* While we have copied the client principal, tests
-        * show that Win2k3 returns the 'corrected' realm, not
-        * the client-specified realm.  This code attempts to
-        * replace the client principal's realm with the one
-        * we determine from our records */
-
-       krb5_principal_set_realm(context, entry_ex->entry.principal, realm);
        entry_ex->entry.flags = int2HDBFlags(0);
        entry_ex->entry.flags.immutable = 1;
        entry_ex->entry.flags.invalid = 0;
@@ -1003,35 +1186,25 @@ static krb5_error_code samba_kdc_lookup_trust(krb5_context context, struct ldb_c
                                        struct ldb_dn *realm_dn,
                                        struct ldb_message **pmsg)
 {
-       int lret;
-       krb5_error_code ret;
-       char *filter = NULL;
+       NTSTATUS status;
        const char * const *attrs = trust_attrs;
 
-       struct ldb_result *res = NULL;
-       filter = talloc_asprintf(mem_ctx, "(&(objectClass=trustedDomain)(|(flatname=%s)(trustPartner=%s)))", realm, realm);
-
-       if (!filter) {
-               ret = ENOMEM;
-               krb5_set_error_message(context, ret, "talloc_asprintf: out of memory");
-               return ret;
-       }
-
-       lret = ldb_search(ldb_ctx, mem_ctx, &res,
-                         ldb_get_default_basedn(ldb_ctx),
-                         LDB_SCOPE_SUBTREE, attrs, "%s", filter);
-       if (lret != LDB_SUCCESS) {
-               DEBUG(3, ("Failed to search for %s: %s\n", filter, ldb_errstring(ldb_ctx)));
-               return HDB_ERR_NOENTRY;
-       } else if (res->count == 0 || res->count > 1) {
-               DEBUG(3, ("Failed find a single entry for %s: got %d\n", filter, res->count));
-               talloc_free(res);
+       status = sam_get_results_trust(ldb_ctx,
+                                      mem_ctx, realm, realm, attrs,
+                                      pmsg);
+       if (NT_STATUS_IS_OK(status)) {
+               return 0;
+       } else if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
                return HDB_ERR_NOENTRY;
+       } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
+               int ret = ENOMEM;
+               krb5_set_error_message(context, ret, "get_sam_result_trust: out of memory");
+               return ret;
+       } else {
+               int ret = EINVAL;
+               krb5_set_error_message(context, ret, "get_sam_result_trust: %s", nt_errstr(status));
+               return ret;
        }
-       talloc_steal(mem_ctx, res->msgs);
-       *pmsg = res->msgs[0];
-       talloc_free(res);
-       return 0;
 }
 
 static krb5_error_code samba_kdc_lookup_client(krb5_context context,
@@ -1070,6 +1243,7 @@ static krb5_error_code samba_kdc_fetch_client(krb5_context context,
                                               struct samba_kdc_db_context *kdc_db_ctx,
                                               TALLOC_CTX *mem_ctx,
                                               krb5_const_principal principal,
+                                              unsigned flags,
                                               hdb_entry_ex *entry_ex) {
        struct ldb_dn *realm_dn;
        krb5_error_code ret;
@@ -1083,8 +1257,9 @@ static krb5_error_code samba_kdc_fetch_client(krb5_context context,
        }
 
        ret = samba_kdc_message2entry(context, kdc_db_ctx, mem_ctx,
-                                      principal, SAMBA_KDC_ENT_TYPE_CLIENT,
-                                      realm_dn, msg, entry_ex);
+                                     principal, SAMBA_KDC_ENT_TYPE_CLIENT,
+                                     flags,
+                                     realm_dn, msg, entry_ex);
        return ret;
 }
 
@@ -1092,7 +1267,8 @@ static krb5_error_code samba_kdc_fetch_krbtgt(krb5_context context,
                                              struct samba_kdc_db_context *kdc_db_ctx,
                                              TALLOC_CTX *mem_ctx,
                                              krb5_const_principal principal,
-                                             uint32_t krbtgt_number,
+                                             unsigned flags,
+                                             uint32_t kvno,
                                              hdb_entry_ex *entry_ex)
 {
        struct loadparm_context *lp_ctx = kdc_db_ctx->lp_ctx;
@@ -1117,12 +1293,25 @@ static krb5_error_code samba_kdc_fetch_krbtgt(krb5_context context,
                 * krbtgt */
 
                int lret;
-               char *realm_fixed;
+               unsigned int krbtgt_number;
+               /* w2k8r2 sometimes gives us a kvno of 255 for inter-domain
+                  trust tickets. We don't yet know what this means, but we do
+                  seem to need to treat it as unspecified */
+               if (flags & HDB_F_KVNO_SPECIFIED) {
+                       krbtgt_number = SAMBA_KVNO_GET_KRBTGT(kvno);
+                       if (kdc_db_ctx->rodc) {
+                               if (krbtgt_number != kdc_db_ctx->my_krbtgt_number) {
+                                       return HDB_ERR_NOT_FOUND_HERE;
+                               }
+                       }
+               } else {
+                       krbtgt_number = kdc_db_ctx->my_krbtgt_number;
+               }
 
                if (krbtgt_number == kdc_db_ctx->my_krbtgt_number) {
                        lret = dsdb_search_one(kdc_db_ctx->samdb, mem_ctx,
                                               &msg, kdc_db_ctx->krbtgt_dn, LDB_SCOPE_BASE,
-                                              krbtgt_attrs, 0,
+                                              krbtgt_attrs, DSDB_SEARCH_NO_GLOBAL_CATALOG,
                                               "(objectClass=user)");
                } else {
                        /* We need to look up an RODC krbtgt (perhaps
@@ -1131,7 +1320,7 @@ static krb5_error_code samba_kdc_fetch_krbtgt(krb5_context context,
                        lret = dsdb_search_one(kdc_db_ctx->samdb, mem_ctx,
                                               &msg, realm_dn, LDB_SCOPE_SUBTREE,
                                               krbtgt_attrs,
-                                              DSDB_SEARCH_SHOW_EXTENDED_DN,
+                                              DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
                                               "(&(objectClass=user)(msDS-SecondaryKrbTgtNumber=%u))", (unsigned)(krbtgt_number));
                }
 
@@ -1151,31 +1340,37 @@ static krb5_error_code samba_kdc_fetch_krbtgt(krb5_context context,
                        return HDB_ERR_NOENTRY;
                }
 
-               realm_fixed = strupper_talloc(mem_ctx, lpcfg_realm(lp_ctx));
-               if (!realm_fixed) {
-                       ret = ENOMEM;
-                       krb5_set_error_message(context, ret, "strupper_talloc: out of memory");
-                       return ret;
-               }
-
-               ret = krb5_copy_principal(context, principal, &alloc_principal);
-               if (ret) {
-                       return ret;
-               }
-
-               free(alloc_principal->name.name_string.val[1]);
-               alloc_principal->name.name_string.val[1] = strdup(realm_fixed);
-               talloc_free(realm_fixed);
-               if (!alloc_principal->name.name_string.val[1]) {
-                       ret = ENOMEM;
-                       krb5_set_error_message(context, ret, "samba_kdc_fetch: strdup() failed!");
-                       return ret;
-               }
-               principal = alloc_principal;
+               /*
+                * Windows seems to canonicalize the principal
+                * in a TGS REP even if the client did not specify
+                * the canonicalize flag.
+                */
+               if (flags & (HDB_F_CANON|HDB_F_FOR_TGS_REQ)) {
+                       ret = krb5_copy_principal(context, principal, &alloc_principal);
+                       if (ret) {
+                               return ret;
+                       }
+
+                       /* When requested to do so, ensure that the
+                        * both realm values in the principal are set
+                        * to the upper case, canonical realm */
+                       free(alloc_principal->name.name_string.val[1]);
+                       alloc_principal->name.name_string.val[1] = strdup(lpcfg_realm(lp_ctx));
+                       if (!alloc_principal->name.name_string.val[1]) {
+                               ret = ENOMEM;
+                               krb5_set_error_message(context, ret, "samba_kdc_fetch: strdup() failed!");
+                               return ret;
+                       }
+                       principal = alloc_principal;
+               }
 
                ret = samba_kdc_message2entry(context, kdc_db_ctx, mem_ctx,
-                                       principal, SAMBA_KDC_ENT_TYPE_KRBTGT,
-                                       realm_dn, msg, entry_ex);
+                                             principal, SAMBA_KDC_ENT_TYPE_KRBTGT,
+                                             flags, realm_dn, msg, entry_ex);
+               if (alloc_principal) {
+                       /* This is again copied in the message2entry call */
+                       krb5_free_principal(context, alloc_principal);
+               }
                if (ret != 0) {
                        krb5_warnx(context, "samba_kdc_fetch: self krbtgt message2entry failed");
                }
@@ -1216,10 +1411,14 @@ static krb5_error_code samba_kdc_fetch_krbtgt(krb5_context context,
                }
 
                ret = samba_kdc_trust_message2entry(context, kdc_db_ctx, mem_ctx,
-                                             principal, direction,
-                                             realm_dn, msg, entry_ex);
+                                                   principal, direction,
+                                                   realm_dn, flags, kvno, msg, entry_ex);
                if (ret != 0) {
-                       krb5_warnx(context, "samba_kdc_fetch: trust_message2entry failed");
+                       krb5_warnx(context, "samba_kdc_fetch: trust_message2entry failed for %s",
+                                  ldb_dn_get_linearized(msg->dn));
+                       krb5_set_error_message(context, ret, "samba_kdc_fetch: "
+                                              "trust_message2entry failed for %s",
+                                              ldb_dn_get_linearized(msg->dn));
                }
                return ret;
        }
@@ -1235,7 +1434,6 @@ static krb5_error_code samba_kdc_lookup_server(krb5_context context,
                                                struct ldb_message **msg)
 {
        krb5_error_code ret;
-       const char *realm;
        if (principal->name.name_string.len >= 2) {
                /* 'normal server' case */
                int ldb_ret;
@@ -1265,20 +1463,22 @@ static krb5_error_code samba_kdc_lookup_server(krb5_context context,
                ldb_ret = dsdb_search_one(kdc_db_ctx->samdb,
                                          mem_ctx,
                                          msg, user_dn, LDB_SCOPE_BASE,
-                                         attrs, DSDB_SEARCH_SHOW_EXTENDED_DN, "(objectClass=*)");
+                                         attrs,
+                                         DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
+                                         "(objectClass=*)");
                if (ldb_ret != LDB_SUCCESS) {
                        return HDB_ERR_NOENTRY;
                }
 
        } else {
                int lret;
-               char *filter = NULL;
                char *short_princ;
+               /* const char *realm; */
                /* server as client principal case, but we must not lookup userPrincipalNames */
                *realm_dn = ldb_get_default_basedn(kdc_db_ctx->samdb);
-               realm = krb5_principal_get_realm(context, principal);
+               /* realm = krb5_principal_get_realm(context, principal); */
 
-               /* TODO: Check if it is our realm, otherwise give referall */
+               /* TODO: Check if it is our realm, otherwise give referral */
 
                ret = krb5_unparse_name_flags(context, principal,  KRB5_PRINCIPAL_UNPARSE_NO_REALM, &short_princ);
 
@@ -1291,29 +1491,32 @@ static krb5_error_code samba_kdc_lookup_server(krb5_context context,
                lret = dsdb_search_one(kdc_db_ctx->samdb, mem_ctx, msg,
                                       *realm_dn, LDB_SCOPE_SUBTREE,
                                       attrs,
-                                      DSDB_SEARCH_SHOW_EXTENDED_DN,
+                                      DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
                                       "(&(objectClass=user)(samAccountName=%s))",
                                       ldb_binary_encode_string(mem_ctx, short_princ));
-               free(short_princ);
                if (lret == LDB_ERR_NO_SUCH_OBJECT) {
-                       DEBUG(3, ("Failed find a entry for %s\n", filter));
+                       DEBUG(3, ("Failed to find an entry for %s\n", short_princ));
+                       free(short_princ);
                        return HDB_ERR_NOENTRY;
                }
                if (lret != LDB_SUCCESS) {
-                       DEBUG(3, ("Failed single search for for %s - %s\n",
-                                 filter, ldb_errstring(kdc_db_ctx->samdb)));
+                       DEBUG(3, ("Failed single search for %s - %s\n",
+                                 short_princ, ldb_errstring(kdc_db_ctx->samdb)));
+                       free(short_princ);
                        return HDB_ERR_NOENTRY;
                }
+               free(short_princ);
        }
 
        return 0;
 }
 
 static krb5_error_code samba_kdc_fetch_server(krb5_context context,
-                                              struct samba_kdc_db_context *kdc_db_ctx,
-                                              TALLOC_CTX *mem_ctx,
-                                              krb5_const_principal principal,
-                                              hdb_entry_ex *entry_ex)
+                                             struct samba_kdc_db_context *kdc_db_ctx,
+                                             TALLOC_CTX *mem_ctx,
+                                             krb5_const_principal principal,
+                                             unsigned flags,
+                                             hdb_entry_ex *entry_ex)
 {
        krb5_error_code ret;
        struct ldb_dn *realm_dn;
@@ -1326,8 +1529,9 @@ static krb5_error_code samba_kdc_fetch_server(krb5_context context,
        }
 
        ret = samba_kdc_message2entry(context, kdc_db_ctx, mem_ctx,
-                               principal, SAMBA_KDC_ENT_TYPE_SERVER,
-                               realm_dn, msg, entry_ex);
+                                     principal, SAMBA_KDC_ENT_TYPE_SERVER,
+                                     flags,
+                                     realm_dn, msg, entry_ex);
        if (ret != 0) {
                krb5_warnx(context, "samba_kdc_fetch: message2entry failed");
        }
@@ -1339,22 +1543,11 @@ krb5_error_code samba_kdc_fetch(krb5_context context,
                                struct samba_kdc_db_context *kdc_db_ctx,
                                krb5_const_principal principal,
                                unsigned flags,
-                               unsigned kvno,
+                               krb5_kvno kvno,
                                hdb_entry_ex *entry_ex)
 {
        krb5_error_code ret = HDB_ERR_NOENTRY;
        TALLOC_CTX *mem_ctx;
-       unsigned int krbtgt_number;
-       if (flags & HDB_F_KVNO_SPECIFIED) {
-               krbtgt_number = kvno >> 16;
-               if (kdc_db_ctx->rodc) {
-                       if (krbtgt_number != kdc_db_ctx->my_krbtgt_number) {
-                               return HDB_ERR_NOT_FOUND_HERE;
-                       }
-               }
-       } else {
-               krbtgt_number = kdc_db_ctx->my_krbtgt_number;
-       }
 
        mem_ctx = talloc_named(kdc_db_ctx, 0, "samba_kdc_fetch context");
        if (!mem_ctx) {
@@ -1364,20 +1557,20 @@ krb5_error_code samba_kdc_fetch(krb5_context context,
        }
 
        if (flags & HDB_F_GET_CLIENT) {
-               ret = samba_kdc_fetch_client(context, kdc_db_ctx, mem_ctx, principal, entry_ex);
+               ret = samba_kdc_fetch_client(context, kdc_db_ctx, mem_ctx, principal, flags, entry_ex);
                if (ret != HDB_ERR_NOENTRY) goto done;
        }
        if (flags & HDB_F_GET_SERVER) {
                /* krbtgt fits into this situation for trusted realms, and for resolving different versions of our own realm name */
-               ret = samba_kdc_fetch_krbtgt(context, kdc_db_ctx, mem_ctx, principal, krbtgt_number, entry_ex);
+               ret = samba_kdc_fetch_krbtgt(context, kdc_db_ctx, mem_ctx, principal, flags, kvno, entry_ex);
                if (ret != HDB_ERR_NOENTRY) goto done;
 
                /* We return 'no entry' if it does not start with krbtgt/, so move to the common case quickly */
-               ret = samba_kdc_fetch_server(context, kdc_db_ctx, mem_ctx, principal, entry_ex);
+               ret = samba_kdc_fetch_server(context, kdc_db_ctx, mem_ctx, principal, flags, entry_ex);
                if (ret != HDB_ERR_NOENTRY) goto done;
        }
        if (flags & HDB_F_GET_KRBTGT) {
-               ret = samba_kdc_fetch_krbtgt(context, kdc_db_ctx, mem_ctx, principal, krbtgt_number, entry_ex);
+               ret = samba_kdc_fetch_krbtgt(context, kdc_db_ctx, mem_ctx, principal, flags, kvno, entry_ex);
                if (ret != HDB_ERR_NOENTRY) goto done;
        }
 
@@ -1417,8 +1610,9 @@ static krb5_error_code samba_kdc_seq(krb5_context context,
 
        if (priv->index < priv->count) {
                ret = samba_kdc_message2entry(context, kdc_db_ctx, mem_ctx,
-                                       NULL, SAMBA_KDC_ENT_TYPE_ANY,
-                                       priv->realm_dn, priv->msgs[priv->index++], entry);
+                                             NULL, SAMBA_KDC_ENT_TYPE_ANY,
+                                             HDB_F_ADMIN_DATA|HDB_F_GET_ANY,
+                                             priv->realm_dn, priv->msgs[priv->index++], entry);
        } else {
                ret = HDB_ERR_NOENTRY;
        }
@@ -1475,10 +1669,12 @@ krb5_error_code samba_kdc_firstkey(krb5_context context,
                TALLOC_FREE(priv);
                return ret;
        }
+       krb5_free_default_realm(context, realm);
 
-       lret = ldb_search(ldb_ctx, priv, &res,
-                         priv->realm_dn, LDB_SCOPE_SUBTREE, user_attrs,
-                         "(objectClass=user)");
+       lret = dsdb_search(ldb_ctx, priv, &res,
+                          priv->realm_dn, LDB_SCOPE_SUBTREE, user_attrs,
+                          DSDB_SEARCH_NO_GLOBAL_CATALOG,
+                          "(objectClass=user)");
 
        if (lret != LDB_SUCCESS) {
                TALLOC_FREE(priv);
@@ -1512,14 +1708,12 @@ krb5_error_code samba_kdc_nextkey(krb5_context context,
 /* Check if a given entry may delegate or do s4u2self to this target principal
  *
  * This is currently a very nasty hack - allowing only delegation to itself.
- *
- * This is shared between the constrained delegation and S4U2Self code.
  */
 krb5_error_code
-samba_kdc_check_identical_client_and_server(krb5_context context,
-                                           struct samba_kdc_db_context *kdc_db_ctx,
-                                           hdb_entry_ex *entry,
-                                           krb5_const_principal target_principal)
+samba_kdc_check_s4u2self(krb5_context context,
+                        struct samba_kdc_db_context *kdc_db_ctx,
+                        hdb_entry_ex *entry,
+                        krb5_const_principal target_principal)
 {
        krb5_error_code ret;
        krb5_principal enterprise_prinicpal = NULL;
@@ -1532,11 +1726,11 @@ samba_kdc_check_identical_client_and_server(krb5_context context,
                "objectSid", NULL
        };
 
-       TALLOC_CTX *mem_ctx = talloc_named(kdc_db_ctx, 0, "samba_kdc_check_constrained_delegation");
+       TALLOC_CTX *mem_ctx = talloc_named(kdc_db_ctx, 0, "samba_kdc_check_s4u2self");
 
        if (!mem_ctx) {
                ret = ENOMEM;
-               krb5_set_error_message(context, ret, "samba_kdc_fetch: talloc_named() failed!");
+               krb5_set_error_message(context, ret, "samba_kdc_check_s4u2self: talloc_named() failed!");
                return ret;
        }
 
@@ -1544,7 +1738,7 @@ samba_kdc_check_identical_client_and_server(krb5_context context,
                /* Need to reparse the enterprise principal to find the real target */
                if (target_principal->name.name_string.len != 1) {
                        ret = KRB5_PARSE_MALFORMED;
-                       krb5_set_error_message(context, ret, "samba_kdc_check_constrained_delegation: request for delegation to enterprise principal with wrong (%d) number of components",
+                       krb5_set_error_message(context, ret, "samba_kdc_check_s4u2self: request for delegation to enterprise principal with wrong (%d) number of components",
                                               target_principal->name.name_string.len);
                        talloc_free(mem_ctx);
                        return ret;
@@ -1636,6 +1830,124 @@ samba_kdc_check_pkinit_ms_upn_match(krb5_context context,
        return ret;
 }
 
+/*
+ * Check if a given entry may delegate to this target principal
+ * with S4U2Proxy.
+ */
+krb5_error_code
+samba_kdc_check_s4u2proxy(krb5_context context,
+                         struct samba_kdc_db_context *kdc_db_ctx,
+                         hdb_entry_ex *entry,
+                         krb5_const_principal target_principal)
+{
+       krb5_error_code ret;
+       char *tmp = NULL;
+       const char *client_dn = NULL;
+       const char *target_principal_name = NULL;
+       struct ldb_message_element *el;
+       struct ldb_val val;
+       unsigned int i;
+       bool found = false;
+       struct samba_kdc_entry *p = talloc_get_type(entry->ctx, struct samba_kdc_entry);
+
+       TALLOC_CTX *mem_ctx = talloc_named(kdc_db_ctx, 0, "samba_kdc_check_s4u2proxy");
+
+       if (!mem_ctx) {
+               ret = ENOMEM;
+               krb5_set_error_message(context, ret,
+                                      "samba_kdc_check_s4u2proxy:"
+                                      " talloc_named() failed!");
+               return ret;
+       }
+
+       client_dn = ldb_dn_get_linearized(p->msg->dn);
+       if (!client_dn) {
+               if (errno == 0) {
+                       errno = ENOMEM;
+               }
+               ret = errno;
+               krb5_set_error_message(context, ret,
+                                      "samba_kdc_check_s4u2proxy:"
+                                      " ldb_dn_get_linearized() failed!");
+               return ret;
+       }
+
+       /*
+        * The main heimdal code already checked that the target_principal
+        * belongs to the same realm as the client.
+        *
+        * So we just need the principal without the realm,
+        * as that is what is configured in the "msDS-AllowedToDelegateTo"
+        * attribute.
+        */
+       ret = krb5_unparse_name_flags(context, target_principal,
+                                     KRB5_PRINCIPAL_UNPARSE_NO_REALM, &tmp);
+       if (ret) {
+               talloc_free(mem_ctx);
+               krb5_set_error_message(context, ret,
+                                      "samba_kdc_check_s4u2proxy:"
+                                      " krb5_unparse_name() failed!");
+               return ret;
+       }
+       DEBUG(10,("samba_kdc_check_s4u2proxy: client[%s] for target[%s]\n",
+                client_dn, tmp));
+
+       target_principal_name = talloc_strdup(mem_ctx, tmp);
+       SAFE_FREE(tmp);
+       if (target_principal_name == NULL) {
+               ret = ENOMEM;
+               krb5_set_error_message(context, ret,
+                                      "samba_kdc_check_s4u2proxy:"
+                                      " talloc_strdup() failed!");
+               return ret;
+       }
+
+       el = ldb_msg_find_element(p->msg, "msDS-AllowedToDelegateTo");
+       if (el == NULL) {
+               goto bad_option;
+       }
+
+       val = data_blob_string_const(target_principal_name);
+
+       for (i=0; i<el->num_values; i++) {
+               struct ldb_val *val1 = &val;
+               struct ldb_val *val2 = &el->values[i];
+               int cmp;
+
+               if (val1->length != val2->length) {
+                       continue;
+               }
+
+               cmp = strncasecmp((const char *)val1->data,
+                                 (const char *)val2->data,
+                                 val1->length);
+               if (cmp != 0) {
+                       continue;
+               }
+
+               found = true;
+               break;
+       }
+
+       if (!found) {
+               goto bad_option;
+       }
+
+       DEBUG(10,("samba_kdc_check_s4u2proxy: client[%s] allowed target[%s]\n",
+                client_dn, tmp));
+       talloc_free(mem_ctx);
+       return 0;
+
+bad_option:
+       krb5_set_error_message(context, ret,
+                              "samba_kdc_check_s4u2proxy: client[%s] "
+                              "not allowed for delegation to target[%s]",
+                              client_dn,
+                              target_principal_name);
+       talloc_free(mem_ctx);
+       return KRB5KDC_ERR_BADOPTION;
+}
+
 NTSTATUS samba_kdc_setup_db_ctx(TALLOC_CTX *mem_ctx, struct samba_kdc_base_context *base_ctx,
                                struct samba_kdc_db_context **kdc_db_ctx_out)
 {
@@ -1658,6 +1970,12 @@ NTSTATUS samba_kdc_setup_db_ctx(TALLOC_CTX *mem_ctx, struct samba_kdc_base_conte
        kdc_db_ctx->ev_ctx = base_ctx->ev_ctx;
        kdc_db_ctx->lp_ctx = base_ctx->lp_ctx;
 
+       /* get default kdc policy */
+       lpcfg_default_kdc_policy(base_ctx->lp_ctx,
+                                &kdc_db_ctx->policy.svc_tkt_lifetime,
+                                &kdc_db_ctx->policy.usr_tkt_lifetime,
+                                &kdc_db_ctx->policy.renewal_lifetime);
+
        session_info = system_session(kdc_db_ctx->lp_ctx);
        if (session_info == NULL) {
                return NT_STATUS_INTERNAL_ERROR;
@@ -1714,7 +2032,7 @@ NTSTATUS samba_kdc_setup_db_ctx(TALLOC_CTX *mem_ctx, struct samba_kdc_base_conte
                ldb_ret = dsdb_search_one(kdc_db_ctx->samdb, kdc_db_ctx,
                                          &msg, kdc_db_ctx->krbtgt_dn, LDB_SCOPE_BASE,
                                          secondary_keytab,
-                                         0,
+                                         DSDB_SEARCH_NO_GLOBAL_CATALOG,
                                          "(&(objectClass=user)(msDS-SecondaryKrbTgtNumber=*))");
                if (ldb_ret != LDB_SUCCESS) {
                        DEBUG(1, ("hdb_samba4_create: Cannot read krbtgt account %s in KDC backend to get msDS-SecondaryKrbTgtNumber: %s: %s\n",
@@ -1737,9 +2055,11 @@ NTSTATUS samba_kdc_setup_db_ctx(TALLOC_CTX *mem_ctx, struct samba_kdc_base_conte
        } else {
                kdc_db_ctx->my_krbtgt_number = 0;
                ldb_ret = dsdb_search_one(kdc_db_ctx->samdb, kdc_db_ctx,
-                                         &msg, NULL, LDB_SCOPE_SUBTREE,
+                                         &msg,
+                                         ldb_get_default_basedn(kdc_db_ctx->samdb),
+                                         LDB_SCOPE_SUBTREE,
                                          krbtgt_attrs,
-                                         DSDB_SEARCH_SHOW_EXTENDED_DN,
+                                         DSDB_SEARCH_NO_GLOBAL_CATALOG,
                                          "(&(objectClass=user)(samAccountName=krbtgt))");
 
                if (ldb_ret != LDB_SUCCESS) {