krb5 commit: Replace AD-SIGNEDPATH with minimal PACs

Greg Hudson ghudson at mit.edu
Wed Jan 12 14:38:40 EST 2022


https://github.com/krb5/krb5/commit/a441fbe329ebbd7775eb5d4ccc4a05eef370f08b
commit a441fbe329ebbd7775eb5d4ccc4a05eef370f08b
Author: Greg Hudson <ghudson at mit.edu>
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 at A': 'rb',
                                             'rb at B': '@B',
                                             'rb at 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 at C': 'impersonator at A'},
+                                  'rbcd': {'rb at C': ['impersonator at A',
+                                                    'service1 at A']},
                                   'alias': {'rb at 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 at A', ra.keytab)
-ra.kinit('impersonator at A', None, ['-F', '-k', '-t', ra.keytab])
+ra.kinit('impersonator at 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:service at rb.c'], env=domain_conf)
 ra.run(['./t_s4u', 'p:' + 'sensitive at A', 'h:service at rb.c'], expected_code=1)
 ra.run(['./t_s4u', 'p:' + rb.user_princ, 'h:service at rb.c'])
 
-mark('With both delegation types, 2nd ticket must be forwardable')
+# Although service1 has RBCD delegation privileges to rb2 at 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 at A', ra.keytab)
-ra.kinit('service1 at 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 at 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 at 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 at B': 'impersonator at A'},
+                                  'rbcd': {'rb at B': 'impersonator2 at A'},
                                   'alias': {'service/rb.b': 'rb',
-                                            'impersonator at A': '@A'}}}}
+                                            'impersonator2 at 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 at A', ra.keytab)
+ra.extract_keytab('impersonator2 at A', ra.keytab)
 rb.extract_keytab('user at B', rb.keytab)
 
 usercache = 'FILE:' + os.path.join(rb.testdir, 'usercache')
@@ -336,11 +315,11 @@ ra.run(['./s4u2proxy', usercache, 'resource at A'])
 
 mark('Cross realm S4U authdata tests')
 
-ra.kinit('impersonator at A', None, ['-k', '-t', ra.keytab])
-ra.run(['./s4u2self', rb.user_princ, 'impersonator at A', usercache, '-2',
+ra.kinit('impersonator2 at A', None, ['-f', '-k', '-t', ra.keytab])
+ra.run(['./s4u2self', rb.user_princ, 'impersonator2 at A', usercache, '-2',
         'cross_s4u_self_ad'])
 out = ra.run(['./adata', '-c', usercache, '-p', rb.user_princ,
-              'impersonator at A', '-2', 'cross_s4u_self_ad'])
+              'impersonator2 at 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')


More information about the cvs-krb5 mailing list