logo_kerberos.gif

Projects/Services4User

From K5Wiki
< Projects
Revision as of 06:55, 8 September 2009 by Lukeh (talk | contribs) (Status)

Jump to: navigation, search
This project has been approved and is being actively worked on. Comments should be addressed to krbdev@mit.edu.


Working code is in the users/lhoward/s4u branch.

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. (Note: the trusted to authentictae for delegation flag controls whether a service can use S4U2Self to get forwardable tickets, not whether it can use S4U2Self.)

There was 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 we have also updated the KDC and kadmin to more completely support S4U2Self.

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 in 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 */

One important change from 1.7 is that the KDC no longer requires that the S4U2Self user principal be an enterprise principal name (UPN). KDCs that assumed this must be updated.

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

The only public interface to Services4User is through GSS-API. 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.

These APIs are defined in gssapi_ext.h and were designed by Nicolas Williams. Note: a mechanism need only implement gss_acquire_cred_impersonate_XXX(), as the mechglue implements gss_add_cred_impersonate_XXX() in terms of it.

There is presently no support for certificate-based protocol transition.

S4U2Self

gss_acquire_cred_impersonate_name

OM_uint32 KRB5_CALLCONV
gss_acquire_cred_impersonate_name(
    OM_uint32 *,            /* minor_status */
    const gss_cred_id_t,    /* impersonator_cred_handle */
    const gss_name_t,       /* desired_name */
    OM_uint32,              /* time_req */
    const gss_OID_set,      /* desired_mechs */
    gss_cred_usage_t,       /* cred_usage */
    gss_cred_id_t *,        /* output_cred_handle */
    gss_OID_set *,          /* actual_mechs */
    OM_uint32 *);           /* time_rec */

This function uses impersonator_cred_handle to acquire a credential for desired_name. Only the initiator credential usage is supported by Kerberos. The returned credential can be passed into gss_init_sec_context(), and is a "proxy" credential (it can be used for constrained delegation without an explicit call to gss_acquire_cred_impersonate_cred(); think of it as equivalent to a delegated credential handle returned by gss_accept_sec_context()).

gss_add_cred_impersonate_name

OM_uint32 KRB5_CALLCONV
gss_add_cred_impersonate_name(
    OM_uint32 *,            /* minor_status */
    gss_cred_id_t,          /* input_cred_handle */
    const gss_cred_id_t,    /* impersonator_cred_handle */
    const gss_name_t,       /* desired_name */
    const gss_OID,          /* desired_mech */
    gss_cred_usage_t,       /* cred_usage */
    OM_uint32,              /* initiator_time_req */
    OM_uint32,              /* acceptor_time_req */
    gss_cred_id_t *,        /* output_cred_handle */
    gss_OID_set *,          /* actual_mechs */
    OM_uint32 *,            /* initiator_time_rec */
    OM_uint32 *);           /* acceptor_time_rec */

S4U2Proxy

gss_accept_sec_context

krb5_gss_accept_sec_context() has been changed such that, if the delegated credential handle parameter is non-NULL, and the verifier credential's usage is GSS_C_BOTH, a delegated credential handle will always be returned, and GSS_C_DELEG_FLAG will be set. This "proxy" credential can be passed directly to gss_init_sec_context() (for constrained delegation).

gss_acquire_cred_impersonate_cred

OM_uint32 KRB5_CALLCONV
gss_acquire_cred_impersonate_cred(
    OM_uint32 *,            /* minor_status */
    const gss_cred_id_t,    /* impersonator_cred_handle */
    const gss_cred_id_t,    /* subject_cred_handle */
    OM_uint32,              /* time_req */
    const gss_OID_set,      /* desired_mechs */
    gss_cred_usage_t,       /* cred_usage */
    gss_cred_id_t *,        /* output_cred_handle */
    gss_OID_set *,          /* actual_mechs */
    OM_uint32 *);           /* time_rec */

This function uses impersonator_cred_handle to acquire a credential for subject_cred_handle. Only the initiator credential usage is supported by Kerberos. The returned credential can be passed into gss_init_sec_context(). (For the Kerberos mechanism, the S4U2Proxy request is deferred until gss_init_sec_context(), because only at that point is the target name known.)

For Kerberos, subject_cred_handle will be used to acquire a service ticket to the impersonation principal. This is of somewhat dubious utility, as if you possess the subject's TGT, you can acquire credentials in the normal way. But this interface may be useful to other GSS mechanisms, and is implemented for completeness.

Note that typically applications will not need to call this function. Constrained delegated credential handles returned by gss_accept_sec_context(), as well as credential handles returned by gss_acquire_cred_impersonate_name(), can be passed directly into gss_init_sec_contxet().

gss_add_cred_impersonate_cred

OM_uint32 KRB5_CALLCONV
gss_add_cred_impersonate_cred(
    OM_uint32 *,            /* minor_status */
    gss_cred_id_t,          /* input_cred_handle */
    const gss_cred_id_t,    /* impersonator_cred_handle */
    const gss_cred_id_t,    /* subject_cred_handle */
    const gss_OID,          /* desired_mech */
    gss_cred_usage_t,       /* cred_usage */
    OM_uint32,              /* initiator_time_req */
    OM_uint32,              /* acceptor_time_req */
    gss_cred_id_t *,        /* output_cred_handle */
    gss_OID_set *,          /* actual_mechs */
    OM_uint32 *,            /* initiator_time_rec */
    OM_uint32 *);           /* acceptor_time_rec */

Implementation details

Most of the implementation can be found in src/lib/krb5/krb/s4u_creds.c.

A new flag, KRB5_GC_NO_STORE, has been added to all the krb5_get_credentials APIs, which causes the retrieved credentials to be not stored in the credentials cache. A summary of additional flags (API compatible with Heimdal, although the actual values may differ) is below:

#define KRB5_GC_NO_STORE        8       /* do not store in credentials cache */
#define KRB5_GC_FORWARDABLE             16  /* acquire forwardable tickets */
#define KRB5_GC_NO_TRANSIT_CHECK        32  /* disable transited check */
#define KRB5_GC_CONSTRAINED_DELEGATION  64  /* constrained delegation */

S4U2Self

API

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
  • if KRB5_GC_NO_STORE is unset, stores the ticket in the credentials cache

Both variants of S4U2Self are supported.

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;

KDC

  • S4U2Self client names are no longer required to be an enterprise principal name, as they were in 1.7
  • the Windows 2008 variant of S4U2Self is supported
  • kadmin has been updated to support the ok_to_auth_as_delegate (and, for completeness) no_auth_data_required attributes. Services with ok_to_auth_as_delegate are allowed to use S4U2Self

S4U2Proxy

API

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
  • if KRB5_GC_NO_STORE is unset, stores the ticket in the credentials cache

Note: most of the logic for this is now in krb5_get_credentials(), so we could probably remove this function entirely. The one difference is that, because krb5_get_credentials() doesn't have access to the decrypted second ticket, verifying the KDC response correctly is not possible. On the other hand, GSS now uses krb5_get_credentials() because it also has no access to the decrypted second ticket (although it does have enough information to verify that the KDC supports S4U2Proxy).

I am in favour of removing this API if we wish to remove the kvno support for S4U2Proxy.

KDC

There are no changes from 1.7. Note that the backend must validate that the evidence ticket was issued by the KDC. In the Windows case, this is done by validating the KDC signature on the PAC. (The MIT KDC will reject constrained delegation requests unless the backend implements a SIGN_AUTH_DATA method.)

Future ideas

  • 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

Implementation-wise, S4U2Self and S4U2Proxy are presently working against Windows 2008. A test program can be found in src/tests/gssapi/t_s4u.c. I have tested:

  • single-domain AD
  • two-domain AD (child domain)
  • three-domain AD (two tree roots)
  • MIT 1.8 KDC single-realm

Review

This section documents the review of the project according to Project policy. It is divided into multiple sections. First, approvals should be listed. To list an approval type

#~~~~

on its own line. The next section is for summarizing discussion, which should take place on krbdev@mit.edu. Provide links to the archive at http://mailman.mit.edu/pipermail/krbdev/ if appropriate. Blocking objections can be noted with {{project-block}}.

Approvals

sbuckley 2009-08-24

Discussion