logo_kerberos.gif

Difference between revisions of "Projects/IAKERB"

From K5Wiki
Jump to: navigation, search
(New page: {{project-early}} <includeonly>Category: early stage projects</includeonly> ==Background== Implement IAKERB ==Architecture== ==Implementation== ===libkrb5=== ===GSS=== ==Open is...)
 
 
(33 intermediate revisions by 2 users not shown)
Line 1: Line 1:
{{project-early}}
+
{{project-rel|1.9}}
 
<includeonly>[[Category: early stage projects]]</includeonly>
 
   
 
==Background==
 
==Background==
   
Implement IAKERB
 
  +
Implement [http://tools.ietf.org/html/draft-zhu-ws-kerb-03 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, including MIT Kerberos 1.7 and Windows 2000 (and above), support referrals.
   
 
==Architecture==
 
==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 a header indicating 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==
 
==Implementation==
   
 
===libkrb5===
 
===libkrb5===
  +
  +
New errors codes are defined:
  +
  +
<pre>
  +
#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 */
  +
</pre>
  +
  +
====ASN.1====
  +
  +
Encodes and decoders are introduced for the IAKERB-HEADER and IAKERB-FINISHED structures. The structures themselves are defined below:
  +
  +
<pre>
  +
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;
  +
</pre>
  +
  +
====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=====
  +
  +
<pre>
  +
void KRB5_CALLCONV
  +
krb5_init_creds_free(krb5_context context,
  +
krb5_init_creds_context ctx);
  +
</pre>
  +
  +
Free an initial credentials asynchronous context.
  +
  +
=====krb5_init_creds_get_creds=====
  +
  +
<pre>
  +
krb5_error_code KRB5_CALLCONV
  +
krb5_init_creds_get_creds(krb5_context context,
  +
krb5_init_creds_context ctx,
  +
krb5_creds *creds);
  +
</pre>
  +
  +
Copy acquired initial credentials into creds.
  +
  +
=====krb5_init_creds_store_creds=====
  +
  +
<pre>
  +
krb5_error_code KRB5_CALLCONV
  +
krb5_init_creds_store_creds(krb5_context context,
  +
krb5_init_creds_context ctx,
  +
krb5_ccache ccache);
  +
</pre>
  +
  +
Store acquired creds in ccache.
  +
  +
=====krb5_init_creds_get_error=====
  +
  +
<pre>
  +
krb5_error_code KRB5_CALLCONV
  +
krb5_init_creds_get_error(krb5_context context,
  +
krb5_init_creds_context ctx,
  +
krb5_error **error);
  +
</pre>
  +
  +
Get last error data from KDC, if any.
  +
  +
=====krb5_init_creds_init=====
  +
  +
<pre>
  +
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);
  +
</pre>
  +
  +
Initialise a new context for acquiring initial credentials asynchronously. Note: the context retains a pointer to ccache; ccache must be valid for the lifetime of the context.
  +
  +
=====krb5_init_creds_step=====
  +
  +
<pre>
  +
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);
  +
</pre>
  +
  +
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=====
  +
  +
<pre>
  +
krb5_error_code KRB5_CALLCONV
  +
krb5_init_creds_set_password(krb5_context context,
  +
krb5_init_creds_context ctx,
  +
const char *password);
  +
</pre>
  +
  +
Sets the password for acquiring initial credentials.
  +
  +
=====krb5_init_creds_get=====
  +
  +
<pre>
  +
krb5_error_code KRB5_CALLCONV
  +
krb5_init_creds_get(krb5_context context,
  +
krb5_init_creds_context ctx);
  +
</pre>
  +
  +
Acquires credentials synchronously.
  +
  +
====TGS====
  +
  +
New APIs are required to acquire credentials asynchronously using a TGT. Equivalent functionality does not exist in Heimdal, 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. However the code duplicate is minimal: the entire asynchronous ticket acquisition implementation is about 400 lines of code.
  +
  +
===== krb5_tkt_creds_init=====
  +
  +
<pre>
  +
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);
  +
</pre>
  +
  +
Initialise a new context handle for asynchronously acquiring a service ticket using a TGT.
  +
  +
===== krb5_tkt_creds_get_creds=====
  +
  +
<pre>
  +
krb5_error_code KRB5_CALLCONV
  +
krb5_tkt_creds_get_creds(krb5_context context,
  +
krb5_tkt_creds_context ctx,
  +
krb5_creds *creds);
  +
</pre>
  +
  +
Copy acquired credentials into creds.
  +
  +
===== krb5_tkt_creds_store_creds=====
  +
  +
<pre>
  +
krb5_error_code KRB5_CALLCONV
  +
krb5_tkt_creds_store_creds(krb5_context context,
  +
krb5_tkt_creds_context ctx,
  +
krb5_ccache ccache);
  +
</pre>
  +
  +
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=====
  +
  +
<pre>
  +
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)
  +
</pre>
  +
  +
Perform the next step of asynchronous credentials acquisition. The in buffer should be empty on the first call; on subsequent calls it must be set to the KDC reply. 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_tkt_creds_free=====
  +
  +
<pre>
  +
void KRB5_CALLCONV
  +
krb5_tkt_creds_free(krb5_context,
  +
krb5_tkt_creds_context);
  +
</pre>
  +
  +
Free a krb5_tkt_creds_context.
  +
 
===GSS===
 
===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. Actual credentials acquisition is deferred until context initialisation in the case of IAKERB.
  +
  +
====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. These variants are not used outside the Kerberos/IAKERB mechanisms; they are internal functions.
  +
* 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 distinct 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:
  +
  +
<pre>
  +
GSS_DLLIMP extern const gss_OID_desc * const gss_mech_iakerb;
  +
</pre>
  +
  +
Presently IAKERB is not the preferred mechanism negotiated via SPNEGO; initiators should request 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 KDC messages are wrapped inside IAKERB tokens. 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 appeared to have a bug with multiple-leg exchanges, resolved by the following patch:
  +
  +
<pre>
  +
@@ -830,7 +836,7 @@
  +
* we're done unless a MIC needs to be
  +
* generated/handled.
  +
*/
  +
- if (*send_token == CONT_TOKEN_SEND &&
  +
+ if (mechtok_in->length != 0 &&
  +
mechtok_out->length == 0 &&
  +
(!sc->mic_reqd ||
  +
!(sc->ctx_flags & GSS_C_INTEG_FLAG))) {
  +
@@ -852,7 +858,8 @@
  +
*send_token = ERROR_TOKEN_SEND;
  +
}
  +
*negState = REJECT;
  +
- }
  +
+ } else if (*send_token == NO_TOKEN_SEND)
  +
+ *send_token = CONT_TOKEN_SEND;
  +
</pre>
  +
  +
===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==
 
==Open issues==
  +
  +
The IAKERB mech returns a different (and less helpful) error than the krb5 mech when invoked with an expired TGT. This is because it is difficult to distinguish between an expired or missing credential with the krb5_get_credential API.
   
 
==Status==
 
==Status==
   
Code is in the [http://src.mit.edu/fisheye/browse/krb5/users/lhoward/iakerb users/lhoward/iakerb branch].
+
Code is in the [http://src.mit.edu/fisheye/browse/krb5/users/lhoward/iakerb users/lhoward/iakerb-refonly branch].

Latest revision as of 17:05, 30 April 2010

This project was completed in release 1.9.


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, including MIT Kerberos 1.7 and Windows 2000 (and above), 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 a header indicating 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 codes are defined:

#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

Encodes and decoders are introduced for the IAKERB-HEADER and IAKERB-FINISHED structures. The structures themselves are defined below:

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. Note: the context retains a pointer to ccache; ccache must be valid for the lifetime of the context.

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 in Heimdal, 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. However the code duplicate is minimal: the entire asynchronous ticket acquisition implementation is about 400 lines of code.

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 asynchronous credentials acquisition. The in buffer should be empty on the first call; on subsequent calls it must be set to the KDC reply. 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_tkt_creds_free
void KRB5_CALLCONV
krb5_tkt_creds_free(krb5_context,
        krb5_tkt_creds_context);

Free a krb5_tkt_creds_context.

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. Actual credentials acquisition is deferred until context initialisation in the case of IAKERB.

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. These variants are not used outside the Kerberos/IAKERB mechanisms; they are internal functions.
  • 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 distinct 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; initiators should request 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 KDC messages are wrapped inside IAKERB tokens. 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 appeared to have a bug with multiple-leg exchanges, resolved by the following patch:

@@ -830,7 +836,7 @@
                 * we're done unless a MIC needs to be
                 * generated/handled.
                 */
-               if (*send_token == CONT_TOKEN_SEND &&
+               if (mechtok_in->length != 0 &&
                    mechtok_out->length == 0 &&
                    (!sc->mic_reqd ||
                     !(sc->ctx_flags & GSS_C_INTEG_FLAG))) {
@@ -852,7 +858,8 @@
                        *send_token = ERROR_TOKEN_SEND;
                }
                *negState = REJECT;
-       }
+       } else if (*send_token == NO_TOKEN_SEND)
+               *send_token = CONT_TOKEN_SEND;

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

The IAKERB mech returns a different (and less helpful) error than the krb5 mech when invoked with an expired TGT. This is because it is difficult to distinguish between an expired or missing credential with the krb5_get_credential API.

Status

Code is in the users/lhoward/iakerb-refonly branch.