Kernel key management
Filesystems, especially remote filesystems, may require some authentication or a key to enable access; the kernel key management interface provides hooks to store and manage this kind of information. The hooks come in two flavors; one used by the kernel to find keys for subsystems that require them and one used by userspace programs to manage keys. The intent is to provide a fast mechanism for the kernel to access the keys that it needs and to push the add, modify and delete operations into userspace.
'Key' is the term used, but it may not be keys in the traditional, cryptographic sense that are stored. Any kind of authentication or access information can be stored as a key; it is essentially an opaque chunk of data that is only interpreted by the kernel subsystem that is interested in it. While filesystems are the main target of the API, any kernel subsystem that requires this kind of information could use it.
At the core, keys are stored in the aptly named struct key which has the following kinds of fields:
- a unique serial number
- a key type that can identify the filesystem that the key belongs to
- a description string that is used for searching for the key
- a payload that contains the actual key data
- user and group information including permissions
- an expiration time
- a key state that tracks instantiation, revocation, deletion, etc.
The key types provide a way for a filesystem to configure its own set of key operations. The operations that a key type can specify are:
- instantiate - create a key of that type
- update - modify a key
- match - match a key to a description, which is used in the key search
- revoke - clear some key data and change the state to KEY_FLAG_REVOKED
- destroy - clear all key data
- describe - summarize the key's description and payload as text
- read - read the key data
- request_key - called when the key is not available in order to retrieve the key from elsewhere
int register_key_type(struct key_type *type);
When the kernel needs to find a key, it calls:
struct key *request_key(const struct key_type *type,
const char *description,
const char *callout_string);
It passes the type and description and the match
function from the struct key_type is used to try and find a matching
key. If no key is found, and callout_string is not NULL,
the kernel will invoke /sbin/request-key, which
attempts to obtain the necessary key from userspace.
The payload field of a key can be accessed once the key has been found, but if it is more complex than a simple integer, some arrangement must be made to prevent simultaneous reads and writes. Support for semaphore locking or Read-Copy-Update (RCU) are present in the key structure and must be used unless the key type has no modification methods. Once the filesystem is done with the key, it should be released with:
void key_put(struct key *key);
Keyrings are, as the name implies, collections of related keys and there are various calls to manipulate them. Each process is associated with three keyrings: a thread-specific keyring, a process-specific keyring and a session-specific keyring. These are the keyrings searched when a request_key is issued. Each user on the system is associated with a user-specific keyring; a default user session keyring used to initialize the session-specific keyring when a process changes its real user id.
Permissions for keys are stored in a bit field, much like Linux file permissions, but are more extensive. Each key has a user and group id and a permissions mask for each of four potential accessors: possessor, user, group, and other. The mask consists of six bits:
- view - allows a key or keyring's attributes to be viewed
- read - allows a key's payload or a keyring's list of keys to be viewed
- write - allows creating or modifying a key's payload or keyring's list of keys
- search - allows keys to be found and keyrings to be searched
- link - allows the key or keyring to be linked into another keyring
- set attribute - allows the key's user id, group id, and permissions mask to be changed
The userspace API consists of the three main system calls:
key_serial_t add_key(const char *type, const char *desc,
const void *payload, size_t plen,
key_serial_t keyring);
key_serial_t request_key(const char *type, const char *description,
const char *callout_info,
key_serial_t dest_keyring);
key_serial_t keyctl(int cmd, key_serial_t id, int create);
add_key() adds a key to the keyring specified. request_key(),
much like its kernel-side counterpart,
searches for the key based on the type and description, possibly calling
out to userspace if callout_info is non-NULL. It can also attach
the key to the specified destination keyring if it is found. keyctl()
is an ioctl-like interface that provides for the management of keys.
<linux/keyctl.h> contains 17 separate commands for
updating, changing permissions, searching, linking, reading and the like.
The /bin/keyctl command-line utility, part of the keyutils package, provides an easy interface to the userspace system calls to facilitate working with keys from userspace. Also, the /proc/keys and /proc/key-users entries in procfs enable a user to view the keys and key users currently managed by the kernel.
The only filesystem in the current 2.6 tree that uses the key management API is eCryptfs, a stacked filesystem that encrypts its data using a password and optional salt. It uses the user key type rather than creating its own type and does not directly support userspace callbacks. Instead it uses the mount.ecryptfs command to prompt the user for the password and stores that as the key.
According to slides from Dave Howells' talk at the 2006 Ottawa Linux Symposium (available here), several other filesystems (including CIFS, NFSv4 and AFS) are planning to use the API in the future. For more information, extensive documentation can be found in the kernel tree in Documentation/keys.txt and Documentation/keys-request-key.txt.
Overall, this looks to be a useful interface for kernel subsystems that require keys and, in keeping with kernel tradition, most of the policy and management pieces are pushed out to userspace. It provides all of the capabilities that one would expect and hopefully more kernel subsystems will be using it in the future.
| Index entries for this article | |
|---|---|
| GuestArticles | Edge, Jake |