November 21, 2006
This article was contributed by Jake Edge.
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
Two standard key types are defined:
key_type_user and
key_type_keyring. New
key types can be registered by filesystems using:
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.
(
Log in to post comments)