Skip Menu |

Subject: S4U2Self with X.509 certificate bugs
Download (untitled) / with headers
text/plain 2.9KiB
Microsoft's S4U2Self extension allows a service to obtain a ticket to
itself from an arbitrary user. The user can be identified by regular
krb5 principal, by enterprise principal, or by X.509 certificate. In
the latter two cases, AS requests are used to identify the client's
realm. After the realm is identified, a TGS-REQ with S4U2Self pa-
data is used to obtain the ticket. (In the cross-realm case,
multiple TGS-REQs are required.)

Commit 0e39f8a3ad915eeb0131fb4a87b0fef304101cfd added the client code
for S4U2Self to the MIT krb5 tree. It contained support for using an
X.509 certificate to identify the user, but that functionality was
not reachable by any public path. Isaac Boukris has identified and
corrected the following issues with the client code for using X.509

* During realm identification, the code attempted to present the
X.509 certificate using an optimistic preauth list and special
handling in preauth2.c. Changes to the preauth framework caused this
attempt to fail (because it did not produce authenticating padata
from a clpreauth module) and fall back to sending an AS request
without the cert.

* When performing realm identification with a cert, the existing code
uses a client principal with NT-ENTERPRISE type and no data
components. Windows clients provide a principal of type NT-X500-
PRINCIPAL and the certificate subject as a single data component.
Doing that would require partially decoding the certificate.
Experimentally, using a single empty data component actually works
better with Windows KDCs than including the certificate subject (it
works with cross-realm), so we should do that instead.

* If an enterprise principal and cert are both presented to the API,
to be consistent with Windows clients we should do realm
identification with just the enterprise principal, and then prsent
the cert in the TGS request. The current code presents (well, tries
to present) the certificate during realm identification in this case.

* In krb5_get_self_cred_from_kdc(), if no client principal is
provided, the code to build s4u_user.user_id.user did not terminate
the krb5_build_principal_ext() variable argument list and could
therefore crash.

* In the same function, the PA-FOR_USER padata input is supplied to
krb5_get_cred_via_tkt_ext() via an in_padata list, while the PA-FOR-
X509-USER padata is constructed via a callback because so that the
TGS subkey can be used to build it. When the TGS reply is received,
krb5int_process_tgs_reply() looks for PA-FOR_USER or PA-FOR-X509-USER
padata in the in_padata list to decide whether to use S4U2Self logic
to validate the client. Because the PA-FOR-X509-USER padata is not
supplied via the in_padata list, the code will never find that type.
In the usual case, we will find the PA-FOR_USER type and do the right
thing, but in the cert-only case there is no PA-FOR_USER padata
value, so the S4U2Self logic is not applied.
One more issue I neglected to note:

* In the TGS part of a S4U2Self request, when multiple TGS requests are
required due to cross-realm, to be consistent with Windows clients,
only the first request should present the certificate; later requests
should present the client principal obtained from the PA-FOR-X509-USER
padata in the first TGS response.

I will also note here that, per Isaac's investigation, the Windows LSA
API will extract a UPN SAN from the client certificate and use that
enterprise principal in preference to the certificate. To do the same
we would need certificate-parsing code or an OpenSSL dependency in the
S4U2Self code.
Subject: git commit
Download (untitled) / with headers
text/plain 1.9KiB

Fix client code for S4U2Self with certificate

During realm identification, don't send the certificate in the AS
request if we have an enterprise name, for consistency with the
Windows LSA API behavior. If we are using just a certificate, use the
appropriate client principal name type with a single empty data

krb5int_process_tgs_reply() needs to see an S4U2Self padata type in
in_padata to apply the correct logic when verifying the client
principal in the reply. If we are using only a certificate, we
currently do not pass any in_padata (because we do not send
PA-FOR-USER in this case, and the PA-S4U-X509-USER is constructed via
a callback). Change the code to place an empty PA-S4U-X509-USER in
in_padata, to be modified by the callback; that way we can reliably
detect the S4U2Self case when processing the reply.

In krb5_get_self_cred_from_kdc(), when constructing an empty client
principal for a cert-only S4U2Self request, properly terminate the
krb5_build_principal_ext() argument list to avoid a crash. Don't
bother setting the name type as it isn't sent.

Only send the certificate in the first TGS-REQ to the client realm.
To the intermediate and final realms, send the principal name only.
Use the checksum-protected principal name in the first KDC's
PA-S4U-X509-USER response for subsequent requests and to verify the
unprotected client name in the final reply.

After receiving the final reply, check if we had cached credentials
under the discovered client name (unless it's the same as the input
client name) and return the cached credentials if we find them.

[ squashed commits; rewrote commit message]
Author: Isaac Boukris <>
Committer: Greg Hudson <>
Commit: ed830223d862bb48ccc43e2c7dbbb4eaf555e679
Branch: master
src/lib/krb5/krb/s4u_creds.c | 148 +++++++++++++++++++++++++++++-------------
1 files changed, 102 insertions(+), 46 deletions(-)