#include <linux/fs.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/user_namespace.h>
#include <asm/uaccess.h>
#include "internal.h"
.nkeys = ATOMIC_INIT(2),
.nikeys = ATOMIC_INIT(2),
.uid = 0,
+ .user_ns = &init_user_ns,
};
/*****************************************************************************/
*/
int install_user_keyrings(void)
{
- struct user_struct *user = current->cred->user;
+ struct user_struct *user;
+ const struct cred *cred;
struct key *uid_keyring, *session_keyring;
char buf[20];
int ret;
+ cred = current_cred();
+ user = cred->user;
+
kenter("%p{%u}", user, user->uid);
if (user->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,
- current, KEY_ALLOC_IN_QUOTA,
+ cred, KEY_ALLOC_IN_QUOTA,
NULL);
if (IS_ERR(uid_keyring)) {
ret = PTR_ERR(uid_keyring);
if (IS_ERR(session_keyring)) {
session_keyring =
keyring_alloc(buf, user->uid, (gid_t) -1,
- current, KEY_ALLOC_IN_QUOTA,
- NULL);
+ cred, KEY_ALLOC_IN_QUOTA, NULL);
if (IS_ERR(session_keyring)) {
ret = PTR_ERR(session_keyring);
goto error_release;
return ret;
}
-/*****************************************************************************/
/*
- * deal with the UID changing
+ * install a fresh thread keyring directly to new credentials
*/
-void switch_uid_keyring(struct user_struct *new_user)
+int install_thread_keyring_to_cred(struct cred *new)
{
-#if 0 /* do nothing for now */
- struct key *old;
-
- /* switch to the new user's session keyring if we were running under
- * root's default session keyring */
- if (new_user->uid != 0 &&
- current->session_keyring == &root_session_keyring
- ) {
- atomic_inc(&new_user->session_keyring->usage);
-
- task_lock(current);
- old = current->session_keyring;
- current->session_keyring = new_user->session_keyring;
- task_unlock(current);
+ struct key *keyring;
- key_put(old);
- }
-#endif
+ keyring = keyring_alloc("_tid", new->uid, new->gid, new,
+ KEY_ALLOC_QUOTA_OVERRUN, NULL);
+ if (IS_ERR(keyring))
+ return PTR_ERR(keyring);
-} /* end switch_uid_keyring() */
+ new->thread_keyring = keyring;
+ return 0;
+}
-/*****************************************************************************/
/*
* install a fresh thread keyring, discarding the old one
*/
-int install_thread_keyring(void)
+static int install_thread_keyring(void)
{
- struct task_struct *tsk = current;
- struct key *keyring, *old;
- char buf[20];
+ struct cred *new;
int ret;
- sprintf(buf, "_tid.%u", tsk->pid);
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
- keyring = keyring_alloc(buf, tsk->cred->uid, tsk->cred->gid, tsk,
- KEY_ALLOC_QUOTA_OVERRUN, NULL);
- if (IS_ERR(keyring)) {
- ret = PTR_ERR(keyring);
- goto error;
+ BUG_ON(new->thread_keyring);
+
+ ret = install_thread_keyring_to_cred(new);
+ if (ret < 0) {
+ abort_creds(new);
+ return ret;
}
- task_lock(tsk);
- old = tsk->cred->thread_keyring;
- tsk->cred->thread_keyring = keyring;
- task_unlock(tsk);
+ return commit_creds(new);
+}
- ret = 0;
+/*
+ * install a process keyring directly to a credentials struct
+ * - returns -EEXIST if there was already a process keyring, 0 if one installed,
+ * and other -ve on any other error
+ */
+int install_process_keyring_to_cred(struct cred *new)
+{
+ struct key *keyring;
+ int ret;
- key_put(old);
-error:
+ if (new->tgcred->process_keyring)
+ return -EEXIST;
+
+ keyring = keyring_alloc("_pid", new->uid, new->gid,
+ new, KEY_ALLOC_QUOTA_OVERRUN, NULL);
+ if (IS_ERR(keyring))
+ return PTR_ERR(keyring);
+
+ spin_lock_irq(&new->tgcred->lock);
+ if (!new->tgcred->process_keyring) {
+ new->tgcred->process_keyring = keyring;
+ keyring = NULL;
+ ret = 0;
+ } else {
+ ret = -EEXIST;
+ }
+ spin_unlock_irq(&new->tgcred->lock);
+ key_put(keyring);
return ret;
+}
-} /* end install_thread_keyring() */
-
-/*****************************************************************************/
/*
* make sure a process keyring is installed
+ * - we
*/
-int install_process_keyring(void)
+static int install_process_keyring(void)
{
- struct task_struct *tsk = current;
- struct key *keyring;
- char buf[20];
+ struct cred *new;
int ret;
- might_sleep();
-
- if (!tsk->signal->process_keyring) {
- sprintf(buf, "_pid.%u", tsk->tgid);
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
- keyring = keyring_alloc(buf, tsk->cred->uid, tsk->cred->gid, tsk,
- KEY_ALLOC_QUOTA_OVERRUN, NULL);
- if (IS_ERR(keyring)) {
- ret = PTR_ERR(keyring);
- goto error;
- }
-
- /* attach keyring */
- spin_lock_irq(&tsk->sighand->siglock);
- if (!tsk->signal->process_keyring) {
- tsk->signal->process_keyring = keyring;
- keyring = NULL;
- }
- spin_unlock_irq(&tsk->sighand->siglock);
-
- key_put(keyring);
+ ret = install_process_keyring_to_cred(new);
+ if (ret < 0) {
+ abort_creds(new);
+ return ret != -EEXIST ?: 0;
}
- ret = 0;
-error:
- return ret;
-
-} /* end install_process_keyring() */
+ return commit_creds(new);
+}
-/*****************************************************************************/
/*
- * install a session keyring, discarding the old one
- * - if a keyring is not supplied, an empty one is invented
+ * install a session keyring directly to a credentials struct
*/
-static int install_session_keyring(struct key *keyring)
+static int install_session_keyring_to_cred(struct cred *cred,
+ struct key *keyring)
{
- struct task_struct *tsk = current;
unsigned long flags;
struct key *old;
- char buf[20];
might_sleep();
/* create an empty session keyring */
if (!keyring) {
- sprintf(buf, "_ses.%u", tsk->tgid);
-
flags = KEY_ALLOC_QUOTA_OVERRUN;
- if (tsk->signal->session_keyring)
+ if (cred->tgcred->session_keyring)
flags = KEY_ALLOC_IN_QUOTA;
- keyring = keyring_alloc(buf, tsk->cred->uid, tsk->cred->gid, tsk,
- flags, NULL);
+ keyring = keyring_alloc("_ses", cred->uid, cred->gid,
+ cred, flags, NULL);
if (IS_ERR(keyring))
return PTR_ERR(keyring);
- }
- else {
+ } else {
atomic_inc(&keyring->usage);
}
/* install the keyring */
- spin_lock_irq(&tsk->sighand->siglock);
- old = tsk->signal->session_keyring;
- rcu_assign_pointer(tsk->signal->session_keyring, keyring);
- spin_unlock_irq(&tsk->sighand->siglock);
+ spin_lock_irq(&cred->tgcred->lock);
+ old = cred->tgcred->session_keyring;
+ rcu_assign_pointer(cred->tgcred->session_keyring, keyring);
+ spin_unlock_irq(&cred->tgcred->lock);
/* we're using RCU on the pointer, but there's no point synchronising
* on it if it didn't previously point to anything */
}
return 0;
+}
-} /* end install_session_keyring() */
-
-/*****************************************************************************/
-/*
- * copy the keys in a thread group for fork without CLONE_THREAD
- */
-int copy_thread_group_keys(struct task_struct *tsk)
-{
- key_check(current->thread_group->session_keyring);
- key_check(current->thread_group->process_keyring);
-
- /* no process keyring yet */
- tsk->signal->process_keyring = NULL;
-
- /* same session keyring */
- rcu_read_lock();
- tsk->signal->session_keyring =
- key_get(rcu_dereference(current->signal->session_keyring));
- rcu_read_unlock();
-
- return 0;
-
-} /* end copy_thread_group_keys() */
-
-/*****************************************************************************/
-/*
- * copy the keys for fork
- */
-int copy_keys(unsigned long clone_flags, struct task_struct *tsk)
-{
- key_check(tsk->cred->thread_keyring);
- key_check(tsk->cred->request_key_auth);
-
- /* no thread keyring yet */
- tsk->cred->thread_keyring = NULL;
-
- /* copy the request_key() authorisation for this thread */
- key_get(tsk->cred->request_key_auth);
-
- return 0;
-
-} /* end copy_keys() */
-
-/*****************************************************************************/
-/*
- * dispose of thread group keys upon thread group destruction
- */
-void exit_thread_group_keys(struct signal_struct *tg)
-{
- key_put(tg->session_keyring);
- key_put(tg->process_keyring);
-
-} /* end exit_thread_group_keys() */
-
-/*****************************************************************************/
-/*
- * dispose of per-thread keys upon thread exit
- */
-void exit_keys(struct task_struct *tsk)
-{
- key_put(tsk->cred->thread_keyring);
- key_put(tsk->cred->request_key_auth);
-
-} /* end exit_keys() */
-
-/*****************************************************************************/
/*
- * deal with execve()
+ * install a session keyring, discarding the old one
+ * - if a keyring is not supplied, an empty one is invented
*/
-int exec_keys(struct task_struct *tsk)
+static int install_session_keyring(struct key *keyring)
{
- struct key *old;
-
- /* newly exec'd tasks don't get a thread keyring */
- task_lock(tsk);
- old = tsk->cred->thread_keyring;
- tsk->cred->thread_keyring = NULL;
- task_unlock(tsk);
-
- key_put(old);
-
- /* discard the process keyring from a newly exec'd task */
- spin_lock_irq(&tsk->sighand->siglock);
- old = tsk->signal->process_keyring;
- tsk->signal->process_keyring = NULL;
- spin_unlock_irq(&tsk->sighand->siglock);
-
- key_put(old);
-
- return 0;
+ struct cred *new;
+ int ret;
-} /* end exec_keys() */
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
-/*****************************************************************************/
-/*
- * deal with SUID programs
- * - we might want to make this invent a new session keyring
- */
-int suid_keys(struct task_struct *tsk)
-{
- return 0;
+ ret = install_session_keyring_to_cred(new, NULL);
+ if (ret < 0) {
+ abort_creds(new);
+ return ret;
+ }
-} /* end suid_keys() */
+ return commit_creds(new);
+}
/*****************************************************************************/
/*
key_ref_t search_process_keyrings(struct key_type *type,
const void *description,
key_match_func_t match,
- struct task_struct *context)
+ const struct cred *cred)
{
struct request_key_auth *rka;
key_ref_t key_ref, ret, err;
err = ERR_PTR(-EAGAIN);
/* search the thread keyring first */
- if (context->cred->thread_keyring) {
+ if (cred->thread_keyring) {
key_ref = keyring_search_aux(
- make_key_ref(context->cred->thread_keyring, 1),
- context, type, description, match);
+ make_key_ref(cred->thread_keyring, 1),
+ cred, type, description, match);
if (!IS_ERR(key_ref))
goto found;
}
/* search the process keyring second */
- if (context->signal->process_keyring) {
+ if (cred->tgcred->process_keyring) {
key_ref = keyring_search_aux(
- make_key_ref(context->signal->process_keyring, 1),
- context, type, description, match);
+ make_key_ref(cred->tgcred->process_keyring, 1),
+ cred, type, description, match);
if (!IS_ERR(key_ref))
goto found;
}
/* search the session keyring */
- if (context->signal->session_keyring) {
+ if (cred->tgcred->session_keyring) {
rcu_read_lock();
key_ref = keyring_search_aux(
make_key_ref(rcu_dereference(
- context->signal->session_keyring),
+ cred->tgcred->session_keyring),
1),
- context, type, description, match);
+ cred, type, description, match);
rcu_read_unlock();
if (!IS_ERR(key_ref))
}
}
/* or search the user-session keyring */
- else if (context->cred->user->session_keyring) {
+ else if (cred->user->session_keyring) {
key_ref = keyring_search_aux(
- make_key_ref(context->cred->user->session_keyring, 1),
- context, type, description, match);
+ make_key_ref(cred->user->session_keyring, 1),
+ cred, type, description, match);
if (!IS_ERR(key_ref))
goto found;
* search the keyrings of the process mentioned there
* - we don't permit access to request_key auth keys via this method
*/
- if (context->cred->request_key_auth &&
- context == current &&
+ if (cred->request_key_auth &&
+ cred == current_cred() &&
type != &key_type_request_key_auth
) {
/* defend against the auth key being revoked */
- down_read(&context->cred->request_key_auth->sem);
+ down_read(&cred->request_key_auth->sem);
- if (key_validate(context->cred->request_key_auth) == 0) {
- rka = context->cred->request_key_auth->payload.data;
+ if (key_validate(cred->request_key_auth) == 0) {
+ rka = cred->request_key_auth->payload.data;
key_ref = search_process_keyrings(type, description,
- match, rka->context);
+ match, rka->cred);
- up_read(&context->cred->request_key_auth->sem);
+ up_read(&cred->request_key_auth->sem);
if (!IS_ERR(key_ref))
goto found;
break;
}
} else {
- up_read(&context->cred->request_key_auth->sem);
+ up_read(&cred->request_key_auth->sem);
}
}
key_perm_t perm)
{
struct request_key_auth *rka;
- struct task_struct *t = current;
- struct cred *cred = t->cred;
+ const struct cred *cred;
struct key *key;
key_ref_t key_ref, skey_ref;
int ret;
+try_again:
+ cred = get_current_cred();
key_ref = ERR_PTR(-ENOKEY);
switch (id) {
key = ERR_PTR(ret);
goto error;
}
+ goto reget_creds;
}
key = cred->thread_keyring;
break;
case KEY_SPEC_PROCESS_KEYRING:
- if (!t->signal->process_keyring) {
+ if (!cred->tgcred->process_keyring) {
if (!create)
goto error;
key = ERR_PTR(ret);
goto error;
}
+ goto reget_creds;
}
- key = t->signal->process_keyring;
+ key = cred->tgcred->process_keyring;
atomic_inc(&key->usage);
key_ref = make_key_ref(key, 1);
break;
case KEY_SPEC_SESSION_KEYRING:
- if (!t->signal->session_keyring) {
+ if (!cred->tgcred->session_keyring) {
/* always install a session keyring upon access if one
* doesn't exist yet */
ret = install_user_keyrings();
goto error;
ret = install_session_keyring(
cred->user->session_keyring);
+
if (ret < 0)
goto error;
+ goto reget_creds;
}
rcu_read_lock();
- key = rcu_dereference(t->signal->session_keyring);
+ key = rcu_dereference(cred->tgcred->session_keyring);
atomic_inc(&key->usage);
rcu_read_unlock();
key_ref = make_key_ref(key, 1);
/* check to see if we possess the key */
skey_ref = search_process_keyrings(key->type, key,
lookup_user_key_possessed,
- current);
+ cred);
if (!IS_ERR(skey_ref)) {
key_put(key);
goto invalid_key;
/* check the permissions */
- ret = key_task_permission(key_ref, t, perm);
+ ret = key_task_permission(key_ref, cred, perm);
if (ret < 0)
goto invalid_key;
error:
+ put_cred(cred);
return key_ref;
invalid_key:
key_ref = ERR_PTR(ret);
goto error;
+ /* if we attempted to install a keyring, then it may have caused new
+ * creds to be installed */
+reget_creds:
+ put_cred(cred);
+ goto try_again;
+
} /* end lookup_user_key() */
/*****************************************************************************/
*/
long join_session_keyring(const char *name)
{
- struct task_struct *tsk = current;
+ const struct cred *old;
+ struct cred *new;
struct key *keyring;
- long ret;
+ long ret, serial;
+
+ /* only permit this if there's a single thread in the thread group -
+ * this avoids us having to adjust the creds on all threads and risking
+ * ENOMEM */
+ if (!is_single_threaded(current))
+ return -EMLINK;
+
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+ old = current_cred();
/* if no name is provided, install an anonymous keyring */
if (!name) {
- ret = install_session_keyring(NULL);
+ ret = install_session_keyring_to_cred(new, NULL);
if (ret < 0)
goto error;
- rcu_read_lock();
- ret = rcu_dereference(tsk->signal->session_keyring)->serial;
- rcu_read_unlock();
- goto error;
+ serial = new->tgcred->session_keyring->serial;
+ ret = commit_creds(new);
+ if (ret == 0)
+ ret = serial;
+ goto okay;
}
/* allow the user to join or create a named keyring */
keyring = find_keyring_by_name(name, false);
if (PTR_ERR(keyring) == -ENOKEY) {
/* not found - try and create a new one */
- keyring = keyring_alloc(name, tsk->cred->uid, tsk->cred->gid, tsk,
+ keyring = keyring_alloc(name, old->uid, old->gid, old,
KEY_ALLOC_IN_QUOTA, NULL);
if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
goto error2;
}
- }
- else if (IS_ERR(keyring)) {
+ } else if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
goto error2;
}
/* we've got a keyring - now to install it */
- ret = install_session_keyring(keyring);
+ ret = install_session_keyring_to_cred(new, keyring);
if (ret < 0)
goto error2;
+ commit_creds(new);
+ mutex_unlock(&key_session_mutex);
+
ret = keyring->serial;
key_put(keyring);
+okay:
+ return ret;
error2:
mutex_unlock(&key_session_mutex);
error:
+ abort_creds(new);
return ret;
-
-} /* end join_session_keyring() */
+}