Projects/Client principal selection
This project's goal is to extend the core krb5 code to allow users to maintain credentials for multiple identities and for the GSSAPI client code to automatically select credentials based on the target service and hostname.
Scope
The following requirements are in scope for this project:
- Implement a collection-enabled cache type for Unix.
- Modify kinit and other tools to support collection-enabled caches.
- Allow client principal selection by GSSAPI apps based on the target service and hostname, using either:
- A configuration file in the user's homedir, or
- A realm heuristic.
The following requirements are out of scope:
- Prompting the user to acquire credentials when none are available.
- Prompting the user to decide on the client principal and remembering the answer.
- Selecting client identities based on application-provided hints such as account identifiers.
Background and previous work
Existing cache collection APIs
MIT krb5 and Heimdal implement the functions krb5_cccol_cursor_new(), krb5_cccol_cursor_next(), and krb5_cccol_cursor_free() to iterate over a global collection of credential caches. The caller does not provide any name or hint information for the location of the collection, other than a krb5 library context.
The MIT implementation of this API yields the context default cache, the default cache from the environment (if different), the OS default cache (if different), and then a series of caches determined by each available cache type. The FILE type yields all currently open (resolved and not closed) FILE caches. The MEMORY type yields all existing (created and not destroyed) memory ccaches within the process. The API type (on non-Unix platforms) yields all caches held by the daemon.
The Heimdal implementation of this API yields a series of caches determined by each available cache type. The FILE type yields the context default cache if it is of type FILE. The MEMORY type yields all existing memory caches within the process. The API and KCM types yield all caches held by the daemon for the uid of the requesting process.
Heimdal also supports the function krb5_cc_cache_match() to search the collection for a cache whose default principal matches a specified principal, and the functions krb5_cc_cache_get_first() and krb5_cc_cache_next() to iterate over the caches for a specified type.
In MIT krb5 and Heimdal, the function krb5_cc_new_unique() generates a new unique cache of a specified type. The function takes a "hint" parameter, but it is currently unused and there is no guidance on how to set it.
Heimdal supports the function krb5_cc_switch() to select a cache as the persistent default for its cache type. The function krb5_cc_support_switch() queries whether a cache type supports the switch operation.
KIM and KfM
The KIM library was designed within the consortium to meet the requirements of Kerberos for Macintosh. See the December 2007 board presentation for an introduction. Some salient design features include:
- KIM is implemented as an add-on library on top of libkrb5, often creating a layer on top of krb5 functionality such as credential cache manipulation.
- KIM relies on collection-enabled cache type called API (often referred to as CCAPI) which stores credentials in the memory of a daemon process.
- kinit and other client tools were changed to support cache collections using KIM API calls. Some API calls were implemented assuming a CCAPI default credential store.
- KIM relies on application integration in the form of hints. If an application provides no hints, default credentials are used.
- KIM provides a framework for prompting the user to select the client principal based on the application-provided hints and remembering the result.
- Certain features of KIM are relegated to OS-specific code, including the user agent and preference storage for remembering client principals.
Heimdal and OS X 10.7
OS X 10.7 (Lion) made a transition from MIT krb5 to Heimdal for its Kerberos implementation. Heimdal's kinit and other tools were modified to support cache collections in a manner similar to KfM's tools, but without assuming CCAPI credential store. The client identity prompting behavior may have been preserved through a separate implementation of the KIM functionality and GSSAPI extensions.
Design components
Additions to the ccache API
The krb5_cc_switch(), krb5_cc_support_switch(), and krb5_cc_cache_match() APIs from Heimdal will be added to MIT krb5. They have the following signatures:
krb5_boolean krb5_cc_support_switch(krb5_context context, const char *type); krb5_error_code krb5_cc_switch(krb5_context context, krb5_ccache id); krb5_error_code krb5_cc_cache_match(krb5_context context, krb5_principal client, krb5_ccache *cache_out);
An additional utility function will be added from Heimdal, as well as a complementary function to free a string (Heimdal uses a different name for this, krb5_xfree, which conflicts with an internal symbol and doesn't match our naming scheme for free functions):
krb5_error_code krb5_cc_get_full_name(krb5_context context, krb5_ccache cache, char **fullname_out); void krb5_free_string(krb5_context context, char *val);
The DIR credential cache type
Currently, no cache type available on Unix supports a persistent collection of caches. There is an early project to port CCAPI to Unix, but it is unclear how much time this would take to complete. Also, some deployments might prefer a more lightweight option, or an option which works over distributed filesystems.
A DIR cache is represented in the filesystem by a directory (which must be created out of band) containing zero or more cache files in FILE format, each with a name beginning with "tkt". The directory also contains a file named "primary" (which will be created automatically on first access) containing a single line naming the currently primary cache for the collection.
Caches of type DIR have one of two forms:
- DIR:dirname refers to the primary cache file within the directory for cache operations, and to the collection itself for type operations.
- DIR::pathname refers to a subsidiary cache file within the directory. Switching to a cache of this type updates the primary file to refer to the named file.
Because the krb5_cccol and krb5_cc_new_unique APIs do not name the collections they operate on, we use the context default cache name for this purpose. If the default cache name is of the form DIR:dirname, then the DIR cache type will add the caches within that directory to the collection as DIR::pathname caches and will generate new caches within that directory. If the default cache name is not of this form, the DIR type will yield no caches in the global connection, and attempting to create a new unique cache of the DIR type will return an error.
Tool alterations to use cache collection
The following tool behavior changes will be implemented, modeled after similar changes in Heimdal:
- kdestroy -a will destroy all caches in the collection.
- If the default cache type supports switching, "kinit princname" will search the collection for a matching cache and store credentials there, or will store credentials in a new unique cache of the default type if no existing cache for the principal exists. Either way, kinit will switch to the selected cache.
- klist -l will list the caches in the collection.
- klist -A will show the content of all caches in the collection.
- kswitch -p princname will search the collection for a matching cache and switch to it.
- kswitch -c cachename will switch to a specified cache.
kswitch is a new command.
Changes to the krb5_cccol implementation
The three "high-priority" caches (context default, environment default, and OS default) will be removed from the cache collection, so the cache collection iterates only over the per-type cursors. Instead, following Heimdal's lead, the FILE type will yield the default ccache if it is of type FILE, and will no longer yield the set of open file caches.
There are three reasons for this. First, it is inappropriate to include a default cache which has been overridden by another mechanism; for instance, kdestroy -A should not destroy the per-uid OS default file ccache if KRB5CCNAME has been set to something else. Second, if the default ccache name is of type DIR:dirname, yielding the default ccache in addition to all of the subsidiary DIR::pathname ccaches would wind up duplicating the primary ccache. Third, this change makes our behavior closer to Heimdal's, which is generally beneficial.
krb5_cc_select API and pluggable interface
A new libkrb5 API will be added to select a credential cache based on a server principal. It has the following signature:
krb5_error_code krb5_cc_select(krb5_context context, krb5_principal server, krb5_ccache *cache_out, krb5_principal *princ_out);
This function will be backed by a pluggable interface with the following signature:
typedef struct krb5_ccselect_moddata_st *krb5_ccselect_moddata; #define KRB5_CCSELECT_PRIORITY_AUTHORITATIVE 2 #define KRB5_CCSELECT_PRIORITY_HEURISTIC 1 typedef krb5_error_code (*krb5_ccselect_init_fn)(krb5_context context, krb5_ccselect_moddata *data, int *priority_out); typedef krb5_error_code (*krb5_ccselect_choose_fn)(krb5_context context, krb5_ccselect_moddata data, krb5_principal server, krb5_ccache *cache_out, krb5_principal *princ_out); typedef void (*krb5_ccselect_fini_fn)(krb5_context context, krb5_ccselect_moddata data); typedef struct krb5_ccselect_vtable_st { const char *name; krb5_ccselect_open_fn init; krb5_ccselect_choose_fn choose; krb5_ccselect_close_fn fini; } *krb5_ccselect_vtable;
Like server location modules, ccselect modules can return KRB5_PLUGIN_NO_HANDLE to defer to other modules. If no module returns a value, krb5_ccselect returns KRB5_CC_NOTFOUND.
A primitive form of hardcoded module ordering is present in the interface, to allow modules to declare themselves authoritative (reflective of deliberate configuration) or heuristic. It will probably become desirable to allow admins to configure the module order. This problem is deferred to the plugin framework, as it also applies to other pluggable interfaces which will likely exist within the framework (KDC location, kuserok).
k5identity module
The k5identity ccselect module consults a $HOME/.k5identity file to determine a client principal for the server principal. If one is found, it attempts to find a cache in the collection matching the client principal.
The .k5identity file is processed line by line. Lines containing only whitespace or beginning with '#' are ignored. Each line is split into whitespace-separated fields. The first field is a principal name and the remaining fields are constraints of the form name:value. Valid constraint names are "service", "host", and "realm". Constraint values are fnmatch expressions matching against the service component, hostname component, and (if known) realm component of the principal.
realm module
The realm ccselect module implements a simple realm heuristic. If the realm of the server principal is known, the default cache and then the cache collection are searched for a client principal within the same realm.
GSSAPI behavior changes
The GSSAPI initiator code will be changed to use the cache collection. There are three cases to consider:
1. The most common case is that the application passes a null initiator cred handle to gss_init_sec_context. This case is relatively simple; we will use krb5_cc_select to choose a client identity from the collection.
2. The application can acquire a credential with no name and pass that credential to gss_init_sec_context. This case is complicated because RFC 2744 section 5.5 requires that if a default credential is queried and the resulting name exported, we get the same name as an acceptor would see on successful authentication using that credential. This means we need to treat a default credential like the box in the Schroedinger's Cat experiment: as long as the application does not ask for the credential's name, we leave it undetermined until gss_init_sec_context time so that we can use krb5_cc_select. If the application does ask, we resolve the credential's state to the default identity.
Further complicating matters, the mechglue's gss_acquire_cred asks for the (first) mechanism's name immediately after acquiring credentials, which would prematurely collapse the waveform. To resolve this, the mechglue will be changed to remove the auxinfo structure caching information in union creds.
3. The application can acquire a credential with a specified principal name (or other name type which is converted to a principal name). In this case we will use krb5_cc_cache_match to find an appropriate cache within the collection.
Release notes
End-user experience:
- Add the DIR credential cache type, which can hold a collection of credential caches.
- Enhance kinit, klist, and kdestroy to support credential cache collections if the cache type supports it.
- Add the kswitch command, which changes the selected default cache within a collection.
- Add heuristic support for choosing client credentials based on the service realm.
- Add support for $HOME/.k5identity, which allows credential choice based on configured rules.