krb5 commit: Replace AD-SIGNEDPATH with minimal PACs

Greg Hudson <[email protected]>
Newsgroups gmane.comp.encryption.kerberos.cvs
Message-ID <[email protected]>
https://github.com/krb5/krb5/commit/a441fbe329ebbd7775eb5d4ccc4a05eef370f08b
commit a441fbe329ebbd7775eb5d4ccc4a05eef370f08b
Author: Greg Hudson <[email protected]>
Date:   Fri Jan 7 22:41:30 2022 -0500

    Replace AD-SIGNEDPATH with minimal PACs
    
    Remove all of the AD-SIGNEDPATH code.  Instead, issue a signed minimal
    PAC in all tickets and require a valid PAC to be present in all
    tickets presented for S4U operations.  Remove the get_authdata_info()
    and sign_authdata() DAL methods, and add an issue_pac() method to
    allow the KDB to add or copy buffers to the PAC.  Add a disable_pac
    realm flag.
    
    Microsoft revised the S4U2Proxy rules for forwardable tickets.  All
    S4U2Proxy operations require forwardable evidence tickets, but
    S4U2Self should issue a forwardable ticket if the requesting service
    has no ok-to-auth-as-delegate bit but also no constrained delegation
    privileges for traditional S4U2Proxy.  Implement these rules,
    extending the check_allowed_to_delegate() DAL method so that the KDC
    can ask if a principal has any delegation privileges.
    
    Combine the KRB5_KDB_FLAG_ISSUE_PAC and
    KRB5_FLAG_CLIENT_REFERRALS_ONLY flags into KRB5_KDB_FLAG_CLIENT.
    
    Rename the KRB5_KDB_FLAG_CANONICALIZE flag to
    KRB5_KDB_FLAG_REFERRAL_OK, and only pass it to get_principal() for
    lookup operations that can use a realm referral.
    
    For consistency with Active Directory, honor the no-auth-data-required
    server principal flag for S4U2Proxy but not for S4U2Self.  Previously
    we did the reverse.
    
    ticket: 9044 (new)

 doc/admin/conf_files/kdc_conf.rst           |    6 +
 src/include/k5-int.h                        |   26 +-
 src/include/kdb.h                           |  330 ++++++---------
 src/kdc/do_as_req.c                         |   33 +-
 src/kdc/do_tgs_req.c                        |  131 +++---
 src/kdc/kdc_authdata.c                      |  651 ++++++++-------------------
 src/kdc/kdc_util.c                          |  221 +++++++---
 src/kdc/kdc_util.h                          |   64 ++-
 src/kdc/main.c                              |    5 +
 src/kdc/realm_data.h                        |    1 +
 src/kdc/tgs_policy.c                        |  222 ++++++++--
 src/lib/kdb/kdb5.c                          |   99 +----
 src/lib/kdb/libkdb5.exports                 |    4 +-
 src/lib/krb5/asn.1/asn1_k_encode.c          |   30 --
 src/lib/krb5/krb/Makefile.in                |    5 +-
 src/lib/krb5/krb/authdata.c                 |    1 -
 src/lib/krb5/krb/deps                       |   12 -
 src/lib/krb5/krb/kfree.c                    |   18 -
 src/lib/krb5/krb/s4u_authdata.c             |  598 ------------------------
 src/lib/krb5/libkrb5.exports                |    4 -
 src/plugins/kdb/db2/db2_exp.c               |   13 +-
 src/plugins/kdb/ldap/ldap_exp.c             |    1 -
 src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.c |    4 +-
 src/plugins/kdb/test/kdb_test.c             |  530 ++++------------------
 src/tests/asn.1/krb5_decode_leak.c          |   10 -
 src/tests/asn.1/krb5_decode_test.c          |    8 -
 src/tests/asn.1/krb5_encode_test.c          |   17 -
 src/tests/asn.1/ktest.c                     |   55 ---
 src/tests/asn.1/ktest.h                     |    4 -
 src/tests/asn.1/ktest_equal.c               |   28 --
 src/tests/asn.1/ktest_equal.h               |    4 -
 src/tests/asn.1/reference_encode.out        |    2 -
 src/tests/asn.1/trval_reference.out         |   49 --
 src/tests/gssapi/t_s4u.py                   |   20 +-
 src/tests/t_authdata.py                     |   76 +---
 35 files changed, 957 insertions(+), 2325 deletions(-)

diff --git a/doc/admin/conf_files/kdc_conf.rst b/doc/admin/conf_files/kdc_conf.rst
index 1dc958d..74a0a2a 100644
--- a/doc/admin/conf_files/kdc_conf.rst
+++ b/doc/admin/conf_files/kdc_conf.rst
@@ -208,6 +208,12 @@ The following tags may be specified in a [realms] subsection:
     if there is no policy assigned to the principal, no dictionary
     checks of passwords will be performed.
 
+**disable_pac**
+    (Boolean value.)  If true, the KDC will not issue PACs for this
+    realm, and S4U2Self and S4U2Proxy operations will be disabled.
+    The default is false, which will permit the KDC to issue PACs.
+    New in release 1.20.
+
 **encrypted_challenge_indicator**
     (String.)  Specifies the authentication indicator value that the KDC
     asserts into tickets obtained using FAST encrypted challenge
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index 12aeb1e..44dc1ee 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -205,6 +205,7 @@ typedef unsigned char   u_char;
 #define KRB5_CONF_DISABLE_ENCRYPTED_TIMESTAMP  "disable_encrypted_timestamp"
 #define KRB5_CONF_DISABLE_LAST_SUCCESS         "disable_last_success"
 #define KRB5_CONF_DISABLE_LOCKOUT              "disable_lockout"
+#define KRB5_CONF_DISABLE_PAC                  "disable_pac"
 #define KRB5_CONF_DNS_CANONICALIZE_HOSTNAME    "dns_canonicalize_hostname"
 #define KRB5_CONF_DNS_FALLBACK                 "dns_fallback"
 #define KRB5_CONF_DNS_LOOKUP_KDC               "dns_lookup_kdc"
@@ -816,21 +817,6 @@ typedef struct _krb5_ad_kdcissued {
     krb5_authdata **elements;
 } krb5_ad_kdcissued;
 
-typedef struct _krb5_ad_signedpath_data {
-    krb5_principal client;
-    krb5_timestamp authtime;
-    krb5_principal *delegated;
-    krb5_pa_data **method_data;
-    krb5_authdata **authorization_data;
-} krb5_ad_signedpath_data;
-
-typedef struct _krb5_ad_signedpath {
-    krb5_enctype enctype;
-    krb5_checksum checksum;
-    krb5_principal *delegated;
-    krb5_pa_data **method_data;
-} krb5_ad_signedpath;
-
 typedef struct _krb5_iakerb_header {
     krb5_data target_realm;
     krb5_data *cookie;
@@ -949,7 +935,6 @@ void KRB5_CALLCONV krb5_free_fast_req(krb5_context, krb5_fast_req *);
 void KRB5_CALLCONV krb5_free_fast_finished(krb5_context, krb5_fast_finished *);
 void KRB5_CALLCONV krb5_free_fast_response(krb5_context, krb5_fast_response *);
 void KRB5_CALLCONV krb5_free_ad_kdcissued(krb5_context, krb5_ad_kdcissued *);
-void KRB5_CALLCONV krb5_free_ad_signedpath(krb5_context, krb5_ad_signedpath *);
 void KRB5_CALLCONV krb5_free_iakerb_header(krb5_context, krb5_iakerb_header *);
 void KRB5_CALLCONV krb5_free_iakerb_finished(krb5_context,
                                              krb5_iakerb_finished *);
@@ -1514,12 +1499,6 @@ krb5_error_code
 encode_krb5_ad_kdcissued(const krb5_ad_kdcissued *, krb5_data **);
 
 krb5_error_code
-encode_krb5_ad_signedpath(const krb5_ad_signedpath *, krb5_data **);
-
-krb5_error_code
-encode_krb5_ad_signedpath_data(const krb5_ad_signedpath_data *, krb5_data **);
-
-krb5_error_code
 encode_krb5_otp_tokeninfo(const krb5_otp_tokeninfo *, krb5_data **);
 
 krb5_error_code
@@ -1696,9 +1675,6 @@ krb5_error_code
 decode_krb5_ad_kdcissued(const krb5_data *, krb5_ad_kdcissued **);
 
 krb5_error_code
-decode_krb5_ad_signedpath(const krb5_data *, krb5_ad_signedpath **);
-
-krb5_error_code
 decode_krb5_iakerb_header(const krb5_data *, krb5_iakerb_header **);
 
 krb5_error_code
diff --git a/src/include/kdb.h b/src/include/kdb.h
index f6cbb47..1fa7bc5 100644
--- a/src/include/kdb.h
+++ b/src/include/kdb.h
@@ -105,12 +105,10 @@
 #define KRB5_KDB_CREATE_HASH            0x00000002
 
 /* Entry get flags */
-/* Name canonicalization requested */
-#define KRB5_KDB_FLAG_CANONICALIZE              0x00000010
-/* Include authorization data generated by backend */
-#define KRB5_KDB_FLAG_INCLUDE_PAC               0x00000020
-/* Is AS-REQ (client referrals only) */
-#define KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY     0x00000040
+/* Okay to generate a referral on lookup */
+#define KRB5_KDB_FLAG_REFERRAL_OK               0x00000010
+/* Client principal lookup (client referrals only) */
+#define KRB5_KDB_FLAG_CLIENT                    0x00000040
 /* Map cross-realm principals */
 #define KRB5_KDB_FLAG_MAP_PRINCIPALS            0x00000080
 /* Protocol transition */
@@ -656,25 +654,6 @@ krb5_db_get_key_data_kvno( krb5_context    context,
                            int             count,
                            krb5_key_data * data);
 
-krb5_error_code krb5_db_sign_authdata(krb5_context kcontext,
-                                      unsigned int flags,
-                                      krb5_const_principal client_princ,
-                                      krb5_const_principal server_princ,
-                                      krb5_db_entry *client,
-                                      krb5_db_entry *server,
-                                      krb5_db_entry *header_server,
-                                      krb5_db_entry *local_tgt,
-                                      krb5_keyblock *client_key,
-                                      krb5_keyblock *server_key,
-                                      krb5_keyblock *header_key,
-                                      krb5_keyblock *local_tgt_key,
-                                      krb5_keyblock *session_key,
-                                      krb5_timestamp authtime,
-                                      krb5_authdata **tgt_auth_data,
-                                      void *ad_info,
-                                      krb5_data ***auth_indicators,
-                                      krb5_authdata ***signed_auth_data);
-
 krb5_error_code krb5_db_check_transited_realms(krb5_context kcontext,
                                                const krb5_data *tr_contents,
                                                const krb5_data *client_realm,
@@ -717,23 +696,9 @@ krb5_error_code krb5_db_get_s4u_x509_principal(krb5_context kcontext,
 krb5_error_code krb5_db_allowed_to_delegate_from(krb5_context context,
                                                  krb5_const_principal client,
                                                  krb5_const_principal server,
-                                                 void *server_ad_info,
+                                                 krb5_pac server_pac,
                                                  const krb5_db_entry *proxy);
 
-krb5_error_code krb5_db_get_authdata_info(krb5_context context,
-                                          unsigned int flags,
-                                          krb5_authdata **in_authdata,
-                                          krb5_const_principal client_princ,
-                                          krb5_const_principal server_princ,
-                                          krb5_keyblock *server_key,
-                                          krb5_keyblock *krbtgt_key,
-                                          krb5_db_entry *krbtgt,
-                                          krb5_timestamp authtime,
-                                          void **ad_info_out,
-                                          krb5_principal *client_out);
-
-void krb5_db_free_authdata_info(krb5_context context, void *ad_info);
-
 /**
  * Sort an array of @a krb5_key_data keys in descending order by their kvno.
  * Key data order within a kvno is preserved.
@@ -747,6 +712,13 @@ void krb5_db_free_authdata_info(krb5_context context, void *ad_info);
 void
 krb5_dbe_sort_key_data(krb5_key_data *key_data, size_t key_data_length);
 
+krb5_error_code
+krb5_db_issue_pac(krb5_context context, unsigned int flags,
+                  krb5_db_entry *client, krb5_keyblock *replaced_reply_key,
+                  krb5_db_entry *server, krb5_db_entry *krbtgt,
+                  krb5_timestamp authtime, krb5_pac old_pac, krb5_pac new_pac,
+                  krb5_data ***auth_indicators);
+
 /* default functions. Should not be directly called */
 /*
  *   Default functions prototype
@@ -894,7 +866,35 @@ krb5_error_code krb5_db_register_keytab(krb5_context context);
  * This number indicates the date of the last incompatible change to the DAL.
  * The maj_ver field of the module's vtable structure must match this version.
  */
-#define KRB5_KDB_DAL_MAJOR_VERSION 8
+#define KRB5_KDB_DAL_MAJOR_VERSION 9
+
+/*
+ * Note the following when converting a module to DAL version 9:
+ *
+ * - get_authdata_info() and sign_authdata() have been removed, and issue_pac()
+ *   has been added.
+ *
+ * - check_allowed_to_delegate() must handle a null proxy argument, returning
+ *   success if server has any authorized delegation targets in the traditional
+ *   scheme.
+ *
+ * - allowed_to_delegate_from() accepts a krb5_pac parameter (in place
+ *   server_ad_info) for the impersonator's PAC.
+ *
+ * - check_allowed_to_delegate() and allowed_to_delegate_from() must return
+ *   KRB5KDC_ERR_BADOPTION on authorization failure.
+ *
+ * - the KRB5_KDB_FLAG_ISSUE_PAC and KRB5_FLAG_CLIENT_REFERRALS_ONLY flags have
+ *   been combined into KRB5_KDB_FLAG_CLIENT.
+ *
+ * - the KRB5_KDB_FLAG_CANONICALIZE flag has been renamed to
+ *   KRB5_KDB_FLAG_REFERRAL_OK, and is only passed to get_principal() when a
+ *   realm referral is allowed (AS client and TGS server lookups, when the
+ *   CANONICALIZE option is requested or, for AS requests, when the client is
+ *   an enterprise principal).  As of DAL version 8 the KDB module should
+ *   always canonicalize aliases within a realm; the KDC will decide whether to
+ *   use the original or canonical principal.
+ */
 
 /*
  * A krb5_context can hold one database object.  Modules should use
@@ -1008,17 +1008,14 @@ typedef struct _kdb_vftabl {
      *
      * The meaning of flags are as follows:
      *
-     * KRB5_KDB_FLAG_CANONICALIZE: Set by the KDC when looking up entries for
-     *     an AS or TGS request with canonicalization requested.  Determines
-     *     whether the module should return out-of-realm referrals.
-     *
-     * KRB5_KDB_FLAG_INCLUDE_PAC: Set by the KDC during an AS request when the
-     *     client requested PAC information during padata, and during most TGS
-     *     requests.  Indicates that the module should include PAC information
-     *     when its sign_authdata method is invoked.
+     * KRB5_KDB_FLAG_REFERRAL_OK: Set by the KDC when looking up entries for an
+     *     AS client with canonicalization requested or for an enterprise
+     *     principal, or for a TGS request server with canonicalization
+     *     requested.  Determines whether the module should return out-of-realm
+     *     referrals.
      *
-     * KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY: Set by the KDC when looking up the
-     *     client entry in an AS request.  Affects how the module should return
+     * KRB5_KDB_FLAG_CLIENT: Set by the KDC when looking up a client principal
+     *     during an AS or TGS request.  Affects how the module should return
      *     out-of-realm referrals.
      *
      * KRB5_KDB_FLAG_MAP_PRINCIPALS: Set by the KDC when looking up the client
@@ -1049,18 +1046,12 @@ typedef struct _kdb_vftabl {
      * canonical name.  The KDC will decide based on the request whether to use
      * the requested name or the canonical name in the issued ticket.
      *
-     * A module can return a referral to another realm if
-     * KRB5_KDB_FLAG_CANONICALIZE is set, or if
-     * KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY is set and search_for->type is
-     * KRB5_NT_ENTERPRISE_PRINCIPAL.  If KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY is
-     * set, the module should return a referral by simply filling in an
-     * out-of-realm name in (*entry)->princ and setting all other fields to
-     * NULL.  Otherwise, the module should return the entry for the cross-realm
-     * TGS of the referred-to realm.  For TGS referals, the module can also
-     * include tl-data of type KRB5_TL_SERVER_REFERRAL containing ASN.1-encoded
-     * Windows referral data as documented in
-     * draft-ietf-krb-wg-kerberos-referrals-11 appendix A; this will be
-     * returned to the client as encrypted padata.
+     * A module can return a referral to another realm if flags contains
+     * KRB5_KDB_FLAG_REFERRAL_OK.  If KRB5_KDB_FLAG_CLIENT is also set, the
+     * module should return a referral by simply filling in an out-of-realm
+     * name in (*entry)->princ and setting all other fields to NULL.
+     * Otherwise, the module should return the entry for the cross-realm TGS of
+     * the referred-to realm.
      */
     krb5_error_code (*get_principal)(krb5_context kcontext,
                                      krb5_const_principal search_for,
@@ -1275,91 +1266,6 @@ typedef struct _kdb_vftabl {
                                         int keyver, krb5_key_data *key_data);
 
     /*
-     * Optional: Generate signed authorization data, such as a Windows PAC, for
-     * the ticket to be returned to the client.  Place the signed authorization
-     * data, if any, in *signed_auth_data.  This function will be invoked for
-     * an AS request if the client included padata requesting a PAC.  This
-     * function will be invoked for a TGS request if there is authorization
-     * data in the TGT, if the client is from another realm, or if the TGS
-     * request is an S4U2Self or S4U2Proxy request.  This function will not be
-     * invoked during TGS requests if the server principal has the
-     * no_auth_data_required attribute set.  Input parameters are:
-     *
-     *   flags: The flags used to look up the client principal.
-     *
-     *   client_princ: For S4U2Self and S4U2Proxy TGS requests, the client
-     *     principal requested by the service; for regular TGS requests, the
-     *     possibly-canonicalized client principal.
-     *
-     *   server_princ: The server principal in the request.
-     *
-     *   client: The DB entry of the client if it is in the local realm, NULL
-     *     if not.  For S4U2Self and S4U2Proxy TGS requests, this is the DB
-     *     entry for the client principal requested by the service.
-     *
-     *   server: The DB entry of the service principal, or of a cross-realm
-     *     krbtgt principal in case of referral.
-     *
-     *   header_server: For S4U2Proxy requests, the DB entry of the second
-     *     ticket server.  For other TGS requests, the DB entry of the header
-     *     ticket server.  For AS requests, NULL.
-     *
-     *   local_tgt: the DB entry of the local krbtgt principal.
-     *
-     *   client_key: The reply key for the KDC request, before any FAST armor
-     *     is applied.  For AS requests, this may be the client's long-term key
-     *     or a key chosen by a preauth mechanism.  For TGS requests, this may
-     *     be the subkey found in the AP-REQ or the session key of the TGT.
-     *
-     *   server_key: The server key used to encrypt the returned ticket.
-     *
-     *   header_key: For S4U2Proxy requests, the key used to decrypt the second
-     *     ticket.  For TGS requests, the key used to decrypt the header
-     *     ticket.  For AS requests, NULL.
-     *
-     *   local_tgt_key: The decrypted first key of local_tgt.
-     *
-     *   session_key: The session key of the ticket being granted to the
-     *     requestor.
-     *
-     *   authtime: The timestamp of the original client authentication time.
-     *     For AS requests, this is the current time.  For TGS requests, this
-     *     is the authtime of the subject ticket (TGT or S4U2Proxy evidence
-     *     ticket).
-     *
-     *   tgt_auth_data: For TGS requests, the authorization data present in the
-     *     subject ticket.  For AS requests, NULL.
-     *
-     *   ad_info: For TGS requests, the parsed authorization data if obtained
-     *     by get_authdata_info method from the authorization data present in
-     *     the subject ticket.  Otherwise NULL.
-     *
-     *   auth_indicators: Points to NULL or a null-terminated list of krb5_data
-     *     pointers, each containing an authentication indicator (RFC 8129).
-     *     The method may modify this list, or free it and replace
-     *     *auth_indicators with NULL, to change which auth indicators will be
-     *     included in the ticket.
-     */
-    krb5_error_code (*sign_authdata)(krb5_context kcontext,
-                                     unsigned int flags,
-                                     krb5_const_principal client_princ,
-                                     krb5_const_principal server_princ,
-                                     krb5_db_entry *client,
-                                     krb5_db_entry *server,
-                                     krb5_db_entry *header_server,
-                                     krb5_db_entry *local_tgt,
-                                     krb5_keyblock *client_key,
-                                     krb5_keyblock *server_key,
-                                     krb5_keyblock *header_key,
-                                     krb5_keyblock *local_tgt_key,
-                                     krb5_keyblock *session_key,
-                                     krb5_timestamp authtime,
-                                     krb5_authdata **tgt_auth_data,
-                                     void *ad_info,
-                                     krb5_data ***auth_indicators,
-                                     krb5_authdata ***signed_auth_data);
-
-    /*
      * Optional: Perform a policy check on a cross-realm ticket's transited
      * field.  Return 0 if the check authoritatively succeeds,
      * KRB5_PLUGIN_NO_HANDLE to use the core transited-checking mechanisms, or
@@ -1424,13 +1330,15 @@ typedef struct _kdb_vftabl {
 
     /*
      * Optional: Perform a policy check on server being allowed to obtain
-     * tickets from client to proxy.  (Note that proxy is the target of the
-     * delegation, not the delegating service; the term "proxy" is from the
-     * viewpoint of the delegating service asking another service to perform
-     * some of its work in the authentication context of the client.  This
-     * terminology comes from the Microsoft S4U protocol documentation.)
-     * Return 0 if policy allows it, or an appropriate error (such as
-     * KRB5KDC_ERR_POLICY) if not.  If this method is not implemented, all
+     * tickets from client to proxy.  If proxy is NULL, check if server has any
+     * authorized delegation targets (client will also be NULL in this case).
+     * (Note that proxy is the target of the delegation, not the delegating
+     * service; the term "proxy" is from the viewpoint of the delegating
+     * service asking another service to perform some of its work in the
+     * authentication context of the client.  This terminology comes from the
+     * Microsoft S4U protocol documentation.)  Return 0 if policy allows
+     * delegation to the specified target (or to any target if proxy is NULL),
+     * or KRB5KDC_ERR_BADOPTION if not.  If this method is not implemented, all
      * S4U2Proxy delegation requests will be rejected.
      */
     krb5_error_code (*check_allowed_to_delegate)(krb5_context context,
@@ -1446,17 +1354,17 @@ typedef struct _kdb_vftabl {
     void (*free_principal_e_data)(krb5_context kcontext, krb5_octet *e_data);
 
     /*
-     * Optional: get a principal entry for S4U2Self based on X509 certificate.
+     * Optional: get a client principal entry based on an X.509 certificate.
      *
-     * If flags include KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY, princ->realm
-     * indicates the request realm, but the data components should be ignored.
-     * The module can return an out-of-realm client referral as it would for
-     * get_principal().
+     * If flags include KRB5_KDB_FLAG_REFERRAL_OK, the certificate was
+     * presented in an AS request.  princ->realm indicates the request realm,
+     * but the data components should be ignored.  The module can return an
+     * out-of-realm client referral as it would for get_principal().
      *
-     * If flags does not include KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY, princ is
-     * from PA-S4U-X509-USER.  If it contains data components (and not just a
-     * realm), the module should verify that it is the same as the lookup
-     * result for client_cert.  The module should not return a referral.
+     * Otherwise, princ is from a TGS request.  If it contains data components
+     * (and not just a realm), the module should verify that it is the same as
+     * the lookup result for client_cert.  The module should not return a
+     * referral.
      */
     krb5_error_code (*get_s4u_x509_principal)(krb5_context kcontext,
                                               const krb5_data *client_cert,
@@ -1469,11 +1377,9 @@ typedef struct _kdb_vftabl {
      * tickets from client to proxy.  This method is similar to
      * check_allowed_to_delegate, but it operates on the target server DB entry
      * (called "proxy" here as in Microsoft's protocol documentation) rather
-     * than the intermediate server entry.  server_ad_info represents the
-     * authdata of the intermediate server, as returned by the
-     * get_authdata_info method on the header ticket.  Return 0 if policy
-     * allows the delegation, or an appropriate error (such as
-     * KRB5KDC_ERR_POLICY) if not.
+     * than the intermediate server entry.  server_pac is the verified PAC from
+     * the authdata of the intermediate server.  Return 0 if policy allows the
+     * delegation, or KRB5KDC_ERR_BADOPTION if not.
      *
      * This method is called for S4U2Proxy requests and implements the
      * resource-based constrained delegation variant, which can support
@@ -1485,46 +1391,64 @@ typedef struct _kdb_vftabl {
     krb5_error_code (*allowed_to_delegate_from)(krb5_context context,
                                                 krb5_const_principal client,
                                                 krb5_const_principal server,
-                                                void *server_ad_info,
+                                                krb5_pac server_pac,
                                                 const krb5_db_entry *proxy);
 
     /*
-     * Optional: Perform verification and policy checks on authorization data,
-     * such as a Windows PAC, based on the request client lookup flags.  Return
-     * 0 if all checks have passed.  Optionally return a representation of the
-     * authdata in *ad_info_out, to be consumed by allowed_to_delegate_from and
-     * sign_authdata.  Returning *ad_info_out is required to support
-     * resource-based constrained delegation.
+     * Optional: Add buffers to new_pac using krb5_pac_add_buffer() before it
+     * is signed.
+     *
+     * The caller will handle the following buffer types, so do not copy or add
+     * them:
+     *
+     *   KRB5_PAC_SERVER_CHECKSUM
+     *   KRB5_PAC_PRIVSVR_CHECKSUM
+     *   KRB5_PAC_TICKET_CHECKSUM
+     *   KRB5_PAC_CLIENT_INFO
+     *   KRB5_PAC_DELEGATION_INFO
+     *
+     * For TGS requests, old_pac is the PAC of the header ticket, except when
+     * KRB5_KDB_FLAG_CONTRAINED_DELEGATION is present in flags, in which case
+     * it is the PAC of the second ticket.  If
+     * KRB5_KDB_FLAG_PROTOCOL_TRANSITION is present in flags and client is not
+     * NULL, old_pac is the PAC of the requesting service, not the subject of
+     * the S4U2Self request, and its buffers should not be copied into new_pac.
+     * The signatures and PAC_CLIENT_INFO of old_pac have been verified by the
+     * caller.
+     *
+     * If replaced_reply_key is not null, the request is an AS request and the
+     * reply key was replaced by a preauth mechanism such as PKINIT, meaning
+     * the Kerberos password or long-term key was not used.  The module may use
+     * this key to encrypt a PAC_CREDENTIALS_INFO buffer containing credentials
+     * (such as an NTLM hash) that the client would ordinarily derive from the
+     * Kerberos password or long-term key.  (Note: this feature is not yet
+     * implemented and the caller will always pass NULL until it is.)
+     *
+     * server is the database entry of the server the ticket will be issued to,
+     * which may be a referral TGS.
      *
-     * If the KRB5_KDB_FLAG_CONSTRAINED_DELEGATION bit is set, a PAC must be
-     * provided and verified, and an error should be returned if the client is
-     * not allowed to delegate.  If the KRB5_KDB_FLAG_CROSS_REALM bit is also
-     * set, set *client_out to the client name in the PAC; this indicates the
-     * requested client principal for a cross-realm S4U2Proxy request.
+     * signing_krbtgt is the database entry of the krbtgt principal used to
+     * verify old_pac (or null if old_pac is null).  If
+     * KRB5_KDB_FLAG_CROSS_REALM is present in flags, this entry will be an
+     * incoming cross-realm TGS, and the PAC fields should undergo appropriate
+     * filtering based on the trust level of the cross-realm relationship.
      *
-     * This method is called for TGS requests on the authorization data from
-     * the header ticket.  For S4U2Proxy requests it is also called on the
-     * authorization data from the evidence ticket.  If the
-     * KRB5_KDB_FLAG_PROTOCOL_TRANSITION bit is set in flags, the authdata is
-     * from the header ticket of an S4U2Self referral request, and the supplied
-     * client_princ is the requested client.
+     * auth_indicators points to NULL or a null-terminated list of krb5_data
+     * pointers, each containing an authentication indicator (RFC 8129).  The
+     * method may modify this list, or free it and replace *auth_indicators
+     * with NULL, to change which auth indicators will be included in the
+     * ticket.
      */
-    krb5_error_code (*get_authdata_info)(krb5_context context,
-                                         unsigned int flags,
-                                         krb5_authdata **in_authdata,
-                                         krb5_const_principal client_princ,
-                                         krb5_const_principal server_princ,
-                                         krb5_keyblock *server_key,
-                                         krb5_keyblock *krbtgt_key,
-                                         krb5_db_entry *krbtgt,
-                                         krb5_timestamp authtime,
-                                         void **ad_info_out,
-                                         krb5_principal *client_out);
-
-    void (*free_authdata_info)(krb5_context context,
-                               void *ad_info);
-
-    /* End of minor version 0 for major version 8. */
+    krb5_error_code (*issue_pac)(krb5_context context, unsigned int flags,
+                                 krb5_db_entry *client,
+                                 krb5_keyblock *replaced_reply_key,
+                                 krb5_db_entry *server,
+                                 krb5_db_entry *signing_krbtgt,
+                                 krb5_timestamp authtime, krb5_pac old_pac,
+                                 krb5_pac new_pac,
+                                 krb5_data ***auth_indicators);
+
+    /* End of minor version 0 for major version 9. */
 } kdb_vftabl;
 
 #endif /* !defined(_WIN32) */
diff --git a/src/kdc/do_as_req.c b/src/kdc/do_as_req.c
index 0144884..5e966de 100644
--- a/src/kdc/do_as_req.c
+++ b/src/kdc/do_as_req.c
@@ -142,6 +142,7 @@ lookup_client(krb5_context context, krb5_kdc_req *req, unsigned int flags,
     if (pa != NULL && pa->length != 0 &&
         req->client->type == KRB5_NT_X500_PRINCIPAL) {
         cert = make_data(pa->contents, pa->length);
+        flags |= KRB5_KDB_FLAG_REFERRAL_OK;
         return krb5_db_get_s4u_x509_principal(context, &cert, req->client,
                                               flags, entry_out);
     } else {
@@ -261,7 +262,7 @@ finish_process_as_req(struct as_req_state *state, krb5_error_code errcode)
         goto egress;
     }
 
-    errcode = handle_authdata(kdc_context, state->c_flags, state->client,
+    errcode = handle_authdata(kdc_active_realm, state->c_flags, state->client,
                               state->server, NULL, state->local_tgt,
                               &state->local_tgt_key, &state->client_keyblock,
                               &state->server_keyblock, NULL, state->req_pkt,
@@ -473,7 +474,6 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
                verto_ctx *vctx, loop_respond_fn respond, void *arg)
 {
     krb5_error_code errcode;
-    unsigned int s_flags = 0;
     krb5_data encoded_req_body;
     krb5_enctype useenctype;
     struct as_req_state *state;
@@ -570,19 +570,10 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
         goto errout;
     limit_string(state->sname);
 
-    /*
-     * We set KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY as a hint
-     * to the backend to return naming information in lieu
-     * of cross realm TGS entries.
-     */
-    setflag(state->c_flags, KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY);
-
-    if (isflagset(state->request->kdc_options, KDC_OPT_CANONICALIZE)) {
-        setflag(state->c_flags, KRB5_KDB_FLAG_CANONICALIZE);
-    }
-    if (include_pac_p(kdc_context, state->request)) {
-        setflag(state->c_flags, KRB5_KDB_FLAG_INCLUDE_PAC);
-    }
+    setflag(state->c_flags, KRB5_KDB_FLAG_CLIENT);
+    if (isflagset(state->request->kdc_options, KDC_OPT_CANONICALIZE) ||
+        state->request->client->type == KRB5_NT_ENTERPRISE_PRINCIPAL)
+        setflag(state->c_flags, KRB5_KDB_FLAG_REFERRAL_OK);
     errcode = lookup_client(kdc_context, state->request, state->c_flags,
                             &state->client);
     if (errcode == KRB5_KDB_CANTLOCK_DB)
@@ -602,12 +593,8 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
 
     au_state->stage = SRVC_PRINC;
 
-    s_flags = 0;
-    if (isflagset(state->request->kdc_options, KDC_OPT_CANONICALIZE)) {
-        setflag(s_flags, KRB5_KDB_FLAG_CANONICALIZE);
-    }
-    errcode = krb5_db_get_principal(kdc_context, state->request->server,
-                                    s_flags, &state->server);
+    errcode = krb5_db_get_principal(kdc_context, state->request->server, 0,
+                                    &state->server);
     if (errcode == KRB5_KDB_CANTLOCK_DB)
         errcode = KRB5KDC_ERR_SVC_UNAVAILABLE;
     if (errcode == KRB5_KDB_NOENTRY) {
@@ -671,7 +658,7 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
      * (the intention is to allow support for Windows "short" realm
      * aliases, nothing more).
      */
-    if (isflagset(s_flags, KRB5_KDB_FLAG_CANONICALIZE) &&
+    if (isflagset(state->request->kdc_options, KDC_OPT_CANONICALIZE) &&
         krb5_is_tgs_principal(state->request->server) &&
         krb5_is_tgs_principal(state->server->princ)) {
         state->ticket_reply.server = state->server->princ;
@@ -692,7 +679,7 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
      */
 
     state->enc_tkt_reply.session = &state->session_key;
-    if (isflagset(state->c_flags, KRB5_KDB_FLAG_CANONICALIZE)) {
+    if (isflagset(state->request->kdc_options, KDC_OPT_CANONICALIZE)) {
         state->client_princ = *(state->client->princ);
     } else {
         state->client_princ = *(state->request->client);
diff --git a/src/kdc/do_tgs_req.c b/src/kdc/do_tgs_req.c
index 45837fb..b1a190f 100644
--- a/src/kdc/do_tgs_req.c
+++ b/src/kdc/do_tgs_req.c
@@ -78,7 +78,8 @@ prepare_error_tgs(struct kdc_request_state *, krb5_kdc_req *,krb5_ticket *,int,
                   krb5_principal,krb5_data **,const char *, krb5_pa_data **);
 
 static krb5_error_code
-decrypt_2ndtkt(kdc_realm_t *, krb5_kdc_req *, krb5_flags, const krb5_ticket **,
+decrypt_2ndtkt(kdc_realm_t *, krb5_kdc_req *, krb5_flags, krb5_db_entry *,
+               krb5_keyblock *, const krb5_ticket **, krb5_pac *,
                krb5_db_entry **, krb5_keyblock **, const char **);
 
 static krb5_error_code
@@ -121,7 +122,6 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
     krb5_keyblock session_key, local_tgt_key;
     krb5_keyblock *reply_key = NULL;
     krb5_principal cprinc = NULL, sprinc = NULL, altcprinc = NULL;
-    krb5_const_principal authdata_client;
     krb5_principal stkt_authdata_client = NULL;
     krb5_last_req_entry *nolrarray[2], nolrentry;
     int errcode;
@@ -142,7 +142,7 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
     krb5_pa_data **e_data = NULL;
     krb5_audit_state *au_state = NULL;
     krb5_data **auth_indicators = NULL;
-    void *ad_info = NULL, *stkt_ad_info = NULL;
+    krb5_pac header_pac = NULL, stkt_pac = NULL, subject_pac;
 
     memset(&reply, 0, sizeof(reply));
     memset(&reply_encpart, 0, sizeof(reply_encpart));
@@ -217,6 +217,14 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
         goto cleanup;
     }
 
+    errcode = get_verified_pac(kdc_context, header_ticket->enc_part2,
+                               header_server->princ, header_key, local_tgt,
+                               &local_tgt_key, &header_pac);
+    if (errcode) {
+        status = "HEADER_PAC";
+        goto cleanup;
+    }
+
     /* Ignore (for now) the request modification due to FAST processing. */
     au_state->request = request;
 
@@ -239,10 +247,8 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
     /* XXX make sure server here has the proper realm...taken from AP_REQ
        header? */
 
-    if (isflagset(request->kdc_options, KDC_OPT_CANONICALIZE)) {
-        setflag(c_flags, KRB5_KDB_FLAG_CANONICALIZE);
-        setflag(s_flags, KRB5_KDB_FLAG_CANONICALIZE);
-    }
+    if (isflagset(request->kdc_options, KDC_OPT_CANONICALIZE))
+        setflag(s_flags, KRB5_KDB_FLAG_REFERRAL_OK);
 
     errcode = search_sprinc(kdc_active_realm, request, s_flags, &server,
                             &status);
@@ -279,17 +285,21 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
         if (errcode)
             goto cleanup;
     }
+    if (s4u_x509_user != NULL)
+        setflag(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION);
 
     /* For user-to-user and S4U2Proxy requests, decrypt the second ticket. */
-    errcode = decrypt_2ndtkt(kdc_active_realm, request, c_flags,
-                             &stkt, &stkt_server, &stkt_server_key, &status);
+    errcode = decrypt_2ndtkt(kdc_active_realm, request, c_flags, local_tgt,
+                             &local_tgt_key, &stkt, &stkt_pac, &stkt_server,
+                             &stkt_server_key, &status);
     if (errcode)
         goto cleanup;
 
     retval = validate_tgs_request(kdc_active_realm, request, server,
-                                  header_ticket, stkt, stkt_server, kdc_time,
-                                  s4u_x509_user, client, is_crossrealm,
-                                  is_referral, &status, &e_data);
+                                  header_ticket, header_pac, stkt, stkt_pac,
+                                  stkt_server, kdc_time, s4u_x509_user,
+                                  client, is_crossrealm, is_referral,
+                                  &status, &e_data);
     if (retval) {
         if (retval == KDC_ERR_POLICY || retval == KDC_ERR_BADOPTION)
             au_state->violation = PROT_CONSTRAINT;
@@ -297,44 +307,16 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
         goto cleanup;
     }
 
-    if (s4u_x509_user != NULL && client == NULL) {
-        /*
-         * For an S4U2Self referral request (the requesting service is
-         * following a referral back to its own realm), the authdata in the
-         * header ticket should be for the requested client.
-         */
-        setflag(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION);
-        authdata_client = s4u_x509_user->user_id.user;
-    } else {
-        /* Otherwise (including for initial S4U2Self requests), the authdata
-         * should be for the header ticket client. */
-        authdata_client = header_enc_tkt->client;
-    }
-    errcode = krb5_db_get_authdata_info(kdc_context, c_flags,
-                                        header_enc_tkt->authorization_data,
-                                        authdata_client, request->server,
-                                        header_key, &local_tgt_key, local_tgt,
-                                        header_enc_tkt->times.authtime,
-                                        &ad_info, NULL);
-    if (errcode && errcode != KRB5_PLUGIN_OP_NOTSUPP)
-        goto cleanup;
-
-    /* Flag all S4U2Self requests now that we have checked the authdata. */
-    if (s4u_x509_user != NULL)
-        setflag(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION);
-
     if (isflagset(request->kdc_options, KDC_OPT_CNAME_IN_ADDL_TKT)) {
         /* Do constrained delegation protocol and authorization checks. */
         setflag(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION);
 
-        errcode = kdc_process_s4u2proxy_req(kdc_active_realm, c_flags, request,
-                                            stkt->enc_part2, local_tgt,
-                                            &local_tgt_key, stkt_server,
-                                            stkt_server_key,
+        errcode = kdc_process_s4u2proxy_req(kdc_active_realm, c_flags,
+                                            request, header_pac,
+                                            stkt->enc_part2, stkt_pac,
+                                            stkt_server, stkt_server_key,
                                             header_ticket->enc_part2->client,
-                                            server, request->server, ad_info,
-                                            &stkt_ad_info,
-                                            &stkt_authdata_client,
+                                            server, &stkt_authdata_client,
                                             &status);
         if (errcode == KDC_ERR_POLICY || errcode == KDC_ERR_BADOPTION)
             au_state->violation = PROT_CONSTRAINT;
@@ -351,12 +333,6 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
             goto cleanup;
 
         assert(krb5_is_tgs_principal(header_ticket->server));
-
-        /* Use the parsed authdata from the second ticket during authdata
-         * handling. */
-        krb5_db_free_authdata_info(kdc_context, ad_info);
-        ad_info = stkt_ad_info;
-        stkt_ad_info = NULL;
     }
 
     au_state->stage = ISSUE_TKT;
@@ -375,10 +351,12 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
 
     if (isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) {
         subject_tkt = stkt->enc_part2;
+        subject_pac = stkt_pac;
         subject_server = stkt_server;
         subject_key = stkt_server_key;
     } else {
         subject_tkt = header_enc_tkt;
+        subject_pac = header_pac;
         subject_server = header_server;
         subject_key = header_key;
     }
@@ -410,12 +388,12 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
                                            server, header_enc_tkt);
     enc_tkt_reply.times.starttime = 0;
 
-    /* OK_TO_AUTH_AS_DELEGATE must be set on the service requesting S4U2Self
-     * for forwardable tickets to be issued. */
-    if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) &&
-        !is_referral &&
-        !isflagset(server->attributes, KRB5_KDB_OK_TO_AUTH_AS_DELEGATE))
-        clear(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
+    if (s4u_x509_user != NULL && !is_referral) {
+        /* Check if we need to suppress the forwardable ticket flag. */
+        errcode = s4u2self_forwardable(kdc_context, server, &enc_tkt_reply);
+        if (errcode)
+            goto cleanup;
+    }
 
     /* don't use new addresses unless forwarded, see below */
 
@@ -521,11 +499,12 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
         encrypting_key = &server_keyblock;
     }
 
-    if (isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) {
+    if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) {
         /*
-         * Don't allow authorization data to be disabled if constrained
-         * delegation is requested. We don't want to deny the server
-         * the ability to validate that delegation was used.
+         * For consistency with Active Directory, don't allow authorization
+         * data to be disabled if S4U2Self is requested.  The server likely
+         * needs a PAC to inspect or for an S4U2Proxy operation, even if it
+         * doesn't need authorization data in tickets received from clients.
          */
         clear(server->attributes, KRB5_KDB_NO_AUTH_DATA_REQUIRED);
     }
@@ -533,8 +512,7 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
         /* If we are not doing protocol transition, try to look up the subject
          * principal so that KDB modules can add additional authdata. */
         if (!isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) {
-            /* Generate authorization data so we can include it in ticket */
-            setflag(c_flags, KRB5_KDB_FLAG_INCLUDE_PAC);
+            setflag(c_flags, KRB5_KDB_FLAG_CLIENT);
             /* Map principals from foreign (possibly non-AD) realms */
             setflag(c_flags, KRB5_KDB_FLAG_MAP_PRINCIPALS);
 
@@ -609,12 +587,12 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
         goto cleanup;
     }
 
-    errcode = handle_authdata(kdc_context, c_flags, client, server,
+    errcode = handle_authdata(kdc_active_realm, c_flags, client, server,
                               subject_server, local_tgt, &local_tgt_key,
                               subkey != NULL ? subkey :
                               header_ticket->enc_part2->session,
                               encrypting_key, subject_key, pkt, request,
-                              altcprinc, ad_info, subject_tkt,
+                              altcprinc, subject_pac, subject_tkt,
                               &auth_indicators, &enc_tkt_reply);
     if (errcode) {
         krb5_klog_syslog(LOG_INFO, _("TGS_REQ : handle_authdata (%d)"),
@@ -694,8 +672,7 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
     errcode = return_enc_padata(kdc_context, pkt, request,
                                 reply_key, server, &reply_encpart,
                                 is_referral &&
-                                isflagset(s_flags,
-                                          KRB5_KDB_FLAG_CANONICALIZE));
+                                isflagset(s_flags, KRB5_KDB_FLAG_REFERRAL_OK));
     if (errcode) {
         status = "KDC_RETURN_ENC_PADATA";
         goto cleanup;
@@ -802,8 +779,8 @@ cleanup:
         krb5_free_authdata(kdc_context, enc_tkt_reply.authorization_data);
     krb5_free_pa_data(kdc_context, e_data);
     k5_free_data_ptr_list(auth_indicators);
-    krb5_db_free_authdata_info(kdc_context, ad_info);
-    krb5_db_free_authdata_info(kdc_context, stkt_ad_info);
+    krb5_pac_free(kdc_context, header_pac);
+    krb5_pac_free(kdc_context, stkt_pac);
     krb5_free_principal(kdc_context, stkt_authdata_client);
 
     return retval;
@@ -888,9 +865,10 @@ prepare_error_tgs (struct kdc_request_state *state,
  */
 static krb5_error_code
 decrypt_2ndtkt(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req,
-               krb5_flags flags, const krb5_ticket **stkt_out,
-               krb5_db_entry **server_out, krb5_keyblock **key_out,
-               const char **status)
+               krb5_flags flags, krb5_db_entry *local_tgt,
+               krb5_keyblock *local_tgt_key, const krb5_ticket **stkt_out,
+               krb5_pac *pac_out, krb5_db_entry **server_out,
+               krb5_keyblock **key_out, const char **status)
 {
     krb5_error_code retval;
     krb5_db_entry *server = NULL;
@@ -899,6 +877,7 @@ decrypt_2ndtkt(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req,
     krb5_ticket *stkt;
 
     *stkt_out = NULL;
+    *pac_out = NULL;
     *server_out = NULL;
     *key_out = NULL;
 
@@ -918,6 +897,12 @@ decrypt_2ndtkt(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req,
         *status = "2ND_TKT_DECRYPT";
         goto cleanup;
     }
+    retval = get_verified_pac(kdc_context, stkt->enc_part2, server->princ,
+                              key, local_tgt, local_tgt_key, pac_out);
+    if (retval != 0) {
+        *status = "2ND_TKT_PAC";
+        goto cleanup;
+    }
     *stkt_out = stkt;
     *server_out = server;
     *key_out = key;
@@ -1181,7 +1166,7 @@ search_sprinc(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req,
      * the server is supposed to match an already-issued ticket. */
     allow_referral = !(req->kdc_options & NO_REFERRAL_OPTION);
     if (!allow_referral)
-        flags &= ~KRB5_KDB_FLAG_CANONICALIZE;
+        flags &= ~KRB5_KDB_FLAG_REFERRAL_OK;
 
     ret = db_get_svc_princ(kdc_context, princ, flags, server, status);
     if (ret == 0 || ret != KRB5_KDB_NOENTRY || !allow_referral)
diff --git a/src/kdc/kdc_authdata.c b/src/kdc/kdc_authdata.c
index 010922c..3909daf 100644
--- a/src/kdc/kdc_authdata.c
+++ b/src/kdc/kdc_authdata.c
@@ -149,23 +149,6 @@ cleanup:
     return result;
 }
 
-/* Return true if authdata contains any elements which should only come from
- * the KDC.  If desired_type is non-zero, look only for that type. */
-static krb5_boolean
-has_kdc_issued_authdata(krb5_authdata **authdata,
-                        krb5_authdatatype desired_type)
-{
-    int i;
-
-    if (authdata == NULL)
-        return FALSE;
-    for (i = 0; authdata[i] != NULL; i++) {
-        if (is_kdc_issued_authdatum(authdata[i], desired_type))
-            return TRUE;
-    }
-    return FALSE;
-}
-
 /* Return true if authdata contains any mandatory-for-KDC elements. */
 static krb5_boolean
 has_mandatory_for_kdc_authdata(krb5_context context, krb5_authdata **authdata)
@@ -312,405 +295,6 @@ copy_tgt_authdata(krb5_context context, krb5_kdc_req *request,
     return add_filtered_authdata(tkt_authdata, tgt_authdata);
 }
 
-/* Fetch authorization data from KDB module. */
-static krb5_error_code
-fetch_kdb_authdata(krb5_context context, unsigned int flags,
-                   krb5_db_entry *client, krb5_db_entry *server,
-                   krb5_db_entry *header_server, krb5_db_entry *local_tgt,
-                   krb5_keyblock *client_key, krb5_keyblock *server_key,
-                   krb5_keyblock *header_key, krb5_keyblock *local_tgt_key,
-                   krb5_kdc_req *req, krb5_const_principal altcprinc,
-                   void *ad_info, krb5_enc_tkt_part *enc_tkt_req,
-                   krb5_enc_tkt_part *enc_tkt_reply,
-                   krb5_data ***auth_indicators)
-{
-    krb5_error_code ret;
-    krb5_authdata **tgt_authdata, **db_authdata = NULL;
-    krb5_boolean tgs_req = (req->msg_type == KRB5_TGS_REQ);
-    krb5_const_principal actual_client;
-
-    /*
-     * Check whether KDC issued authorization data should be included.
-     * A server can explicitly disable the inclusion of authorization
-     * data by setting the KRB5_KDB_NO_AUTH_DATA_REQUIRED flag on its
-     * principal entry. Otherwise authorization data will be included
-     * if it was present in the TGT, the client is from another realm
-     * or protocol transition/constrained delegation was used, or, in
-     * the AS-REQ case, if the pre-auth data indicated the PAC should
-     * be present.
-     */
-    if (tgs_req) {
-        assert(enc_tkt_req != NULL);
-
-        if (isflagset(server->attributes, KRB5_KDB_NO_AUTH_DATA_REQUIRED))
-            return 0;
-
-        if (enc_tkt_req->authorization_data == NULL &&
-            !isflagset(flags, KRB5_KDB_FLAG_CROSS_REALM | KRB5_KDB_FLAGS_S4U))
-            return 0;
-
-        assert(enc_tkt_reply->times.authtime == enc_tkt_req->times.authtime);
-    } else {
-        if (!isflagset(flags, KRB5_KDB_FLAG_INCLUDE_PAC))
-            return 0;
-    }
-
-    /* S4U referral replies should contain authdata for the requested client,
-     * even though they use the requesting service as the ticket client. */
-    if (isflagset(flags, KRB5_KDB_FLAGS_S4U))
-        actual_client = altcprinc;
-    else
-        actual_client = enc_tkt_reply->client;
-
-    tgt_authdata = tgs_req ? enc_tkt_req->authorization_data : NULL;
-    ret = krb5_db_sign_authdata(context, flags, actual_client, req->server,
-                                client, server, header_server, local_tgt,
-                                client_key, server_key, header_key,
-                                local_tgt_key, enc_tkt_reply->session,
-                                enc_tkt_reply->times.authtime, tgt_authdata,
-                                ad_info, auth_indicators, &db_authdata);
-    if (ret)
-        return (ret == KRB5_PLUGIN_OP_NOTSUPP) ? 0 : ret;
-
-    /* Put the KDB authdata first in the ticket.  A successful merge places the
-     * combined list in db_authdata and releases the old ticket authdata. */
-    ret = merge_authdata(&db_authdata, &enc_tkt_reply->authorization_data);
-    if (ret)
-        krb5_free_authdata(context, db_authdata);
-    else
-        enc_tkt_reply->authorization_data = db_authdata;
-    return ret;
-}
-
-static krb5_error_code
-make_signedpath_data(krb5_context context, krb5_const_principal client,
-                     krb5_timestamp authtime, krb5_principal *deleg_path,
-                     krb5_pa_data **method_data, krb5_authdata **authdata,
-                     krb5_data **data)
-{
-    krb5_error_code ret;
-    krb5_ad_signedpath_data sp_data;
-    krb5_authdata **sign_authdata = NULL;
-    size_t i, j, count;
-
-    memset(&sp_data, 0, sizeof(sp_data));
-
-    for (count = 0; authdata != NULL && authdata[count] != NULL; count++);
-    if (count != 0) {
-        /* Make a shallow copy with AD-SIGNTICKET filtered out. */
-        sign_authdata = k5calloc(count + 1, sizeof(krb5_authdata *), &ret);
-        if (sign_authdata == NULL)
-            return ret;
-
-        for (i = 0, j = 0; authdata[i] != NULL; i++) {
-            if (is_kdc_issued_authdatum(authdata[i], KRB5_AUTHDATA_SIGNTICKET))
-                continue;
-
-            sign_authdata[j++] = authdata[i];
-        }
-
-        sign_authdata[j] = NULL;
-    }
-
-    sp_data.client = (krb5_principal)client;
-    sp_data.authtime = authtime;
-    sp_data.delegated = deleg_path;
-    sp_data.method_data = method_data;
-    sp_data.authorization_data = sign_authdata;
-
-    ret = encode_krb5_ad_signedpath_data(&sp_data, data);
-
-    if (sign_authdata != NULL)
-        free(sign_authdata);
-
-    return ret;
-}
-
-static krb5_error_code
-verify_signedpath_checksum(krb5_context context, krb5_db_entry *local_tgt,
-                           krb5_keyblock *local_tgt_key,
-                           krb5_enc_tkt_part *enc_tkt_part,
-                           krb5_principal *deleg_path,
-                           krb5_pa_data **method_data, krb5_checksum *cksum,
-                           krb5_boolean *valid_out)
-{
-    krb5_error_code ret;
-    krb5_data *data;
-    krb5_key_data *kd;
-    krb5_keyblock tgtkey;
-    krb5_kvno kvno;
-    krb5_boolean valid = FALSE;
-    int tries;
-
-    *valid_out = FALSE;
-    memset(&tgtkey, 0, sizeof(tgtkey));
-
-    if (!krb5_c_is_keyed_cksum(cksum->checksum_type))
-        return KRB5KRB_AP_ERR_INAPP_CKSUM;
-
-    ret = make_signedpath_data(context, enc_tkt_part->client,
-                               enc_tkt_part->times.authtime, deleg_path,
-                               method_data, enc_tkt_part->authorization_data,
-                               &data);
-    if (ret)
-        return ret;
-
-    ret = krb5_c_verify_checksum(context, local_tgt_key,
-                                 KRB5_KEYUSAGE_AD_SIGNEDPATH, data, cksum,
-                                 &valid);
-    if (ret || !valid) {
-        /* There is no kvno in AD-SIGNTICKET, so try two previous versions. */
-        kvno = local_tgt->key_data[0].key_data_kvno - 1;
-        for (tries = 2; tries > 0 && kvno > 0; tries--, kvno--) {
-            /* Get the first local tgt key of this kvno. */
-            ret = krb5_dbe_find_enctype(context, local_tgt, -1, -1, kvno, &kd);
-            if (ret) {
-                ret = 0;
-                break;
-            }
-            ret = krb5_dbe_decrypt_key_data(context, NULL, kd, &tgtkey, NULL);
-            if (ret)
-                break;
-
-            ret = krb5_c_verify_checksum(context, &tgtkey,
-                                         KRB5_KEYUSAGE_AD_SIGNEDPATH, data,
-                                         cksum, &valid);
-            krb5_free_keyblock_contents(context, &tgtkey);
-            if (!ret && valid)
-                break;
-        }
-    }
-
-    *valid_out = valid;
-    krb5_free_data(context, data);
-    return ret;
-}
-
-
-static krb5_error_code
-verify_signedpath(krb5_context context, krb5_db_entry *local_tgt,
-                  krb5_keyblock *local_tgt_key,
-                  krb5_enc_tkt_part *enc_tkt_part,
-                  krb5_principal **delegated_out, krb5_boolean *pathsigned_out)
-{
-    krb5_error_code ret;
-    krb5_ad_signedpath *sp = NULL;
-    krb5_authdata **sp_authdata = NULL;
-    krb5_data enc_sp;
-
-    *delegated_out = NULL;
-    *pathsigned_out = FALSE;
-
-    ret = krb5_find_authdata(context, enc_tkt_part->authorization_data, NULL,
-                             KRB5_AUTHDATA_SIGNTICKET, &sp_authdata);
-    if (ret)
-        goto cleanup;
-
-    if (sp_authdata == NULL ||
-        sp_authdata[0]->ad_type != KRB5_AUTHDATA_SIGNTICKET ||
-        sp_authdata[1] != NULL)
-        goto cleanup;
-
-    enc_sp.data = (char *)sp_authdata[0]->contents;
-    enc_sp.length = sp_authdata[0]->length;
-
-    ret = decode_krb5_ad_signedpath(&enc_sp, &sp);
-    if (ret) {
-        /* Treat an invalid signedpath authdata element as a missing one, since
-         * we believe MS is using the same number for something else. */
-        ret = 0;
-        goto cleanup;
-    }
-
-    ret = verify_signedpath_checksum(context, local_tgt, local_tgt_key,
-                                     enc_tkt_part, sp->delegated,
-                                     sp->method_data, &sp->checksum,
-                                     pathsigned_out);
-    if (ret)
-        goto cleanup;
-
-    if (*pathsigned_out) {
-        *delegated_out = sp->delegated;
-        sp->delegated = NULL;
-    }
-
-cleanup:
-    krb5_free_ad_signedpath(context, sp);
-    krb5_free_authdata(context, sp_authdata);
-    return ret;
-}
-
-static krb5_error_code
-make_signedpath_checksum(krb5_context context,
-                         krb5_const_principal for_user_princ,
-                         krb5_keyblock *local_tgt_key,
-                         krb5_enc_tkt_part *enc_tkt_part,
-                         krb5_principal *deleg_path,
-                         krb5_pa_data **method_data, krb5_checksum *cksum_out,
-                         krb5_enctype *enctype_out)
-{
-    krb5_error_code ret;
-    krb5_data *data = NULL;
-    krb5_const_principal client;
-
-    memset(cksum_out, 0, sizeof(*cksum_out));
-    *enctype_out = ENCTYPE_NULL;
-
-    client = (for_user_princ != NULL) ? for_user_princ : enc_tkt_part->client;
-
-    ret = make_signedpath_data(context, client, enc_tkt_part->times.authtime,
-                               deleg_path, method_data,
-                               enc_tkt_part->authorization_data, &data);
-    if (ret)
-        return ret;
-
-    ret = krb5_c_make_checksum(context, 0, local_tgt_key,
-                               KRB5_KEYUSAGE_AD_SIGNEDPATH, data, cksum_out);
-    krb5_free_data(context, data);
-    if (ret)
-        return ret;
-
-    *enctype_out = local_tgt_key->enctype;
-    return 0;
-}
-
-static krb5_error_code
-make_signedpath(krb5_context context, krb5_const_principal for_user_princ,
-                krb5_principal server, krb5_keyblock *local_tgt_key,
-                krb5_principal *deleg_path, krb5_enc_tkt_part *enc_tkt_reply)
-{
-    krb5_error_code ret;
-    krb5_ad_signedpath sp;
-    krb5_data *data = NULL;
-    krb5_authdata ad_datum, *ad_data[2];
-    krb5_authdata **if_relevant = NULL;
-    size_t count;
-
-    memset(&sp, 0, sizeof(sp));
-
-    for (count = 0; deleg_path != NULL && deleg_path[count] != NULL; count++);
-
-    sp.delegated = k5calloc(count + 2, sizeof(krb5_principal), &ret);
-    if (sp.delegated == NULL)
-        goto cleanup;
-
-    /* Combine existing and new transited services, if any */
-    if (deleg_path != NULL)
-        memcpy(sp.delegated, deleg_path, count * sizeof(krb5_principal));
-    if (server != NULL)
-        sp.delegated[count++] = server;
-    sp.delegated[count] = NULL;
-    sp.method_data = NULL;
-
-    ret = make_signedpath_checksum(context, for_user_princ, local_tgt_key,
-                                   enc_tkt_reply, sp.delegated, sp.method_data,
-                                   &sp.checksum, &sp.enctype);
-    if (ret) {
-        if (ret == KRB5KRB_AP_ERR_INAPP_CKSUM) {
-            /*
-             * In the hopefully unlikely case the TGS key enctype has an
-             * unkeyed mandatory checksum type, do not fail so we do not
-             * prevent the KDC from servicing requests.
-             */
-            ret = 0;
-        }
-        goto cleanup;
-    }
-
-    ret = encode_krb5_ad_signedpath(&sp, &data);
-    if (ret)
-        goto cleanup;
-
-    ad_datum.ad_type = KRB5_AUTHDATA_SIGNTICKET;
-    ad_datum.contents = (krb5_octet *)data->data;
-    ad_datum.length = data->length;
-
-    ad_data[0] = &ad_datum;
-    ad_data[1] = NULL;
-
-    ret = krb5_encode_authdata_container(context, KRB5_AUTHDATA_IF_RELEVANT,
-                                         ad_data, &if_relevant);
-    if (ret)
-        goto cleanup;
-
-    /* Add the signedpath authdata to the ticket. */
-    ret = merge_authdata(&enc_tkt_reply->authorization_data, &if_relevant);
-
-cleanup:
-    free(sp.delegated);
-    krb5_free_authdata(context, if_relevant);
-    krb5_free_data(context, data);
-    krb5_free_checksum_contents(context, &sp.checksum);
-    krb5_free_pa_data(context, sp.method_data);
-    return ret;
-}
-
-static void
-free_deleg_path(krb5_context context, krb5_principal *deleg_path)
-{
-    int i;
-
-    for (i = 0; deleg_path != NULL && deleg_path[i] != NULL; i++)
-        krb5_free_principal(context, deleg_path[i]);
-    free(deleg_path);
-}
-
-/* Return true if the Windows PAC is present in authorization data. */
-static krb5_boolean
-has_pac(krb5_context context, krb5_authdata **authdata)
-{
-    return has_kdc_issued_authdata(authdata, KRB5_AUTHDATA_WIN2K_PAC);
-}
-
-/* Verify AD-SIGNTICKET authdata if we need to, and insert an AD-SIGNEDPATH
- * element if we should. */
-static krb5_error_code
-handle_signticket(krb5_context context, unsigned int flags,
-                  krb5_db_entry *subject_server, krb5_db_entry *server,
-                  krb5_db_entry *local_tgt, krb5_keyblock *local_tgt_key,
-                  krb5_kdc_req *req, krb5_const_principal for_user_princ,
-                  krb5_enc_tkt_part *enc_tkt_req,
-                  krb5_enc_tkt_part *enc_tkt_reply)
-{
-    krb5_error_code ret = 0;
-    krb5_principal *deleg_path = NULL;
-    krb5_boolean signed_path = FALSE;
-    krb5_boolean s4u2proxy;
-
-    s4u2proxy = isflagset(flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION);
-
-    /* For cross-realm the Windows PAC must have been verified, and it
-     * fulfills the same role as the signed path. */
-    if (req->msg_type == KRB5_TGS_REQ &&
-        (!isflagset(flags, KRB5_KDB_FLAG_CROSS_REALM) ||
-         !has_pac(context, enc_tkt_req->authorization_data))) {
-        ret = verify_signedpath(context, local_tgt, local_tgt_key, enc_tkt_req,
-                                &deleg_path, &signed_path);
-        if (ret)
-            goto cleanup;
-
-        if (s4u2proxy && signed_path == FALSE) {
-            ret = KRB5KDC_ERR_BADOPTION;
-            goto cleanup;
-        }
-    }
-
-    /* No point in including signedpath authdata for a cross-realm TGT, since
-     * it will be presented to a different KDC. */
-    if (!isflagset(server->attributes, KRB5_KDB_NO_AUTH_DATA_REQUIRED) &&
-        !is_cross_tgs_principal(server->princ)) {
-        ret = make_signedpath(context, for_user_princ,
-                              s4u2proxy ? subject_server->princ : NULL,
-                              local_tgt_key, deleg_path, enc_tkt_reply);
-        if (ret)
-            goto cleanup;
-    }
-
-cleanup:
-    free_deleg_path(context, deleg_path);
-    return ret;
-}
-
 /* Add authentication indicator authdata to enc_tkt_reply, wrapped in a CAMMAC
  * and an IF-RELEVANT container. */
 static krb5_error_code
@@ -723,6 +307,9 @@ add_auth_indicators(krb5_context context, krb5_data *const *auth_indicators,
     krb5_data *der_indicators = NULL;
     krb5_authdata ad, *list[2], **cammac = NULL;
 
+    if (auth_indicators == NULL || *auth_indicators == NULL)
+        return 0;
+
     /* Format the authentication indicators into an authdata list. */
     ret = encode_utf8_strings(auth_indicators, &der_indicators);
     if (ret)
@@ -792,21 +379,209 @@ cleanup:
     return ret;
 }
 
+static krb5_error_code
+update_delegation_info(krb5_context context, krb5_kdc_req *req,
+                       krb5_pac old_pac, krb5_pac new_pac)
+{
+    krb5_error_code ret;
+    krb5_data ndr_di_in = empty_data(), ndr_di_out = empty_data();
+    struct pac_s4u_delegation_info *di = NULL;
+    char *namestr = NULL;
+
+    ret = krb5_pac_get_buffer(context, old_pac, KRB5_PAC_DELEGATION_INFO,
+                              &ndr_di_in);
+    if (ret && ret != ENOENT)
+        goto cleanup;
+    if (ret) {
+        /* Create new delegation info. */
+        di = k5alloc(sizeof(*di), &ret);
+        if (di == NULL)
+            goto cleanup;
+        di->transited_services = k5calloc(1, sizeof(char *), &ret);
+        if (di->transited_services == NULL)
+            goto cleanup;
+    } else {
+        /* Decode and modify old delegation info. */
+        ret = ndr_dec_delegation_info(&ndr_di_in, &di);
+        if (ret)
+            goto cleanup;
+    }
+
+    /* Set proxy_target to the requested server, without realm. */
+    ret = krb5_unparse_name_flags(context, req->server,
+                                  KRB5_PRINCIPAL_UNPARSE_DISPLAY |
+                                  KRB5_PRINCIPAL_UNPARSE_NO_REALM,
+                                  &namestr);
+    if (ret)
+        goto cleanup;
+    free(di->proxy_target);
+    di->proxy_target = namestr;
+
+    /* Add a transited entry for the requesting service, with realm. */
+    assert(req->second_ticket != NULL && req->second_ticket[0] != NULL);
+    ret = krb5_unparse_name(context, req->second_ticket[0]->server, &namestr);
+    if (ret)
+        goto cleanup;
+    di->transited_services[di->transited_services_length++] = namestr;
+
+    ret = ndr_enc_delegation_info(di, &ndr_di_out);
+    if (ret)
+        goto cleanup;
+
+    ret = krb5_pac_add_buffer(context, new_pac, KRB5_PAC_DELEGATION_INFO,
+                              &ndr_di_out);
+
+cleanup:
+    krb5_free_data_contents(context, &ndr_di_in);
+    krb5_free_data_contents(context, &ndr_di_out);
+    ndr_free_delegation_info(di);
+    return ret;
+}
+
+static krb5_error_code
+copy_pac_buffer(krb5_context context, uint32_t buffer_type, krb5_pac old_pac,
+                krb5_pac new_pac)
+{
+    krb5_error_code ret;
+    krb5_data data;
+
+    ret = krb5_pac_get_buffer(context, old_pac, buffer_type, &data);
+    if (ret)
+        return ret;
+    ret = krb5_pac_add_buffer(context, new_pac, buffer_type, &data);
+    krb5_free_data_contents(context, &data);
+    return ret;
+}
+
+/*
+ * Possibly add a signed PAC to enc_tkt_reply.  Also possibly add auth
+ * indicators; these are handled here so that the KDB module's issue_pac()
+ * method can alter the auth indicator list.
+ */
+static krb5_error_code
+handle_pac(kdc_realm_t *kdc_active_realm, unsigned int flags,
+           krb5_db_entry *client, krb5_db_entry *server,
+           krb5_db_entry *subject_server, krb5_db_entry *local_tgt,
+           krb5_keyblock *local_tgt_key, krb5_keyblock *server_key,
+           krb5_keyblock *subject_key, krb5_enc_tkt_part *subject_tkt,
+           krb5_pac subject_pac, krb5_kdc_req *req,
+           krb5_const_principal altcprinc, krb5_timestamp authtime,
+           krb5_enc_tkt_part *enc_tkt_reply, krb5_data ***auth_indicators)
+{
+    krb5_error_code ret;
+    krb5_context context = kdc_context;
+    krb5_pac new_pac = NULL;
+    krb5_const_principal pac_client = NULL;
+    krb5_boolean with_realm, is_as_req = (req->msg_type == KRB5_AS_REQ);
+    krb5_db_entry *signing_tgt;
+
+    /* Don't add a PAC or auth indicators if the server disables authdata. */
+    if (server->attributes & KRB5_KDB_NO_AUTH_DATA_REQUIRED)
+        return 0;
+
+    /*
+     * Don't add a PAC if the realm disables them, or to an anonymous ticket,
+     * or for an AS-REQ if the client requested not to get one, or for a
+     * TGS-REQ if the subject ticket didn't contain one.
+     */
+    if (kdc_active_realm->realm_disable_pac ||
+        (enc_tkt_reply->flags & TKT_FLG_ANONYMOUS) ||
+        (is_as_req && !include_pac_p(context, req)) ||
+        (!is_as_req && subject_pac == NULL)) {
+        return add_auth_indicators(context, *auth_indicators, server_key,
+                                   local_tgt, local_tgt_key, enc_tkt_reply);
+    }
+
+    ret = krb5_pac_init(context, &new_pac);
+    if (ret)
+        goto cleanup;
+
+    if (subject_pac == NULL)
+        signing_tgt = NULL;
+    else if (krb5_is_tgs_principal(subject_server->princ))
+        signing_tgt = subject_server;
+    else
+        signing_tgt = local_tgt;
+
+    ret = krb5_db_issue_pac(context, flags, client, NULL, server, signing_tgt,
+                            authtime, subject_pac, new_pac, auth_indicators);
+    if (ret) {
+        if (ret == KRB5_PLUGIN_OP_NOTSUPP)
+            ret = 0;
+        if (ret)
+            goto cleanup;
+    }
+
+    ret = add_auth_indicators(context, *auth_indicators, server_key,
+                              local_tgt, local_tgt_key, enc_tkt_reply);
+
+    if ((flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) &&
+        !(flags & KRB5_KDB_FLAG_CROSS_REALM)) {
+        /* Add delegation info for the first S4U2Proxy request. */
+        ret = update_delegation_info(context, req, subject_pac, new_pac);
+        if (ret)
+            goto cleanup;
+    } else if (subject_pac != NULL) {
+        /* Copy delegation info if it was present in the subject PAC. */
+        ret = copy_pac_buffer(context, KRB5_PAC_DELEGATION_INFO, subject_pac,
+                              new_pac);
+        if (ret && ret != ENOENT)
+            goto cleanup;
+    }
+
+    if ((flags & KRB5_KDB_FLAGS_S4U) &&
+        (flags & KRB5_KDB_FLAG_ISSUING_REFERRAL)) {
+        /* When issuing a referral for either kind of S4U request, add client
+         * info for the subject with realm. */
+        pac_client = altcprinc;
+        with_realm = TRUE;
+    } else if (subject_pac == NULL || (flags & KRB5_KDB_FLAGS_S4U)) {
+        /* For a new PAC or when issuing a final ticket for either kind of S4U
+         * request, add client info for the ticket client without the realm. */
+        pac_client = enc_tkt_reply->client;
+        with_realm = FALSE;
+    } else {
+        /*
+         * For regular TGS and transitive RBCD requests, copy the client info
+         * from the incoming PAC, and don't add client info during signing.  We
+         * validated the incoming client info in validate_tgs_request().
+         */
+        ret = copy_pac_buffer(context, KRB5_PAC_CLIENT_INFO, subject_pac,
+                              new_pac);
+        if (ret)
+            goto cleanup;
+        pac_client = NULL;
+        with_realm = FALSE;
+    }
+
+    ret = krb5_kdc_sign_ticket(context, enc_tkt_reply, new_pac, server->princ,
+                               pac_client, server_key, local_tgt_key,
+                               with_realm);
+    if (ret)
+        goto cleanup;
+
+    ret = 0;
+
+cleanup:
+    krb5_pac_free(context, new_pac);
+    return ret;
+}
+
 krb5_error_code
-handle_authdata(krb5_context context, unsigned int flags,
+handle_authdata(kdc_realm_t *kdc_active_realm, unsigned int flags,
                 krb5_db_entry *client, krb5_db_entry *server,
                 krb5_db_entry *subject_server, krb5_db_entry *local_tgt,
                 krb5_keyblock *local_tgt_key, krb5_keyblock *client_key,
                 krb5_keyblock *server_key, krb5_keyblock *subject_key,
                 krb5_data *req_pkt, krb5_kdc_req *req,
-                krb5_const_principal altcprinc, void *ad_info,
-                krb5_enc_tkt_part *enc_tkt_req,
-                krb5_data ***auth_indicators,
+                krb5_const_principal altcprinc, krb5_pac subject_pac,
+                krb5_enc_tkt_part *enc_tkt_req, krb5_data ***auth_indicators,
                 krb5_enc_tkt_part *enc_tkt_reply)
 {
     kdcauthdata_handle *h;
     krb5_error_code ret = 0;
     size_t i;
+    krb5_context context = kdc_active_realm->realm_context;
 
     if (req->msg_type == KRB5_TGS_REQ &&
         req->authorization_data.ciphertext.data != NULL) {
@@ -839,35 +614,9 @@ handle_authdata(krb5_context context, unsigned int flags,
             return ret;
     }
 
-    if (!isflagset(enc_tkt_reply->flags, TKT_FLG_ANONYMOUS)) {
-        /* Fetch authdata from the KDB if appropriate. */
-        ret = fetch_kdb_authdata(context, flags, client, server,
-                                 subject_server, local_tgt, client_key,
-                                 server_key, subject_key, local_tgt_key,
-                                 req, altcprinc, ad_info, enc_tkt_req,
-                                 enc_tkt_reply, auth_indicators);
-        if (ret)
-            return ret;
-    }
-
-    /* Add auth indicators if any were given. */
-    if (auth_indicators != NULL && *auth_indicators != NULL &&
-        !isflagset(server->attributes, KRB5_KDB_NO_AUTH_DATA_REQUIRED)) {
-        ret = add_auth_indicators(context, *auth_indicators, server_key,
-                                  local_tgt, local_tgt_key, enc_tkt_reply);
-        if (ret)
-            return ret;
-    }
-
-    if (!isflagset(enc_tkt_reply->flags, TKT_FLG_ANONYMOUS)) {
-        /* Validate and insert AD-SIGNTICKET authdata.  This must happen last
-         * since it contains a signature over the other authdata. */
-        ret = handle_signticket(context, flags, subject_server, server,
-                                local_tgt, local_tgt_key, req, altcprinc,
-                                enc_tkt_req, enc_tkt_reply);
-        if (ret)
-            return ret;
-    }
-
-    return 0;
+    return handle_pac(kdc_active_realm, flags, client, server, subject_server,
+                      local_tgt, local_tgt_key, server_key, subject_key,
+                      enc_tkt_req, subject_pac, req, altcprinc,
+                      enc_tkt_reply->times.authtime, enc_tkt_reply,
+                      auth_indicators);
 }
diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c
index 60f30c4..9f2a67d 100644
--- a/src/kdc/kdc_util.c
+++ b/src/kdc/kdc_util.c
@@ -520,6 +520,100 @@ cleanup:
     return ret;
 }
 
+/*
+ * If a PAC is present in enc_tkt, verify it and place it in *pac_out.  sprinc
+ * is the canonical name of the server principal entry used to decrypt enc_tkt.
+ * server_key is the ticket decryption key.  tgt is the local krbtgt entry for
+ * the ticket server realm, and tgt_key is its first key.
+ */
+krb5_error_code
+get_verified_pac(krb5_context context, const krb5_enc_tkt_part *enc_tkt,
+                 krb5_const_principal sprinc, krb5_keyblock *server_key,
+                 krb5_db_entry *tgt, krb5_keyblock *tgt_key, krb5_pac *pac_out)
+{
+    krb5_error_code ret;
+    krb5_key_data *kd;
+    krb5_keyblock old_key;
+    krb5_kvno kvno;
+    int tries;
+
+    *pac_out = NULL;
+
+    /* For local or cross-realm TGTs we only check the server signature. */
+    if (krb5_is_tgs_principal(sprinc)) {
+        return krb5_kdc_verify_ticket(context, enc_tkt, sprinc, server_key,
+                                      NULL, pac_out);
+    }
+
+    ret = krb5_kdc_verify_ticket(context, enc_tkt, sprinc, server_key,
+                                 tgt_key, pac_out);
+    if (ret != KRB5KRB_AP_ERR_MODIFIED && ret != KRB5_BAD_ENCTYPE)
+        return ret;
+
+    /* There is no kvno in PAC signatures, so try two previous versions. */
+    kvno = tgt->key_data[0].key_data_kvno - 1;
+    for (tries = 2; tries > 0 && kvno > 0; tries--, kvno--) {
+        ret = krb5_dbe_find_enctype(context, tgt, -1, -1, kvno, &kd);
+        if (ret)
+            return KRB5KRB_AP_ERR_MODIFIED;
+        ret = krb5_dbe_decrypt_key_data(context, NULL, kd, &old_key, NULL);
+        if (ret)
+            return ret;
+        ret = krb5_kdc_verify_ticket(context, enc_tkt, sprinc, server_key,
+                                     &old_key, pac_out);
+        krb5_free_keyblock_contents(context, &old_key);
+        if (!ret)
+            return 0;
+    }
+
+    return KRB5KRB_AP_ERR_MODIFIED;
+}
+
+/*
+ * Fetch the client info from pac and parse it into a principal name, expecting
+ * a realm in the string.  Set *authtime_out to the client info authtime if it
+ * is not null.
+ */
+krb5_error_code
+get_pac_princ_with_realm(krb5_context context, krb5_pac pac,
+                         krb5_principal *princ_out,
+                         krb5_timestamp *authtime_out)
+{
+    krb5_error_code ret;
+    int n_atsigns, flags = KRB5_PRINCIPAL_PARSE_REQUIRE_REALM;
+    char *client_str = NULL;
+    const char *p;
+
+    *princ_out = NULL;
+
+    ret = krb5_pac_get_client_info(context, pac, authtime_out, &client_str);
+    if (ret)
+        return ret;
+
+    n_atsigns = 0;
+    for (p = client_str; *p != '\0'; p++) {
+        if (*p == '@')
+            n_atsigns++;
+    }
+
+    if (n_atsigns == 2) {
+        flags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE;
+    } else if (n_atsigns != 1) {
+        ret = KRB5_PARSE_MALFORMED;
+        goto cleanup;
+    }
+
+    ret = krb5_parse_name_flags(context, client_str, flags, princ_out);
+    if (ret)
+        return ret;
+
+    (*princ_out)->type = KRB5_NT_MS_PRINCIPAL;
+
+cleanup:
+    free(client_str);
+    return 0;
+}
+
 /* This probably wants to be updated if you support last_req stuff */
 
 static krb5_last_req_entry nolrentry = { KV5M_LAST_REQ_ENTRY, KRB5_LRQ_NONE, 0 };
@@ -1387,8 +1481,7 @@ is_client_db_alias(krb5_context context, const krb5_db_entry *entry,
     krb5_db_entry *self;
     krb5_boolean is_self = FALSE;
 
-    ret = krb5_db_get_principal(context, princ,
-                                KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY, &self);
+    ret = krb5_db_get_principal(context, princ, KRB5_KDB_FLAG_CLIENT, &self);
     if (!ret) {
         is_self = krb5_principal_compare(context, entry->princ, self->princ);
         krb5_db_free_principal(context, self);
@@ -1451,7 +1544,7 @@ kdc_process_s4u2self_req(kdc_realm_t *kdc_active_realm,
         if (id->subject_cert.length != 0) {
             code = krb5_db_get_s4u_x509_principal(kdc_context,
                                                   &id->subject_cert, id->user,
-                                                  KRB5_KDB_FLAG_INCLUDE_PAC,
+                                                  KRB5_KDB_FLAG_CLIENT,
                                                   &princ);
             if (code == 0 && id->user->length == 0) {
                 krb5_free_principal(kdc_context, id->user);
@@ -1460,7 +1553,7 @@ kdc_process_s4u2self_req(kdc_realm_t *kdc_active_realm,
             }
         } else {
             code = krb5_db_get_principal(kdc_context, id->user,
-                                         KRB5_KDB_FLAG_INCLUDE_PAC, &princ);
+                                         KRB5_KDB_FLAG_CLIENT, &princ);
         }
         if (code == KRB5_KDB_NOENTRY) {
             *status = "UNKNOWN_S4U2SELF_PRINCIPAL";
@@ -1481,6 +1574,29 @@ kdc_process_s4u2self_req(kdc_realm_t *kdc_active_realm,
     return 0;
 }
 
+/* Clear the forwardable flag in tkt if server cannot obtain forwardable
+ * S4U2Self tickets according to [MS-SFU] 3.2.5.1.2. */
+krb5_error_code
+s4u2self_forwardable(krb5_context context, krb5_db_entry *server,
+                     krb5_enc_tkt_part *tkt)
+{
+    krb5_error_code ret;
+
+    /* Allow the forwardable flag if server has ok-to-auth-as-delegate set. */
+    if (server->attributes & KRB5_KDB_OK_TO_AUTH_AS_DELEGATE)
+        return 0;
+
+    /* Deny the forwardable flag if server has any authorized delegation
+     * targets for traditional S4U2Proxy. */
+    ret = krb5_db_check_allowed_to_delegate(context, NULL, server, NULL);
+    if (!ret)
+        tkt->flags &= ~TKT_FLG_FORWARDABLE;
+
+    if (ret == KRB5KDC_ERR_BADOPTION || ret == KRB5_PLUGIN_OP_NOTSUPP)
+        return 0;
+    return ret;
+}
+
 /*
  * Determine if an S4U2Proxy request is authorized.  Set **stkt_ad_info to the
  * KDB authdata handle for the second ticket if the KDB module supplied one.
@@ -1489,97 +1605,80 @@ kdc_process_s4u2self_req(kdc_realm_t *kdc_active_realm,
  */
 krb5_error_code
 kdc_process_s4u2proxy_req(kdc_realm_t *kdc_active_realm, unsigned int flags,
-                          krb5_kdc_req *request,
-                          const krb5_enc_tkt_part *t2enc,
-                          krb5_db_entry *krbtgt, krb5_keyblock *krbtgt_key,
+                          krb5_kdc_req *request, krb5_pac header_pac,
+                          const krb5_enc_tkt_part *t2enc, krb5_pac t2_pac,
                           const krb5_db_entry *server,
                           krb5_keyblock *server_key,
                           krb5_const_principal server_princ,
                           const krb5_db_entry *proxy,
-                          krb5_const_principal proxy_princ,
-                          void *ad_info, void **stkt_ad_info,
-                          krb5_principal *stkt_authdata_client,
+                          krb5_principal *stkt_pac_client,
                           const char **status)
 {
     krb5_error_code errcode;
     krb5_boolean support_rbcd;
-    krb5_principal client_princ = t2enc->client;
+    krb5_principal client_princ = t2enc->client, t2_pac_princ = NULL;
+
+    *stkt_pac_client = NULL;
 
     /* Check if the client supports resource-based constrained delegation. */
     errcode = kdc_get_pa_pac_rbcd(kdc_context, request->padata, &support_rbcd);
     if (errcode)
         return errcode;
 
-    errcode = krb5_db_get_authdata_info(kdc_context, flags,
-                                        t2enc->authorization_data,
-                                        t2enc->client, proxy_princ, server_key,
-                                        krbtgt_key, krbtgt,
-                                        t2enc->times.authtime, stkt_ad_info,
-                                        stkt_authdata_client);
-    if (errcode != 0 && errcode != KRB5_PLUGIN_OP_NOTSUPP) {
-        *status = "NOT_ALLOWED_TO_DELEGATE";
-        return errcode;
-    }
-
-    /* For RBCD we require that both client and impersonator's authdata have
-     * been verified. */
-    if (errcode != 0 || ad_info == NULL)
-        support_rbcd = FALSE;
-
-    /* For an RBCD final request, the KDB module must be able to recover the
-     * reply ticket client name from the evidence ticket authorization data. */
-    if (isflagset(flags, KRB5_KDB_FLAG_CROSS_REALM)) {
-        if (*stkt_authdata_client == NULL ||
-            (*stkt_authdata_client)->realm.length == 0) {
-            *status = "UNSUPPORTED_S4U2PROXY_REQUEST";
-            return KRB5KDC_ERR_BADOPTION;
+    /* For an RBCD final request, recover the reply ticket client name from
+     * the evidence ticket PAC. */
+    if (flags & KRB5_KDB_FLAG_CROSS_REALM) {
+        if (get_pac_princ_with_realm(kdc_context, t2_pac, &t2_pac_princ,
+                                     NULL) != 0) {
+            *status = "RBCD_PAC_PRINC";
+            errcode = KRB5KDC_ERR_BADOPTION;
+            goto done;
         }
-
-        client_princ = *stkt_authdata_client;
+        client_princ = t2_pac_princ;
     }
 
     /* If both are in the same realm, try allowed_to_delegate first. */
-    if (krb5_realm_compare(kdc_context, server->princ, proxy_princ)) {
+    if (krb5_realm_compare(kdc_context, server->princ, request->server)) {
 
         errcode = krb5_db_check_allowed_to_delegate(kdc_context, client_princ,
-                                                    server, proxy_princ);
-        if (errcode != 0 && errcode != KRB5KDC_ERR_POLICY &&
+                                                    server, request->server);
+        if (errcode != KRB5KDC_ERR_BADOPTION &&
             errcode != KRB5_PLUGIN_OP_NOTSUPP)
-            return errcode;
-
-        if (errcode == 0) {
-
-            /*
-             * In legacy constrained-delegation, the evidence ticket must be
-             * forwardable.  This check deliberately causes an error response
-             * even if the delegation is also authorized by resource-based
-             * constrained delegation (which does not require a forwardable
-             * evidence ticket).  Windows KDCs behave the same way.
-             */
-            if (!isflagset(t2enc->flags, TKT_FLG_FORWARDABLE)) {
-                *status = "EVIDENCE_TKT_NOT_FORWARDABLE";
-                return KRB5KDC_ERR_BADOPTION;
-            }
+            goto done;
 
-            return 0;
-        }
         /* Fall back to resource-based constrained-delegation. */
     }
 
     if (!support_rbcd) {
         *status = "UNSUPPORTED_S4U2PROXY_REQUEST";
-        return KRB5KDC_ERR_BADOPTION;
+        errcode = KRB5KDC_ERR_BADOPTION;
+        goto done;
     }
 
     /* If we are issuing a referral, the KDC in the resource realm will check
      * if delegation is allowed. */
-    if (isflagset(flags, KRB5_KDB_FLAG_ISSUING_REFERRAL))
-        return 0;
+    if (isflagset(flags, KRB5_KDB_FLAG_ISSUING_REFERRAL)) {
+        errcode = 0;
+        goto done;
+    }
 
     errcode = krb5_db_allowed_to_delegate_from(kdc_context, client_princ,
-                                               server_princ, ad_info, proxy);
-    if (errcode)
+                                               server_princ, header_pac,
+                                               proxy);
+    if (errcode == KRB5_PLUGIN_OP_NOTSUPP) {
+        *status = "UNSUPPORTED_S4U2PROXY_REQUEST";
+        errcode = KRB5KDC_ERR_BADOPTION;
+        goto done;
+    } else if (errcode) {
         *status = "NOT_ALLOWED_TO_DELEGATE";
+        goto done;
+    }
+
+    *stkt_pac_client = t2_pac_princ;
+    t2_pac_princ = NULL;
+
+done:
+    krb5_free_principal(kdc_context, t2_pac_princ);
     return errcode;
 }
 
diff --git a/src/kdc/kdc_util.h b/src/kdc/kdc_util.h
index f2d179c..45683ec 100644
--- a/src/kdc/kdc_util.h
+++ b/src/kdc/kdc_util.h
@@ -86,7 +86,8 @@ validate_as_request (kdc_realm_t *, krb5_kdc_req *, krb5_db_entry *,
 int
 validate_tgs_request(kdc_realm_t *kdc_active_realm,
                      krb5_kdc_req *request, krb5_db_entry *server,
-                     krb5_ticket *ticket, const krb5_ticket *stkt,
+                     krb5_ticket *ticket, krb5_pac pac,
+                     const krb5_ticket *stkt, krb5_pac stkt_pac,
                      krb5_db_entry *stkt_server, krb5_timestamp kdc_time,
                      krb5_pa_s4u_x509_user *s4u_x509_user,
                      krb5_db_entry *s4u2self_client,
@@ -225,7 +226,7 @@ get_auth_indicators(krb5_context context, krb5_enc_tkt_part *enc_tkt,
                     krb5_data ***indicators_out);
 
 krb5_error_code
-handle_authdata (krb5_context context,
+handle_authdata (kdc_realm_t *kdc_active_realm,
                  unsigned int flags,
                  krb5_db_entry *client,
                  krb5_db_entry *server,
@@ -238,7 +239,7 @@ handle_authdata (krb5_context context,
                  krb5_data *req_pkt,
                  krb5_kdc_req *request,
                  krb5_const_principal altcprinc,
-                 void *ad_info,
+                 krb5_pac subject_pac,
                  krb5_enc_tkt_part *enc_tkt_request,
                  krb5_data ***auth_indicators,
                  krb5_enc_tkt_part *enc_tkt_reply);
@@ -253,6 +254,17 @@ void kdc_free_lookaside(krb5_context);
 /* kdc_util.c */
 void reset_for_hangup(void *);
 
+krb5_error_code
+get_verified_pac(krb5_context context, const krb5_enc_tkt_part *enc_tkt,
+                 krb5_const_principal sprinc, krb5_keyblock *server_key,
+                 krb5_db_entry *tgt, krb5_keyblock *tgt_key,
+                 krb5_pac *pac_out);
+
+krb5_error_code
+get_pac_princ_with_realm(krb5_context context, krb5_pac pac,
+                         krb5_principal *princ_out,
+                         krb5_timestamp *authtime_out);
+
 krb5_boolean
 include_pac_p(krb5_context context, krb5_kdc_req *request);
 
@@ -275,6 +287,10 @@ kdc_process_s4u2self_req (kdc_realm_t *kdc_active_realm,
                           const char **status);
 
 krb5_error_code
+s4u2self_forwardable(krb5_context context, krb5_db_entry *server,
+                     krb5_enc_tkt_part *enc_tkt);
+
+krb5_error_code
 kdc_make_s4u2self_rep (krb5_context context,
                        krb5_keyblock *tgs_subkey,
                        krb5_keyblock *tgs_session,
@@ -283,21 +299,15 @@ kdc_make_s4u2self_rep (krb5_context context,
                        krb5_enc_kdc_rep_part *reply_encpart);
 
 krb5_error_code
-kdc_process_s4u2proxy_req (kdc_realm_t *kdc_active_realm,
-                           unsigned int flags,
-                           krb5_kdc_req *request,
-                           const krb5_enc_tkt_part *t2enc,
-                           krb5_db_entry *krbtgt,
-                           krb5_keyblock *krbtgt_key,
-                           const krb5_db_entry *server,
-                           krb5_keyblock *server_key,
-                           krb5_const_principal server_princ,
-                           const krb5_db_entry *proxy,
-                           krb5_const_principal proxy_princ,
-                           void *ad_info,
-                           void **stkt_ad_info,
-                           krb5_principal *stkt_ad_client,
-                           const char **status);
+kdc_process_s4u2proxy_req(kdc_realm_t *kdc_active_realm, unsigned int flags,
+                          krb5_kdc_req *request, krb5_pac header_pac,
+                          const krb5_enc_tkt_part *t2enc, krb5_pac t2_pac,
+                          const krb5_db_entry *server,
+                          krb5_keyblock *server_key,
+                          krb5_const_principal server_princ,
+                          const krb5_db_entry *proxy,
+                          krb5_principal *stkt_ad_client,
+                          const char **status);
 
 krb5_error_code
 kdc_check_transited_list (kdc_realm_t *kdc_active_realm,
@@ -546,4 +556,22 @@ current_kvno(krb5_db_entry *entry)
     return (entry->n_key_data == 0) ? 0 : entry->key_data[0].key_data_kvno;
 }
 
+/* MS-PAC section 2.9 */
+struct pac_s4u_delegation_info {
+    char *proxy_target;
+    uint32_t transited_services_length;
+    char **transited_services;
+};
+
+/* Leaves room for one additional service. */
+krb5_error_code
+ndr_dec_delegation_info(krb5_data *data,
+                        struct pac_s4u_delegation_info **res);
+
+krb5_error_code
+ndr_enc_delegation_info(struct pac_s4u_delegation_info *in,
+                        krb5_data *out);
+
+void ndr_free_delegation_info(struct pac_s4u_delegation_info *in);
+
 #endif /* __KRB5_KDC_UTIL__ */
diff --git a/src/kdc/main.c b/src/kdc/main.c
index 7917ffb..074680d 100644
--- a/src/kdc/main.c
+++ b/src/kdc/main.c
@@ -333,6 +333,11 @@ init_realm(kdc_realm_t * rdp, krb5_pointer aprof, char *realm,
     free(svalue);
     svalue = NULL;
 
+    hierarchy[2] = KRB5_CONF_DISABLE_PAC;
+    if (krb5_aprof_get_boolean(aprof, hierarchy, TRUE,
+                               &rdp->realm_disable_pac))
+        rdp->realm_disable_pac = FALSE;
+
     /*
      * We've got our parameters, now go and setup our realm context.
      */
diff --git a/src/kdc/realm_data.h b/src/kdc/realm_data.h
index 8d698dc..2d66915 100644
--- a/src/kdc/realm_data.h
+++ b/src/kdc/realm_data.h
@@ -73,6 +73,7 @@ typedef struct __kdc_realm_data {
     krb5_deltat         realm_maxrlife; /* Maximum renewable life for realm */
     krb5_boolean        realm_reject_bad_transit; /* Accept unverifiable transited_realm ? */
     krb5_boolean        realm_restrict_anon;  /* Anon to local TGT only */
+    krb5_boolean        realm_disable_pac; /* Prevent issuance of PACs. */
 } kdc_realm_t;
 
 struct server_handle {
diff --git a/src/kdc/tgs_policy.c b/src/kdc/tgs_policy.c
index ae6c763..f33ad50 100644
--- a/src/kdc/tgs_policy.c
+++ b/src/kdc/tgs_policy.c
@@ -260,7 +260,7 @@ check_tgs_lineage(krb5_db_entry *server, krb5_ticket *tkt,
 
 static int
 check_tgs_s4u2self(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req,
-                   krb5_db_entry *server, krb5_ticket *tkt,
+                   krb5_db_entry *server, krb5_ticket *tkt, krb5_pac pac,
                    krb5_timestamp kdc_time,
                    krb5_pa_s4u_x509_user *s4u_x509_user, krb5_db_entry *client,
                    krb5_boolean is_crossrealm, krb5_boolean is_referral,
@@ -272,7 +272,7 @@ check_tgs_s4u2self(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req,
     if (!is_referral &&
         !is_client_db_alias(kdc_context, server, tkt->enc_part2->client)) {
         *status = "INVALID_S4U2SELF_REQUEST_SERVER_MISMATCH";
-        return KDC_ERR_C_PRINCIPAL_UNKNOWN; /* match Windows error */
+        return KRB_AP_ERR_BADMATCH;
     }
 
     /* S4U2Self requests must use options valid for AS requests. */
@@ -325,28 +325,117 @@ check_tgs_s4u2self(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req,
         return KDC_ERR_POLICY; /* match Windows error */
     }
 
+    /* The header ticket PAC must be present. */
+    if (pac == NULL) {
+        *status = "S4U2SELF_NO_PAC";
+        return KDC_ERR_TGT_REVOKED;
+    }
+
     if (client != NULL) {
+        /* The header ticket PAC must be for the impersonator. */
+        if (krb5_pac_verify(kdc_context, pac, tkt->enc_part2->times.authtime,
+                            tkt->enc_part2->client, NULL, NULL) != 0) {
+            *status = "S4U2SELF_LOCAL_PAC_CLIENT";
+            return KDC_ERR_BADOPTION;
+        }
+
         /* Validate the client policy.  Use an empty server principal to bypass
          * server policy checks. */
         return validate_as_request(kdc_active_realm, req, client,
                                    &empty_server, kdc_time, status, e_data);
+    } else {
+        /* The header ticket PAC must be for the subject, with realm. */
+        if (krb5_pac_verify_ext(kdc_context, pac,
+                                tkt->enc_part2->times.authtime,
+                                s4u_x509_user->user_id.user, NULL, NULL,
+                                TRUE) != 0) {
+            *status = "S4U2SELF_FOREIGN_PAC_CLIENT";
+            return KDC_ERR_BADOPTION;
+        }
     }
 
     return 0;
 }
 
+/*
+ * Validate pac as an S4U2Proxy subject PAC contained within a cross-realm TGT.
+ * If target_server is non-null, verify that it matches the PAC proxy target.
+ * Return 0 on success, non-zero on failure.
+ */
+static int
+verify_deleg_pac(krb5_context context, krb5_pac pac,
+                 krb5_enc_tkt_part *enc_tkt,
+                 krb5_const_principal target_server)
+{
+    krb5_timestamp pac_authtime;
+    krb5_data deleg_buf = empty_data();
+    krb5_principal princ = NULL;
+    struct pac_s4u_delegation_info *di = NULL;
+    char *client_str = NULL, *target_str = NULL;
+    const char *last_transited;
+    int result = -1;
+
+    /* Make sure the PAC client string can be parsed as a principal with
+     * realm. */
+    if (get_pac_princ_with_realm(context, pac, &princ, &pac_authtime) != 0)
+        goto cleanup;
+    if (pac_authtime != enc_tkt->times.authtime)
+        goto cleanup;
+
+    if (krb5_pac_get_buffer(context, pac, KRB5_PAC_DELEGATION_INFO,
+                            &deleg_buf) != 0)
+        goto cleanup;
+
+    if (ndr_dec_delegation_info(&deleg_buf, &di) != 0)
+        goto cleanup;
+
+    if (target_server != NULL) {
+        if (krb5_unparse_name_flags(context, target_server,
+                                    KRB5_PRINCIPAL_UNPARSE_DISPLAY |
+                                    KRB5_PRINCIPAL_UNPARSE_NO_REALM,
+                                    &target_str) != 0)
+            goto cleanup;
+        if (strcmp(target_str, di->proxy_target) != 0)
+            goto cleanup;
+    }
+
+    /* Check that the most recently added PAC transited service matches the
+     * requesting impersonator. */
+    if (di->transited_services_length == 0)
+        goto cleanup;
+    if (krb5_unparse_name(context, enc_tkt->client, &client_str) != 0)
+        goto cleanup;
+    last_transited = di->transited_services[di->transited_services_length - 1];
+    if (strcmp(last_transited, client_str) != 0)
+        goto cleanup;
+
+    result = 0;
+
+cleanup:
+    free(target_str);
+    free(client_str);
+    ndr_free_delegation_info(di);
+    krb5_free_principal(context, princ);
+    krb5_free_data_contents(context, &deleg_buf);
+    return result;
+}
+
 static int
 check_tgs_s4u2proxy(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req,
-                    krb5_db_entry *server, krb5_ticket *tkt,
-                    const krb5_ticket *stkt, krb5_db_entry *stkt_server,
-                    krb5_boolean is_crossrealm, krb5_boolean is_referral,
-                    const char **status)
+                    krb5_db_entry *server, krb5_ticket *tkt, krb5_pac pac,
+                    const krb5_ticket *stkt, krb5_pac stkt_pac,
+                    krb5_db_entry *stkt_server, krb5_boolean is_crossrealm,
+                    krb5_boolean is_referral, const char **status)
 {
-    /* A second ticket must be present in the request. */
+    /* A forwardable second ticket must be present in the request. */
     if (stkt == NULL) {
         *status = "NO_2ND_TKT";
         return KDC_ERR_BADOPTION;
     }
+    if (!(stkt->enc_part2->flags & TKT_FLG_FORWARDABLE)) {
+        *status = "EVIDENCE_TKT_NOT_FORWARDABLE";
+        return KDC_ERR_BADOPTION;
+    }
 
     /* Constrained delegation is mutually exclusive with renew/forward/etc.
      * (and therefore requires the header ticket to be a TGT). */
@@ -361,6 +450,17 @@ check_tgs_s4u2proxy(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req,
         return KDC_ERR_POLICY;
     }
 
+    /* The header ticket PAC must be present and for the impersonator. */
+    if (pac == NULL) {
+        *status = "S4U2PROXY_NO_HEADER_PAC";
+        return KDC_ERR_TGT_REVOKED;
+    }
+    if (krb5_pac_verify(kdc_context, pac, tkt->enc_part2->times.authtime,
+                        tkt->enc_part2->client, NULL, NULL) != 0) {
+        *status = "S4U2PROXY_HEADER_PAC";
+        return KDC_ERR_BADOPTION;
+    }
+
     /*
      * An S4U2Proxy request must be an initial request to the impersonator's
      * realm (possibly for a target resource in the same realm), or a final
@@ -368,27 +468,51 @@ check_tgs_s4u2proxy(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req,
      * referral-chasing requests do not use the CNAME-IN-ADDL-TKT flag.
      */
 
-    /* For an initial or same-realm request, the second ticket server and
-     * header ticket client must be the same principal. */
-    if (!is_crossrealm && !is_client_db_alias(kdc_context, stkt_server,
-                                              tkt->enc_part2->client)) {
-        *status = "EVIDENCE_TICKET_MISMATCH";
-        return KDC_ERR_SERVER_NOMATCH;
-    }
+    if (stkt_pac == NULL) {
+        *status = "S4U2PROXY_NO_STKT_PAC";
+        return KRB_AP_ERR_MODIFIED;
+    }
+    if (!is_crossrealm) {
+        /* For an initial or same-realm request, the second ticket server and
+         * header ticket client must be the same principal. */
+        if (!is_client_db_alias(kdc_context, stkt_server,
+                                tkt->enc_part2->client)) {
+            *status = "EVIDENCE_TICKET_MISMATCH";
+            return KDC_ERR_SERVER_NOMATCH;
+        }
 
-    /*
-     * For a cross-realm request, the second ticket must be a referral TGT to
-     * our realm with the impersonator as client.  (Unlike the header ticket,
-     * the second ticket contains authdata for the subject client.)  The target
-     * server must also be local, so we must not be issuing a referral.
-     */
-    if (is_crossrealm &&
-        (is_referral || !is_cross_tgs_principal(stkt_server->princ) ||
-         !data_eq(stkt_server->princ->data[1], server->princ->realm) ||
-         !krb5_principal_compare(kdc_context, stkt->enc_part2->client,
-                                 tkt->enc_part2->client))) {
-        *status = "XREALM_EVIDENCE_TICKET_MISMATCH";
-        return KDC_ERR_BADOPTION;
+        /* The second ticket client and PAC client are the subject, and must
+         * match. */
+        if (krb5_pac_verify(kdc_context, stkt_pac,
+                            stkt->enc_part2->times.authtime,
+                            stkt->enc_part2->client, NULL, NULL) != 0) {
+            *status = "S4U2PROXY_LOCAL_STKT_PAC";
+            return KDC_ERR_BADOPTION;
+        }
+
+    } else {
+
+        /*
+         * For a cross-realm request, the second ticket must be a referral TGT
+         * to our realm with the impersonator as client.  The target server
+         * must also be local, so we must not be issuing a referral.
+         */
+        if (is_referral || !is_cross_tgs_principal(stkt_server->princ) ||
+            !data_eq(stkt_server->princ->data[1], server->princ->realm) ||
+            !krb5_principal_compare(kdc_context, stkt->enc_part2->client,
+                                    tkt->enc_part2->client)) {
+            *status = "XREALM_EVIDENCE_TICKET_MISMATCH";
+            return KDC_ERR_BADOPTION;
+        }
+
+        /* The second ticket PAC must be present and for the impersonated
+         * client, with delegation info. */
+        if (stkt_pac == NULL ||
+            verify_deleg_pac(kdc_context, stkt_pac, stkt->enc_part2,
+                             req->server) != 0) {
+            *status = "S4U2PROXY_CROSS_STKT_PAC";
+            return KDC_ERR_BADOPTION;
+        }
     }
 
     return 0;
@@ -421,6 +545,32 @@ check_tgs_u2u(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req,
     return 0;
 }
 
+/* Validate the PAC of a non-S4U TGS request, if one is present. */
+static int
+check_normal_tgs_pac(krb5_context context, krb5_enc_tkt_part *enc_tkt,
+                     krb5_pac pac, krb5_db_entry *server,
+                     krb5_boolean is_crossrealm, const char **status)
+{
+    /* We don't require a PAC for regular TGS requests. */
+    if (pac == NULL)
+        return 0;
+
+    /* For most requests the header ticket PAC will be for the ticket
+     * client. */
+    if (krb5_pac_verify(context, pac, enc_tkt->times.authtime, enc_tkt->client,
+                        NULL, NULL) == 0)
+        return 0;
+
+    /* For intermediate RBCD requests the header ticket PAC will be for the
+     * impersonated client. */
+    if (is_crossrealm && is_cross_tgs_principal(server->princ) &&
+        verify_deleg_pac(context, pac, enc_tkt, NULL) == 0)
+        return 0;
+
+    *status = "HEADER_PAC";
+    return KDC_ERR_BADOPTION;
+}
+
 /*
  * Some TGS-REQ options allow for a non-TGS principal in the ticket.  Do some
  * checks that are peculiar to these cases.  (e.g., ticket service principal
@@ -468,7 +618,8 @@ check_tgs_tgt(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req,
 int
 validate_tgs_request(kdc_realm_t *kdc_active_realm,
                      krb5_kdc_req *request, krb5_db_entry *server,
-                     krb5_ticket *ticket, const krb5_ticket *stkt,
+                     krb5_ticket *ticket, krb5_pac pac,
+                     const krb5_ticket *stkt, krb5_pac stkt_pac,
                      krb5_db_entry *stkt_server, krb5_timestamp kdc_time,
                      krb5_pa_s4u_x509_user *s4u_x509_user,
                      krb5_db_entry *s4u2self_client,
@@ -508,9 +659,9 @@ validate_tgs_request(kdc_realm_t *kdc_active_realm,
 
     if (s4u_x509_user != NULL) {
         errcode = check_tgs_s4u2self(kdc_active_realm, request, server, ticket,
-                                     kdc_time, s4u_x509_user, s4u2self_client,
-                                     is_crossrealm, is_referral,
-                                     status, e_data);
+                                     pac, kdc_time, s4u_x509_user,
+                                     s4u2self_client, is_crossrealm,
+                                     is_referral, status, e_data);
     } else {
         errcode = check_tgs_lineage(server, ticket, is_crossrealm, status);
     }
@@ -526,8 +677,13 @@ validate_tgs_request(kdc_realm_t *kdc_active_realm,
 
     if (request->kdc_options & KDC_OPT_CNAME_IN_ADDL_TKT) {
         errcode = check_tgs_s4u2proxy(kdc_active_realm, request, server,
-                                      ticket, stkt, stkt_server, is_crossrealm,
-                                      is_referral, status);
+                                      ticket, pac, stkt, stkt_pac, stkt_server,
+                                      is_crossrealm, is_referral, status);
+        if (errcode != 0)
+            return errcode;
+    } else if (s4u_x509_user == NULL) {
+        errcode = check_normal_tgs_pac(kdc_context, ticket->enc_part2, pac,
+                                       server, is_crossrealm, status);
         if (errcode != 0)
             return errcode;
     }
diff --git a/src/lib/kdb/kdb5.c b/src/lib/kdb/kdb5.c
index c497218..415ae64 100644
--- a/src/lib/kdb/kdb5.c
+++ b/src/lib/kdb/kdb5.c
@@ -315,7 +315,6 @@ copy_vtable(const kdb_vftabl *in, kdb_vftabl *out)
     out->promote_db = in->promote_db;
     out->decrypt_key_data = in->decrypt_key_data;
     out->encrypt_key_data = in->encrypt_key_data;
-    out->sign_authdata = in->sign_authdata;
     out->check_transited_realms = in->check_transited_realms;
     out->check_policy_as = in->check_policy_as;
     out->check_policy_tgs = in->check_policy_tgs;
@@ -325,8 +324,7 @@ copy_vtable(const kdb_vftabl *in, kdb_vftabl *out)
     out->free_principal_e_data = in->free_principal_e_data;
     out->get_s4u_x509_principal = in->get_s4u_x509_principal;
     out->allowed_to_delegate_from = in->allowed_to_delegate_from;
-    out->get_authdata_info = in->get_authdata_info;
-    out->free_authdata_info = in->free_authdata_info;
+    out->issue_pac = in->issue_pac;
 
     /* Set defaults for optional fields. */
     if (out->fetch_master_key == NULL)
@@ -2597,34 +2595,6 @@ krb5_db_set_context(krb5_context context, void *db_context)
 }
 
 krb5_error_code
-krb5_db_sign_authdata(krb5_context kcontext, unsigned int flags,
-                      krb5_const_principal client_princ,
-                      krb5_const_principal server_princ, krb5_db_entry *client,
-                      krb5_db_entry *server, krb5_db_entry *header_server,
-                      krb5_db_entry *local_tgt, krb5_keyblock *client_key,
-                      krb5_keyblock *server_key, krb5_keyblock *header_key,
-                      krb5_keyblock *local_tgt_key, krb5_keyblock *session_key,
-                      krb5_timestamp authtime, krb5_authdata **tgt_auth_data,
-                      void *ad_info, krb5_data ***auth_indicators,
-                      krb5_authdata ***signed_auth_data)
-{
-    krb5_error_code status = 0;
-    kdb_vftabl *v;
-
-    *signed_auth_data = NULL;
-    status = get_vftabl(kcontext, &v);
-    if (status)
-        return status;
-    if (v->sign_authdata == NULL)
-        return KRB5_PLUGIN_OP_NOTSUPP;
-    return v->sign_authdata(kcontext, flags, client_princ, server_princ,
-                            client, server, header_server, local_tgt,
-                            client_key, server_key, header_key, local_tgt_key,
-                            session_key, authtime, tgt_auth_data, ad_info,
-                            auth_indicators, signed_auth_data);
-}
-
-krb5_error_code
 krb5_db_check_transited_realms(krb5_context kcontext,
                                const krb5_data *tr_contents,
                                const krb5_data *client_realm,
@@ -2757,7 +2727,7 @@ krb5_error_code
 krb5_db_allowed_to_delegate_from(krb5_context kcontext,
                                  krb5_const_principal client,
                                  krb5_const_principal server,
-                                 void *server_ad_info,
+                                 krb5_pac server_pac,
                                  const krb5_db_entry *proxy)
 {
     krb5_error_code ret;
@@ -2768,50 +2738,8 @@ krb5_db_allowed_to_delegate_from(krb5_context kcontext,
         return ret;
     if (v->allowed_to_delegate_from == NULL)
         return KRB5_PLUGIN_OP_NOTSUPP;
-    return v->allowed_to_delegate_from(kcontext, client, server,
-                                       server_ad_info, proxy);
-}
-
-krb5_error_code
-krb5_db_get_authdata_info(krb5_context kcontext, unsigned int flags,
-                          krb5_authdata **in_authdata,
-                          krb5_const_principal client_princ,
-                          krb5_const_principal server_princ,
-                          krb5_keyblock *server_key, krb5_keyblock *krbtgt_key,
-                          krb5_db_entry *krbtgt, krb5_timestamp authtime,
-                          void **ad_info_out, krb5_principal *client_out)
-{
-    krb5_error_code ret;
-    kdb_vftabl *v;
-
-    *ad_info_out = NULL;
-    if (client_out != NULL)
-        *client_out = NULL;
-
-    ret = get_vftabl(kcontext, &v);
-    if (ret)
-        return ret;
-    if (v->get_authdata_info == NULL)
-        return KRB5_PLUGIN_OP_NOTSUPP;
-    return v->get_authdata_info(kcontext, flags, in_authdata, client_princ,
-                                server_princ, server_key, krbtgt_key, krbtgt,
-                                authtime, ad_info_out, client_out);
-}
-
-void
-krb5_db_free_authdata_info(krb5_context kcontext, void *ad_info)
-{
-    krb5_error_code ret;
-    kdb_vftabl *v;
-
-    if (ad_info == NULL)
-        return;
-    ret = get_vftabl(kcontext, &v);
-    if (ret)
-        return;
-    if (v->free_authdata_info == NULL)
-        return;
-    v->free_authdata_info(kcontext, ad_info);
+    return v->allowed_to_delegate_from(kcontext, client, server, server_pac,
+                                       proxy);
 }
 
 void
@@ -2832,3 +2760,22 @@ krb5_dbe_sort_key_data(krb5_key_data *key_data, size_t key_data_length)
         }
     }
 }
+
+krb5_error_code
+krb5_db_issue_pac(krb5_context context, unsigned int flags,
+                  krb5_db_entry *client, krb5_keyblock *replaced_reply_key,
+                  krb5_db_entry *server, krb5_db_entry *krbtgt,
+                  krb5_timestamp authtime, krb5_pac old_pac, krb5_pac new_pac,
+                  krb5_data ***auth_indicators)
+{
+    krb5_error_code ret;
+    kdb_vftabl *v;
+
+    ret = get_vftabl(context, &v);
+    if (ret)
+        return ret;
+    if (v->issue_pac == NULL)
+        return KRB5_PLUGIN_OP_NOTSUPP;
+    return v->issue_pac(context, flags, client, replaced_reply_key, server,
+                        krbtgt, authtime, old_pac, new_pac, auth_indicators);
+}
diff --git a/src/lib/kdb/libkdb5.exports b/src/lib/kdb/libkdb5.exports
index b71984d..97dc5c0 100644
--- a/src/lib/kdb/libkdb5.exports
+++ b/src/lib/kdb/libkdb5.exports
@@ -16,13 +16,12 @@ krb5_db_destroy
 krb5_db_fetch_mkey
 krb5_db_fetch_mkey_list
 krb5_db_fini
-krb5_db_free_authdata_info
 krb5_db_free_principal
 krb5_db_get_age
-krb5_db_get_authdata_info
 krb5_db_get_key_data_kvno
 krb5_db_get_context
 krb5_db_get_principal
+krb5_db_issue_pac
 krb5_db_iterate
 krb5_db_lock
 krb5_db_mkey_list_alias
@@ -31,7 +30,6 @@ krb5_db_refresh_config
 krb5_db_rename_principal
 krb5_db_set_context
 krb5_db_setup_mkey_name
-krb5_db_sign_authdata
 krb5_db_unlock
 krb5_db_store_master_key
 krb5_db_store_master_key_list
diff --git a/src/lib/krb5/asn.1/asn1_k_encode.c b/src/lib/krb5/asn.1/asn1_k_encode.c
index d91964e..5378b5c 100644
--- a/src/lib/krb5/asn.1/asn1_k_encode.c
+++ b/src/lib/krb5/asn.1/asn1_k_encode.c
@@ -1091,33 +1091,6 @@ DEFPTRTYPE(ptr_seqof_princ_plus_realm, seqof_princ_plus_realm);
 DEFOPTIONALEMPTYTYPE(opt_ptr_seqof_princ_plus_realm,
                      ptr_seqof_princ_plus_realm);
 
-DEFFIELD(spdata_0, krb5_ad_signedpath_data, client, 0, princ_plus_realm);
-DEFFIELD(spdata_1, krb5_ad_signedpath_data, authtime, 1, kerberos_time);
-DEFFIELD(spdata_2, krb5_ad_signedpath_data, delegated, 2,
-         opt_ptr_seqof_princ_plus_realm);
-DEFFIELD(spdata_3, krb5_ad_signedpath_data, method_data, 3,
-         opt_ptr_seqof_pa_data);
-DEFFIELD(spdata_4, krb5_ad_signedpath_data, authorization_data, 4,
-         opt_auth_data_ptr);
-static const struct atype_info *ad_signedpath_data_fields[] = {
-    &k5_atype_spdata_0, &k5_atype_spdata_1, &k5_atype_spdata_2,
-    &k5_atype_spdata_3, &k5_atype_spdata_4
-};
-DEFSEQTYPE(ad_signedpath_data, krb5_ad_signedpath_data,
-           ad_signedpath_data_fields);
-
-DEFFIELD(signedpath_0, krb5_ad_signedpath, enctype, 0, int32);
-DEFFIELD(signedpath_1, krb5_ad_signedpath, checksum, 1, checksum);
-DEFFIELD(signedpath_2, krb5_ad_signedpath, delegated, 2,
-         opt_ptr_seqof_princ_plus_realm);
-DEFFIELD(signedpath_3, krb5_ad_signedpath, method_data, 3,
-         opt_ptr_seqof_pa_data);
-static const struct atype_info *ad_signedpath_fields[] = {
-    &k5_atype_signedpath_0, &k5_atype_signedpath_1, &k5_atype_signedpath_2,
-    &k5_atype_signedpath_3
-};
-DEFSEQTYPE(ad_signedpath, krb5_ad_signedpath, ad_signedpath_fields);
-
 /* First context tag is 1, not 0. */
 DEFFIELD(iakerb_header_1, krb5_iakerb_header, target_realm, 1, ostring_data);
 DEFFIELD(iakerb_header_2, krb5_iakerb_header, cookie, 2, opt_ostring_data_ptr);
@@ -1323,9 +1296,6 @@ MAKE_DECODER(decode_krb5_fast_response, fast_response);
 
 MAKE_ENCODER(encode_krb5_ad_kdcissued, ad_kdc_issued);
 MAKE_DECODER(decode_krb5_ad_kdcissued, ad_kdc_issued);
-MAKE_ENCODER(encode_krb5_ad_signedpath_data, ad_signedpath_data);
-MAKE_ENCODER(encode_krb5_ad_signedpath, ad_signedpath);
-MAKE_DECODER(decode_krb5_ad_signedpath, ad_signedpath);
 MAKE_ENCODER(encode_krb5_iakerb_header, iakerb_header);
 MAKE_DECODER(decode_krb5_iakerb_header, iakerb_header);
 MAKE_ENCODER(encode_krb5_iakerb_finished, iakerb_finished);
diff --git a/src/lib/krb5/krb/Makefile.in b/src/lib/krb5/krb/Makefile.in
index 5a163ab..e4b560f 100644
--- a/src/lib/krb5/krb/Makefile.in
+++ b/src/lib/krb5/krb/Makefile.in
@@ -100,7 +100,6 @@ STLIBOBJS= \
 	rd_safe.o	\
 	recvauth.o	\
 	response_items.o	\
-	s4u_authdata.o	\
 	s4u_creds.o	\
 	sendauth.o	\
 	send_tgs.o	\
@@ -214,7 +213,6 @@ OBJS=	$(OUTPRE)addr_comp.$(OBJEXT)	\
 	$(OUTPRE)rd_safe.$(OBJEXT)	\
 	$(OUTPRE)recvauth.$(OBJEXT)	\
 	$(OUTPRE)response_items.$(OBJEXT)	\
-	$(OUTPRE)s4u_authdata.$(OBJEXT)	\
 	$(OUTPRE)s4u_creds.$(OBJEXT)	\
 	$(OUTPRE)sendauth.$(OBJEXT)	\
 	$(OUTPRE)send_tgs.$(OBJEXT)	\
@@ -328,7 +326,6 @@ SRCS=	$(srcdir)/addr_comp.c	\
 	$(srcdir)/rd_safe.c	\
 	$(srcdir)/recvauth.c	\
 	$(srcdir)/response_items.c	\
-	$(srcdir)/s4u_authdata.c\
 	$(srcdir)/s4u_creds.c	\
 	$(srcdir)/sendauth.c	\
 	$(srcdir)/send_tgs.c	\
@@ -396,7 +393,7 @@ T_KERB_OBJS= t_kerb.o conv_princ.o unparse.o set_realm.o str_conv.o
 
 T_SER_OBJS= t_ser.o ser_actx.o ser_adata.o ser_addr.o ser_auth.o ser_cksum.o \
 	ser_ctx.o ser_key.o ser_princ.o serialize.o authdata.o pac.o \
-	pac_sign.o ai_authdata.o authdata_exp.o s4u_authdata.o copy_data.o etype_list.o
+	pac_sign.o ai_authdata.o authdata_exp.o copy_data.o etype_list.o
 
 T_DELTAT_OBJS= t_deltat.o deltat.o
 
diff --git a/src/lib/krb5/krb/authdata.c b/src/lib/krb5/krb/authdata.c
index 7f28883..a9023b0 100644
--- a/src/lib/krb5/krb/authdata.c
+++ b/src/lib/krb5/krb/authdata.c
@@ -44,7 +44,6 @@ static const char *objdirs[] = {
 /* Internal authdata systems */
 static krb5plugin_authdata_client_ftable_v0 *authdata_systems[] = {
     &k5_mspac_ad_client_ftable,
-    &k5_s4u2proxy_ad_client_ftable,
     &k5_authind_ad_client_ftable,
     NULL
 };
diff --git a/src/lib/krb5/krb/deps b/src/lib/krb5/krb/deps
index ca6ab25..dd1fbf8 100644
--- a/src/lib/krb5/krb/deps
+++ b/src/lib/krb5/krb/deps
@@ -985,18 +985,6 @@ response_items.so response_items.po $(OUTPRE)response_items.$(OBJEXT): \
   $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
   $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
   int-proto.h response_items.c
-s4u_authdata.so s4u_authdata.po $(OUTPRE)s4u_authdata.$(OBJEXT): \
-  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
-  $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
-  $(COM_ERR_DEPS) $(srcdir)/../rcache/memrcache.h $(top_srcdir)/include/k5-buf.h \
-  $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
-  $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
-  $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
-  $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
-  $(top_srcdir)/include/k5-utf8.h $(top_srcdir)/include/krb5.h \
-  $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
-  $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
-  auth_con.h authdata.h int-proto.h s4u_authdata.c
 s4u_creds.so s4u_creds.po $(OUTPRE)s4u_creds.$(OBJEXT): \
   $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
   $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
diff --git a/src/lib/krb5/krb/kfree.c b/src/lib/krb5/krb/kfree.c
index 6e38044..b4503d2 100644
--- a/src/lib/krb5/krb/kfree.c
+++ b/src/lib/krb5/krb/kfree.c
@@ -744,24 +744,6 @@ krb5_free_ad_kdcissued(krb5_context context, krb5_ad_kdcissued *val)
 }
 
 void KRB5_CALLCONV
-krb5_free_ad_signedpath(krb5_context context, krb5_ad_signedpath *val)
-{
-    int i;
-
-    if (val == NULL)
-        return;
-
-    krb5_free_checksum_contents(context, &val->checksum);
-    if (val->delegated != NULL) {
-        for (i = 0; val->delegated[i] != NULL; i++)
-            krb5_free_principal(context, val->delegated[i]);
-        free(val->delegated);
-    }
-    krb5_free_pa_data(context, val->method_data);
-    free(val);
-}
-
-void KRB5_CALLCONV
 krb5_free_iakerb_header(krb5_context context, krb5_iakerb_header *val)
 {
     if (val == NULL)
diff --git a/src/lib/krb5/krb/s4u_authdata.c b/src/lib/krb5/krb/s4u_authdata.c
deleted file mode 100644
index c33a50a..0000000
--- a/src/lib/krb5/krb/s4u_authdata.c
+++ /dev/null
@@ -1,598 +0,0 @@
-/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
- * Copyright 2010 by the Massachusetts Institute of Technology.  All
- * Rights Reserved.
- *
- * Export of this software from the United States of America may
- *   require a specific license from the United States Government.
- *   It is the responsibility of any person or organization contemplating
- *   export to obtain such a license before exporting.
- *
- * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
- * distribute this software and its documentation for any purpose and
- * without fee is hereby granted, provided that the above copyright
- * notice appear in all copies and that both that copyright notice and
- * this permission notice appear in supporting documentation, and that
- * the name of M.I.T. not be used in advertising or publicity pertaining
- * to distribution of the software without specific, written prior
- * permission.  Furthermore if you modify this software you must label
- * your software as modified software and not distribute it in such a
- * fashion that it might be confused with the original M.I.T. software.
- * M.I.T. makes no representations about the suitability of
- * this software for any purpose.  It is provided "as is" without express
- * or implied warranty.
- *
- */
-
-#include "k5-int.h"
-#include "authdata.h"
-#include "auth_con.h"
-#include "int-proto.h"
-
-/*
- * Authdata backend for processing SignedPath. Presently does not handle
- * the equivalent information in [MS-PAC], as that would require an NDR
- * interpreter.
- */
-
-struct s4u2proxy_context {
-    int count;
-    krb5_principal *delegated;
-    krb5_boolean authenticated;
-};
-
-static krb5_error_code
-s4u2proxy_init(krb5_context kcontext, void **plugin_context)
-{
-    *plugin_context = NULL;
-    return 0;
-}
-
-static void
-s4u2proxy_flags(krb5_context kcontext,
-                void *plugin_context,
-                krb5_authdatatype ad_type,
-                krb5_flags *flags)
-{
-    *flags = AD_USAGE_KDC_ISSUED;
-}
-
-static void
-s4u2proxy_fini(krb5_context kcontext, void *plugin_context)
-{
-    return;
-}
-
-static krb5_error_code
-s4u2proxy_request_init(krb5_context kcontext,
-                       krb5_authdata_context context,
-                       void *plugin_context,
-                       void **request_context)
-{
-    krb5_error_code code;
-    struct s4u2proxy_context *s4uctx;
-
-    s4uctx = k5alloc(sizeof(*s4uctx), &code);
-    if (s4uctx == NULL)
-        return code;
-
-    s4uctx->count = 0;
-    s4uctx->delegated = NULL;
-    s4uctx->authenticated = FALSE;
-
-    *request_context = s4uctx;
-
-    return 0;
-}
-
-static void
-s4u2proxy_free_internal(krb5_context kcontext,
-                        krb5_authdata_context context,
-                        void *plugin_context,
-                        void *request_context,
-                        void *ptr)
-{
-    krb5_principal *delegated = (krb5_principal *)ptr;
-    int i;
-
-    if (delegated != NULL) {
-        for (i = 0; delegated[i] != NULL; i++)
-            krb5_free_principal(kcontext, delegated[i]);
-        free(delegated);
-    }
-}
-
-static krb5_error_code
-s4u2proxy_import_authdata(krb5_context kcontext,
-                          krb5_authdata_context context,
-                          void *plugin_context,
-                          void *request_context,
-                          krb5_authdata **authdata,
-                          krb5_boolean kdc_issued,
-                          krb5_const_principal kdc_issuer)
-{
-    struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
-    krb5_error_code code;
-    krb5_ad_signedpath *sp;
-    krb5_data enc_sp;
-
-    enc_sp.data = (char *)authdata[0]->contents;
-    enc_sp.length = authdata[0]->length;
-
-    code = decode_krb5_ad_signedpath(&enc_sp, &sp);
-    if (code != 0)
-        return code;
-
-    s4u2proxy_free_internal(kcontext, context,
-                            plugin_context, request_context,
-                            s4uctx->delegated);
-
-    s4uctx->delegated = sp->delegated;
-    sp->delegated = NULL;
-
-    krb5_free_ad_signedpath(kcontext, sp);
-
-    s4uctx->count = 0;
-
-    if (s4uctx->delegated != NULL) {
-        for (s4uctx->count = 0; s4uctx->delegated[s4uctx->count];
-             s4uctx->count++)
-            ;
-    }
-
-    s4uctx->authenticated = FALSE;
-
-    return 0;
-}
-
-static krb5_error_code
-s4u2proxy_export_authdata(krb5_context kcontext,
-                          krb5_authdata_context context,
-                          void *plugin_context,
-                          void *request_context,
-                          krb5_flags usage,
-                          krb5_authdata ***out_authdata)
-{
-    struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
-    krb5_error_code code;
-    krb5_ad_signedpath sp;
-    krb5_authdata **authdata;
-    krb5_data *data;
-
-    if (s4uctx->count == 0)
-        return 0;
-
-    memset(&sp, 0, sizeof(sp));
-    sp.delegated = s4uctx->delegated;
-
-    authdata = k5calloc(2, sizeof(krb5_authdata *), &code);
-    if (authdata == NULL)
-        return code;
-
-    authdata[0] = k5alloc(sizeof(krb5_authdata), &code);
-    if (authdata[0] == NULL) {
-        free(authdata);
-        return code;
-    }
-
-    code = encode_krb5_ad_signedpath(&sp, &data);
-    if (code != 0) {
-        krb5_free_authdata(kcontext, authdata);
-        return code;
-    }
-
-    authdata[0]->magic = KV5M_AUTHDATA;
-    authdata[0]->ad_type = KRB5_AUTHDATA_SIGNTICKET;
-    authdata[0]->length = data->length;
-    authdata[0]->contents = (krb5_octet *)data->data;
-
-    authdata[1] = NULL;
-
-    free(data);
-
-    *out_authdata = authdata;
-
-    return 0;
-}
-
-static krb5_error_code
-s4u2proxy_verify(krb5_context kcontext,
-                 krb5_authdata_context context,
-                 void *plugin_context,
-                 void *request_context,
-                 const krb5_auth_context *auth_context,
-                 const krb5_keyblock *key,
-                 const krb5_ap_req *req)
-{
-    /*
-     * XXX there is no way to verify the SignedPath without the TGS
-     * key. This means that we can never mark this as authenticated.
-     */
-
-    return 0;
-}
-
-static void
-s4u2proxy_request_fini(krb5_context kcontext,
-                       krb5_authdata_context context,
-                       void *plugin_context,
-                       void *request_context)
-{
-    struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
-
-    if (s4uctx == NULL)
-        return;
-
-    s4u2proxy_free_internal(kcontext, context,
-                            plugin_context, request_context,
-                            s4uctx->delegated);
-    free(s4uctx);
-}
-
-/*
- * Nomenclature defined to be similar to [MS-PAC] 2.9, for future
- * interoperability
- */
-
-static krb5_data s4u2proxy_transited_services_attr = {
-    KV5M_DATA,
-    sizeof("urn:constrained-delegation:transited-services") - 1,
-    "urn:constrained-delegation:transited-services"
-};
-
-static krb5_error_code
-s4u2proxy_get_attribute_types(krb5_context kcontext,
-                              krb5_authdata_context context,
-                              void *plugin_context,
-                              void *request_context,
-                              krb5_data **out_attrs)
-{
-    struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
-    krb5_error_code code;
-    krb5_data *attrs;
-    int i = 0;
-
-    if (s4uctx->count == 0)
-        return ENOENT;
-
-    attrs = k5calloc(2, sizeof(krb5_data), &code);
-    if (attrs == NULL)
-        goto cleanup;
-
-    code = krb5int_copy_data_contents(kcontext,
-                                      &s4u2proxy_transited_services_attr,
-                                      &attrs[i++]);
-    if (code != 0)
-        goto cleanup;
-
-    attrs[i].data = NULL;
-    attrs[i].length = 0;
-
-    *out_attrs = attrs;
-    attrs = NULL;
-
-cleanup:
-    if (attrs != NULL) {
-        for (i = 0; attrs[i].data; i++)
-            krb5_free_data_contents(kcontext, &attrs[i]);
-        free(attrs);
-    }
-
-    return 0;
-}
-
-static krb5_error_code
-s4u2proxy_get_attribute(krb5_context kcontext,
-                        krb5_authdata_context context,
-                        void *plugin_context,
-                        void *request_context,
-                        const krb5_data *attribute,
-                        krb5_boolean *authenticated,
-                        krb5_boolean *complete,
-                        krb5_data *value,
-                        krb5_data *display_value,
-                        int *more)
-{
-    struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
-    krb5_error_code code;
-    krb5_principal principal;
-    int i;
-
-    if (display_value != NULL) {
-        display_value->data = NULL;
-        display_value->length = 0;
-    }
-
-    if (!data_eq(*attribute, s4u2proxy_transited_services_attr))
-        return ENOENT;
-
-    i = -(*more) - 1;
-    if (i < 0)
-        return EINVAL;
-    else if (i >= s4uctx->count)
-        return ENOENT;
-
-    principal = s4uctx->delegated[i];
-    assert(principal != NULL);
-
-    code = krb5_unparse_name_flags(kcontext, principal, 0, &value->data);
-    if (code != 0)
-        return code;
-
-    value->length = strlen(value->data);
-
-    if (display_value != NULL) {
-        code = krb5_unparse_name_flags(kcontext, principal,
-                                       KRB5_PRINCIPAL_UNPARSE_DISPLAY,
-                                       &display_value->data);
-        if (code != 0)
-            return code;
-
-        display_value->length = strlen(display_value->data);
-    }
-
-    i++;
-
-    if (i == s4uctx->count)
-        *more = 0;
-    else
-        *more = -(i + 1);
-
-    *authenticated = s4uctx->authenticated;
-    *complete = TRUE;
-
-    return 0;
-}
-
-static krb5_error_code
-s4u2proxy_set_attribute(krb5_context kcontext,
-                        krb5_authdata_context context,
-                        void *plugin_context,
-                        void *request_context,
-                        krb5_boolean complete,
-                        const krb5_data *attribute,
-                        const krb5_data *value)
-{
-    /* Only the KDC can set this attribute. */
-    if (!data_eq(*attribute, s4u2proxy_transited_services_attr))
-        return ENOENT;
-
-    return EPERM;
-}
-
-static krb5_error_code
-s4u2proxy_export_internal(krb5_context kcontext,
-                          krb5_authdata_context context,
-                          void *plugin_context,
-                          void *request_context,
-                          krb5_boolean restrict_authenticated,
-                          void **ptr)
-{
-    struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
-    krb5_error_code code;
-    int i;
-    krb5_principal *delegated;
-
-    *ptr = NULL;
-
-    if (s4uctx->count == 0)
-        return ENOENT;
-
-    if (restrict_authenticated)
-        return ENOENT;
-
-    delegated = k5calloc(s4uctx->count + 1, sizeof(krb5_principal), &code);
-    if (delegated == NULL)
-        return code;
-
-    for (i = 0; i < s4uctx->count; i++) {
-        code = krb5_copy_principal(kcontext, s4uctx->delegated[i],
-                                   &delegated[i]);
-        if (code != 0)
-            goto cleanup;
-    }
-
-    delegated[i] = NULL;
-
-    *ptr = delegated;
-    delegated = NULL;
-
-cleanup:
-    s4u2proxy_free_internal(kcontext, context,
-                            plugin_context, request_context,
-                            delegated);
-
-    return code;
-}
-
-static krb5_error_code
-s4u2proxy_size(krb5_context kcontext,
-               krb5_authdata_context context,
-               void *plugin_context,
-               void *request_context,
-               size_t *sizep)
-{
-    struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
-    krb5_error_code code = 0;
-    int i;
-
-    *sizep += sizeof(krb5_int32); /* version */
-    *sizep += sizeof(krb5_int32); /* princ count */
-
-    for (i = 0; i < s4uctx->count; i++) {
-        code = k5_size_principal(s4uctx->delegated[i], sizep);
-        if (code != 0)
-            return code;
-    }
-
-    *sizep += sizeof(krb5_int32); /* authenticated flag */
-
-    return code;
-}
-
-static krb5_error_code
-s4u2proxy_externalize(krb5_context kcontext,
-                      krb5_authdata_context context,
-                      void *plugin_context,
-                      void *request_context,
-                      krb5_octet **buffer,
-                      size_t *lenremain)
-{
-    struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
-    krb5_error_code code = 0;
-    size_t required = 0;
-    krb5_octet *bp;
-    size_t remain;
-    int i = 0;
-
-    bp = *buffer;
-    remain = *lenremain;
-
-    s4u2proxy_size(kcontext, context, plugin_context,
-                   request_context, &required);
-
-    if (required > remain)
-        return ENOMEM;
-
-    krb5_ser_pack_int32(1, &bp, &remain); /* version */
-    krb5_ser_pack_int32(s4uctx->count, &bp, &remain); /* princ count */
-
-    for (i = 0; i < s4uctx->count; i++) {
-        code = k5_externalize_principal(s4uctx->delegated[i], &bp, &remain);
-        if (code != 0)
-            return code;
-    }
-
-    krb5_ser_pack_int32(s4uctx->authenticated, &bp, &remain); /* authenticated */
-
-    *buffer = bp;
-    *lenremain = remain;
-
-    return 0;
-}
-
-static krb5_error_code
-s4u2proxy_internalize(krb5_context kcontext,
-                      krb5_authdata_context context,
-                      void *plugin_context,
-                      void *request_context,
-                      krb5_octet **buffer,
-                      size_t *lenremain)
-{
-    struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
-    krb5_error_code code;
-    krb5_int32 ibuf;
-    krb5_octet *bp;
-    size_t remain;
-    int count;
-    krb5_principal *delegated = NULL;
-
-    bp = *buffer;
-    remain = *lenremain;
-
-    /* version */
-    code = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
-    if (code != 0)
-        goto cleanup;
-
-    if (ibuf != 1) {
-        code = EINVAL;
-        goto cleanup;
-    }
-
-    /* count */
-    code = krb5_ser_unpack_int32(&count, &bp, &remain);
-    if (code != 0)
-        goto cleanup;
-
-    if (count > 65535)
-        return ERANGE; /* let's set some reasonable limits here */
-    else if (count > 0) {
-        int i;
-
-        delegated = k5calloc(count + 1, sizeof(krb5_principal), &code);
-        if (delegated == NULL)
-            goto cleanup;
-
-        for (i = 0; i < count; i++) {
-            code = k5_internalize_principal(&delegated[i], &bp, &remain);
-            if (code != 0)
-                goto cleanup;
-        }
-
-        delegated[i] = NULL;
-    }
-
-    code = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
-    if (code != 0)
-        goto cleanup;
-
-    s4u2proxy_free_internal(kcontext, context,
-                            plugin_context, request_context,
-                            s4uctx->delegated);
-
-    s4uctx->count = count;
-    s4uctx->delegated = delegated;
-    s4uctx->authenticated = (ibuf != 0);
-
-    delegated = NULL;
-
-    *buffer = bp;
-    *lenremain = remain;
-
-cleanup:
-    s4u2proxy_free_internal(kcontext, context,
-                            plugin_context, request_context,
-                            delegated);
-
-    return code;
-}
-
-static krb5_error_code
-s4u2proxy_copy(krb5_context kcontext,
-               krb5_authdata_context context,
-               void *plugin_context,
-               void *request_context,
-               void *dst_plugin_context,
-               void *dst_request_context)
-{
-    struct s4u2proxy_context *srcctx = (struct s4u2proxy_context *)request_context;
-    struct s4u2proxy_context *dstctx = (struct s4u2proxy_context *)dst_request_context;
-    krb5_error_code code;
-
-    code = s4u2proxy_export_internal(kcontext, context,
-                                     plugin_context, request_context,
-                                     FALSE, (void **)&dstctx->delegated);
-    if (code != 0 && code != ENOENT)
-        return code;
-
-    dstctx->count = srcctx->count;
-    dstctx->authenticated = srcctx->authenticated;
-
-    return 0;
-}
-
-static krb5_authdatatype s4u2proxy_ad_types[] = { KRB5_AUTHDATA_SIGNTICKET, 0 };
-
-krb5plugin_authdata_client_ftable_v0 k5_s4u2proxy_ad_client_ftable = {
-    "constrained-delegation",
-    s4u2proxy_ad_types,
-    s4u2proxy_init,
-    s4u2proxy_fini,
-    s4u2proxy_flags,
-    s4u2proxy_request_init,
-    s4u2proxy_request_fini,
-    s4u2proxy_get_attribute_types,
-    s4u2proxy_get_attribute,
-    s4u2proxy_set_attribute,
-    NULL, /* delete_attribute_proc */
-    s4u2proxy_export_authdata,
-    s4u2proxy_import_authdata,
-    s4u2proxy_export_internal,
-    s4u2proxy_free_internal,
-    s4u2proxy_verify,
-    s4u2proxy_size,
-    s4u2proxy_externalize,
-    s4u2proxy_internalize,
-    s4u2proxy_copy
-};
diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports
index 9a7a16f..4c50e93 100644
--- a/src/lib/krb5/libkrb5.exports
+++ b/src/lib/krb5/libkrb5.exports
@@ -1,6 +1,5 @@
 _krb5_conf_boolean
 decode_krb5_ad_kdcissued
-decode_krb5_ad_signedpath
 decode_krb5_ap_rep
 decode_krb5_ap_rep_enc_part
 decode_krb5_ap_req
@@ -53,8 +52,6 @@ decode_krb5_ticket
 decode_krb5_typed_data
 decode_utf8_strings
 encode_krb5_ad_kdcissued
-encode_krb5_ad_signedpath_data
-encode_krb5_ad_signedpath
 encode_krb5_ap_rep
 encode_krb5_ap_rep_enc_part
 encode_krb5_ap_req
@@ -323,7 +320,6 @@ krb5_expand_hostname
 krb5_fcc_ops
 krb5_find_authdata
 krb5_free_ad_kdcissued
-krb5_free_ad_signedpath
 krb5_free_address
 krb5_free_addresses
 krb5_free_ap_rep
diff --git a/src/plugins/kdb/db2/db2_exp.c b/src/plugins/kdb/db2/db2_exp.c
index 4d905db..7cf8aa4 100644
--- a/src/plugins/kdb/db2/db2_exp.c
+++ b/src/plugins/kdb/db2/db2_exp.c
@@ -220,11 +220,16 @@ kdb_vftabl PLUGIN_SYMBOL_NAME(krb5_db2, kdb_function_table) = {
     /* put_policy */                    wrap_krb5_db2_put_policy,
     /* iter_policy */                   wrap_krb5_db2_iter_policy,
     /* delete_policy */                 wrap_krb5_db2_delete_policy,
-    /* blah blah blah */ 0,0,0,0,0,
+    /* fetch_master_key */              NULL,
+    /* fetch_master_key_list */         NULL,
+    /* store_master_key_list */         NULL,
+    /* dbe_search_enctype */            NULL,
+    /* change_pwd */                    NULL,
     /* promote_db */                    wrap_krb5_db2_promote_db,
-    0, 0, 0, 0,
+    /* decrypt_key_data */              NULL,
+    /* encrypt_key_data */              NULL,
+    /* check_transited_realms */        NULL,
     /* check_policy_as */               wrap_krb5_db2_check_policy_as,
-    0,
+    /* check_policy_tgs */              NULL,
     /* audit_as_req */                  wrap_krb5_db2_audit_as_req,
-    0, 0
 };
diff --git a/src/plugins/kdb/ldap/ldap_exp.c b/src/plugins/kdb/ldap/ldap_exp.c
index 1d7cd14..d5cd82b 100644
--- a/src/plugins/kdb/ldap/ldap_exp.c
+++ b/src/plugins/kdb/ldap/ldap_exp.c
@@ -76,7 +76,6 @@ kdb_vftabl PLUGIN_SYMBOL_NAME(krb5_ldap, kdb_function_table) = {
     /* promote_db */                        NULL,
     /* decrypt_key_data */                  NULL,
     /* encrypt_key_data */                  NULL,
-    /* sign_authdata */                     NULL,
     /* check_transited_realms */            NULL,
     /* check_policy_as */                   krb5_ldap_check_policy_as,
     /* check_policy_tgs */                  NULL,
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.c b/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.c
index 4fbf898..1f0c868 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.c
+++ b/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.c
@@ -294,7 +294,7 @@ krb5_ldap_check_allowed_to_delegate(krb5_context context,
     krb5_error_code code;
     krb5_tl_data *tlp;
 
-    code = KRB5KDC_ERR_POLICY;
+    code = KRB5KDC_ERR_BADOPTION;
 
     for (tlp = server->tl_data; tlp != NULL; tlp = tlp->tl_data_next) {
         krb5_principal acl;
@@ -305,7 +305,7 @@ krb5_ldap_check_allowed_to_delegate(krb5_context context,
         if (krb5_parse_name(context, (char *)tlp->tl_data_contents, &acl) != 0)
             continue;
 
-        if (krb5_principal_compare(context, proxy, acl)) {
+        if (proxy == NULL || krb5_principal_compare(context, proxy, acl)) {
             code = 0;
             krb5_free_principal(context, acl);
             break;
diff --git a/src/plugins/kdb/test/kdb_test.c b/src/plugins/kdb/test/kdb_test.c
index 495bec4..8e7015d 100644
--- a/src/plugins/kdb/test/kdb_test.c
+++ b/src/plugins/kdb/test/kdb_test.c
@@ -407,9 +407,8 @@ test_get_principal(krb5_context context, krb5_const_principal search_for,
         check(krb5_parse_name(context, canon, &princ));
         if (!krb5_realm_compare(context, search_for, princ)) {
             /* Out of realm */
-            if ((flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) &&
-                ((flags & KRB5_KDB_FLAG_CANONICALIZE) ||
-                 search_for->type == KRB5_NT_ENTERPRISE_PRINCIPAL)) {
+            if ((flags & KRB5_KDB_FLAG_CLIENT) &&
+                (flags & KRB5_KDB_FLAG_REFERRAL_OK)) {
                 /* Return a client referral by creating an entry with only the
                  * principal set. */
                 *entry = ealloc(sizeof(**entry));
@@ -417,7 +416,7 @@ test_get_principal(krb5_context context, krb5_const_principal search_for,
                 princ = NULL;
                 ret = 0;
                 goto cleanup;
-            } else if (flags & KRB5_KDB_FLAG_CANONICALIZE) {
+            } else if (flags & KRB5_KDB_FLAG_REFERRAL_OK) {
                 /* Generate a server referral by looking up the TGT for the
                  * canonical name's realm. */
                 tgtprinc = tgtname(context, &princ->realm, &search_for->realm);
@@ -530,7 +529,7 @@ test_get_s4u_x509_principal(krb5_context context, const krb5_data *client_cert,
 
     ret = test_get_principal(context, cert_princ, flags, entry);
     krb5_free_principal(context, cert_princ);
-    if (ret || (flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY))
+    if (ret || (flags & KRB5_KDB_FLAG_REFERRAL_OK))
         return ret;
 
     if (!krb5_realm_compare(context, princ, (*entry)->princ))
@@ -612,362 +611,12 @@ test_encrypt_key_data(krb5_context context, const krb5_keyblock *mkey,
     return 0;
 }
 
-typedef struct {
-    char *pac_princ;
-    struct {
-        char *proxy_target;
-        char *impersonator;
-    } deleg_info;
-    krb5_boolean not_delegated;
-    krb5_pac pac;
-} pac_info;
-
-static void
-free_pac_info(krb5_context context, pac_info *info)
-{
-    if (info == NULL)
-        return;
-
-    free(info->pac_princ);
-    free(info->deleg_info.proxy_target);
-    free(info->deleg_info.impersonator);
-    krb5_pac_free(context, info->pac);
-    free(info);
-}
-
-/*
- * Create a PAC object with a fake logon-info blob.  Instead of a real
- * KERB_VALIDATION_INFO structure, store a byte indicating whether the
- * USER_NOT_DELEGATED bit is set.
- */
-static krb5_error_code
-create_pac(krb5_context context, krb5_boolean not_delegated, krb5_pac *pac_out)
-{
-    krb5_data data;
-    krb5_pac pac;
-    char nd;
-
-    nd = not_delegated ? 1 : 0;
-    data = make_data(&nd, 1);
-    check(krb5_pac_init(context, &pac));
-    check(krb5_pac_add_buffer(context, pac, KRB5_PAC_LOGON_INFO, &data));
-
-    *pac_out = pac;
-    return 0;
-}
-
-/* Create a fake PAC, setting the USER_NOT_DELEGATED bit if the client DB entry
- * disallows forwardable tickets. */
-static krb5_error_code
-create_pac_db(krb5_context context, krb5_db_entry *client, krb5_pac *pac_out)
-{
-    krb5_boolean not_delegated;
-    /* Use disallow_forwardable as delegation_not_allowed attribute */
-    not_delegated = (client->attributes & KRB5_KDB_DISALLOW_FORWARDABLE);
-    return create_pac(context, not_delegated, pac_out);
-}
-
-/* Locate the PAC in tgt_authdata and set *pac_out to its PAC object
- * representation.  Set it to NULL if no PAC is present. */
-static void
-parse_ticket_pac(krb5_context context, krb5_authdata **tgt_auth_data,
-                 krb5_pac *pac_out)
-{
-    krb5_authdata **authdata;
-
-    *pac_out = NULL;
-
-    check(krb5_find_authdata(context, tgt_auth_data, NULL,
-                             KRB5_AUTHDATA_WIN2K_PAC, &authdata));
-    if (authdata == NULL)
-        return;
-    assert(authdata[1] == NULL);
-    check(krb5_pac_parse(context, authdata[0]->contents, authdata[0]->length,
-                         pac_out));
-    krb5_free_authdata(context, authdata);
-}
-
-/* Verify the KDC signature against the local TGT key.  tgt_key must be the
- * decrypted first key data entry of tgt. */
-static krb5_error_code
-verify_kdc_signature(krb5_context context, krb5_pac pac,
-                     krb5_keyblock *tgt_key, krb5_db_entry *tgt)
-{
-    krb5_error_code ret;
-    krb5_key_data *kd;
-    krb5_keyblock old_key;
-    krb5_kvno kvno;
-    int tries;
-
-    ret = krb5_pac_verify(context, pac, 0, NULL, NULL, tgt_key);
-    if (ret != KRB5KRB_AP_ERR_MODIFIED)
-        return ret;
-
-    kvno = tgt->key_data[0].key_data_kvno - 1;
-
-    /* There is no kvno in PAC signatures, so try two previous versions. */
-    for (tries = 2; tries > 0 && kvno > 0; tries--, kvno--) {
-        ret = krb5_dbe_find_enctype(context, tgt, -1, -1, kvno, &kd);
-        if (ret)
-            return KRB5KRB_AP_ERR_MODIFIED;
-        ret = krb5_dbe_decrypt_key_data(context, NULL, kd, &old_key, NULL);
-        if (ret)
-            return ret;
-        ret = krb5_pac_verify(context, pac, 0, NULL, NULL, &old_key);
-        krb5_free_keyblock_contents(context, &old_key);
-        if (!ret)
-            return 0;
-
-        /* Try the next lower kvno on the next iteration. */
-        kvno = kd->key_data_kvno - 1;
-    }
-
-    return KRB5KRB_AP_ERR_MODIFIED;
-}
-
-static krb5_error_code
-verify_ticket_pac(krb5_context context, krb5_pac pac, unsigned int flags,
-                  krb5_const_principal client_princ, krb5_boolean check_realm,
-                  krb5_keyblock *server_key, krb5_keyblock *local_tgt_key,
-                  krb5_db_entry *local_tgt, krb5_timestamp authtime)
-{
-    check(krb5_pac_verify_ext(context, pac, authtime, client_princ, server_key,
-                              NULL, check_realm));
-    if (flags & KRB5_KDB_FLAG_CROSS_REALM)
-        return 0;
-    return verify_kdc_signature(context, pac, local_tgt_key, local_tgt);
-}
-
-static void
-get_pac_info(krb5_context context, krb5_authdata **in_authdata,
-             pac_info **info_out)
-{
-    krb5_error_code ret;
-    krb5_pac pac = NULL;
-    krb5_data data;
-    char *sep = NULL;
-    pac_info *info;
-
-    *info_out = NULL;
-
-    parse_ticket_pac(context, in_authdata, &pac);
-    if (pac == NULL)
-        return;
-
-    info = ealloc(sizeof(*info));
-
-    /* Read the fake logon-info buffer from the PAC and set not_delegated
-     * according to the byte value. */
-    check(krb5_pac_get_client_info(context, pac, NULL, &info->pac_princ));
-    check(krb5_pac_get_buffer(context, pac, KRB5_PAC_LOGON_INFO, &data));
-    assert(data.length == 1);
-    info->not_delegated = *data.data;
-    krb5_free_data_contents(context, &data);
-
-    ret = krb5_pac_get_buffer(context, pac, KRB5_PAC_DELEGATION_INFO, &data);
-    if (ret && ret != ENOENT)
-        abort();
-    if (!ret) {
-        sep = memchr(data.data, ':', data.length);
-        assert(sep != NULL);
-        info->deleg_info.proxy_target = k5memdup0(data.data, sep - data.data,
-                                                  &ret);
-        check(ret);
-        info->deleg_info.impersonator = k5memdup0(sep + 1, data.length - 1 -
-                                                  (sep - data.data), &ret);
-        check(ret);
-        krb5_free_data_contents(context, &data);
-    }
-
-    info->pac = pac;
-    *info_out = info;
-}
-
-/* Add a fake delegation-info buffer to pac containing the proxy target and
- * impersonator from info. */
-static void
-add_delegation_info(krb5_context context, krb5_pac pac, pac_info *info)
-{
-    krb5_data data;
-    char *str;
-
-    if (info->deleg_info.proxy_target == NULL)
-        return;
-
-    if (asprintf(&str, "%s:%s", info->deleg_info.proxy_target,
-                 info->deleg_info.impersonator) < 0)
-        abort();
-    data = string2data(str);
-    check(krb5_pac_add_buffer(context, pac, KRB5_PAC_DELEGATION_INFO, &data));
-    free(str);
-}
-
-/* Set *out to an AD-IF-RELEVANT authdata element containing a PAC authdata
- * element with contents pac_data. */
-static void
-encode_pac_ad(krb5_context context, krb5_data *pac_data, krb5_authdata **out)
-{
-    krb5_authdata pac_ad, *list[2], **ifrel;
-
-    pac_ad.magic = KV5M_AUTHDATA;
-    pac_ad.ad_type = KRB5_AUTHDATA_WIN2K_PAC;
-    pac_ad.contents = (krb5_octet *)pac_data->data;;
-    pac_ad.length = pac_data->length;
-    list[0] = &pac_ad;
-    list[1] = NULL;
-
-    check(krb5_encode_authdata_container(context, KRB5_AUTHDATA_IF_RELEVANT,
-                                         list, &ifrel));
-    assert(ifrel[1] == NULL);
-    *out = ifrel[0];
-    free(ifrel);
-}
-
-/* Parse a PAC client-info string into a principal name.  If xrealm_s4u is
- * true, expect a realm in the string. */
-static krb5_error_code
-parse_pac_princ(krb5_context context, krb5_boolean xrealm_s4u, char *pac_princ,
-                krb5_principal *client_out)
-{
-    int n_atsigns = 0, flags = 0;
-    char *p = pac_princ;
-
-    while (*p++) {
-        if (*p == '@')
-            n_atsigns++;
-    }
-    if (xrealm_s4u) {
-        flags |= KRB5_PRINCIPAL_PARSE_REQUIRE_REALM;
-        n_atsigns--;
-    } else {
-        flags |= KRB5_PRINCIPAL_PARSE_NO_REALM;
-    }
-    assert(n_atsigns == 0 || n_atsigns == 1);
-    if (n_atsigns == 1)
-        flags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE;
-    check(krb5_parse_name_flags(context, pac_princ, flags, client_out));
-    (*client_out)->type = KRB5_NT_MS_PRINCIPAL;
-    return 0;
-}
-
-/* Set *ad_out to a fake PAC for testing, or to NULL if it doesn't make sense
- * to generate a PAC for the request. */
 static void
-generate_pac(krb5_context context, unsigned int flags,
-             krb5_const_principal client_princ,
-             krb5_const_principal server_princ, krb5_db_entry *client,
-             krb5_db_entry *header_server, krb5_db_entry *local_tgt,
-             krb5_keyblock *server_key, krb5_keyblock *header_key,
-             krb5_keyblock *local_tgt_key, krb5_timestamp authtime,
-             pac_info *info, krb5_authdata **ad_out)
-{
-    krb5_boolean sign_realm, check_realm;
-    krb5_data pac_data;
-    krb5_pac pac = NULL;
-    krb5_principal pac_princ = NULL;
-
-    *ad_out = NULL;
-
-    check_realm = ((flags & KRB5_KDB_FLAGS_S4U) &&
-                   (flags & KRB5_KDB_FLAG_CROSS_REALM));
-    sign_realm = ((flags & KRB5_KDB_FLAGS_S4U) &&
-                  (flags & KRB5_KDB_FLAG_ISSUING_REFERRAL));
-
-    if (client != NULL &&
-        ((flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) ||
-         (flags & KRB5_KDB_FLAG_PROTOCOL_TRANSITION))) {
-        /* For AS or local-realm S4U2Self, generate an initial PAC. */
-        check(create_pac_db(context, client, &pac));
-    } else if (info == NULL) {
-        /* If there is no input PAC, do not generate one. */
-        assert((flags & KRB5_KDB_FLAGS_S4U) == 0);
-        return;
-    } else {
-        if (IS_TGS_PRINC(server_princ) &&
-            info->deleg_info.proxy_target != NULL) {
-            /* RBCD transitive trust. */
-            assert(flags & KRB5_KDB_FLAG_CROSS_REALM);
-            assert(!(flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION));
-            check(parse_pac_princ(context, TRUE, info->pac_princ, &pac_princ));
-            client_princ = pac_princ;
-            check_realm = TRUE;
-            sign_realm = TRUE;
-        } else if ((flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) &&
-                   !(flags & KRB5_KDB_FLAG_CROSS_REALM)) {
-            /*
-             * Initial RBCD and old constrained delegation requests to
-             * impersonator realm; create delegation info blob.  We cannot
-             * assume that proxy_target is NULL as the evidence ticket could
-             * have been acquired via constrained delegation.
-             */
-            free(info->deleg_info.proxy_target);
-            check(krb5_unparse_name_flags(context, server_princ,
-                                          KRB5_PRINCIPAL_UNPARSE_NO_REALM,
-                                          &info->deleg_info.proxy_target));
-            /* This is supposed to be a list of impersonators, but we currently
-             * only deal with one. */
-            free(info->deleg_info.impersonator);
-            check(krb5_unparse_name(context, header_server->princ,
-                                    &info->deleg_info.impersonator));
-        } else if (flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) {
-            /* Last cross realm RBCD request to proxy realm. */
-            assert(info->deleg_info.proxy_target != NULL);
-        }
-
-        /* We have already verified the PAC in get_authdata_info, but we should
-         * be able to verify the signatures here as well. */
-        check(verify_ticket_pac(context, info->pac, flags, client_princ,
-                                check_realm, header_key, local_tgt_key,
-                                local_tgt, authtime));
-
-        /* Create a new pac as we may be altering pac principal's realm */
-        check(create_pac(context, info->not_delegated, &pac));
-        add_delegation_info(context, pac, info);
-    }
-    check(krb5_pac_sign_ext(context, pac, authtime, client_princ, server_key,
-                            local_tgt_key, sign_realm, &pac_data));
-    krb5_pac_free(context, pac);
-    krb5_free_principal(context, pac_princ);
-    encode_pac_ad(context, &pac_data, ad_out);
-    krb5_free_data_contents(context, &pac_data);
-}
-
-static krb5_error_code
-test_sign_authdata(krb5_context context, unsigned int flags,
-                   krb5_const_principal client_princ,
-                   krb5_const_principal server_princ, krb5_db_entry *client,
-                   krb5_db_entry *server, krb5_db_entry *header_server,
-                   krb5_db_entry *local_tgt, krb5_keyblock *client_key,
-                   krb5_keyblock *server_key, krb5_keyblock *header_key,
-                   krb5_keyblock *local_tgt_key, krb5_keyblock *session_key,
-                   krb5_timestamp authtime, krb5_authdata **tgt_auth_data,
-                   void *ad_info, krb5_data ***auth_indicators,
-                   krb5_authdata ***signed_auth_data)
+change_auth_indicators(krb5_context context, krb5_data ***auth_indicators)
 {
-    krb5_authdata *pac_ad = NULL, *test_ad = NULL, **list;
     krb5_data **inds, d;
     int i, val;
 
-    /* Possibly create a PAC authdata element. */
-    generate_pac(context, flags, client_princ, server_princ, client,
-                 header_server, local_tgt, server_key, header_key,
-                 local_tgt_key, authtime, ad_info, &pac_ad);
-
-    /* Always create a TEST_AD_TYPE element. */
-    test_ad = ealloc(sizeof(*test_ad));
-    test_ad->magic = KV5M_AUTHDATA;
-    test_ad->ad_type = TEST_AD_TYPE;
-    test_ad->contents = (uint8_t *)estrdup("db-authdata-test");
-    test_ad->length = strlen((char *)test_ad->contents);
-
-    /* Assemble the authdata into a one-element or two-element list.
-     * The PAC must be the first element. */
-    list = ealloc(3 * sizeof(*list));
-    list[0] = (pac_ad != NULL) ? pac_ad : test_ad;
-    list[1] = (pac_ad != NULL) ? test_ad : NULL;
-    list[2] = NULL;
-    *signed_auth_data = list;
-
     /* If we see an auth indicator "dbincrX", replace the whole indicator list
      * with "dbincr{X+1}". */
     inds = *auth_indicators;
@@ -984,6 +633,58 @@ test_sign_authdata(krb5_context context, unsigned int flags,
             break;
         }
     }
+}
+
+static krb5_error_code
+test_issue_pac(krb5_context context, unsigned int flags, krb5_db_entry *client,
+               krb5_keyblock *replaced_reply_key, krb5_db_entry *server,
+               krb5_db_entry *krb5tgt, krb5_timestamp authtime,
+               krb5_pac old_pac, krb5_pac new_pac,
+               krb5_data ***auth_indicators)
+{
+    krb5_data data = empty_data();
+    krb5_boolean found_logon_info = FALSE;
+    krb5_ui_4 *types;
+    size_t num_buffers = 0, i;
+
+    change_auth_indicators(context, auth_indicators);
+
+    if (old_pac == NULL ||
+        (client != NULL && (flags & KRB5_KDB_FLAG_PROTOCOL_TRANSITION))) {
+        /* Generating an initial PAC. */
+        assert(client != NULL);
+        data = string2data("fake");
+        check(krb5_pac_add_buffer(context, new_pac, KRB5_PAC_LOGON_INFO,
+                                  &data));
+        return 0;
+    } else {
+        /* Field copying - my favorite! */
+        if (old_pac != NULL)
+            check(krb5_pac_get_types(context, old_pac, &num_buffers, &types));
+
+        for (i = 0; i < num_buffers; i++) {
+            /* Skip buffer types handled by KDC. */
+            if (types[i] == KRB5_PAC_SERVER_CHECKSUM ||
+                types[i] == KRB5_PAC_PRIVSVR_CHECKSUM ||
+                types[i] == KRB5_PAC_TICKET_CHECKSUM ||
+                types[i] == KRB5_PAC_CLIENT_INFO ||
+                types[i] == KRB5_PAC_DELEGATION_INFO)
+                continue;
+
+            check(krb5_pac_get_buffer(context, old_pac, types[i], &data));
+
+            if (types[i] == KRB5_PAC_LOGON_INFO) {
+                found_logon_info = TRUE;
+                assert(data_eq_string(data, "fake"));
+            }
+
+            check(krb5_pac_add_buffer(context, new_pac, types[i], &data));
+            krb5_free_data_contents(context, &data);
+        }
+
+        if (old_pac != NULL)
+            assert(found_logon_info);
+    }
 
     return 0;
 }
@@ -1003,7 +704,7 @@ match_in_table(krb5_context context, const char *table, const char *sprinc,
     if (ret)
         return FALSE;
     for (v = values; *v != NULL; v++) {
-        if (strcmp(*v, tprinc) == 0) {
+        if (tprinc == NULL || strcmp(*v, tprinc) == 0) {
             found = TRUE;
             break;
         }
@@ -1018,114 +719,51 @@ test_check_allowed_to_delegate(krb5_context context,
                                const krb5_db_entry *server,
                                krb5_const_principal proxy)
 {
-    char *sprinc, *tprinc;
+    char *sprinc, *tprinc = NULL;
     krb5_boolean found = FALSE;
 
     check(krb5_unparse_name_flags(context, server->princ,
                                   KRB5_PRINCIPAL_UNPARSE_NO_REALM, &sprinc));
-    check(krb5_unparse_name_flags(context, proxy,
-                                  KRB5_PRINCIPAL_UNPARSE_NO_REALM, &tprinc));
+    if (proxy != NULL) {
+        check(krb5_unparse_name_flags(context, proxy,
+                                      KRB5_PRINCIPAL_UNPARSE_NO_REALM,
+                                      &tprinc));
+    }
     found = match_in_table(context, "delegation", sprinc, tprinc);
     krb5_free_unparsed_name(context, sprinc);
     krb5_free_unparsed_name(context, tprinc);
-    return found ? 0 : KRB5KDC_ERR_POLICY;
+    return found ? 0 : KRB5KDC_ERR_BADOPTION;
 }
 
 static krb5_error_code
 test_allowed_to_delegate_from(krb5_context context,
                               krb5_const_principal client,
                               krb5_const_principal server,
-                              void *server_ad_info, const krb5_db_entry *proxy)
+                              krb5_pac server_pac,
+                              const krb5_db_entry *proxy)
 {
-    char *sprinc, *tprinc;
-    pac_info *info = (pac_info *)server_ad_info;
+    char *proxy_princ, *server_princ, *pac_client_princ, *client_princ;
     krb5_boolean found = FALSE;
 
-    check(krb5_unparse_name(context, proxy->princ, &sprinc));
-    check(krb5_unparse_name(context, server, &tprinc));
-    assert(strncmp(info->pac_princ, tprinc, strlen(info->pac_princ)) == 0);
-    found = match_in_table(context, "rbcd", sprinc, tprinc);
-    krb5_free_unparsed_name(context, sprinc);
-    krb5_free_unparsed_name(context, tprinc);
-    return found ? 0 : KRB5KDC_ERR_POLICY;
-}
+    assert(server_pac != NULL);
 
-static krb5_error_code
-test_get_authdata_info(krb5_context context, unsigned int flags,
-                       krb5_authdata **in_authdata,
-                       krb5_const_principal client_princ,
-                       krb5_const_principal server_princ,
-                       krb5_keyblock *server_key, krb5_keyblock *krbtgt_key,
-                       krb5_db_entry *krbtgt, krb5_timestamp authtime,
-                       void **ad_info_out, krb5_principal *client_out)
-{
-    pac_info *info = NULL;
-    krb5_boolean rbcd_transitive, xrealm_s4u;
-    krb5_principal pac_princ = NULL;
-    char *proxy_name = NULL, *impersonator_name = NULL;
-
-    get_pac_info(context, in_authdata, &info);
-    if (info == NULL)
-        return 0;
+    check(krb5_unparse_name(context, proxy->princ, &proxy_princ));
+    check(krb5_unparse_name(context, server, &server_princ));
+    check(krb5_unparse_name(context, client, &client_princ));
 
-    /* Transitive RBCD requests are not flagged as constrained delegation */
-    if (info->not_delegated &&
-        (info->deleg_info.proxy_target ||
-         (flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION))) {
-        free_pac_info(context, info);
-        return KRB5KDC_ERR_BADOPTION;
-    }
-
-    rbcd_transitive = IS_TGS_PRINC(server_princ) &&
-        (flags & KRB5_KDB_FLAG_CROSS_REALM) && info->deleg_info.proxy_target &&
-        !(flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION);
-
-    xrealm_s4u = rbcd_transitive || ((flags & KRB5_KDB_FLAG_CROSS_REALM) &&
-                                     (flags & KRB5_KDB_FLAGS_S4U));
-
-    check(parse_pac_princ(context, xrealm_s4u, info->pac_princ, &pac_princ));
-
-    /* Cross-realm and transitive trust RBCD requests */
-    if (rbcd_transitive || ((flags & KRB5_KDB_FLAG_CROSS_REALM) &&
-                            (flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION))) {
-        assert(info->deleg_info.proxy_target != NULL);
-        assert(info->deleg_info.impersonator != NULL);
-        /* We must be able to find the impersonator in the delegation info. */
-        assert(!krb5_principal_compare(context, client_princ, pac_princ));
-        check(krb5_unparse_name(context, client_princ, &impersonator_name));
-        assert(strcmp(info->deleg_info.impersonator, impersonator_name) == 0);
-        krb5_free_unparsed_name(context, impersonator_name);
-        client_princ = pac_princ;
-        /* In the non-transitive case we can match the proxy too. */
-        if (!rbcd_transitive) {
-            check(krb5_unparse_name_flags(context, server_princ,
-                                          KRB5_PRINCIPAL_UNPARSE_NO_REALM,
-                                          &proxy_name));
-            assert(info->deleg_info.proxy_target != NULL);
-            assert(strcmp(info->deleg_info.proxy_target, proxy_name) == 0);
-            krb5_free_unparsed_name(context, proxy_name);
-        }
-    }
+    check(krb5_pac_get_client_info(context, server_pac, NULL,
+                                   &pac_client_princ));
 
-    check(verify_ticket_pac(context, info->pac, flags, client_princ,
-                            xrealm_s4u, server_key, krbtgt_key, krbtgt,
-                            authtime));
+    /* Skip realm portion if not present in PAC. */
+    assert(strncmp(pac_client_princ, server_princ,
+                   strlen(pac_client_princ)) == 0);
 
-    *ad_info_out = info;
-    if (client_out != NULL)
-        *client_out = pac_princ;
-    else
-        krb5_free_principal(context, pac_princ);
-
-    return 0;
-}
-
-static void
-test_free_authdata_info(krb5_context context, void *ad_info)
-{
-    pac_info *info = (pac_info *)ad_info;
+    free(pac_client_princ);
 
-    free_pac_info(context, info);
+    found = match_in_table(context, "rbcd", proxy_princ, server_princ);
+    krb5_free_unparsed_name(context, proxy_princ);
+    krb5_free_unparsed_name(context, server_princ);
+    return found ? 0 : KRB5KDC_ERR_BADOPTION;
 }
 
 kdb_vftabl PLUGIN_SYMBOL_NAME(krb5_test, kdb_function_table) = {
@@ -1158,7 +796,6 @@ kdb_vftabl PLUGIN_SYMBOL_NAME(krb5_test, kdb_function_table) = {
     NULL, /* promote_db */
     test_decrypt_key_data,
     test_encrypt_key_data,
-    test_sign_authdata,
     NULL, /* check_transited_realms */
     NULL, /* check_policy_as */
     NULL, /* check_policy_tgs */
@@ -1168,6 +805,5 @@ kdb_vftabl PLUGIN_SYMBOL_NAME(krb5_test, kdb_function_table) = {
     NULL, /* free_principal_e_data */
     test_get_s4u_x509_principal,
     test_allowed_to_delegate_from,
-    test_get_authdata_info,
-    test_free_authdata_info
+    test_issue_pac,
 };
diff --git a/src/tests/asn.1/krb5_decode_leak.c b/src/tests/asn.1/krb5_decode_leak.c
index 77fd3ee..2a5313b 100644
--- a/src/tests/asn.1/krb5_decode_leak.c
+++ b/src/tests/asn.1/krb5_decode_leak.c
@@ -634,16 +634,6 @@ main(int argc, char **argv)
         ktest_empty_ad_kdcissued(&kdci);
     }
     /****************************************************************/
-    /* encode_krb5_ad_signedpath */
-    {
-        krb5_ad_signedpath sp, *tmp;
-        ktest_make_sample_ad_signedpath(&sp);
-        leak_test(sp, encode_krb5_ad_signedpath,
-                  decode_krb5_ad_signedpath,
-                  krb5_free_ad_signedpath);
-        ktest_empty_ad_signedpath(&sp);
-    }
-    /****************************************************************/
     /* encode_krb5_iakerb_header */
     {
         krb5_iakerb_header ih, *tmp;
diff --git a/src/tests/asn.1/krb5_decode_test.c b/src/tests/asn.1/krb5_decode_test.c
index 4fa67bf..926aa94 100644
--- a/src/tests/asn.1/krb5_decode_test.c
+++ b/src/tests/asn.1/krb5_decode_test.c
@@ -999,14 +999,6 @@ int main(argc, argv)
     }
 
     /****************************************************************/
-    /* decode_ad_signedpath */
-    {
-        setup(krb5_ad_signedpath,ktest_make_sample_ad_signedpath);
-        decode_run("ad_signedpath","","30 3E A0 03 02 01 01 A1 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34 A3 26 30 24 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61",decode_krb5_ad_signedpath,ktest_equal_ad_signedpath,krb5_free_ad_signedpath);
-        ktest_empty_ad_signedpath(&ref);
-    }
-
-    /****************************************************************/
     /* decode_iakerb_header */
     {
         setup(krb5_iakerb_header,ktest_make_sample_iakerb_header);
diff --git a/src/tests/asn.1/krb5_encode_test.c b/src/tests/asn.1/krb5_encode_test.c
index 72c0134..26c064e 100644
--- a/src/tests/asn.1/krb5_encode_test.c
+++ b/src/tests/asn.1/krb5_encode_test.c
@@ -640,23 +640,6 @@ main(argc, argv)
         ktest_empty_ad_kdcissued(&kdci);
     }
     /****************************************************************/
-    /* encode_krb5_ad_signedpath_data */
-    {
-        krb5_ad_signedpath_data spd;
-        ktest_make_sample_ad_signedpath_data(&spd);
-        encode_run(spd, "ad_signedpath_data", "",
-                   encode_krb5_ad_signedpath_data);
-        ktest_empty_ad_signedpath_data(&spd);
-    }
-    /****************************************************************/
-    /* encode_krb5_ad_signedpath */
-    {
-        krb5_ad_signedpath sp;
-        ktest_make_sample_ad_signedpath(&sp);
-        encode_run(sp, "ad_signedpath", "", encode_krb5_ad_signedpath);
-        ktest_empty_ad_signedpath(&sp);
-    }
-    /****************************************************************/
     /* encode_krb5_iakerb_header */
     {
         krb5_iakerb_header ih;
diff --git a/src/tests/asn.1/ktest.c b/src/tests/asn.1/ktest.c
index 270d5b7..d37e4fa 100644
--- a/src/tests/asn.1/ktest.c
+++ b/src/tests/asn.1/ktest.c
@@ -560,28 +560,6 @@ ktest_make_sample_ad_kdcissued(krb5_ad_kdcissued *p)
 }
 
 void
-ktest_make_sample_ad_signedpath_data(krb5_ad_signedpath_data *p)
-{
-    ktest_make_sample_principal(&p->client);
-    p->authtime = SAMPLE_TIME;
-    p->delegated = ealloc(2 * sizeof(krb5_principal));
-    ktest_make_sample_principal(&p->delegated[0]);
-    p->delegated[1] = NULL;
-    ktest_make_sample_authorization_data(&p->authorization_data);
-    ktest_make_sample_pa_data_array(&p->method_data);
-}
-
-void
-ktest_make_sample_ad_signedpath(krb5_ad_signedpath *p)
-{
-    p->enctype = 1;
-    ktest_make_sample_checksum(&p->checksum);
-    p->delegated = ealloc(2 * sizeof(krb5_principal));
-    p->delegated[1] = NULL;
-    ktest_make_sample_pa_data_array(&p->method_data);
-}
-
-void
 ktest_make_sample_iakerb_header(krb5_iakerb_header *ih)
 {
     ktest_make_sample_data(&(ih->target_realm));
@@ -1525,39 +1503,6 @@ ktest_empty_ad_kdcissued(krb5_ad_kdcissued *p)
 }
 
 void
-ktest_empty_ad_signedpath_data(krb5_ad_signedpath_data *p)
-{
-    int i;
-
-    ktest_destroy_principal(&p->client);
-    if (p->delegated != NULL) {
-        for (i = 0; p->delegated[i] != NULL; i++) {
-            krb5_principal princ = p->delegated[i];
-            ktest_destroy_principal(&princ);
-        }
-        free(p->delegated);
-    }
-    ktest_destroy_pa_data_array(&p->method_data);
-    ktest_destroy_authorization_data(&p->authorization_data);
-}
-
-void
-ktest_empty_ad_signedpath(krb5_ad_signedpath *p)
-{
-    int i;
-
-    free(p->checksum.contents);
-    if (p->delegated != NULL) {
-        for (i = 0; p->delegated[i] != NULL; i++) {
-            krb5_principal princ = p->delegated[i];
-            ktest_destroy_principal(&princ);
-        }
-        free(p->delegated);
-    }
-    ktest_destroy_pa_data_array(&p->method_data);
-}
-
-void
 ktest_empty_iakerb_header(krb5_iakerb_header *p)
 {
     krb5_free_data_contents(NULL, &p->target_realm);
diff --git a/src/tests/asn.1/ktest.h b/src/tests/asn.1/ktest.h
index d9cc90a..53180ab 100644
--- a/src/tests/asn.1/ktest.h
+++ b/src/tests/asn.1/ktest.h
@@ -85,8 +85,6 @@ void ktest_make_sample_enc_sam_response_enc_2(krb5_enc_sam_response_enc_2 *p);
 void ktest_make_sample_pa_for_user(krb5_pa_for_user *p);
 void ktest_make_sample_pa_s4u_x509_user(krb5_pa_s4u_x509_user *p);
 void ktest_make_sample_ad_kdcissued(krb5_ad_kdcissued *p);
-void ktest_make_sample_ad_signedpath_data(krb5_ad_signedpath_data *p);
-void ktest_make_sample_ad_signedpath(krb5_ad_signedpath *p);
 void ktest_make_sample_iakerb_header(krb5_iakerb_header *p);
 void ktest_make_sample_iakerb_finished(krb5_iakerb_finished *p);
 void ktest_make_sample_fast_response(krb5_fast_response *p);
@@ -179,8 +177,6 @@ void ktest_empty_enc_sam_response_enc_2(krb5_enc_sam_response_enc_2 *p);
 void ktest_empty_pa_for_user(krb5_pa_for_user *p);
 void ktest_empty_pa_s4u_x509_user(krb5_pa_s4u_x509_user *p);
 void ktest_empty_ad_kdcissued(krb5_ad_kdcissued *p);
-void ktest_empty_ad_signedpath_data(krb5_ad_signedpath_data *p);
-void ktest_empty_ad_signedpath(krb5_ad_signedpath *p);
 void ktest_empty_iakerb_header(krb5_iakerb_header *p);
 void ktest_empty_iakerb_finished(krb5_iakerb_finished *p);
 void ktest_empty_fast_response(krb5_fast_response *p);
diff --git a/src/tests/asn.1/ktest_equal.c b/src/tests/asn.1/ktest_equal.c
index f4678b6..b48a028 100644
--- a/src/tests/asn.1/ktest_equal.c
+++ b/src/tests/asn.1/ktest_equal.c
@@ -538,34 +538,6 @@ ktest_equal_ad_kdcissued(krb5_ad_kdcissued *ref, krb5_ad_kdcissued *var)
 }
 
 int
-ktest_equal_ad_signedpath_data(krb5_ad_signedpath_data *ref,
-                               krb5_ad_signedpath_data *var)
-{
-    int p = TRUE;
-    if (ref == var) return TRUE;
-    else if (ref == NULL || var == NULL) return FALSE;
-    p = p && ptr_equal(client,ktest_equal_principal_data);
-    p = p && scalar_equal(authtime);
-    p = p && ptr_equal(delegated,ktest_equal_sequence_of_principal);
-    p = p && ptr_equal(method_data,ktest_equal_sequence_of_pa_data);
-    p = p && ptr_equal(authorization_data,ktest_equal_authorization_data);
-    return p;
-}
-
-int
-ktest_equal_ad_signedpath(krb5_ad_signedpath *ref, krb5_ad_signedpath *var)
-{
-    int p = TRUE;
-    if (ref == var) return TRUE;
-    else if (ref == NULL || var == NULL) return FALSE;
-    p = p && scalar_equal(enctype);
-    p = p && struct_equal(checksum,ktest_equal_checksum);
-    p = p && ptr_equal(delegated,ktest_equal_sequence_of_principal);
-    p = p && ptr_equal(method_data,ktest_equal_sequence_of_pa_data);
-    return p;
-}
-
-int
 ktest_equal_iakerb_header(krb5_iakerb_header *ref, krb5_iakerb_header *var)
 {
     int p = TRUE;
diff --git a/src/tests/asn.1/ktest_equal.h b/src/tests/asn.1/ktest_equal.h
index 80a0d78..8c15cc0 100644
--- a/src/tests/asn.1/ktest_equal.h
+++ b/src/tests/asn.1/ktest_equal.h
@@ -118,10 +118,6 @@ int ktest_equal_pa_for_user(krb5_pa_for_user *ref, krb5_pa_for_user *var);
 int ktest_equal_pa_s4u_x509_user(krb5_pa_s4u_x509_user *ref,
                                  krb5_pa_s4u_x509_user *var);
 int ktest_equal_ad_kdcissued(krb5_ad_kdcissued *ref, krb5_ad_kdcissued *var);
-int ktest_equal_ad_signedpath_data(krb5_ad_signedpath_data *ref,
-                                   krb5_ad_signedpath_data *var);
-int ktest_equal_ad_signedpath(krb5_ad_signedpath *ref,
-                              krb5_ad_signedpath *var);
 int ktest_equal_iakerb_header(krb5_iakerb_header *ref,
                               krb5_iakerb_header *var);
 int ktest_equal_iakerb_finished(krb5_iakerb_finished *ref,
diff --git a/src/tests/asn.1/reference_encode.out b/src/tests/asn.1/reference_encode.out
index 80b18a2..faa3dba 100644
--- a/src/tests/asn.1/reference_encode.out
+++ b/src/tests/asn.1/reference_encode.out
@@ -55,8 +55,6 @@ encode_krb5_enc_sam_response_enc_2: 30 1F A0 03 02 01 58 A1 18 04 16 65 6E 63 5F
 encode_krb5_pa_for_user: 30 4B A0 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A1 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A2 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34 A3 0A 1B 08 6B 72 62 35 64 61 74 61
 encode_krb5_pa_s4u_x509_user: 30 68 A0 55 30 53 A0 06 02 04 00 CA 14 9A A1 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A2 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A3 12 04 10 70 61 5F 73 34 75 5F 78 35 30 39 5F 75 73 65 72 A4 07 03 05 00 80 00 00 00 A1 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34
 encode_krb5_ad_kdcissued: 30 65 A0 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34 A1 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A2 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A3 24 30 22 30 0F A0 03 02 01 01 A1 08 04 06 66 6F 6F 62 61 72 30 0F A0 03 02 01 01 A1 08 04 06 66 6F 6F 62 61 72
-encode_krb5_ad_signedpath_data: 30 81 C7 A0 30 30 2E A0 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A1 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A1 11 18 0F 31 39 39 34 30 36 31 30 30 36 30 33 31 37 5A A2 32 30 30 30 2E A0 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A1 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A3 26 30 24 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61 A4 24 30 22 30 0F A0 03 02 01 01 A1 08 04 06 66 6F 6F 62 61 72 30 0F A0 03 02 01 01 A1 08 04 06 66 6F 6F 62 61 72
-encode_krb5_ad_signedpath: 30 3E A0 03 02 01 01 A1 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34 A3 26 30 24 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61
 encode_krb5_iakerb_header: 30 18 A1 0A 04 08 6B 72 62 35 64 61 74 61 A2 0A 04 08 6B 72 62 35 64 61 74 61
 encode_krb5_iakerb_finished: 30 11 A1 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34
 encode_krb5_fast_response: 30 81 9F A0 26 30 24 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61 A1 13 30 11 A0 03 02 01 01 A1 0A 04 08 31 32 33 34 35 36 37 38 A2 5B 30 59 A0 11 18 0F 31 39 39 34 30 36 31 30 30 36 30 33 31 37 5A A1 05 02 03 01 E2 40 A2 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A3 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A4 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34 A3 03 02 01 2A
diff --git a/src/tests/asn.1/trval_reference.out b/src/tests/asn.1/trval_reference.out
index 432fdce..9bedad4 100644
--- a/src/tests/asn.1/trval_reference.out
+++ b/src/tests/asn.1/trval_reference.out
@@ -1251,55 +1251,6 @@ encode_krb5_ad_kdcissued:
 .  .  .  [0] [Integer] 1
 .  .  .  [1] [Octet String] "foobar"
 
-encode_krb5_ad_signedpath_data:
-
-[Sequence/Sequence Of]
-.  [0] [Sequence/Sequence Of]
-.  .  [0] [Sequence/Sequence Of]
-.  .  .  [0] [Integer] 1
-.  .  .  [1] [Sequence/Sequence Of]
-.  .  .  .  [General string] "hftsai"
-.  .  .  .  [General string] "extra"
-.  .  [1] [General string] "ATHENA.MIT.EDU"
-.  [1] [Generalized Time] "19940610060317Z"
-.  [2] [Sequence/Sequence Of]
-.  .  [Sequence/Sequence Of]
-.  .  .  [0] [Sequence/Sequence Of]
-.  .  .  .  [0] [Integer] 1
-.  .  .  .  [1] [Sequence/Sequence Of]
-.  .  .  .  .  [General string] "hftsai"
-.  .  .  .  .  [General string] "extra"
-.  .  .  [1] [General string] "ATHENA.MIT.EDU"
-.  [3] [Sequence/Sequence Of]
-.  .  [Sequence/Sequence Of]
-.  .  .  [1] [Integer] 13
-.  .  .  [2] [Octet String] "pa-data"
-.  .  [Sequence/Sequence Of]
-.  .  .  [1] [Integer] 13
-.  .  .  [2] [Octet String] "pa-data"
-.  [4] [Sequence/Sequence Of]
-.  .  [Sequence/Sequence Of]
-.  .  .  [0] [Integer] 1
-.  .  .  [1] [Octet String] "foobar"
-.  .  [Sequence/Sequence Of]
-.  .  .  [0] [Integer] 1
-.  .  .  [1] [Octet String] "foobar"
-
-encode_krb5_ad_signedpath:
-
-[Sequence/Sequence Of]
-.  [0] [Integer] 1
-.  [1] [Sequence/Sequence Of]
-.  .  [0] [Integer] 1
-.  .  [1] [Octet String] "1234"
-.  [3] [Sequence/Sequence Of]
-.  .  [Sequence/Sequence Of]
-.  .  .  [1] [Integer] 13
-.  .  .  [2] [Octet String] "pa-data"
-.  .  [Sequence/Sequence Of]
-.  .  .  [1] [Integer] 13
-.  .  .  [2] [Octet String] "pa-data"
-
 encode_krb5_iakerb_header:
 
 [Sequence/Sequence Of]
diff --git a/src/tests/gssapi/t_s4u.py b/src/tests/gssapi/t_s4u.py
index 746c07f..4a1cdb2 100755
--- a/src/tests/gssapi/t_s4u.py
+++ b/src/tests/gssapi/t_s4u.py
@@ -298,8 +298,7 @@ a_princs = {'krbtgt/A': {'keys': 'aes128-cts'},
             'sensitive': {'keys': 'aes128-cts',
                           'flags': '+disallow_forwardable'},
             'impersonator': {'keys': 'aes128-cts'},
-            'service1': {'keys': 'aes128-cts',
-                         'flags': '+ok_to_auth_as_delegate'},
+            'service1': {'keys': 'aes128-cts'},
             'rb2': {'keys': 'aes128-cts'},
             'rb': {'keys': 'aes128-cts'}}
 a_kconf = {'realms': {'$realm': {'database_module': 'test'}},
@@ -311,7 +310,6 @@ a_kconf = {'realms': {'$realm': {'database_module': 'test'}},
                                   'alias': {'rb@A': 'rb',
                                             'rb@B': '@B',
                                             'rb@C': '@B',
-                                            'rb2_alias': 'rb2',
                                             'service/rb.a': 'rb',
                                             'service/rb.b': '@B',
                                             'service/rb.c': '@B' }}}}
@@ -338,7 +336,8 @@ c_kconf = {'realms': {'$realm': {'database_module': 'test'}},
            'capaths': { 'A' : { 'C' : 'B' }},
            'dbmodules': {'test': {'db_library': 'test',
                                   'princs': c_princs,
-                                  'rbcd': {'rb@C': 'impersonator@A'},
+                                  'rbcd': {'rb@C': ['impersonator@A',
+                                                    'service1@A']},
                                   'alias': {'rb@C': 'rb',
                                             'service/rb.c': 'rb' }}}}
 
@@ -356,7 +355,7 @@ domain_realm = {'domain_realm': {'.a':'A', '.b':'B', '.c':'C'}}
 domain_conf = ra.special_env('domain_conf', False, krb5_conf=domain_realm)
 
 ra.extract_keytab('impersonator@A', ra.keytab)
-ra.kinit('impersonator@A', None, ['-F', '-k', '-t', ra.keytab])
+ra.kinit('impersonator@A', None, ['-f', '-k', '-t', ra.keytab])
 
 mark('Local-realm RBCD')
 ra.run(['./t_s4u', 'p:' + ra.user_princ, 'p:rb'])
@@ -388,13 +387,14 @@ ra.run(['./t_s4u', 'p:' + ra.user_princ, 'h:[email protected]'], env=domain_conf)
 ra.run(['./t_s4u', 'p:' + 'sensitive@A', 'h:[email protected]'], expected_code=1)
 ra.run(['./t_s4u', 'p:' + rb.user_princ, 'h:[email protected]'])
 
-mark('With both delegation types, 2nd ticket must be forwardable')
+# Although service1 has RBCD delegation privileges to rb2@A, it does
+# not have ok-to-auth-as-delegate and does have traditional delegation
+# privileges, so it cannot get a forwardable S4U2Self ticket.
+mark('RBCD forwardable blocked by forward delegation privileges')
 ra.extract_keytab('service1@A', ra.keytab)
-ra.kinit('service1@A', None, ['-F', '-k', '-t', ra.keytab])
-ra.run(['./t_s4u', 'p:' + ra.user_princ, 'p:rb2'], expected_code=1)
-ra.run(['./t_s4u', 'p:' + ra.user_princ, 'p:rb2_alias'])
 ra.kinit('service1@A', None, ['-f', '-k', '-t', ra.keytab])
-ra.run(['./t_s4u', 'p:' + ra.user_princ, 'p:rb2'])
+ra.run(['./t_s4u', 'p:' + ra.user_princ, 'e:rb2@A'], expected_code=1,
+       expected_msg='KDC can\'t fulfill requested option')
 
 ra.stop()
 rb.stop()
diff --git a/src/tests/t_authdata.py b/src/tests/t_authdata.py
index a1d7c7e..fef8a79 100644
--- a/src/tests/t_authdata.py
+++ b/src/tests/t_authdata.py
@@ -6,12 +6,12 @@ greet_path = os.path.join(buildtop, 'plugins', 'authdata', 'greet_server',
 conf = {'plugins': {'kdcauthdata': {'module': 'greet:' + greet_path}}}
 realm = K5Realm(krb5_conf=conf)
 
-# With no requested authdata, we expect to see SIGNTICKET (512) in an
+# With no requested authdata, we expect to see PAC (128) in an
 # if-relevant container and the greet authdata in a kdc-issued
 # container.
 mark('baseline authdata')
 out = realm.run(['./adata', realm.host_princ])
-if '?512: ' not in out or '^-42: Hello' not in out:
+if '?128: ' not in out or '^-42: Hello' not in out:
     fail('expected authdata not seen for basic request')
 
 # Requested authdata is copied into the ticket, with KDC-only types
@@ -29,27 +29,25 @@ mark('AD-MANDATORY-FOR-KDC')
 realm.run(['./adata', realm.host_princ, '!-1', 'mandatoryforkdc'],
           expected_code=1, expected_msg='KDC policy rejects request')
 
-# The no_auth_data_required server flag should suppress SIGNTICKET,
-# but not module or request authdata.
+# The no_auth_data_required server flag should suppress the PAC, but
+# not module or request authdata.
 mark('no_auth_data_required server flag')
 realm.run([kadminl, 'ank', '-randkey', '+no_auth_data_required', 'noauth'])
 realm.extract_keytab('noauth', realm.keytab)
 out = realm.run(['./adata', 'noauth', '-2', 'test'])
 if '^-42: Hello' not in out or ' -2: test' not in out:
     fail('expected authdata not seen for no_auth_data_required request')
-if '512: ' in out:
-    fail('SIGNTICKET authdata seen for no_auth_data_required request')
+if '128: ' in out:
+    fail('PAC authdata seen for no_auth_data_required request')
 
-# Cross-realm TGT requests should also suppress SIGNTICKET, but not
-# module or request authdata.
+# Cross-realm TGT requests should not suppress PAC or request
+# authdata.
 mark('cross-realm')
 realm.addprinc('krbtgt/XREALM')
 realm.extract_keytab('krbtgt/XREALM', realm.keytab)
 out = realm.run(['./adata', 'krbtgt/XREALM', '-3', 'test'])
-if '^-42: Hello' not in out or ' -3: test' not in out:
+if '128:' not in out or  '^-42: Hello' not in out or ' -3: test' not in out:
     fail('expected authdata not seen for cross-realm TGT request')
-if '512: ' in out:
-    fail('SIGNTICKET authdata seen in cross-realm TGT')
 
 realm.stop()
 
@@ -69,14 +67,14 @@ else:
     realm.addprinc('WELLKNOWN/ANONYMOUS')
     realm.kinit('@%s' % realm.realm, flags=['-n'])
 
-    # SIGNTICKET and module authdata should be suppressed for
-    # anonymous tickets, but not request authdata.
+    # PAC and module authdata should be suppressed for anonymous
+    # tickets, but not request authdata.
     mark('anonymous')
     out = realm.run(['./adata', realm.host_princ, '-4', 'test'])
     if ' -4: test' not in out:
         fail('expected authdata not seen for anonymous request')
-    if '512: ' in out or '-42: ' in out:
-        fail('SIGNTICKET or greet authdata seen for anonymous request')
+    if '128: ' in out or '-42: ' in out:
+        fail('PAC or greet authdata seen for anonymous request')
 
 realm.stop()
 
@@ -264,40 +262,20 @@ realm.kinit(realm.user_princ, None,
             ['-k', '-X', 'indicators=strong dbincr1', '-S', 'rservice'],
             expected_code=1)
 
-# Test that KDB module authdata is included in an AS request, by
-# default or with an explicit PAC request.
-mark('AS-REQ KDB module authdata')
-realm.kinit(realm.user_princ, None, ['-k'])
-realm.run(['./adata', realm.krbtgt_princ],
-          expected_msg='-456: db-authdata-test')
-realm.kinit(realm.user_princ, None, ['-k', '--request-pac'])
-realm.run(['./adata', realm.krbtgt_princ],
-          expected_msg='-456: db-authdata-test')
-
-# Test that KDB module authdata is suppressed in an AS request by a
-# negative PAC request.
-mark('AS-REQ KDB module authdata client supression')
+# Test that the PAC is suppressed in an AS request by a negative PAC
+# request.
+mark('AS-REQ PAC client supression')
 realm.kinit(realm.user_princ, None, ['-k', '--no-request-pac'])
 out = realm.run(['./adata', realm.krbtgt_princ])
-if '-456: db-authdata-test' in out:
-    fail('DB authdata not suppressed by --no-request-pac')
-
-# Test that KDB authdata is included in a TGS request by default.
-mark('TGS-REQ KDB authdata')
-realm.run(['./adata', 'service/1'], expected_msg='-456: db-authdata-test')
-
-# Test that KDB authdata is suppressed in a TGS request by the
-# +no_auth_data_required flag.
-mark('TGS-REQ KDB authdata service suppression')
-out = realm.run(['./adata', 'noauthdata'])
-if '-456: db-authdata-test' in out:
-    fail('DB authdata not suppressed by +no_auth_data_required')
+if '128:' in out:
+    fail('PAC not suppressed by --no-request-pac')
 
 mark('S4U2Proxy with a foreign client')
 
 a_princs = {'krbtgt/A': {'keys': 'aes128-cts'},
             'krbtgt/B': {'keys': 'aes128-cts'},
             'impersonator': {'keys': 'aes128-cts'},
+            'impersonator2': {'keys': 'aes128-cts'},
             'resource': {'keys': 'aes128-cts'}}
 a_kconf = {'realms': {'$realm': {'database_module': 'test'}},
            'dbmodules': {'test': {'db_library': 'test',
@@ -312,9 +290,9 @@ b_princs = {'krbtgt/B': {'keys': 'aes128-cts'},
 b_kconf = {'realms': {'$realm': {'database_module': 'test'}},
            'dbmodules': {'test': {'db_library': 'test',
                                   'princs': b_princs,
-                                  'rbcd': {'rb@B': 'impersonator@A'},
+                                  'rbcd': {'rb@B': 'impersonator2@A'},
                                   'alias': {'service/rb.b': 'rb',
-                                            'impersonator@A': '@A'}}}}
+                                            'impersonator2@A': '@A'}}}}
 
 ra, rb = cross_realms(2, xtgts=(),
                           args=({'realm': 'A', 'kdc_conf': a_kconf},
@@ -325,6 +303,7 @@ ra.start_kdc()
 rb.start_kdc()
 
 ra.extract_keytab('impersonator@A', ra.keytab)
+ra.extract_keytab('impersonator2@A', ra.keytab)
 rb.extract_keytab('user@B', rb.keytab)
 
 usercache = 'FILE:' + os.path.join(rb.testdir, 'usercache')
@@ -336,11 +315,11 @@ ra.run(['./s4u2proxy', usercache, 'resource@A'])
 
 mark('Cross realm S4U authdata tests')
 
-ra.kinit('impersonator@A', None, ['-k', '-t', ra.keytab])
-ra.run(['./s4u2self', rb.user_princ, 'impersonator@A', usercache, '-2',
+ra.kinit('impersonator2@A', None, ['-f', '-k', '-t', ra.keytab])
+ra.run(['./s4u2self', rb.user_princ, 'impersonator2@A', usercache, '-2',
         'cross_s4u_self_ad'])
 out = ra.run(['./adata', '-c', usercache, '-p', rb.user_princ,
-              'impersonator@A', '-2', 'cross_s4u_self_ad'])
+              'impersonator2@A', '-2', 'cross_s4u_self_ad'])
 if out.count(' -2: cross_s4u_self_ad') != 1:
     fail('expected one cross_s4u_self_ad, got: %s' % count)
 
@@ -357,9 +336,4 @@ if out.count(' -2: cross_s4u_proxy_ad') != 1:
 ra.stop()
 rb.stop()
 
-# Additional KDB module authdata behavior we don't currently test:
-# * KDB module authdata is suppressed in TGS requests if the TGT
-#   contains no authdata and the request is not cross-realm or S4U.
-# * KDB module authdata is suppressed for anonymous tickets.
-
 success('Authorization data tests')
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.