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

From: Stefan Metzmacher <[email-masked]>
Date: Fri, 18 Aug 2017 14:35:51 +0200

Hi,

I'm currently researching on how I can implement S4U2Self in
Samba's winbindd in order to get the PAC with the full
Windows authorization token in a reliable way for any user
within an active directory forest as well across transitive
forest trusts.

The only thing that should be required is a service (computer) account
in the primary domain/realm.

But in practice I'm facing several problems:

Heimdal (at least the copy of ~ 1.5 within Samba)
doesn't support S4U2Self for cross-realm trusts.

MIT (tested with 1.14.3) supports S4U2Self for
cross-realm trusts, which are in simple hierarchy.
Otherwise it complains and returns KRB5KRB_AP_ERR_ILL_CR_TKT.
That can be fixed if I add the correct magic to the [capaths] section
of krb5.conf.

The problem happens when you have 2 tree root domains within an
active directory forest together with a forest trust.

In my case I have a forest called W4EDOM-L4.BASE with a single domain
and a forest called BLA.BASE with a 2nd domain BLA2.BASE.

So trust path between W4EDOM-L4.BASE and BLA2.BASE goes via BLA.BASE.

In an active directory environment domain members just delegate
authentication to the domain controllers, so they trust
their DCs to do the correct things, e.g. applying SID-Filtering
for the PAC within the tickets.

So the service can just verify the PAC was correctly signed by
a KDC of it's own realm and everything else shouldn't matter,
it doesn't have to know about the full trust topology!

While thinking about this I can't see any value in checking the
transited list of the ticket. As that list is always under the
control of the KDC that issued the ticket. And the service
trusts it's own KDC anyway, as well as any KDC in the trust
chain trusts the next hop. The only reason for this list
might be debugging.

The thing is that KDC's should apply some policies
of which client realms can come over which direct trust.
As KDC's have some knowledge about the trust topology.
This is basically what the SID-Filtering in active directory
is for, it prevents DCs from other domains/realms to impersonate
principals of the local realm.

Is there any reason to keep the krb5_check_transited() (in Heimdal)
and krb5_check_transited_list() (in MIT) is their current form?

If a KDC checks something it should be checking the PA-TGS-REQ,
and verify the client realm is allowed to transit via the
realm of the (cross-realm) tgt. But checking the transited field
of the ticket seems pointless to me.

If there's however a good reason to keep the checks for non
active directory realms, I'd propose to add something like
gss_set_cred_option(GSS_KRB5_CRED_NO_TRANSIT_CHECK_X)
to Heimdal and MIT in order to allow applications to avoid
the pointless checks.

Comments on this would be highly appreciated!

If you're not so familiar with active directory domains,
please have a look at:
https://www.samba.org/~metze/presentations/2017/SambaXP/StefanMetzmacher_sambaxp2017_windows_authentication-rev1-handout.pdf

Thanks!
metze

---

From: Stefan Metzmacher <[email-masked]>
Date: Fri, 18 Aug 2017 15:22:04 +0200

Moving this from [email-masked] to [email-masked],
sorry...

---

From: Greg Hudson <[email-masked]>
Date: Mon, 21 Aug 2017 10:05:45 -0400

> chain trusts the next hop. The only reason for this list

I'm not sure about "any KDC in the trust chain trusts the next hop."
RFC 4120 doesn't think about cross-realm relationships in terms of
trust.  Simply having cross-realm keys with another realm doesn't
necessarily imply that the other realm is trustworthy.

> and krb5_check_transited_list() (in MIT) is their current form?

Well, it's mandatory in RFC 4120 section 2.7:

   Application servers MUST either do the transited-realm checks
   themselves or reject cross-realm tickets without
   TRANSITED-POLICY-CHECKED set.

It would be okay to skip this check on application servers if the ticket
has the TRANSITED-POLICY-CHECKED flag.  Heimdal appears to do this but
MIT krb5 does not; I'm not sure why as that behavior dates to before my
time.

---

From: "Henry B (Hank) Hotz, CISSP" <[email-masked]>
Date: Mon, 21 Aug 2017 11:02:37 -0700

> On Aug 21, 2017, at 7:05 AM, Greg Hudson <[email-masked]> wrote:
> 

That’s always been a slippery distinction in practice. Trust depends on “local policy” which may be determined by many things that are orthogonal to what the crypto can actually provide. Unless you’re writing the code yourself, I would presume that anything with an exchanged set of keys is trusted for authentication. Authorization is, of course, outside the scope of Kerberos.

Personal email.  [email-masked]

---

From: Stefan Metzmacher <[email-masked]>
Date: Tue, 22 Aug 2017 13:22:20 +0200

> necessarily imply that the other realm is trustworthy.

At least it allows the other realm to issue cross-realm referral
tickets, which the local realm will most likely convert into service
tickets which can be used by a principal of the other realm to
access services in the local realm.

And the client principal names (including client realm) in the
cross-realm ticket can contain any value, which is fully controlled
by the other realms KDCs.

I guess that's what RFC 4120 means by "The presence of trusted KDCs in
this list does not provide any guarantee; an untrusted KDC may have
fabricated the list."

> MIT krb5 does not; I'm not sure why as that behavior dates to before my

The fact that it's mandatory according to the RFC doesn't mean it
gains anything for the application server. But if other's believe
that the check helps, I'm fine with that.

Would be acceptable then if I implement
gss_set_cred_option(GSS_KRB5_CRED_NO_TRANSIT_CHECK_X) for
MIT and Heimdal in order to let an application service to skip the check
if they know what they're doing by checking trusting there KDC and doing
the PAC verification.

metze

---

From: Greg Hudson <[email-masked]>
Date: Tue, 22 Aug 2017 11:02:40 -0400

> access services in the local realm.

To authenticate, yes; whether that provides any access depends on the
services in the local realm.

> by the other realms KDCs.

Yes, which makes it the local realm's job (either at the KDC or the
application server) to decide whether the other realm's KDC should be
able to claim that client realm name.  Completely trusting the foreign
realm is one option, but that option mostly restricts cross-realm
relationships to foreign realms of equal or greater privilege.  (I say
"mostly" because a KDC can trivially deny a ticket from the foreign
realm which purport to authenticate a client in the local realm.  So one
might be willing to trust a foreign realm to authenticate clients in all
non-local realms if one doesn't grant much privilege to any non-local
user anyway.)

> if they know what they're doing by checking trusting there KDC and doing

I think we should first consider whether it would be sufficient for MIT
krb5 to suppress the rd_req transited check if the
TRANSITED-POLICY-CHECKED flag is set in the ticket.  MIT and Heimdal
KDCs both appear to perform the transited check and set the flag by default.

An application server always trusts its local KDC, but that trust isn't
useful for cross-realm relationships if the TRANSITED-POLICY-CHECKED
flag isn't in the ticket.  Clients can, in general, suppress the KDC
transited check with the DISABLE-TRANSITED-CHECK flag; therefore, a
ticket issued by the local KDC without the TRANSITED-POLICY-CHECKED flag
asserts nothing about the acceptability of the KDC path.

I am not sufficiently familiar with PACs to know whether PAC
verification is a fitting alternative to a transited policy check.

---

From: Stefan Metzmacher <[email-masked]>
Date: Thu, 24 Aug 2017 01:01:44 +0200

> KDCs both appear to perform the transited check and set the flag by default.

But Windows KDCs doesn't set this bit (I guess because it's just not
useful).

> verification is a fitting alternative to a transited policy check.

I've attached network caputures with a keytab that allows wireshark
to decrypt the messages.

We have the computer
[email-masked] (fd3a:aaa3:ee87:ff09:200:ff:fe09:73)
that tries to do S4U2Self for
[email-masked].

W4EDOM-L4.BASE (fd3a:aaa3:ee87:ff09:200:ff:fe09:133)
is it's own forest. It trusts the forest W2012R2-L4.BASE
(fd3a:aaa3:ee87:ff09:200:ff:fe09:183) which has subdomains
S1-W2012-L4.W2012R2-L4.BASE (fd3a:aaa3:ee87:ff09:200:ff:fe09:181)
and S2-W2012-L4.S1-W2012-L4.W2012R2-L4.BASE
(fd3a:aaa3:ee87:ff09:200:ff:fe09:182)

The interesting parts are the ticket returned in frames 218
it is a normal cross-realm ticket with transited = W2012R2-L4.BASE.
Frame 242, 285 and 352 are the S4U2Self (reverse) referrals
with more values in the transited field. Note the cname
is still [email-masked], but the PAC within the
authorization-data already belongs to
[email-masked] (e.g. see the
Client Info Type).
The final S4U2Self ticket in frame 377 has also 3 domains
(with a different encoding compared to 285).
In the final ticket the Client Info Name need to match exactly
the cname of the ticket and the Client Info time the authtime
of the ticket, that's verified by krb5_pac_verify().

The 2nd example uses a forest trust from W4EDOM-L4.BASE to
BLA.BASE (fd3a:aaa3:ee87:ff09:200:ff:fe09:219) which has a 2nd tree
root domain in the same forest called BLA2.BASE
(fd3a:aaa3:ee87:ff09:200:ff:fe09:194).
We try S4U2Self for [email-masked] here.

There we have transited = BLA.BASE in frame 231, which is the
first S4U2Self (reverse) referral. The final ticket in frame 323
has BLA.BASE and BLA2.BASE in the transited field, which causes
the transited verification to fail unless I manually configure
a [capaths] section.

Each KDC applies some SID-Filtering to the group memberships
of the Logon Info field in the PAC. Which prevents impersonation
of local principals via cross-realm tickets. The PAC contains to
signatures one using the long term key that belongs to
the sname@realm of the ticket (either the cross-realm key or the key of
the final service). This service signature is checksummed by local
krbtgt long term key. So that the service itself can't fool the KDC
and generate it's own PAC.

See [MS-PAC] 4.1.2 Authorization Validation and Filtering
https://msdn.microsoft.com/en-us/library/cc237938.aspx
to get more details.

Windows and also Samba basically use the Logon Info part of the
PAC to form the authorization token for the client.
Which means the verification of the PAC service signature is enough
in order to trust our KDC, which already applied the required
SID-Filtering for us.

I hope I was able to bring some light into this complex topic.

In order to work without manual configuration, we need a way
to disable the transited check when we're the service.

If you have wireshark with kerberos decryption support you can use:
wireshark -K all-domains.keytab
net-ads-kerberos-pac-fd23-73\@W4EDOM-L4-impersonate-administrator\@S2-W2012-L4.S1-W2012-L4.W2012R2-L4.BASE-ok02.pcap.gz
and
wireshark -K all-domains.keytab
net-ads-kerberos-pac-fd23-73\@W4EDOM-L4-impersonate-administrator\@BLA2.BASE-fail-transit-03.pcap.gz

metze

---

From: Greg Hudson <[email-masked]>
Date: Wed, 23 Aug 2017 20:38:07 -0400

> But Windows KDCs doesn't set this bit (I guess because it's just not

I don't agree at all that the bit isn't useful.  That bit is how a KDC
communicates that it vouches for the transited path.  Unfortunately, you
do appear to be correct about Windows KDCs.  MS-KILE says:

    The TRANSITED-POLICY-CHECKED flag ([RFC4120] section 2.7): KILE
    MUST NOT check for transited domains on servers or a KDC.
    Application servers MUST ignore the TRANSITED-POLICYCHECKED flag.

which basically means Microsoft has declined to conform to RFC 4120 in
this area, instead requiring servers to implement PACs to interoperate
in a cross-realm environment.

I guess the proposed credential option is necessary, in that case.

---

From: Simo Sorce <[email-masked]>
Date: Thu, 24 Aug 2017 08:36:24 -0400

> I guess the proposed credential option is necessary, in that case.

I think in this case ignoring the flag should probably be conditional
to whether a PAC is present.

My2c.

Simo.
-- 
Simo Sorce
Sr. Principal Software Engineer
Red Hat, Inc

---

From: Stefan Metzmacher <[email-masked]>
Date: Thu, 24 Aug 2017 15:11:16 +0200

Hi Simo,

> to whether a PAC is present.

We should enforce a PAC always to be present, as we don't support
trusted domains with LSA_TRUST_TYPE_MIT anyway.

metze

---

From: Simo Sorce <[email-masked]>
Date: Thu, 24 Aug 2017 13:36:29 -0400

> trusted domains with LSA_TRUST_TYPE_MIT anyway.

In samba, yes, but that option can be used in other clients that can
connect to multiple types of servers so in case they do not get a PAC
the flag should be respected.

Simo.

---

From: Viktor Dukhovni <[email-masked]>
Date: Thu, 24 Aug 2017 16:47:01 -0400

[ Just kitten, as either not subcribed or subscribed with a different
  address to some of the other lists. ]

> On Aug 24, 2017, at 1:36 PM, Simo Sorce <[email-masked]> wrote:
> 

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.

-- 
	Viktor.

---

From: Stefan Metzmacher <[email-masked]>
Date: Fri, 25 Aug 2017 00:29:27 +0200

> 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.

metze

---

From: Simo Sorce <[email-masked]>
Date: Thu, 24 Aug 2017 19:10:17 -0400

> the the transited list should not be checked.

It's my idea as well, but if you are operating in a mixed environment
and the ticket happens to come without a PAC the transited list should
probably be checked instead. A service *may* decide to bail out if no
PAC is present but it shouldn't have to.

Simo.

---

From: [email-masked] (Martin Rex)
Date: Thu, 31 Aug 2017 00:37:32 +0200 (CEST)

> trusted domains with LSA_TRUST_TYPE_MIT anyway.

I haven't really followed the discussion here, but Microsoft PACs are
a royal PITA, wasting huge amounts of network bandwidth, creating
bad latency on authentication, causing spurious authentication failures
during high-load situations and completely breaking certain rfc4559
HTTP Negotiate scenarios when users are assigned to lots of groups
in Microsoft AD.  Bottlenecks in LSA PAC verifications are extremely
annoying, such as using Outlook&Exchange with 50k+ Users and
at least once an hour an Outlook Password logon prompt pops up because
one of the crazy many rfc4559 Kerberos authentications failed between
Outlook and Exchange.  (Hitting cancel on the popup and toggling
offline/online in the Outlook window status bar retries Kerberos
authentication and typically succeeds...

Simply disabling PACs on the service account for service tickets
solves all of the hard and annoying problems, by enabling the bit
UF_NO_AUTH_DATA_REQUIRED (0x02000000) in the UserAccountControl
property of the service account.

Without setting this bit to omit the crazy PACs, common problems with
heavy (mindless) Group membership usage are this:

   https://blogs.technet.microsoft.com/shanecothran/2010/07/16/maxtokensize-and-kerberos-token-bloat/

Reasons why the myriads of PAC verfications occasionally fail:

   https://blogs.technet.microsoft.com/instan/2011/11/14/the-return-of-pac-mania-aka-some-reasons-why-pac-verification-can-fail/

and btw. huge kerberos tickets can also make rfc4559 fail because of
the excessive size of the HTTP header field to carry the AP_REQ with the
kerberos ticket.

Whenever authorization isn't actually managed through PACs,
i.e. pretty much 100% of the time when the backend is a database
and access through rfc4559 HTTP Negotiate, omitting PACs from Kerberos
tickets comes close to a universal panacea for a huge amount of
"occasional" failures.

-Martin