// SPDX-License-Identifier: GPL-2.0-or-later
/* Manage a process's keyrings
*
* Copyright (C) 2004-2005, 2008 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*/
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/sched/user.h>
#include <linux/keyctl.h>
#include <linux/fs.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/security.h>
#include <linux/user_namespace.h>
#include <linux/uaccess.h>
#include <linux/init_task.h>
#include <keys/request_key_auth-type.h>
#include "internal.h"
/* Session keyring create vs join semaphore */
static DEFINE_MUTEX(key_session_mutex);
/* The root user's tracking struct */
struct key_user root_key_user = {
.usage = REFCOUNT_INIT(3),
.cons_lock = __MUTEX_INITIALIZER(root_key_user.cons_lock),
.lock = __SPIN_LOCK_UNLOCKED(root_key_user.lock),
.nkeys = ATOMIC_INIT(2),
.nikeys = ATOMIC_INIT(2),
.uid = GLOBAL_ROOT_UID,
};
/*
* Get or create a user register keyring.
*/
static struct key *get_user_register(struct user_namespace *user_ns)
{
struct key *reg_keyring = READ_ONCE(user_ns->user_keyring_register);
if (reg_keyring)
return reg_keyring;
down_write(&user_ns->keyring_sem);
/* Make sure there's a register keyring. It gets owned by the
* user_namespace's owner.
*/
reg_keyring = user_ns->user_keyring_register;
if (!reg_keyring) {
reg_keyring = keyring_alloc(".user_reg",
user_ns->owner, INVALID_GID,
&init_cred,
KEY_POS_WRITE | KEY_POS_SEARCH |
KEY_USR_VIEW | KEY_USR_READ,
0,
NULL, NULL);
if (!IS_ERR(reg_keyring))
smp_store_release(&user_ns->user_keyring_register,
reg_keyring);
}
up_write(&user_ns->keyring_sem);
/* We don't return a ref since the keyring is pinned by the user_ns */
return reg_keyring;
}
/*
* Look up the user and user session keyrings for the current process's UID,
* creating them if they don't exist.
*/
int look_up_user_keyrings(struct key **_user_keyring,
struct key **_user_session_keyring)
{
const struct cred *cred = current_cred();
struct user_namespace *user_ns = current_user_ns();
struct key *reg_keyring, *uid_keyring, *session_keyring;
key_perm_t user_keyring_perm;
key_ref_t uid_keyring_r, session_keyring_r;
uid_t uid = from_kuid(user_ns, cred->user->uid);
char buf[20];
int ret;
user_keyring_perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL;
kenter("%u", uid);
reg_keyring = get_user_register(user_ns);
if (IS_ERR(reg_keyring))
return PTR_ERR(reg_keyring);
down_write(&user_ns->keyring_sem);
ret = 0;
/* Get the user keyring. Note that 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.
*/
snprintf(buf, sizeof(buf), "_uid.%u", uid);
uid_keyring_r = keyring_search(make_key_ref(reg_keyring, true),
&key_type_keyring, buf, false);
kdebug("_uid %p", uid_keyring_r);
if (uid_keyring_r == ERR_PTR(-EAGAIN)) {
uid_keyring = keyring_alloc(buf, cred->user->uid, INVALID_GID,
cred, user_keyring_perm,
KEY_ALLOC_UID_KEYRING |
KEY_ALLOC_IN_QUOTA,
NULL, reg_keyring);
if (IS_ERR(uid_keyring)) {
ret = PTR_ERR(uid_keyring);
goto error;
}
} else if (IS_ERR(uid_keyring_r)) {
ret = PTR_ERR(uid_keyring_r);
goto error;
} else {
uid_keyring = key_ref_to_ptr(uid_keyring_r);
}
/* Get a default session keyring (which might also exist already) */
snprintf(buf, sizeof(buf), "_uid_ses.%u", uid);
session_keyring_r = keyring_search(make_key_ref(reg_keyring, true),
&key_type_keyring, buf, false);
kdebug("_uid_ses %p", session_keyring_r);
if (session_keyring_r == ERR_PTR(-EAGAIN)) {
session_keyring = keyring_alloc(buf, cred->user->uid, INVALID_GID,
cred, user_keyring_perm,
KEY_ALLOC_UID_KEYRING |
KEY_ALLOC_IN_QUOTA,
NULL, 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_session;
/* And only then link the user-session keyring to the
* register.
*/
ret = key_link(reg_keyring, session_keyring);
if (ret < 0)
goto error_release_session;
} else if (IS_ERR(session_keyring_r)) {
ret = PTR_ERR(session_keyring_r);
goto