gpo: Apply kerberos settings
[nivanova/samba-autobuild/.git] / source4 / kdc / db-glue.c
index f43e8d918a60831e3c133b36e4b7b61b3af4a1b9..69c54b00c5ba82b9bf3b7b96013883db30bb3c00 100644 (file)
 #include "../lib/crypto/md4.h"
 #include "system/kerberos.h"
 #include "auth/kerberos/kerberos.h"
-#include <hdb.h>
+#include "kdc/sdb.h"
 #include "kdc/samba_kdc.h"
 #include "kdc/db-glue.h"
+#include "librpc/gen_ndr/ndr_irpc_c.h"
+#include "lib/messaging/irpc.h"
+
 
 #define SAMBA_KVNO_GET_KRBTGT(kvno) \
        ((uint16_t)(((uint32_t)kvno) >> 16))
@@ -65,8 +68,54 @@ static const char *trust_attrs[] = {
        NULL
 };
 
+/*
+  send a message to the drepl server telling it to initiate a
+  REPL_SECRET getncchanges extended op to fetch the users secrets
+ */
+static void auth_sam_trigger_repl_secret(TALLOC_CTX *mem_ctx,
+                                  struct imessaging_context *msg_ctx,
+                                  struct tevent_context *event_ctx,
+                                  struct ldb_dn *user_dn)
+{
+        struct dcerpc_binding_handle *irpc_handle;
+        struct drepl_trigger_repl_secret r;
+        struct tevent_req *req;
+        TALLOC_CTX *tmp_ctx;
+
+        tmp_ctx = talloc_new(mem_ctx);
+        if (tmp_ctx == NULL) {
+                return;
+        }
+
+        irpc_handle = irpc_binding_handle_by_name(tmp_ctx, msg_ctx,
+                                                  "dreplsrv",
+                                                  &ndr_table_irpc);
+        if (irpc_handle == NULL) {
+                DEBUG(1,(__location__ ": Unable to get binding handle for dreplsrv\n"));
+                TALLOC_FREE(tmp_ctx);
+                return;
+        }
+
+        r.in.user_dn = ldb_dn_get_linearized(user_dn);
+
+        /*
+         * This seem to rely on the current IRPC implementation,
+         * which delivers the message in the _send function.
+         *
+         * TODO: we need a ONE_WAY IRPC handle and register
+         * a callback and wait for it to be triggered!
+         */
+        req = dcerpc_drepl_trigger_repl_secret_r_send(tmp_ctx,
+                                                      event_ctx,
+                                                      irpc_handle,
+                                                      &r);
+
+        /* we aren't interested in a reply */
+        talloc_free(req);
+        TALLOC_FREE(tmp_ctx);
+}
 
-static KerberosTime ldb_msg_find_krb5time_ldap_time(struct ldb_message *msg, const char *attr, KerberosTime default_val)
+static time_t ldb_msg_find_krb5time_ldap_time(struct ldb_message *msg, const char *attr, time_t default_val)
 {
     const char *tmp;
     const char *gentime;
@@ -84,9 +133,9 @@ static KerberosTime ldb_msg_find_krb5time_ldap_time(struct ldb_message *msg, con
     return timegm(&tm);
 }
 
-static HDBFlags uf2HDBFlags(krb5_context context, uint32_t userAccountControl, enum samba_kdc_ent_type ent_type)
+static struct SDBFlags uf2SDBFlags(krb5_context context, uint32_t userAccountControl, enum samba_kdc_ent_type ent_type)
 {
-       HDBFlags flags = int2HDBFlags(0);
+       struct SDBFlags flags = int2SDBFlags(0);
 
        /* we don't allow kadmin deletes */
        flags.immutable = 1;
@@ -188,21 +237,75 @@ static HDBFlags uf2HDBFlags(krb5_context context, uint32_t userAccountControl, e
 
 static int samba_kdc_entry_destructor(struct samba_kdc_entry *p)
 {
-    hdb_entry_ex *entry_ex = p->entry_ex;
-    free_hdb_entry(&entry_ex->entry);
-    return 0;
+       if (p->entry_ex != NULL) {
+               struct sdb_entry_ex *entry_ex = p->entry_ex;
+               free_sdb_entry(&entry_ex->entry);
+       }
+
+       return 0;
 }
 
-static void samba_kdc_free_entry(krb5_context context, hdb_entry_ex *entry_ex)
+/*
+ * Sort keys in descending order of strength.
+ *
+ * Explanaton from Greg Hudson:
+ *
+ * To encrypt tickets only the first returned key is used by the MIT KDC.  The
+ * other keys just communicate support for session key enctypes, and aren't
+ * really used.  The encryption key for the ticket enc part doesn't have
+ * to be of a type requested by the client. The session key enctype is chosen
+ * based on the client preference order, limited by the set of enctypes present
+ * in the server keys (unless the string attribute is set on the server
+ * principal overriding that set).
+ */
+static int samba_kdc_sort_encryption_keys(struct sdb_entry_ex *entry_ex)
 {
-       /* this function is called only from hdb_free_entry().
-        * Make sure we neutralize the destructor or we will
-        * get a double free later when hdb_free_entry() will
-        * try to call free_hdb_entry() */
-       talloc_set_destructor(entry_ex->ctx, NULL);
-
-       /* now proceed to free the talloc part */
-       talloc_free(entry_ex->ctx);
+       unsigned int i, j, idx = 0;
+       static const krb5_enctype etype_list[] = {
+               ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+               ENCTYPE_AES128_CTS_HMAC_SHA1_96,
+               ENCTYPE_DES3_CBC_SHA1,
+               ENCTYPE_ARCFOUR_HMAC,
+               ENCTYPE_DES_CBC_MD5,
+               ENCTYPE_DES_CBC_MD4,
+               ENCTYPE_DES_CBC_CRC,
+               ENCTYPE_NULL
+       };
+       size_t etype_len = ARRAY_SIZE(etype_list);
+       size_t keys_size = entry_ex->entry.keys.len;
+       struct sdb_key *keys = entry_ex->entry.keys.val;
+       struct sdb_key *sorted_keys;
+
+       sorted_keys = calloc(keys_size, sizeof(struct sdb_key));
+       if (sorted_keys == NULL) {
+               return -1;
+       }
+
+       for (i = 0; i < etype_len; i++) {
+               for (j = 0; j < keys_size; j++) {
+                       const struct sdb_key skey = keys[j];
+
+                       if (idx == keys_size) {
+                               break;
+                       }
+
+                       if (KRB5_KEY_TYPE(&skey.key) == etype_list[i]) {
+                               sorted_keys[idx] = skey;
+                               idx++;
+                       }
+               }
+       }
+
+       /* Paranoia: Something went wrong during data copy */
+       if (idx != keys_size) {
+               free(sorted_keys);
+               return -1;
+       }
+
+       free(entry_ex->entry.keys.val);
+       entry_ex->entry.keys.val = sorted_keys;
+
+       return 0;
 }
 
 static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
@@ -213,7 +316,7 @@ static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
                                                    bool is_rodc,
                                                    uint32_t userAccountControl,
                                                    enum samba_kdc_ent_type ent_type,
-                                                   hdb_entry_ex *entry_ex)
+                                                   struct sdb_entry_ex *entry_ex)
 {
        krb5_error_code ret = 0;
        enum ndr_err_code ndr_err;
@@ -270,6 +373,81 @@ static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
 
        entry_ex->entry.keys.val = NULL;
        entry_ex->entry.keys.len = 0;
+       entry_ex->entry.kvno = 0;
+
+       if ((ent_type == SAMBA_KDC_ENT_TYPE_CLIENT)
+           && (userAccountControl & UF_SMARTCARD_REQUIRED)) {
+               uint8_t secretbuffer[32];
+
+               /*
+                * Fake keys until we have a better way to reject
+                * non-pkinit requests.
+                *
+                * We just need to indicate which encryption types are
+                * supported.
+                */
+               generate_secret_buffer(secretbuffer, sizeof(secretbuffer));
+
+               allocated_keys = 3;
+               entry_ex->entry.keys.len = 0;
+               entry_ex->entry.keys.val = calloc(allocated_keys, sizeof(struct sdb_key));
+               if (entry_ex->entry.keys.val == NULL) {
+                       ZERO_STRUCT(secretbuffer);
+                       ret = ENOMEM;
+                       goto out;
+               }
+
+               if (supported_enctypes & ENC_HMAC_SHA1_96_AES256) {
+                       struct sdb_key key = {};
+
+                       ret = smb_krb5_keyblock_init_contents(context,
+                                                             ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+                                                             secretbuffer, 32,
+                                                             &key.key);
+                       if (ret) {
+                               ZERO_STRUCT(secretbuffer);
+                               goto out;
+                       }
+
+                       entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key;
+                       entry_ex->entry.keys.len++;
+               }
+
+               if (supported_enctypes & ENC_HMAC_SHA1_96_AES128) {
+                       struct sdb_key key = {};
+
+                       ret = smb_krb5_keyblock_init_contents(context,
+                                                             ENCTYPE_AES128_CTS_HMAC_SHA1_96,
+                                                             secretbuffer, 16,
+                                                             &key.key);
+                       if (ret) {
+                               ZERO_STRUCT(secretbuffer);
+                               goto out;
+                       }
+
+                       entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key;
+                       entry_ex->entry.keys.len++;
+               }
+
+               if (supported_enctypes & ENC_RC4_HMAC_MD5) {
+                       struct sdb_key key = {};
+
+                       ret = smb_krb5_keyblock_init_contents(context,
+                                                             ENCTYPE_ARCFOUR_HMAC,
+                                                             secretbuffer, 16,
+                                                             &key.key);
+                       if (ret) {
+                               ZERO_STRUCT(secretbuffer);
+                               goto out;
+                       }
+
+                       entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key;
+                       entry_ex->entry.keys.len++;
+               }
+
+               ret = 0;
+               goto out;
+       }
 
        kvno = ldb_msg_find_attr_as_int(msg, "msDS-KeyVersionNumber", 0);
        if (is_rodc) {
@@ -298,9 +476,11 @@ static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
                }
 
                if (scb.sub.signature != SUPPLEMENTAL_CREDENTIALS_SIGNATURE) {
-                       NDR_PRINT_DEBUG(supplementalCredentialsBlob, &scb);
-                       ret = EINVAL;
-                       goto out;
+                       if (scb.sub.num_packages != 0) {
+                               NDR_PRINT_DEBUG(supplementalCredentialsBlob, &scb);
+                               ret = EINVAL;
+                               goto out;
+                       }
                }
 
                for (i=0; i < scb.sub.num_packages; i++) {
@@ -373,8 +553,9 @@ static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
        if (allocated_keys == 0) {
                if (kdc_db_ctx->rodc) {
                        /* We are on an RODC, but don't have keys for this account.  Signal this to the caller */
-                       /* TODO:  We need to call a generalised version of auth_sam_trigger_repl_secret from here */
-                       return HDB_ERR_NOT_FOUND_HERE;
+                       auth_sam_trigger_repl_secret(kdc_db_ctx, kdc_db_ctx->msg_ctx,
+                                                    kdc_db_ctx->ev_ctx, msg->dn);
+                       return SDB_ERR_NOT_FOUND_HERE;
                }
 
                /* oh, no password.  Apparently (comment in
@@ -385,17 +566,14 @@ static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
 
        /* allocate space to decode into */
        entry_ex->entry.keys.len = 0;
-       entry_ex->entry.keys.val = calloc(allocated_keys, sizeof(Key));
+       entry_ex->entry.keys.val = calloc(allocated_keys, sizeof(struct sdb_key));
        if (entry_ex->entry.keys.val == NULL) {
                ret = ENOMEM;
                goto out;
        }
 
        if (hash && (supported_enctypes & ENC_RC4_HMAC_MD5)) {
-               Key key;
-
-               key.mkvno = 0;
-               key.salt = NULL; /* No salt for this enc type */
+               struct sdb_key key = {};
 
                ret = smb_krb5_keyblock_init_contents(context,
                                                      ENCTYPE_ARCFOUR_HMAC,
@@ -412,7 +590,7 @@ static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
 
        if (pkb4) {
                for (i=0; i < pkb4->num_keys; i++) {
-                       Key key;
+                       struct sdb_key key = {};
 
                        if (!pkb4->keys[i].value) continue;
 
@@ -420,9 +598,6 @@ static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
                                continue;
                        }
 
-                       key.mkvno = 0;
-                       key.salt = NULL;
-
                        if (pkb4->salt.string) {
                                DATA_BLOB salt;
 
@@ -434,9 +609,11 @@ static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
                                        goto out;
                                }
 
-                               key.salt->type = hdb_pw_salt;
+                               key.salt->type = KRB5_PW_SALT;
 
-                               ret = krb5_data_copy(&key.salt->salt, salt.data, salt.length);
+                               ret = smb_krb5_copy_data_contents(&key.salt->salt,
+                                                                 salt.data,
+                                                                 salt.length);
                                if (ret) {
                                        free(key.salt);
                                        key.salt = NULL;
@@ -459,7 +636,7 @@ static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
                        }
                        if (ret) {
                                if (key.salt) {
-                                       free_Salt(key.salt);
+                                       smb_krb5_free_data_contents(context, &key.salt->salt);
                                        free(key.salt);
                                        key.salt = NULL;
                                }
@@ -471,7 +648,7 @@ static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
                }
        } else if (pkb3) {
                for (i=0; i < pkb3->num_keys; i++) {
-                       Key key;
+                       struct sdb_key key = {};
 
                        if (!pkb3->keys[i].value) continue;
 
@@ -479,9 +656,6 @@ static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
                                continue;
                        }
 
-                       key.mkvno = 0;
-                       key.salt = NULL;
-
                        if (pkb3->salt.string) {
                                DATA_BLOB salt;
 
@@ -493,9 +667,11 @@ static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
                                        goto out;
                                }
 
-                               key.salt->type = hdb_pw_salt;
+                               key.salt->type = KRB5_PW_SALT;
 
-                               ret = krb5_data_copy(&key.salt->salt, salt.data, salt.length);
+                               ret = smb_krb5_copy_data_contents(&key.salt->salt,
+                                                                 salt.data,
+                                                                 salt.length);
                                if (ret) {
                                        free(key.salt);
                                        key.salt = NULL;
@@ -510,7 +686,7 @@ static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
                                                              &key.key);
                        if (ret) {
                                if (key.salt) {
-                                       free_Salt(key.salt);
+                                       smb_krb5_free_data_contents(context, &key.salt->salt);
                                        free(key.salt);
                                        key.salt = NULL;
                                }
@@ -525,6 +701,13 @@ static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
 out:
        if (ret != 0) {
                entry_ex->entry.keys.len = 0;
+       } else if (entry_ex->entry.keys.len > 0 &&
+                  entry_ex->entry.keys.val != NULL) {
+               ret = samba_kdc_sort_encryption_keys(entry_ex);
+               if (ret != 0) {
+                       entry_ex->entry.keys.len = 0;
+                       ret = ENOMEM;
+               }
        }
        if (entry_ex->entry.keys.len == 0 && entry_ex->entry.keys.val) {
                free(entry_ex->entry.keys.val);
@@ -533,22 +716,76 @@ out:
        return ret;
 }
 
+static int principal_comp_strcmp_int(krb5_context context,
+                                    krb5_const_principal principal,
+                                    unsigned int component,
+                                    const char *string,
+                                    bool do_strcasecmp)
+{
+       const char *p;
+       size_t len;
+
+#if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING)
+       p = krb5_principal_get_comp_string(context, principal, component);
+       if (p == NULL) {
+               return -1;
+       }
+       len = strlen(p);
+#else
+       krb5_data *d;
+       if (component >= krb5_princ_size(context, principal)) {
+               return -1;
+       }
+
+       d = krb5_princ_component(context, principal, component);
+       if (d == NULL) {
+               return -1;
+       }
+
+       p = d->data;
+       len = d->length;
+#endif
+       if (do_strcasecmp) {
+               return strncasecmp(p, string, len);
+       } else {
+               return strncmp(p, string, len);
+       }
+}
+
+static int principal_comp_strcasecmp(krb5_context context,
+                                    krb5_const_principal principal,
+                                    unsigned int component,
+                                    const char *string)
+{
+       return principal_comp_strcmp_int(context, principal,
+                                        component, string, true);
+}
+
+static int principal_comp_strcmp(krb5_context context,
+                                krb5_const_principal principal,
+                                unsigned int component,
+                                const char *string)
+{
+       return principal_comp_strcmp_int(context, principal,
+                                        component, string, false);
+}
+
 /*
  * 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,
+                                              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 sdb_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;
 
@@ -580,16 +817,15 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
                is_computer = TRUE;
        }
 
-       memset(entry_ex, 0, sizeof(*entry_ex));
+       ZERO_STRUCTP(entry_ex);
 
-       p = talloc(mem_ctx, struct samba_kdc_entry);
+       p = talloc_zero(mem_ctx, struct samba_kdc_entry);
        if (!p) {
                ret = ENOMEM;
                goto out;
        }
 
        p->kdc_db_ctx = kdc_db_ctx;
-       p->entry_ex = entry_ex;
        p->realm_dn = talloc_reference(p, realm_dn);
        if (!p->realm_dn) {
                ret = ENOMEM;
@@ -598,11 +834,7 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
 
        talloc_set_destructor(p, samba_kdc_entry_destructor);
 
-       /* make sure we do not have bogus data in there */
-       memset(&entry_ex->entry, 0, sizeof(hdb_entry));
-
        entry_ex->ctx = p;
-       entry_ex->free_entry = samba_kdc_free_entry;
 
        userAccountControl = ldb_msg_find_attr_as_uint(msg, "userAccountControl", 0);
 
@@ -638,9 +870,8 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
         * fixed UPPER case realm, but the as-sent username
         */
 
-       entry_ex->entry.principal = malloc(sizeof(*(entry_ex->entry.principal)));
        if (ent_type == SAMBA_KDC_ENT_TYPE_KRBTGT) {
-               if (flags & (HDB_F_CANON)) {
+               if (flags & (SDB_F_CANON)) {
                        /*
                         * When requested to do so, ensure that the
                         * both realm values in the principal are set
@@ -653,7 +884,7 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
                                krb5_clear_error_message(context);
                                goto out;
                        }
-                       krb5_principal_set_type(context, entry_ex->entry.principal, KRB5_NT_SRV_INST);
+                       smb_krb5_principal_set_type(context, entry_ex->entry.principal, KRB5_NT_SRV_INST);
                } else {
                        ret = krb5_copy_principal(context, principal, &entry_ex->entry.principal);
                        if (ret) {
@@ -677,9 +908,9 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
                        krb5_clear_error_message(context);
                        goto out;
                }
-       } else if (flags & HDB_F_CANON && flags & HDB_F_FOR_AS_REQ) {
+       } else if (flags & SDB_F_CANON && flags & SDB_F_FOR_AS_REQ) {
                /*
-                * HDB_F_CANON maps from the canonicalize flag in the
+                * SDB_F_CANON maps from the canonicalize flag in the
                 * packet, and has a different meaning between AS-REQ
                 * and TGS-REQ.  We only change the principal in the AS-REQ case
                 */
@@ -695,7 +926,7 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
                        goto out;
                }
 
-               if (krb5_principal_get_type(context, principal) != KRB5_NT_ENTERPRISE_PRINCIPAL) {
+               if (smb_krb5_principal_get_type(context, principal) != KRB5_NT_ENTERPRISE_PRINCIPAL) {
                        /* While we have copied the client principal, tests
                         * show that Win2k3 returns the 'corrected' realm, not
                         * the client-specified realm.  This code attempts to
@@ -712,7 +943,7 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
        }
 
        /* First try and figure out the flags based on the userAccountControl */
-       entry_ex->entry.flags = uf2HDBFlags(context, userAccountControl, ent_type);
+       entry_ex->entry.flags = uf2SDBFlags(context, userAccountControl, ent_type);
 
        /* Windows 2008 seems to enforce this (very sensible) rule by
         * default - don't allow offline attacks on a user's password
@@ -733,11 +964,11 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
         * KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
         */
        if (ent_type == SAMBA_KDC_ENT_TYPE_SERVER && entry_ex->entry.flags.server == 0) {
-               ret = HDB_ERR_NOENTRY;
+               ret = SDB_ERR_NOENTRY;
                krb5_set_error_message(context, ret, "samba_kdc_message2entry: no servicePrincipalName present for this server, refusing with no-such-entry");
                goto out;
        }
-       if (flags & HDB_F_ADMIN_DATA) {
+       if (flags & SDB_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
@@ -755,7 +986,7 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
                        goto out;
                }
 
-               entry_ex->entry.modified_by = (Event *) malloc(sizeof(Event));
+               entry_ex->entry.modified_by = (struct sdb_event *) malloc(sizeof(struct sdb_event));
                if (entry_ex->entry.modified_by == NULL) {
                        ret = ENOMEM;
                        krb5_set_error_message(context, ret, "malloc: out of memory");
@@ -805,8 +1036,8 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
                 * Instead, only do it when request is for the kpasswd service */
                if (ent_type == SAMBA_KDC_ENT_TYPE_SERVER
                    && krb5_princ_size(context, principal) == 2
-                   && (strcmp(principal->name.name_string.val[0], "kadmin") == 0)
-                   && (strcmp(principal->name.name_string.val[1], "changepw") == 0)
+                   && (principal_comp_strcmp(context, principal, 0, "kadmin") == 0)
+                   && (principal_comp_strcmp(context, principal, 1, "changepw") == 0)
                    && lpcfg_is_my_domain_or_realm(lp_ctx, realm)) {
                        entry_ex->entry.flags.change_pw = 1;
                }
@@ -845,8 +1076,9 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
 
        } else {
                NTTIME must_change_time
-                       = samdb_result_force_password_change(kdc_db_ctx->samdb, mem_ctx,
-                                                            realm_dn, msg);
+                       = samdb_result_nttime(msg,
+                                       "msDS-UserPasswordExpiryTimeComputed",
+                                       0);
                if (must_change_time == 0x7FFFFFFFFFFFFFFFULL) {
                        entry_ex->entry.pw_end = NULL;
                } else {
@@ -901,34 +1133,17 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
                                           rid, is_rodc, userAccountControl,
                                           ent_type, entry_ex);
        if (ret) {
-               /* Could be bougus data in the entry, or out of memory */
-               goto out;
-       }
-
-       entry_ex->entry.etypes = malloc(sizeof(*(entry_ex->entry.etypes)));
-       if (entry_ex->entry.etypes == NULL) {
-               krb5_clear_error_message(context);
-               ret = ENOMEM;
-               goto out;
-       }
-       entry_ex->entry.etypes->len = entry_ex->entry.keys.len;
-       entry_ex->entry.etypes->val = calloc(entry_ex->entry.etypes->len, sizeof(int));
-       if (entry_ex->entry.etypes->val == NULL) {
-               krb5_clear_error_message(context);
-               ret = ENOMEM;
+               /* Could be bogus data in the entry, or out of memory */
                goto out;
        }
-       for (i=0; i < entry_ex->entry.etypes->len; i++) {
-               entry_ex->entry.etypes->val[i] = entry_ex->entry.keys.val[i].key.keytype;
-       }
-
 
        p->msg = talloc_steal(p, msg);
 
 out:
        if (ret != 0) {
                /* This doesn't free ent itself, that is for the eventual caller to do */
-               hdb_free_entry(context, entry_ex);
+               sdb_free_entry(entry_ex);
+               ZERO_STRUCTP(entry_ex);
        } else {
                talloc_steal(kdc_db_ctx, entry_ex->ctx);
        }
@@ -948,11 +1163,14 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
                                               unsigned flags,
                                               uint32_t kvno,
                                               struct ldb_message *msg,
-                                              hdb_entry_ex *entry_ex)
+                                              struct sdb_entry_ex *entry_ex)
 {
        struct loadparm_context *lp_ctx = kdc_db_ctx->lp_ctx;
-       const char *dnsdomain;
-       const char *realm = lpcfg_realm(lp_ctx);
+       const char *our_realm = lpcfg_realm(lp_ctx);
+       const char *dnsdomain = NULL;
+       char *partner_realm = NULL;
+       const char *realm = NULL;
+       const char *krbtgt_realm = NULL;
        DATA_BLOB password_utf16 = data_blob_null;
        DATA_BLOB password_utf8 = data_blob_null;
        struct samr_Password _password_hash;
@@ -960,13 +1178,18 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
        const struct ldb_val *password_val;
        struct trustAuthInOutBlob password_blob;
        struct samba_kdc_entry *p;
-       bool use_previous;
+       bool use_previous = false;
        uint32_t current_kvno;
+       uint32_t previous_kvno;
        uint32_t num_keys = 0;
        enum ndr_err_code ndr_err;
        int ret, trust_direction_flags;
        unsigned int i;
        struct AuthenticationInformationArray *auth_array;
+       struct timeval tv;
+       NTTIME an_hour_ago;
+       uint32_t *auth_kvno;
+       bool preferr_current = false;
        uint32_t supported_enctypes = ENC_RC4_HMAC_MD5;
 
        if (dsdb_functional_level(kdc_db_ctx->samdb) >= DS_DOMAIN_FUNCTION_2008) {
@@ -976,20 +1199,40 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
        }
 
        trust_direction_flags = ldb_msg_find_attr_as_int(msg, "trustDirection", 0);
+       if (!(trust_direction_flags & direction)) {
+               krb5_clear_error_message(context);
+               ret = SDB_ERR_NOENTRY;
+               goto out;
+       }
+
+       dnsdomain = ldb_msg_find_attr_as_string(msg, "trustPartner", NULL);
+       if (dnsdomain == NULL) {
+               krb5_clear_error_message(context);
+               ret = SDB_ERR_NOENTRY;
+               goto out;
+       }
+       partner_realm = strupper_talloc(mem_ctx, dnsdomain);
+       if (partner_realm == NULL) {
+               krb5_clear_error_message(context);
+               ret = ENOMEM;
+               goto out;
+       }
 
        if (direction == INBOUND) {
-               password_val = ldb_msg_find_ldb_val(msg, "trustAuthIncoming");
+               realm = our_realm;
+               krbtgt_realm = partner_realm;
 
+               password_val = ldb_msg_find_ldb_val(msg, "trustAuthIncoming");
        } else { /* OUTBOUND */
-               dnsdomain = ldb_msg_find_attr_as_string(msg, "trustPartner", NULL);
-               /* replace realm */
-               realm = strupper_talloc(mem_ctx, dnsdomain);
+               realm = partner_realm;
+               krbtgt_realm = our_realm;
+
                password_val = ldb_msg_find_ldb_val(msg, "trustAuthOutgoing");
        }
 
-       if (!password_val || !(trust_direction_flags & direction)) {
+       if (password_val == NULL) {
                krb5_clear_error_message(context);
-               ret = HDB_ERR_NOENTRY;
+               ret = SDB_ERR_NOENTRY;
                goto out;
        }
 
@@ -1008,16 +1251,14 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
        }
 
        p->kdc_db_ctx = kdc_db_ctx;
-       p->entry_ex = entry_ex;
        p->realm_dn = realm_dn;
 
        talloc_set_destructor(p, samba_kdc_entry_destructor);
 
        /* make sure we do not have bogus data in there */
-       memset(&entry_ex->entry, 0, sizeof(hdb_entry));
+       memset(&entry_ex->entry, 0, sizeof(struct sdb_entry));
 
        entry_ex->ctx = p;
-       entry_ex->free_entry = samba_kdc_free_entry;
 
        /* use 'whenCreated' */
        entry_ex->entry.created_by.time = ldb_msg_find_krb5time_ldap_time(msg, "whenCreated", 0);
@@ -1030,25 +1271,18 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
                goto out;
        }
 
-       ret = krb5_copy_principal(context, 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
+        * We always need to generate the canonicalized principal
+        * with the values of our database.
         */
-
-       ret = smb_krb5_principal_set_realm(context, entry_ex->entry.principal, realm);
+       ret = smb_krb5_make_principal(context, &entry_ex->entry.principal, realm,
+                                     "krbtgt", krbtgt_realm, NULL);
        if (ret) {
                krb5_clear_error_message(context);
                goto out;
        }
+       smb_krb5_principal_set_type(context, entry_ex->entry.principal,
+                                   KRB5_NT_SRV_INST);
 
        entry_ex->entry.valid_start = NULL;
 
@@ -1060,11 +1294,40 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
         * then we use the previous substrucure.
         */
 
+       /*
+        * Windows preferrs the previous key for one hour.
+        */
+       tv = timeval_current();
+       if (tv.tv_sec > 3600) {
+               tv.tv_sec -= 3600;
+       }
+       an_hour_ago = timeval_to_nttime(&tv);
+
        /* 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) {
-                       current_kvno = password_blob.current.array[i].AuthInfo.version.version;
+               struct AuthenticationInformation *a =
+                       &password_blob.current.array[i];
+
+               if (a->LastUpdateTime <= an_hour_ago) {
+                       preferr_current = true;
+               }
+
+               if (a->AuthType == TRUST_AUTH_TYPE_VERSION) {
+                       current_kvno = a->AuthInfo.version.version;
+               }
+       }
+       if (current_kvno == 0) {
+               previous_kvno = 255;
+       } else {
+               previous_kvno = current_kvno - 1;
+       }
+       for (i=0; i < password_blob.count; i++) {
+               struct AuthenticationInformation *a =
+                       &password_blob.previous.array[i];
+
+               if (a->AuthType == TRUST_AUTH_TYPE_VERSION) {
+                       previous_kvno = a->AuthInfo.version.version;
                }
        }
 
@@ -1073,31 +1336,48 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
        if (password_blob.previous.count == 0) {
                /* there is no previous password */
                use_previous = false;
-       } else if (!(flags & HDB_F_KVNO_SPECIFIED) ||
-           kvno == current_kvno) {
+       } else if (!(flags & SDB_F_KVNO_SPECIFIED)) {
+               /*
+                * If not specified we use the lowest kvno
+                * for the first hour after an update.
+                */
+               if (preferr_current) {
+                       use_previous = false;
+               } else if (previous_kvno < current_kvno) {
+                       use_previous = true;
+               } else {
+                       use_previous = false;
+               }
+       } else if (kvno == current_kvno) {
+               /*
+                * Exact match ...
+                */
                use_previous = false;
-       } else if ((kvno+1 == current_kvno) ||
-                  (kvno == 255 && current_kvno == 0)) {
+       } else if (kvno == previous_kvno) {
+               /*
+                * Exact match ...
+                */
                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;
+               /*
+                * Fallback to the current one for anything else
+                */
+               use_previous = false;
        }
 
        if (use_previous) {
                auth_array = &password_blob.previous;
+               auth_kvno = &previous_kvno;
        } else {
                auth_array = &password_blob.current;
+               auth_kvno = &current_kvno;
        }
 
        /* use the kvno the client specified, if available */
-       if (flags & HDB_F_KVNO_SPECIFIED) {
+       if (flags & SDB_F_KVNO_SPECIFIED) {
                entry_ex->entry.kvno = kvno;
        } else {
-               entry_ex->entry.kvno = current_kvno;
+               entry_ex->entry.kvno = *auth_kvno;
        }
 
        for (i=0; i < auth_array->count; i++) {
@@ -1153,11 +1433,11 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
        if (num_keys == 0) {
                DEBUG(1,(__location__ ": no usable key found\n"));
                krb5_clear_error_message(context);
-               ret = HDB_ERR_NOENTRY;
+               ret = SDB_ERR_NOENTRY;
                goto out;
        }
 
-       entry_ex->entry.keys.val = calloc(num_keys, sizeof(Key));
+       entry_ex->entry.keys.val = calloc(num_keys, sizeof(struct sdb_key));
        if (entry_ex->entry.keys.val == NULL) {
                krb5_clear_error_message(context);
                ret = ENOMEM;
@@ -1165,29 +1445,30 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
        }
 
        if (password_utf8.length != 0) {
-               Key key = {};
-               krb5_const_principal salt_principal = principal;
-               krb5_salt salt;
+               struct sdb_key key = {};
+               krb5_const_principal salt_principal = entry_ex->entry.principal;
+               krb5_data salt;
                krb5_data cleartext_data;
 
-               cleartext_data.data = password_utf8.data;
+               cleartext_data.data = discard_const_p(char, password_utf8.data);
                cleartext_data.length = password_utf8.length;
 
-               ret = krb5_get_pw_salt(context,
-                                      salt_principal,
-                                      &salt);
+               ret = smb_krb5_get_pw_salt(context,
+                                          salt_principal,
+                                          &salt);
                if (ret != 0) {
                        goto out;
                }
 
                if (supported_enctypes & ENC_HMAC_SHA1_96_AES256) {
-                       ret = krb5_string_to_key_data_salt(context,
-                                                          ENCTYPE_AES256_CTS_HMAC_SHA1_96,
-                                                          cleartext_data,
-                                                          salt,
-                                                          &key.key);
+                       ret = smb_krb5_create_key_from_string(context,
+                                                             salt_principal,
+                                                             &salt,
+                                                             &cleartext_data,
+                                                             ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+                                                             &key.key);
                        if (ret != 0) {
-                               krb5_free_salt(context, salt);
+                               smb_krb5_free_data_contents(context, &salt);
                                goto out;
                        }
 
@@ -1196,13 +1477,14 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
                }
 
                if (supported_enctypes & ENC_HMAC_SHA1_96_AES128) {
-                       ret = krb5_string_to_key_data_salt(context,
-                                                          ENCTYPE_AES128_CTS_HMAC_SHA1_96,
-                                                          cleartext_data,
-                                                          salt,
-                                                          &key.key);
+                       ret = smb_krb5_create_key_from_string(context,
+                                                             salt_principal,
+                                                             &salt,
+                                                             &cleartext_data,
+                                                             ENCTYPE_AES128_CTS_HMAC_SHA1_96,
+                                                             &key.key);
                        if (ret != 0) {
-                               krb5_free_salt(context, salt);
+                               smb_krb5_free_data_contents(context, &salt);
                                goto out;
                        }
 
@@ -1210,11 +1492,11 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
                        entry_ex->entry.keys.len++;
                }
 
-               krb5_free_salt(context, salt);
+               smb_krb5_free_data_contents(context, &salt);
        }
 
        if (password_hash != NULL) {
-               Key key = {};
+               struct sdb_key key = {};
 
                ret = smb_krb5_keyblock_init_contents(context,
                                                      ENCTYPE_ARCFOUR_HMAC,
@@ -1229,7 +1511,7 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
                entry_ex->entry.keys.len++;
        }
 
-       entry_ex->entry.flags = int2HDBFlags(0);
+       entry_ex->entry.flags = int2SDBFlags(0);
        entry_ex->entry.flags.immutable = 1;
        entry_ex->entry.flags.invalid = 0;
        entry_ex->entry.flags.server = 1;
@@ -1241,30 +1523,21 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
 
        entry_ex->entry.max_renew = NULL;
 
-       entry_ex->entry.etypes = malloc(sizeof(*(entry_ex->entry.etypes)));
-       if (entry_ex->entry.etypes == NULL) {
-               krb5_clear_error_message(context);
-               ret = ENOMEM;
-               goto out;
-       }
-       entry_ex->entry.etypes->len = entry_ex->entry.keys.len;
-       entry_ex->entry.etypes->val = calloc(entry_ex->entry.etypes->len, sizeof(int));
-       if (entry_ex->entry.etypes->val == NULL) {
+       ret = samba_kdc_sort_encryption_keys(entry_ex);
+       if (ret != 0) {
                krb5_clear_error_message(context);
                ret = ENOMEM;
                goto out;
        }
-       for (i=0; i < entry_ex->entry.etypes->len; i++) {
-               entry_ex->entry.etypes->val[i] = entry_ex->entry.keys.val[i].key.keytype;
-       }
-
 
        p->msg = talloc_steal(p, msg);
 
 out:
+       TALLOC_FREE(partner_realm);
+
        if (ret != 0) {
                /* This doesn't free ent itself, that is for the eventual caller to do */
-               hdb_free_entry(context, entry_ex);
+               sdb_free_entry(entry_ex);
        } else {
                talloc_steal(kdc_db_ctx, entry_ex->ctx);
        }
@@ -1282,13 +1555,12 @@ static krb5_error_code samba_kdc_lookup_trust(krb5_context context, struct ldb_c
        NTSTATUS status;
        const char * const *attrs = trust_attrs;
 
-       status = sam_get_results_trust(ldb_ctx,
-                                      mem_ctx, realm, realm, attrs,
-                                      pmsg);
+       status = dsdb_trust_search_tdo(ldb_ctx, realm, realm,
+                                      attrs, mem_ctx, 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_OBJECT_NAME_NOT_FOUND)) {
+               return SDB_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");
@@ -1306,34 +1578,125 @@ static krb5_error_code samba_kdc_lookup_client(krb5_context context,
                                                krb5_const_principal principal,
                                                const char **attrs,
                                                struct ldb_dn **realm_dn,
-                                               struct ldb_message **msg) {
+                                               struct ldb_message **msg)
+{
        NTSTATUS nt_status;
-       char *principal_string;
+       char *principal_string = NULL;
 
-       if (krb5_principal_get_type(context, principal) == KRB5_NT_ENTERPRISE_PRINCIPAL) {
+       if (smb_krb5_principal_get_type(context, principal) == KRB5_NT_ENTERPRISE_PRINCIPAL) {
                principal_string = smb_krb5_principal_get_comp_string(mem_ctx, context,
                                                                      principal, 0);
                if (principal_string == NULL) {
                        return ENOMEM;
                }
-               nt_status = sam_get_results_principal(kdc_db_ctx->samdb,
-                                                     mem_ctx, principal_string, attrs,
-                                                     realm_dn, msg);
-               TALLOC_FREE(principal_string);
        } else {
+               char *principal_string_m = NULL;
                krb5_error_code ret;
-               ret = krb5_unparse_name(context, principal, &principal_string);
+
+               ret = krb5_unparse_name(context, principal, &principal_string_m);
                if (ret != 0) {
                        return ret;
                }
-               nt_status = sam_get_results_principal(kdc_db_ctx->samdb,
-                                                     mem_ctx, principal_string, attrs,
-                                                     realm_dn, msg);
-               free(principal_string);
+
+               principal_string = talloc_strdup(mem_ctx, principal_string_m);
+               SAFE_FREE(principal_string_m);
+               if (principal_string == NULL) {
+                       return ENOMEM;
+               }
+       }
+
+       nt_status = sam_get_results_principal(kdc_db_ctx->samdb,
+                                             mem_ctx, principal_string, attrs,
+                                             realm_dn, msg);
+       if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER)) {
+               krb5_principal fallback_principal = NULL;
+               unsigned int num_comp;
+               char *fallback_realm = NULL;
+               char *fallback_account = NULL;
+               krb5_error_code ret;
+
+               ret = krb5_parse_name(context, principal_string,
+                                     &fallback_principal);
+               TALLOC_FREE(principal_string);
+               if (ret != 0) {
+                       return ret;
+               }
+
+               num_comp = krb5_princ_size(context, fallback_principal);
+               fallback_realm = smb_krb5_principal_get_realm(context,
+                                                             fallback_principal);
+               if (fallback_realm == NULL) {
+                       krb5_free_principal(context, fallback_principal);
+                       return ENOMEM;
+               }
+
+               if (num_comp == 1) {
+                       size_t len;
+
+                       fallback_account = smb_krb5_principal_get_comp_string(mem_ctx,
+                                               context, fallback_principal, 0);
+                       if (fallback_account == NULL) {
+                               krb5_free_principal(context, fallback_principal);
+                               SAFE_FREE(fallback_realm);
+                               return ENOMEM;
+                       }
+
+                       len = strlen(fallback_account);
+                       if (len >= 2 && fallback_account[len - 1] == '$') {
+                               TALLOC_FREE(fallback_account);
+                       }
+               }
+               krb5_free_principal(context, fallback_principal);
+               fallback_principal = NULL;
+
+               if (fallback_account != NULL) {
+                       char *with_dollar;
+
+                       with_dollar = talloc_asprintf(mem_ctx, "%s$",
+                                                    fallback_account);
+                       if (with_dollar == NULL) {
+                               SAFE_FREE(fallback_realm);
+                               return ENOMEM;
+                       }
+                       TALLOC_FREE(fallback_account);
+
+                       ret = smb_krb5_make_principal(context,
+                                                     &fallback_principal,
+                                                     fallback_realm,
+                                                     with_dollar, NULL);
+                       TALLOC_FREE(with_dollar);
+                       if (ret != 0) {
+                               SAFE_FREE(fallback_realm);
+                               return ret;
+                       }
+               }
+               SAFE_FREE(fallback_realm);
+
+               if (fallback_principal != NULL) {
+                       char *fallback_string = NULL;
+
+                       ret = krb5_unparse_name(context,
+                                               fallback_principal,
+                                               &fallback_string);
+                       if (ret != 0) {
+                               krb5_free_principal(context, fallback_principal);
+                               return ret;
+                       }
+
+                       nt_status = sam_get_results_principal(kdc_db_ctx->samdb,
+                                                             mem_ctx,
+                                                             fallback_string,
+                                                             attrs,
+                                                             realm_dn, msg);
+                       SAFE_FREE(fallback_string);
+               }
+               krb5_free_principal(context, fallback_principal);
+               fallback_principal = NULL;
        }
+       TALLOC_FREE(principal_string);
 
        if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER)) {
-               return HDB_ERR_NOENTRY;
+               return SDB_ERR_NOENTRY;
        } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_MEMORY)) {
                return ENOMEM;
        } else if (!NT_STATUS_IS_OK(nt_status)) {
@@ -1348,7 +1711,7 @@ static krb5_error_code samba_kdc_fetch_client(krb5_context context,
                                               TALLOC_CTX *mem_ctx,
                                               krb5_const_principal principal,
                                               unsigned flags,
-                                              hdb_entry_ex *entry_ex) {
+                                              struct sdb_entry_ex *entry_ex) {
        struct ldb_dn *realm_dn;
        krb5_error_code ret;
        struct ldb_message *msg = NULL;
@@ -1373,36 +1736,36 @@ static krb5_error_code samba_kdc_fetch_krbtgt(krb5_context context,
                                              krb5_const_principal principal,
                                              unsigned flags,
                                              uint32_t kvno,
-                                             hdb_entry_ex *entry_ex)
+                                             struct sdb_entry_ex *entry_ex)
 {
        struct loadparm_context *lp_ctx = kdc_db_ctx->lp_ctx;
        krb5_error_code ret;
        struct ldb_message *msg = NULL;
        struct ldb_dn *realm_dn = ldb_get_default_basedn(kdc_db_ctx->samdb);
        char *realm_from_princ, *realm_from_princ_malloc;
-       krb5_principal alloc_principal = NULL;
+       char *realm_princ_comp = smb_krb5_principal_get_comp_string(mem_ctx, context, principal, 1);
 
        realm_from_princ_malloc = smb_krb5_principal_get_realm(context, principal);
        if (realm_from_princ_malloc == NULL) {
                /* can't happen */
-               return HDB_ERR_NOENTRY;
+               return SDB_ERR_NOENTRY;
        }
        realm_from_princ = talloc_strdup(mem_ctx, realm_from_princ_malloc);
        free(realm_from_princ_malloc);
        if (realm_from_princ == NULL) {
-               return HDB_ERR_NOENTRY;
+               return SDB_ERR_NOENTRY;
        }
 
        if (krb5_princ_size(context, principal) != 2
-           || (strcmp(principal->name.name_string.val[0], KRB5_TGS_NAME) != 0)) {
+           || (principal_comp_strcmp(context, principal, 0, KRB5_TGS_NAME) != 0)) {
                /* Not a krbtgt */
-               return HDB_ERR_NOENTRY;
+               return SDB_ERR_NOENTRY;
        }
 
        /* krbtgt case.  Either us or a trusted realm */
 
        if (lpcfg_is_my_domain_or_realm(lp_ctx, realm_from_princ)
-           && lpcfg_is_my_domain_or_realm(lp_ctx, principal->name.name_string.val[1])) {
+           && lpcfg_is_my_domain_or_realm(lp_ctx, realm_princ_comp)) {
                /* us, or someone quite like us */
                /* Cludge, cludge cludge.  If the realm part of krbtgt/realm,
                 * is in our db, then direct the caller at our primary
@@ -1413,11 +1776,11 @@ static krb5_error_code samba_kdc_fetch_krbtgt(krb5_context context,
                /* 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) {
+               if (flags & SDB_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;
+                                       return SDB_ERR_NOT_FOUND_HERE;
                                }
                        }
                } else {
@@ -1443,26 +1806,22 @@ static krb5_error_code samba_kdc_fetch_krbtgt(krb5_context context,
                if (lret == LDB_ERR_NO_SUCH_OBJECT) {
                        krb5_warnx(context, "samba_kdc_fetch: could not find KRBTGT number %u in DB!",
                                   (unsigned)(krbtgt_number));
-                       krb5_set_error_message(context, HDB_ERR_NOENTRY,
+                       krb5_set_error_message(context, SDB_ERR_NOENTRY,
                                               "samba_kdc_fetch: could not find KRBTGT number %u in DB!",
                                               (unsigned)(krbtgt_number));
-                       return HDB_ERR_NOENTRY;
+                       return SDB_ERR_NOENTRY;
                } else if (lret != LDB_SUCCESS) {
                        krb5_warnx(context, "samba_kdc_fetch: could not find KRBTGT number %u in DB!",
                                   (unsigned)(krbtgt_number));
-                       krb5_set_error_message(context, HDB_ERR_NOENTRY,
+                       krb5_set_error_message(context, SDB_ERR_NOENTRY,
                                               "samba_kdc_fetch: could not find KRBTGT number %u in DB!",
                                               (unsigned)(krbtgt_number));
-                       return HDB_ERR_NOENTRY;
+                       return SDB_ERR_NOENTRY;
                }
 
                ret = samba_kdc_message2entry(context, kdc_db_ctx, mem_ctx,
                                              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");
                }
@@ -1477,19 +1836,19 @@ static krb5_error_code samba_kdc_fetch_krbtgt(krb5_context context,
                if (strcasecmp(lpcfg_realm(lp_ctx), realm_from_princ) == 0) {
                        /* look for inbound trust */
                        direction = INBOUND;
-                       realm = principal->name.name_string.val[1];
-               } else if (strcasecmp(lpcfg_realm(lp_ctx), principal->name.name_string.val[1]) == 0) {
+                       realm = realm_princ_comp;
+               } else if (principal_comp_strcasecmp(context, principal, 1, lpcfg_realm(lp_ctx)) == 0) {
                        /* look for outbound trust */
                        direction = OUTBOUND;
                        realm = realm_from_princ;
                } else {
                        krb5_warnx(context, "samba_kdc_fetch: not our realm for trusts ('%s', '%s')",
                                   realm_from_princ,
-                                  principal->name.name_string.val[1]);
-                       krb5_set_error_message(context, HDB_ERR_NOENTRY, "samba_kdc_fetch: not our realm for trusts ('%s', '%s')",
+                                  realm_princ_comp);
+                       krb5_set_error_message(context, SDB_ERR_NOENTRY, "samba_kdc_fetch: not our realm for trusts ('%s', '%s')",
                                               realm_from_princ,
-                                              principal->name.name_string.val[1]);
-                       return HDB_ERR_NOENTRY;
+                                              realm_princ_comp);
+                       return SDB_ERR_NOENTRY;
                }
 
                /* Trusted domains are under CN=system */
@@ -1553,7 +1912,7 @@ static krb5_error_code samba_kdc_lookup_server(krb5_context context,
                free(principal_string);
 
                if (!NT_STATUS_IS_OK(nt_status)) {
-                       return HDB_ERR_NOENTRY;
+                       return SDB_ERR_NOENTRY;
                }
 
                ldb_ret = dsdb_search_one(kdc_db_ctx->samdb,
@@ -1563,10 +1922,10 @@ static krb5_error_code samba_kdc_lookup_server(krb5_context context,
                                          DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
                                          "(objectClass=*)");
                if (ldb_ret != LDB_SUCCESS) {
-                       return HDB_ERR_NOENTRY;
+                       return SDB_ERR_NOENTRY;
                }
                return 0;
-       } else if (!(flags & HDB_F_FOR_AS_REQ)
+       } else if (!(flags & SDB_F_FOR_AS_REQ)
                   && smb_krb5_principal_get_type(context, principal) == KRB5_NT_ENTERPRISE_PRINCIPAL) {
                /*
                 * The behaviour of accepting an
@@ -1590,9 +1949,14 @@ static krb5_error_code samba_kdc_lookup_server(krb5_context context,
                 */
                int lret;
                char *short_princ;
-               krb5_principal enterprise_prinicpal = NULL;
+               krb5_principal enterprise_principal = NULL;
+               krb5_const_principal used_principal = NULL;
+               char *name1 = NULL;
+               size_t len1 = 0;
+               char *filter = NULL;
 
                if (smb_krb5_principal_get_type(context, principal) == KRB5_NT_ENTERPRISE_PRINCIPAL) {
+                       char *str = NULL;
                        /* Need to reparse the enterprise principal to find the real target */
                        if (krb5_princ_size(context, principal) != 1) {
                                ret = KRB5_PARSE_MALFORMED;
@@ -1601,13 +1965,19 @@ static krb5_error_code samba_kdc_lookup_server(krb5_context context,
                                                       krb5_princ_size(context, principal));
                                return ret;
                        }
-                       ret = krb5_parse_name(context, principal->name.name_string.val[0],
-                                             &enterprise_prinicpal);
+                       str = smb_krb5_principal_get_comp_string(mem_ctx, context, principal, 0);
+                       if (str == NULL) {
+                               return KRB5_PARSE_MALFORMED;
+                       }
+                       ret = krb5_parse_name(context, str,
+                                             &enterprise_principal);
+                       talloc_free(str);
                        if (ret) {
-                               talloc_free(mem_ctx);
                                return ret;
                        }
-                       principal = enterprise_prinicpal;
+                       used_principal = enterprise_principal;
+               } else {
+                       used_principal = principal;
                }
 
                /* server as client principal case, but we must not lookup userPrincipalNames */
@@ -1615,10 +1985,13 @@ static krb5_error_code samba_kdc_lookup_server(krb5_context context,
 
                /* TODO: Check if it is our realm, otherwise give referral */
 
-               ret = krb5_unparse_name_flags(context, principal,
+               ret = krb5_unparse_name_flags(context, used_principal,
                                              KRB5_PRINCIPAL_UNPARSE_NO_REALM |
                                              KRB5_PRINCIPAL_UNPARSE_DISPLAY,
                                              &short_princ);
+               used_principal = NULL;
+               krb5_free_principal(context, enterprise_principal);
+               enterprise_principal = NULL;
 
                if (ret != 0) {
                        krb5_set_error_message(context, ret, "samba_kdc_lookup_principal: could not parse principal");
@@ -1626,27 +1999,51 @@ static krb5_error_code samba_kdc_lookup_server(krb5_context context,
                        return ret;
                }
 
+               name1 = ldb_binary_encode_string(mem_ctx, short_princ);
+               SAFE_FREE(short_princ);
+               if (name1 == NULL) {
+                       return ENOMEM;
+               }
+               len1 = strlen(name1);
+               if (len1 >= 1 && name1[len1 - 1] != '$') {
+                       filter = talloc_asprintf(mem_ctx,
+                                       "(&(objectClass=user)(|(samAccountName=%s)(samAccountName=%s$)))",
+                                       name1, name1);
+                       if (filter == NULL) {
+                               return ENOMEM;
+                       }
+               } else {
+                       filter = talloc_asprintf(mem_ctx,
+                                       "(&(objectClass=user)(samAccountName=%s))",
+                                       name1);
+                       if (filter == NULL) {
+                               return ENOMEM;
+                       }
+               }
+
                lret = dsdb_search_one(kdc_db_ctx->samdb, mem_ctx, msg,
                                       *realm_dn, LDB_SCOPE_SUBTREE,
                                       attrs,
                                       DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
-                                      "(&(objectClass=user)(samAccountName=%s))",
-                                      ldb_binary_encode_string(mem_ctx, short_princ));
+                                      "%s", filter);
                if (lret == LDB_ERR_NO_SUCH_OBJECT) {
-                       DEBUG(3, ("Failed to find an entry for %s\n", short_princ));
-                       free(short_princ);
-                       return HDB_ERR_NOENTRY;
+                       DEBUG(10, ("Failed to find an entry for %s filter:%s\n",
+                                 name1, filter));
+                       return SDB_ERR_NOENTRY;
+               }
+               if (lret == LDB_ERR_CONSTRAINT_VIOLATION) {
+                       DEBUG(10, ("Failed to find unique entry for %s filter:%s\n",
+                                 name1, filter));
+                       return SDB_ERR_NOENTRY;
                }
                if (lret != LDB_SUCCESS) {
-                       DEBUG(3, ("Failed single search for %s - %s\n",
-                                 short_princ, ldb_errstring(kdc_db_ctx->samdb)));
-                       free(short_princ);
-                       return HDB_ERR_NOENTRY;
+                       DEBUG(0, ("Failed single search for %s - %s\n",
+                                 name1, ldb_errstring(kdc_db_ctx->samdb)));
+                       return SDB_ERR_NOENTRY;
                }
-               free(short_princ);
                return 0;
        }
-       return HDB_ERR_NOENTRY;
+       return SDB_ERR_NOENTRY;
 }
 
 
@@ -1656,7 +2053,7 @@ static krb5_error_code samba_kdc_fetch_server(krb5_context context,
                                              TALLOC_CTX *mem_ctx,
                                              krb5_const_principal principal,
                                              unsigned flags,
-                                             hdb_entry_ex *entry_ex)
+                                             struct sdb_entry_ex *entry_ex)
 {
        krb5_error_code ret;
        struct ldb_dn *realm_dn;
@@ -1679,14 +2076,223 @@ static krb5_error_code samba_kdc_fetch_server(krb5_context context,
        return ret;
 }
 
+static krb5_error_code samba_kdc_lookup_realm(krb5_context context,
+                                             struct samba_kdc_db_context *kdc_db_ctx,
+                                             TALLOC_CTX *mem_ctx,
+                                             krb5_const_principal principal,
+                                             unsigned flags,
+                                             struct sdb_entry_ex *entry_ex)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       NTSTATUS status;
+       krb5_error_code ret;
+       char *_realm = NULL;
+       bool check_realm = false;
+       const char *realm = NULL;
+       struct dsdb_trust_routing_table *trt = NULL;
+       const struct lsa_TrustDomainInfoInfoEx *tdo = NULL;
+       unsigned int num_comp;
+       bool ok;
+       char *upper = NULL;
+
+       num_comp = krb5_princ_size(context, principal);
+
+       if (flags & SDB_F_GET_CLIENT) {
+               if (flags & SDB_F_FOR_AS_REQ) {
+                       check_realm = true;
+               }
+       }
+       if (flags & SDB_F_GET_SERVER) {
+               if (flags & SDB_F_FOR_TGS_REQ) {
+                       check_realm = true;
+               }
+       }
+
+       if (!check_realm) {
+               TALLOC_FREE(frame);
+               return 0;
+       }
+
+       _realm = smb_krb5_principal_get_realm(context, principal);
+       if (_realm == NULL) {
+               TALLOC_FREE(frame);
+               return ENOMEM;
+       }
+
+       /*
+        * The requested realm needs to be our own
+        */
+       ok = lpcfg_is_my_domain_or_realm(kdc_db_ctx->lp_ctx, _realm);
+       if (!ok) {
+               /*
+                * The request is not for us...
+                */
+               SAFE_FREE(_realm);
+               TALLOC_FREE(frame);
+               return SDB_ERR_NOENTRY;
+       }
+
+       realm = talloc_strdup(frame, _realm);
+       SAFE_FREE(_realm);
+       if (realm == NULL) {
+               TALLOC_FREE(frame);
+               return ENOMEM;
+       }
+
+       if (smb_krb5_principal_get_type(context, principal) == KRB5_NT_ENTERPRISE_PRINCIPAL) {
+               char *principal_string = NULL;
+               krb5_principal enterprise_principal = NULL;
+               char *enterprise_realm = NULL;
+
+               if (num_comp != 1) {
+                       TALLOC_FREE(frame);
+                       return SDB_ERR_NOENTRY;
+               }
+
+               principal_string = smb_krb5_principal_get_comp_string(frame, context,
+                                                                     principal, 0);
+               if (principal_string == NULL) {
+                       TALLOC_FREE(frame);
+                       return ENOMEM;
+               }
+
+               ret = krb5_parse_name(context, principal_string,
+                                     &enterprise_principal);
+               TALLOC_FREE(principal_string);
+               if (ret) {
+                       TALLOC_FREE(frame);
+                       return ret;
+               }
+
+               enterprise_realm = smb_krb5_principal_get_realm(context,
+                                                       enterprise_principal);
+               krb5_free_principal(context, enterprise_principal);
+               if (enterprise_realm != NULL) {
+                       realm = talloc_strdup(frame, enterprise_realm);
+                       SAFE_FREE(enterprise_realm);
+                       if (realm == NULL) {
+                               TALLOC_FREE(frame);
+                               return ENOMEM;
+                       }
+               }
+       }
+
+       if (flags & SDB_F_GET_SERVER) {
+               char *service_realm = NULL;
+
+               ret = principal_comp_strcmp(context, principal, 0, KRB5_TGS_NAME);
+               if (ret == 0) {
+                       /*
+                        * we need to search krbtgt/ locally
+                        */
+                       TALLOC_FREE(frame);
+                       return 0;
+               }
+
+               /*
+                * We need to check the last component against the routing table.
+                *
+                * Note this works only with 2 or 3 component principals, e.g:
+                *
+                * servicePrincipalName: ldap/W2K8R2-219.bla.base
+                * servicePrincipalName: ldap/W2K8R2-219.bla.base/bla.base
+                * servicePrincipalName: ldap/W2K8R2-219.bla.base/ForestDnsZones.bla.base
+                * servicePrincipalName: ldap/W2K8R2-219.bla.base/DomainDnsZones.bla.base
+                */
+
+               if (num_comp == 2 || num_comp == 3) {
+                       service_realm = smb_krb5_principal_get_comp_string(frame,
+                                                                          context,
+                                                                          principal,
+                                                                          num_comp - 1);
+               }
+
+               if (service_realm != NULL) {
+                       realm = service_realm;
+               }
+       }
+
+       ok = lpcfg_is_my_domain_or_realm(kdc_db_ctx->lp_ctx, realm);
+       if (ok) {
+               /*
+                * skip the expensive routing lookup
+                */
+               TALLOC_FREE(frame);
+               return 0;
+       }
+
+       status = dsdb_trust_routing_table_load(kdc_db_ctx->samdb,
+                                              frame, &trt);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(frame);
+               return EINVAL;
+       }
+
+       tdo = dsdb_trust_routing_by_name(trt, realm);
+       if (tdo == NULL) {
+               /*
+                * This principal has to be local
+                */
+               TALLOC_FREE(frame);
+               return 0;
+       }
+
+       if (tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST) {
+               /*
+                * TODO: handle the routing within the forest
+                *
+                * This should likely be handled in
+                * samba_kdc_message2entry() in case we're
+                * a global catalog. We'd need to check
+                * if realm_dn is our own domain and derive
+                * the dns domain name from realm_dn and check that
+                * against the routing table or fallback to
+                * the tdo we found here.
+                *
+                * But for now we don't support multiple domains
+                * in our forest correctly anyway.
+                *
+                * Just search in our local database.
+                */
+               TALLOC_FREE(frame);
+               return 0;
+       }
+
+       ZERO_STRUCT(entry_ex->entry);
+
+       ret = krb5_copy_principal(context, principal,
+                                 &entry_ex->entry.principal);
+       if (ret) {
+               TALLOC_FREE(frame);
+               return ret;
+       }
+
+       upper = strupper_talloc(frame, tdo->domain_name.string);
+       if (upper == NULL) {
+               TALLOC_FREE(frame);
+               return ENOMEM;
+       }
+
+       ret = smb_krb5_principal_set_realm(context,
+                                          entry_ex->entry.principal,
+                                          upper);
+       if (ret) {
+               TALLOC_FREE(frame);
+               return ret;
+       }
+
+       TALLOC_FREE(frame);
+       return SDB_ERR_WRONG_REALM;
+}
+
 krb5_error_code samba_kdc_fetch(krb5_context context,
                                struct samba_kdc_db_context *kdc_db_ctx,
                                krb5_const_principal principal,
                                unsigned flags,
                                krb5_kvno kvno,
-                               hdb_entry_ex *entry_ex)
+                               struct sdb_entry_ex *entry_ex)
 {
-       krb5_error_code ret = HDB_ERR_NOENTRY;
+       krb5_error_code ret = SDB_ERR_NOENTRY;
        TALLOC_CTX *mem_ctx;
 
        mem_ctx = talloc_named(kdc_db_ctx, 0, "samba_kdc_fetch context");
@@ -1696,22 +2302,30 @@ krb5_error_code samba_kdc_fetch(krb5_context context,
                return ret;
        }
 
-       if (flags & HDB_F_GET_CLIENT) {
+       ret = samba_kdc_lookup_realm(context, kdc_db_ctx, mem_ctx,
+                                    principal, flags, entry_ex);
+       if (ret != 0) {
+               goto done;
+       }
+
+       ret = SDB_ERR_NOENTRY;
+
+       if (flags & SDB_F_GET_CLIENT) {
                ret = samba_kdc_fetch_client(context, kdc_db_ctx, mem_ctx, principal, flags, entry_ex);
-               if (ret != HDB_ERR_NOENTRY) goto done;
+               if (ret != SDB_ERR_NOENTRY) goto done;
        }
-       if (flags & HDB_F_GET_SERVER) {
+       if (flags & SDB_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, flags, kvno, entry_ex);
-               if (ret != HDB_ERR_NOENTRY) goto done;
+               if (ret != SDB_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, flags, entry_ex);
-               if (ret != HDB_ERR_NOENTRY) goto done;
+               if (ret != SDB_ERR_NOENTRY) goto done;
        }
-       if (flags & HDB_F_GET_KRBTGT) {
+       if (flags & SDB_F_GET_KRBTGT) {
                ret = samba_kdc_fetch_krbtgt(context, kdc_db_ctx, mem_ctx, principal, flags, kvno, entry_ex);
-               if (ret != HDB_ERR_NOENTRY) goto done;
+               if (ret != SDB_ERR_NOENTRY) goto done;
        }
 
 done:
@@ -1728,7 +2342,7 @@ struct samba_kdc_seq {
 
 static krb5_error_code samba_kdc_seq(krb5_context context,
                                     struct samba_kdc_db_context *kdc_db_ctx,
-                                    hdb_entry_ex *entry)
+                                    struct sdb_entry_ex *entry)
 {
        krb5_error_code ret;
        struct samba_kdc_seq *priv = kdc_db_ctx->seq_ctx;
@@ -1739,7 +2353,7 @@ static krb5_error_code samba_kdc_seq(krb5_context context,
        TALLOC_CTX *mem_ctx;
 
        if (!priv) {
-               return HDB_ERR_NOENTRY;
+               return SDB_ERR_NOENTRY;
        }
 
        mem_ctx = talloc_named(priv, 0, "samba_kdc_seq context");
@@ -1760,7 +2374,7 @@ static krb5_error_code samba_kdc_seq(krb5_context context,
        }
 
        if (sAMAccountName == NULL) {
-               ret = HDB_ERR_NOENTRY;
+               ret = SDB_ERR_NOENTRY;
                goto out;
        }
 
@@ -1772,7 +2386,7 @@ static krb5_error_code samba_kdc_seq(krb5_context context,
 
        ret = samba_kdc_message2entry(context, kdc_db_ctx, mem_ctx,
                                      principal, SAMBA_KDC_ENT_TYPE_ANY,
-                                     HDB_F_ADMIN_DATA|HDB_F_GET_ANY,
+                                     SDB_F_ADMIN_DATA|SDB_F_GET_ANY,
                                      priv->realm_dn, msg, entry);
 
 out:
@@ -1792,7 +2406,7 @@ out:
 
 krb5_error_code samba_kdc_firstkey(krb5_context context,
                                   struct samba_kdc_db_context *kdc_db_ctx,
-                                  hdb_entry_ex *entry)
+                                  struct sdb_entry_ex *entry)
 {
        struct ldb_context *ldb_ctx = kdc_db_ctx->samdb;
        struct samba_kdc_seq *priv = kdc_db_ctx->seq_ctx;
@@ -1841,7 +2455,7 @@ krb5_error_code samba_kdc_firstkey(krb5_context context,
 
        if (lret != LDB_SUCCESS) {
                TALLOC_FREE(priv);
-               return HDB_ERR_NOENTRY;
+               return SDB_ERR_NOENTRY;
        }
 
        priv->count = res->count;
@@ -1863,7 +2477,7 @@ krb5_error_code samba_kdc_firstkey(krb5_context context,
 
 krb5_error_code samba_kdc_nextkey(krb5_context context,
                                  struct samba_kdc_db_context *kdc_db_ctx,
-                                 hdb_entry_ex *entry)
+                                 struct sdb_entry_ex *entry)
 {
        return samba_kdc_seq(context, kdc_db_ctx, entry);
 }
@@ -1875,7 +2489,7 @@ krb5_error_code samba_kdc_nextkey(krb5_context context,
 krb5_error_code
 samba_kdc_check_s4u2self(krb5_context context,
                         struct samba_kdc_db_context *kdc_db_ctx,
-                        hdb_entry_ex *entry,
+                        struct samba_kdc_entry *skdc_entry,
                         krb5_const_principal target_principal)
 {
        krb5_error_code ret;
@@ -1883,7 +2497,6 @@ samba_kdc_check_s4u2self(krb5_context context,
        struct ldb_message *msg;
        struct dom_sid *orig_sid;
        struct dom_sid *target_sid;
-       struct samba_kdc_entry *p = talloc_get_type(entry->ctx, struct samba_kdc_entry);
        const char *delegation_check_attrs[] = {
                "objectSid", NULL
        };
@@ -1897,7 +2510,7 @@ samba_kdc_check_s4u2self(krb5_context context,
        }
 
        ret = samba_kdc_lookup_server(context, kdc_db_ctx, mem_ctx, target_principal,
-                                     HDB_F_GET_CLIENT|HDB_F_GET_SERVER,
+                                     SDB_F_GET_CLIENT|SDB_F_GET_SERVER,
                                      delegation_check_attrs, &realm_dn, &msg);
 
        if (ret != 0) {
@@ -1905,7 +2518,7 @@ samba_kdc_check_s4u2self(krb5_context context,
                return ret;
        }
 
-       orig_sid = samdb_result_dom_sid(mem_ctx, p->msg, "objectSid");
+       orig_sid = samdb_result_dom_sid(mem_ctx, skdc_entry->msg, "objectSid");
        target_sid = samdb_result_dom_sid(mem_ctx, msg, "objectSid");
 
        /* Allow delegation to the same principal, even if by a different
@@ -1928,7 +2541,7 @@ samba_kdc_check_s4u2self(krb5_context context,
 krb5_error_code
 samba_kdc_check_pkinit_ms_upn_match(krb5_context context,
                                    struct samba_kdc_db_context *kdc_db_ctx,
-                                    hdb_entry_ex *entry,
+                                   struct samba_kdc_entry *skdc_entry,
                                     krb5_const_principal certificate_principal)
 {
        krb5_error_code ret;
@@ -1936,7 +2549,6 @@ samba_kdc_check_pkinit_ms_upn_match(krb5_context context,
        struct ldb_message *msg;
        struct dom_sid *orig_sid;
        struct dom_sid *target_sid;
-       struct samba_kdc_entry *p = talloc_get_type(entry->ctx, struct samba_kdc_entry);
        const char *ms_upn_check_attrs[] = {
                "objectSid", NULL
        };
@@ -1958,7 +2570,7 @@ samba_kdc_check_pkinit_ms_upn_match(krb5_context context,
                return ret;
        }
 
-       orig_sid = samdb_result_dom_sid(mem_ctx, p->msg, "objectSid");
+       orig_sid = samdb_result_dom_sid(mem_ctx, skdc_entry->msg, "objectSid");
        target_sid = samdb_result_dom_sid(mem_ctx, msg, "objectSid");
 
        /* Consider these to be the same principal, even if by a different
@@ -1966,7 +2578,11 @@ samba_kdc_check_pkinit_ms_upn_match(krb5_context context,
         * comparison */
        if (!(orig_sid && target_sid && dom_sid_equal(orig_sid, target_sid))) {
                talloc_free(mem_ctx);
+#ifdef KRB5_KDC_ERR_CLIENT_NAME_MISMATCH /* Heimdal */
                return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
+#elif defined(KRB5KDC_ERR_CLIENT_NAME_MISMATCH) /* MIT */
+               return KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
+#endif
        }
 
        talloc_free(mem_ctx);
@@ -1980,7 +2596,7 @@ samba_kdc_check_pkinit_ms_upn_match(krb5_context context,
 krb5_error_code
 samba_kdc_check_s4u2proxy(krb5_context context,
                          struct samba_kdc_db_context *kdc_db_ctx,
-                         hdb_entry_ex *entry,
+                         struct samba_kdc_entry *skdc_entry,
                          krb5_const_principal target_principal)
 {
        krb5_error_code ret;
@@ -1991,7 +2607,6 @@ samba_kdc_check_s4u2proxy(krb5_context context,
        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");
 
@@ -2003,7 +2618,7 @@ samba_kdc_check_s4u2proxy(krb5_context context,
                return ret;
        }
 
-       client_dn = ldb_dn_get_linearized(p->msg->dn);
+       client_dn = ldb_dn_get_linearized(skdc_entry->msg->dn);
        if (!client_dn) {
                if (errno == 0) {
                        errno = ENOMEM;
@@ -2045,7 +2660,7 @@ samba_kdc_check_s4u2proxy(krb5_context context,
                return ret;
        }
 
-       el = ldb_msg_find_element(p->msg, "msDS-AllowedToDelegateTo");
+       el = ldb_msg_find_element(skdc_entry->msg, "msDS-AllowedToDelegateTo");
        if (el == NULL) {
                goto bad_option;
        }
@@ -2112,9 +2727,11 @@ 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;
+       kdc_db_ctx->msg_ctx = base_ctx->msg_ctx;
 
        /* get default kdc policy */
-       lpcfg_default_kdc_policy(base_ctx->lp_ctx,
+       lpcfg_default_kdc_policy(mem_ctx,
+                                base_ctx->lp_ctx,
                                 &kdc_db_ctx->policy.svc_tkt_lifetime,
                                 &kdc_db_ctx->policy.usr_tkt_lifetime,
                                 &kdc_db_ctx->policy.renewal_lifetime);