keys: don't generate user and user session keyrings unless they're accessed

Don't generate the per-UID user and user session keyrings unless they're
explicitly accessed.  This solves a problem during a login process whereby
set*uid() is called before the SELinux PAM module, resulting in the per-UID
keyrings having the wrong security labels.

This also cures the problem of multiple per-UID keyrings sometimes appearing
due to PAM modules (including pam_keyinit) setuiding and causing user_structs
to come into and go out of existence whilst the session keyring pins the user
keyring.  This is achieved by first searching for extant per-UID keyrings
before inventing new ones.

The serial bound argument is also dropped from find_keyring_by_name() as it's
not currently made use of (setting it to 0 disables the feature).

Signed-off-by: David Howells <dhowells@redhat.com>
Cc: <kwc@citi.umich.edu>
Cc: <arunsr@cse.iitk.ac.in>
Cc: <dwalsh@redhat.com>
Cc: Stephen Smalley <sds@tycho.nsa.gov>
Cc: James Morris <jmorris@namei.org>
Cc: Chris Wright <chrisw@sous-sol.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
David Howells 2008-04-29 01:01:31 -07:00 committed by Linus Torvalds
parent 6b79ccb514
commit 69664cf16a
7 changed files with 99 additions and 148 deletions

View File

@ -268,9 +268,6 @@ extern struct key *key_lookup(key_serial_t id);
/* /*
* the userspace interface * the userspace interface
*/ */
extern struct key root_user_keyring, root_session_keyring;
extern int alloc_uid_keyring(struct user_struct *user,
struct task_struct *ctx);
extern void switch_uid_keyring(struct user_struct *new_user); extern void switch_uid_keyring(struct user_struct *new_user);
extern int copy_keys(unsigned long clone_flags, struct task_struct *tsk); extern int copy_keys(unsigned long clone_flags, struct task_struct *tsk);
extern int copy_thread_group_keys(struct task_struct *tsk); extern int copy_thread_group_keys(struct task_struct *tsk);
@ -299,7 +296,6 @@ extern void key_init(void);
#define make_key_ref(k, p) ({ NULL; }) #define make_key_ref(k, p) ({ NULL; })
#define key_ref_to_ptr(k) ({ NULL; }) #define key_ref_to_ptr(k) ({ NULL; })
#define is_key_possessed(k) 0 #define is_key_possessed(k) 0
#define alloc_uid_keyring(u,c) 0
#define switch_uid_keyring(u) do { } while(0) #define switch_uid_keyring(u) do { } while(0)
#define __install_session_keyring(t, k) ({ NULL; }) #define __install_session_keyring(t, k) ({ NULL; })
#define copy_keys(f,t) 0 #define copy_keys(f,t) 0
@ -312,10 +308,6 @@ extern void key_init(void);
#define key_fsgid_changed(t) do { } while(0) #define key_fsgid_changed(t) do { } while(0)
#define key_init() do { } while(0) #define key_init() do { } while(0)
/* Initial keyrings */
extern struct key root_user_keyring;
extern struct key root_session_keyring;
#endif /* CONFIG_KEYS */ #endif /* CONFIG_KEYS */
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* _LINUX_KEY_H */ #endif /* _LINUX_KEY_H */

View File

@ -53,10 +53,6 @@ struct user_struct root_user = {
.files = ATOMIC_INIT(0), .files = ATOMIC_INIT(0),
.sigpending = ATOMIC_INIT(0), .sigpending = ATOMIC_INIT(0),
.locked_shm = 0, .locked_shm = 0,
#ifdef CONFIG_KEYS
.uid_keyring = &root_user_keyring,
.session_keyring = &root_session_keyring,
#endif
#ifdef CONFIG_USER_SCHED #ifdef CONFIG_USER_SCHED
.tg = &init_task_group, .tg = &init_task_group,
#endif #endif
@ -420,12 +416,12 @@ struct user_struct * alloc_uid(struct user_namespace *ns, uid_t uid)
new->mq_bytes = 0; new->mq_bytes = 0;
#endif #endif
new->locked_shm = 0; new->locked_shm = 0;
#ifdef CONFIG_KEYS
if (alloc_uid_keyring(new, current) < 0) new->uid_keyring = new->session_keyring = NULL;
goto out_free_user; #endif
if (sched_create_user(new) < 0) if (sched_create_user(new) < 0)
goto out_put_keys; goto out_free_user;
if (uids_user_create(new)) if (uids_user_create(new))
goto out_destoy_sched; goto out_destoy_sched;
@ -459,9 +455,6 @@ struct user_struct * alloc_uid(struct user_namespace *ns, uid_t uid)
out_destoy_sched: out_destoy_sched:
sched_destroy_user(new); sched_destroy_user(new);
out_put_keys:
key_put(new->uid_keyring);
key_put(new->session_keyring);
out_free_user: out_free_user:
kmem_cache_free(uid_cachep, new); kmem_cache_free(uid_cachep, new);
out_unlock: out_unlock:

View File

@ -77,8 +77,6 @@ extern struct mutex key_construction_mutex;
extern wait_queue_head_t request_key_conswq; extern wait_queue_head_t request_key_conswq;
extern void keyring_publish_name(struct key *keyring);
extern int __key_link(struct key *keyring, struct key *key); extern int __key_link(struct key *keyring, struct key *key);
extern key_ref_t __keyring_search_one(key_ref_t keyring_ref, extern key_ref_t __keyring_search_one(key_ref_t keyring_ref,
@ -102,7 +100,7 @@ extern key_ref_t search_process_keyrings(struct key_type *type,
key_match_func_t match, key_match_func_t match,
struct task_struct *tsk); struct task_struct *tsk);
extern struct key *find_keyring_by_name(const char *name, key_serial_t bound); extern struct key *find_keyring_by_name(const char *name, bool skip_perm_check);
extern int install_thread_keyring(struct task_struct *tsk); extern int install_thread_keyring(struct task_struct *tsk);
extern int install_process_keyring(struct task_struct *tsk); extern int install_process_keyring(struct task_struct *tsk);

View File

@ -1,6 +1,6 @@
/* Basic authentication token and access key management /* Basic authentication token and access key management
* *
* Copyright (C) 2004-2007 Red Hat, Inc. All Rights Reserved. * Copyright (C) 2004-2008 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com) * Written by David Howells (dhowells@redhat.com)
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -137,36 +137,6 @@ void key_user_put(struct key_user *user)
} /* end key_user_put() */ } /* end key_user_put() */
/*****************************************************************************/
/*
* insert a key with a fixed serial number
*/
static void __init __key_insert_serial(struct key *key)
{
struct rb_node *parent, **p;
struct key *xkey;
parent = NULL;
p = &key_serial_tree.rb_node;
while (*p) {
parent = *p;
xkey = rb_entry(parent, struct key, serial_node);
if (key->serial < xkey->serial)
p = &(*p)->rb_left;
else if (key->serial > xkey->serial)
p = &(*p)->rb_right;
else
BUG();
}
/* we've found a suitable hole - arrange for this key to occupy it */
rb_link_node(&key->serial_node, parent, p);
rb_insert_color(&key->serial_node, &key_serial_tree);
} /* end __key_insert_serial() */
/*****************************************************************************/ /*****************************************************************************/
/* /*
* assign a key the next unique serial number * assign a key the next unique serial number
@ -1020,17 +990,4 @@ void __init key_init(void)
rb_insert_color(&root_key_user.node, rb_insert_color(&root_key_user.node,
&key_user_tree); &key_user_tree);
/* record root's user standard keyrings */
key_check(&root_user_keyring);
key_check(&root_session_keyring);
__key_insert_serial(&root_user_keyring);
__key_insert_serial(&root_session_keyring);
keyring_publish_name(&root_user_keyring);
keyring_publish_name(&root_session_keyring);
/* link the two root keyrings together */
key_link(&root_session_keyring, &root_user_keyring);
} /* end key_init() */ } /* end key_init() */

View File

@ -1,6 +1,6 @@
/* keyring.c: keyring handling /* Keyring handling
* *
* Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved. * Copyright (C) 2004-2005, 2008 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com) * Written by David Howells (dhowells@redhat.com)
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -79,7 +79,7 @@ static DECLARE_RWSEM(keyring_serialise_link_sem);
* publish the name of a keyring so that it can be found by name (if it has * publish the name of a keyring so that it can be found by name (if it has
* one) * one)
*/ */
void keyring_publish_name(struct key *keyring) static void keyring_publish_name(struct key *keyring)
{ {
int bucket; int bucket;
@ -516,10 +516,9 @@ key_ref_t __keyring_search_one(key_ref_t keyring_ref,
/* /*
* find a keyring with the specified name * find a keyring with the specified name
* - all named keyrings are searched * - all named keyrings are searched
* - only find keyrings with search permission for the process * - normally only finds keyrings with search permission for the current process
* - only find keyrings with a serial number greater than the one specified
*/ */
struct key *find_keyring_by_name(const char *name, key_serial_t bound) struct key *find_keyring_by_name(const char *name, bool skip_perm_check)
{ {
struct key *keyring; struct key *keyring;
int bucket; int bucket;
@ -545,15 +544,11 @@ struct key *find_keyring_by_name(const char *name, key_serial_t bound)
if (strcmp(keyring->description, name) != 0) if (strcmp(keyring->description, name) != 0)
continue; continue;
if (key_permission(make_key_ref(keyring, 0), if (!skip_perm_check &&
key_permission(make_key_ref(keyring, 0),
KEY_SEARCH) < 0) KEY_SEARCH) < 0)
continue; continue;
/* found a potential candidate, but we still need to
* check the serial number */
if (keyring->serial <= bound)
continue;
/* we've got a match */ /* we've got a match */
atomic_inc(&keyring->usage); atomic_inc(&keyring->usage);
read_unlock(&keyring_name_lock); read_unlock(&keyring_name_lock);

View File

@ -1,6 +1,6 @@
/* process_keys.c: management of a process's keyrings /* Management of a process's keyrings
* *
* Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved. * Copyright (C) 2004-2005, 2008 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com) * Written by David Howells (dhowells@redhat.com)
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -23,6 +23,9 @@
/* session keyring create vs join semaphore */ /* session keyring create vs join semaphore */
static DEFINE_MUTEX(key_session_mutex); static DEFINE_MUTEX(key_session_mutex);
/* user keyring creation semaphore */
static DEFINE_MUTEX(key_user_keyring_mutex);
/* the root user's tracking struct */ /* the root user's tracking struct */
struct key_user root_key_user = { struct key_user root_key_user = {
.usage = ATOMIC_INIT(3), .usage = ATOMIC_INIT(3),
@ -33,78 +36,84 @@ struct key_user root_key_user = {
.uid = 0, .uid = 0,
}; };
/* the root user's UID keyring */
struct key root_user_keyring = {
.usage = ATOMIC_INIT(1),
.serial = 2,
.type = &key_type_keyring,
.user = &root_key_user,
.sem = __RWSEM_INITIALIZER(root_user_keyring.sem),
.perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL,
.flags = 1 << KEY_FLAG_INSTANTIATED,
.description = "_uid.0",
#ifdef KEY_DEBUGGING
.magic = KEY_DEBUG_MAGIC,
#endif
};
/* the root user's default session keyring */
struct key root_session_keyring = {
.usage = ATOMIC_INIT(1),
.serial = 1,
.type = &key_type_keyring,
.user = &root_key_user,
.sem = __RWSEM_INITIALIZER(root_session_keyring.sem),
.perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL,
.flags = 1 << KEY_FLAG_INSTANTIATED,
.description = "_uid_ses.0",
#ifdef KEY_DEBUGGING
.magic = KEY_DEBUG_MAGIC,
#endif
};
/*****************************************************************************/ /*****************************************************************************/
/* /*
* allocate the keyrings to be associated with a UID * install user and user session keyrings for a particular UID
*/ */
int alloc_uid_keyring(struct user_struct *user, static int install_user_keyrings(struct task_struct *tsk)
struct task_struct *ctx)
{ {
struct user_struct *user = tsk->user;
struct key *uid_keyring, *session_keyring; struct key *uid_keyring, *session_keyring;
char buf[20]; char buf[20];
int ret; int ret;
/* concoct a default session keyring */ kenter("%p{%u}", user, user->uid);
sprintf(buf, "_uid_ses.%u", user->uid);
session_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, ctx, if (user->uid_keyring) {
KEY_ALLOC_IN_QUOTA, NULL); kleave(" = 0 [exist]");
if (IS_ERR(session_keyring)) { return 0;
ret = PTR_ERR(session_keyring);
goto error;
} }
/* and a UID specific keyring, pointed to by the default session mutex_lock(&key_user_keyring_mutex);
* keyring */
sprintf(buf, "_uid.%u", user->uid);
uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, ctx,
KEY_ALLOC_IN_QUOTA, session_keyring);
if (IS_ERR(uid_keyring)) {
key_put(session_keyring);
ret = PTR_ERR(uid_keyring);
goto error;
}
/* install the keyrings */
user->uid_keyring = uid_keyring;
user->session_keyring = session_keyring;
ret = 0; ret = 0;
error: if (!user->uid_keyring) {
return ret; /* get the UID-specific keyring
* - there may be one in existence already as it may have been
* pinned by a session, but the user_struct pointing to it
* may have been destroyed by setuid */
sprintf(buf, "_uid.%u", user->uid);
} /* end alloc_uid_keyring() */ uid_keyring = find_keyring_by_name(buf, true);
if (IS_ERR(uid_keyring)) {
uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1,
tsk, KEY_ALLOC_IN_QUOTA,
NULL);
if (IS_ERR(uid_keyring)) {
ret = PTR_ERR(uid_keyring);
goto error;
}
}
/* get a default session keyring (which might also exist
* already) */
sprintf(buf, "_uid_ses.%u", user->uid);
session_keyring = find_keyring_by_name(buf, true);
if (IS_ERR(session_keyring)) {
session_keyring =
keyring_alloc(buf, user->uid, (gid_t) -1,
tsk, KEY_ALLOC_IN_QUOTA, NULL);
if (IS_ERR(session_keyring)) {
ret = PTR_ERR(session_keyring);
goto error_release;
}
/* we install a link from the user session keyring to
* the user keyring */
ret = key_link(session_keyring, uid_keyring);
if (ret < 0)
goto error_release_both;
}
/* install the keyrings */
user->uid_keyring = uid_keyring;
user->session_keyring = session_keyring;
}
mutex_unlock(&key_user_keyring_mutex);
kleave(" = 0");
return 0;
error_release_both:
key_put(session_keyring);
error_release:
key_put(uid_keyring);
error:
mutex_unlock(&key_user_keyring_mutex);
kleave(" = %d", ret);
return ret;
}
/*****************************************************************************/ /*****************************************************************************/
/* /*
@ -481,7 +490,7 @@ key_ref_t search_process_keyrings(struct key_type *type,
} }
} }
/* or search the user-session keyring */ /* or search the user-session keyring */
else { else if (context->user->session_keyring) {
key_ref = keyring_search_aux( key_ref = keyring_search_aux(
make_key_ref(context->user->session_keyring, 1), make_key_ref(context->user->session_keyring, 1),
context, type, description, match); context, type, description, match);
@ -614,6 +623,9 @@ key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
if (!context->signal->session_keyring) { if (!context->signal->session_keyring) {
/* always install a session keyring upon access if one /* always install a session keyring upon access if one
* doesn't exist yet */ * doesn't exist yet */
ret = install_user_keyrings(context);
if (ret < 0)
goto error;
ret = install_session_keyring( ret = install_session_keyring(
context, context->user->session_keyring); context, context->user->session_keyring);
if (ret < 0) if (ret < 0)
@ -628,12 +640,24 @@ key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
break; break;
case KEY_SPEC_USER_KEYRING: case KEY_SPEC_USER_KEYRING:
if (!context->user->uid_keyring) {
ret = install_user_keyrings(context);
if (ret < 0)
goto error;
}
key = context->user->uid_keyring; key = context->user->uid_keyring;
atomic_inc(&key->usage); atomic_inc(&key->usage);
key_ref = make_key_ref(key, 1); key_ref = make_key_ref(key, 1);
break; break;
case KEY_SPEC_USER_SESSION_KEYRING: case KEY_SPEC_USER_SESSION_KEYRING:
if (!context->user->session_keyring) {
ret = install_user_keyrings(context);
if (ret < 0)
goto error;
}
key = context->user->session_keyring; key = context->user->session_keyring;
atomic_inc(&key->usage); atomic_inc(&key->usage);
key_ref = make_key_ref(key, 1); key_ref = make_key_ref(key, 1);
@ -744,7 +768,7 @@ long join_session_keyring(const char *name)
mutex_lock(&key_session_mutex); mutex_lock(&key_session_mutex);
/* look for an existing keyring of this name */ /* look for an existing keyring of this name */
keyring = find_keyring_by_name(name, 0); keyring = find_keyring_by_name(name, false);
if (PTR_ERR(keyring) == -ENOKEY) { if (PTR_ERR(keyring) == -ENOKEY) {
/* not found - try and create a new one */ /* not found - try and create a new one */
keyring = keyring_alloc(name, tsk->uid, tsk->gid, tsk, keyring = keyring_alloc(name, tsk->uid, tsk->gid, tsk,

View File

@ -5551,14 +5551,6 @@ static __init int selinux_init(void)
else else
printk(KERN_DEBUG "SELinux: Starting in permissive mode\n"); printk(KERN_DEBUG "SELinux: Starting in permissive mode\n");
#ifdef CONFIG_KEYS
/* Add security information to initial keyrings */
selinux_key_alloc(&root_user_keyring, current,
KEY_ALLOC_NOT_IN_QUOTA);
selinux_key_alloc(&root_session_keyring, current,
KEY_ALLOC_NOT_IN_QUOTA);
#endif
return 0; return 0;
} }