Skip to content

Commit

Permalink
KEYS: Make request_key() and co fundamentally asynchronous
Browse files Browse the repository at this point in the history
Make request_key() and co fundamentally asynchronous to make it easier for
NFS to make use of them.  There are now accessor functions that do
asynchronous constructions, a wait function to wait for construction to
complete, and a completion function for the key type to indicate completion
of construction.

Note that the construction queue is now gone.  Instead, keys under
construction are linked in to the appropriate keyring in advance, and that
anyone encountering one must wait for it to be complete before they can use
it.  This is done automatically for userspace.

The following auxiliary changes are also made:

 (1) Key type implementation stuff is split from linux/key.h into
     linux/key-type.h.

 (2) AF_RXRPC provides a way to allocate null rxrpc-type keys so that AFS does
     not need to call key_instantiate_and_link() directly.

 (3) Adjust the debugging macros so that they're -Wformat checked even if
     they are disabled, and make it so they can be enabled simply by defining
     __KDEBUG to be consistent with other code of mine.

 (3) Documentation.

[[email protected]: keys: missing word in documentation]
Signed-off-by: David Howells <[email protected]>
Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
dhowells authored and Linus Torvalds committed Oct 17, 2007
1 parent 398c95b commit 76181c1
Show file tree
Hide file tree
Showing 14 changed files with 605 additions and 435 deletions.
25 changes: 21 additions & 4 deletions Documentation/keys-request-key.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,19 @@ or:
const char *callout_string,
void *aux);

or:

struct key *request_key_async(const struct key_type *type,
const char *description,
const char *callout_string);

or:

struct key *request_key_async_with_auxdata(const struct key_type *type,
const char *description,
const char *callout_string,
void *aux);

Or by userspace invoking the request_key system call:

key_serial_t request_key(const char *type,
Expand All @@ -32,10 +45,14 @@ does not need to link the key to a keyring to prevent it from being immediately
destroyed. The kernel interface returns a pointer directly to the key, and
it's up to the caller to destroy the key.

The request_key_with_auxdata() call is like the in-kernel request_key() call,
except that it permits auxiliary data to be passed to the upcaller (the default
is NULL). This is only useful for those key types that define their own upcall
mechanism rather than using /sbin/request-key.
The request_key*_with_auxdata() calls are like the in-kernel request_key*()
calls, except that they permit auxiliary data to be passed to the upcaller (the
default is NULL). This is only useful for those key types that define their
own upcall mechanism rather than using /sbin/request-key.

The two async in-kernel calls may return keys that are still in the process of
being constructed. The two non-async ones will wait for construction to
complete first.

The userspace interface links the key to a keyring associated with the process
to prevent the key from going away, and returns the serial number of the key to
Expand Down
93 changes: 79 additions & 14 deletions Documentation/keys.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

This service allows cryptographic keys, authentication tokens, cross-domain
user mappings, and similar to be cached in the kernel for the use of
filesystems other kernel services.
filesystems and other kernel services.

Keyrings are permitted; these are a special type of key that can hold links to
other keys. Processes each have three standard keyring subscriptions that a
Expand Down Expand Up @@ -726,6 +726,15 @@ call, and the key released upon close. How to deal with conflicting keys due to
two different users opening the same file is left to the filesystem author to
solve.

To access the key manager, the following header must be #included:

<linux/key.h>

Specific key types should have a header file under include/keys/ that should be
used to access that type. For keys of type "user", for example, that would be:

<keys/user-type.h>

Note that there are two different types of pointers to keys that may be
encountered:

Expand Down Expand Up @@ -791,6 +800,36 @@ payload contents" for more information.
passed to the key_type->request_key() op if it exists.


(*) A key can be requested asynchronously by calling one of:

struct key *request_key_async(const struct key_type *type,
const char *description,
const char *callout_string);

or:

struct key *request_key_async_with_auxdata(const struct key_type *type,
const char *description,
const char *callout_string,
void *aux);

which are asynchronous equivalents of request_key() and
request_key_with_auxdata() respectively.

These two functions return with the key potentially still under
construction. To wait for contruction completion, the following should be
called:

int wait_for_key_construction(struct key *key, bool intr);

The function will wait for the key to finish being constructed and then
invokes key_validate() to return an appropriate value to indicate the state
of the key (0 indicates the key is usable).

If intr is true, then the wait can be interrupted by a signal, in which
case error ERESTARTSYS will be returned.


(*) When it is no longer required, the key should be released using:

void key_put(struct key *key);
Expand Down Expand Up @@ -924,7 +963,11 @@ DEFINING A KEY TYPE

A kernel service may want to define its own key type. For instance, an AFS
filesystem might want to define a Kerberos 5 ticket key type. To do this, it
author fills in a struct key_type and registers it with the system.
author fills in a key_type struct and registers it with the system.

Source files that implement key types should include the following header file:

<linux/key-type.h>

The structure has a number of fields, some of which are mandatory:

Expand Down Expand Up @@ -1053,22 +1096,44 @@ The structure has a number of fields, some of which are mandatory:
as might happen when the userspace buffer is accessed.


(*) int (*request_key)(struct key *key, struct key *authkey, const char *op,
(*) int (*request_key)(struct key_construction *cons, const char *op,
void *aux);

This method is optional. If provided, request_key() and
request_key_with_auxdata() will invoke this function rather than
upcalling to /sbin/request-key to operate upon a key of this type.
This method is optional. If provided, request_key() and friends will
invoke this function rather than upcalling to /sbin/request-key to operate
upon a key of this type.

The aux parameter is as passed to request_key_async_with_auxdata() and
similar or is NULL otherwise. Also passed are the construction record for
the key to be operated upon and the operation type (currently only
"create").

This method is permitted to return before the upcall is complete, but the
following function must be called under all circumstances to complete the
instantiation process, whether or not it succeeds, whether or not there's
an error:

void complete_request_key(struct key_construction *cons, int error);

The error parameter should be 0 on success, -ve on error. The
construction record is destroyed by this action and the authorisation key
will be revoked. If an error is indicated, the key under construction
will be negatively instantiated if it wasn't already instantiated.

If this method returns an error, that error will be returned to the
caller of request_key*(). complete_request_key() must be called prior to
returning.

The key under construction and the authorisation key can be found in the
key_construction struct pointed to by cons:

(*) struct key *key;

The key under construction.

The aux parameter is as passed to request_key_with_auxdata() or is NULL
otherwise. Also passed are the key to be operated upon, the
authorisation key for this operation and the operation type (currently
only "create").
(*) struct key *authkey;

This function should return only when the upcall is complete. Upon return
the authorisation key will be revoked, and the target key will be
negatively instantiated if it is still uninstantiated. The error will be
returned to the caller of request_key*().
The authorisation key.


============================
Expand Down
7 changes: 7 additions & 0 deletions Documentation/networking/rxrpc.txt
Original file line number Diff line number Diff line change
Expand Up @@ -857,3 +857,10 @@ The kernel interface functions are as follows:

This is used to extract the error number from a message indicating either
a local error occurred or a network error occurred.

(*) Allocate a null key for doing anonymous security.

struct key *rxrpc_get_null_key(const char *keyname);

This is used to allocate a null RxRPC key that can be used to indicate
anonymous security for a particular domain.
17 changes: 6 additions & 11 deletions fs/afs/cell.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ static struct afs_cell *afs_cell_root;
static struct afs_cell *afs_cell_alloc(const char *name, char *vllist)
{
struct afs_cell *cell;
struct key *key;
size_t namelen;
char keyname[4 + AFS_MAXCELLNAME + 1], *cp, *dp, *next;
int ret;
Expand Down Expand Up @@ -89,20 +90,14 @@ static struct afs_cell *afs_cell_alloc(const char *name, char *vllist)
do {
*dp++ = toupper(*cp);
} while (*cp++);
cell->anonymous_key = key_alloc(&key_type_rxrpc, keyname, 0, 0, current,
KEY_POS_SEARCH, KEY_ALLOC_NOT_IN_QUOTA);
if (IS_ERR(cell->anonymous_key)) {
_debug("no key");
ret = PTR_ERR(cell->anonymous_key);
goto error;
}

ret = key_instantiate_and_link(cell->anonymous_key, NULL, 0,
NULL, NULL);
if (ret < 0) {
_debug("instantiate failed");
key = rxrpc_get_null_key(keyname);
if (IS_ERR(key)) {
_debug("no key");
ret = PTR_ERR(key);
goto error;
}
cell->anonymous_key = key;

_debug("anon key %p{%x}",
cell->anonymous_key, key_serial(cell->anonymous_key));
Expand Down
2 changes: 2 additions & 0 deletions include/keys/rxrpc-type.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@
*/
extern struct key_type key_type_rxrpc;

extern struct key *rxrpc_get_null_key(const char *);

#endif /* _KEYS_USER_TYPE_H */
112 changes: 112 additions & 0 deletions include/linux/key-type.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/* Definitions for key type implementations
*
* Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells ([email protected])
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/

#ifndef _LINUX_KEY_TYPE_H
#define _LINUX_KEY_TYPE_H

#include <linux/key.h>

#ifdef CONFIG_KEYS

/*
* key under-construction record
* - passed to the request_key actor if supplied
*/
struct key_construction {
struct key *key; /* key being constructed */
struct key *authkey;/* authorisation for key being constructed */
};

typedef int (*request_key_actor_t)(struct key_construction *key,
const char *op, void *aux);

/*
* kernel managed key type definition
*/
struct key_type {
/* name of the type */
const char *name;

/* default payload length for quota precalculation (optional)
* - this can be used instead of calling key_payload_reserve(), that
* function only needs to be called if the real datalen is different
*/
size_t def_datalen;

/* instantiate a key of this type
* - this method should call key_payload_reserve() to determine if the
* user's quota will hold the payload
*/
int (*instantiate)(struct key *key, const void *data, size_t datalen);

/* update a key of this type (optional)
* - this method should call key_payload_reserve() to recalculate the
* quota consumption
* - the key must be locked against read when modifying
*/
int (*update)(struct key *key, const void *data, size_t datalen);

/* match a key against a description */
int (*match)(const struct key *key, const void *desc);

/* clear some of the data from a key on revokation (optional)
* - the key's semaphore will be write-locked by the caller
*/
void (*revoke)(struct key *key);

/* clear the data from a key (optional) */
void (*destroy)(struct key *key);

/* describe a key */
void (*describe)(const struct key *key, struct seq_file *p);

/* read a key's data (optional)
* - permission checks will be done by the caller
* - the key's semaphore will be readlocked by the caller
* - should return the amount of data that could be read, no matter how
* much is copied into the buffer
* - shouldn't do the copy if the buffer is NULL
*/
long (*read)(const struct key *key, char __user *buffer, size_t buflen);

/* handle request_key() for this type instead of invoking
* /sbin/request-key (optional)
* - key is the key to instantiate
* - authkey is the authority to assume when instantiating this key
* - op is the operation to be done, usually "create"
* - the call must not return until the instantiation process has run
* its course
*/
request_key_actor_t request_key;

/* internal fields */
struct list_head link; /* link in types list */
};

extern struct key_type key_type_keyring;

extern int register_key_type(struct key_type *ktype);
extern void unregister_key_type(struct key_type *ktype);

extern int key_payload_reserve(struct key *key, size_t datalen);
extern int key_instantiate_and_link(struct key *key,
const void *data,
size_t datalen,
struct key *keyring,
struct key *instkey);
extern int key_negate_and_link(struct key *key,
unsigned timeout,
struct key *keyring,
struct key *instkey);
extern void complete_request_key(struct key_construction *cons, int error);

#endif /* CONFIG_KEYS */
#endif /* _LINUX_KEY_TYPE_H */
Loading

0 comments on commit 76181c1

Please sign in to comment.