Projects/Services4User
This is an early stage project for MIT Kerberos. It is being fleshed out by its proponents. Feel free to help flesh out the details of this project. After the project is ready, it will be presented for review and approval.
Working code is in the users/lhoward/s4u branch.
Contents
Background
[MS-SFU] describes two extensions to the Kerberos protocol:
- S4U2Self, or protocol transition, which enables a service to acquire a ticket from an arbitrary principal to itself (the service is trusted to have authenticated the principal)
- S4U2Proxy, or constrained delegation, which enables a service to use a client's ticket to itself to request another ticket for delegation
Together, these extensions are referred to as Services4User, or S4U.
The KDC will typically make some authorization checks before permitting the above: for S4U2Self, a flag on the service indicating that it is "trusted to authenticate for delegation" and, for S4U2Proxy, a set of server principals to which the service is permitted to delegate to. There is already some KDC-side support for these protocols in MIT Kerberos 1.7 (although supporting S4U2Proxy requires explicit backend support that is not included with the standard distribution, and S4U2Self does not support some protocol extensions Microsoft made in Windows 2008).
This proposal is principally concerned with the client-side (or, more correctly, service-side) implementation of S4U, although it would be interesting to update the KDC to address the limitations mentioned above.
Protocol extensions
S4U2Self
Readers are referred to [MS-SFU] for protocol details, but the premise behind S4U2Self is that a service requests a ticket from itself, to itself, presenting some additional preauthentication data containing the name of the user it has presumably authenticated. The KDC, after making any access checks, returns a ticket with the client-name rewritten to that in the preauthentication data. The server name is unchanged.
There are two types of PA data:
- PA-FOR-USER, introduced in Windows 2003, which consists of the "user name" (client principal) and a checksum of the PA data with the TGT session key
- PA_S4U_X509_USER, introduced in Windows 2008, which includes the TGS-REQ nonce to bind the S4U request to the TGS-REQ, as well as allowing the user to be identified with an X.509 certificate rather than a name.
The protocol itself is quite complicated, particularly where cross-realm authentication is concerned, so I refer the reader to [MS-SFU] for further details.
We introduce the following key usage types to krb5.h:
/* Defined in [MS-SFU] */ #define KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST 26 /* XXX note conflict with above */ #define KRB5_KEYUSAGE_PA_S4U_X509_USER_REPLY 27 /* XXX note conflict with above */
and the following PA data types (although PA-FOR-USER is present since 1.7):
#define KRB5_PADATA_FOR_USER 129 /* username protocol transition request */ #define KRB5_PADATA_S4U_X509_USER 130 /* certificate protocol transition request */
Flags for use with PA_S4U_X509_USER are also defined here, although as they are not exposed through any APIs they could be moved to k5-int.h:
#define KRB5_S4U_OPTS_CHECK_LOGON_HOURS 0400000000 /* check logon hour restrictions */ #define KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE 0x20000000 /* sign with usage 27 instead of 26 */
S4U2Proxy
Again, refer to [MS-SFU] for protocol details, but in this case the service presents a ticket (from an AP-REQ) to the KDC in the additional tickets field, whilst requesting a ticket from itself to the service to which it wishes to delegate. (A new KDC option, CNAME-IN-ADDL-TKT, is used to disambiguate this from user-to-user authentication.) The KDC, after making any access checks, returns a ticket from the additional ticket client to the delegatee.
The CNAME-IN-ADDL-TKT option is present since 1.7:
#define KDC_OPT_CNAME_IN_ADDL_TKT 0x00020000
Proposed APIs
I propose that the only public APIs are at the GSS-API layer. We will need to export krb5 APIs for GSS to use, and possibly the kvno tool (for testing); it's probably not necessary to indirect these through an kaccess, though.
gss_krb5_create_sec_context_for_principal()
OM_uint32 gss_krb5_create_sec_context_for_principal(OM_uint32 *minor_status, gss_ctx_id_t *context_handle, gss_cred_id_t verifier_cred_handle, gss_name_t principal, OM_uint32 req_flags, OM_uint32 time_req, gss_name_t *src_name, gss_OID *mech_type, OM_uint32 *ret_flags, OM_uint32 *time_ret, gss_cred_id_t *delegated_cred_handle);
gss_krb5_create_sec_context_for_principal() synthesises an acceptor-side security context for an arbitrary principal. (There's probably a better name for this. The word "impersonate" seems good, but it has a different meaning on Windows, which could be confusing.)
verifier_cred_handle must be both an initiator- and acceptor-side credentials handle, because a TGT is required to perform S4U2Self.
Essentially this function acquires Kerberos credentials using S4U2Self, wraps them up as GSS credentials, and then calls gss_init_sec_context() and gss_accept_sec_context(). This provides a simple interface for developers, at the expense perhaps of some transparency. I do suspect that if we provide complex APIs they won't be used, or giant slabs of sample code will be copied and pasted.
That said, alternatives to this API are:
- one which returns a GSS initiator credential (gss_krb5_acquire_creds_for_principal())
- one which returns a GSS initial context token (gss_init_sec_context_for_principal())
The current API has no provision for certificate-based protocol transition.
gss_krb5_add_sec_context_delegatee()
gss_krb5_add_sec_context_delegatee() creates or updates a skeletal context that can be passed to gss_accept_sec_context(), such that delegated_cred_handle will contain credentials for delegating to the specified principals. The existing GSS-API delegation model is maintained; the only difference being that the delegated credentials handle contains a set of service tickets, instead of a TGT.
This takes advantage of the new behaviour of gss_accept_sec_context() introduced in 1.7 (although there was a bug in that implementation), where gss_set_sec_context_option() can be used to create a context with some parameters which is passed to gss_accept_sec_context() with the initial context token. Prior to that, the context handle on the first call to gss_accept_sec_context() was always GSS_C_NO_CONTEXT.
Some changes were required to gss_accept_sec_context() and gss_init_sec_context() to accept and validate these skeletal contexts. (Note that such a context should never be passed to gss_init_sec_context().)
Implementation details
Most of the implementation can be found in src/lib/krb5/krb/s4u_creds.c.
S4U2Self
krb5_error_code KRB5_CALLCONV krb5_get_credentials_for_user(krb5_context context, krb5_flags options, krb5_ccache ccache, krb5_creds *in_creds, krb5_data *subject_cert, krb5_creds **out_creds)
krb5_get_credentials_for_user() does the following:
- checks the credentials cache for an existing ticket
- identifies the user's realm using an AS-REQ, if a certificate or enterprise principal name is being used
- acquires a TGT to the user's realm
- makes the S4U2Self request, following any referrals
- stores the ticket in the credentials cache
Both variants of S4U2Self are supported, but the Windows 2008 version is currently disabled pending some interoperability debugging.
The following data types are introduced into k5-int.h, along with their complementary encode/decode/free routines. Note that these types are not exposed via exported APIs.
typedef struct _krb5_pa_for_user { krb5_principal user; krb5_checksum cksum; krb5_data auth_package; } krb5_pa_for_user; typedef struct _krb5_s4u_userid { krb5_int32 nonce; krb5_principal user; krb5_data subject_cert; krb5_flags options; } krb5_s4u_userid; typedef struct _krb5_pa_s4u_x509_user { krb5_s4u_userid user_id; krb5_checksum cksum; } krb5_pa_s4u_x509_user;
S4U2Proxy
krb5_error_code KRB5_CALLCONV krb5_get_credentials_for_proxy(krb5_context context, krb5_flags options, krb5_ccache ccache, krb5_creds *in_creds, krb5_ticket *evidence_tkt, krb5_creds **out_creds)
krb5_get_credentials_for_proxy() does the following:
- checks the credentials cache for an existing ticket
- makes the S4U2Proxy request
- stores the ticket in the credentials cache
Open issues
- Do we actually want to store the returned S4U2Self/S4U2Proxy tickets in the credentials cache, or should we just return them as krb5_creds? The credentials cache could fill up rapidly on a busy server.
- Do we want to update our KDC to handle the Window 2008 variant of S4U2Self?
- Do we want to implement sample code to handle S4U2Proxy in our KDC? Note that, without Windows authorization data, this requires additional authorization data (which Heimdal appears to support) to avoid a service forging an evidence ticket
- What level of abstraction do we wish to expose at the GSS-API layer?
- Do we wish to offer APIs for certificate based S4U2Self?
- Would it be interesting/useful to offer a SAML variant of S4U2Self?
Tools
The kvno utility has been updated to support the -U user option (for S4U2Self) and the -P option (for S4U2Proxy, which changes the meaning of the additional service arguments to be delegatees).
Status
Currently, S4U2Self (Windows 2003) and S4U2Proxy are working against Windows 2008. A test program can be found in src/tests/gssapi/t_s4u.c. What remains is to debug the Windows 2008 version of S4U2Self (signature verification is presently failing) and test the code in a multiple-realm setup.