Projects/Keytab initiation
This project is to add support for automatically getting credentials for gss-krb5 security context initiation using a keytab.
Contents
Overview
To initiate a gss-krb5 security context, one needs credentials (typically a TGT). During a user login session, these credentials are typically obtained ahead of time by the login system or using kinit (or on Windows or OSX, by bringing up a UI prompt from inside the GSSAPI functions). When a service wishes to communicate with another service, it uses its keytab to acquire credentials. Currently, it is necessary to use kinit -k or k5start out of band in order to ensure that credentials are available for the server's use. This project is to automate this process within the GSSAPI calls.
Requirements
- The system should work, perhaps with some external configuration, for a typical caller which invokes gss_init_sec_context() with no initiator cred (or, equivalently, acquires an initiator cred with no desired name).
- The system should also behave as well as possible when the caller does supply a desired name, or when krb5_cc_select() can deduce a client principal from the target name. In this case, multiple principals from the same keytab might be used for different initiations.
- The system should work well with credential cache collections (see Projects/Client_principal_selection). For example, an application should be able to, from the same process, initiate two security contexts with different desired names, using the same keytab, and have the resulting credentials both be cached within the collection (assuming the default ccache is collection-enabled).
- There should be minimal or no surprise. An existing deployment of GSSAPI applications should not begin doing keytab-based initiation and write to the default per-uid ccache without reason to believe this is intended.
- Credentials obtained this way should be cached. Otherwise, applications which initialize many security contexts (such as HTTP clients) could generate large numbers of unnecessary AS and TGS requests.
It is not required for this system to work with initiators like krlogin which directly use libkrb5. However, some of the underlying components may be useful for FAST armor ccaches, so putting reusable parts of the code in libkrb5 is desirable.
Design
Default client keytab
libkrb5 provides an API for discovering the default keytab (krb5_kt_default()). The underlying mechanism for discovering this keytab may be an environment variable (KRB5_KTNAME), a profile relation (default_keytab_name under libdefaults), or a hardcoded default (/etc/krb5.keytab). This keytab is currently used, absent application input the contrary, for accepting security contexts with gss_accept_sec_context or krb5_rd_req.
Automated keytab initiation will not make use of this keytab. Instead, there will be a new complementary "default client keytab", obtained with krb5_kt_client_default(), with its own set of underlying discovery mechanisms. Unless an administrator sets up a client keytab, automated keytab initiation will not take place, thus minimizing surprise.
Initially, the default client keytab will have the following discovery mechanisms, in decreasing priority order:
- The KRB5_CLIENT_KTNAME environment variable (analgous to KRB5_KTNAME)
- The default_client_keytab_name profile relation in libdefaults (analagous to default_keytab_name).
- A hardcoded default, FILE:${localstatedir}/krb5/user/%{euid}/client.keytab.
The following discovery mechanisms will not be provided until we determine a need for them:
- A revision of gss_krb5_import_cred with a client keytab argument
- A GSS extension similar to krb5_gss_register_acceptor_identity
After Projects/Credential_Store_extensions is complete, we will likely add a cred store URN for the client keytab.
Caching and refreshing
Initial credentials obtained through automated keytab initiation will be cached in the default ccache or, if the default ccache type supports it, the associated collection. The new ccache will remember, using the "refresh_time" ccache config variable, the time of the next scheduled attempt to refresh the cache. Initially this time will be set to halfway between the TGT's start and end time; it will be incremented by 30 seconds before attempting to refresh in order to avoid trying to refresh too often. The gss-krb5 initiator code will attempt to refresh the cache with the keytab again if the current time is equal to or greater than the refresh time.
Code changes
Introducing a client keytab requires a careful re-evaluation of the lifetime of an initiator credential, which has three or four stages:
- acquire_init_cred, called from gss_acquire_cred, records the information given by the caller (which may include client principal, ccache, client keytab, and/or password), infers a ccache from the supplied client principal or vice versa if either is given, detects errors based on the information supplied, and acquires initial credentials if necessary and possible. If neither client principal nor ccache was specified, resolution of the client principal is deferred until the next stage.
- kg_cred_resolve, called from gss_init_sec_context or gss_inquire_cred, forces resolution of the client principal, krb5_cc_select if the target principal is known. A ccache is then chosen if necessary, and initial credentials acquired if necessary and possible.
- If the IAKERB mechanism was requested, iakerb_get_initial_state sets up an AS request for getting initial credentials or a TGS request for the service ticket.
- get_credentials, called from gss_init_sec_context, gets a service ticket from the ccache (making a TGS request if necessary, in the non-IAKERB case).
The client principal is determined by, in order of preference:
- (Stage 1) The caller's desired_name parameter if given.
- (Stage 1) The principal of the caller-specified ccache, if it is initialized.
- (Stage 2) The principal determined by krb5_cc_select using the target principal.
- (Stage 2) The principal of the default ccache, if it exists.
- (Stage 1 or 2) The first principal listed in the client keytab, if we have one. (If we choose this, we need to re-check the collection for an existing ccache for this principal.) This choice is used in stage 1 if the caller specifies an uninitialized ccache and no client principal, since it is not possible to use the cache collection in that case.
- Give up and return an error
If it is necessary to choose a ccache based on the client principal, the following methods are used in order of preference. An uninitialized ccache is only chosen if we detect that we can acquire initial credentials for the client principal, either with a caller-supplied password or with the client keytab.
- An existing ccache in the collection for the client principal.
- The uninitialized default cache, if it is not collection-enabled or if credentials are to be obtained by password.
- A new unique ccache within the collection.
We return an error in stage 1 or 2 if:
- Client principal and ccache are caller-specified, and ccache contains creds for a different principal
- Client principal is known, there is a default ccache which is not collection-enabled, and it contains creds for a different principal
- Client principal is known, there are no creds available for it in the collection (or caller-specified ccache), and we have no way to acquire creds for it
- ccache is known but is empty, and we have no way to acquire creds
- There are no creds at all in the collection and we have no way to acquire creds for any principal
Initial credentials are acquired in stage 1 or 2, after the client principal and ccache are known, if:
- ccache contains a config variable indicating that it was derived from the client keytab, and it is time for a refresh. (If this fails, we will continue on and try to use ccache if it contains current creds.)
- ccache doesn't contain a current TGT and we have a password
- ccache doesn't contain a current TGT and we have a client keytab containing a key for the client principal
(Note: as of krb5 1.10, getting initial credentials with a password is deferred until get_credentials, even if IAKERB is not in use. Heimdal does not defer this, and we will no longer defer this after the restructuring.)
Testing
src/appl/gss-sample/t_gss_sample.py will be amended to try the krb5, krb5-DCE, SPNEGO, and IAKERB context establishments using a client keytab.
A new test script under src/tests/gssapi, called t_client_keytab.py, will test the following cases:
- no name/cache specified, pick first principal from client keytab
- no name/cache specified, pick principal from k5identity
- no name/cache specified, default ccache has name but no creds
- name specified, non-collectable default cache doesn't match
- name specified, nonexistent default cache
- name specified, matches default cache, time to refresh
- empty ccache specified, pick first principal from client keytab
- ccache specified with name but no creds; name not in client keytab
- ccache specified with name but no creds; name in client keytab
- ccache specified with creds, time to refresh
- name specified, matching cache in collection with no creds
- name specified, matching cache in collection, time to refresh
- name specified, collection has default for different principal
- name specified, collection has no default cache
Two new small C programs, ccinit and ccrefresh, will be used by this script to initialize an empty ccache and to set or get the refresh_time value on a ccache. To test credential acquisition and context establishment, the existing t_ccselect and t_imp_cred programs will be used.
Documentation
The new krb5_kt_client_default() API will receive Doxygen markup and be added to the API reference.
The new environment variable KRB5_CLIENT_KTNAME will be documented in env_variables.rst.
The new profile relation default_client_keytab_name will be documented in krb5_conf.rst.
The behavior of acquiring initiator credentials will be documented in a new section of gssapi.rst named "Initiator credentials".
Mailing list discussions
Here is the primary design discussion thread, which was broken into four pieces in the archives:
- http://mailman.mit.edu/pipermail/krbdev/2012-May/010871.html
- http://mailman.mit.edu/pipermail/krbdev/2012-June/010885.html
- http://mailman.mit.edu/pipermail/krbdev/2012-June/010901.html
- http://mailman.mit.edu/pipermail/krbdev/2012-June/010948.html
The project writeup review thread is at:
Release notes
Administrator experience:
- Add support for a "default client keytab". Its location is determined by the KRB5_CLIENT_KTNAME environment variable, the default_client_keytab profile relation, or a hardcoded path (TBD).
- GSSAPI initiator applications can now acquire credentials automatically from the default client keytab, if one is available.
Developer experience:
- Add a new API krb5_kt_client_default() to resolve the default client keytab.