packaging: Add missing quotes in smbprint
[samba.git] / source4 / kdc / mit_samba.c
index 056e18092381ed4680b7b70190251f29c84ca81c..4af02fa00d0530edd09921ccc187f67f0ad651d8 100644 (file)
@@ -25,6 +25,7 @@
 #include "param/param.h"
 #include "dsdb/samdb/samdb.h"
 #include "system/kerberos.h"
+#include "lib/replace/system/filesys.h"
 #include <com_err.h>
 #include <kdb.h>
 #include <kadm5/kadm_err.h>
@@ -46,7 +47,7 @@
 
 void mit_samba_context_free(struct mit_samba_context *ctx)
 {
-       /* free heimdal's krb5_context */
+       /* free MIT's krb5_context */
        if (ctx->context) {
                krb5_free_context(ctx->context);
        }
@@ -56,7 +57,7 @@ void mit_samba_context_free(struct mit_samba_context *ctx)
 }
 
 /*
- * Implemant a callback to log to the MIT KDC log facility
+ * Implement a callback to log to the MIT KDC log facility
  *
  * http://web.mit.edu/kerberos/krb5-devel/doc/plugindev/general.html#logging-from-kdc-and-kadmind-plugin-modules
  */
@@ -68,16 +69,16 @@ static void mit_samba_debug(void *private_ptr, int msg_level, const char *msg)
                is_error = 0;
        }
 
-       com_err("", is_error, "%s", msg);
+       com_err("mitkdc", is_error, "%s", msg);
 }
 
-int mit_samba_context_init(struct mit_samba_context **_ctx)
+krb5_error_code mit_samba_context_init(struct mit_samba_context **_ctx)
 {
        NTSTATUS status;
        struct mit_samba_context *ctx;
        const char *s4_conf_file;
-       int ret;
-       struct samba_kdc_base_context base_ctx;
+       krb5_error_code ret;
+       struct samba_kdc_base_context base_ctx = {};
 
        ctx = talloc_zero(NULL, struct mit_samba_context);
        if (!ctx) {
@@ -113,13 +114,19 @@ int mit_samba_context_init(struct mit_samba_context **_ctx)
                lpcfg_load_default(base_ctx.lp_ctx);
        }
 
+       base_ctx.current_nttime_ull = talloc_zero(ctx, unsigned long long);
+       if (base_ctx.current_nttime_ull == NULL) {
+               ret = ENOMEM;
+               goto done;
+       }
+
        status = samba_kdc_setup_db_ctx(ctx, &base_ctx, &ctx->db_ctx);
        if (!NT_STATUS_IS_OK(status)) {
                ret = EINVAL;
                goto done;
        }
 
-       /* init heimdal's krb_context and log facilities */
+       /* init MIT's krb_context and log facilities */
        ret = smb_krb5_init_context_basic(ctx,
                                          ctx->db_ctx->lp_ctx,
                                          &ctx->context);
@@ -138,22 +145,6 @@ done:
        return ret;
 }
 
-static krb5_error_code ks_is_tgs_principal(struct mit_samba_context *ctx,
-                                          krb5_const_principal principal)
-{
-       char *p;
-       int eq = -1;
-
-       p = smb_krb5_principal_get_comp_string(ctx, ctx->context, principal, 0);
-
-       eq = krb5_princ_size(ctx->context, principal) == 2 &&
-            (strcmp(p, KRB5_TGS_NAME) == 0);
-
-       talloc_free(p);
-
-       return eq;
-}
-
 int mit_samba_generate_salt(krb5_data *salt)
 {
        if (salt == NULL) {
@@ -175,100 +166,88 @@ int mit_samba_generate_random_password(krb5_data *pwd)
 {
        TALLOC_CTX *tmp_ctx;
        char *password;
+       char *data = NULL;
+       const unsigned length = 24;
 
        if (pwd == NULL) {
                return EINVAL;
        }
-       pwd->length = 24;
 
        tmp_ctx = talloc_named(NULL,
                               0,
-                              "mit_samba_create_principal_password context");
+                              "mit_samba_generate_random_password context");
        if (tmp_ctx == NULL) {
                return ENOMEM;
        }
 
-       password = generate_random_password(tmp_ctx, pwd->length, pwd->length);
+       password = generate_random_password(tmp_ctx, length, length);
        if (password == NULL) {
                talloc_free(tmp_ctx);
                return ENOMEM;
        }
 
-       pwd->data = strdup(password);
+       data = strdup(password);
        talloc_free(tmp_ctx);
-       if (pwd->data == NULL) {
+       if (data == NULL) {
                return ENOMEM;
        }
 
+       *pwd = smb_krb5_make_data(data, length);
+
        return 0;
 }
 
-int mit_samba_get_principal(struct mit_samba_context *ctx,
-                           krb5_const_principal principal,
-                           unsigned int kflags,
-                           krb5_db_entry **_kentry)
+krb5_error_code mit_samba_get_principal(struct mit_samba_context *ctx,
+                                       krb5_const_principal principal,
+                                       unsigned int kflags,
+                                       krb5_db_entry **_kentry)
 {
-       struct sdb_entry_ex sentry = {
-               .free_entry = NULL,
-       };
+       struct sdb_entry sentry = {};
        krb5_db_entry *kentry;
-       int ret;
+       krb5_error_code ret;
        uint32_t sflags = 0;
        krb5_principal referral_principal = NULL;
+       NTTIME now;
+       bool time_ok;
+
+       time_ok = gmsa_current_time(&now);
+       if (!time_ok) {
+               return EINVAL;
+       }
+
+       *ctx->db_ctx->current_nttime_ull = now;
 
        kentry = calloc(1, sizeof(krb5_db_entry));
        if (kentry == NULL) {
                return ENOMEM;
        }
 
-#if KRB5_KDB_API_VERSION >= 10
        /*
         * The MIT KDC code that wants the canonical name in all lookups, and
         * takes care to canonicalize only when appropriate.
         */
        sflags |= SDB_F_FORCE_CANON;
-#endif
 
-#if KRB5_KDB_DAL_MAJOR_VERSION >= 9
        if (kflags & KRB5_KDB_FLAG_REFERRAL_OK) {
                sflags |= SDB_F_CANON;
        }
 
        if (kflags & KRB5_KDB_FLAG_CLIENT) {
                sflags |= SDB_F_GET_CLIENT;
-
-               if (!(kflags & KRB5_KDB_FLAG_REFERRAL_OK)) {
-                       sflags |= SDB_F_FOR_AS_REQ;
-               }
-       } else if (ks_is_tgs_principal(ctx, principal)) {
-               sflags |= SDB_F_GET_KRBTGT;
+               sflags |= SDB_F_FOR_AS_REQ;
        } else {
-               sflags |= SDB_F_GET_SERVER;
+               int equal = smb_krb5_principal_is_tgs(ctx->context, principal);
+               if (equal == -1) {
+                       return ENOMEM;
+               }
 
-               if (!(kflags & KRB5_KDB_FLAG_REFERRAL_OK)) {
+               if (equal) {
+                       sflags |= SDB_F_GET_KRBTGT;
+               } else {
+                       sflags |= SDB_F_GET_SERVER;
                        sflags |= SDB_F_FOR_TGS_REQ;
                }
        }
-#else /* KRB5_KDB_DAL_MAJOR_VERSION < 9 */
-       if (kflags & KRB5_KDB_FLAG_CANONICALIZE) {
-               sflags |= SDB_F_CANON;
-       }
-       if (kflags & (KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY |
-                     KRB5_KDB_FLAG_INCLUDE_PAC)) {
-               /*
-                * KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY is equal to
-                * SDB_F_FOR_AS_REQ
-                *
-                * We use ANY to also allow AS_REQ for service principal names
-                * This is supported by Windows.
-                */
-               sflags |= SDB_F_GET_ANY|SDB_F_FOR_AS_REQ;
-       } else if (ks_is_tgs_principal(ctx, principal)) {
-               sflags |= SDB_F_GET_KRBTGT;
-       } else {
-               sflags |= SDB_F_GET_SERVER|SDB_F_FOR_TGS_REQ;
-       }
-#endif /* KRB5_KDB_DAL_MAJOR_VERSION */
 
        /* always set this or the created_by data will not be populated by samba's
         * backend and we will fail to parse the entry later */
@@ -298,7 +277,7 @@ fetch_referral_principal:
                }
 
                if (referral_principal != NULL) {
-                       sdb_free_entry(&sentry);
+                       sdb_entry_free(&sentry);
                        ret = KRB5_KDB_NOENTRY;
                        goto done;
                }
@@ -312,15 +291,15 @@ fetch_referral_principal:
                 *
                 *     ADDOM.SAMBA.EXAMPLE.COM
                 *
-                * We look up if we have and entry in the database and get an
-                * entry with the pricipal:
+                * We look up if we have an entry in the database and get an
+                * entry with the principal:
                 *
                 *     cifs/dc7.SAMBA2008R2.EXAMPLE.COM@SAMBA2008R2.EXAMPLE.COM
                 *
                 * and the error: SDB_ERR_WRONG_REALM.
                 *
                 * In the case of a TGS-REQ we need to return a referral ticket
-                * fo the next trust hop to the client. This ticket will have
+                * for the next trust hop to the client. This ticket will have
                 * the following principal:
                 *
                 *     krbtgt/SAMBA2008R2.EXAMPLE.COM@ADDOM.SAMBA.EXAMPLE.COM
@@ -329,8 +308,8 @@ fetch_referral_principal:
                 * principal and return success.
                 */
                dest_realm = smb_krb5_principal_get_realm(
-                       ctx, ctx->context, sentry.entry.principal);
-               sdb_free_entry(&sentry);
+                       ctx, ctx->context, sentry.principal);
+               sdb_entry_free(&sentry);
                if (dest_realm == NULL) {
                        ret = KRB5_KDB_NOENTRY;
                        goto done;
@@ -356,9 +335,9 @@ fetch_referral_principal:
                goto done;
        }
 
-       ret = sdb_entry_ex_to_kdb_entry_ex(ctx->context, &sentry, kentry);
+       ret = sdb_entry_to_krb5_db_entry(ctx->context, &sentry, kentry);
 
-       sdb_free_entry(&sentry);
+       sdb_entry_free(&sentry);
 
 done:
        krb5_free_principal(ctx->context, referral_principal);
@@ -372,21 +351,29 @@ done:
        return ret;
 }
 
-int mit_samba_get_firstkey(struct mit_samba_context *ctx,
-                          krb5_db_entry **_kentry)
+krb5_error_code mit_samba_get_firstkey(struct mit_samba_context *ctx,
+                                      krb5_db_entry **_kentry)
 {
-       struct sdb_entry_ex sentry = {
-               .free_entry = NULL,
-       };
+       struct sdb_entry sentry = {};
        krb5_db_entry *kentry;
-       int ret;
+       krb5_error_code ret;
+
+       NTTIME now;
+       bool time_ok;
+
+       time_ok = gmsa_current_time(&now);
+       if (!time_ok) {
+               return EINVAL;
+       }
+
+       *ctx->db_ctx->current_nttime_ull = now;
 
        kentry = malloc(sizeof(krb5_db_entry));
        if (kentry == NULL) {
                return ENOMEM;
        }
 
-       ret = samba_kdc_firstkey(ctx->context, ctx->db_ctx, &sentry);
+       ret = samba_kdc_firstkey(ctx->context, ctx->db_ctx, SDB_F_ADMIN_DATA, &sentry);
        switch (ret) {
        case 0:
                break;
@@ -400,9 +387,9 @@ int mit_samba_get_firstkey(struct mit_samba_context *ctx,
                return ret;
        }
 
-       ret = sdb_entry_ex_to_kdb_entry_ex(ctx->context, &sentry, kentry);
+       ret = sdb_entry_to_krb5_db_entry(ctx->context, &sentry, kentry);
 
-       sdb_free_entry(&sentry);
+       sdb_entry_free(&sentry);
 
        if (ret) {
                free(kentry);
@@ -412,21 +399,21 @@ int mit_samba_get_firstkey(struct mit_samba_context *ctx,
        return ret;
 }
 
-int mit_samba_get_nextkey(struct mit_samba_context *ctx,
-                         krb5_db_entry **_kentry)
+krb5_error_code mit_samba_get_nextkey(struct mit_samba_context *ctx,
+                                     krb5_db_entry **_kentry)
 {
-       struct sdb_entry_ex sentry = {
-               .free_entry = NULL,
-       };
+       struct sdb_entry sentry = {};
        krb5_db_entry *kentry;
-       int ret;
+       krb5_error_code ret;
+
+       /* Not updating time, keep the same for the whole operation */
 
        kentry = malloc(sizeof(krb5_db_entry));
        if (kentry == NULL) {
                return ENOMEM;
        }
 
-       ret = samba_kdc_nextkey(ctx->context, ctx->db_ctx, &sentry);
+       ret = samba_kdc_nextkey(ctx->context, ctx->db_ctx, SDB_F_ADMIN_DATA, &sentry);
        switch (ret) {
        case 0:
                break;
@@ -440,9 +427,9 @@ int mit_samba_get_nextkey(struct mit_samba_context *ctx,
                return ret;
        }
 
-       ret = sdb_entry_ex_to_kdb_entry_ex(ctx->context, &sentry, kentry);
+       ret = sdb_entry_to_krb5_db_entry(ctx->context, &sentry, kentry);
 
-       sdb_free_entry(&sentry);
+       sdb_entry_free(&sentry);
 
        if (ret) {
                free(kentry);
@@ -452,14 +439,17 @@ int mit_samba_get_nextkey(struct mit_samba_context *ctx,
        return ret;
 }
 
-int mit_samba_get_pac(struct mit_samba_context *smb_ctx,
-                     krb5_context context,
-                     krb5_db_entry *client,
-                     krb5_db_entry *server,
-                     krb5_keyblock *replaced_reply_key,
-                     krb5_pac *pac)
+krb5_error_code mit_samba_get_pac(struct mit_samba_context *smb_ctx,
+                                 krb5_context context,
+                                 uint32_t flags,
+                                 krb5_db_entry *client,
+                                 krb5_db_entry *server,
+                                 krb5_keyblock *replaced_reply_key,
+                                 krb5_pac *pac)
 {
        TALLOC_CTX *tmp_ctx;
+       const struct auth_user_info_dc *user_info_dc = NULL;
+       struct auth_user_info_dc *user_info_dc_shallow_copy = NULL;
        DATA_BLOB *logon_info_blob = NULL;
        DATA_BLOB *upn_dns_info_blob = NULL;
        DATA_BLOB *cred_ndr = NULL;
@@ -468,17 +458,54 @@ int mit_samba_get_pac(struct mit_samba_context *smb_ctx,
        DATA_BLOB *pcred_blob = NULL;
        DATA_BLOB *pac_attrs_blob = NULL;
        DATA_BLOB *requester_sid_blob = NULL;
+       const DATA_BLOB *client_claims_blob = NULL;
        NTSTATUS nt_status;
        krb5_error_code code;
        struct samba_kdc_entry *skdc_entry;
+       struct samba_kdc_entry *server_entry = NULL;
        bool is_krbtgt;
-
+       /* Only include resource groups in a service ticket. */
+       enum auth_group_inclusion group_inclusion;
+       enum samba_asserted_identity asserted_identity =
+               (flags & KRB5_KDB_FLAG_PROTOCOL_TRANSITION) ?
+                       SAMBA_ASSERTED_IDENTITY_SERVICE :
+                       SAMBA_ASSERTED_IDENTITY_AUTHENTICATION_AUTHORITY;
+
+       if (client == NULL) {
+               return EINVAL;
+       }
        skdc_entry = talloc_get_type_abort(client->e_data,
                                           struct samba_kdc_entry);
 
+       /* This sets the time into the DSDB opaque */
+       *smb_ctx->db_ctx->current_nttime_ull = skdc_entry->current_nttime;
+
+       if (server == NULL) {
+               return EINVAL;
+       }
+       {
+               int result = smb_krb5_principal_is_tgs(smb_ctx->context, server->princ);
+               if (result == -1) {
+                       return ENOMEM;
+               }
+
+               is_krbtgt = result;
+       }
+       server_entry = talloc_get_type_abort(server->e_data,
+                                            struct samba_kdc_entry);
+
+       /* Only include resource groups in a service ticket. */
+       if (is_krbtgt) {
+               group_inclusion = AUTH_EXCLUDE_RESOURCE_GROUPS;
+       } else if (server_entry->supported_enctypes & KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED) {
+               group_inclusion = AUTH_INCLUDE_RESOURCE_GROUPS;
+       } else {
+               group_inclusion = AUTH_INCLUDE_RESOURCE_GROUPS_COMPRESSED;
+       }
+
        tmp_ctx = talloc_named(smb_ctx,
                               0,
-                              "mit_samba_get_pac_data_blobs context");
+                              "mit_samba_get_pac context");
        if (tmp_ctx == NULL) {
                return ENOMEM;
        }
@@ -488,465 +515,135 @@ int mit_samba_get_pac(struct mit_samba_context *smb_ctx,
                cred_ndr_ptr = &cred_ndr;
        }
 
-       is_krbtgt = ks_is_tgs_principal(smb_ctx, server->princ);
-
-       nt_status = samba_kdc_get_pac_blobs(tmp_ctx,
-                                           skdc_entry,
-                                           &logon_info_blob,
-                                           cred_ndr_ptr,
-                                           &upn_dns_info_blob,
-                                           is_krbtgt ? &pac_attrs_blob : NULL,
-                                           PAC_ATTRIBUTE_FLAG_PAC_WAS_GIVEN_IMPLICITLY,
-                                           is_krbtgt ? &requester_sid_blob : NULL,
-                                           NULL);
-       if (!NT_STATUS_IS_OK(nt_status)) {
+       code = samba_kdc_get_user_info_from_db(tmp_ctx,
+                                              server_entry->kdc_db_ctx->samdb,
+                                              skdc_entry,
+                                              skdc_entry->msg,
+                                              &user_info_dc);
+       if (code) {
                talloc_free(tmp_ctx);
-               if (NT_STATUS_EQUAL(nt_status,
-                                   NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
-                       return ENOENT;
-               }
-               return EINVAL;
+               return code;
        }
 
-       if (replaced_reply_key != NULL && cred_ndr != NULL) {
-               code = samba_kdc_encrypt_pac_credentials(context,
-                                                        replaced_reply_key,
-                                                        cred_ndr,
-                                                        tmp_ctx,
-                                                        &cred_blob);
-               if (code != 0) {
-                       talloc_free(tmp_ctx);
-                       return code;
-               }
-               pcred_blob = &cred_blob;
-       }
-
-       code = samba_make_krb5_pac(context,
-                                  logon_info_blob,
-                                  pcred_blob,
-                                  upn_dns_info_blob,
-                                  pac_attrs_blob,
-                                  requester_sid_blob,
-                                  NULL,
-                                  *pac);
-
-       talloc_free(tmp_ctx);
-       return code;
-}
+       /* Make a shallow copy of the user_info_dc structure. */
+       nt_status = authsam_shallow_copy_user_info_dc(tmp_ctx,
+                                                     user_info_dc,
+                                                     &user_info_dc_shallow_copy);
+       user_info_dc = NULL;
 
-#if KRB5_KDB_DAL_MAJOR_VERSION < 9
-krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx,
-                                   krb5_context context,
-                                   int flags,
-                                   krb5_const_principal client_principal,
-                                   krb5_db_entry *client,
-                                   krb5_db_entry *server,
-                                   krb5_db_entry *krbtgt,
-                                   krb5_keyblock *krbtgt_keyblock,
-                                   krb5_pac *pac)
-{
-       TALLOC_CTX *tmp_ctx;
-       krb5_error_code code;
-       NTSTATUS nt_status;
-       DATA_BLOB *pac_blob = NULL;
-       DATA_BLOB *upn_blob = NULL;
-       DATA_BLOB *deleg_blob = NULL;
-       struct samba_kdc_entry *client_skdc_entry = NULL;
-       struct samba_kdc_entry *krbtgt_skdc_entry = NULL;
-       struct samba_kdc_entry *server_skdc_entry = NULL;
-       bool is_in_db = false;
-       bool is_untrusted = false;
-       size_t num_types = 0;
-       uint32_t *types = NULL;
-       uint32_t forced_next_type = 0;
-       size_t i = 0;
-       ssize_t logon_info_idx = -1;
-       ssize_t delegation_idx = -1;
-       ssize_t logon_name_idx = -1;
-       ssize_t upn_dns_info_idx = -1;
-       ssize_t srv_checksum_idx = -1;
-       ssize_t kdc_checksum_idx = -1;
-       krb5_pac new_pac = NULL;
-       bool ok;
-
-       /* Create a memory context early so code can use talloc_stackframe() */
-       tmp_ctx = talloc_named(ctx, 0, "mit_samba_reget_pac context");
-       if (tmp_ctx == NULL) {
-               return ENOMEM;
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DBG_ERR("Failed to allocate shallow copy of user_info_dc: %s\n",
+                       nt_errstr(nt_status));
+               talloc_free(tmp_ctx);
+               return map_errno_from_nt_status(nt_status);
        }
 
-       if (client != NULL) {
-               client_skdc_entry =
-                       talloc_get_type_abort(client->e_data,
-                                             struct samba_kdc_entry);
-
-               /*
-                * Check the objectSID of the client and pac data are the same.
-                * Does a parse and SID check, but no crypto.
-                */
-               code = samba_kdc_validate_pac_blob(context, client_skdc_entry, *pac);
-               if (code != 0) {
-                       goto done;
-               }
-       }
 
-       if (server == NULL) {
-               code = EINVAL;
-               goto done;
+       nt_status = samba_kdc_add_asserted_identity(asserted_identity,
+                                                   user_info_dc_shallow_copy);
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DBG_ERR("Failed to add asserted identity: %s\n",
+                       nt_errstr(nt_status));
+               talloc_free(tmp_ctx);
+               return EINVAL;
        }
 
-       server_skdc_entry =
-               talloc_get_type_abort(server->e_data,
-                                     struct samba_kdc_entry);
-
-       /* The account may be set not to want the PAC */
-       ok = samba_princ_needs_pac(server_skdc_entry);
-       if (!ok) {
-               code = EINVAL;
-               goto done;
+       nt_status = samba_kdc_add_claims_valid(user_info_dc_shallow_copy);
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DBG_ERR("Failed to add Claims Valid: %s\n",
+                       nt_errstr(nt_status));
+               talloc_free(tmp_ctx);
+               return EINVAL;
        }
 
-       if (krbtgt == NULL) {
-               code = EINVAL;
-               goto done;
-       }
-       krbtgt_skdc_entry =
-               talloc_get_type_abort(krbtgt->e_data,
-                                     struct samba_kdc_entry);
+       /* We no longer need to modify this, so assign to const variable */
+       user_info_dc = user_info_dc_shallow_copy;
 
-       code = samba_krbtgt_is_in_db(krbtgt_skdc_entry,
-                                    &is_in_db,
-                                    &is_untrusted);
-       if (code != 0) {
-               goto done;
+       nt_status = samba_kdc_get_logon_info_blob(tmp_ctx,
+                                                 user_info_dc,
+                                                 group_inclusion,
+                                                 &logon_info_blob);
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               talloc_free(tmp_ctx);
+               return EINVAL;
        }
 
-       if (is_untrusted) {
-               if (client == NULL) {
-                       code = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
-                       goto done;
-               }
-
-               nt_status = samba_kdc_get_pac_blobs(tmp_ctx,
-                                                   client_skdc_entry,
-                                                   &pac_blob,
-                                                   NULL,
-                                                   &upn_blob,
-                                                   NULL,
-                                                   0,
-                                                   NULL,
-                                                   NULL);
-               if (!NT_STATUS_IS_OK(nt_status)) {
-                       code = EINVAL;
-                       goto done;
-               }
-       } else {
-               struct PAC_SIGNATURE_DATA *pac_srv_sig;
-               struct PAC_SIGNATURE_DATA *pac_kdc_sig;
-
-               pac_blob = talloc_zero(tmp_ctx, DATA_BLOB);
-               if (pac_blob == NULL) {
-                       code = ENOMEM;
-                       goto done;
-               }
-
-               pac_srv_sig = talloc_zero(tmp_ctx, struct PAC_SIGNATURE_DATA);
-               if (pac_srv_sig == NULL) {
-                       code = ENOMEM;
-                       goto done;
-               }
-
-               pac_kdc_sig = talloc_zero(tmp_ctx, struct PAC_SIGNATURE_DATA);
-               if (pac_kdc_sig == NULL) {
-                       code = ENOMEM;
-                       goto done;
-               }
-
-               nt_status = samba_kdc_update_pac_blob(tmp_ctx,
-                                                     context,
-                                                     krbtgt_skdc_entry->kdc_db_ctx->samdb,
-                                                     *pac,
-                                                     pac_blob,
-                                                     pac_srv_sig,
-                                                     pac_kdc_sig);
+       if (cred_ndr_ptr != NULL) {
+               nt_status = samba_kdc_get_cred_ndr_blob(tmp_ctx,
+                                                       skdc_entry,
+                                                       cred_ndr_ptr);
                if (!NT_STATUS_IS_OK(nt_status)) {
-                       DEBUG(0, ("Update PAC blob failed: %s\n",
-                                 nt_errstr(nt_status)));
-                       code = EINVAL;
-                       goto done;
-               }
-
-               if (is_in_db) {
-                       /*
-                        * Now check the KDC signature, fetching the correct
-                        * key based on the enc type.
-                        */
-                       code = check_pac_checksum(pac_srv_sig->signature,
-                                                 pac_kdc_sig,
-                                                 context,
-                                                 krbtgt_keyblock);
-                       if (code != 0) {
-                               DBG_INFO("PAC KDC signature failed to verify\n");
-                               goto done;
-                       }
+                       talloc_free(tmp_ctx);
+                       return EINVAL;
                }
        }
 
-       if (flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) {
-               deleg_blob = talloc_zero(tmp_ctx, DATA_BLOB);
-               if (deleg_blob == NULL) {
-                       code = ENOMEM;
-                       goto done;
-               }
+       nt_status = samba_kdc_get_upn_info_blob(tmp_ctx,
+                                               user_info_dc,
+                                               &upn_dns_info_blob);
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               talloc_free(tmp_ctx);
+               return EINVAL;
+       }
 
-               nt_status = samba_kdc_update_delegation_info_blob(tmp_ctx,
-                                                                 context,
-                                                                 *pac,
-                                                                 server->princ,
-                                                                 discard_const(client_principal),
-                                                                 deleg_blob);
+       if (is_krbtgt) {
+               nt_status = samba_kdc_get_pac_attrs_blob(tmp_ctx,
+                                                        PAC_ATTRIBUTE_FLAG_PAC_WAS_GIVEN_IMPLICITLY,
+                                                        &pac_attrs_blob);
                if (!NT_STATUS_IS_OK(nt_status)) {
-                       DEBUG(0, ("Update delegation info failed: %s\n",
-                                 nt_errstr(nt_status)));
-                       code = EINVAL;
-                       goto done;
+                       talloc_free(tmp_ctx);
+                       return EINVAL;
                }
-       }
-
-       /* Check the types of the given PAC */
-       code = krb5_pac_get_types(context, *pac, &num_types, &types);
-       if (code != 0) {
-               goto done;
-       }
 
-       for (i = 0; i < num_types; i++) {
-               switch (types[i]) {
-               case PAC_TYPE_LOGON_INFO:
-                       if (logon_info_idx != -1) {
-                               DBG_WARNING("logon type[%u] twice [%zd] and [%zu]: \n",
-                                           types[i],
-                                           logon_info_idx,
-                                           i);
-                               SAFE_FREE(types);
-                               code = EINVAL;
-                               goto done;
-                       }
-                       logon_info_idx = i;
-                       break;
-               case PAC_TYPE_CONSTRAINED_DELEGATION:
-                       if (delegation_idx != -1) {
-                               DBG_WARNING("logon type[%u] twice [%zd] and [%zu]: \n",
-                                           types[i],
-                                           delegation_idx,
-                                           i);
-                               SAFE_FREE(types);
-                               code = EINVAL;
-                               goto done;
-                       }
-                       delegation_idx = i;
-                       break;
-               case PAC_TYPE_LOGON_NAME:
-                       if (logon_name_idx != -1) {
-                               DBG_WARNING("logon type[%u] twice [%zd] and [%zu]: \n",
-                                           types[i],
-                                           logon_name_idx,
-                                           i);
-                               SAFE_FREE(types);
-                               code = EINVAL;
-                               goto done;
-                       }
-                       logon_name_idx = i;
-                       break;
-               case PAC_TYPE_UPN_DNS_INFO:
-                       if (upn_dns_info_idx != -1) {
-                               DBG_WARNING("logon type[%u] twice [%zd] and [%zu]: \n",
-                                           types[i],
-                                           upn_dns_info_idx,
-                                           i);
-                               SAFE_FREE(types);
-                               code = EINVAL;
-                               goto done;
-                       }
-                       upn_dns_info_idx = i;
-                       break;
-               case PAC_TYPE_SRV_CHECKSUM:
-                       if (srv_checksum_idx != -1) {
-                               DBG_WARNING("logon type[%u] twice [%zd] and [%zu]: \n",
-                                           types[i],
-                                           srv_checksum_idx,
-                                           i);
-                               SAFE_FREE(types);
-                               code = EINVAL;
-                               goto done;
-                       }
-                       srv_checksum_idx = i;
-                       break;
-               case PAC_TYPE_KDC_CHECKSUM:
-                       if (kdc_checksum_idx != -1) {
-                               DBG_WARNING("logon type[%u] twice [%zd] and [%zu]: \n",
-                                           types[i],
-                                           kdc_checksum_idx,
-                                           i);
-                               SAFE_FREE(types);
-                               code = EINVAL;
-                               goto done;
-                       }
-                       kdc_checksum_idx = i;
-                       break;
-               default:
-                       continue;
+               nt_status = samba_kdc_get_requester_sid_blob(tmp_ctx,
+                                                            user_info_dc,
+                                                            &requester_sid_blob);
+               if (!NT_STATUS_IS_OK(nt_status)) {
+                       talloc_free(tmp_ctx);
+                       return EINVAL;
                }
        }
 
-       if (logon_info_idx == -1) {
-               DEBUG(1, ("PAC_TYPE_LOGON_INFO missing\n"));
-               SAFE_FREE(types);
-               code = EINVAL;
-               goto done;
-       }
-       if (logon_name_idx == -1) {
-               DEBUG(1, ("PAC_TYPE_LOGON_NAME missing\n"));
-               SAFE_FREE(types);
-               code = EINVAL;
-               goto done;
-       }
-       if (srv_checksum_idx == -1) {
-               DEBUG(1, ("PAC_TYPE_SRV_CHECKSUM missing\n"));
-               SAFE_FREE(types);
-               code = EINVAL;
-               goto done;
-       }
-       if (kdc_checksum_idx == -1) {
-               DEBUG(1, ("PAC_TYPE_KDC_CHECKSUM missing\n"));
-               SAFE_FREE(types);
-               code = EINVAL;
-               goto done;
-       }
-
-       /* Build an updated PAC */
-       code = krb5_pac_init(context, &new_pac);
-       if (code != 0) {
-               SAFE_FREE(types);
-               goto done;
+       nt_status = samba_kdc_get_claims_blob(tmp_ctx,
+                                             skdc_entry,
+                                             &client_claims_blob);
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               talloc_free(tmp_ctx);
+               return EINVAL;
        }
 
-       for (i = 0;;) {
-               krb5_data type_data;
-               DATA_BLOB type_blob = data_blob_null;
-               uint32_t type;
-
-               if (forced_next_type != 0) {
-                       /*
-                        * We need to inject possible missing types
-                        */
-                       type = forced_next_type;
-                       forced_next_type = 0;
-               } else if (i < num_types) {
-                       type = types[i];
-                       i++;
-               } else {
-                       break;
-               }
-
-               switch (type) {
-               case PAC_TYPE_LOGON_INFO:
-                       type_blob = *pac_blob;
-
-                       if (delegation_idx == -1 && deleg_blob != NULL) {
-                               /* inject CONSTRAINED_DELEGATION behind */
-                               forced_next_type = PAC_TYPE_CONSTRAINED_DELEGATION;
-                       }
-                       break;
-               case PAC_TYPE_CONSTRAINED_DELEGATION:
-                       if (deleg_blob != NULL) {
-                               type_blob = *deleg_blob;
-                       }
-                       break;
-               case PAC_TYPE_CREDENTIAL_INFO:
-                       /*
-                        * Note that we copy the credential blob,
-                        * as it's only usable with the PKINIT based
-                        * AS-REP reply key, it's only available on the
-                        * host which did the AS-REQ/AS-REP exchange.
-                        *
-                        * This matches Windows 2008R2...
-                        */
-                       break;
-               case PAC_TYPE_LOGON_NAME:
-                       /*
-                        * This is generated in the main KDC code
-                        */
-                       continue;
-               case PAC_TYPE_UPN_DNS_INFO:
-                       /*
-                        * Replace in the RODC case, otherwise
-                        * upn_blob is NULL and we just copy.
-                        */
-                       if (upn_blob != NULL) {
-                               type_blob = *upn_blob;
-                       }
-                       break;
-               case PAC_TYPE_SRV_CHECKSUM:
-                       /*
-                        * This is generated in the main KDC code
-                        */
-                       continue;
-               case PAC_TYPE_KDC_CHECKSUM:
-                       /*
-                        * This is generated in the main KDC code
-                        */
-                       continue;
-               default:
-                       /* just copy... */
-                       break;
-               }
-
-               if (type_blob.length != 0) {
-                       code = smb_krb5_copy_data_contents(&type_data,
-                                                          type_blob.data,
-                                                          type_blob.length);
-                       if (code != 0) {
-                               SAFE_FREE(types);
-                               krb5_pac_free(context, new_pac);
-                               goto done;
-                       }
-               } else {
-                       code = krb5_pac_get_buffer(context,
-                                                  *pac,
-                                                  type,
-                                                  &type_data);
-                       if (code != 0) {
-                               SAFE_FREE(types);
-                               krb5_pac_free(context, new_pac);
-                               goto done;
-                       }
-               }
-
-               code = krb5_pac_add_buffer(context,
-                                          new_pac,
-                                          type,
-                                          &type_data);
-               smb_krb5_free_data_contents(context, &type_data);
+       if (replaced_reply_key != NULL && cred_ndr != NULL) {
+               code = samba_kdc_encrypt_pac_credentials(context,
+                                                        replaced_reply_key,
+                                                        cred_ndr,
+                                                        tmp_ctx,
+                                                        &cred_blob);
                if (code != 0) {
-                       SAFE_FREE(types);
-                       krb5_pac_free(context, new_pac);
-                       goto done;
+                       talloc_free(tmp_ctx);
+                       return code;
                }
+               pcred_blob = &cred_blob;
        }
 
-       SAFE_FREE(types);
+       code = samba_make_krb5_pac(context,
+                                  logon_info_blob,
+                                  pcred_blob,
+                                  upn_dns_info_blob,
+                                  pac_attrs_blob,
+                                  requester_sid_blob,
+                                  NULL /* deleg_blob */,
+                                  client_claims_blob,
+                                  NULL /* device_info_blob */,
+                                  NULL /* device_claims_blob */,
+                                  *pac);
 
-       /* We now replace the pac */
-       krb5_pac_free(context, *pac);
-       *pac = new_pac;
-done:
        talloc_free(tmp_ctx);
        return code;
 }
-#else
+
 krb5_error_code mit_samba_update_pac(struct mit_samba_context *ctx,
                                    krb5_context context,
-                                   int flags,
+                                   int kdc_flags,
                                    krb5_db_entry *client,
                                    krb5_db_entry *server,
                                    krb5_db_entry *krbtgt,
@@ -955,28 +652,13 @@ krb5_error_code mit_samba_update_pac(struct mit_samba_context *ctx,
 {
        TALLOC_CTX *tmp_ctx = NULL;
        krb5_error_code code;
-       NTSTATUS nt_status;
-       DATA_BLOB *pac_blob = NULL;
-       DATA_BLOB *upn_blob = NULL;
-       DATA_BLOB *requester_sid_blob = NULL;
        struct samba_kdc_entry *client_skdc_entry = NULL;
        struct samba_kdc_entry *server_skdc_entry = NULL;
        struct samba_kdc_entry *krbtgt_skdc_entry = NULL;
+       struct samba_kdc_entry_pac client_pac_entry = {};
        bool is_in_db = false;
-       bool is_untrusted = false;
-       bool is_krbtgt = false;
-       size_t num_types = 0;
-       uint32_t *types = NULL;
-       size_t i = 0;
-       ssize_t logon_info_idx = -1;
-       ssize_t delegation_idx = -1;
-       ssize_t logon_name_idx = -1;
-       ssize_t upn_dns_info_idx = -1;
-       ssize_t srv_checksum_idx = -1;
-       ssize_t kdc_checksum_idx = -1;
-       ssize_t tkt_checksum_idx = -1;
-       ssize_t attrs_info_idx = -1;
-       ssize_t requester_sid_idx = -1;
+       bool is_trusted = false;
+       uint32_t flags = 0;
 
        /* Create a memory context early so code can use talloc_stackframe() */
        tmp_ctx = talloc_named(ctx, 0, "mit_samba_update_pac context");
@@ -988,17 +670,6 @@ krb5_error_code mit_samba_update_pac(struct mit_samba_context *ctx,
                client_skdc_entry =
                        talloc_get_type_abort(client->e_data,
                                              struct samba_kdc_entry);
-
-               /*
-                * Check the objectSID of the client and pac data are the same.
-                * Does a parse and SID check, but no crypto.
-                */
-               code = samba_kdc_validate_pac_blob(context,
-                                                  client_skdc_entry,
-                                                  old_pac);
-               if (code != 0) {
-                       goto done;
-               }
        }
 
        if (krbtgt == NULL) {
@@ -1009,6 +680,17 @@ krb5_error_code mit_samba_update_pac(struct mit_samba_context *ctx,
                talloc_get_type_abort(krbtgt->e_data,
                                      struct samba_kdc_entry);
 
+       /* This sets the time into the DSDB opaque */
+       *ctx->db_ctx->current_nttime_ull = krbtgt_skdc_entry->current_nttime;
+
+       if (server == NULL) {
+               code = EINVAL;
+               goto done;
+       }
+       server_skdc_entry =
+               talloc_get_type_abort(server->e_data,
+                                     struct samba_kdc_entry);
+
        /*
         * If the krbtgt was generated by an RODC, and we are not that
         * RODC, then we need to regenerate the PAC - we can't trust
@@ -1020,377 +702,62 @@ krb5_error_code mit_samba_update_pac(struct mit_samba_context *ctx,
         */
        code = samba_krbtgt_is_in_db(krbtgt_skdc_entry,
                                     &is_in_db,
-                                    &is_untrusted);
+                                    &is_trusted);
        if (code != 0) {
                goto done;
        }
 
-       if (is_untrusted) {
-               struct auth_user_info_dc *user_info_dc = NULL;
-               WERROR werr;
-
-               if (client == NULL) {
-                       code = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
-                       goto done;
-               }
-
-               nt_status = samba_kdc_get_pac_blobs(tmp_ctx,
-                                                   client_skdc_entry,
-                                                   &pac_blob,
-                                                   NULL,
-                                                   &upn_blob,
-                                                   NULL,
-                                                   PAC_ATTRIBUTE_FLAG_PAC_WAS_GIVEN_IMPLICITLY,
-                                                   &requester_sid_blob,
-                                                   &user_info_dc);
-               if (!NT_STATUS_IS_OK(nt_status)) {
-                       code = EINVAL;
-                       goto done;
-               }
-
-               /*
-                * Check if the SID list in the user_info_dc intersects
-                * correctly with the RODC allow/deny lists.
-                */
-               werr = samba_rodc_confirm_user_is_allowed(user_info_dc->num_sids,
-                                                         user_info_dc->sids,
-                                                         krbtgt_skdc_entry,
-                                                         client_skdc_entry);
-               if (!W_ERROR_IS_OK(werr)) {
-                       code = KRB5KDC_ERR_TGT_REVOKED;
-                       if (W_ERROR_EQUAL(werr,
-                                         WERR_DOMAIN_CONTROLLER_NOT_FOUND)) {
-                               code = KRB5KDC_ERR_POLICY;
-                       }
-                       goto done;
-               }
-       } else {
-               pac_blob = talloc_zero(tmp_ctx, DATA_BLOB);
-               if (pac_blob == NULL) {
-                       code = ENOMEM;
-                       goto done;
-               }
-
-               nt_status = samba_kdc_update_pac_blob(tmp_ctx,
-                                                     context,
-                                                     krbtgt_skdc_entry->kdc_db_ctx->samdb,
-                                                     old_pac,
-                                                     pac_blob,
-                                                     NULL,
-                                                     NULL);
-               if (!NT_STATUS_IS_OK(nt_status)) {
-                       DEBUG(0, ("Update PAC blob failed: %s\n",
-                                 nt_errstr(nt_status)));
-                       code = EINVAL;
-                       goto done;
-               }
+       if (kdc_flags & KRB5_KDB_FLAG_PROTOCOL_TRANSITION) {
+               flags |= SAMBA_KDC_FLAG_PROTOCOL_TRANSITION;
        }
 
-       /* Check the types of the given PAC */
-       code = krb5_pac_get_types(context, old_pac, &num_types, &types);
-       if (code != 0) {
-               goto done;
+       if (kdc_flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) {
+               flags |= SAMBA_KDC_FLAG_CONSTRAINED_DELEGATION;
        }
 
-       for (i = 0; i < num_types; i++) {
-               switch (types[i]) {
-               case PAC_TYPE_LOGON_INFO:
-                       if (logon_info_idx != -1) {
-                               DBG_WARNING("logon info type[%u] twice [%zd] and "
-                                           "[%zu]: \n",
-                                           types[i],
-                                           logon_info_idx,
-                                           i);
-                               code = EINVAL;
-                               goto done;
-                       }
-                       logon_info_idx = i;
-                       break;
-               case PAC_TYPE_CONSTRAINED_DELEGATION:
-                       if (delegation_idx != -1) {
-                               DBG_WARNING("constrained delegation type[%u] "
-                                           "twice [%zd] and [%zu]: \n",
-                                           types[i],
-                                           delegation_idx,
-                                           i);
-                               code = EINVAL;
-                               goto done;
-                       }
-                       delegation_idx = i;
-                       break;
-               case PAC_TYPE_LOGON_NAME:
-                       if (logon_name_idx != -1) {
-                               DBG_WARNING("logon name type[%u] twice [%zd] "
-                                           "and [%zu]: \n",
-                                           types[i],
-                                           logon_name_idx,
-                                           i);
-                               code = EINVAL;
-                               goto done;
-                       }
-                       logon_name_idx = i;
-                       break;
-               case PAC_TYPE_UPN_DNS_INFO:
-                       if (upn_dns_info_idx != -1) {
-                               DBG_WARNING("upn dns info type[%u] twice [%zd] "
-                                           "and [%zu]: \n",
-                                           types[i],
-                                           upn_dns_info_idx,
-                                           i);
-                               code = EINVAL;
-                               goto done;
-                       }
-                       upn_dns_info_idx = i;
-                       break;
-               case PAC_TYPE_SRV_CHECKSUM:
-                       if (srv_checksum_idx != -1) {
-                               DBG_WARNING("srv checksum type[%u] twice [%zd] "
-                                           "and [%zu]: \n",
-                                           types[i],
-                                           srv_checksum_idx,
-                                           i);
-                               code = EINVAL;
-                               goto done;
-                       }
-                       srv_checksum_idx = i;
-                       break;
-               case PAC_TYPE_KDC_CHECKSUM:
-                       if (kdc_checksum_idx != -1) {
-                               DBG_WARNING("kdc checksum type[%u] twice [%zd] "
-                                           "and [%zu]: \n",
-                                           types[i],
-                                           kdc_checksum_idx,
-                                           i);
-                               code = EINVAL;
-                               goto done;
-                       }
-                       kdc_checksum_idx = i;
-                       break;
-               case PAC_TYPE_TICKET_CHECKSUM:
-                       if (tkt_checksum_idx != -1) {
-                               DBG_WARNING("ticket checksum type[%u] twice "
-                                           "[%zd] and [%zu]: \n",
-                                           types[i],
-                                           tkt_checksum_idx,
-                                           i);
-                               code = EINVAL;
-                               goto done;
-                       }
-                       tkt_checksum_idx = i;
-                       break;
-               case PAC_TYPE_ATTRIBUTES_INFO:
-                       if (attrs_info_idx != -1) {
-                               DBG_WARNING("attributes info type[%u] twice "
-                                           "[%zd] and [%zu]: \n",
-                                           types[i],
-                                           attrs_info_idx,
-                                           i);
-                               code = EINVAL;
-                               goto done;
-                       }
-                       attrs_info_idx = i;
-                       break;
-               case PAC_TYPE_REQUESTER_SID:
-                       if (requester_sid_idx != -1) {
-                               DBG_WARNING("requester sid type[%u] twice"
-                                           "[%zd] and [%zu]: \n",
-                                           types[i],
-                                           requester_sid_idx,
-                                           i);
-                               code = EINVAL;
-                               goto done;
-                       }
-                       requester_sid_idx = i;
-                       break;
-               default:
-                       continue;
-               }
-       }
+       client_pac_entry = samba_kdc_entry_pac_from_trusted(old_pac,
+                                                           client_skdc_entry,
+                                                           samba_kdc_entry_is_trust(krbtgt_skdc_entry),
+                                                           is_trusted);
 
-       if (logon_info_idx == -1) {
-               DBG_WARNING("PAC_TYPE_LOGON_INFO missing\n");
-               code = EINVAL;
-               goto done;
-       }
-       if (logon_name_idx == -1) {
-               DBG_WARNING("PAC_TYPE_LOGON_NAME missing\n");
-               code = EINVAL;
-               goto done;
-       }
-       if (srv_checksum_idx == -1) {
-               DBG_WARNING("PAC_TYPE_SRV_CHECKSUM missing\n");
-               code = EINVAL;
-               goto done;
-       }
-       if (kdc_checksum_idx == -1) {
-               DBG_WARNING("PAC_TYPE_KDC_CHECKSUM missing\n");
-               code = EINVAL;
-               goto done;
-       }
-       if (!(flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) &&
-           requester_sid_idx == -1) {
-               DBG_WARNING("PAC_TYPE_REQUESTER_SID missing\n");
-               code = KRB5KDC_ERR_TGT_REVOKED;
-               goto done;
-       }
-
-       server_skdc_entry = talloc_get_type_abort(server->e_data,
-                                                 struct samba_kdc_entry);
-
-       /*
-        * The server account may be set not to want the PAC.
-        *
-        * While this is wasteful if the above cacluations were done
-        * and now thrown away, this is cleaner as we do any ticket
-        * signature checking etc always.
-        *
-        * UF_NO_AUTH_DATA_REQUIRED is the rare case and most of the
-        * time (eg not accepting a ticket from the RODC) we do not
-        * need to re-generate anything anyway.
-        */
-       if (!samba_princ_needs_pac(server_skdc_entry)) {
-               code = 0;
+       code = samba_kdc_verify_pac(tmp_ctx,
+                                   context,
+                                   krbtgt_skdc_entry->kdc_db_ctx->samdb,
+                                   flags,
+                                   client_pac_entry,
+                                   krbtgt_skdc_entry);
+       if (code != 0) {
                goto done;
        }
 
-       is_krbtgt = ks_is_tgs_principal(ctx, server->princ);
-
-       if (!is_untrusted && !is_krbtgt) {
-               /*
-                * The client may have requested no PAC when obtaining the
-                * TGT.
-                */
-               bool requested_pac = false;
-
-               code = samba_client_requested_pac(context,
-                                                 &old_pac,
-                                                 tmp_ctx,
-                                                 &requested_pac);
-               if (code != 0 || !requested_pac) {
-                       goto done;
-               }
-       }
-
-#define MAX_PAC_BUFFERS 64 /* Avoid infinite loops */
-
-       for (i = 0; i < MAX_PAC_BUFFERS;) {
-               krb5_data type_data;
-               DATA_BLOB type_blob = data_blob_null;
-               uint32_t type;
-
-               if (i < num_types) {
-                       type = types[i];
-                       i++;
-               } else {
-                       break;
-               }
-
-               switch (type) {
-               case PAC_TYPE_LOGON_INFO:
-                       type_blob = *pac_blob;
-                       break;
-               case PAC_TYPE_CREDENTIAL_INFO:
-                       /*
-                        * Note that we copy the credential blob,
-                        * as it's only usable with the PKINIT based
-                        * AS-REP reply key, it's only available on the
-                        * host which did the AS-REQ/AS-REP exchange.
-                        *
-                        * This matches Windows 2008R2...
-                        */
-                       break;
-               case PAC_TYPE_LOGON_NAME:
-                       /*
-                        * This is generated in the main KDC code
-                        */
-                       continue;
-               case PAC_TYPE_UPN_DNS_INFO:
-                       /*
-                        * Replace in the RODC case, otherwise
-                        * upn_blob is NULL and we just copy.
-                        */
-                       if (upn_blob != NULL) {
-                               type_blob = *upn_blob;
-                       }
-                       break;
-               case PAC_TYPE_SRV_CHECKSUM:
-                       /*
-                        * This is generated in the main KDC code
-                        */
-                       continue;
-               case PAC_TYPE_KDC_CHECKSUM:
-                       /*
-                        * This is generated in the main KDC code
-                        */
-                       continue;
-               case PAC_TYPE_TICKET_CHECKSUM:
-                       /*
-                        * This is generated in the main KDC code
-                        */
-                       continue;
-               case PAC_TYPE_CONSTRAINED_DELEGATION:
+       code = samba_kdc_update_pac(tmp_ctx,
+                                   context,
+                                   krbtgt_skdc_entry->kdc_db_ctx->samdb,
+                                   krbtgt_skdc_entry->kdc_db_ctx->lp_ctx,
+                                   flags,
+                                   client_pac_entry,
+                                   server->princ,
+                                   server_skdc_entry,
+                                   NULL /* delegated_proxy_principal */,
+                                   (struct samba_kdc_entry_pac) {} /* delegated_proxy */,
+                                   (struct samba_kdc_entry_pac) {} /* device */,
+                                   new_pac,
+                                   NULL /* server_audit_info_out */,
+                                   NULL /* status_out */);
+       if (code != 0) {
+               if (code == ENOATTR) {
                        /*
-                        * This is generated in the main KDC code
+                        * We can't tell the KDC to not issue a PAC. It will
+                        * just return the newly allocated empty PAC.
                         */
-                       continue;
-               case PAC_TYPE_ATTRIBUTES_INFO:
-                       if (!is_untrusted && is_krbtgt) {
-                               /* just copy... */
-                               break;
-                       } else {
-                               continue;
-                       }
-               case PAC_TYPE_REQUESTER_SID:
-                       if (is_krbtgt) {
-                               /*
-                                * Replace in the RODC case, otherwise
-                                * requester_sid_blob is NULL and we just copy.
-                                */
-                               if (requester_sid_blob != NULL) {
-                                       type_blob = *requester_sid_blob;
-                               }
-                               break;
-                       } else {
-                               continue;
-                       }
-               default:
-                       /* just copy... */
-                       break;
-               }
-
-               if (type_blob.length != 0) {
-                       code = smb_krb5_copy_data_contents(&type_data,
-                                                          type_blob.data,
-                                                          type_blob.length);
-                       if (code != 0) {
-                               goto done;
-                       }
-               } else {
-                       code = krb5_pac_get_buffer(context,
-                                                  old_pac,
-                                                  type,
-                                                  &type_data);
-                       if (code != 0) {
-                               goto done;
-                       }
-               }
-
-               code = krb5_pac_add_buffer(context,
-                                          new_pac,
-                                          type,
-                                          &type_data);
-               smb_krb5_free_data_contents(context, &type_data);
-               if (code != 0) {
-                       goto done;
+                       code = 0;
                }
        }
 
 done:
-       SAFE_FREE(types);
        talloc_free(tmp_ctx);
        return code;
 }
-#endif
 
 /* provide header, function is exported but there are no public headers */
 
@@ -1411,7 +778,7 @@ static void samba_kdc_build_edata_reply(NTSTATUS nt_status, DATA_BLOB *e_data)
        e_data->length = 0;
 
        pa.magic                = KV5M_PA_DATA;
-       pa.pa_type              = KRB5_PADATA_PW_SALT;
+       pa.pa_type              = KRB5_PADATA_PW_SALT /* KERB_ERR_TYPE_EXTENDED */;
        pa.length               = 12;
        pa.contents             = malloc(pa.length);
        if (!pa.contents) {
@@ -1440,20 +807,23 @@ static void samba_kdc_build_edata_reply(NTSTATUS nt_status, DATA_BLOB *e_data)
        return;
 }
 
-int mit_samba_check_client_access(struct mit_samba_context *ctx,
-                                 krb5_db_entry *client,
-                                 const char *client_name,
-                                 krb5_db_entry *server,
-                                 const char *server_name,
-                                 const char *netbios_name,
-                                 bool password_change,
-                                 DATA_BLOB *e_data)
+krb5_error_code mit_samba_check_client_access(struct mit_samba_context *ctx,
+                                             krb5_db_entry *client,
+                                             const char *client_name,
+                                             krb5_db_entry *server,
+                                             const char *server_name,
+                                             const char *netbios_name,
+                                             bool password_change,
+                                             DATA_BLOB *e_data)
 {
        struct samba_kdc_entry *skdc_entry;
        NTSTATUS nt_status;
 
        skdc_entry = talloc_get_type(client->e_data, struct samba_kdc_entry);
 
+       /* This sets the time into the DSDB opaque */
+       *ctx->db_ctx->current_nttime_ull = skdc_entry->current_nttime;
+
        nt_status = samba_kdc_check_client_access(skdc_entry,
                                                  client_name,
                                                  netbios_name,
@@ -1472,24 +842,23 @@ int mit_samba_check_client_access(struct mit_samba_context *ctx,
        return 0;
 }
 
-int mit_samba_check_s4u2proxy(struct mit_samba_context *ctx,
-                             const krb5_db_entry *server,
-                             krb5_const_principal target_principal)
+krb5_error_code mit_samba_check_s4u2proxy(struct mit_samba_context *ctx,
+                                         const krb5_db_entry *server,
+                                         krb5_const_principal target_principal)
 {
-#if KRB5_KDB_DAL_MAJOR_VERSION < 9
-       return KRB5KDC_ERR_BADOPTION;
-#else
        struct samba_kdc_entry *server_skdc_entry =
                talloc_get_type_abort(server->e_data, struct samba_kdc_entry);
        krb5_error_code code;
 
+       /* This sets the time into the DSDB opaque */
+       *ctx->db_ctx->current_nttime_ull = server_skdc_entry->current_nttime;
+
        code = samba_kdc_check_s4u2proxy(ctx->context,
                                         ctx->db_ctx,
                                         server_skdc_entry,
                                         target_principal);
 
        return code;
-#endif
 }
 
 krb5_error_code mit_samba_check_allowed_to_delegate_from(
@@ -1499,22 +868,49 @@ krb5_error_code mit_samba_check_allowed_to_delegate_from(
                krb5_pac header_pac,
                const krb5_db_entry *proxy)
 {
-#if KRB5_KDB_DAL_MAJOR_VERSION < 8
-       return KRB5KDC_ERR_POLICY;
-#else
        struct samba_kdc_entry *proxy_skdc_entry =
                talloc_get_type_abort(proxy->e_data, struct samba_kdc_entry);
+       struct auth_user_info_dc *user_info_dc = NULL;
+       TALLOC_CTX *mem_ctx = NULL;
        krb5_error_code code;
 
+       /* This sets the time into the DSDB opaque */
+       *ctx->db_ctx->current_nttime_ull = proxy_skdc_entry->current_nttime;
+
+       mem_ctx = talloc_new(NULL);
+       if (mem_ctx == NULL) {
+               return ENOMEM;
+       }
+
+       /*
+        * FIXME: If ever we support RODCs, we must check that the PAC has not
+        * been issued by an RODC (other than ourselves) — otherwise the PAC
+        * cannot be trusted. Because the plugin interface does not give us the
+        * client entry, we cannot look up its groups in the database.
+        */
+       code = kerberos_pac_to_user_info_dc(mem_ctx,
+                                           header_pac,
+                                           ctx->context,
+                                           &user_info_dc,
+                                           AUTH_INCLUDE_RESOURCE_GROUPS,
+                                           NULL,
+                                           NULL,
+                                           NULL);
+       if (code != 0) {
+               goto out;
+       }
+
        code = samba_kdc_check_s4u2proxy_rbcd(ctx->context,
                                              ctx->db_ctx,
                                              client_principal,
                                              server_principal,
-                                             header_pac,
+                                             user_info_dc,
+                                             NULL /* device_info_dc */,
+                                             (struct auth_claims) {},
                                              proxy_skdc_entry);
-
+out:
+       talloc_free(mem_ctx);
        return code;
-#endif
 }
 
 static krb5_error_code mit_samba_change_pwd_error(krb5_context context,
@@ -1581,9 +977,9 @@ static krb5_error_code mit_samba_change_pwd_error(krb5_context context,
        return code;
 }
 
-int mit_samba_kpasswd_change_password(struct mit_samba_context *ctx,
-                                     char *pwd,
-                                     krb5_db_entry *db_entry)
+krb5_error_code mit_samba_kpasswd_change_password(struct mit_samba_context *ctx,
+                                                 char *pwd,
+                                                 krb5_db_entry *db_entry)
 {
        NTSTATUS status;
        NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
@@ -1592,13 +988,16 @@ int mit_samba_kpasswd_change_password(struct mit_samba_context *ctx,
        enum samPwdChangeReason reject_reason;
        struct samr_DomInfo1 *dominfo;
        const char *error_string = NULL;
-       struct auth_user_info_dc *user_info_dc;
+       const struct auth_user_info_dc *user_info_dc = NULL;
        struct samba_kdc_entry *p =
                talloc_get_type_abort(db_entry->e_data, struct samba_kdc_entry);
        krb5_error_code code = 0;
 
+       /* This sets the time into the DSDB opaque */
+       *ctx->db_ctx->current_nttime_ull = p->current_nttime;
+
 #ifdef DEBUG_PASSWORD
-       DEBUG(1,("mit_samba_kpasswd_change_password called with: %s\n", pwd));
+       DBG_WARNING("mit_samba_kpasswd_change_password called with: %s\n", pwd);
 #endif
 
        tmp_ctx = talloc_named(ctx, 0, "mit_samba_kpasswd_change_password");
@@ -1606,21 +1005,18 @@ int mit_samba_kpasswd_change_password(struct mit_samba_context *ctx,
                return ENOMEM;
        }
 
-       status = authsam_make_user_info_dc(tmp_ctx,
-                                          ctx->db_ctx->samdb,
-                                          lpcfg_netbios_name(ctx->db_ctx->lp_ctx),
-                                          lpcfg_sam_name(ctx->db_ctx->lp_ctx),
-                                          lpcfg_sam_dnsname(ctx->db_ctx->lp_ctx),
-                                          p->realm_dn,
-                                          p->msg,
-                                          data_blob(NULL, 0),
-                                          data_blob(NULL, 0),
-                                          &user_info_dc);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(1,("authsam_make_user_info_dc failed: %s\n",
-                       nt_errstr(status)));
-               talloc_free(tmp_ctx);
-               return EINVAL;
+       code = samba_kdc_get_user_info_from_db(tmp_ctx,
+                                              ctx->db_ctx->samdb,
+                                              p,
+                                              p->msg,
+                                              &user_info_dc);
+       if (code) {
+               const char *krb5err = krb5_get_error_message(ctx->context, code);
+               DBG_WARNING("samba_kdc_get_user_info_from_db failed: %s\n",
+                       krb5err != NULL ? krb5err : "<unknown>");
+               krb5_free_error_message(ctx->context, krb5err);
+
+               goto out;
        }
 
        status = auth_generate_session_info(tmp_ctx,
@@ -1631,10 +1027,10 @@ int mit_samba_kpasswd_change_password(struct mit_samba_context *ctx,
                                            &ctx->session_info);
 
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(1,("auth_generate_session_info failed: %s\n",
-                       nt_errstr(status)));
-               talloc_free(tmp_ctx);
-               return EINVAL;
+               DBG_WARNING("auth_generate_session_info failed: %s\n",
+                           nt_errstr(status));
+               code = EINVAL;
+               goto out;
        }
 
        /* password is expected as UTF16 */
@@ -1642,15 +1038,14 @@ int mit_samba_kpasswd_change_password(struct mit_samba_context *ctx,
        if (!convert_string_talloc(tmp_ctx, CH_UTF8, CH_UTF16,
                                   pwd, strlen(pwd),
                                   &password.data, &password.length)) {
-               DEBUG(1,("convert_string_talloc failed\n"));
-               talloc_free(tmp_ctx);
-               return EINVAL;
+               DBG_WARNING("convert_string_talloc failed\n");
+               code = EINVAL;
+               goto out;
        }
 
        status = samdb_kpasswd_change_password(tmp_ctx,
                                               ctx->db_ctx->lp_ctx,
                                               ctx->db_ctx->ev_ctx,
-                                              ctx->db_ctx->samdb,
                                               ctx->session_info,
                                               &password,
                                               &reject_reason,
@@ -1658,8 +1053,8 @@ int mit_samba_kpasswd_change_password(struct mit_samba_context *ctx,
                                               &error_string,
                                               &result);
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(1,("samdb_kpasswd_change_password failed: %s\n",
-                       nt_errstr(status)));
+               DBG_WARNING("samdb_kpasswd_change_password failed: %s\n",
+                           nt_errstr(status));
                code = KADM5_PASS_Q_GENERIC;
                krb5_set_error_message(ctx->context, code, "%s", error_string);
                goto out;
@@ -1680,18 +1075,21 @@ out:
 
 void mit_samba_zero_bad_password_count(krb5_db_entry *db_entry)
 {
-       struct netr_SendToSamBase *send_to_sam = NULL;
+       /* struct netr_SendToSamBase *send_to_sam = NULL; */
        struct samba_kdc_entry *p =
                talloc_get_type_abort(db_entry->e_data, struct samba_kdc_entry);
        struct ldb_dn *domain_dn;
 
+       /* This sets the time into the DSDB opaque */
+       *p->kdc_db_ctx->current_nttime_ull = p->current_nttime;
+
        domain_dn = ldb_get_default_basedn(p->kdc_db_ctx->samdb);
 
        authsam_logon_success_accounting(p->kdc_db_ctx->samdb,
                                         p->msg,
                                         domain_dn,
                                         true,
-                                        &send_to_sam);
+                                        NULL, NULL);
        /* TODO: RODC support */
 }
 
@@ -1701,6 +1099,9 @@ void mit_samba_update_bad_password_count(krb5_db_entry *db_entry)
        struct samba_kdc_entry *p =
                talloc_get_type_abort(db_entry->e_data, struct samba_kdc_entry);
 
+       /* This sets the time into the DSDB opaque */
+       *p->kdc_db_ctx->current_nttime_ull = p->current_nttime;
+
        authsam_update_bad_pwd_count(p->kdc_db_ctx->samdb,
                                     p->msg,
                                     ldb_get_default_basedn(p->kdc_db_ctx->samdb));
@@ -1711,5 +1112,8 @@ bool mit_samba_princ_needs_pac(krb5_db_entry *db_entry)
        struct samba_kdc_entry *skdc_entry =
                talloc_get_type_abort(db_entry->e_data, struct samba_kdc_entry);
 
+       /* This sets the time into the DSDB opaque */
+       *skdc_entry->kdc_db_ctx->current_nttime_ull = skdc_entry->current_nttime;
+
        return samba_princ_needs_pac(skdc_entry);
 }