Subject: Re: [kitten] Checking the transited list of a kerberos ticket in a transitive cross-realm trust situation...

From: Stefan Metzmacher <[email-masked]>
Date: Tue, 24 Sep 2019 02:05:05 +0200

Hi,

resuming this old thread...
https://lists.samba.org/archive/samba-technical/2017-August/122422.html

>> Does the Kerberos library know whether whether the application is going
>> to look at PACs and SIDs or just use the client principal name?  I am
>> guessing it does not.  Thus in Samba, one might need a dedicated
>> krb5.conf configuration file that disables the transit check.  Other
>> applications should still apply transit check even if a PAC happens
>> to be present, as AFAIK it may well remain unused.
> 
> My idea was that Samba would use
> gss_set_cred_option(GSS_KRB5_CRED_NO_TRANSIT_CHECK_X) to indicate
> the the transited list should not be checked.

I implemented GSS_KRB5_CRED_NO_TRANSIT_CHECK_X for
MIT, Heimdal (both upstream and Samba) and make use of
it in Samba.

Note that I took a OID from Heimdal:
GSS_KRB5_CRED_NO_TRANSIT_CHECK_X - 1.2.752.43.13.31
So we need to push it Heimdal first in order to avoid
conflicts later.

The code for Heimdal can be found here:
https://github.com/metze-samba/krb5/tree/master-no-transit-check
(also attached as heimdal-no_transit_check-01.patches.txt
and heimdal-no_transit_check-wip-tests-01.patches.txt)
Sadly I wasn't able to create a test that was able to
trigger the desired code path and verify it works as
expected and avoid regressions. Maybe someone can
help me with that or give some useful hints.
Currently it's only tested via Samba.

The code for MIT can be found here:
https://github.com/metze-samba/krb5/tree/master-no-transit-check
(also attached as mit-krb5-no_transit_check-01.patches.txt)
It also have tests to verify it works as expected.

The work in progress for Samba can be found here:
https://gitlab.com/samba-team/samba/merge_requests/809
(also attached as samba-no_transit_check-wip-01.txt)
The key is that Samba will require a verified PAC in the
Kerberos service ticket and be sure the authorization token
is generated by a DC of the primary domain, which is all we care
about as we just trust the domain. In such a situation
we'll use GSS_KRB5_CRED_NO_TRANSIT_CHECK_X to disable
the for us useless transit check.

Is it required to get regression tests for heimdal added
in order process on this, or could be go on without them?

metze

From 75ab6bb518945865d649356c271923dbf6f7a637 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Fri, 18 Aug 2017 15:33:17 +0200
Subject: [PATCH 1/6] lib/krb5: add krb5_rd_req_in_set_verify_ap_req_flags()

In the next commits we want to be able to pass down
things like KRB5_VERIFY_AP_REQ_NO_TRANSITED_CHECK.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 lib/krb5/rd_req.c           | 18 ++++++++++++++++--
 lib/krb5/version-script.map |  1 +
 2 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/lib/krb5/rd_req.c b/lib/krb5/rd_req.c
index fbced144e72..98dd2dbf256 100644
--- a/lib/krb5/rd_req.c
+++ b/lib/krb5/rd_req.c
@@ -481,6 +481,7 @@ struct krb5_rd_req_in_ctx_data {
     krb5_keytab keytab;
     krb5_keyblock *keyblock;
     krb5_boolean check_pac;
+    krb5_flags verify_ap_req_flags;
 };
 
 struct krb5_rd_req_out_ctx_data {
@@ -536,6 +537,15 @@ krb5_rd_req_in_set_keytab(krb5_context context,
     return 0;
 }
 
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rd_req_in_set_verify_ap_req_flags(krb5_context context,
+			     krb5_rd_req_in_ctx in,
+			     krb5_flags flags)
+{
+    in->verify_ap_req_flags = flags;
+    return 0;
+}
+
 /**
  * Set if krb5_rq_red() is going to check the Windows PAC or not
  *
@@ -821,6 +831,7 @@ krb5_rd_req_ctx(krb5_context context,
     krb5_rd_req_out_ctx o = NULL;
     krb5_keytab id = NULL, keytab = NULL;
     krb5_principal service = NULL;
+    krb5_flags verify_ap_req_flags = 0;
 
     *outctx = NULL;
 
@@ -858,6 +869,9 @@ krb5_rd_req_ctx(krb5_context context,
     if (inctx && inctx->keytab)
 	id = inctx->keytab;
 
+    if (inctx)
+        verify_ap_req_flags = inctx->verify_ap_req_flags;
+
     if((*auth_context)->keyblock){
 	ret = krb5_copy_keyblock(context,
 				 (*auth_context)->keyblock,
@@ -915,7 +929,7 @@ krb5_rd_req_ctx(krb5_context context,
 				  &ap_req,
 				  server,
 				  o->keyblock,
-				  0,
+				  verify_ap_req_flags,
 				  &o->ap_req_options,
 				  &o->ticket,
 				  KRB5_KU_AP_REQ_AUTH);
@@ -963,7 +977,7 @@ krb5_rd_req_ctx(krb5_context context,
 				      &ap_req,
 				      server,
 				      &entry.keyblock,
-				      0,
+				      verify_ap_req_flags,
 				      &o->ap_req_options,
 				      &o->ticket,
 				      KRB5_KU_AP_REQ_AUTH);
diff --git a/lib/krb5/version-script.map b/lib/krb5/version-script.map
index 023f9bd6820..9b202c36edd 100644
--- a/lib/krb5/version-script.map
+++ b/lib/krb5/version-script.map
@@ -529,6 +529,7 @@ HEIMDAL_KRB5_2.0 {
 		krb5_rd_req_in_set_keyblock;
 		krb5_rd_req_in_set_keytab;
 		krb5_rd_req_in_set_pac_check;
+		krb5_rd_req_in_set_verify_ap_req_flags;
 		krb5_rd_req_out_ctx_free;
 		krb5_rd_req_out_get_ap_req_options;
 		krb5_rd_req_out_get_keyblock;
-- 
2.17.1

From ddbeb8a53fc9a8989ba9694c2f8f1b2ccce632a5 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Fri, 18 Aug 2017 15:33:17 +0200
Subject: [PATCH 2/6] lib/krb5: add KRB5_VERIFY_AP_REQ_NO_TRANSITED_CHECK

In active directory a domain member replies on (trusts) the [K]DCs
of the domain. It's the job of the [K]DCs to only generate useful
tickets as they know about the trust topology.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 lib/krb5/krb5.h   | 1 +
 lib/krb5/rd_req.c | 3 ++-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/lib/krb5/krb5.h b/lib/krb5/krb5.h
index ea32052f0a7..43fdf947322 100644
--- a/lib/krb5/krb5.h
+++ b/lib/krb5/krb5.h
@@ -442,6 +442,7 @@ typedef union {
 /* flags for krb5_verify_ap_req */
 
 #define KRB5_VERIFY_AP_REQ_IGNORE_INVALID	(1 << 0)
+#define KRB5_VERIFY_AP_REQ_NO_TRANSITED_CHECK	(1 << 1)
 
 #define KRB5_GC_CACHED			(1U << 0)
 #define KRB5_GC_USER_USER		(1U << 1)
diff --git a/lib/krb5/rd_req.c b/lib/krb5/rd_req.c
index 98dd2dbf256..a3993826145 100644
--- a/lib/krb5/rd_req.c
+++ b/lib/krb5/rd_req.c
@@ -220,7 +220,8 @@ krb5_decrypt_ticket(krb5_context context,
 	    return KRB5KRB_AP_ERR_TKT_EXPIRED;
 	}
 
-	if(!t.flags.transited_policy_checked) {
+	if(!t.flags.transited_policy_checked
+	   && !(flags & KRB5_VERIFY_AP_REQ_NO_TRANSITED_CHECK)) {
 	    ret = check_transited(context, ticket, &t);
 	    if(ret) {
 		free_EncTicketPart(&t);
-- 
2.17.1

From 8abc8be36622c172ab82abeddcd51705f1d1f4d4 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Fri, 18 Aug 2017 15:33:17 +0200
Subject: [PATCH 3/6] lib/gssapi/krb5: add GSS_KRB5_CRED_NO_TRANSIT_CHECK_X

This allows KRB5_GC_NO_TRANSIT_CHECK (on the initiator) and
KRB5_VERIFY_AP_REQ_NO_TRANSITED_CHECK (on the acceptor) to
be controlled via the gssapi layer.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 lib/gssapi/gssapi/gssapi_oid.h       |  3 +++
 lib/gssapi/krb5/accept_sec_context.c | 11 ++++++++++-
 lib/gssapi/krb5/gsskrb5_locl.h       |  1 +
 lib/gssapi/krb5/init_sec_context.c   | 12 +++++++++---
 lib/gssapi/krb5/set_cred_option.c    | 24 ++++++++++++++++++++++++
 lib/gssapi/mech/gss_oid.c            |  3 +++
 lib/gssapi/version-script.map        |  1 +
 7 files changed, 51 insertions(+), 4 deletions(-)

diff --git a/lib/gssapi/gssapi/gssapi_oid.h b/lib/gssapi/gssapi/gssapi_oid.h
index 30ffbe4adb3..1a619cfd167 100644
--- a/lib/gssapi/gssapi/gssapi_oid.h
+++ b/lib/gssapi/gssapi/gssapi_oid.h
@@ -96,6 +96,9 @@ extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_c_ntlm_force_v1_oid_desc;
 extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_krb5_cred_no_ci_flags_x_oid_desc;
 #define GSS_KRB5_CRED_NO_CI_FLAGS_X (&__gss_krb5_cred_no_ci_flags_x_oid_desc)
 
+extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_krb5_cred_no_transit_check_x_oid_desc;
+#define GSS_KRB5_CRED_NO_TRANSIT_CHECK_X (&__gss_krb5_cred_no_transit_check_x_oid_desc)
+
 extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_krb5_import_cred_x_oid_desc;
 #define GSS_KRB5_IMPORT_CRED_X (&__gss_krb5_import_cred_x_oid_desc)
 
diff --git a/lib/gssapi/krb5/accept_sec_context.c b/lib/gssapi/krb5/accept_sec_context.c
index d4680e9e8fb..42482c51f1f 100644
--- a/lib/gssapi/krb5/accept_sec_context.c
+++ b/lib/gssapi/krb5/accept_sec_context.c
@@ -412,13 +412,22 @@ gsskrb5_acceptor_start(OM_uint32 * minor_status,
 	krb5_rd_req_in_ctx in = NULL;
 	krb5_rd_req_out_ctx out = NULL;
 	krb5_principal server = NULL;
+	krb5_flags verify_ap_req_flags = 0;
 
-	if (acceptor_cred)
+	if (acceptor_cred) {
 	    server = acceptor_cred->principal;
 
+	    if (acceptor_cred->cred_flags & GSS_CF_NO_TRANSIT_CHECK) {
+		verify_ap_req_flags |= KRB5_VERIFY_AP_REQ_NO_TRANSITED_CHECK;
+	    }
+	}
+
 	kret = krb5_rd_req_in_ctx_alloc(context, &in);
 	if (kret == 0)
 	    kret = krb5_rd_req_in_set_keytab(context, in, keytab);
+	if (kret == 0)
+	    kret = krb5_rd_req_in_set_verify_ap_req_flags(context, in,
+							  verify_ap_req_flags);
 	if (kret) {
 	    if (in)
 		krb5_rd_req_in_ctx_free(context, in);
diff --git a/lib/gssapi/krb5/gsskrb5_locl.h b/lib/gssapi/krb5/gsskrb5_locl.h
index 555d095de88..072260bcf13 100644
--- a/lib/gssapi/krb5/gsskrb5_locl.h
+++ b/lib/gssapi/krb5/gsskrb5_locl.h
@@ -94,6 +94,7 @@ typedef struct {
   int cred_flags;
 #define GSS_CF_DESTROY_CRED_ON_RELEASE	1
 #define GSS_CF_NO_CI_FLAGS		2
+#define GSS_CF_NO_TRANSIT_CHECK		4
   struct krb5_keytab_data *keytab;
   time_t endtime;
   gss_cred_usage_t usage;
diff --git a/lib/gssapi/krb5/init_sec_context.c b/lib/gssapi/krb5/init_sec_context.c
index 4ef5c9c7123..0053b76381a 100644
--- a/lib/gssapi/krb5/init_sec_context.c
+++ b/lib/gssapi/krb5/init_sec_context.c
@@ -205,6 +205,7 @@ gsskrb5_get_creds(
         OM_uint32 * minor_status,
 	krb5_context context,
 	krb5_ccache ccache,
+	krb5_flags options,
 	gsskrb5_ctx ctx,
 	gss_const_name_t target_name,
 	OM_uint32 time_req,
@@ -245,7 +246,7 @@ gsskrb5_get_creds(
     this_cred.session.keytype = KEYTYPE_NULL;
 
     kret = krb5_get_credentials(context,
-				0,
+				options,
 				ccache,
 				&this_cred,
 				&ctx->kcred);
@@ -393,6 +394,7 @@ init_auth
     krb5_error_code kret;
     krb5_data fwd_data;
     OM_uint32 lifetime_rec;
+    krb5_flags options = 0;
 
     krb5_data_zero(&fwd_data);
 
@@ -409,8 +411,12 @@ init_auth
 	    goto failure;
 	}
 	ctx->more_flags |= CLOSE_CCACHE;
-    } else
+    } else {
 	ctx->ccache = cred->ccache;
+	if (cred->cred_flags & GSS_CF_NO_TRANSIT_CHECK) {
+	    options |= KRB5_GC_NO_TRANSIT_CHECK;
+	}
+    }
 
     kret = krb5_cc_get_principal (context, ctx->ccache, &ctx->source);
     if (kret) {
@@ -428,7 +434,7 @@ init_auth
 	krb5_set_default_in_tkt_etypes(context, cred->enctypes);
 
     ret = gsskrb5_get_creds(minor_status, context, ctx->ccache,
-			    ctx, name, time_req, time_rec);
+			    options, ctx, name, time_req, time_rec);
     if (ret)
 	goto failure;
 
diff --git a/lib/gssapi/krb5/set_cred_option.c b/lib/gssapi/krb5/set_cred_option.c
index 1411f866563..e59568c9bd0 100644
--- a/lib/gssapi/krb5/set_cred_option.c
+++ b/lib/gssapi/krb5/set_cred_option.c
@@ -217,6 +217,26 @@ no_ci_flags(OM_uint32 *minor_status,
 }

+static OM_uint32
+no_transit_check(OM_uint32 *minor_status,
+		 krb5_context context,
+		 gss_cred_id_t *cred_handle,
+		 const gss_buffer_t value)
+{
+    gsskrb5_cred cred;
+
+    if (cred_handle == NULL || *cred_handle == GSS_C_NO_CREDENTIAL) {
+	*minor_status = 0;
+	return GSS_S_FAILURE;
+    }
+
+    cred = (gsskrb5_cred)*cred_handle;
+    cred->cred_flags |= GSS_CF_NO_TRANSIT_CHECK;
+
+    *minor_status = 0;
+    return GSS_S_COMPLETE;
+}
+
 OM_uint32 GSSAPI_CALLCONV
 _gsskrb5_set_cred_option
            (OM_uint32 *minor_status,
@@ -244,6 +264,10 @@ _gsskrb5_set_cred_option
     }

+    if (gss_oid_equal(desired_object, GSS_KRB5_CRED_NO_TRANSIT_CHECK_X)) {
+	return no_transit_check(minor_status, context, cred_handle, value);
+    }
+
     *minor_status = EINVAL;
     return GSS_S_FAILURE;
 }
diff --git a/lib/gssapi/mech/gss_oid.c b/lib/gssapi/mech/gss_oid.c
index 5b9bd362360..e8973d6fdd3 100644
--- a/lib/gssapi/mech/gss_oid.c
+++ b/lib/gssapi/mech/gss_oid.c
@@ -94,6 +94,9 @@ gss_OID_desc GSSAPI_LIB_VARIABLE __gss_krb5_cred_no_ci_flags_x_oid_desc = { 6, r
 /* GSS_KRB5_IMPORT_CRED_X - 1.2.752.43.13.30 */
 gss_OID_desc GSSAPI_LIB_VARIABLE __gss_krb5_import_cred_x_oid_desc = { 6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x1e") };
 
+/* GSS_KRB5_CRED_NO_TRANSIT_CHECK_X - 1.2.752.43.13.31 */
+gss_OID_desc GSSAPI_LIB_VARIABLE __gss_krb5_cred_no_transit_check_x_oid_desc = { 6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x1f") };
+
 /* GSS_C_MA_SASL_MECH_NAME - 1.2.752.43.13.100 */
 gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_ma_sasl_mech_name_oid_desc = { 6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x64") };
 
diff --git a/lib/gssapi/version-script.map b/lib/gssapi/version-script.map
index 3265970344f..ce85b402dc8 100644
--- a/lib/gssapi/version-script.map
+++ b/lib/gssapi/version-script.map
@@ -161,6 +161,7 @@ HEIMDAL_GSS_2.0 {
 		__gss_c_ntlm_session_key_oid_desc;
 		__gss_c_ntlm_force_v1_oid_desc;
 		__gss_krb5_cred_no_ci_flags_x_oid_desc;
+		__gss_krb5_cred_no_transit_check_x_oid_desc;
 		__gss_krb5_import_cred_x_oid_desc;
 		__gss_c_ma_sasl_mech_name_oid_desc;
 		__gss_c_ma_mech_name_oid_desc;
-- 
2.17.1

From 83fd10a8e980a7ffa9299ac880ba992589f45041 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Sat, 20 Jul 2019 10:15:04 +0000
Subject: [PATCH 4/6] lib/krb5: let krb5_rd_req_ctx() fallback only on
 KRB5KRB_AP_ERR_BAD_INTEGRITY

This avoids hidding a real error like KRB5KRB_AP_ERR_ILL_CR_TKT.

We only want to retry with the next key if the decryption
failed.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=14125

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 lib/krb5/rd_req.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/lib/krb5/rd_req.c b/lib/krb5/rd_req.c
index a3993826145..dd0352791cb 100644
--- a/lib/krb5/rd_req.c
+++ b/lib/krb5/rd_req.c
@@ -982,10 +982,15 @@ krb5_rd_req_ctx(krb5_context context,
 				      &o->ap_req_options,
 				      &o->ticket,
 				      KRB5_KU_AP_REQ_AUTH);
-	    if (ret) {
+	    if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
+		/* failed to decrypt, try the next key */
 		krb5_kt_free_entry (context, &entry);
 		continue;
 	    }
+	    if (ret) {
+		krb5_kt_free_entry (context, &entry);
+		break;
+	    }
 
 	    /*
 	     * Found a match, save the keyblock for PAC processing,
-- 
2.17.1

From 4a7ad28148b53791eb3660879166d4a302481b0f Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Sat, 20 Jul 2019 10:15:04 +0000
Subject: [PATCH 5/6] lib/krb5: add krb5_rd_req_in_set_iterate_keytab()

A caller might not know the kvno maintained by the KDC.
And most often there's need to know it.

So this function makes it possible to force the keytab
iteration in order to get a consistent behavior.
Otherwise it's possible to get a different behavior
if the guessed kvno in the keytab accidentally matches
the kvno of the ticket and we'll give up if the
key is not able to decrypt the ticket.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=14125

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 lib/krb5/rd_req.c           | 27 +++++++++++++++++++++++++++
 lib/krb5/version-script.map |  1 +
 2 files changed, 28 insertions(+)

diff --git a/lib/krb5/rd_req.c b/lib/krb5/rd_req.c
index dd0352791cb..5426dd70040 100644
--- a/lib/krb5/rd_req.c
+++ b/lib/krb5/rd_req.c
@@ -480,6 +480,7 @@ krb5_verify_ap_req2(krb5_context context,
 
 struct krb5_rd_req_in_ctx_data {
     krb5_keytab keytab;
+    krb5_boolean iterate_keytab;
     krb5_keyblock *keyblock;
     krb5_boolean check_pac;
     krb5_flags verify_ap_req_flags;
@@ -547,6 +548,27 @@ krb5_rd_req_in_set_verify_ap_req_flags(krb5_context context,
     return 0;
 }
 
+/**
+ * Set if krb5_rq_red() is going to iterate the keytab to find a key
+ *
+ * @param context Keberos 5 context.
+ * @param in krb5_rd_req_in_ctx to check the option on.
+ * @param flag flag to select if the keytab should be iterated (TRUE) or not (FALSE).
+ *
+ * @return Kerberos 5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_auth
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rd_req_in_set_iterate_keytab(krb5_context context,
+				  krb5_rd_req_in_ctx in,
+				  krb5_boolean flag)
+{
+    in->iterate_keytab = flag;
+    return 0;
+}
+
 /**
  * Set if krb5_rq_red() is going to check the Windows PAC or not
  *
@@ -885,6 +907,11 @@ krb5_rd_req_ctx(krb5_context context,
 				 &o->keyblock);
 	if (ret)
 	    goto out;
+    } else if (id && inctx && inctx->iterate_keytab) {
+	    /*
+	     * Force iterating over the keytab.
+	     */
+	    o->keyblock = NULL;
     } else {
 
 	if(id == NULL) {
diff --git a/lib/krb5/version-script.map b/lib/krb5/version-script.map
index 9b202c36edd..5d2db0c3589 100644
--- a/lib/krb5/version-script.map
+++ b/lib/krb5/version-script.map
@@ -526,6 +526,7 @@ HEIMDAL_KRB5_2.0 {
 		krb5_rd_req_ctx;
 		krb5_rd_req_in_ctx_alloc;
 		krb5_rd_req_in_ctx_free;
+		krb5_rd_req_in_set_iterate_keytab;
 		krb5_rd_req_in_set_keyblock;
 		krb5_rd_req_in_set_keytab;
 		krb5_rd_req_in_set_pac_check;
-- 
2.17.1

From ea5ab863ab5121fa4628b71c8422c8b8600eb798 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Thu, 22 Aug 2019 10:30:01 +0200
Subject: [PATCH 6/6] lib/gssapi/krb5: add
 GSS_KRB5_CRED_ITERATE_ACCEPTOR_KEYTAB_X

This allows krb5_rd_req_in_set_iterate_keytab() to be used via the
gssapi layer.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=14125

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 lib/gssapi/gssapi/gssapi_oid.h       |  3 +++
 lib/gssapi/krb5/accept_sec_context.c |  7 +++++++
 lib/gssapi/krb5/gsskrb5_locl.h       |  1 +
 lib/gssapi/krb5/set_cred_option.c    | 22 ++++++++++++++++++++++
 lib/gssapi/mech/gss_oid.c            |  3 +++
 lib/gssapi/version-script.map        |  1 +
 6 files changed, 37 insertions(+)

diff --git a/lib/gssapi/gssapi/gssapi_oid.h b/lib/gssapi/gssapi/gssapi_oid.h
index 1a619cfd167..ecfcd8e5520 100644
--- a/lib/gssapi/gssapi/gssapi_oid.h
+++ b/lib/gssapi/gssapi/gssapi_oid.h
@@ -99,6 +99,9 @@ extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_krb5_cred_no_ci_flags_x_oid_desc;
 extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_krb5_cred_no_transit_check_x_oid_desc;
 #define GSS_KRB5_CRED_NO_TRANSIT_CHECK_X (&__gss_krb5_cred_no_transit_check_x_oid_desc)
 
+extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_krb5_cred_iterate_acceptor_keytab_x_oid_desc;
+#define GSS_KRB5_CRED_ITERATE_ACCEPTOR_KEYTAB_X (&__gss_krb5_cred_iterate_acceptor_keytab_x_oid_desc)
+
 extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_krb5_import_cred_x_oid_desc;
 #define GSS_KRB5_IMPORT_CRED_X (&__gss_krb5_import_cred_x_oid_desc)
 
diff --git a/lib/gssapi/krb5/accept_sec_context.c b/lib/gssapi/krb5/accept_sec_context.c
index 42482c51f1f..37786934850 100644
--- a/lib/gssapi/krb5/accept_sec_context.c
+++ b/lib/gssapi/krb5/accept_sec_context.c
@@ -412,11 +412,16 @@ gsskrb5_acceptor_start(OM_uint32 * minor_status,
 	krb5_rd_req_in_ctx in = NULL;
 	krb5_rd_req_out_ctx out = NULL;
 	krb5_principal server = NULL;
+	krb5_boolean iterate_keytab = FALSE;
 	krb5_flags verify_ap_req_flags = 0;
 
 	if (acceptor_cred) {
 	    server = acceptor_cred->principal;
 
+	    if (acceptor_cred->cred_flags & GSS_CF_ITERATE_ACCEPTOR_KEYTAB) {
+		iterate_keytab = TRUE;
+	    }
+
 	    if (acceptor_cred->cred_flags & GSS_CF_NO_TRANSIT_CHECK) {
 		verify_ap_req_flags |= KRB5_VERIFY_AP_REQ_NO_TRANSITED_CHECK;
 	    }
@@ -425,6 +430,8 @@ gsskrb5_acceptor_start(OM_uint32 * minor_status,
 	kret = krb5_rd_req_in_ctx_alloc(context, &in);
 	if (kret == 0)
 	    kret = krb5_rd_req_in_set_keytab(context, in, keytab);
+	if (kret == 0 && iterate_keytab)
+	    kret = krb5_rd_req_in_set_iterate_keytab(context, in, TRUE);
 	if (kret == 0)
 	    kret = krb5_rd_req_in_set_verify_ap_req_flags(context, in,
 							  verify_ap_req_flags);
diff --git a/lib/gssapi/krb5/gsskrb5_locl.h b/lib/gssapi/krb5/gsskrb5_locl.h
index 072260bcf13..92501fc4900 100644
--- a/lib/gssapi/krb5/gsskrb5_locl.h
+++ b/lib/gssapi/krb5/gsskrb5_locl.h
@@ -95,6 +95,7 @@ typedef struct {
 #define GSS_CF_DESTROY_CRED_ON_RELEASE	1
 #define GSS_CF_NO_CI_FLAGS		2
 #define GSS_CF_NO_TRANSIT_CHECK		4
+#define GSS_CF_ITERATE_ACCEPTOR_KEYTAB	8
   struct krb5_keytab_data *keytab;
   time_t endtime;
   gss_cred_usage_t usage;
diff --git a/lib/gssapi/krb5/set_cred_option.c b/lib/gssapi/krb5/set_cred_option.c
index e59568c9bd0..9aec0203671 100644
--- a/lib/gssapi/krb5/set_cred_option.c
+++ b/lib/gssapi/krb5/set_cred_option.c
@@ -216,6 +216,25 @@ no_ci_flags(OM_uint32 *minor_status,
 
 }
 
+static OM_uint32
+iterate_acceptor_keytab(OM_uint32 *minor_status,
+			krb5_context context,
+			gss_cred_id_t *cred_handle,
+			const gss_buffer_t value)
+{
+    gsskrb5_cred cred;
+
+    if (cred_handle == NULL || *cred_handle == GSS_C_NO_CREDENTIAL) {
+	*minor_status = 0;
+	return GSS_S_FAILURE;
+    }
+
+    cred = (gsskrb5_cred)*cred_handle;
+    cred->cred_flags |= GSS_CF_ITERATE_ACCEPTOR_KEYTAB;
+
+    *minor_status = 0;
+    return GSS_S_COMPLETE;
+}
 
 static OM_uint32
 no_transit_check(OM_uint32 *minor_status,
@@ -263,6 +282,9 @@ _gsskrb5_set_cred_option
 	return no_ci_flags(minor_status, context, cred_handle, value);
     }
 
+    if (gss_oid_equal(desired_object, GSS_KRB5_CRED_ITERATE_ACCEPTOR_KEYTAB_X)) {
+	return iterate_acceptor_keytab(minor_status, context, cred_handle, value);
+    }
 
     if (gss_oid_equal(desired_object, GSS_KRB5_CRED_NO_TRANSIT_CHECK_X)) {
 	return no_transit_check(minor_status, context, cred_handle, value);
diff --git a/lib/gssapi/mech/gss_oid.c b/lib/gssapi/mech/gss_oid.c
index e8973d6fdd3..4bd59b6d974 100644
--- a/lib/gssapi/mech/gss_oid.c
+++ b/lib/gssapi/mech/gss_oid.c
@@ -97,6 +97,9 @@ gss_OID_desc GSSAPI_LIB_VARIABLE __gss_krb5_import_cred_x_oid_desc = { 6, rk_UNC
 /* GSS_KRB5_CRED_NO_TRANSIT_CHECK_X - 1.2.752.43.13.31 */
 gss_OID_desc GSSAPI_LIB_VARIABLE __gss_krb5_cred_no_transit_check_x_oid_desc = { 6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x1f") };
 
+/* GSS_KRB5_CRED_ITERATE_ACCEPTOR_KEYTAB_X - 1.2.752.43.13.32 */
+gss_OID_desc GSSAPI_LIB_VARIABLE __gss_krb5_cred_iterate_acceptor_keytab_x_oid_desc = { 6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x20") };
+
 /* GSS_C_MA_SASL_MECH_NAME - 1.2.752.43.13.100 */
 gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_ma_sasl_mech_name_oid_desc = { 6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x64") };
 
diff --git a/lib/gssapi/version-script.map b/lib/gssapi/version-script.map
index ce85b402dc8..c7cda5adcb1 100644
--- a/lib/gssapi/version-script.map
+++ b/lib/gssapi/version-script.map
@@ -160,6 +160,7 @@ HEIMDAL_GSS_2.0 {
 		__gss_c_ntlm_v2_oid_desc;
 		__gss_c_ntlm_session_key_oid_desc;
 		__gss_c_ntlm_force_v1_oid_desc;
+		__gss_krb5_cred_iterate_acceptor_keytab_x_oid_desc;
 		__gss_krb5_cred_no_ci_flags_x_oid_desc;
 		__gss_krb5_cred_no_transit_check_x_oid_desc;
 		__gss_krb5_import_cred_x_oid_desc;
-- 
2.17.1

From cede7ccb322d9975010cf65aa01bd54bc08004ed Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Thu, 29 Aug 2019 15:38:11 +0200
Subject: [PATCH 1/7] TODO: lib/gssapi: maintain a global acceptor_cred in
 test_context.c

---
 lib/gssapi/test_context.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/lib/gssapi/test_context.c b/lib/gssapi/test_context.c
index 7cbe73b8b42..fcebb7e7c57 100644
--- a/lib/gssapi/test_context.c
+++ b/lib/gssapi/test_context.c
@@ -142,6 +142,8 @@ oid_to_string(const gss_OID oid)
     return "unknown oid";
 }
 
+static gss_cred_id_t acceptor_cred = GSS_C_NO_CREDENTIAL;
+
 static void
 loop(gss_OID mechoid,
      gss_OID nameoid, const char *target,
@@ -224,7 +226,7 @@ loop(gss_OID mechoid,
 
 	maj_stat = gss_accept_sec_context(&min_stat,
 					  sctx,
-					  GSS_C_NO_CREDENTIAL,
+					  acceptor_cred,
 					  &output_token,
 					  GSS_C_NO_CHANNEL_BINDINGS,
 					  NULL,
-- 
2.17.1

From 1eab887cdf2e64405861c37b16e1fb22b34fa763 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Thu, 29 Aug 2019 15:38:11 +0200
Subject: [PATCH 2/7] TODO: lib/gssapi: make use of gss_acquire_cred_from() in
 test_context.c

---
 lib/gssapi/test_context.c | 28 +++++++++++++++++++++++-----
 1 file changed, 23 insertions(+), 5 deletions(-)

diff --git a/lib/gssapi/test_context.c b/lib/gssapi/test_context.c
index fcebb7e7c57..ab67972fc25 100644
--- a/lib/gssapi/test_context.c
+++ b/lib/gssapi/test_context.c
@@ -619,6 +619,8 @@ main(int argc, char **argv)
     gss_OID_set mechoids = GSS_C_NO_OID_SET;
     gss_key_value_element_desc client_cred_elements[2];
     gss_key_value_set_desc client_cred_store;
+    gss_key_value_element_desc acceptor_cred_elements[1];
+    gss_key_value_set_desc acceptor_cred_store;
 
     setprogname(argv[0]);
 
@@ -694,14 +696,30 @@ main(int argc, char **argv)
                        oids, sizeof(oids)/sizeof(oids[0]), mechs_string);
     }
 
+    acceptor_cred_store.count = 0;
+    acceptor_cred_store.elements = acceptor_cred_elements;
+
     if (gsskrb5_acceptor_identity) {
-	/* XXX replace this with cred store, but test suites will need work */
-	maj_stat = gsskrb5_register_acceptor_identity(gsskrb5_acceptor_identity);
-	if (maj_stat)
-	    errx(1, "gsskrb5_acceptor_identity: %s",
-		 gssapi_err(maj_stat, 0, GSS_C_NO_OID));
+	acceptor_cred_store.elements[acceptor_cred_store.count].key = "keytab";
+	acceptor_cred_store.elements[acceptor_cred_store.count].value = gsskrb5_acceptor_identity;
+
+	acceptor_cred_store.count++;
     }
 
+    maj_stat = gss_acquire_cred_from(&min_stat,
+				     NULL,
+				     GSS_C_INDEFINITE,
+				     mechoids,
+				     GSS_C_INITIATE,
+				     acceptor_cred_store.count ? &acceptor_cred_store
+							 : GSS_C_NO_CRED_STORE,
+				     &acceptor_cred,
+				     NULL,
+				     NULL);
+    if (GSS_ERROR(maj_stat))
+	errx(1, "gss_acquire_cred(acceptor): %s",
+	     gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
+
     if (client_password && (client_ccache || client_keytab)) {
 	errx(1, "password option mutually exclusive with ccache or keytab option");
     }
-- 
2.17.1

From 4ea33e7236044672295ac275fb985d5a81f0945b Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Thu, 29 Aug 2019 15:38:11 +0200
Subject: [PATCH 3/7] TODO: lib/gssapi: add --acceptor-no-transit-check option
 to test_context.c

---
 lib/gssapi/test_context.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/lib/gssapi/test_context.c b/lib/gssapi/test_context.c
index ab67972fc25..8935e273798 100644
--- a/lib/gssapi/test_context.c
+++ b/lib/gssapi/test_context.c
@@ -60,6 +60,7 @@ static int ei_flag = 0;
 static char *client_ccache = NULL;
 static char *client_keytab = NULL;
 static char *gsskrb5_acceptor_identity = NULL;
+static int acceptor_no_transit_check = 0;
 static char *session_enctype_string = NULL;
 static int client_time_offset = 0;
 static int server_time_offset = 0;
@@ -586,6 +587,8 @@ static struct getargs args[] = {
      "server should get a credential", NULL },
     {"export-import-cred",0,	arg_flag,	&ei_flag, "test export/import cred", NULL },
     {"gsskrb5-acceptor-identity", 0, arg_string, &gsskrb5_acceptor_identity, "keytab", NULL },
+    {"acceptor-no-transit-check", 0, arg_flag, &acceptor_no_transit_check,
+     "skip transited checks", NULL },
     {"session-enctype",	0, arg_string,	&session_enctype_string, "enctype", NULL },
     {"client-time-offset",	0, arg_integer,	&client_time_offset, "time", NULL },
     {"server-time-offset",	0, arg_integer,	&server_time_offset, "time", NULL },
@@ -720,6 +723,18 @@ main(int argc, char **argv)
 	errx(1, "gss_acquire_cred(acceptor): %s",
 	     gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
 
+    if (acceptor_no_transit_check) {
+	gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
+
+	maj_stat = gss_set_cred_option(&min_stat,
+				       &acceptor_cred,
+				       (gss_OID)GSS_KRB5_CRED_NO_TRANSIT_CHECK_X,
+				       &empty_buffer);
+	if (GSS_ERROR(maj_stat))
+	    errx(1, "gss_set_cred_option(GSS_KRB5_CRED_NO_TRANSIT_CHECK_X): %s",
+		 gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
+    }
+
     if (client_password && (client_ccache || client_keytab)) {
 	errx(1, "password option mutually exclusive with ccache or keytab option");
     }
-- 
2.17.1

From e85f618b879dad5c4a4bd0c95e93798ede77cd61 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Tue, 24 Sep 2019 01:33:05 +0200
Subject: [PATCH 4/7] Revert "TODO: lib/gssapi: add --acceptor-no-transit-check
 option to test_context.c"

This reverts commit 4ea33e7236044672295ac275fb985d5a81f0945b.
---
 lib/gssapi/test_context.c | 15 ---------------
 1 file changed, 15 deletions(-)

diff --git a/lib/gssapi/test_context.c b/lib/gssapi/test_context.c
index 8935e273798..ab67972fc25 100644
--- a/lib/gssapi/test_context.c
+++ b/lib/gssapi/test_context.c
@@ -60,7 +60,6 @@ static int ei_flag = 0;
 static char *client_ccache = NULL;
 static char *client_keytab = NULL;
 static char *gsskrb5_acceptor_identity = NULL;
-static int acceptor_no_transit_check = 0;
 static char *session_enctype_string = NULL;
 static int client_time_offset = 0;
 static int server_time_offset = 0;
@@ -587,8 +586,6 @@ static struct getargs args[] = {
      "server should get a credential", NULL },
     {"export-import-cred",0,	arg_flag,	&ei_flag, "test export/import cred", NULL },
     {"gsskrb5-acceptor-identity", 0, arg_string, &gsskrb5_acceptor_identity, "keytab", NULL },
-    {"acceptor-no-transit-check", 0, arg_flag, &acceptor_no_transit_check,
-     "skip transited checks", NULL },
     {"session-enctype",	0, arg_string,	&session_enctype_string, "enctype", NULL },
     {"client-time-offset",	0, arg_integer,	&client_time_offset, "time", NULL },
     {"server-time-offset",	0, arg_integer,	&server_time_offset, "time", NULL },
@@ -723,18 +720,6 @@ main(int argc, char **argv)
 	errx(1, "gss_acquire_cred(acceptor): %s",
 	     gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
 
-    if (acceptor_no_transit_check) {
-	gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
-
-	maj_stat = gss_set_cred_option(&min_stat,
-				       &acceptor_cred,
-				       (gss_OID)GSS_KRB5_CRED_NO_TRANSIT_CHECK_X,
-				       &empty_buffer);
-	if (GSS_ERROR(maj_stat))
-	    errx(1, "gss_set_cred_option(GSS_KRB5_CRED_NO_TRANSIT_CHECK_X): %s",
-		 gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
-    }
-
     if (client_password && (client_ccache || client_keytab)) {
 	errx(1, "password option mutually exclusive with ccache or keytab option");
     }
-- 
2.17.1

From 835159d405bc62085fef98614af5de8c4bc5c1f5 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Tue, 24 Sep 2019 01:33:09 +0200
Subject: [PATCH 5/7] Revert "TODO: lib/gssapi: make use of
 gss_acquire_cred_from() in test_context.c"

This reverts commit 1eab887cdf2e64405861c37b16e1fb22b34fa763.
---
 lib/gssapi/test_context.c | 28 +++++-----------------------
 1 file changed, 5 insertions(+), 23 deletions(-)

diff --git a/lib/gssapi/test_context.c b/lib/gssapi/test_context.c
index ab67972fc25..fcebb7e7c57 100644
--- a/lib/gssapi/test_context.c
+++ b/lib/gssapi/test_context.c
@@ -619,8 +619,6 @@ main(int argc, char **argv)
     gss_OID_set mechoids = GSS_C_NO_OID_SET;
     gss_key_value_element_desc client_cred_elements[2];
     gss_key_value_set_desc client_cred_store;
-    gss_key_value_element_desc acceptor_cred_elements[1];
-    gss_key_value_set_desc acceptor_cred_store;
 
     setprogname(argv[0]);
 
@@ -696,30 +694,14 @@ main(int argc, char **argv)
                        oids, sizeof(oids)/sizeof(oids[0]), mechs_string);
     }
 
-    acceptor_cred_store.count = 0;
-    acceptor_cred_store.elements = acceptor_cred_elements;
-
     if (gsskrb5_acceptor_identity) {
-	acceptor_cred_store.elements[acceptor_cred_store.count].key = "keytab";
-	acceptor_cred_store.elements[acceptor_cred_store.count].value = gsskrb5_acceptor_identity;
-
-	acceptor_cred_store.count++;
+	/* XXX replace this with cred store, but test suites will need work */
+	maj_stat = gsskrb5_register_acceptor_identity(gsskrb5_acceptor_identity);
+	if (maj_stat)
+	    errx(1, "gsskrb5_acceptor_identity: %s",
+		 gssapi_err(maj_stat, 0, GSS_C_NO_OID));
     }
 
-    maj_stat = gss_acquire_cred_from(&min_stat,
-				     NULL,
-				     GSS_C_INDEFINITE,
-				     mechoids,
-				     GSS_C_INITIATE,
-				     acceptor_cred_store.count ? &acceptor_cred_store
-							 : GSS_C_NO_CRED_STORE,
-				     &acceptor_cred,
-				     NULL,
-				     NULL);
-    if (GSS_ERROR(maj_stat))
-	errx(1, "gss_acquire_cred(acceptor): %s",
-	     gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
-
     if (client_password && (client_ccache || client_keytab)) {
 	errx(1, "password option mutually exclusive with ccache or keytab option");
     }
-- 
2.17.1

From 67331e368e5b7a2ddde709a2ea7b5ad98e70d2c5 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Fri, 30 Aug 2019 19:07:35 +0200
Subject: [PATCH 6/7] DOES NOT WORK

---
 kdc/krb5tgs.c                        |   2 +-
 lib/gssapi/krb5/accept_sec_context.c |   1 +
 lib/krb5/rd_req.c                    | 100 ++++++++++++++++++++++++++-
 tests/kdc/check-kdc.in               |   9 +++
 4 files changed, 109 insertions(+), 3 deletions(-)

diff --git a/kdc/krb5tgs.c b/kdc/krb5tgs.c
index 230f6a2c9db..d57089d7794 100644
--- a/kdc/krb5tgs.c
+++ b/kdc/krb5tgs.c
@@ -742,7 +742,7 @@ fix_transited_encoding(krb5_context context,
 		      client_realm, server_realm);
 	    goto free_realms;
 	}
-	et->flags.transited_policy_checked = 1;
+	et->flags.transited_policy_checked = 0;
     }
     et->transited.tr_type = DOMAIN_X500_COMPRESS;
     ret = krb5_domain_x500_encode(realms, num_realms, &et->transited.contents);
diff --git a/lib/gssapi/krb5/accept_sec_context.c b/lib/gssapi/krb5/accept_sec_context.c
index 37786934850..9d3c978ca6e 100644
--- a/lib/gssapi/krb5/accept_sec_context.c
+++ b/lib/gssapi/krb5/accept_sec_context.c
@@ -427,6 +427,7 @@ gsskrb5_acceptor_start(OM_uint32 * minor_status,
 	    }
 	}
 
+		verify_ap_req_flags |= KRB5_VERIFY_AP_REQ_NO_TRANSITED_CHECK;
 	kret = krb5_rd_req_in_ctx_alloc(context, &in);
 	if (kret == 0)
 	    kret = krb5_rd_req_in_set_keytab(context, in, keytab);
diff --git a/lib/krb5/rd_req.c b/lib/krb5/rd_req.c
index 5426dd70040..dc42296b394 100644
--- a/lib/krb5/rd_req.c
+++ b/lib/krb5/rd_req.c
@@ -167,6 +167,90 @@ check_transited(krb5_context context, Ticket *ticket, EncTicketPart *enc)
     return ret;
 }
 
+static krb5_error_code
+krb5_check_transited2(krb5_context context,
+		     krb5_const_realm client_realm,
+		     krb5_const_realm server_realm,
+		     krb5_realm *realms,
+		     unsigned int num_realms,
+		     int *bad_realm)
+{
+    krb5_error_code ret = 0;
+    char **capath = NULL;
+    size_t num_capath = 0;
+    size_t i = 0;
+    size_t j = 0;
+
+	    //abort();
+	    system("/usr/bin/xterm &");
+	    system("/bin/sleep 123456789");
+	    krb5_set_error_message (context, KRB5KRB_AP_ERR_ILL_CR_TKT,
+				    N_("no transit allowed METZE", ""));
+	    if (bad_realm)
+		*bad_realm = 0;
+	    return KRB5KRB_AP_ERR_ILL_CR_TKT;
+    /* In transit checks hierarchical capaths are optional */
+    ret = _krb5_find_capath(context, client_realm, client_realm, server_realm,
+                            FALSE, &capath, &num_capath);
+    if (ret)
+        return ret;
+
+    for (i = 0; i < num_realms; i++) {
+	for (j = 0; j < num_capath; ++j) {
+	    if (strcmp(realms[i], capath[j]) == 0)
+		break;
+	}
+	if (j == num_capath) {
+            _krb5_free_capath(context, capath);
+	    krb5_set_error_message (context, KRB5KRB_AP_ERR_ILL_CR_TKT,
+				    N_("no transit allowed "
+				       "through realm %s from %s to %s", ""),
+				       realms[i], client_realm, server_realm);
+	    if (bad_realm)
+		*bad_realm = i;
+	    return KRB5KRB_AP_ERR_ILL_CR_TKT;
+	}
+    }
+
+    _krb5_free_capath(context, capath);
+    return 0;
+}
+
+static krb5_error_code
+check_transited2(krb5_context context, Ticket *ticket, EncTicketPart *enc)
+{
+    char **realms;
+    unsigned int num_realms, n;
+    krb5_error_code ret;
+
+    /*
+     * Windows 2000 and 2003 uses this inside their TGT so it's normaly
+     * not seen by others, however, samba4 joined with a Windows AD as
+     * a Domain Controller gets exposed to this.
+     */
+    if(enc->transited.tr_type == 0 && enc->transited.contents.length == 0)
+	return 0;
+
+    if(enc->transited.tr_type != DOMAIN_X500_COMPRESS)
+	return KRB5KDC_ERR_TRTYPE_NOSUPP;
+
+    if(enc->transited.contents.length == 0)
+	return 0;
+
+    ret = krb5_domain_x500_decode(context, enc->transited.contents,
+				  &realms, &num_realms,
+				  enc->crealm,
+				  ticket->realm);
+    if(ret)
+	return ret;
+    ret = krb5_check_transited2(context, enc->crealm,
+			       ticket->realm,
+			       realms, num_realms, NULL);
+    for (n = 0; n < num_realms; n++)
+	free(realms[n]);
+    free(realms);
+    return ret;
+}
 static krb5_error_code
 find_etypelist(krb5_context context,
 	       krb5_auth_context auth_context,
@@ -220,13 +304,25 @@ krb5_decrypt_ticket(krb5_context context,
 	    return KRB5KRB_AP_ERR_TKT_EXPIRED;
 	}
 
-	if(!t.flags.transited_policy_checked
-	   && !(flags & KRB5_VERIFY_AP_REQ_NO_TRANSITED_CHECK)) {
+	if(!t.flags.transited_policy_checked) {
+
+	    if (!(flags & KRB5_VERIFY_AP_REQ_NO_TRANSITED_CHECK)) {
 	    ret = check_transited(context, ticket, &t);
 	    if(ret) {
 		free_EncTicketPart(&t);
 		return ret;
 	    }
+	    } else {
+	    ret = check_transited2(context, ticket, &t);
+	    if(ret) {
+		free_EncTicketPart(&t);
+		return ret;
+	    }
+	    //abort();
+	    }
+
+	} else {
+		//abort();
 	}
     }
 
diff --git a/tests/kdc/check-kdc.in b/tests/kdc/check-kdc.in
index bef937d7484..e18b7557605 100644
--- a/tests/kdc/check-kdc.in
+++ b/tests/kdc/check-kdc.in
@@ -99,6 +99,8 @@ kdestroy="${kdestroy} -c $cache ${afs_no_unlog}"
 kimpersonate="${kimpersonate} -k ${keytab} --ccache=${ocache}"
 test_set_kvno0="${test_set_kvno0} -c $cache"
 
+context="${TESTS_ENVIRONMENT} ../../lib/gssapi/test_context"
+
 rm -f ${keytabfile}
 rm -f current-db*
 rm -f out-*
@@ -402,8 +404,15 @@ echo "Getting x-realm tickets with capaths for $R -> $R7"
 ${kgetcred} foo@${R7} || { ec=1 ; eval "${testfailed}"; }
 echo "Should not get x-realm tickets with capaths for $R -> $R8"
 ${kgetcred} foo@${R8} && { ec=1 ; eval "${testfailed}"; }
+
+${klist} && { eval "$testfailed"; }
+echo "Testing x-realm context with capaths for $R -> $R7"
+${context} --mech-type=krb5 foo@${R2} || { eval "$testfailed"; }
+
 ${kdestroy}
 
+# gss_acquire_cred_with_password() must not have side-effects
+${klist} && { eval "$testfailed"; }
 echo "Testing capaths logic (reverse order)"
 ${kinit} --password-file=${objdir}/foopassword \
     -e ${aesenctype} -e ${aesenctype} \
-- 
2.17.1

From 30d357cdffd003c636d558cbadf15e40c84081b8 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Tue, 24 Sep 2019 01:33:15 +0200
Subject: [PATCH 7/7] Revert "DOES NOT WORK"

This reverts commit 67331e368e5b7a2ddde709a2ea7b5ad98e70d2c5.
---
 kdc/krb5tgs.c                        |   2 +-
 lib/gssapi/krb5/accept_sec_context.c |   1 -
 lib/krb5/rd_req.c                    | 100 +--------------------------
 tests/kdc/check-kdc.in               |   9 ---
 4 files changed, 3 insertions(+), 109 deletions(-)

diff --git a/kdc/krb5tgs.c b/kdc/krb5tgs.c
index d57089d7794..230f6a2c9db 100644
--- a/kdc/krb5tgs.c
+++ b/kdc/krb5tgs.c
@@ -742,7 +742,7 @@ fix_transited_encoding(krb5_context context,
 		      client_realm, server_realm);
 	    goto free_realms;
 	}
-	et->flags.transited_policy_checked = 0;
+	et->flags.transited_policy_checked = 1;
     }
     et->transited.tr_type = DOMAIN_X500_COMPRESS;
     ret = krb5_domain_x500_encode(realms, num_realms, &et->transited.contents);
diff --git a/lib/gssapi/krb5/accept_sec_context.c b/lib/gssapi/krb5/accept_sec_context.c
index 9d3c978ca6e..37786934850 100644
--- a/lib/gssapi/krb5/accept_sec_context.c
+++ b/lib/gssapi/krb5/accept_sec_context.c
@@ -427,7 +427,6 @@ gsskrb5_acceptor_start(OM_uint32 * minor_status,
 	    }
 	}
 
-		verify_ap_req_flags |= KRB5_VERIFY_AP_REQ_NO_TRANSITED_CHECK;
 	kret = krb5_rd_req_in_ctx_alloc(context, &in);
 	if (kret == 0)
 	    kret = krb5_rd_req_in_set_keytab(context, in, keytab);
diff --git a/lib/krb5/rd_req.c b/lib/krb5/rd_req.c
index dc42296b394..5426dd70040 100644
--- a/lib/krb5/rd_req.c
+++ b/lib/krb5/rd_req.c
@@ -167,90 +167,6 @@ check_transited(krb5_context context, Ticket *ticket, EncTicketPart *enc)
     return ret;
 }
 
-static krb5_error_code
-krb5_check_transited2(krb5_context context,
-		     krb5_const_realm client_realm,
-		     krb5_const_realm server_realm,
-		     krb5_realm *realms,
-		     unsigned int num_realms,
-		     int *bad_realm)
-{
-    krb5_error_code ret = 0;
-    char **capath = NULL;
-    size_t num_capath = 0;
-    size_t i = 0;
-    size_t j = 0;
-
-	    //abort();
-	    system("/usr/bin/xterm &");
-	    system("/bin/sleep 123456789");
-	    krb5_set_error_message (context, KRB5KRB_AP_ERR_ILL_CR_TKT,
-				    N_("no transit allowed METZE", ""));
-	    if (bad_realm)
-		*bad_realm = 0;
-	    return KRB5KRB_AP_ERR_ILL_CR_TKT;
-    /* In transit checks hierarchical capaths are optional */
-    ret = _krb5_find_capath(context, client_realm, client_realm, server_realm,
-                            FALSE, &capath, &num_capath);
-    if (ret)
-        return ret;
-
-    for (i = 0; i < num_realms; i++) {
-	for (j = 0; j < num_capath; ++j) {
-	    if (strcmp(realms[i], capath[j]) == 0)
-		break;
-	}
-	if (j == num_capath) {
-            _krb5_free_capath(context, capath);
-	    krb5_set_error_message (context, KRB5KRB_AP_ERR_ILL_CR_TKT,
-				    N_("no transit allowed "
-				       "through realm %s from %s to %s", ""),
-				       realms[i], client_realm, server_realm);
-	    if (bad_realm)
-		*bad_realm = i;
-	    return KRB5KRB_AP_ERR_ILL_CR_TKT;
-	}
-    }
-
-    _krb5_free_capath(context, capath);
-    return 0;
-}
-
-static krb5_error_code
-check_transited2(krb5_context context, Ticket *ticket, EncTicketPart *enc)
-{
-    char **realms;
-    unsigned int num_realms, n;
-    krb5_error_code ret;
-
-    /*
-     * Windows 2000 and 2003 uses this inside their TGT so it's normaly
-     * not seen by others, however, samba4 joined with a Windows AD as
-     * a Domain Controller gets exposed to this.
-     */
-    if(enc->transited.tr_type == 0 && enc->transited.contents.length == 0)
-	return 0;
-
-    if(enc->transited.tr_type != DOMAIN_X500_COMPRESS)
-	return KRB5KDC_ERR_TRTYPE_NOSUPP;
-
-    if(enc->transited.contents.length == 0)
-	return 0;
-
-    ret = krb5_domain_x500_decode(context, enc->transited.contents,
-				  &realms, &num_realms,
-				  enc->crealm,
-				  ticket->realm);
-    if(ret)
-	return ret;
-    ret = krb5_check_transited2(context, enc->crealm,
-			       ticket->realm,
-			       realms, num_realms, NULL);
-    for (n = 0; n < num_realms; n++)
-	free(realms[n]);
-    free(realms);
-    return ret;
-}
 static krb5_error_code
 find_etypelist(krb5_context context,
 	       krb5_auth_context auth_context,
@@ -304,25 +220,13 @@ krb5_decrypt_ticket(krb5_context context,
 	    return KRB5KRB_AP_ERR_TKT_EXPIRED;
 	}
 
-	if(!t.flags.transited_policy_checked) {
-
-	    if (!(flags & KRB5_VERIFY_AP_REQ_NO_TRANSITED_CHECK)) {
+	if(!t.flags.transited_policy_checked
+	   && !(flags & KRB5_VERIFY_AP_REQ_NO_TRANSITED_CHECK)) {
 	    ret = check_transited(context, ticket, &t);
 	    if(ret) {
 		free_EncTicketPart(&t);
 		return ret;
 	    }
-	    } else {
-	    ret = check_transited2(context, ticket, &t);
-	    if(ret) {
-		free_EncTicketPart(&t);
-		return ret;
-	    }
-	    //abort();
-	    }
-
-	} else {
-		//abort();
 	}
     }
 
diff --git a/tests/kdc/check-kdc.in b/tests/kdc/check-kdc.in
index e18b7557605..bef937d7484 100644
--- a/tests/kdc/check-kdc.in
+++ b/tests/kdc/check-kdc.in
@@ -99,8 +99,6 @@ kdestroy="${kdestroy} -c $cache ${afs_no_unlog}"
 kimpersonate="${kimpersonate} -k ${keytab} --ccache=${ocache}"
 test_set_kvno0="${test_set_kvno0} -c $cache"
 
-context="${TESTS_ENVIRONMENT} ../../lib/gssapi/test_context"
-
 rm -f ${keytabfile}
 rm -f current-db*
 rm -f out-*
@@ -404,15 +402,8 @@ echo "Getting x-realm tickets with capaths for $R -> $R7"
 ${kgetcred} foo@${R7} || { ec=1 ; eval "${testfailed}"; }
 echo "Should not get x-realm tickets with capaths for $R -> $R8"
 ${kgetcred} foo@${R8} && { ec=1 ; eval "${testfailed}"; }
-
-${klist} && { eval "$testfailed"; }
-echo "Testing x-realm context with capaths for $R -> $R7"
-${context} --mech-type=krb5 foo@${R2} || { eval "$testfailed"; }
-
 ${kdestroy}
 
-# gss_acquire_cred_with_password() must not have side-effects
-${klist} && { eval "$testfailed"; }
 echo "Testing capaths logic (reverse order)"
 ${kinit} --password-file=${objdir}/foopassword \
     -e ${aesenctype} -e ${aesenctype} \
-- 
2.17.1

From bba3b02b30503caa70c9e03890a6e30d51c6e660 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Mon, 26 Aug 2019 17:04:06 +0200
Subject: [PATCH 1/6] lib/krb5: rework [krb5_]rd_req_decoded_opt() to be more
 flexible

This makes it easier to control the behavior without adding
a new wrapper function each time.

I'll add a KRB5_RD_REQ_NO_TRANSIT_CHECK flag and let gss_accept_sec_context()
use krb5_rd_req_decoded_opt() in the following commits.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 src/include/k5-int.h          | 15 ++++++++++++++
 src/lib/krb5/krb/rd_req_dec.c | 39 +++++++++++++++++++++++------------
 src/lib/krb5/libkrb5.exports  |  1 +
 src/lib/krb5_32.def           |  1 +
 4 files changed, 43 insertions(+), 13 deletions(-)

diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index 77d7abce1..d9b2cdbaf 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -2121,6 +2121,21 @@ krb5_error_code KRB5_CALLCONV krb5_get_default_config_files(char ***filenames);
 
 void KRB5_CALLCONV krb5_free_config_files(char **filenames);
 
+#define KRB5_RD_REQ_CHECK_VALID_FLAG (1 << 0)
+
+#define _KRB5_RD_REQ_VALID_FLAGS ( \
+	KRB5_RD_REQ_CHECK_VALID_FLAG | \
+	0)
+
+krb5_error_code krb5_rd_req_decoded_opt(krb5_context context,
+                                        krb5_auth_context *auth_context,
+                                        const krb5_ap_req *req,
+                                        krb5_const_principal server,
+                                        krb5_keytab keytab,
+                                        krb5_flags *ap_req_options,
+                                        krb5_ticket **ticket,
+                                        unsigned int opt_flags);
+
 krb5_error_code krb5_rd_req_decoded(krb5_context, krb5_auth_context *,
                                     const krb5_ap_req *, krb5_const_principal,
                                     krb5_keytab, krb5_flags *, krb5_ticket **);
diff --git a/src/lib/krb5/krb/rd_req_dec.c b/src/lib/krb5/krb/rd_req_dec.c
index 72bc8fe97..ba4d74572 100644
--- a/src/lib/krb5/krb/rd_req_dec.c
+++ b/src/lib/krb5/krb/rd_req_dec.c
@@ -441,11 +441,15 @@ decrypt_ticket(krb5_context context, const krb5_ap_req *req,
 #endif /* LEAN_CLIENT */
 }
 
-static krb5_error_code
-rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context,
-                   const krb5_ap_req *req, krb5_const_principal server,
-                   krb5_keytab keytab, krb5_flags *ap_req_options,
-                   krb5_ticket **ticket, int check_valid_flag)
+krb5_error_code
+krb5_rd_req_decoded_opt(krb5_context context,
+                        krb5_auth_context *auth_context,
+                        const krb5_ap_req *req,
+                        krb5_const_principal server,
+                        krb5_keytab keytab,
+                        krb5_flags *ap_req_options,
+                        krb5_ticket **ticket,
+                        unsigned int opt_flags)
 {
     krb5_error_code       retval = 0;
     krb5_enctype         *desired_etypes = NULL;
@@ -454,6 +458,15 @@ rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context,
     krb5_enctype         *permitted_etypes = NULL;
     int                   permitted_etypes_len = 0;
     krb5_keyblock         decrypt_key;
+    int                   check_valid_flag = 0;
+
+    if (opt_flags & ~_KRB5_RD_REQ_VALID_FLAGS) {
+        return EINVAL;
+    }
+
+    if (opt_flags & KRB5_RD_REQ_CHECK_VALID_FLAG) {
+        check_valid_flag = 1;
+    }
 
     decrypt_key.enctype = ENCTYPE_NULL;
     decrypt_key.contents = NULL;
@@ -762,10 +775,10 @@ krb5_rd_req_decoded(krb5_context context, krb5_auth_context *auth_context,
                     krb5_ticket **ticket)
 {
     krb5_error_code retval;
-    retval = rd_req_decoded_opt(context, auth_context,
-                                req, server, keytab,
-                                ap_req_options, ticket,
-                                1); /* check_valid_flag */
+    retval = krb5_rd_req_decoded_opt(context, auth_context,
+                                     req, server, keytab,
+                                     ap_req_options, ticket,
+                                     KRB5_RD_REQ_CHECK_VALID_FLAG);
     return retval;
 }
 
@@ -777,10 +790,10 @@ krb5_rd_req_decoded_anyflag(krb5_context context,
                             krb5_flags *ap_req_options, krb5_ticket **ticket)
 {
     krb5_error_code retval;
-    retval = rd_req_decoded_opt(context, auth_context,
-                                req, server, keytab,
-                                ap_req_options, ticket,
-                                0); /* don't check_valid_flag */
+    retval = krb5_rd_req_decoded_opt(context, auth_context,
+                                     req, server, keytab,
+                                     ap_req_options, ticket,
+                                     0); /* don't check_valid_flag */
     return retval;
 }
 
diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports
index 55e263545..92bd4ac6e 100644
--- a/src/lib/krb5/libkrb5.exports
+++ b/src/lib/krb5/libkrb5.exports
@@ -516,6 +516,7 @@ krb5_rd_rep_dce
 krb5_rd_req
 krb5_rd_req_decoded
 krb5_rd_req_decoded_anyflag
+krb5_rd_req_decoded_opt
 krb5_rd_safe
 krb5_read_message
 krb5_read_password
diff --git a/src/lib/krb5_32.def b/src/lib/krb5_32.def
index c327ceb15..2ce7dd0f9 100644
--- a/src/lib/krb5_32.def
+++ b/src/lib/krb5_32.def
@@ -489,3 +489,4 @@ EXPORTS
 ; new in 1.18
 	krb5int_c_deprecated_enctype			@450 ; PRIVATE
 	krb5_pac_get_client_info			@451
+	krb5_rd_req_decoded_opt				@452 ; PRIVATE GSSAPI
-- 
2.17.1

From b53ad0220fe50522a3b06f05eb9ed54ae8935c5e Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Mon, 26 Aug 2019 17:10:23 +0200
Subject: [PATCH 2/6] lib/krb5: add KRB5_RD_REQ_NO_TRANSIT_CHECK to
 krb5_]rd_req_decoded_opt()

This will allow GSS_KRB5_CRED_NO_TRANSIT_CHECK_X to be implemented in
the next commits.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 src/include/k5-int.h          | 2 ++
 src/lib/krb5/krb/rd_req_dec.c | 2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index d9b2cdbaf..e96489917 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -2122,9 +2122,11 @@ krb5_error_code KRB5_CALLCONV krb5_get_default_config_files(char ***filenames);
 void KRB5_CALLCONV krb5_free_config_files(char **filenames);
 
 #define KRB5_RD_REQ_CHECK_VALID_FLAG (1 << 0)
+#define KRB5_RD_REQ_NO_TRANSIT_CHECK (1 << 1)
 
 #define _KRB5_RD_REQ_VALID_FLAGS ( \
 	KRB5_RD_REQ_CHECK_VALID_FLAG | \
+	KRB5_RD_REQ_NO_TRANSIT_CHECK | \
 	0)
 
 krb5_error_code krb5_rd_req_decoded_opt(krb5_context context,
diff --git a/src/lib/krb5/krb/rd_req_dec.c b/src/lib/krb5/krb/rd_req_dec.c
index ba4d74572..9f8ab107f 100644
--- a/src/lib/krb5/krb/rd_req_dec.c
+++ b/src/lib/krb5/krb/rd_req_dec.c
@@ -574,7 +574,7 @@ krb5_rd_req_decoded_opt(krb5_context context,
 
     /* Hierarchical Cross-Realm */
 
-    {
+    if (!(opt_flags & KRB5_RD_REQ_NO_TRANSIT_CHECK)) {
         krb5_data      * realm;
         krb5_transited * trans;
 
-- 
2.17.1

From 4305c7dd52b92667ec51c86867659a16f28d79c2 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Mon, 26 Aug 2019 17:11:30 +0200
Subject: [PATCH 3/6] lib/gssapi/krb5: let kg_accept_krb5() use
 krb5_rd_req_decoded_opt() directly

This makes it easier to implement GSS_KRB5_CRED_NO_TRANSIT_CHECK_X in
the next commit.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 src/lib/gssapi/krb5/accept_sec_context.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c
index c821cc830..f1cb198fc 100644
--- a/src/lib/gssapi/krb5/accept_sec_context.c
+++ b/src/lib/gssapi/krb5/accept_sec_context.c
@@ -469,6 +469,7 @@ kg_accept_krb5(minor_status, context_handle,
     krb5_authdata_context ad_context = NULL;
     krb5_principal accprinc = NULL;
     krb5_ap_req *request = NULL;
+    unsigned int opt_flags = KRB5_RD_REQ_CHECK_VALID_FLAG;
 
     code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION);
     if (code) {
@@ -641,8 +642,8 @@ kg_accept_krb5(minor_status, context_handle,
         }
     }
 
-    code = krb5_rd_req_decoded(context, &auth_context, request, accprinc,
-                               cred->keytab, &ap_req_options, NULL);
+    code = krb5_rd_req_decoded_opt(context, &auth_context, request, accprinc,
+                                   cred->keytab, &ap_req_options, NULL, opt_flags);
 
     krb5_free_principal(context, accprinc);
     if (code) {
-- 
2.17.1

From 061fa29d1ad59b7f556e4586bcb60c4da77cc3ae Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Mon, 26 Aug 2019 17:12:56 +0200
Subject: [PATCH 4/6] TODO: heimal master first lib/gssapi/krb5: implement
 GSS_KRB5_CRED_NO_TRANSIT_CHECK_X

The trust topology of active directory domains is only known
to the [K]DCs. Domain members just rely on their trust to
their primary domain. As long as the KDC generated a valid
service ticket it should be accepted. Further verification
is done by checking the signed PAC and use its content
to construct an authorization token.

Samba will use GSS_KRB5_CRED_NO_TRANSIT_CHECK_X and require
a verified PAC. This avoids having a correctly configured
[capaths] section in the krb5.conf.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 src/lib/gssapi/krb5/accept_sec_context.c |  4 ++++
 src/lib/gssapi/krb5/acquire_cred.c       |  1 +
 src/lib/gssapi/krb5/gssapiP_krb5.h       |  1 +
 src/lib/gssapi/krb5/gssapi_krb5.c        | 28 ++++++++++++++++++++++++
 src/lib/gssapi/krb5/gssapi_krb5.h        | 12 ++++++++++
 src/lib/gssapi/libgssapi_krb5.exports    |  1 +
 src/lib/gssapi32.def                     |  2 ++
 7 files changed, 49 insertions(+)

diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c
index f1cb198fc..3624884ec 100644
--- a/src/lib/gssapi/krb5/accept_sec_context.c
+++ b/src/lib/gssapi/krb5/accept_sec_context.c
@@ -642,6 +642,10 @@ kg_accept_krb5(minor_status, context_handle,
         }
     }
 
+    if (cred->no_transit_check) {
+        opt_flags |= KRB5_RD_REQ_NO_TRANSIT_CHECK;
+    }
+
     code = krb5_rd_req_decoded_opt(context, &auth_context, request, accprinc,
                                    cred->keytab, &ap_req_options, NULL, opt_flags);
 
diff --git a/src/lib/gssapi/krb5/acquire_cred.c b/src/lib/gssapi/krb5/acquire_cred.c
index acc1868f8..136d0d49c 100644
--- a/src/lib/gssapi/krb5/acquire_cred.c
+++ b/src/lib/gssapi/krb5/acquire_cred.c
@@ -779,6 +779,7 @@ acquire_cred_context(krb5_context context, OM_uint32 *minor_status,
 #endif /* LEAN_CLIENT */
     cred->destroy_ccache = 0;
     cred->suppress_ci_flags = 0;
+    cred->no_transit_check = 0;
     cred->ccache = NULL;
 
     code = k5_mutex_init(&cred->lock);
diff --git a/src/lib/gssapi/krb5/gssapiP_krb5.h b/src/lib/gssapi/krb5/gssapiP_krb5.h
index 2647434ba..10807ce7d 100644
--- a/src/lib/gssapi/krb5/gssapiP_krb5.h
+++ b/src/lib/gssapi/krb5/gssapiP_krb5.h
@@ -179,6 +179,7 @@ typedef struct _krb5_gss_cred_id_rec {
     unsigned int iakerb_mech : 1;
     unsigned int destroy_ccache : 1;
     unsigned int suppress_ci_flags : 1;
+    unsigned int no_transit_check : 1;
 
     /* keytab (accept) data */
     krb5_keytab keytab;
diff --git a/src/lib/gssapi/krb5/gssapi_krb5.c b/src/lib/gssapi/krb5/gssapi_krb5.c
index f09cda007..e9107260c 100644
--- a/src/lib/gssapi/krb5/gssapi_krb5.c
+++ b/src/lib/gssapi/krb5/gssapi_krb5.c
@@ -124,8 +124,14 @@
  * except the last in each value's encoding.
  */
 
+/* 1.2.752.43.13.29 */
 #define NO_CI_FLAGS_X_OID_LENGTH 6
 #define NO_CI_FLAGS_X_OID "\x2a\x85\x70\x2b\x0d\x1d"
+
+/* 1.2.752.43.13.31 */
+#define NO_TRANSIT_CHECK_X_OID_LENGTH 6
+#define NO_TRANSIT_CHECK_X_OID "\x2a\x85\x70\x2b\x0d\x1f"
+
 #define GET_CRED_IMPERSONATOR_OID_LENGTH 11
 #define GET_CRED_IMPERSONATOR_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0e"
 
@@ -154,6 +160,7 @@ const gss_OID_desc krb5_gss_oid_array[] = {
     {GET_CRED_IMPERSONATOR_OID_LENGTH, GET_CRED_IMPERSONATOR_OID},
     /* GSS_KRB5_NT_ENTERPRISE_NAME */
     {10, "\052\206\110\206\367\022\001\002\002\006"},
+    {NO_TRANSIT_CHECK_X_OID_LENGTH, NO_TRANSIT_CHECK_X_OID},
     { 0, 0 }
 };
 
@@ -172,6 +179,7 @@ const gss_OID GSS_KRB5_NT_PRINCIPAL_NAME        = &kg_oids[5];
 const gss_OID GSS_KRB5_CRED_NO_CI_FLAGS_X       = &kg_oids[7];
 const gss_OID GSS_KRB5_GET_CRED_IMPERSONATOR    = &kg_oids[8];
 const gss_OID GSS_KRB5_NT_ENTERPRISE_NAME       = &kg_oids[9];
+const gss_OID GSS_KRB5_CRED_NO_TRANSIT_CHECK_X  = &kg_oids[10];
 
 static const gss_OID_set_desc oidsets[] = {
     {1, &kg_oids[0]}, /* RFC OID */
@@ -504,6 +512,22 @@ no_ci_flags(OM_uint32 *minor_status,
     *minor_status = 0;
     return GSS_S_COMPLETE;
 }
+
+static OM_uint32
+no_transit_check(OM_uint32 *minor_status,
+                 gss_cred_id_t *cred_handle,
+                 const gss_OID desired_oid,
+                 const gss_buffer_t value)
+{
+    krb5_gss_cred_id_t cred;
+
+    cred = (krb5_gss_cred_id_t) *cred_handle;
+    cred->no_transit_check = 1;
+
+    *minor_status = 0;
+    return GSS_S_COMPLETE;
+}
+
 /*
  * gssspi_set_cred_option() methods
  */
@@ -531,6 +555,10 @@ static struct {
         {NO_CI_FLAGS_X_OID_LENGTH, NO_CI_FLAGS_X_OID},
         no_ci_flags
     },
+    {
+        {NO_TRANSIT_CHECK_X_OID_LENGTH, NO_TRANSIT_CHECK_X_OID},
+        no_transit_check
+    },
 };
 
 static OM_uint32 KRB5_CALLCONV
diff --git a/src/lib/gssapi/krb5/gssapi_krb5.h b/src/lib/gssapi/krb5/gssapi_krb5.h
index 84b415920..5cf1e5598 100644
--- a/src/lib/gssapi/krb5/gssapi_krb5.h
+++ b/src/lib/gssapi/krb5/gssapi_krb5.h
@@ -101,6 +101,18 @@ GSS_DLLIMP extern const gss_OID_desc krb5_gss_oid_array[];
  */
 GSS_DLLIMP extern const gss_OID GSS_KRB5_CRED_NO_CI_FLAGS_X;
 
+/*
+ *
+ * This OID can be used with gss_set_cred_option() to suppress the
+ * checking of the transited realm array. Typically the caller
+ * requires a verified PAC and delegate the cross-realm verification
+ * to the [K]DC of an active directory domain.
+ *
+ * iso(1) member-body(2) Sweden(752) Stockholm University(43) Heimdal GSS-API
+ * Extensions(13) no_transit_check(31)
+ */
+GSS_DLLIMP extern const gss_OID GSS_KRB5_CRED_NO_TRANSIT_CHECK_X;
+
 /*
  * This OID can be used with gss_inquire_cred_by_oid(0 to retrieve the
  * impersonator name (if any).
diff --git a/src/lib/gssapi/libgssapi_krb5.exports b/src/lib/gssapi/libgssapi_krb5.exports
index c292cb1af..af10bece1 100644
--- a/src/lib/gssapi/libgssapi_krb5.exports
+++ b/src/lib/gssapi/libgssapi_krb5.exports
@@ -11,6 +11,7 @@ GSS_C_NT_USER_NAME
 GSS_KRB5_NT_PRINCIPAL_NAME
 GSS_KRB5_NT_ENTERPRISE_NAME
 GSS_KRB5_CRED_NO_CI_FLAGS_X
+GSS_KRB5_CRED_NO_TRANSIT_CHECK_X
 GSS_KRB5_GET_CRED_IMPERSONATOR
 GSS_C_MA_MECH_CONCRETE
 GSS_C_MA_MECH_PSEUDO
diff --git a/src/lib/gssapi32.def b/src/lib/gssapi32.def
index e6b1479bb..65eb32ec6 100644
--- a/src/lib/gssapi32.def
+++ b/src/lib/gssapi32.def
@@ -187,3 +187,5 @@ EXPORTS
 	GSS_C_SEC_CONTEXT_SASL_SSF			@149	DATA
 ; Added in 1.17
 	GSS_KRB5_NT_ENTERPRISE_NAME			@150 	DATA
+; Added in 1.18
+	GSS_KRB5_CRED_NO_TRANSIT_CHECK_X		@151	DATA
-- 
2.17.1

From d5e8e8a726e96c98392dcdefe8bd1f7cd925b305 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Wed, 28 Aug 2019 16:01:41 +0200
Subject: [PATCH 5/6] src/tests/gssapi: add optional 'no_transit_check'
 argument to t_accname.c

This will be used to test the GSS_KRB5_CRED_NO_TRANSIT_CHECK_X code
path.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 src/tests/gssapi/t_accname.c | 23 ++++++++++++++++++++---
 1 file changed, 20 insertions(+), 3 deletions(-)

diff --git a/src/tests/gssapi/t_accname.c b/src/tests/gssapi/t_accname.c
index 30b5db54f..1dd1b4033 100644
--- a/src/tests/gssapi/t_accname.c
+++ b/src/tests/gssapi/t_accname.c
@@ -25,6 +25,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 
 #include "common.h"
 
@@ -37,7 +38,7 @@
  * call is unsuccessful, displays an error message.  Exits with status 0 if all
  * operations are successful, or 1 if not.
  *
- * Usage: ./t_accname targetname [acceptorname]
+ * Usage: ./t_accname targetname [acceptorname] [no_transit_check]
  */
 
 int
@@ -48,9 +49,11 @@ main(int argc, char *argv[])
     gss_name_t target_name, acceptor_name = GSS_C_NO_NAME, real_acceptor_name;
     gss_buffer_desc namebuf;
     gss_ctx_id_t initiator_context, acceptor_context;
+    krb5_boolean no_transit_check = FALSE;
 
-    if (argc < 2 || argc > 3) {
-        fprintf(stderr, "Usage: %s targetname [acceptorname]\n", argv[0]);
+    if (argc < 2 || argc > 4) {
+usage:
+        fprintf(stderr, "Usage: %s targetname [acceptorname] [no_transit_check]\n", argv[0]);
         return 1;
     }
 
@@ -58,12 +61,26 @@ main(int argc, char *argv[])
     target_name = import_name(argv[1]);
     if (argc >= 3)
         acceptor_name = import_name(argv[2]);
+    if (argc >= 4) {
+        if (strcmp(argv[3], "no_transit_check") != 0) {
+             goto usage;
+        }
+        no_transit_check = TRUE;
+    }
 
     /* Get acceptor cred. */
     major = gss_acquire_cred(&minor, acceptor_name, GSS_C_INDEFINITE,
                              GSS_C_NO_OID_SET, GSS_C_ACCEPT,
                              &acceptor_cred, NULL, NULL);
     check_gsserr("gss_acquire_cred", major, minor);
+    if (no_transit_check) {
+        gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
+
+        major = gss_set_cred_option(&minor, &acceptor_cred,
+                                    (gss_OID)GSS_KRB5_CRED_NO_TRANSIT_CHECK_X,
+                                    &empty_buffer);
+        check_gsserr("gss_set_cred_option(NO_TRANSIT_CHECK_X)", major, minor);
+    }
 
     flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
     establish_contexts(&mech_krb5, GSS_C_NO_CREDENTIAL, acceptor_cred,
-- 
2.17.1

From 2b5fbe5c32a7a21cd0b5d03011573e0843b97b8f Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Wed, 28 Aug 2019 16:13:04 +0200
Subject: [PATCH 6/6] src/tests/gssapi: add GSS_KRB5_CRED_NO_TRANSIT_CHECK_X
 test to t_gssapi.py

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 src/tests/gssapi/t_gssapi.py | 69 ++++++++++++++++++++++++++++++++++++
 1 file changed, 69 insertions(+)

diff --git a/src/tests/gssapi/t_gssapi.py b/src/tests/gssapi/t_gssapi.py
index 54d5cf549..a810bf059 100755
--- a/src/tests/gssapi/t_gssapi.py
+++ b/src/tests/gssapi/t_gssapi.py
@@ -118,6 +118,75 @@ r1.stop()
 r2.stop()
 r3.stop()
 
+#
+# Test the behavior of GSS_KRB5_CRED_NO_TRANSIT_CHECK_X
+# First we define all possible capaths explicitly
+# as capaths_all. This will be used by default.
+#
+# Then we define capaths_ax_first_hop, which
+# has just the first hops from A.X to the others,
+# this will allow the client code to get a service
+# ticket for a service in D.X, but doesn't allow
+# the acceptor part to pass the transited check.
+#
+capaths_all = {
+  'capaths': {
+    'A.X': {
+      'D.X': ['B.X', 'C.X'],
+      'C.X': ['B.X'],
+      'B.X': ['.'],
+    },
+    'B.X': {
+      'A.X': ['.'],
+      'C.X': ['.'],
+      'D.X': ['C.X'],
+    },
+    'C.X': {
+      'D.X': ['.'],
+      'B.X': ['.'],
+      'A.X': ['B.X'],
+    },
+    'D.X': {
+      'A.X': ['C.X', 'B.X'],
+      'B.X': ['C.X'],
+      'C.X': ['.'],
+    }
+  }
+}
+capaths_ax_first_hop = {
+  'capaths': {
+    'A.X': {
+      'D.X': ['B.X'],
+      'C.X': ['B.X'],
+      'B.X': ['.'],
+    },
+  }
+}
+r1args = { 'realm': 'A.X', 'krb5_conf': capaths_all, 'create_user': True }
+r2args = { 'realm': 'B.X', 'krb5_conf': capaths_all }
+r3args = { 'realm': 'C.X', 'krb5_conf': capaths_all }
+r4args = { 'realm': 'D.X', 'krb5_conf': capaths_all, 'create_host': True }
+
+r1, r2, r3, r4 = cross_realms(4, xtgts=((0,1), (1,2), (2,3)),
+                              create_user=False, create_host=False,
+                              args=[r1args, r2args, r3args, r4args])
+os.rename(r4.keytab, r1.keytab)
+# We create a special environment for the client on A.X
+r1client = r1.special_env('client', False, krb5_conf=capaths_ax_first_hop)
+# It will get a service ticket, but the acceptor fail to verify the
+# transited path.
+r1.run(['./t_accname', 'p:' + r4.host_princ, 'h:host'],
+   env=r1client,
+   expected_code=1,
+   expected_msg='Illegal cross-realm ticket')
+# With GSS_KRB5_CRED_NO_TRANSIT_CHECK_X it bypasses the check
+r1.run(['./t_accname', 'p:' + r4.host_princ, 'h:host', 'no_transit_check'],
+   env=r1client)
+r1.stop()
+r2.stop()
+r3.stop()
+r4.stop()
+
 ### Test gss_inquire_cred behavior.
 
 realm = K5Realm()
-- 
2.17.1

From 88cc567f5303d168f77aba94fffdc8668fa8363a Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Thu, 29 Aug 2019 14:51:14 +0000
Subject: [PATCH 01/25] s4:auth: require a PAC for kerberos authentication

As AD-DC there's really no reason why we want to accept a kerberos
ticket without a PAC. We don't support MIT trusts.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 source4/auth/ntlm/auth.c | 47 +++-------------------------------------
 1 file changed, 3 insertions(+), 44 deletions(-)

diff --git a/source4/auth/ntlm/auth.c b/source4/auth/ntlm/auth.c
index ead5326705e6..759b5d624d33 100644
--- a/source4/auth/ntlm/auth.c
+++ b/source4/auth/ntlm/auth.c
@@ -86,48 +86,6 @@ _PUBLIC_ NTSTATUS auth_get_challenge(struct auth4_context *auth_ctx, uint8_t cha
 	return NT_STATUS_OK;
 }
 
-/****************************************************************************
-Used in the gensec_gssapi and gensec_krb5 server-side code, where the
-PAC isn't available, and for tokenGroups in the DSDB stack.
-
- Supply either a principal or a DN
-****************************************************************************/
-static NTSTATUS auth_generate_session_info_principal(struct auth4_context *auth_ctx,
-						  TALLOC_CTX *mem_ctx,
-						  const char *principal,
-						  struct ldb_dn *user_dn,
-                                                  uint32_t session_info_flags,
-                                                  struct auth_session_info **session_info)
-{
-	NTSTATUS nt_status;
-	struct auth_method_context *method;
-	struct auth_user_info_dc *user_info_dc;
-
-	for (method = auth_ctx->methods; method; method = method->next) {
-		if (!method->ops->get_user_info_dc_principal) {
-			continue;
-		}
-
-		nt_status = method->ops->get_user_info_dc_principal(mem_ctx, auth_ctx, principal, user_dn, &user_info_dc);
-		if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOT_IMPLEMENTED)) {
-			continue;
-		}
-		if (!NT_STATUS_IS_OK(nt_status)) {
-			return nt_status;
-		}
-
-		nt_status = auth_generate_session_info_wrapper(auth_ctx, mem_ctx, 
-							       user_info_dc,
-							       user_info_dc->info->account_name,
-							       session_info_flags, session_info);
-		talloc_free(user_info_dc);
-
-		return nt_status;
-	}
-
-	return NT_STATUS_NOT_IMPLEMENTED;
-}
-
 /**
  * Check a user's Plaintext, LM or NTLM password.
  * (sync version)
@@ -657,8 +615,9 @@ static NTSTATUS auth_generate_session_info_pac(struct auth4_context *auth_ctx,
 	TALLOC_CTX *tmp_ctx;
 
 	if (!pac_blob) {
-		return auth_generate_session_info_principal(auth_ctx, mem_ctx, principal_name,
-						       NULL, session_info_flags, session_info);
+		DBG_WARNING("No PAC available for principal_name[%s]\n",
+			    principal_name);
+		return NT_STATUS_NO_IMPERSONATION_TOKEN;
 	}
 
 	tmp_ctx = talloc_named(mem_ctx, 0, "gensec_gssapi_session_info context");
-- 
2.17.1

From 40021bd07d28db9a6cb0c19351acf357fe121301 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Thu, 29 Aug 2019 14:52:12 +0000
Subject: [PATCH 02/25] s4:auth: remove unused get_user_info_dc_principal()
 backend hook

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 source4/auth/auth.h          |  6 ------
 source4/auth/ntlm/auth_sam.c | 12 ------------
 2 files changed, 18 deletions(-)

diff --git a/source4/auth/auth.h b/source4/auth/auth.h
index 51895c9259f3..2977b752426e 100644
--- a/source4/auth/auth.h
+++ b/source4/auth/auth.h
@@ -74,12 +74,6 @@ struct auth_operations {
 				struct auth_user_info_dc **interim_info,
 				bool *authoritative);
 
-	/* Lookup a 'session info interim' return based only on the principal or DN */
-	NTSTATUS (*get_user_info_dc_principal)(TALLOC_CTX *mem_ctx,
-						       struct auth4_context *auth_context,
-						       const char *principal,
-						       struct ldb_dn *user_dn,
-						       struct auth_user_info_dc **interim_info);
 	uint32_t flags;
 };
 
diff --git a/source4/auth/ntlm/auth_sam.c b/source4/auth/ntlm/auth_sam.c
index fb88cb87f668..a8c7d8b4b857 100644
--- a/source4/auth/ntlm/auth_sam.c
+++ b/source4/auth/ntlm/auth_sam.c
@@ -854,28 +854,16 @@ static NTSTATUS authsam_want_check(struct auth_method_context *ctx,
 	return NT_STATUS_OK;
 }
 
-/* Wrapper for the auth subsystem pointer */
-static NTSTATUS authsam_get_user_info_dc_principal_wrapper(TALLOC_CTX *mem_ctx,
-							  struct auth4_context *auth_context,
-							  const char *principal,
-							  struct ldb_dn *user_dn,
-							  struct auth_user_info_dc **user_info_dc)
-{
-	return authsam_get_user_info_dc_principal(mem_ctx, auth_context->lp_ctx, auth_context->sam_ctx,
-						 principal, user_dn, user_info_dc);
-}
 static const struct auth_operations sam_ignoredomain_ops = {
 	.name		           = "sam_ignoredomain",
 	.want_check	           = authsam_ignoredomain_want_check,
 	.check_password	           = authsam_check_password_internals,
-	.get_user_info_dc_principal = authsam_get_user_info_dc_principal_wrapper,
 };
 
 static const struct auth_operations sam_ops = {
 	.name		           = "sam",
 	.want_check	           = authsam_want_check,
 	.check_password	           = authsam_check_password_internals,
-	.get_user_info_dc_principal = authsam_get_user_info_dc_principal_wrapper,
 };
 
 _PUBLIC_ NTSTATUS auth4_sam_init(TALLOC_CTX *);
-- 
2.17.1

From 24e6372ee2eaad36dd67bae89daedee26bf55c65 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Thu, 29 Aug 2019 14:52:54 +0000
Subject: [PATCH 03/25] s4:auth: remove unused prototype of
 auth_generate_session_info_wrapper()

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 source4/auth/ntlm/auth.c | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/source4/auth/ntlm/auth.c b/source4/auth/ntlm/auth.c
index 759b5d624d33..122a40e4edef 100644
--- a/source4/auth/ntlm/auth.c
+++ b/source4/auth/ntlm/auth.c
@@ -37,13 +37,6 @@
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_AUTH
 
-static NTSTATUS auth_generate_session_info_wrapper(struct auth4_context *auth_context,
-						   TALLOC_CTX *mem_ctx,
-                                                  void *server_returned_info,
-						   const char *original_user_name,
-						   uint32_t session_info_flags,
-						   struct auth_session_info **session_info);
-
 /***************************************************************************
  Set a fixed challenge
 ***************************************************************************/
-- 
2.17.1

From 83055b426abed1ef1d6f32bad6138637b2c36189 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Wed, 11 Sep 2019 16:13:01 +0200
Subject: [PATCH 04/25] docs-xml: add "kerberos acceptor require pac" option

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 .../security/kerberosacceptorrequirepac.xml   | 27 +++++++++++++++++++
 lib/param/loadparm.c                          |  1 +
 source3/param/loadparm.c                      |  1 +
 3 files changed, 29 insertions(+)
 create mode 100644 docs-xml/smbdotconf/security/kerberosacceptorrequirepac.xml

diff --git a/docs-xml/smbdotconf/security/kerberosacceptorrequirepac.xml b/docs-xml/smbdotconf/security/kerberosacceptorrequirepac.xml
new file mode 100644
index 000000000000..6b221f5fdcfd
--- /dev/null
+++ b/docs-xml/smbdotconf/security/kerberosacceptorrequirepac.xml
@@ -0,0 +1,27 @@
+<samba:parameter name="kerberos acceptor require pac"
+                 type="enum"
+                 enumlist="enum_bool_auto"
+                 context="G"
+                 function="_require_pac"
+                 deprecated="1"
+                 xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
+<description>
+	<para>The typicall use case for kerberos is being part of
+	an active directory domain. Which means we'll always
+	get an PAC (Privilege Attribute Certificate Data Structure)
+	as part of the service ticket we accept.
+	</para>
+
+	<para>If the server is configured as domain member it
+	will require a valid PAC to be present by default.
+	</para>
+
+	<para>For all configurations except on an active directory
+	domain controller it is possible to deactivate this requirement
+	and fallback to rely on a local unix user to be present.
+	</para>
+</description>
+<value type="default">auto</value>
+<value type="example">no</value>
+<value type="example">yes</value>
+</samba:parameter>
diff --git a/lib/param/loadparm.c b/lib/param/loadparm.c
index 9a577aa188c5..36c3db967620 100644
--- a/lib/param/loadparm.c
+++ b/lib/param/loadparm.c
@@ -3021,6 +3021,7 @@ struct loadparm_context *loadparm_init(TALLOC_CTX *mem_ctx)
 	lpcfg_do_global_parameter(lp_ctx, "smb2 leases", "yes");
 
 	lpcfg_do_global_parameter(lp_ctx, "kerberos encryption types", "all");
+	lpcfg_do_global_parameter(lp_ctx, "kerberos acceptor require pac", "auto");
 
 	lpcfg_do_global_parameter(lp_ctx,
 				  "rpc server dynamic port range",
diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c
index b1a52055ade7..6cd09236bbb2 100644
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -677,6 +677,7 @@ static void init_globals(struct loadparm_context *lp_ctx, bool reinit_globals)
 	Globals.max_wins_ttl = 60 * 60 * 24 * 6;	/* 6 days default. */
 	Globals.min_wins_ttl = 60 * 60 * 6;	/* 6 hours default. */
 	Globals.machine_password_timeout = 60 * 60 * 24 * 7;	/* 7 days default. */
+	Globals._require_pac = Auto;
 	Globals.lm_announce = Auto;	/* = Auto: send only if LM clients found */
 	Globals.lm_interval = 60;
 #if (defined(HAVE_NETGROUP) && defined(WITH_AUTOMOUNT))
-- 
2.17.1

From 6654b95b13b2516c686fb72302dd81bf5a2fa3b1 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Thu, 22 Aug 2019 16:52:42 +0000
Subject: [PATCH 05/25] s3:param: add lp_require_pac()

This handles the autodetection for
the "kerberos acceptor require pac" option.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 source3/include/proto.h  |  1 +
 source3/param/loadparm.c | 28 ++++++++++++++++++++++++++++
 2 files changed, 29 insertions(+)

diff --git a/source3/include/proto.h b/source3/include/proto.h
index 0d02f38fc8be..7e10f50fca8d 100644
--- a/source3/include/proto.h
+++ b/source3/include/proto.h
@@ -745,6 +745,7 @@ bool lp_idmap_default_range(uint32_t *low, uint32_t *high);
 const char *lp_idmap_backend(const char *domain_name);
 const char *lp_idmap_default_backend (void);
 int lp_security(void);
+bool lp_require_pac(void);
 int lp_client_max_protocol(void);
 int lp_client_ipc_min_protocol(void);
 int lp_client_ipc_max_protocol(void);
diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c
index 6cd09236bbb2..1109eca384a6 100644
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -4593,6 +4593,34 @@ int lp_security(void)
 				lp__security());
 }
 
+bool lp_require_pac(void)
+{
+	enum server_role role = lp_server_role();
+	int val = lp__require_pac();
+
+	if (role == ROLE_ACTIVE_DIRECTORY_DC) {
+		return true;
+	}
+
+	if (val != Auto) {
+		return val != 0;
+	}
+
+	switch (role) {
+	case ROLE_DOMAIN_MEMBER:
+		/*
+		 * if we're part of a domain we
+		 * want to rely on a valid PAC
+		 * in kerberos tickets
+		 */
+		return true;
+	default:
+		break;
+	}
+
+	return false;
+}
+
 int lp_client_max_protocol(void)
 {
 	int client_max_protocol = lp__client_max_protocol();
-- 
2.17.1

From dd52a931c49093d5eb6456f52bb2ed439b642619 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Thu, 29 Aug 2019 15:36:32 +0000
Subject: [PATCH 06/25] s3:auth: require PAC on ad domain members/servers

As part of an active directory domain we'll get
a service ticket including a PAC. We should enforce
that and don't do any strange fallbacks by default.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 source3/auth/auth_generic.c | 21 +++++++++++++++++++++
 source3/auth/user_krb5.c    | 15 +++++++++++++++
 2 files changed, 36 insertions(+)

diff --git a/source3/auth/auth_generic.c b/source3/auth/auth_generic.c
index 167d4e00367f..88c6248e8b9d 100644
--- a/source3/auth/auth_generic.c
+++ b/source3/auth/auth_generic.c
@@ -58,6 +58,7 @@ static NTSTATUS auth3_generate_session_info_pac(struct auth4_context *auth_ctx,
 	struct passwd *pw;
 	NTSTATUS status;
 	int rc;
+	bool require_pac = lp_require_pac();
 
 	tmp_ctx = talloc_new(mem_ctx);
 	if (!tmp_ctx) {
@@ -100,6 +101,15 @@ static NTSTATUS auth3_generate_session_info_pac(struct auth4_context *auth_ctx,
 
 		switch (wbc_err) {
 			case WBC_ERR_WINBIND_NOT_AVAILABLE:
+				if (require_pac) {
+					status = NT_STATUS_NO_LOGON_SERVERS;
+					DBG_ERR("winbindd not running - "
+						"but required as domain member: "
+						"princ_name[%s] %s\n",
+						princ_name, nt_errstr(status));
+					goto done;
+				}
+				//FALL_THROUGH;
 			case WBC_ERR_SUCCESS:
 				break;
 			case WBC_ERR_AUTH_ERROR:
@@ -119,6 +129,11 @@ static NTSTATUS auth3_generate_session_info_pac(struct auth4_context *auth_ctx,
 		if (!NT_STATUS_IS_OK(status)) {
 			goto done;
 		}
+	} else if (require_pac) {
+		status = NT_STATUS_NO_IMPERSONATION_TOKEN;
+		DBG_WARNING("No PAC available for princ_name[%s] %s\n",
+			    princ_name, nt_errstr(status));
+		goto done;
 	}
 
 	rc = get_remote_hostname(remote_address,
@@ -157,6 +172,12 @@ static NTSTATUS auth3_generate_session_info_pac(struct auth4_context *auth_ctx,
 		if (!NT_STATUS_IS_OK(status)) {
 			goto done;
 		}
+	} else if (require_pac) {
+		status = NT_STATUS_NO_IMPERSONATION_TOKEN;
+		DBG_WARNING("No PAC LOGON_INFO available for "
+			    "princ_name[%s] %s\n",
+			    princ_name, nt_errstr(status));
+		goto done;
 	}
 
 	/* setup the string used by %U */
diff --git a/source3/auth/user_krb5.c b/source3/auth/user_krb5.c
index 8998f9c8f8ae..ca3d3714e624 100644
--- a/source3/auth/user_krb5.c
+++ b/source3/auth/user_krb5.c
@@ -193,8 +193,17 @@ NTSTATUS make_session_info_krb5(TALLOC_CTX *mem_ctx,
 {
 	NTSTATUS status;
 	struct auth_serversupplied_info *server_info;
+	bool require_pac = lp_require_pac();
 
 	if (mapped_to_guest) {
+		if (require_pac) {
+			status = NT_STATUS_BAD_TOKEN_TYPE;
+			DBG_WARNING("Mapped to guest for PAC required "
+				    "[%s\\%s] %s\n",
+				    ntdomain, ntuser, nt_errstr(status));
+			return status;
+		}
+
 		status = make_server_info_guest(mem_ctx, &server_info);
 		if (!NT_STATUS_IS_OK(status)) {
 			DEBUG(1, ("make_server_info_guest failed: %s!\n",
@@ -216,6 +225,12 @@ NTSTATUS make_session_info_krb5(TALLOC_CTX *mem_ctx,
 			return status;
 		}
 
+	} else if (require_pac) {
+		status = NT_STATUS_NO_IMPERSONATION_TOKEN;
+		DBG_WARNING("No INFO3 available for "
+			    "[%s\\%s] %s\n",
+			    ntdomain, ntuser, nt_errstr(status));
+		return status;
 	} else {
 		/*
 		 * We didn't get a PAC, we have to make up the user
-- 
2.17.1

From 3a4953ee1e7be48edb59882b3b95a5f079ada6a1 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Mon, 16 Sep 2019 06:58:16 +0200
Subject: [PATCH 07/25] auth3_generate_session_info_pac
 WBC_ERR_WINBIND_NOT_AVAILABLE:

---
 source3/auth/auth_generic.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/source3/auth/auth_generic.c b/source3/auth/auth_generic.c
index 88c6248e8b9d..6fddad54cdcb 100644
--- a/source3/auth/auth_generic.c
+++ b/source3/auth/auth_generic.c
@@ -101,14 +101,14 @@ static NTSTATUS auth3_generate_session_info_pac(struct auth4_context *auth_ctx,
 
 		switch (wbc_err) {
 			case WBC_ERR_WINBIND_NOT_AVAILABLE:
-				if (require_pac) {
-					status = NT_STATUS_NO_LOGON_SERVERS;
-					DBG_ERR("winbindd not running - "
-						"but required as domain member: "
-						"princ_name[%s] %s\n",
-						princ_name, nt_errstr(status));
-					goto done;
-				}
+				//if (require_pac) {
+				//	status = NT_STATUS_NO_LOGON_SERVERS;
+				//	DBG_ERR("winbindd not running - "
+				//		"but required as domain member: "
+				//		"princ_name[%s] %s\n",
+				//		princ_name, nt_errstr(status));
+				//	goto done;
+				//}
 				//FALL_THROUGH;
 			case WBC_ERR_SUCCESS:
 				break;
-- 
2.17.1

From b0dd91b1180a65b48765c7b70371628bc9731570 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Fri, 18 Aug 2017 15:33:17 +0200
Subject: [PATCH 08/25] UPSTREAM-FIRST!!! HEIMDAL:lib/krb5: add
 krb5_rd_req_in_set_verify_ap_req_flags()

In the next commits we want to be able to pass down
things like KRB5_VERIFY_AP_REQ_NO_TRANSITED_CHECK.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 source4/heimdal/lib/krb5/rd_req.c           | 18 ++++++++++++++++--
 source4/heimdal/lib/krb5/version-script.map |  1 +
 2 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/source4/heimdal/lib/krb5/rd_req.c b/source4/heimdal/lib/krb5/rd_req.c
index 21daeb596b55..76d5caadb318 100644
--- a/source4/heimdal/lib/krb5/rd_req.c
+++ b/source4/heimdal/lib/krb5/rd_req.c
@@ -519,6 +519,7 @@ struct krb5_rd_req_in_ctx_data {
     krb5_keytab keytab;
     krb5_keyblock *keyblock;
     krb5_boolean check_pac;
+    krb5_flags verify_ap_req_flags;
 };
 
 struct krb5_rd_req_out_ctx_data {
@@ -577,6 +578,15 @@ krb5_rd_req_in_set_keytab(krb5_context context,
     return 0;
 }
 
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rd_req_in_set_verify_ap_req_flags(krb5_context context,
+			     krb5_rd_req_in_ctx in,
+			     krb5_flags flags)
+{
+    in->verify_ap_req_flags = flags;
+    return 0;
+}
+
 /**
  * Set if krb5_rq_red() is going to check the Windows PAC or not
  *
@@ -850,6 +860,7 @@ krb5_rd_req_ctx(krb5_context context,
     krb5_rd_req_out_ctx o = NULL;
     krb5_keytab id = NULL, keytab = NULL;
     krb5_principal service = NULL;
+    krb5_flags verify_ap_req_flags = 0;
 
     *outctx = NULL;
 
@@ -890,6 +901,9 @@ krb5_rd_req_ctx(krb5_context context,
     if (inctx && inctx->keytab)
 	id = inctx->keytab;
 
+    if (inctx)
+        verify_ap_req_flags = inctx->verify_ap_req_flags;
+
     if((*auth_context)->keyblock){
 	ret = krb5_copy_keyblock(context,
 				 (*auth_context)->keyblock,
@@ -947,7 +961,7 @@ krb5_rd_req_ctx(krb5_context context,
 				  &ap_req,
 				  server,
 				  o->keyblock,
-				  0,
+				  verify_ap_req_flags,
 				  &o->ap_req_options,
 				  &o->ticket,
 				  KRB5_KU_AP_REQ_AUTH);
@@ -995,7 +1009,7 @@ krb5_rd_req_ctx(krb5_context context,
 				      &ap_req,
 				      server,
 				      &entry.keyblock,
-				      0,
+				      verify_ap_req_flags,
 				      &o->ap_req_options,
 				      &o->ticket,
 				      KRB5_KU_AP_REQ_AUTH);
diff --git a/source4/heimdal/lib/krb5/version-script.map b/source4/heimdal/lib/krb5/version-script.map
index ddae2a067645..7b49c15dfc0e 100644
--- a/source4/heimdal/lib/krb5/version-script.map
+++ b/source4/heimdal/lib/krb5/version-script.map
@@ -529,6 +529,7 @@ HEIMDAL_KRB5_2.0 {
 		krb5_rd_req_in_set_keyblock;
 		krb5_rd_req_in_set_keytab;
 		krb5_rd_req_in_set_pac_check;
+		krb5_rd_req_in_set_verify_ap_req_flags;
 		krb5_rd_req_out_ctx_free;
 		krb5_rd_req_out_get_ap_req_options;
 		krb5_rd_req_out_get_keyblock;
-- 
2.17.1

From 853f3737800f5a84c1a617075522fbd61759c6d5 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Fri, 18 Aug 2017 15:33:17 +0200
Subject: [PATCH 09/25] UPSTREAM-FIRST!!! HEIMDAL:lib/krb5: add
 KRB5_VERIFY_AP_REQ_NO_TRANSITED_CHECK

In active directory a domain member replies on (trusts) the [K]DCs
of the domain. It's the job of the [K]DCs to only generate useful
tickets as they know about the trust topology.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 source4/heimdal/lib/krb5/krb5.h   | 1 +
 source4/heimdal/lib/krb5/rd_req.c | 3 ++-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/source4/heimdal/lib/krb5/krb5.h b/source4/heimdal/lib/krb5/krb5.h
index 9c0f5669466c..03119fa78321 100644
--- a/source4/heimdal/lib/krb5/krb5.h
+++ b/source4/heimdal/lib/krb5/krb5.h
@@ -422,6 +422,7 @@ typedef union {
 /* flags for krb5_verify_ap_req */
 
 #define KRB5_VERIFY_AP_REQ_IGNORE_INVALID	(1 << 0)
+#define KRB5_VERIFY_AP_REQ_NO_TRANSITED_CHECK	(1 << 1)
 
 #define KRB5_GC_CACHED			(1U << 0)
 #define KRB5_GC_USER_USER		(1U << 1)
diff --git a/source4/heimdal/lib/krb5/rd_req.c b/source4/heimdal/lib/krb5/rd_req.c
index 76d5caadb318..1a1c31d8d8ba 100644
--- a/source4/heimdal/lib/krb5/rd_req.c
+++ b/source4/heimdal/lib/krb5/rd_req.c
@@ -253,7 +253,8 @@ krb5_decrypt_ticket(krb5_context context,
 	    return KRB5KRB_AP_ERR_TKT_EXPIRED;
 	}
 
-	if(!t.flags.transited_policy_checked) {
+	if(!t.flags.transited_policy_checked
+	   && !(flags & KRB5_VERIFY_AP_REQ_NO_TRANSITED_CHECK)) {
 	    ret = check_transited(context, ticket, &t);
 	    if(ret) {
 		free_EncTicketPart(&t);
-- 
2.17.1

From 75f03351e6f289a81d570c3c6989166073af218f Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Fri, 18 Aug 2017 15:33:17 +0200
Subject: [PATCH 10/25] UPSTREAM-FIRST!!! HEIMDAL:lib/gssapi/krb5: add
 GSS_KRB5_CRED_NO_TRANSIT_CHECK_X

This allows KRB5_GC_NO_TRANSIT_CHECK (on the initiator) and
KRB5_VERIFY_AP_REQ_NO_TRANSITED_CHECK (on the acceptor) to
be controlled via the gssapi layer.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 .../heimdal/lib/gssapi/gssapi/gssapi_oid.h    |  3 +++
 .../lib/gssapi/krb5/accept_sec_context.c      | 11 ++++++++-
 .../heimdal/lib/gssapi/krb5/gsskrb5_locl.h    |  1 +
 .../lib/gssapi/krb5/init_sec_context.c        | 16 ++++++++-----
 .../heimdal/lib/gssapi/krb5/set_cred_option.c | 24 +++++++++++++++++++
 source4/heimdal/lib/gssapi/mech/gss_oid.c     |  3 +++
 source4/heimdal/lib/gssapi/version-script.map |  1 +
 7 files changed, 52 insertions(+), 7 deletions(-)

diff --git a/source4/heimdal/lib/gssapi/gssapi/gssapi_oid.h b/source4/heimdal/lib/gssapi/gssapi/gssapi_oid.h
index 3d00c060d10d..0eed639d53e1 100644
--- a/source4/heimdal/lib/gssapi/gssapi/gssapi_oid.h
+++ b/source4/heimdal/lib/gssapi/gssapi/gssapi_oid.h
@@ -96,6 +96,9 @@ extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_c_ntlm_force_v1_oid_desc;
 extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_krb5_cred_no_ci_flags_x_oid_desc;
 #define GSS_KRB5_CRED_NO_CI_FLAGS_X (&__gss_krb5_cred_no_ci_flags_x_oid_desc)
 
+extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_krb5_cred_no_transit_check_x_oid_desc;
+#define GSS_KRB5_CRED_NO_TRANSIT_CHECK_X (&__gss_krb5_cred_no_transit_check_x_oid_desc)
+
 extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_krb5_import_cred_x_oid_desc;
 #define GSS_KRB5_IMPORT_CRED_X (&__gss_krb5_import_cred_x_oid_desc)
 
diff --git a/source4/heimdal/lib/gssapi/krb5/accept_sec_context.c b/source4/heimdal/lib/gssapi/krb5/accept_sec_context.c
index cfe27ace875e..2e4ed53ba08e 100644
--- a/source4/heimdal/lib/gssapi/krb5/accept_sec_context.c
+++ b/source4/heimdal/lib/gssapi/krb5/accept_sec_context.c
@@ -398,13 +398,22 @@ gsskrb5_acceptor_start(OM_uint32 * minor_status,
 	krb5_rd_req_in_ctx in = NULL;
 	krb5_rd_req_out_ctx out = NULL;
 	krb5_principal server = NULL;
+	krb5_flags verify_ap_req_flags = 0;
 
-	if (acceptor_cred)
+	if (acceptor_cred) {
 	    server = acceptor_cred->principal;
 
+	    if (acceptor_cred->cred_flags & GSS_CF_NO_TRANSIT_CHECK) {
+		verify_ap_req_flags |= KRB5_VERIFY_AP_REQ_NO_TRANSITED_CHECK;
+	    }
+	}
+
 	kret = krb5_rd_req_in_ctx_alloc(context, &in);
 	if (kret == 0)
 	    kret = krb5_rd_req_in_set_keytab(context, in, keytab);
+	if (kret == 0)
+	    kret = krb5_rd_req_in_set_verify_ap_req_flags(context, in,
+							  verify_ap_req_flags);
 	if (kret) {
 	    if (in)
 		krb5_rd_req_in_ctx_free(context, in);
diff --git a/source4/heimdal/lib/gssapi/krb5/gsskrb5_locl.h b/source4/heimdal/lib/gssapi/krb5/gsskrb5_locl.h
index 6b9b03f34908..6a1e977452cf 100644
--- a/source4/heimdal/lib/gssapi/krb5/gsskrb5_locl.h
+++ b/source4/heimdal/lib/gssapi/krb5/gsskrb5_locl.h
@@ -94,6 +94,7 @@ typedef struct {
   int cred_flags;
 #define GSS_CF_DESTROY_CRED_ON_RELEASE	1
 #define GSS_CF_NO_CI_FLAGS		2
+#define GSS_CF_NO_TRANSIT_CHECK		4
   struct krb5_keytab_data *keytab;
   OM_uint32 lifetime;
   gss_cred_usage_t usage;
diff --git a/source4/heimdal/lib/gssapi/krb5/init_sec_context.c b/source4/heimdal/lib/gssapi/krb5/init_sec_context.c
index 0a89ae1f36cf..2f55f27000c2 100644
--- a/source4/heimdal/lib/gssapi/krb5/init_sec_context.c
+++ b/source4/heimdal/lib/gssapi/krb5/init_sec_context.c
@@ -205,6 +205,7 @@ gsskrb5_get_creds(
         OM_uint32 * minor_status,
 	krb5_context context,
 	krb5_ccache ccache,
+	krb5_flags options,
 	gsskrb5_ctx ctx,
 	const gss_name_t target_name,
 	int use_dns,
@@ -246,7 +247,7 @@ gsskrb5_get_creds(
     this_cred.session.keytype = KEYTYPE_NULL;
 
     kret = krb5_get_credentials(context,
-				0,
+				options,
 				ccache,
 				&this_cred,
 				&ctx->kcred);
@@ -397,6 +398,7 @@ init_auth
     krb5_data fwd_data;
     OM_uint32 lifetime_rec;
     int allow_dns = 1;
+    krb5_flags options = 0;
 
     krb5_data_zero(&outbuf);
     krb5_data_zero(&fwd_data);
@@ -414,8 +416,12 @@ init_auth
 	    goto failure;
 	}
 	ctx->more_flags |= CLOSE_CCACHE;
-    } else
+    } else {
 	ctx->ccache = cred->ccache;
+	if (cred->cred_flags & GSS_CF_NO_TRANSIT_CHECK) {
+	    options |= KRB5_GC_NO_TRANSIT_CHECK;
+	}
+    }
 
     kret = krb5_cc_get_principal (context, ctx->ccache, &ctx->source);
     if (kret) {
@@ -455,12 +461,10 @@ init_auth
      * DNS canonicalizion.
      */
     ret = gsskrb5_get_creds(minor_status, context, ctx->ccache,
-			    ctx, name, 0, time_req,
-			    time_rec);
+			    options, ctx, name, 0, time_req, time_rec);
     if (ret && allow_dns)
 	ret = gsskrb5_get_creds(minor_status, context, ctx->ccache,
-				ctx, name, 1, time_req,
-				time_rec);
+				options, ctx, name, 1, time_req, time_rec);
     if (ret)
 	goto failure;
 
diff --git a/source4/heimdal/lib/gssapi/krb5/set_cred_option.c b/source4/heimdal/lib/gssapi/krb5/set_cred_option.c
index bd3871675109..8d9f49884884 100644
--- a/source4/heimdal/lib/gssapi/krb5/set_cred_option.c
+++ b/source4/heimdal/lib/gssapi/krb5/set_cred_option.c
@@ -216,6 +216,26 @@ no_ci_flags(OM_uint32 *minor_status,
 }

+static OM_uint32
+no_transit_check(OM_uint32 *minor_status,
+		 krb5_context context,
+		 gss_cred_id_t *cred_handle,
+		 const gss_buffer_t value)
+{
+    gsskrb5_cred cred;
+
+    if (cred_handle == NULL || *cred_handle == GSS_C_NO_CREDENTIAL) {
+	*minor_status = 0;
+	return GSS_S_FAILURE;
+    }
+
+    cred = (gsskrb5_cred)*cred_handle;
+    cred->cred_flags |= GSS_CF_NO_TRANSIT_CHECK;
+
+    *minor_status = 0;
+    return GSS_S_COMPLETE;
+}
+
 OM_uint32 GSSAPI_CALLCONV
 _gsskrb5_set_cred_option
            (OM_uint32 *minor_status,
@@ -243,6 +263,10 @@ _gsskrb5_set_cred_option
     }

+    if (gss_oid_equal(desired_object, GSS_KRB5_CRED_NO_TRANSIT_CHECK_X)) {
+	return no_transit_check(minor_status, context, cred_handle, value);
+    }
+
     *minor_status = EINVAL;
     return GSS_S_FAILURE;
 }
diff --git a/source4/heimdal/lib/gssapi/mech/gss_oid.c b/source4/heimdal/lib/gssapi/mech/gss_oid.c
index 916d1e4dda5e..6f86eec65c39 100644
--- a/source4/heimdal/lib/gssapi/mech/gss_oid.c
+++ b/source4/heimdal/lib/gssapi/mech/gss_oid.c
@@ -94,6 +94,9 @@ gss_OID_desc GSSAPI_LIB_VARIABLE __gss_krb5_cred_no_ci_flags_x_oid_desc = { 6, r
 /* GSS_KRB5_IMPORT_CRED_X - 1.2.752.43.13.30 */
 gss_OID_desc GSSAPI_LIB_VARIABLE __gss_krb5_import_cred_x_oid_desc = { 6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x1e") };
 
+/* GSS_KRB5_CRED_NO_TRANSIT_CHECK_X - 1.2.752.43.13.31 */
+gss_OID_desc GSSAPI_LIB_VARIABLE __gss_krb5_cred_no_transit_check_x_oid_desc = { 6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x1f") };
+
 /* GSS_C_MA_SASL_MECH_NAME - 1.2.752.43.13.100 */
 gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_ma_sasl_mech_name_oid_desc = { 6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x64") };
 
diff --git a/source4/heimdal/lib/gssapi/version-script.map b/source4/heimdal/lib/gssapi/version-script.map
index bcb79bf8f76e..776fe19dbf3c 100644
--- a/source4/heimdal/lib/gssapi/version-script.map
+++ b/source4/heimdal/lib/gssapi/version-script.map
@@ -154,6 +154,7 @@ HEIMDAL_GSS_2.0 {
 		__gss_c_ntlm_session_key_oid_desc;
 		__gss_c_ntlm_force_v1_oid_desc;
 		__gss_krb5_cred_no_ci_flags_x_oid_desc;
+		__gss_krb5_cred_no_transit_check_x_oid_desc;
 		__gss_krb5_import_cred_x_oid_desc;
 		__gss_c_ma_sasl_mech_name_oid_desc;
 		__gss_c_ma_mech_name_oid_desc;
-- 
2.17.1

From f2275c528ef5f4a5482356f55244cc84e5ad24b1 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Sat, 20 Jul 2019 10:15:04 +0000
Subject: [PATCH 11/25] UPSTREAM-FIRST!!! HEIMDAL:lib/krb5: let
 krb5_rd_req_ctx() fallback only on KRB5KRB_AP_ERR_BAD_INTEGRITY

This avoids hidding a real error like KRB5KRB_AP_ERR_ILL_CR_TKT.

We only want to retry with the next key if the decryption
failed.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=14125

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 source4/heimdal/lib/krb5/rd_req.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/source4/heimdal/lib/krb5/rd_req.c b/source4/heimdal/lib/krb5/rd_req.c
index 1a1c31d8d8ba..aef42d8b4ffc 100644
--- a/source4/heimdal/lib/krb5/rd_req.c
+++ b/source4/heimdal/lib/krb5/rd_req.c
@@ -1014,10 +1014,15 @@ krb5_rd_req_ctx(krb5_context context,
 				      &o->ap_req_options,
 				      &o->ticket,
 				      KRB5_KU_AP_REQ_AUTH);
-	    if (ret) {
+	    if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
+		/* failed to decrypt, try the next key */
 		krb5_kt_free_entry (context, &entry);
 		continue;
 	    }
+	    if (ret) {
+		krb5_kt_free_entry (context, &entry);
+		goto out;
+	    }
 
 	    /*
 	     * Found a match, save the keyblock for PAC processing,
-- 
2.17.1

From 2e8a62b2975fdd6fc6d3aee5ad9f0a0379d0efb2 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Sat, 20 Jul 2019 10:15:04 +0000
Subject: [PATCH 12/25] UPSTREAM-FIRST!!! HEIMDAL:lib/krb5: add
 krb5_rd_req_in_set_iterate_keytab()

A caller might not know the kvno maintained by the KDC.
And most often there's need to know it.

So this function makes it possible to force the keytab
iteration in order to get a consistent behavior.
Otherwise it's possible to get a different behavior
if the guessed kvno in the keytab accidentally matches
the kvno of the ticket and we'll give up if the
key is not able to decrypt the ticket.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=14125

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 source4/heimdal/lib/krb5/rd_req.c           | 27 +++++++++++++++++++++
 source4/heimdal/lib/krb5/version-script.map |  1 +
 2 files changed, 28 insertions(+)

diff --git a/source4/heimdal/lib/krb5/rd_req.c b/source4/heimdal/lib/krb5/rd_req.c
index aef42d8b4ffc..4ea3cee959dc 100644
--- a/source4/heimdal/lib/krb5/rd_req.c
+++ b/source4/heimdal/lib/krb5/rd_req.c
@@ -518,6 +518,7 @@ krb5_verify_ap_req2(krb5_context context,
 
 struct krb5_rd_req_in_ctx_data {
     krb5_keytab keytab;
+    krb5_boolean iterate_keytab;
     krb5_keyblock *keyblock;
     krb5_boolean check_pac;
     krb5_flags verify_ap_req_flags;
@@ -588,6 +589,27 @@ krb5_rd_req_in_set_verify_ap_req_flags(krb5_context context,
     return 0;
 }
 
+/**
+ * Set if krb5_rq_red() is going to iterate the keytab to find a key
+ *
+ * @param context Keberos 5 context.
+ * @param in krb5_rd_req_in_ctx to check the option on.
+ * @param flag flag to select if the keytab should be iterated (TRUE) or not (FALSE).
+ *
+ * @return Kerberos 5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_auth
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rd_req_in_set_iterate_keytab(krb5_context context,
+				  krb5_rd_req_in_ctx in,
+				  krb5_boolean flag)
+{
+    in->iterate_keytab = flag;
+    return 0;
+}
+
 /**
  * Set if krb5_rq_red() is going to check the Windows PAC or not
  *
@@ -917,6 +939,11 @@ krb5_rd_req_ctx(krb5_context context,
 				 &o->keyblock);
 	if (ret)
 	    goto out;
+    } else if (id && inctx && inctx->iterate_keytab) {
+	    /*
+	     * Force iterating over the keytab.
+	     */
+	    o->keyblock = NULL;
     } else {
 
 	if(id == NULL) {
diff --git a/source4/heimdal/lib/krb5/version-script.map b/source4/heimdal/lib/krb5/version-script.map
index 7b49c15dfc0e..b18a1c5d7375 100644
--- a/source4/heimdal/lib/krb5/version-script.map
+++ b/source4/heimdal/lib/krb5/version-script.map
@@ -526,6 +526,7 @@ HEIMDAL_KRB5_2.0 {
 		krb5_rd_req_ctx;
 		krb5_rd_req_in_ctx_alloc;
 		krb5_rd_req_in_ctx_free;
+		krb5_rd_req_in_set_iterate_keytab;
 		krb5_rd_req_in_set_keyblock;
 		krb5_rd_req_in_set_keytab;
 		krb5_rd_req_in_set_pac_check;
-- 
2.17.1

From d6a9a47c72cbee78c3b97c36d7db9977655e4bcd Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Thu, 22 Aug 2019 10:30:01 +0200
Subject: [PATCH 13/25] UPSTREAM-FIRST!!! HEIMDAL:lib/gssapi/krb5: add
 GSS_KRB5_CRED_ITERATE_ACCEPTOR_KEYTAB_X

This allows krb5_rd_req_in_set_iterate_keytab() to be used via the
gssapi layer.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=14125

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 .../heimdal/lib/gssapi/gssapi/gssapi_oid.h    |  3 +++
 .../lib/gssapi/krb5/accept_sec_context.c      |  7 ++++++
 .../heimdal/lib/gssapi/krb5/gsskrb5_locl.h    |  1 +
 .../heimdal/lib/gssapi/krb5/set_cred_option.c | 22 +++++++++++++++++++
 source4/heimdal/lib/gssapi/mech/gss_oid.c     |  3 +++
 source4/heimdal/lib/gssapi/version-script.map |  1 +
 6 files changed, 37 insertions(+)

diff --git a/source4/heimdal/lib/gssapi/gssapi/gssapi_oid.h b/source4/heimdal/lib/gssapi/gssapi/gssapi_oid.h
index 0eed639d53e1..48ff4035d854 100644
--- a/source4/heimdal/lib/gssapi/gssapi/gssapi_oid.h
+++ b/source4/heimdal/lib/gssapi/gssapi/gssapi_oid.h
@@ -99,6 +99,9 @@ extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_krb5_cred_no_ci_flags_x_oid_desc;
 extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_krb5_cred_no_transit_check_x_oid_desc;
 #define GSS_KRB5_CRED_NO_TRANSIT_CHECK_X (&__gss_krb5_cred_no_transit_check_x_oid_desc)
 
+extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_krb5_cred_iterate_acceptor_keytab_x_oid_desc;
+#define GSS_KRB5_CRED_ITERATE_ACCEPTOR_KEYTAB_X (&__gss_krb5_cred_iterate_acceptor_keytab_x_oid_desc)
+
 extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_krb5_import_cred_x_oid_desc;
 #define GSS_KRB5_IMPORT_CRED_X (&__gss_krb5_import_cred_x_oid_desc)
 
diff --git a/source4/heimdal/lib/gssapi/krb5/accept_sec_context.c b/source4/heimdal/lib/gssapi/krb5/accept_sec_context.c
index 2e4ed53ba08e..d5865e5bdd71 100644
--- a/source4/heimdal/lib/gssapi/krb5/accept_sec_context.c
+++ b/source4/heimdal/lib/gssapi/krb5/accept_sec_context.c
@@ -398,11 +398,16 @@ gsskrb5_acceptor_start(OM_uint32 * minor_status,
 	krb5_rd_req_in_ctx in = NULL;
 	krb5_rd_req_out_ctx out = NULL;
 	krb5_principal server = NULL;
+	krb5_boolean iterate_keytab = FALSE;
 	krb5_flags verify_ap_req_flags = 0;
 
 	if (acceptor_cred) {
 	    server = acceptor_cred->principal;
 
+	    if (acceptor_cred->cred_flags & GSS_CF_ITERATE_ACCEPTOR_KEYTAB) {
+		iterate_keytab = TRUE;
+	    }
+
 	    if (acceptor_cred->cred_flags & GSS_CF_NO_TRANSIT_CHECK) {
 		verify_ap_req_flags |= KRB5_VERIFY_AP_REQ_NO_TRANSITED_CHECK;
 	    }
@@ -411,6 +416,8 @@ gsskrb5_acceptor_start(OM_uint32 * minor_status,
 	kret = krb5_rd_req_in_ctx_alloc(context, &in);
 	if (kret == 0)
 	    kret = krb5_rd_req_in_set_keytab(context, in, keytab);
+	if (kret == 0 && iterate_keytab)
+	    kret = krb5_rd_req_in_set_iterate_keytab(context, in, TRUE);
 	if (kret == 0)
 	    kret = krb5_rd_req_in_set_verify_ap_req_flags(context, in,
 							  verify_ap_req_flags);
diff --git a/source4/heimdal/lib/gssapi/krb5/gsskrb5_locl.h b/source4/heimdal/lib/gssapi/krb5/gsskrb5_locl.h
index 6a1e977452cf..00b6f3405e29 100644
--- a/source4/heimdal/lib/gssapi/krb5/gsskrb5_locl.h
+++ b/source4/heimdal/lib/gssapi/krb5/gsskrb5_locl.h
@@ -95,6 +95,7 @@ typedef struct {
 #define GSS_CF_DESTROY_CRED_ON_RELEASE	1
 #define GSS_CF_NO_CI_FLAGS		2
 #define GSS_CF_NO_TRANSIT_CHECK		4
+#define GSS_CF_ITERATE_ACCEPTOR_KEYTAB	8
   struct krb5_keytab_data *keytab;
   OM_uint32 lifetime;
   gss_cred_usage_t usage;
diff --git a/source4/heimdal/lib/gssapi/krb5/set_cred_option.c b/source4/heimdal/lib/gssapi/krb5/set_cred_option.c
index 8d9f49884884..9ad17bfc322c 100644
--- a/source4/heimdal/lib/gssapi/krb5/set_cred_option.c
+++ b/source4/heimdal/lib/gssapi/krb5/set_cred_option.c
@@ -215,6 +215,25 @@ no_ci_flags(OM_uint32 *minor_status,
 
 }
 
+static OM_uint32
+iterate_acceptor_keytab(OM_uint32 *minor_status,
+			krb5_context context,
+			gss_cred_id_t *cred_handle,
+			const gss_buffer_t value)
+{
+    gsskrb5_cred cred;
+
+    if (cred_handle == NULL || *cred_handle == GSS_C_NO_CREDENTIAL) {
+	*minor_status = 0;
+	return GSS_S_FAILURE;
+    }
+
+    cred = (gsskrb5_cred)*cred_handle;
+    cred->cred_flags |= GSS_CF_ITERATE_ACCEPTOR_KEYTAB;
+
+    *minor_status = 0;
+    return GSS_S_COMPLETE;
+}
 
 static OM_uint32
 no_transit_check(OM_uint32 *minor_status,
@@ -262,6 +281,9 @@ _gsskrb5_set_cred_option
 	return no_ci_flags(minor_status, context, cred_handle, value);
     }
 
+    if (gss_oid_equal(desired_object, GSS_KRB5_CRED_ITERATE_ACCEPTOR_KEYTAB_X)) {
+	return iterate_acceptor_keytab(minor_status, context, cred_handle, value);
+    }
 
     if (gss_oid_equal(desired_object, GSS_KRB5_CRED_NO_TRANSIT_CHECK_X)) {
 	return no_transit_check(minor_status, context, cred_handle, value);
diff --git a/source4/heimdal/lib/gssapi/mech/gss_oid.c b/source4/heimdal/lib/gssapi/mech/gss_oid.c
index 6f86eec65c39..de4ed2e571f3 100644
--- a/source4/heimdal/lib/gssapi/mech/gss_oid.c
+++ b/source4/heimdal/lib/gssapi/mech/gss_oid.c
@@ -97,6 +97,9 @@ gss_OID_desc GSSAPI_LIB_VARIABLE __gss_krb5_import_cred_x_oid_desc = { 6, rk_UNC
 /* GSS_KRB5_CRED_NO_TRANSIT_CHECK_X - 1.2.752.43.13.31 */
 gss_OID_desc GSSAPI_LIB_VARIABLE __gss_krb5_cred_no_transit_check_x_oid_desc = { 6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x1f") };
 
+/* GSS_KRB5_CRED_ITERATE_ACCEPTOR_KEYTAB_X - 1.2.752.43.13.31 */
+gss_OID_desc GSSAPI_LIB_VARIABLE __gss_krb5_cred_iterate_acceptor_keytab_x_oid_desc = { 6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x20") };
+
 /* GSS_C_MA_SASL_MECH_NAME - 1.2.752.43.13.100 */
 gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_ma_sasl_mech_name_oid_desc = { 6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x64") };
 
diff --git a/source4/heimdal/lib/gssapi/version-script.map b/source4/heimdal/lib/gssapi/version-script.map
index 776fe19dbf3c..6fcfce3ec5ea 100644
--- a/source4/heimdal/lib/gssapi/version-script.map
+++ b/source4/heimdal/lib/gssapi/version-script.map
@@ -153,6 +153,7 @@ HEIMDAL_GSS_2.0 {
 		__gss_c_ntlm_v2_oid_desc;
 		__gss_c_ntlm_session_key_oid_desc;
 		__gss_c_ntlm_force_v1_oid_desc;
+		__gss_krb5_cred_iterate_acceptor_keytab_x_oid_desc;
 		__gss_krb5_cred_no_ci_flags_x_oid_desc;
 		__gss_krb5_cred_no_transit_check_x_oid_desc;
 		__gss_krb5_import_cred_x_oid_desc;
-- 
2.17.1

From 19f92c6fcf287e991e43969681c730805c6e69b1 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Thu, 22 Aug 2019 16:09:47 +0000
Subject: [PATCH 14/25] s4:heimdal_build: define
 HAVE_GSS_KRB5_CRED_{NO_TRANSIT_CHECK,ITERATE_ACCEPTOR_KEYTAB}_X

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14125

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 source4/heimdal_build/wscript_configure | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/source4/heimdal_build/wscript_configure b/source4/heimdal_build/wscript_configure
index 903fb5139db8..57658b577a1a 100644
--- a/source4/heimdal_build/wscript_configure
+++ b/source4/heimdal_build/wscript_configure
@@ -87,6 +87,8 @@ conf.define('HAVE_GSS_KRB5_EXPORT_LUCID_SEC_CONTEXT', 1)
 conf.define('HAVE_GSS_IMPORT_CRED', 1)
 conf.define('HAVE_GSS_EXPORT_CRED', 1)
 conf.define('HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X', 1)
+conf.define('HAVE_GSS_KRB5_CRED_NO_TRANSIT_CHECK_X', 1)
+conf.define('HAVE_GSS_KRB5_CRED_ITERATE_ACCEPTOR_KEYTAB_X', 1)
 conf.define('HAVE_GSSAPI', 1)
 conf.define('HAVE_ADDR_TYPE_IN_KRB5_ADDRESS', 1)
 conf.define('HAVE_CHECKSUM_IN_KRB5_CHECKSUM', 1)
-- 
2.17.1

From c58950ecc242656c77b66ec8865d0f3e4dc4f293 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Thu, 22 Aug 2019 16:09:47 +0000
Subject: [PATCH 15/25] configure_mitkrb5: check for
 GSS_KRB5_CRED_NO_TRANSIT_CHECK_X

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 wscript_configure_system_mitkrb5 | 1 +
 1 file changed, 1 insertion(+)

diff --git a/wscript_configure_system_mitkrb5 b/wscript_configure_system_mitkrb5
index b05ac3f3e50c..bad6ce9a95d6 100644
--- a/wscript_configure_system_mitkrb5
+++ b/wscript_configure_system_mitkrb5
@@ -115,6 +115,7 @@ conf.CHECK_FUNCS_IN('''
        gss_acquire_cred_from
        ''', 'gssapi gssapi_krb5')
 conf.CHECK_VARIABLE('GSS_KRB5_CRED_NO_CI_FLAGS_X', headers=possible_gssapi_headers)
+conf.CHECK_VARIABLE('GSS_KRB5_CRED_NO_TRANSIT_CHECK_X', headers=possible_gssapi_headers)
 conf.CHECK_FUNCS_IN('krb5_mk_req_extended krb5_kt_compare', 'krb5')
 conf.CHECK_FUNCS('''
        krb5_auth_con_getrecvsubkey
-- 
2.17.1

From a5d0cdb8a6187005ecf85b50452b61feb7e130a9 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Thu, 22 Aug 2019 16:51:49 +0000
Subject: [PATCH 16/25] s3:gse: add helper define __TMP_NEED_EMPTY_BUFFFER

This will simplify the following commits.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14125

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 source3/librpc/crypto/gse.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/source3/librpc/crypto/gse.c b/source3/librpc/crypto/gse.c
index 9a9f4261222f..343e0f581735 100644
--- a/source3/librpc/crypto/gse.c
+++ b/source3/librpc/crypto/gse.c
@@ -256,9 +256,12 @@ static NTSTATUS gse_init_client(TALLOC_CTX *mem_ctx,
 	struct gse_context *gse_ctx;
 	OM_uint32 gss_maj, gss_min;
 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
-	gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
 	gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
-#endif
+#define __TMP_NEED_EMPTY_BUFFFER 1
+#endif /* HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X */
+#ifdef __TMP_NEED_EMPTY_BUFFFER
+	gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
+#endif /* __TMP_NEED_EMPTY_BUFFFER */
 	NTSTATUS status;
 
 	if (!server || !service) {
-- 
2.17.1

From fad297a2696f54b4ad581fbeb4a73bde9b84100d Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Thu, 22 Aug 2019 16:51:49 +0000
Subject: [PATCH 17/25] s3:gse: rename oid to no_ci_flags_oid

This will simplify the following commits.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14125

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 source3/librpc/crypto/gse.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/source3/librpc/crypto/gse.c b/source3/librpc/crypto/gse.c
index 343e0f581735..25aac7e0448f 100644
--- a/source3/librpc/crypto/gse.c
+++ b/source3/librpc/crypto/gse.c
@@ -256,7 +256,7 @@ static NTSTATUS gse_init_client(TALLOC_CTX *mem_ctx,
 	struct gse_context *gse_ctx;
 	OM_uint32 gss_maj, gss_min;
 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
-	gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
+	gss_OID no_ci_flags_oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
 #define __TMP_NEED_EMPTY_BUFFFER 1
 #endif /* HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X */
 #ifdef __TMP_NEED_EMPTY_BUFFFER
@@ -314,7 +314,7 @@ static NTSTATUS gse_init_client(TALLOC_CTX *mem_ctx,
 	 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
 	 */
 	gss_maj = gss_set_cred_option(&gss_min, &gse_ctx->creds,
-				      oid,
+				      no_ci_flags_oid,
 				      &empty_buffer);
 	if (gss_maj) {
 		DEBUG(0, ("gss_set_cred_option(GSS_KRB5_CRED_NO_CI_FLAGS_X), "
-- 
2.17.1

From 548f527f3fa0dcc718288decc6a81f1d609d0168 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Thu, 22 Aug 2019 16:51:49 +0000
Subject: [PATCH 18/25] s3:gse: let gse_init_client() use
 GSS_KRB5_CRED_NO_TRANSIT_CHECK_X

There no need to do any possible transit checking on
the client side.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 source3/librpc/crypto/gse.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/source3/librpc/crypto/gse.c b/source3/librpc/crypto/gse.c
index 25aac7e0448f..dc074d1ae1ae 100644
--- a/source3/librpc/crypto/gse.c
+++ b/source3/librpc/crypto/gse.c
@@ -259,6 +259,12 @@ static NTSTATUS gse_init_client(TALLOC_CTX *mem_ctx,
 	gss_OID no_ci_flags_oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
 #define __TMP_NEED_EMPTY_BUFFFER 1
 #endif /* HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X */
+#ifdef HAVE_GSS_KRB5_CRED_NO_TRANSIT_CHECK_X
+	gss_OID no_transit_oid = discard_const(GSS_KRB5_CRED_NO_TRANSIT_CHECK_X);
+#ifndef __TMP_NEED_EMPTY_BUFFFER
+#define __TMP_NEED_EMPTY_BUFFFER 1
+#endif /* __TMP_NEED_EMPTY_BUFFFER */
+#endif /* HAVE_GSS_KRB5_CRED_NO_TRANSIT_CHECK_X */
 #ifdef __TMP_NEED_EMPTY_BUFFFER
 	gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
 #endif /* __TMP_NEED_EMPTY_BUFFFER */
@@ -325,6 +331,19 @@ static NTSTATUS gse_init_client(TALLOC_CTX *mem_ctx,
 	}
 #endif
 
+#ifdef HAVE_GSS_KRB5_CRED_NO_TRANSIT_CHECK_X
+	gss_maj = gss_set_cred_option(&gss_min, &gse_ctx->creds,
+				      no_transit_oid,
+				      &empty_buffer);
+	if (gss_maj) {
+		DEBUG(0, ("gss_set_cred_option(GSS_KRB5_CRED_NO_TRANSIT_CHECK_X), "
+			  "failed with [%s]\n",
+			  gse_errstr(gse_ctx, gss_maj, gss_min)));
+		status = NT_STATUS_INTERNAL_ERROR;
+		goto err_out;
+	}
+#endif
+
 	*_gse_ctx = gse_ctx;
 	return NT_STATUS_OK;
 
-- 
2.17.1

From dc7ae1a365d0a176a13b147ed6c117a9dd142a63 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Thu, 22 Aug 2019 16:52:15 +0000
Subject: [PATCH 19/25] krb5_wrap: add smb_gss_krb5_prepare_acceptor_cred()

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14125

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 lib/krb5_wrap/gss_samba.c | 45 +++++++++++++++++++++++++++++++++++++++
 lib/krb5_wrap/gss_samba.h |  4 ++++
 2 files changed, 49 insertions(+)

diff --git a/lib/krb5_wrap/gss_samba.c b/lib/krb5_wrap/gss_samba.c
index 2a99661ddee2..2d3016088ed8 100644
--- a/lib/krb5_wrap/gss_samba.c
+++ b/lib/krb5_wrap/gss_samba.c
@@ -210,5 +210,50 @@ uint32_t smb_gss_krb5_import_cred(uint32_t *minor_status, krb5_context ctx,
 	return major_status;
 }
 
+uint32_t smb_gss_krb5_prepare_acceptor_cred(uint32_t *minor_status,
+					    bool require_pac,
+					    gss_cred_id_t *cred)
+{
+#ifdef HAVE_GSS_KRB5_CRED_NO_TRANSIT_CHECK_X
+	OM_uint32 gss_maj, gss_min;
+	gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
+	gss_OID no_transit_oid = discard_const(GSS_KRB5_CRED_NO_TRANSIT_CHECK_X);
+#ifdef HAVE_GSS_KRB5_CRED_ITERATE_ACCEPTOR_KEYTAB_X /* only heimdal */
+	gss_OID iterate_keytab_oid =
+		discard_const(GSS_KRB5_CRED_ITERATE_ACCEPTOR_KEYTAB_X);
+
+	gss_maj = gss_set_cred_option(&gss_min, cred,
+				      iterate_keytab_oid,
+				      &empty_buffer);
+	if (gss_maj) {
+		DBG_ERR("gss_set_cred_option(ITERATE_ACCEPTOR_KEYTAB_X)\n");
+		*minor_status = gss_min;
+		return gss_maj;
+	}
+#endif /* HAVE_GSS_KRB5_CRED_ITERATE_ACCEPTOR_KEYTAB_X */
+
+	if (!require_pac) {
+		goto done;
+	}
+
+	/*
+	 * If we require a valid PAC we can
+	 * skip the transit checks in the krb5
+	 * code.
+	 */
+	gss_maj = gss_set_cred_option(&gss_min, cred,
+				      no_transit_oid,
+				      &empty_buffer);
+	if (gss_maj) {
+		DBG_ERR("gss_set_cred_option(NO_TRANSIT_CHECK_X)\n");
+		*minor_status = gss_min;
+		return gss_maj;
+	}
+
+done:
+#endif /* HAVE_GSS_KRB5_CRED_NO_TRANSIT_CHECK_X */
+	*minor_status = 0;
+	return 0;
+}
 
 #endif /* HAVE_GSSAPI */
diff --git a/lib/krb5_wrap/gss_samba.h b/lib/krb5_wrap/gss_samba.h
index 89aee3479c55..77c8758ed3d8 100644
--- a/lib/krb5_wrap/gss_samba.h
+++ b/lib/krb5_wrap/gss_samba.h
@@ -45,5 +45,9 @@ uint32_t smb_gss_krb5_import_cred(OM_uint32 *minor_status, krb5_context ctx,
 				  krb5_ccache id, krb5_principal keytab_principal,
 				  krb5_keytab keytab, gss_cred_id_t *cred);
 
+uint32_t smb_gss_krb5_prepare_acceptor_cred(uint32_t *minor_status,
+					    bool require_pac,
+					    gss_cred_id_t *cred);
+
 #endif /* HAVE_GSSAPI */
 #endif /* _GSS_SAMBA_H */
-- 
2.17.1

From 57ca0acf6dfc40341d3b060738dd068ab60359b4 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Thu, 22 Aug 2019 17:13:41 +0000
Subject: [PATCH 20/25] s3:gse: let gse_init_server() use
 smb_gss_krb5_prepare_acceptor_cred()

We should check all keys in our in memory keytab
and don't need any transited checks if we require a PAC.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14125

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 source3/librpc/crypto/gse.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/source3/librpc/crypto/gse.c b/source3/librpc/crypto/gse.c
index dc074d1ae1ae..cd225b6373d1 100644
--- a/source3/librpc/crypto/gse.c
+++ b/source3/librpc/crypto/gse.c
@@ -598,6 +598,7 @@ static NTSTATUS gse_init_server(TALLOC_CTX *mem_ctx,
 	OM_uint32 gss_maj, gss_min;
 	krb5_error_code ret;
 	NTSTATUS status;
+	bool require_pac = lp_require_pac();
 
 	status = gse_context_init(mem_ctx, do_sign, do_seal,
 				  NULL, add_gss_c_flags, &gse_ctx);
@@ -624,6 +625,17 @@ static NTSTATUS gse_init_server(TALLOC_CTX *mem_ctx,
 		goto done;
 	}
 
+	gss_maj = smb_gss_krb5_prepare_acceptor_cred(&gss_min,
+						     require_pac,
+						     &gse_ctx->creds);
+
+	if (gss_maj != 0) {
+		DEBUG(0, ("smb_gss_krb5_prepare_acceptor_cred failed with [%s]\n",
+			  gse_errstr(gse_ctx, gss_maj, gss_min)));
+		status = NT_STATUS_INTERNAL_ERROR;
+		goto done;
+	}
+
 	status = NT_STATUS_OK;
 
 done:
-- 
2.17.1

From 2e772f301b50cc4ab4a6cabe0858213faead8d47 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Thu, 22 Aug 2019 17:24:24 +0000
Subject: [PATCH 21/25] auth/credentials_krb5: add helper define
 __TMP_NEED_EMPTY_BUFFFER

This simplifies the following commits.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 auth/credentials/credentials_krb5.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/auth/credentials/credentials_krb5.c b/auth/credentials/credentials_krb5.c
index 20e677e521a8..c7247cfe9790 100644
--- a/auth/credentials/credentials_krb5.c
+++ b/auth/credentials/credentials_krb5.c
@@ -835,9 +835,12 @@ _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
 	struct gssapi_creds_container *gcc;
 	struct ccache_container *ccache;
 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
-	gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
 	gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
-#endif
+#define __TMP_NEED_EMPTY_BUFFFER 1
+#endif /* HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X */
+#ifdef __TMP_NEED_EMPTY_BUFFFER
+	gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
+#endif /* __TMP_NEED_EMPTY_BUFFFER */
 	krb5_enctype *etypes = NULL;
 
 	if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold && 
-- 
2.17.1

From 9b0a70b3e879d5c1fe2359eb6f2563f7ab4a4a3f Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Thu, 22 Aug 2019 17:24:24 +0000
Subject: [PATCH 22/25] auth/credentials_krb5: rename oid to no_ci_flags_oid

This will simplify the following commit.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 auth/credentials/credentials_krb5.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/auth/credentials/credentials_krb5.c b/auth/credentials/credentials_krb5.c
index c7247cfe9790..03c1e8a9a115 100644
--- a/auth/credentials/credentials_krb5.c
+++ b/auth/credentials/credentials_krb5.c
@@ -835,7 +835,7 @@ _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
 	struct gssapi_creds_container *gcc;
 	struct ccache_container *ccache;
 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
-	gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
+	gss_OID no_ci_flags_oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
 #define __TMP_NEED_EMPTY_BUFFFER 1
 #endif /* HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X */
 #ifdef __TMP_NEED_EMPTY_BUFFFER
@@ -971,7 +971,7 @@ _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
 	 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
 	 */
 	maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
-				       oid,
+				       no_ci_flags_oid,
 				       &empty_buffer);
 	if (maj_stat) {
 		talloc_free(gcc);
-- 
2.17.1

From 44d978fa86c15ab4a5d31bd0e41a60ba79f6661d Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Thu, 22 Aug 2019 17:24:24 +0000
Subject: [PATCH 23/25] auth/credentials_krb5: let
 cli_credentials_get_client_gss_creds() use NO_TRANSIT_CHECK_X

There no need to do any possible transit checking on
the client side.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 auth/credentials/credentials_krb5.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/auth/credentials/credentials_krb5.c b/auth/credentials/credentials_krb5.c
index 03c1e8a9a115..b4cb438f09d3 100644
--- a/auth/credentials/credentials_krb5.c
+++ b/auth/credentials/credentials_krb5.c
@@ -838,6 +838,12 @@ _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
 	gss_OID no_ci_flags_oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
 #define __TMP_NEED_EMPTY_BUFFFER 1
 #endif /* HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X */
+#ifdef HAVE_GSS_KRB5_CRED_NO_TRANSIT_CHECK_X
+	gss_OID no_transit_oid = discard_const(GSS_KRB5_CRED_NO_TRANSIT_CHECK_X);
+#ifndef __TMP_NEED_EMPTY_BUFFFER
+#define __TMP_NEED_EMPTY_BUFFFER 1
+#endif /* __TMP_NEED_EMPTY_BUFFFER */
+#endif /* HAVE_GSS_KRB5_CRED_NO_TRANSIT_CHECK_X */
 #ifdef __TMP_NEED_EMPTY_BUFFFER
 	gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
 #endif /* __TMP_NEED_EMPTY_BUFFFER */
@@ -983,6 +989,21 @@ _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
 		(*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
 		return ret;
 	}
+#endif
+#ifdef HAVE_GSS_KRB5_CRED_NO_TRANSIT_CHECK_X
+	maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
+				       no_transit_oid,
+				       &empty_buffer);
+	if (maj_stat) {
+		talloc_free(gcc);
+		if (min_stat) {
+			ret = min_stat;
+		} else {
+			ret = EINVAL;
+		}
+		(*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
+		return ret;
+	}
 #endif
 	cred->client_gss_creds_obtained = cred->ccache_obtained;
 	talloc_set_destructor(gcc, free_gssapi_creds);
-- 
2.17.1

From c0cefd0a0c717f40a3a69b2f09cd883f3740c1ca Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Thu, 22 Aug 2019 17:25:09 +0000
Subject: [PATCH 24/25] auth/credentials_krb5: let
 cli_credentials_get_server_gss_creds() use an early return

This will simplify the next commits.

Check with: git show -w

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 auth/credentials/credentials_krb5.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/auth/credentials/credentials_krb5.c b/auth/credentials/credentials_krb5.c
index b4cb438f09d3..8de18c440a16 100644
--- a/auth/credentials/credentials_krb5.c
+++ b/auth/credentials/credentials_krb5.c
@@ -1383,14 +1383,16 @@ _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
 			ret = EINVAL;
 		}
 	}
-	if (ret == 0) {
-		cred->server_gss_creds_obtained = cred->keytab_obtained;
-		talloc_set_destructor(gcc, free_gssapi_creds);
-		cred->server_gss_creds = gcc;
-		*_gcc = gcc;
+	if (ret != 0) {
+		talloc_free(mem_ctx);
+		return ret;
 	}
+	cred->server_gss_creds_obtained = cred->keytab_obtained;
+	talloc_set_destructor(gcc, free_gssapi_creds);
+	cred->server_gss_creds = gcc;
+	*_gcc = gcc;
 	talloc_free(mem_ctx);
-	return ret;
+	return 0;
 }
 
 /** 
-- 
2.17.1

From f656f81667a454d06af72ac8ce6c12227a26f131 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <[email-masked]>
Date: Thu, 22 Aug 2019 17:25:30 +0000
Subject: [PATCH 25/25] auth/credentials_krb5: make use of
 smb_gss_krb5_prepare_acceptor_cred()

We should check all keys in our in memory keytab
and don't need any transited checks if we require a PAC.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12907
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14125

Signed-off-by: Stefan Metzmacher <[email-masked]>
---
 auth/credentials/credentials_krb5.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/auth/credentials/credentials_krb5.c b/auth/credentials/credentials_krb5.c
index 8de18c440a16..31d29d3fd90e 100644
--- a/auth/credentials/credentials_krb5.c
+++ b/auth/credentials/credentials_krb5.c
@@ -1387,6 +1387,20 @@ _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
 		talloc_free(mem_ctx);
 		return ret;
 	}
+	maj_stat = smb_gss_krb5_prepare_acceptor_cred(&min_stat,
+						      true, /* require_pac */
+						      &gcc->creds);
+	if (maj_stat) {
+		if (min_stat) {
+			ret = min_stat;
+		} else {
+			ret = EINVAL;
+		}
+	}
+	if (ret != 0) {
+		talloc_free(mem_ctx);
+		return ret;
+	}
 	cred->server_gss_creds_obtained = cred->keytab_obtained;
 	talloc_set_destructor(gcc, free_gssapi_creds);
 	cred->server_gss_creds = gcc;
-- 
2.17.1

---

From: Stefan Metzmacher <[email-masked]>
Date: Wed, 25 Sep 2019 10:09:56 +0200

> the for us useless transit check.

I just realized that verifying the PAC gains no additional protection.
As the client realm, client principal and transited fields is
in the encrypted part of the ticket, which is encrypted with the machine
trust password.

For now I added a simple "kerberos acceptor disable transited check"
option, which is off by default for now in order to backport that fix,
but in master we should enable that by default.

I've updated the Samba merge request with a much simpler patchset.

metze

---

From: Greg Hudson <[email-masked]>
Date: Fri, 27 Sep 2019 11:01:18 -0400

> in the encrypted part of the ticket, which is encrypted with the machine

I don't think this follows.  It's true that the PAC, client principal,
and transited list are all coming from the service's KDC with integrity
protection, but the question is to what degree the KDC is vouching for
that information.

If the TRANSITED-POLICY-CHECKED flag is not set in the ticket, the
service should assume that the KDC applied no policy to the transit
path.  In practice, the DISABLE-TRANSITED-CHECK request flag, together
with MS-KILE 3.1.5.4, means that it is easy to get most KDCs not to
apply policy.  Without policy controls, any realm in the transitive
closure of cross-realm keys can issue tickets for clients in any other
realm (except perhaps the service realm itself).

The PAC contents, on the other hand, may be subject to policy controls.

> MIT, Heimdal (both upstream and Samba) and make use of
[...]

>From past discussions I would not expect the Heimdal project to take
action on a patch in an email attachment sent to the discussion list.  I
would suggest making a pull request at
https://github.com/heimdal/heimdal .