[PATCH] Keys: Allow in-kernel key requestor to pass auxiliary data to upcaller

The proposed NFS key type uses its own method of passing key requests to
userspace (upcalling) rather than invoking /sbin/request-key.  This is
because the responsible userspace daemon should already be running and will
be contacted through rpc_pipefs.

This patch permits the NFS filesystem to pass auxiliary data to the upcall
operation (struct key_type::request_key) so that the upcaller can use a
pre-existing communications channel more easily.

Signed-off-by: David Howells <dhowells@redhat.com>
Acked-By: Kevin Coffman <kwc@citi.umich.edu>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
David Howells 2006-06-29 02:24:28 -07:00 committed by Linus Torvalds
parent 94583779e6
commit 4e54f08543
6 changed files with 108 additions and 30 deletions

View File

@ -3,16 +3,23 @@
===================
The key request service is part of the key retention service (refer to
Documentation/keys.txt). This document explains more fully how that the
requesting algorithm works.
Documentation/keys.txt). This document explains more fully how the requesting
algorithm works.
The process starts by either the kernel requesting a service by calling
request_key():
request_key*():
struct key *request_key(const struct key_type *type,
const char *description,
const char *callout_string);
or:
struct key *request_key_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,
@ -20,16 +27,26 @@ Or by userspace invoking the request_key system call:
const char *callout_info,
key_serial_t dest_keyring);
The main difference between the two access points is that the in-kernel
interface 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 main difference between the access points is that the in-kernel interface
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 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
the caller.
The following example assumes that the key types involved don't define their
own upcall mechanisms. If they do, then those should be substituted for the
forking and execution of /sbin/request-key.
===========
THE PROCESS
===========
@ -40,8 +57,8 @@ A request proceeds in the following manner:
interface].
(2) request_key() searches the process's subscribed keyrings to see if there's
a suitable key there. If there is, it returns the key. If there isn't, and
callout_info is not set, an error is returned. Otherwise the process
a suitable key there. If there is, it returns the key. If there isn't,
and callout_info is not set, an error is returned. Otherwise the process
proceeds to the next step.
(3) request_key() sees that A doesn't have the desired key yet, so it creates
@ -62,7 +79,7 @@ A request proceeds in the following manner:
instantiation.
(7) The program may want to access another key from A's context (say a
Kerberos TGT key). It just requests the appropriate key, and the keyring
Kerberos TGT key). It just requests the appropriate key, and the keyring
search notes that the session keyring has auth key V in its bottom level.
This will permit it to then search the keyrings of process A with the
@ -79,10 +96,11 @@ A request proceeds in the following manner:
(10) The program then exits 0 and request_key() deletes key V and returns key
U to the caller.
This also extends further. If key W (step 7 above) didn't exist, key W would be
created uninstantiated, another auth key (X) would be created (as per step 3)
and another copy of /sbin/request-key spawned (as per step 4); but the context
specified by auth key X will still be process A, as it was in auth key V.
This also extends further. If key W (step 7 above) didn't exist, key W would
be created uninstantiated, another auth key (X) would be created (as per step
3) and another copy of /sbin/request-key spawned (as per step 4); but the
context specified by auth key X will still be process A, as it was in auth key
V.
This is because process A's keyrings can't simply be attached to
/sbin/request-key at the appropriate places because (a) execve will discard two
@ -118,17 +136,17 @@ A search of any particular keyring proceeds in the following fashion:
(2) It considers all the non-keyring keys within that keyring and, if any key
matches the criteria specified, calls key_permission(SEARCH) on it to see
if the key is allowed to be found. If it is, that key is returned; if
if the key is allowed to be found. If it is, that key is returned; if
not, the search continues, and the error code is retained if of higher
priority than the one currently set.
(3) It then considers all the keyring-type keys in the keyring it's currently
searching. It calls key_permission(SEARCH) on each keyring, and if this
searching. It calls key_permission(SEARCH) on each keyring, and if this
grants permission, it recurses, executing steps (2) and (3) on that
keyring.
The process stops immediately a valid key is found with permission granted to
use it. Any error from a previous match attempt is discarded and the key is
use it. Any error from a previous match attempt is discarded and the key is
returned.
When search_process_keyrings() is invoked, it performs the following searches
@ -153,7 +171,7 @@ The moment one succeeds, all pending errors are discarded and the found key is
returned.
Only if all these fail does the whole thing fail with the highest priority
error. Note that several errors may have come from LSM.
error. Note that several errors may have come from LSM.
The error priority is:

View File

@ -780,6 +780,17 @@ payload contents" for more information.
See also Documentation/keys-request-key.txt.
(*) To search for a key, passing auxiliary data to the upcaller, call:
struct key *request_key_with_auxdata(const struct key_type *type,
const char *description,
const char *callout_string,
void *aux);
This is identical to request_key(), except that the auxiliary data is
passed to the key_type->request_key() op if it exists.
(*) When it is no longer required, the key should be released using:
void key_put(struct key *key);
@ -1031,6 +1042,24 @@ 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,
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.
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").
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*().
============================
REQUEST-KEY CALLBACK SERVICE
============================

View File

@ -177,7 +177,8 @@ struct key {
/*
* kernel managed key type definition
*/
typedef int (*request_key_actor_t)(struct key *key, struct key *authkey, const char *op);
typedef int (*request_key_actor_t)(struct key *key, struct key *authkey,
const char *op, void *aux);
struct key_type {
/* name of the type */
@ -285,6 +286,11 @@ extern struct key *request_key(struct key_type *type,
const char *description,
const char *callout_info);
extern struct key *request_key_with_auxdata(struct key_type *type,
const char *description,
const char *callout_info,
void *aux);
extern int key_validate(struct key *key);
extern key_ref_t key_create_or_update(key_ref_t keyring,

View File

@ -99,6 +99,7 @@ extern int install_process_keyring(struct task_struct *tsk);
extern struct key *request_key_and_link(struct key_type *type,
const char *description,
const char *callout_info,
void *aux,
struct key *dest_keyring,
unsigned long flags);

View File

@ -183,7 +183,7 @@ asmlinkage long sys_request_key(const char __user *_type,
}
/* do the search */
key = request_key_and_link(ktype, description, callout_info,
key = request_key_and_link(ktype, description, callout_info, NULL,
key_ref_to_ptr(dest_ref),
KEY_ALLOC_IN_QUOTA);
if (IS_ERR(key)) {

View File

@ -1,6 +1,6 @@
/* request_key.c: request a key from userspace
*
* Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved.
* Copyright (C) 2004-6 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
@ -33,7 +33,8 @@ DECLARE_WAIT_QUEUE_HEAD(request_key_conswq);
*/
static int call_sbin_request_key(struct key *key,
struct key *authkey,
const char *op)
const char *op,
void *aux)
{
struct task_struct *tsk = current;
key_serial_t prkey, sskey;
@ -127,6 +128,7 @@ error_alloc:
static struct key *__request_key_construction(struct key_type *type,
const char *description,
const char *callout_info,
void *aux,
unsigned long flags)
{
request_key_actor_t actor;
@ -164,7 +166,7 @@ static struct key *__request_key_construction(struct key_type *type,
actor = call_sbin_request_key;
if (type->request_key)
actor = type->request_key;
ret = actor(key, authkey, "create");
ret = actor(key, authkey, "create", aux);
if (ret < 0)
goto request_failed;
@ -258,8 +260,9 @@ alloc_failed:
*/
static struct key *request_key_construction(struct key_type *type,
const char *description,
struct key_user *user,
const char *callout_info,
void *aux,
struct key_user *user,
unsigned long flags)
{
struct key_construction *pcons;
@ -284,7 +287,7 @@ static struct key *request_key_construction(struct key_type *type,
}
/* see about getting userspace to construct the key */
key = __request_key_construction(type, description, callout_info,
key = __request_key_construction(type, description, callout_info, aux,
flags);
error:
kleave(" = %p", key);
@ -392,6 +395,7 @@ static void request_key_link(struct key *key, struct key *dest_keyring)
struct key *request_key_and_link(struct key_type *type,
const char *description,
const char *callout_info,
void *aux,
struct key *dest_keyring,
unsigned long flags)
{
@ -399,8 +403,9 @@ struct key *request_key_and_link(struct key_type *type,
struct key *key;
key_ref_t key_ref;
kenter("%s,%s,%s,%p,%lx",
type->name, description, callout_info, dest_keyring, flags);
kenter("%s,%s,%s,%p,%p,%lx",
type->name, description, callout_info, aux,
dest_keyring, flags);
/* search all the process keyrings for a key */
key_ref = search_process_keyrings(type, description, type->match,
@ -433,8 +438,8 @@ struct key *request_key_and_link(struct key_type *type,
/* ask userspace (returns NULL if it waited on a key
* being constructed) */
key = request_key_construction(type, description,
user, callout_info,
flags);
callout_info, aux,
user, flags);
if (key)
break;
@ -491,8 +496,27 @@ struct key *request_key(struct key_type *type,
const char *callout_info)
{
return request_key_and_link(type, description, callout_info, NULL,
KEY_ALLOC_IN_QUOTA);
NULL, KEY_ALLOC_IN_QUOTA);
} /* end request_key() */
EXPORT_SYMBOL(request_key);
/*****************************************************************************/
/*
* request a key with auxiliary data for the upcaller
* - search the process's keyrings
* - check the list of keys being created or updated
* - call out to userspace for a key if supplementary info was provided
*/
struct key *request_key_with_auxdata(struct key_type *type,
const char *description,
const char *callout_info,
void *aux)
{
return request_key_and_link(type, description, callout_info, aux,
NULL, KEY_ALLOC_IN_QUOTA);
} /* end request_key_with_auxdata() */
EXPORT_SYMBOL(request_key_with_auxdata);