Skip to content

Commit

Permalink
[PATCH] keys: Permit running process to instantiate keys
Browse files Browse the repository at this point in the history
Make it possible for a running process (such as gssapid) to be able to
instantiate a key, as was requested by Trond Myklebust for NFS4.

The patch makes the following changes:

 (1) A new, optional key type method has been added. This permits a key type
     to intercept requests at the point /sbin/request-key is about to be
     spawned and do something else with them - passing them over the
     rpc_pipefs files or netlink sockets for instance.

     The uninstantiated key, the authorisation key and the intended operation
     name are passed to the method.

 (2) The callout_info is no longer passed as an argument to /sbin/request-key
     to prevent unauthorised viewing of this data using ps or by looking in
     /proc/pid/cmdline.

     This means that the old /sbin/request-key program will not work with the
     patched kernel as it will expect to see an extra argument that is no
     longer there.

     A revised keyutils package will be made available tomorrow.

 (3) The callout_info is now attached to the authorisation key. Reading this
     key will retrieve the information.

 (4) A new field has been added to the task_struct. This holds the
     authorisation key currently active for a thread. Searches now look here
     for the caller's set of keys rather than looking for an auth key in the
     lowest level of the session keyring.

     This permits a thread to be servicing multiple requests at once and to
     switch between them. Note that this is per-thread, not per-process, and
     so is usable in multithreaded programs.

     The setting of this field is inherited across fork and exec.

 (5) A new keyctl function (KEYCTL_ASSUME_AUTHORITY) has been added that
     permits a thread to assume the authority to deal with an uninstantiated
     key. Assumption is only permitted if the authorisation key associated
     with the uninstantiated key is somewhere in the thread's keyrings.

     This function can also clear the assumption.

 (6) A new magic key specifier has been added to refer to the currently
     assumed authorisation key (KEY_SPEC_REQKEY_AUTH_KEY).

 (7) Instantiation will only proceed if the appropriate authorisation key is
     assumed first. The assumed authorisation key is discarded if
     instantiation is successful.

 (8) key_validate() is moved from the file of request_key functions to the
     file of permissions functions.

 (9) The documentation is updated.

From: <Valdis.Kletnieks@vt.edu>

    Build fix.

Signed-off-by: David Howells <dhowells@redhat.com>
Cc: Trond Myklebust <trond.myklebust@fys.uio.no>
Cc: Alexander Zangerl <az@bond.edu.au>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
  • Loading branch information
David Howells authored and Linus Torvalds committed Jan 9, 2006
1 parent cab8eb5 commit b5f545c
Show file tree
Hide file tree
Showing 13 changed files with 378 additions and 245 deletions.
22 changes: 12 additions & 10 deletions Documentation/keys-request-key.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,30 +56,32 @@ A request proceeds in the following manner:
(4) request_key() then forks and executes /sbin/request-key with a new session
keyring that contains a link to auth key V.

(5) /sbin/request-key execs an appropriate program to perform the actual
(5) /sbin/request-key assumes the authority associated with key U.

(6) /sbin/request-key execs an appropriate program to perform the actual
instantiation.

(6) The program may want to access another key from A's context (say a
(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
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
UID, GID, groups and security info of process A as if it was process A,
and come up with key W.

(7) The program then does what it must to get the data with which to
(8) The program then does what it must to get the data with which to
instantiate key U, using key W as a reference (perhaps it contacts a
Kerberos server using the TGT) and then instantiates key U.

(8) Upon instantiating key U, auth key V is automatically revoked so that it
(9) Upon instantiating key U, auth key V is automatically revoked so that it
may not be used again.

(9) The program then exits 0 and request_key() deletes key V and returns key
(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 5 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
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
Expand Down Expand Up @@ -138,8 +140,8 @@ until one succeeds:

(3) The process's session keyring is searched.

(4) If the process has a request_key() authorisation key in its session
keyring then:
(4) If the process has assumed the authority associated with a request_key()
authorisation key then:

(a) If extant, the calling process's thread keyring is searched.

Expand Down
24 changes: 24 additions & 0 deletions Documentation/keys.txt
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,8 @@ process making the call:
KEY_SPEC_USER_KEYRING -4 UID-specific keyring
KEY_SPEC_USER_SESSION_KEYRING -5 UID-session keyring
KEY_SPEC_GROUP_KEYRING -6 GID-specific keyring
KEY_SPEC_REQKEY_AUTH_KEY -7 assumed request_key()
authorisation key


The main syscalls are:
Expand Down Expand Up @@ -645,6 +647,28 @@ The keyctl syscall functions are:
or expired keys.


(*) Assume the authority granted to instantiate a key

long keyctl(KEYCTL_ASSUME_AUTHORITY, key_serial_t key);

This assumes or divests the authority required to instantiate the
specified key. Authority can only be assumed if the thread has the
authorisation key associated with the specified key in its keyrings
somewhere.

Once authority is assumed, searches for keys will also search the
requester's keyrings using the requester's security label, UID, GID and
groups.

If the requested authority is unavailable, error EPERM will be returned,
likewise if the authority has been revoked because the target key is
already instantiated.

If the specified key is 0, then any assumed authority will be divested.

The assumed authorititive key is inherited across fork and exec.


===============
KERNEL SERVICES
===============
Expand Down
12 changes: 12 additions & 0 deletions include/linux/key.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ struct key {
/*
* kernel managed key type definition
*/
typedef int (*request_key_actor_t)(struct key *key, struct key *authkey, const char *op);

struct key_type {
/* name of the type */
const char *name;
Expand Down Expand Up @@ -218,6 +220,16 @@ struct key_type {
*/
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 */
};
Expand Down
2 changes: 2 additions & 0 deletions include/linux/keyctl.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#define KEY_SPEC_USER_KEYRING -4 /* - key ID for UID-specific keyring */
#define KEY_SPEC_USER_SESSION_KEYRING -5 /* - key ID for UID-session keyring */
#define KEY_SPEC_GROUP_KEYRING -6 /* - key ID for GID-specific keyring */
#define KEY_SPEC_REQKEY_AUTH_KEY -7 /* - key ID for assumed request_key auth key */

/* request-key default keyrings */
#define KEY_REQKEY_DEFL_NO_CHANGE -1
Expand Down Expand Up @@ -47,5 +48,6 @@
#define KEYCTL_NEGATE 13 /* negate a partially constructed key */
#define KEYCTL_SET_REQKEY_KEYRING 14 /* set default request-key keyring */
#define KEYCTL_SET_TIMEOUT 15 /* set key timeout */
#define KEYCTL_ASSUME_AUTHORITY 16 /* assume request_key() authorisation */

#endif /* _LINUX_KEYCTL_H */
1 change: 1 addition & 0 deletions include/linux/sched.h
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,7 @@ struct task_struct {
unsigned keep_capabilities:1;
struct user_struct *user;
#ifdef CONFIG_KEYS
struct key *request_key_auth; /* assumed request_key authority */
struct key *thread_keyring; /* keyring private to this thread */
unsigned char jit_keyring; /* default keyring to attach requested keys to */
#endif
Expand Down
3 changes: 3 additions & 0 deletions security/keys/compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ asmlinkage long compat_sys_keyctl(u32 option,
case KEYCTL_SET_TIMEOUT:
return keyctl_set_timeout(arg2, arg3);

case KEYCTL_ASSUME_AUTHORITY:
return keyctl_assume_authority(arg2);

default:
return -EOPNOTSUPP;
}
Expand Down
4 changes: 3 additions & 1 deletion security/keys/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,13 @@ extern struct key *request_key_and_link(struct key_type *type,
struct request_key_auth {
struct key *target_key;
struct task_struct *context;
const char *callout_info;
pid_t pid;
};

extern struct key_type key_type_request_key_auth;
extern struct key *request_key_auth_new(struct key *target,
struct key **_rkakey);
const char *callout_info);

extern struct key *key_get_instantiation_authkey(key_serial_t target_id);

Expand All @@ -137,6 +138,7 @@ extern long keyctl_instantiate_key(key_serial_t, const void __user *,
extern long keyctl_negate_key(key_serial_t, unsigned, key_serial_t);
extern long keyctl_set_reqkey_keyring(int);
extern long keyctl_set_timeout(key_serial_t, unsigned);
extern long keyctl_assume_authority(key_serial_t);


/*
Expand Down
107 changes: 84 additions & 23 deletions security/keys/keyctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,17 @@ long keyctl_instantiate_key(key_serial_t id,
if (plen > 32767)
goto error;

/* the appropriate instantiation authorisation key must have been
* assumed before calling this */
ret = -EPERM;
instkey = current->request_key_auth;
if (!instkey)
goto error;

rka = instkey->payload.data;
if (rka->target_key->serial != id)
goto error;

/* pull the payload in if one was supplied */
payload = NULL;

Expand All @@ -848,15 +859,6 @@ long keyctl_instantiate_key(key_serial_t id,
goto error2;
}

/* find the instantiation authorisation key */
instkey = key_get_instantiation_authkey(id);
if (IS_ERR(instkey)) {
ret = PTR_ERR(instkey);
goto error2;
}

rka = instkey->payload.data;

/* find the destination keyring amongst those belonging to the
* requesting task */
keyring_ref = NULL;
Expand All @@ -865,7 +867,7 @@ long keyctl_instantiate_key(key_serial_t id,
KEY_WRITE);
if (IS_ERR(keyring_ref)) {
ret = PTR_ERR(keyring_ref);
goto error3;
goto error2;
}
}

Expand All @@ -874,11 +876,17 @@ long keyctl_instantiate_key(key_serial_t id,
key_ref_to_ptr(keyring_ref), instkey);

key_ref_put(keyring_ref);
error3:
key_put(instkey);
error2:

/* discard the assumed authority if it's just been disabled by
* instantiation of the key */
if (ret == 0) {
key_put(current->request_key_auth);
current->request_key_auth = NULL;
}

error2:
kfree(payload);
error:
error:
return ret;

} /* end keyctl_instantiate_key() */
Expand All @@ -895,14 +903,16 @@ long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
key_ref_t keyring_ref;
long ret;

/* find the instantiation authorisation key */
instkey = key_get_instantiation_authkey(id);
if (IS_ERR(instkey)) {
ret = PTR_ERR(instkey);
/* the appropriate instantiation authorisation key must have been
* assumed before calling this */
ret = -EPERM;
instkey = current->request_key_auth;
if (!instkey)
goto error;
}

rka = instkey->payload.data;
if (rka->target_key->serial != id)
goto error;

/* find the destination keyring if present (which must also be
* writable) */
Expand All @@ -911,7 +921,7 @@ long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
keyring_ref = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE);
if (IS_ERR(keyring_ref)) {
ret = PTR_ERR(keyring_ref);
goto error2;
goto error;
}
}

Expand All @@ -920,9 +930,15 @@ long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
key_ref_to_ptr(keyring_ref), instkey);

key_ref_put(keyring_ref);
error2:
key_put(instkey);
error:

/* discard the assumed authority if it's just been disabled by
* instantiation of the key */
if (ret == 0) {
key_put(current->request_key_auth);
current->request_key_auth = NULL;
}

error:
return ret;

} /* end keyctl_negate_key() */
Expand Down Expand Up @@ -1005,6 +1021,48 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout)

} /* end keyctl_set_timeout() */

/*****************************************************************************/
/*
* assume the authority to instantiate the specified key
*/
long keyctl_assume_authority(key_serial_t id)
{
struct key *authkey;
long ret;

/* special key IDs aren't permitted */
ret = -EINVAL;
if (id < 0)
goto error;

/* we divest ourselves of authority if given an ID of 0 */
if (id == 0) {
key_put(current->request_key_auth);
current->request_key_auth = NULL;
ret = 0;
goto error;
}

/* attempt to assume the authority temporarily granted to us whilst we
* instantiate the specified key
* - the authorisation key must be in the current task's keyrings
* somewhere
*/
authkey = key_get_instantiation_authkey(id);
if (IS_ERR(authkey)) {
ret = PTR_ERR(authkey);
goto error;
}

key_put(current->request_key_auth);
current->request_key_auth = authkey;
ret = authkey->serial;

error:
return ret;

} /* end keyctl_assume_authority() */

/*****************************************************************************/
/*
* the key control system call
Expand Down Expand Up @@ -1082,6 +1140,9 @@ asmlinkage long sys_keyctl(int option, unsigned long arg2, unsigned long arg3,
return keyctl_set_timeout((key_serial_t) arg2,
(unsigned) arg3);

case KEYCTL_ASSUME_AUTHORITY:
return keyctl_assume_authority((key_serial_t) arg2);

default:
return -EOPNOTSUPP;
}
Expand Down
45 changes: 0 additions & 45 deletions security/keys/keyring.c
Original file line number Diff line number Diff line change
Expand Up @@ -479,51 +479,6 @@ key_ref_t __keyring_search_one(key_ref_t keyring_ref,

} /* end __keyring_search_one() */

/*****************************************************************************/
/*
* search for an instantiation authorisation key matching a target key
* - the RCU read lock must be held by the caller
* - a target_id of zero specifies any valid token
*/
struct key *keyring_search_instkey(struct key *keyring,
key_serial_t target_id)
{
struct request_key_auth *rka;
struct keyring_list *klist;
struct key *instkey;
int loop;

klist = rcu_dereference(keyring->payload.subscriptions);
if (klist) {
for (loop = 0; loop < klist->nkeys; loop++) {
instkey = klist->keys[loop];

if (instkey->type != &key_type_request_key_auth)
continue;

rka = instkey->payload.data;
if (target_id && rka->target_key->serial != target_id)
continue;

/* the auth key is revoked during instantiation */
if (!test_bit(KEY_FLAG_REVOKED, &instkey->flags))
goto found;

instkey = ERR_PTR(-EKEYREVOKED);
goto error;
}
}

instkey = ERR_PTR(-EACCES);
goto error;

found:
atomic_inc(&instkey->usage);
error:
return instkey;

} /* end keyring_search_instkey() */

/*****************************************************************************/
/*
* find a keyring with the specified name
Expand Down
Loading

0 comments on commit b5f545c

Please sign in to comment.