Projects/IAKERB
Contents
Background
Implement IAKERB. IAKERB is a protocol for proxying KDC exchanges via GSS-API.
Note: the implementation requires the KDC to support referrals to work in cross-realm environments. Making the non-referral cross-realm heuristics asynchronous will be a considerable amount of work. Most modern KDCs support referrals.
Architecture
IAKERB specifies a mechanism by which a GSS initiator can proxy KDC messages via a GSS acceptor. In order to do this, we must separate the client-side processing of KDC messages from their transmission to the KDC. This requires as asynchronous interface to the credentials acquisition functions, one which does not call sendto_kdc() but instead returns a buffer that can be forwarded to the acceptor.
The IAKERB exchange consists of the aforementioned KDC exchange, wrapped in a IAKERB token (which contains the realm to which to send the request and an optional cookie). A checksum of the entire IAKERB conversation is included in the GSS checksum. Once the IAKERB exchange is complete, the Kerberos mechanism proceeds as normal. If the client already has credentials for the target service, the IAKERB exchange may be skipped entirely. If the client already has a TGT, then the AS-REQ may be skipped.
Implementation
libkrb5
New errors:
#define KRB_AP_ERR_IAKERB_KDC_NOT_FOUND 85 /* The IAKERB proxy could not find a KDC */ #define KRB_AP_ERR_IAKERB_KDC_NO_RESPONSE 86 /* The KDC did not respond to the IAKERB proxy */
ASN.1
Helper functions are introduced to encode IAKERB-HEADER and IAKERB-FINISHED structures.
typedef struct _krb5_iakerb_header { krb5_data target_realm; krb5_data *cookie; } krb5_iakerb_header; typedef struct _krb5_iakerb_finished { krb5_checksum checksum; } krb5_iakerb_finished;
AS
New APIs are required to acquire initial credentials asynchronously. These APIs are also present in Heimdal (although krb5_init_creds_store_creds() is a MIT addition). The synchronous APIs are re-factored in terms of the asynchronous APIs.
krb5_init_creds_free
void KRB5_CALLCONV krb5_init_creds_free(krb5_context context, krb5_init_creds_context ctx);
Free an initial credentials asynchronous context.
krb5_init_creds_get_creds
krb5_error_code KRB5_CALLCONV krb5_init_creds_get_creds(krb5_context context, krb5_init_creds_context ctx, krb5_creds *creds);
Copy acquired initial credentials into creds.
krb5_init_creds_store_creds
krb5_error_code KRB5_CALLCONV krb5_init_creds_store_creds(krb5_context context, krb5_init_creds_context ctx, krb5_ccache ccache);
Store acquired creds in ccache.
krb5_init_creds_get_error
krb5_error_code KRB5_CALLCONV krb5_init_creds_get_error(krb5_context context, krb5_init_creds_context ctx, krb5_error **error);
Get last error data from KDC, if any.
krb5_init_creds_init
krb5_error_code KRB5_CALLCONV krb5_init_creds_init(krb5_context context, krb5_principal client, krb5_prompter_fct prompter, void *data, krb5_deltat start_time, krb5_get_init_creds_opt *options, krb5_init_creds_context *pctx);
Initialise a new context for acquiring initial credentials asynchronously.
krb5_init_creds_step
krb5_error_code KRB5_CALLCONV krb5_init_creds_step(krb5_context context, krb5_init_creds_context ctx, krb5_data *in, krb5_data *out, krb5_data *realm, unsigned int *flags);
Performs next step of asynchronous initial credentials acquisition. The in buffer is the KDC response (empty on the first call). The out buffer is to be sent by the caller to the KDC, for the indicated realm. Once the credentials have been acquired, the out buffer is empty and flags is set to 1. If the request should be retried with TCP, KRB5KRB_ERR_RESPONSE_TOO_BIG is returned.
krb5_init_creds_set_password
krb5_error_code KRB5_CALLCONV krb5_init_creds_set_password(krb5_context context, krb5_init_creds_context ctx, const char *password);
Sets the password for acquiring initial credentials.
krb5_init_creds_get
krb5_error_code KRB5_CALLCONV krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx);
Acquires credentials synchronously.
TGS
New APIs are required to acquire credentials asynchronously using a TGT. Equivalent functionality does not exist elsewhere, but they are modelled on the synchronous APIs and on the initial credentials asynchronous APIs. Note the synchronous APIs are not yet factored in terms of these, because of the referrals-only limitation mentioned previously.
krb5_tkt_creds_init
krb5_error_code KRB5_CALLCONV krb5_tkt_creds_init(krb5_context context, krb5_ccache ccache, krb5_creds *creds, int kdcopt, krb5_tkt_creds_context *pctx);
Initialise a new context handle for asynchronously acquiring a service ticket using a TGT.
krb5_tkt_creds_get_creds
krb5_error_code KRB5_CALLCONV krb5_tkt_creds_get_creds(krb5_context context, krb5_tkt_creds_context ctx, krb5_creds *creds);
Copy acquired credentials into creds.
krb5_tkt_creds_store_creds
krb5_error_code KRB5_CALLCONV krb5_tkt_creds_store_creds(krb5_context context, krb5_tkt_creds_context ctx, krb5_ccache ccache);
Store acquired credentials and any TGTs in ccache. If ccache is NULL, the ccache the context was initialised with is used.
krb5_tkt_creds_step
krb5_error_code KRB5_CALLCONV krb5_tkt_creds_step(krb5_context context, krb5_tkt_creds_context ctx, krb5_data *in, krb5_data *out, krb5_data *realm, unsigned int *flags)
Perform the next step of credentials acquisition. The in buffer should be empty on the first call; on subsequent calls it must be set to the KDC reply. The caller must send the out buffer to a KDC for realm; this is set on output. Once the credentials have been acquired, flags is set to 1.
GSS
Mechglue
The gss_acquire_cred_with_password() method from the Sun mechglue code drop is resurrected. This acquires a credentials handle given a password.
Kerberos mech
- New internal variants of gss_init_sec_context() and gss_accept_sec_context() are introduced which pass an extensible (internal) structure, krb5_gss_ctx_ext_t. This is presently used only for carrying the IAKERB conversation to be checksummed in the GSS authenticator.
- Support is added for acquiring credentials using a password. In the IAKERB case, the password is stashed in the credential; otherwise the credentials are acquired immediately.
IAKERB mech
The IAKERB mechanism is implemented as a separate mechanism within the Kerberos mechanism; however, it shares all methods and data structures except for those related to the context. In the case where a IAKERB method could be passed either a Kerberos or a IAKERB context, magic numbers are used to disambiguate (similar to the SPNEGO mechanism).
The IAKERB mechanism is identified by the following exported symbol:
GSS_DLLIMP extern const gss_OID_desc * const gss_mech_iakerb;
Presently IAKERB is not the preferred mechanism negotiated via SPNEGO; you must select it explicitly. Note that if the initiator has a ticket for the target service, then IAKERB will be skipped, even if the IAKERB mechanism was requested.
iakerb_gss_init_sec_context()
This is implemented as a state machine with three states: AS-REQ, TGS-REQ and AP-REQ. The first two states call into the libkrb5 asynchronous ticket acquisition APIs. The latter state is handled by directly calling the Kerberos 5 GSS mechanism. Once a context is complete, then the native Kerberos context handle is returned to the initiator; this avoids modifying any other Kerberos mechanism methods to be explicitly aware of IAKERB. The IAKERB conversation is saved and used to generate the IAKERB-FINISHED checksum.
iakerb_gss_accept_sec_context()
This method unpacks the IAKERB token and forwards it to the KDC for the indicated realm. All tokens after and including the first non-IAKERB token are forwarded to the Kerberos 5 GSS mechanism. The IAKERB conversation is saved and used to verify the IAKERB-FINISHED checksum.
SPNEGO mech
The SPNEGO mechanism was modified to handle multiple leg context exchanges. There appeared to be some bugs here.
gss-sample
gss-sample is enhanced to support acquiring credentials with a password and specifying IAKERB and SPNEGO mechanisms using command-line options.
Open issues
Status
Code is in the users/lhoward/iakerb-refonly branch.