LWN.net Logo

Kernel key management

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)

Kernel key management

Posted Nov 22, 2006 13:44 UTC (Wed) by NAR (subscriber, #1313) [Link]

I was wondering - what's better (faster, safer, etc.) for local disks (e.g. for disks in laptops that could be easily stolen): encrypt a filesystem or encrypt the block device under the filesystem?

Bye,NAR

Kernel key management

Posted Nov 22, 2006 19:21 UTC (Wed) by arjan (subscriber, #36785) [Link]

technically it's "Safer" to encrypt the device as a whole, there can't then be leaked blocks from files that moved all over the disk..

HOwever encrypting files is more convenient for several things: for example if you need to legally remove all data from a certain person... all you need to do is destroy his key now, and that takes care automatically of all your backup tapes too ;)

Kernel key management

Posted Nov 22, 2006 16:15 UTC (Wed) by kleptog (subscriber, #1183) [Link]

Seems to me that this could pave the way for pervasive authentication. When a user logs in (say with a some token) that could be stored in the session keyring. This would then be available for any subprocess (it's preserved over fork, clone, execve, etc) to authenticate itself to something else. Sort of like ssh-agent on steriods.

Or am I missing something important?

ssh-agent

Posted Nov 23, 2006 17:19 UTC (Thu) by ncm (subscriber, #165) [Link]

Seems like ssh-agent itself would be a good choice for this role.

ssh-agent

Posted Nov 23, 2006 18:07 UTC (Thu) by kleptog (subscriber, #1183) [Link]

Hmm, the reason why I think ssh-agent isn't up to the job are two-fold:

- The link to the agent is via an environment variable, which isn't preserved in a variety of situations.
- One issue with ssh-agent is that any root user can use any agents on the machine, since the only access control once the agent is started is access to the socket.

The stuff in this article can fix both since preservation is guarenteed and the keys can be protected even from root (barring direct memory reads ofcourse).

ssh-agent

Posted Nov 24, 2006 18:44 UTC (Fri) by dlang (✭ supporter ✭, #313) [Link]

and the fact that root can su to any other user

Copyright © 2006, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds