965 lines · 23.7 KB · C
RawDownload
// 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 error_release;	} else {		session_keyring = key_ref_to_ptr(session_keyring_r);	} 	up_write(&user_ns->keyring_sem); 	if (_user_session_keyring)		*_user_session_keyring = session_keyring;	else		key_put(session_keyring);	if (_user_keyring)		*_user_keyring = uid_keyring;	else		key_put(uid_keyring);	kleave(" = 0");	return 0; error_release_session:	key_put(session_keyring);error_release:	key_put(uid_keyring);error:	up_write(&user_ns->keyring_sem);	kleave(" = %d", ret);	return ret;} /* * Get the user session keyring if it exists, but don't create it if it * doesn't. */struct key *get_user_session_keyring_rcu(const struct cred *cred){	struct key *reg_keyring = READ_ONCE(cred->user_ns->user_keyring_register);	key_ref_t session_keyring_r;	char buf[20]; 	struct keyring_search_context ctx = {		.index_key.type		= &key_type_keyring,		.index_key.description	= buf,		.cred			= cred,		.match_data.cmp		= key_default_cmp,		.match_data.raw_data	= buf,		.match_data.lookup_type	= KEYRING_SEARCH_LOOKUP_DIRECT,		.flags			= KEYRING_SEARCH_DO_STATE_CHECK,	}; 	if (!reg_keyring)		return NULL; 	ctx.index_key.desc_len = snprintf(buf, sizeof(buf), "_uid_ses.%u",					  from_kuid(cred->user_ns,						    cred->user->uid)); 	session_keyring_r = keyring_search_rcu(make_key_ref(reg_keyring, true),					       &ctx);	if (IS_ERR(session_keyring_r))		return NULL;	return key_ref_to_ptr(session_keyring_r);} /* * Install a thread keyring to the given credentials struct if it didn't have * one already.  This is allowed to overrun the quota. * * Return: 0 if a thread keyring is now present; -errno on failure. */int install_thread_keyring_to_cred(struct cred *new){	struct key *keyring; 	if (new->thread_keyring)		return 0; 	keyring = keyring_alloc("_tid", new->uid, new->gid, new,				KEY_POS_ALL | KEY_USR_VIEW,				KEY_ALLOC_QUOTA_OVERRUN,				NULL, NULL);	if (IS_ERR(keyring))		return PTR_ERR(keyring); 	new->thread_keyring = keyring;	return 0;} /* * Install a thread keyring to the current task if it didn't have one already. * * Return: 0 if a thread keyring is now present; -errno on failure. */static int install_thread_keyring(void){	struct cred *new;	int ret; 	new = prepare_creds();	if (!new)		return -ENOMEM; 	ret = install_thread_keyring_to_cred(new);	if (ret < 0) {		abort_creds(new);		return ret;	} 	return commit_creds(new);} /* * Install a process keyring to the given credentials struct if it didn't have * one already.  This is allowed to overrun the quota. * * Return: 0 if a process keyring is now present; -errno on failure. */int install_process_keyring_to_cred(struct cred *new){	struct key *keyring; 	if (new->process_keyring)		return 0; 	keyring = keyring_alloc("_pid", new->uid, new->gid, new,				KEY_POS_ALL | KEY_USR_VIEW,				KEY_ALLOC_QUOTA_OVERRUN,				NULL, NULL);	if (IS_ERR(keyring))		return PTR_ERR(keyring); 	new->process_keyring = keyring;	return 0;} /* * Install a process keyring to the current task if it didn't have one already. * * Return: 0 if a process keyring is now present; -errno on failure. */static int install_process_keyring(void){	struct cred *new;	int ret; 	new = prepare_creds();	if (!new)		return -ENOMEM; 	ret = install_process_keyring_to_cred(new);	if (ret < 0) {		abort_creds(new);		return ret;	} 	return commit_creds(new);} /* * Install the given keyring as the session keyring of the given credentials * struct, replacing the existing one if any.  If the given keyring is NULL, * then install a new anonymous session keyring. * @cred can not be in use by any task yet. * * Return: 0 on success; -errno on failure. */int install_session_keyring_to_cred(struct cred *cred, struct key *keyring){	unsigned long flags;	struct key *old; 	might_sleep(); 	/* create an empty session keyring */	if (!keyring) {		flags = KEY_ALLOC_QUOTA_OVERRUN;		if (cred->session_keyring)			flags = KEY_ALLOC_IN_QUOTA; 		keyring = keyring_alloc("_ses", cred->uid, cred->gid, cred,					KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ,					flags, NULL, NULL);		if (IS_ERR(keyring))			return PTR_ERR(keyring);	} else {		__key_get(keyring);	} 	/* install the keyring */	old = cred->session_keyring;	cred->session_keyring = keyring; 	if (old)		key_put(old); 	return 0;} /* * Install the given keyring as the session keyring of the current task, * replacing the existing one if any.  If the given keyring is NULL, then * install a new anonymous session keyring. * * Return: 0 on success; -errno on failure. */static int install_session_keyring(struct key *keyring){	struct cred *new;	int ret; 	new = prepare_creds();	if (!new)		return -ENOMEM; 	ret = install_session_keyring_to_cred(new, keyring);	if (ret < 0) {		abort_creds(new);		return ret;	} 	return commit_creds(new);} /* * Handle the fsuid changing. */void key_fsuid_changed(struct cred *new_cred){	/* update the ownership of the thread keyring */	if (new_cred->thread_keyring) {		down_write(&new_cred->thread_keyring->sem);		new_cred->thread_keyring->uid = new_cred->fsuid;		up_write(&new_cred->thread_keyring->sem);	}} /* * Handle the fsgid changing. */void key_fsgid_changed(struct cred *new_cred){	/* update the ownership of the thread keyring */	if (new_cred->thread_keyring) {		down_write(&new_cred->thread_keyring->sem);		new_cred->thread_keyring->gid = new_cred->fsgid;		up_write(&new_cred->thread_keyring->sem);	}} /* * Search the process keyrings attached to the supplied cred for the first * matching key under RCU conditions (the caller must be holding the RCU read * lock). * * The search criteria are the type and the match function.  The description is * given to the match function as a parameter, but doesn't otherwise influence * the search.  Typically the match function will compare the description * parameter to the key's description. * * This can only search keyrings that grant Search permission to the supplied * credentials.  Keyrings linked to searched keyrings will also be searched if * they grant Search permission too.  Keys can only be found if they grant * Search permission to the credentials. * * Returns a pointer to the key with the key usage count incremented if * successful, -EAGAIN if we didn't find any matching key or -ENOKEY if we only * matched negative keys. * * In the case of a successful return, the possession attribute is set on the * returned key reference. */key_ref_t search_cred_keyrings_rcu(struct keyring_search_context *ctx){	struct key *user_session;	key_ref_t key_ref, ret, err;	const struct cred *cred = ctx->cred; 	/* we want to return -EAGAIN or -ENOKEY if any of the keyrings were	 * searchable, but we failed to find a key or we found a negative key;	 * otherwise we want to return a sample error (probably -EACCES) if	 * none of the keyrings were searchable	 *	 * in terms of priority: success > -ENOKEY > -EAGAIN > other error	 */	key_ref = NULL;	ret = NULL;	err = ERR_PTR(-EAGAIN); 	/* search the thread keyring first */	if (cred->thread_keyring) {		key_ref = keyring_search_rcu(			make_key_ref(cred->thread_keyring, 1), ctx);		if (!IS_ERR(key_ref))			goto found; 		switch (PTR_ERR(key_ref)) {		case -EAGAIN: /* no key */		case -ENOKEY: /* negative key */			ret = key_ref;			break;		default:			err = key_ref;			break;		}	} 	/* search the process keyring second */	if (cred->process_keyring) {		key_ref = keyring_search_rcu(			make_key_ref(cred->process_keyring, 1), ctx);		if (!IS_ERR(key_ref))			goto found; 		switch (PTR_ERR(key_ref)) {		case -EAGAIN: /* no key */			if (ret)				break;			fallthrough;		case -ENOKEY: /* negative key */			ret = key_ref;			break;		default:			err = key_ref;			break;		}	} 	/* search the session keyring */	if (cred->session_keyring) {		key_ref = keyring_search_rcu(			make_key_ref(cred->session_keyring, 1), ctx); 		if (!IS_ERR(key_ref))			goto found; 		switch (PTR_ERR(key_ref)) {		case -EAGAIN: /* no key */			if (ret)				break;			fallthrough;		case -ENOKEY: /* negative key */			ret = key_ref;			break;		default:			err = key_ref;			break;		}	}	/* or search the user-session keyring */	else if ((user_session = get_user_session_keyring_rcu(cred))) {

Lines 1–500 of 965.