162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* Manage a process's keyrings 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2004-2005, 2008 Red Hat, Inc. All Rights Reserved. 562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/init.h> 962306a36Sopenharmony_ci#include <linux/sched.h> 1062306a36Sopenharmony_ci#include <linux/sched/user.h> 1162306a36Sopenharmony_ci#include <linux/keyctl.h> 1262306a36Sopenharmony_ci#include <linux/fs.h> 1362306a36Sopenharmony_ci#include <linux/err.h> 1462306a36Sopenharmony_ci#include <linux/mutex.h> 1562306a36Sopenharmony_ci#include <linux/security.h> 1662306a36Sopenharmony_ci#include <linux/user_namespace.h> 1762306a36Sopenharmony_ci#include <linux/uaccess.h> 1862306a36Sopenharmony_ci#include <linux/init_task.h> 1962306a36Sopenharmony_ci#include <keys/request_key_auth-type.h> 2062306a36Sopenharmony_ci#include "internal.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* Session keyring create vs join semaphore */ 2362306a36Sopenharmony_cistatic DEFINE_MUTEX(key_session_mutex); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* The root user's tracking struct */ 2662306a36Sopenharmony_cistruct key_user root_key_user = { 2762306a36Sopenharmony_ci .usage = REFCOUNT_INIT(3), 2862306a36Sopenharmony_ci .cons_lock = __MUTEX_INITIALIZER(root_key_user.cons_lock), 2962306a36Sopenharmony_ci .lock = __SPIN_LOCK_UNLOCKED(root_key_user.lock), 3062306a36Sopenharmony_ci .nkeys = ATOMIC_INIT(2), 3162306a36Sopenharmony_ci .nikeys = ATOMIC_INIT(2), 3262306a36Sopenharmony_ci .uid = GLOBAL_ROOT_UID, 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* 3662306a36Sopenharmony_ci * Get or create a user register keyring. 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_cistatic struct key *get_user_register(struct user_namespace *user_ns) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci struct key *reg_keyring = READ_ONCE(user_ns->user_keyring_register); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci if (reg_keyring) 4362306a36Sopenharmony_ci return reg_keyring; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci down_write(&user_ns->keyring_sem); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci /* Make sure there's a register keyring. It gets owned by the 4862306a36Sopenharmony_ci * user_namespace's owner. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_ci reg_keyring = user_ns->user_keyring_register; 5162306a36Sopenharmony_ci if (!reg_keyring) { 5262306a36Sopenharmony_ci reg_keyring = keyring_alloc(".user_reg", 5362306a36Sopenharmony_ci user_ns->owner, INVALID_GID, 5462306a36Sopenharmony_ci &init_cred, 5562306a36Sopenharmony_ci KEY_POS_WRITE | KEY_POS_SEARCH | 5662306a36Sopenharmony_ci KEY_USR_VIEW | KEY_USR_READ, 5762306a36Sopenharmony_ci 0, 5862306a36Sopenharmony_ci NULL, NULL); 5962306a36Sopenharmony_ci if (!IS_ERR(reg_keyring)) 6062306a36Sopenharmony_ci smp_store_release(&user_ns->user_keyring_register, 6162306a36Sopenharmony_ci reg_keyring); 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci up_write(&user_ns->keyring_sem); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* We don't return a ref since the keyring is pinned by the user_ns */ 6762306a36Sopenharmony_ci return reg_keyring; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* 7162306a36Sopenharmony_ci * Look up the user and user session keyrings for the current process's UID, 7262306a36Sopenharmony_ci * creating them if they don't exist. 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_ciint look_up_user_keyrings(struct key **_user_keyring, 7562306a36Sopenharmony_ci struct key **_user_session_keyring) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci const struct cred *cred = current_cred(); 7862306a36Sopenharmony_ci struct user_namespace *user_ns = current_user_ns(); 7962306a36Sopenharmony_ci struct key *reg_keyring, *uid_keyring, *session_keyring; 8062306a36Sopenharmony_ci key_perm_t user_keyring_perm; 8162306a36Sopenharmony_ci key_ref_t uid_keyring_r, session_keyring_r; 8262306a36Sopenharmony_ci uid_t uid = from_kuid(user_ns, cred->user->uid); 8362306a36Sopenharmony_ci char buf[20]; 8462306a36Sopenharmony_ci int ret; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci user_keyring_perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci kenter("%u", uid); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci reg_keyring = get_user_register(user_ns); 9162306a36Sopenharmony_ci if (IS_ERR(reg_keyring)) 9262306a36Sopenharmony_ci return PTR_ERR(reg_keyring); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci down_write(&user_ns->keyring_sem); 9562306a36Sopenharmony_ci ret = 0; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* Get the user keyring. Note that there may be one in existence 9862306a36Sopenharmony_ci * already as it may have been pinned by a session, but the user_struct 9962306a36Sopenharmony_ci * pointing to it may have been destroyed by setuid. 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_ci snprintf(buf, sizeof(buf), "_uid.%u", uid); 10262306a36Sopenharmony_ci uid_keyring_r = keyring_search(make_key_ref(reg_keyring, true), 10362306a36Sopenharmony_ci &key_type_keyring, buf, false); 10462306a36Sopenharmony_ci kdebug("_uid %p", uid_keyring_r); 10562306a36Sopenharmony_ci if (uid_keyring_r == ERR_PTR(-EAGAIN)) { 10662306a36Sopenharmony_ci uid_keyring = keyring_alloc(buf, cred->user->uid, INVALID_GID, 10762306a36Sopenharmony_ci cred, user_keyring_perm, 10862306a36Sopenharmony_ci KEY_ALLOC_UID_KEYRING | 10962306a36Sopenharmony_ci KEY_ALLOC_IN_QUOTA, 11062306a36Sopenharmony_ci NULL, reg_keyring); 11162306a36Sopenharmony_ci if (IS_ERR(uid_keyring)) { 11262306a36Sopenharmony_ci ret = PTR_ERR(uid_keyring); 11362306a36Sopenharmony_ci goto error; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci } else if (IS_ERR(uid_keyring_r)) { 11662306a36Sopenharmony_ci ret = PTR_ERR(uid_keyring_r); 11762306a36Sopenharmony_ci goto error; 11862306a36Sopenharmony_ci } else { 11962306a36Sopenharmony_ci uid_keyring = key_ref_to_ptr(uid_keyring_r); 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* Get a default session keyring (which might also exist already) */ 12362306a36Sopenharmony_ci snprintf(buf, sizeof(buf), "_uid_ses.%u", uid); 12462306a36Sopenharmony_ci session_keyring_r = keyring_search(make_key_ref(reg_keyring, true), 12562306a36Sopenharmony_ci &key_type_keyring, buf, false); 12662306a36Sopenharmony_ci kdebug("_uid_ses %p", session_keyring_r); 12762306a36Sopenharmony_ci if (session_keyring_r == ERR_PTR(-EAGAIN)) { 12862306a36Sopenharmony_ci session_keyring = keyring_alloc(buf, cred->user->uid, INVALID_GID, 12962306a36Sopenharmony_ci cred, user_keyring_perm, 13062306a36Sopenharmony_ci KEY_ALLOC_UID_KEYRING | 13162306a36Sopenharmony_ci KEY_ALLOC_IN_QUOTA, 13262306a36Sopenharmony_ci NULL, NULL); 13362306a36Sopenharmony_ci if (IS_ERR(session_keyring)) { 13462306a36Sopenharmony_ci ret = PTR_ERR(session_keyring); 13562306a36Sopenharmony_ci goto error_release; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* We install a link from the user session keyring to 13962306a36Sopenharmony_ci * the user keyring. 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_ci ret = key_link(session_keyring, uid_keyring); 14262306a36Sopenharmony_ci if (ret < 0) 14362306a36Sopenharmony_ci goto error_release_session; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* And only then link the user-session keyring to the 14662306a36Sopenharmony_ci * register. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_ci ret = key_link(reg_keyring, session_keyring); 14962306a36Sopenharmony_ci if (ret < 0) 15062306a36Sopenharmony_ci goto error_release_session; 15162306a36Sopenharmony_ci } else if (IS_ERR(session_keyring_r)) { 15262306a36Sopenharmony_ci ret = PTR_ERR(session_keyring_r); 15362306a36Sopenharmony_ci goto error_release; 15462306a36Sopenharmony_ci } else { 15562306a36Sopenharmony_ci session_keyring = key_ref_to_ptr(session_keyring_r); 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci up_write(&user_ns->keyring_sem); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (_user_session_keyring) 16162306a36Sopenharmony_ci *_user_session_keyring = session_keyring; 16262306a36Sopenharmony_ci else 16362306a36Sopenharmony_ci key_put(session_keyring); 16462306a36Sopenharmony_ci if (_user_keyring) 16562306a36Sopenharmony_ci *_user_keyring = uid_keyring; 16662306a36Sopenharmony_ci else 16762306a36Sopenharmony_ci key_put(uid_keyring); 16862306a36Sopenharmony_ci kleave(" = 0"); 16962306a36Sopenharmony_ci return 0; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cierror_release_session: 17262306a36Sopenharmony_ci key_put(session_keyring); 17362306a36Sopenharmony_cierror_release: 17462306a36Sopenharmony_ci key_put(uid_keyring); 17562306a36Sopenharmony_cierror: 17662306a36Sopenharmony_ci up_write(&user_ns->keyring_sem); 17762306a36Sopenharmony_ci kleave(" = %d", ret); 17862306a36Sopenharmony_ci return ret; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci/* 18262306a36Sopenharmony_ci * Get the user session keyring if it exists, but don't create it if it 18362306a36Sopenharmony_ci * doesn't. 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_cistruct key *get_user_session_keyring_rcu(const struct cred *cred) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct key *reg_keyring = READ_ONCE(cred->user_ns->user_keyring_register); 18862306a36Sopenharmony_ci key_ref_t session_keyring_r; 18962306a36Sopenharmony_ci char buf[20]; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci struct keyring_search_context ctx = { 19262306a36Sopenharmony_ci .index_key.type = &key_type_keyring, 19362306a36Sopenharmony_ci .index_key.description = buf, 19462306a36Sopenharmony_ci .cred = cred, 19562306a36Sopenharmony_ci .match_data.cmp = key_default_cmp, 19662306a36Sopenharmony_ci .match_data.raw_data = buf, 19762306a36Sopenharmony_ci .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, 19862306a36Sopenharmony_ci .flags = KEYRING_SEARCH_DO_STATE_CHECK, 19962306a36Sopenharmony_ci }; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (!reg_keyring) 20262306a36Sopenharmony_ci return NULL; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci ctx.index_key.desc_len = snprintf(buf, sizeof(buf), "_uid_ses.%u", 20562306a36Sopenharmony_ci from_kuid(cred->user_ns, 20662306a36Sopenharmony_ci cred->user->uid)); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci session_keyring_r = keyring_search_rcu(make_key_ref(reg_keyring, true), 20962306a36Sopenharmony_ci &ctx); 21062306a36Sopenharmony_ci if (IS_ERR(session_keyring_r)) 21162306a36Sopenharmony_ci return NULL; 21262306a36Sopenharmony_ci return key_ref_to_ptr(session_keyring_r); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci/* 21662306a36Sopenharmony_ci * Install a thread keyring to the given credentials struct if it didn't have 21762306a36Sopenharmony_ci * one already. This is allowed to overrun the quota. 21862306a36Sopenharmony_ci * 21962306a36Sopenharmony_ci * Return: 0 if a thread keyring is now present; -errno on failure. 22062306a36Sopenharmony_ci */ 22162306a36Sopenharmony_ciint install_thread_keyring_to_cred(struct cred *new) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci struct key *keyring; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (new->thread_keyring) 22662306a36Sopenharmony_ci return 0; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci keyring = keyring_alloc("_tid", new->uid, new->gid, new, 22962306a36Sopenharmony_ci KEY_POS_ALL | KEY_USR_VIEW, 23062306a36Sopenharmony_ci KEY_ALLOC_QUOTA_OVERRUN, 23162306a36Sopenharmony_ci NULL, NULL); 23262306a36Sopenharmony_ci if (IS_ERR(keyring)) 23362306a36Sopenharmony_ci return PTR_ERR(keyring); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci new->thread_keyring = keyring; 23662306a36Sopenharmony_ci return 0; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci/* 24062306a36Sopenharmony_ci * Install a thread keyring to the current task if it didn't have one already. 24162306a36Sopenharmony_ci * 24262306a36Sopenharmony_ci * Return: 0 if a thread keyring is now present; -errno on failure. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_cistatic int install_thread_keyring(void) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci struct cred *new; 24762306a36Sopenharmony_ci int ret; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci new = prepare_creds(); 25062306a36Sopenharmony_ci if (!new) 25162306a36Sopenharmony_ci return -ENOMEM; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci ret = install_thread_keyring_to_cred(new); 25462306a36Sopenharmony_ci if (ret < 0) { 25562306a36Sopenharmony_ci abort_creds(new); 25662306a36Sopenharmony_ci return ret; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci return commit_creds(new); 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci/* 26362306a36Sopenharmony_ci * Install a process keyring to the given credentials struct if it didn't have 26462306a36Sopenharmony_ci * one already. This is allowed to overrun the quota. 26562306a36Sopenharmony_ci * 26662306a36Sopenharmony_ci * Return: 0 if a process keyring is now present; -errno on failure. 26762306a36Sopenharmony_ci */ 26862306a36Sopenharmony_ciint install_process_keyring_to_cred(struct cred *new) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci struct key *keyring; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (new->process_keyring) 27362306a36Sopenharmony_ci return 0; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci keyring = keyring_alloc("_pid", new->uid, new->gid, new, 27662306a36Sopenharmony_ci KEY_POS_ALL | KEY_USR_VIEW, 27762306a36Sopenharmony_ci KEY_ALLOC_QUOTA_OVERRUN, 27862306a36Sopenharmony_ci NULL, NULL); 27962306a36Sopenharmony_ci if (IS_ERR(keyring)) 28062306a36Sopenharmony_ci return PTR_ERR(keyring); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci new->process_keyring = keyring; 28362306a36Sopenharmony_ci return 0; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci/* 28762306a36Sopenharmony_ci * Install a process keyring to the current task if it didn't have one already. 28862306a36Sopenharmony_ci * 28962306a36Sopenharmony_ci * Return: 0 if a process keyring is now present; -errno on failure. 29062306a36Sopenharmony_ci */ 29162306a36Sopenharmony_cistatic int install_process_keyring(void) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci struct cred *new; 29462306a36Sopenharmony_ci int ret; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci new = prepare_creds(); 29762306a36Sopenharmony_ci if (!new) 29862306a36Sopenharmony_ci return -ENOMEM; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci ret = install_process_keyring_to_cred(new); 30162306a36Sopenharmony_ci if (ret < 0) { 30262306a36Sopenharmony_ci abort_creds(new); 30362306a36Sopenharmony_ci return ret; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci return commit_creds(new); 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci/* 31062306a36Sopenharmony_ci * Install the given keyring as the session keyring of the given credentials 31162306a36Sopenharmony_ci * struct, replacing the existing one if any. If the given keyring is NULL, 31262306a36Sopenharmony_ci * then install a new anonymous session keyring. 31362306a36Sopenharmony_ci * @cred can not be in use by any task yet. 31462306a36Sopenharmony_ci * 31562306a36Sopenharmony_ci * Return: 0 on success; -errno on failure. 31662306a36Sopenharmony_ci */ 31762306a36Sopenharmony_ciint install_session_keyring_to_cred(struct cred *cred, struct key *keyring) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci unsigned long flags; 32062306a36Sopenharmony_ci struct key *old; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci might_sleep(); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* create an empty session keyring */ 32562306a36Sopenharmony_ci if (!keyring) { 32662306a36Sopenharmony_ci flags = KEY_ALLOC_QUOTA_OVERRUN; 32762306a36Sopenharmony_ci if (cred->session_keyring) 32862306a36Sopenharmony_ci flags = KEY_ALLOC_IN_QUOTA; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci keyring = keyring_alloc("_ses", cred->uid, cred->gid, cred, 33162306a36Sopenharmony_ci KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ, 33262306a36Sopenharmony_ci flags, NULL, NULL); 33362306a36Sopenharmony_ci if (IS_ERR(keyring)) 33462306a36Sopenharmony_ci return PTR_ERR(keyring); 33562306a36Sopenharmony_ci } else { 33662306a36Sopenharmony_ci __key_get(keyring); 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci /* install the keyring */ 34062306a36Sopenharmony_ci old = cred->session_keyring; 34162306a36Sopenharmony_ci cred->session_keyring = keyring; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (old) 34462306a36Sopenharmony_ci key_put(old); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci return 0; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci/* 35062306a36Sopenharmony_ci * Install the given keyring as the session keyring of the current task, 35162306a36Sopenharmony_ci * replacing the existing one if any. If the given keyring is NULL, then 35262306a36Sopenharmony_ci * install a new anonymous session keyring. 35362306a36Sopenharmony_ci * 35462306a36Sopenharmony_ci * Return: 0 on success; -errno on failure. 35562306a36Sopenharmony_ci */ 35662306a36Sopenharmony_cistatic int install_session_keyring(struct key *keyring) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci struct cred *new; 35962306a36Sopenharmony_ci int ret; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci new = prepare_creds(); 36262306a36Sopenharmony_ci if (!new) 36362306a36Sopenharmony_ci return -ENOMEM; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci ret = install_session_keyring_to_cred(new, keyring); 36662306a36Sopenharmony_ci if (ret < 0) { 36762306a36Sopenharmony_ci abort_creds(new); 36862306a36Sopenharmony_ci return ret; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci return commit_creds(new); 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci/* 37562306a36Sopenharmony_ci * Handle the fsuid changing. 37662306a36Sopenharmony_ci */ 37762306a36Sopenharmony_civoid key_fsuid_changed(struct cred *new_cred) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci /* update the ownership of the thread keyring */ 38062306a36Sopenharmony_ci if (new_cred->thread_keyring) { 38162306a36Sopenharmony_ci down_write(&new_cred->thread_keyring->sem); 38262306a36Sopenharmony_ci new_cred->thread_keyring->uid = new_cred->fsuid; 38362306a36Sopenharmony_ci up_write(&new_cred->thread_keyring->sem); 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci/* 38862306a36Sopenharmony_ci * Handle the fsgid changing. 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_civoid key_fsgid_changed(struct cred *new_cred) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci /* update the ownership of the thread keyring */ 39362306a36Sopenharmony_ci if (new_cred->thread_keyring) { 39462306a36Sopenharmony_ci down_write(&new_cred->thread_keyring->sem); 39562306a36Sopenharmony_ci new_cred->thread_keyring->gid = new_cred->fsgid; 39662306a36Sopenharmony_ci up_write(&new_cred->thread_keyring->sem); 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci/* 40162306a36Sopenharmony_ci * Search the process keyrings attached to the supplied cred for the first 40262306a36Sopenharmony_ci * matching key under RCU conditions (the caller must be holding the RCU read 40362306a36Sopenharmony_ci * lock). 40462306a36Sopenharmony_ci * 40562306a36Sopenharmony_ci * The search criteria are the type and the match function. The description is 40662306a36Sopenharmony_ci * given to the match function as a parameter, but doesn't otherwise influence 40762306a36Sopenharmony_ci * the search. Typically the match function will compare the description 40862306a36Sopenharmony_ci * parameter to the key's description. 40962306a36Sopenharmony_ci * 41062306a36Sopenharmony_ci * This can only search keyrings that grant Search permission to the supplied 41162306a36Sopenharmony_ci * credentials. Keyrings linked to searched keyrings will also be searched if 41262306a36Sopenharmony_ci * they grant Search permission too. Keys can only be found if they grant 41362306a36Sopenharmony_ci * Search permission to the credentials. 41462306a36Sopenharmony_ci * 41562306a36Sopenharmony_ci * Returns a pointer to the key with the key usage count incremented if 41662306a36Sopenharmony_ci * successful, -EAGAIN if we didn't find any matching key or -ENOKEY if we only 41762306a36Sopenharmony_ci * matched negative keys. 41862306a36Sopenharmony_ci * 41962306a36Sopenharmony_ci * In the case of a successful return, the possession attribute is set on the 42062306a36Sopenharmony_ci * returned key reference. 42162306a36Sopenharmony_ci */ 42262306a36Sopenharmony_cikey_ref_t search_cred_keyrings_rcu(struct keyring_search_context *ctx) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct key *user_session; 42562306a36Sopenharmony_ci key_ref_t key_ref, ret, err; 42662306a36Sopenharmony_ci const struct cred *cred = ctx->cred; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* we want to return -EAGAIN or -ENOKEY if any of the keyrings were 42962306a36Sopenharmony_ci * searchable, but we failed to find a key or we found a negative key; 43062306a36Sopenharmony_ci * otherwise we want to return a sample error (probably -EACCES) if 43162306a36Sopenharmony_ci * none of the keyrings were searchable 43262306a36Sopenharmony_ci * 43362306a36Sopenharmony_ci * in terms of priority: success > -ENOKEY > -EAGAIN > other error 43462306a36Sopenharmony_ci */ 43562306a36Sopenharmony_ci key_ref = NULL; 43662306a36Sopenharmony_ci ret = NULL; 43762306a36Sopenharmony_ci err = ERR_PTR(-EAGAIN); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci /* search the thread keyring first */ 44062306a36Sopenharmony_ci if (cred->thread_keyring) { 44162306a36Sopenharmony_ci key_ref = keyring_search_rcu( 44262306a36Sopenharmony_ci make_key_ref(cred->thread_keyring, 1), ctx); 44362306a36Sopenharmony_ci if (!IS_ERR(key_ref)) 44462306a36Sopenharmony_ci goto found; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci switch (PTR_ERR(key_ref)) { 44762306a36Sopenharmony_ci case -EAGAIN: /* no key */ 44862306a36Sopenharmony_ci case -ENOKEY: /* negative key */ 44962306a36Sopenharmony_ci ret = key_ref; 45062306a36Sopenharmony_ci break; 45162306a36Sopenharmony_ci default: 45262306a36Sopenharmony_ci err = key_ref; 45362306a36Sopenharmony_ci break; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* search the process keyring second */ 45862306a36Sopenharmony_ci if (cred->process_keyring) { 45962306a36Sopenharmony_ci key_ref = keyring_search_rcu( 46062306a36Sopenharmony_ci make_key_ref(cred->process_keyring, 1), ctx); 46162306a36Sopenharmony_ci if (!IS_ERR(key_ref)) 46262306a36Sopenharmony_ci goto found; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci switch (PTR_ERR(key_ref)) { 46562306a36Sopenharmony_ci case -EAGAIN: /* no key */ 46662306a36Sopenharmony_ci if (ret) 46762306a36Sopenharmony_ci break; 46862306a36Sopenharmony_ci fallthrough; 46962306a36Sopenharmony_ci case -ENOKEY: /* negative key */ 47062306a36Sopenharmony_ci ret = key_ref; 47162306a36Sopenharmony_ci break; 47262306a36Sopenharmony_ci default: 47362306a36Sopenharmony_ci err = key_ref; 47462306a36Sopenharmony_ci break; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci /* search the session keyring */ 47962306a36Sopenharmony_ci if (cred->session_keyring) { 48062306a36Sopenharmony_ci key_ref = keyring_search_rcu( 48162306a36Sopenharmony_ci make_key_ref(cred->session_keyring, 1), ctx); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci if (!IS_ERR(key_ref)) 48462306a36Sopenharmony_ci goto found; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci switch (PTR_ERR(key_ref)) { 48762306a36Sopenharmony_ci case -EAGAIN: /* no key */ 48862306a36Sopenharmony_ci if (ret) 48962306a36Sopenharmony_ci break; 49062306a36Sopenharmony_ci fallthrough; 49162306a36Sopenharmony_ci case -ENOKEY: /* negative key */ 49262306a36Sopenharmony_ci ret = key_ref; 49362306a36Sopenharmony_ci break; 49462306a36Sopenharmony_ci default: 49562306a36Sopenharmony_ci err = key_ref; 49662306a36Sopenharmony_ci break; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci /* or search the user-session keyring */ 50062306a36Sopenharmony_ci else if ((user_session = get_user_session_keyring_rcu(cred))) { 50162306a36Sopenharmony_ci key_ref = keyring_search_rcu(make_key_ref(user_session, 1), 50262306a36Sopenharmony_ci ctx); 50362306a36Sopenharmony_ci key_put(user_session); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (!IS_ERR(key_ref)) 50662306a36Sopenharmony_ci goto found; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci switch (PTR_ERR(key_ref)) { 50962306a36Sopenharmony_ci case -EAGAIN: /* no key */ 51062306a36Sopenharmony_ci if (ret) 51162306a36Sopenharmony_ci break; 51262306a36Sopenharmony_ci fallthrough; 51362306a36Sopenharmony_ci case -ENOKEY: /* negative key */ 51462306a36Sopenharmony_ci ret = key_ref; 51562306a36Sopenharmony_ci break; 51662306a36Sopenharmony_ci default: 51762306a36Sopenharmony_ci err = key_ref; 51862306a36Sopenharmony_ci break; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* no key - decide on the error we're going to go for */ 52362306a36Sopenharmony_ci key_ref = ret ? ret : err; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cifound: 52662306a36Sopenharmony_ci return key_ref; 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci/* 53062306a36Sopenharmony_ci * Search the process keyrings attached to the supplied cred for the first 53162306a36Sopenharmony_ci * matching key in the manner of search_my_process_keyrings(), but also search 53262306a36Sopenharmony_ci * the keys attached to the assumed authorisation key using its credentials if 53362306a36Sopenharmony_ci * one is available. 53462306a36Sopenharmony_ci * 53562306a36Sopenharmony_ci * The caller must be holding the RCU read lock. 53662306a36Sopenharmony_ci * 53762306a36Sopenharmony_ci * Return same as search_cred_keyrings_rcu(). 53862306a36Sopenharmony_ci */ 53962306a36Sopenharmony_cikey_ref_t search_process_keyrings_rcu(struct keyring_search_context *ctx) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci struct request_key_auth *rka; 54262306a36Sopenharmony_ci key_ref_t key_ref, ret = ERR_PTR(-EACCES), err; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci key_ref = search_cred_keyrings_rcu(ctx); 54562306a36Sopenharmony_ci if (!IS_ERR(key_ref)) 54662306a36Sopenharmony_ci goto found; 54762306a36Sopenharmony_ci err = key_ref; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci /* if this process has an instantiation authorisation key, then we also 55062306a36Sopenharmony_ci * search the keyrings of the process mentioned there 55162306a36Sopenharmony_ci * - we don't permit access to request_key auth keys via this method 55262306a36Sopenharmony_ci */ 55362306a36Sopenharmony_ci if (ctx->cred->request_key_auth && 55462306a36Sopenharmony_ci ctx->cred == current_cred() && 55562306a36Sopenharmony_ci ctx->index_key.type != &key_type_request_key_auth 55662306a36Sopenharmony_ci ) { 55762306a36Sopenharmony_ci const struct cred *cred = ctx->cred; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (key_validate(cred->request_key_auth) == 0) { 56062306a36Sopenharmony_ci rka = ctx->cred->request_key_auth->payload.data[0]; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci //// was search_process_keyrings() [ie. recursive] 56362306a36Sopenharmony_ci ctx->cred = rka->cred; 56462306a36Sopenharmony_ci key_ref = search_cred_keyrings_rcu(ctx); 56562306a36Sopenharmony_ci ctx->cred = cred; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci if (!IS_ERR(key_ref)) 56862306a36Sopenharmony_ci goto found; 56962306a36Sopenharmony_ci ret = key_ref; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* no key - decide on the error we're going to go for */ 57462306a36Sopenharmony_ci if (err == ERR_PTR(-ENOKEY) || ret == ERR_PTR(-ENOKEY)) 57562306a36Sopenharmony_ci key_ref = ERR_PTR(-ENOKEY); 57662306a36Sopenharmony_ci else if (err == ERR_PTR(-EACCES)) 57762306a36Sopenharmony_ci key_ref = ret; 57862306a36Sopenharmony_ci else 57962306a36Sopenharmony_ci key_ref = err; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cifound: 58262306a36Sopenharmony_ci return key_ref; 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci/* 58562306a36Sopenharmony_ci * See if the key we're looking at is the target key. 58662306a36Sopenharmony_ci */ 58762306a36Sopenharmony_cibool lookup_user_key_possessed(const struct key *key, 58862306a36Sopenharmony_ci const struct key_match_data *match_data) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci return key == match_data->raw_data; 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci/* 59462306a36Sopenharmony_ci * Look up a key ID given us by userspace with a given permissions mask to get 59562306a36Sopenharmony_ci * the key it refers to. 59662306a36Sopenharmony_ci * 59762306a36Sopenharmony_ci * Flags can be passed to request that special keyrings be created if referred 59862306a36Sopenharmony_ci * to directly, to permit partially constructed keys to be found and to skip 59962306a36Sopenharmony_ci * validity and permission checks on the found key. 60062306a36Sopenharmony_ci * 60162306a36Sopenharmony_ci * Returns a pointer to the key with an incremented usage count if successful; 60262306a36Sopenharmony_ci * -EINVAL if the key ID is invalid; -ENOKEY if the key ID does not correspond 60362306a36Sopenharmony_ci * to a key or the best found key was a negative key; -EKEYREVOKED or 60462306a36Sopenharmony_ci * -EKEYEXPIRED if the best found key was revoked or expired; -EACCES if the 60562306a36Sopenharmony_ci * found key doesn't grant the requested permit or the LSM denied access to it; 60662306a36Sopenharmony_ci * or -ENOMEM if a special keyring couldn't be created. 60762306a36Sopenharmony_ci * 60862306a36Sopenharmony_ci * In the case of a successful return, the possession attribute is set on the 60962306a36Sopenharmony_ci * returned key reference. 61062306a36Sopenharmony_ci */ 61162306a36Sopenharmony_cikey_ref_t lookup_user_key(key_serial_t id, unsigned long lflags, 61262306a36Sopenharmony_ci enum key_need_perm need_perm) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci struct keyring_search_context ctx = { 61562306a36Sopenharmony_ci .match_data.cmp = lookup_user_key_possessed, 61662306a36Sopenharmony_ci .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, 61762306a36Sopenharmony_ci .flags = (KEYRING_SEARCH_NO_STATE_CHECK | 61862306a36Sopenharmony_ci KEYRING_SEARCH_RECURSE), 61962306a36Sopenharmony_ci }; 62062306a36Sopenharmony_ci struct request_key_auth *rka; 62162306a36Sopenharmony_ci struct key *key, *user_session; 62262306a36Sopenharmony_ci key_ref_t key_ref, skey_ref; 62362306a36Sopenharmony_ci int ret; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_citry_again: 62662306a36Sopenharmony_ci ctx.cred = get_current_cred(); 62762306a36Sopenharmony_ci key_ref = ERR_PTR(-ENOKEY); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci switch (id) { 63062306a36Sopenharmony_ci case KEY_SPEC_THREAD_KEYRING: 63162306a36Sopenharmony_ci if (!ctx.cred->thread_keyring) { 63262306a36Sopenharmony_ci if (!(lflags & KEY_LOOKUP_CREATE)) 63362306a36Sopenharmony_ci goto error; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci ret = install_thread_keyring(); 63662306a36Sopenharmony_ci if (ret < 0) { 63762306a36Sopenharmony_ci key_ref = ERR_PTR(ret); 63862306a36Sopenharmony_ci goto error; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci goto reget_creds; 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci key = ctx.cred->thread_keyring; 64462306a36Sopenharmony_ci __key_get(key); 64562306a36Sopenharmony_ci key_ref = make_key_ref(key, 1); 64662306a36Sopenharmony_ci break; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci case KEY_SPEC_PROCESS_KEYRING: 64962306a36Sopenharmony_ci if (!ctx.cred->process_keyring) { 65062306a36Sopenharmony_ci if (!(lflags & KEY_LOOKUP_CREATE)) 65162306a36Sopenharmony_ci goto error; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci ret = install_process_keyring(); 65462306a36Sopenharmony_ci if (ret < 0) { 65562306a36Sopenharmony_ci key_ref = ERR_PTR(ret); 65662306a36Sopenharmony_ci goto error; 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci goto reget_creds; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci key = ctx.cred->process_keyring; 66262306a36Sopenharmony_ci __key_get(key); 66362306a36Sopenharmony_ci key_ref = make_key_ref(key, 1); 66462306a36Sopenharmony_ci break; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci case KEY_SPEC_SESSION_KEYRING: 66762306a36Sopenharmony_ci if (!ctx.cred->session_keyring) { 66862306a36Sopenharmony_ci /* always install a session keyring upon access if one 66962306a36Sopenharmony_ci * doesn't exist yet */ 67062306a36Sopenharmony_ci ret = look_up_user_keyrings(NULL, &user_session); 67162306a36Sopenharmony_ci if (ret < 0) 67262306a36Sopenharmony_ci goto error; 67362306a36Sopenharmony_ci if (lflags & KEY_LOOKUP_CREATE) 67462306a36Sopenharmony_ci ret = join_session_keyring(NULL); 67562306a36Sopenharmony_ci else 67662306a36Sopenharmony_ci ret = install_session_keyring(user_session); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci key_put(user_session); 67962306a36Sopenharmony_ci if (ret < 0) 68062306a36Sopenharmony_ci goto error; 68162306a36Sopenharmony_ci goto reget_creds; 68262306a36Sopenharmony_ci } else if (test_bit(KEY_FLAG_UID_KEYRING, 68362306a36Sopenharmony_ci &ctx.cred->session_keyring->flags) && 68462306a36Sopenharmony_ci lflags & KEY_LOOKUP_CREATE) { 68562306a36Sopenharmony_ci ret = join_session_keyring(NULL); 68662306a36Sopenharmony_ci if (ret < 0) 68762306a36Sopenharmony_ci goto error; 68862306a36Sopenharmony_ci goto reget_creds; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci key = ctx.cred->session_keyring; 69262306a36Sopenharmony_ci __key_get(key); 69362306a36Sopenharmony_ci key_ref = make_key_ref(key, 1); 69462306a36Sopenharmony_ci break; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci case KEY_SPEC_USER_KEYRING: 69762306a36Sopenharmony_ci ret = look_up_user_keyrings(&key, NULL); 69862306a36Sopenharmony_ci if (ret < 0) 69962306a36Sopenharmony_ci goto error; 70062306a36Sopenharmony_ci key_ref = make_key_ref(key, 1); 70162306a36Sopenharmony_ci break; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci case KEY_SPEC_USER_SESSION_KEYRING: 70462306a36Sopenharmony_ci ret = look_up_user_keyrings(NULL, &key); 70562306a36Sopenharmony_ci if (ret < 0) 70662306a36Sopenharmony_ci goto error; 70762306a36Sopenharmony_ci key_ref = make_key_ref(key, 1); 70862306a36Sopenharmony_ci break; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci case KEY_SPEC_GROUP_KEYRING: 71162306a36Sopenharmony_ci /* group keyrings are not yet supported */ 71262306a36Sopenharmony_ci key_ref = ERR_PTR(-EINVAL); 71362306a36Sopenharmony_ci goto error; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci case KEY_SPEC_REQKEY_AUTH_KEY: 71662306a36Sopenharmony_ci key = ctx.cred->request_key_auth; 71762306a36Sopenharmony_ci if (!key) 71862306a36Sopenharmony_ci goto error; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci __key_get(key); 72162306a36Sopenharmony_ci key_ref = make_key_ref(key, 1); 72262306a36Sopenharmony_ci break; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci case KEY_SPEC_REQUESTOR_KEYRING: 72562306a36Sopenharmony_ci if (!ctx.cred->request_key_auth) 72662306a36Sopenharmony_ci goto error; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci down_read(&ctx.cred->request_key_auth->sem); 72962306a36Sopenharmony_ci if (test_bit(KEY_FLAG_REVOKED, 73062306a36Sopenharmony_ci &ctx.cred->request_key_auth->flags)) { 73162306a36Sopenharmony_ci key_ref = ERR_PTR(-EKEYREVOKED); 73262306a36Sopenharmony_ci key = NULL; 73362306a36Sopenharmony_ci } else { 73462306a36Sopenharmony_ci rka = ctx.cred->request_key_auth->payload.data[0]; 73562306a36Sopenharmony_ci key = rka->dest_keyring; 73662306a36Sopenharmony_ci __key_get(key); 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci up_read(&ctx.cred->request_key_auth->sem); 73962306a36Sopenharmony_ci if (!key) 74062306a36Sopenharmony_ci goto error; 74162306a36Sopenharmony_ci key_ref = make_key_ref(key, 1); 74262306a36Sopenharmony_ci break; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci default: 74562306a36Sopenharmony_ci key_ref = ERR_PTR(-EINVAL); 74662306a36Sopenharmony_ci if (id < 1) 74762306a36Sopenharmony_ci goto error; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci key = key_lookup(id); 75062306a36Sopenharmony_ci if (IS_ERR(key)) { 75162306a36Sopenharmony_ci key_ref = ERR_CAST(key); 75262306a36Sopenharmony_ci goto error; 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci key_ref = make_key_ref(key, 0); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci /* check to see if we possess the key */ 75862306a36Sopenharmony_ci ctx.index_key = key->index_key; 75962306a36Sopenharmony_ci ctx.match_data.raw_data = key; 76062306a36Sopenharmony_ci kdebug("check possessed"); 76162306a36Sopenharmony_ci rcu_read_lock(); 76262306a36Sopenharmony_ci skey_ref = search_process_keyrings_rcu(&ctx); 76362306a36Sopenharmony_ci rcu_read_unlock(); 76462306a36Sopenharmony_ci kdebug("possessed=%p", skey_ref); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci if (!IS_ERR(skey_ref)) { 76762306a36Sopenharmony_ci key_put(key); 76862306a36Sopenharmony_ci key_ref = skey_ref; 76962306a36Sopenharmony_ci } 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci break; 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci /* unlink does not use the nominated key in any way, so can skip all 77562306a36Sopenharmony_ci * the permission checks as it is only concerned with the keyring */ 77662306a36Sopenharmony_ci if (need_perm != KEY_NEED_UNLINK) { 77762306a36Sopenharmony_ci if (!(lflags & KEY_LOOKUP_PARTIAL)) { 77862306a36Sopenharmony_ci ret = wait_for_key_construction(key, true); 77962306a36Sopenharmony_ci switch (ret) { 78062306a36Sopenharmony_ci case -ERESTARTSYS: 78162306a36Sopenharmony_ci goto invalid_key; 78262306a36Sopenharmony_ci default: 78362306a36Sopenharmony_ci if (need_perm != KEY_AUTHTOKEN_OVERRIDE && 78462306a36Sopenharmony_ci need_perm != KEY_DEFER_PERM_CHECK) 78562306a36Sopenharmony_ci goto invalid_key; 78662306a36Sopenharmony_ci break; 78762306a36Sopenharmony_ci case 0: 78862306a36Sopenharmony_ci break; 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci } else if (need_perm != KEY_DEFER_PERM_CHECK) { 79162306a36Sopenharmony_ci ret = key_validate(key); 79262306a36Sopenharmony_ci if (ret < 0) 79362306a36Sopenharmony_ci goto invalid_key; 79462306a36Sopenharmony_ci } 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci ret = -EIO; 79762306a36Sopenharmony_ci if (!(lflags & KEY_LOOKUP_PARTIAL) && 79862306a36Sopenharmony_ci key_read_state(key) == KEY_IS_UNINSTANTIATED) 79962306a36Sopenharmony_ci goto invalid_key; 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci /* check the permissions */ 80362306a36Sopenharmony_ci ret = key_task_permission(key_ref, ctx.cred, need_perm); 80462306a36Sopenharmony_ci if (ret < 0) 80562306a36Sopenharmony_ci goto invalid_key; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci key->last_used_at = ktime_get_real_seconds(); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_cierror: 81062306a36Sopenharmony_ci put_cred(ctx.cred); 81162306a36Sopenharmony_ci return key_ref; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ciinvalid_key: 81462306a36Sopenharmony_ci key_ref_put(key_ref); 81562306a36Sopenharmony_ci key_ref = ERR_PTR(ret); 81662306a36Sopenharmony_ci goto error; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci /* if we attempted to install a keyring, then it may have caused new 81962306a36Sopenharmony_ci * creds to be installed */ 82062306a36Sopenharmony_cireget_creds: 82162306a36Sopenharmony_ci put_cred(ctx.cred); 82262306a36Sopenharmony_ci goto try_again; 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ciEXPORT_SYMBOL(lookup_user_key); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci/* 82762306a36Sopenharmony_ci * Join the named keyring as the session keyring if possible else attempt to 82862306a36Sopenharmony_ci * create a new one of that name and join that. 82962306a36Sopenharmony_ci * 83062306a36Sopenharmony_ci * If the name is NULL, an empty anonymous keyring will be installed as the 83162306a36Sopenharmony_ci * session keyring. 83262306a36Sopenharmony_ci * 83362306a36Sopenharmony_ci * Named session keyrings are joined with a semaphore held to prevent the 83462306a36Sopenharmony_ci * keyrings from going away whilst the attempt is made to going them and also 83562306a36Sopenharmony_ci * to prevent a race in creating compatible session keyrings. 83662306a36Sopenharmony_ci */ 83762306a36Sopenharmony_cilong join_session_keyring(const char *name) 83862306a36Sopenharmony_ci{ 83962306a36Sopenharmony_ci const struct cred *old; 84062306a36Sopenharmony_ci struct cred *new; 84162306a36Sopenharmony_ci struct key *keyring; 84262306a36Sopenharmony_ci long ret, serial; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci new = prepare_creds(); 84562306a36Sopenharmony_ci if (!new) 84662306a36Sopenharmony_ci return -ENOMEM; 84762306a36Sopenharmony_ci old = current_cred(); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci /* if no name is provided, install an anonymous keyring */ 85062306a36Sopenharmony_ci if (!name) { 85162306a36Sopenharmony_ci ret = install_session_keyring_to_cred(new, NULL); 85262306a36Sopenharmony_ci if (ret < 0) 85362306a36Sopenharmony_ci goto error; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci serial = new->session_keyring->serial; 85662306a36Sopenharmony_ci ret = commit_creds(new); 85762306a36Sopenharmony_ci if (ret == 0) 85862306a36Sopenharmony_ci ret = serial; 85962306a36Sopenharmony_ci goto okay; 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci /* allow the user to join or create a named keyring */ 86362306a36Sopenharmony_ci mutex_lock(&key_session_mutex); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci /* look for an existing keyring of this name */ 86662306a36Sopenharmony_ci keyring = find_keyring_by_name(name, false); 86762306a36Sopenharmony_ci if (PTR_ERR(keyring) == -ENOKEY) { 86862306a36Sopenharmony_ci /* not found - try and create a new one */ 86962306a36Sopenharmony_ci keyring = keyring_alloc( 87062306a36Sopenharmony_ci name, old->uid, old->gid, old, 87162306a36Sopenharmony_ci KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_LINK, 87262306a36Sopenharmony_ci KEY_ALLOC_IN_QUOTA, NULL, NULL); 87362306a36Sopenharmony_ci if (IS_ERR(keyring)) { 87462306a36Sopenharmony_ci ret = PTR_ERR(keyring); 87562306a36Sopenharmony_ci goto error2; 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci } else if (IS_ERR(keyring)) { 87862306a36Sopenharmony_ci ret = PTR_ERR(keyring); 87962306a36Sopenharmony_ci goto error2; 88062306a36Sopenharmony_ci } else if (keyring == new->session_keyring) { 88162306a36Sopenharmony_ci ret = 0; 88262306a36Sopenharmony_ci goto error3; 88362306a36Sopenharmony_ci } 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci /* we've got a keyring - now to install it */ 88662306a36Sopenharmony_ci ret = install_session_keyring_to_cred(new, keyring); 88762306a36Sopenharmony_ci if (ret < 0) 88862306a36Sopenharmony_ci goto error3; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci commit_creds(new); 89162306a36Sopenharmony_ci mutex_unlock(&key_session_mutex); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci ret = keyring->serial; 89462306a36Sopenharmony_ci key_put(keyring); 89562306a36Sopenharmony_ciokay: 89662306a36Sopenharmony_ci return ret; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_cierror3: 89962306a36Sopenharmony_ci key_put(keyring); 90062306a36Sopenharmony_cierror2: 90162306a36Sopenharmony_ci mutex_unlock(&key_session_mutex); 90262306a36Sopenharmony_cierror: 90362306a36Sopenharmony_ci abort_creds(new); 90462306a36Sopenharmony_ci return ret; 90562306a36Sopenharmony_ci} 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci/* 90862306a36Sopenharmony_ci * Replace a process's session keyring on behalf of one of its children when 90962306a36Sopenharmony_ci * the target process is about to resume userspace execution. 91062306a36Sopenharmony_ci */ 91162306a36Sopenharmony_civoid key_change_session_keyring(struct callback_head *twork) 91262306a36Sopenharmony_ci{ 91362306a36Sopenharmony_ci const struct cred *old = current_cred(); 91462306a36Sopenharmony_ci struct cred *new = container_of(twork, struct cred, rcu); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci if (unlikely(current->flags & PF_EXITING)) { 91762306a36Sopenharmony_ci put_cred(new); 91862306a36Sopenharmony_ci return; 91962306a36Sopenharmony_ci } 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci /* If get_ucounts fails more bits are needed in the refcount */ 92262306a36Sopenharmony_ci if (unlikely(!get_ucounts(old->ucounts))) { 92362306a36Sopenharmony_ci WARN_ONCE(1, "In %s get_ucounts failed\n", __func__); 92462306a36Sopenharmony_ci put_cred(new); 92562306a36Sopenharmony_ci return; 92662306a36Sopenharmony_ci } 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci new-> uid = old-> uid; 92962306a36Sopenharmony_ci new-> euid = old-> euid; 93062306a36Sopenharmony_ci new-> suid = old-> suid; 93162306a36Sopenharmony_ci new->fsuid = old->fsuid; 93262306a36Sopenharmony_ci new-> gid = old-> gid; 93362306a36Sopenharmony_ci new-> egid = old-> egid; 93462306a36Sopenharmony_ci new-> sgid = old-> sgid; 93562306a36Sopenharmony_ci new->fsgid = old->fsgid; 93662306a36Sopenharmony_ci new->user = get_uid(old->user); 93762306a36Sopenharmony_ci new->ucounts = old->ucounts; 93862306a36Sopenharmony_ci new->user_ns = get_user_ns(old->user_ns); 93962306a36Sopenharmony_ci new->group_info = get_group_info(old->group_info); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci new->securebits = old->securebits; 94262306a36Sopenharmony_ci new->cap_inheritable = old->cap_inheritable; 94362306a36Sopenharmony_ci new->cap_permitted = old->cap_permitted; 94462306a36Sopenharmony_ci new->cap_effective = old->cap_effective; 94562306a36Sopenharmony_ci new->cap_ambient = old->cap_ambient; 94662306a36Sopenharmony_ci new->cap_bset = old->cap_bset; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci new->jit_keyring = old->jit_keyring; 94962306a36Sopenharmony_ci new->thread_keyring = key_get(old->thread_keyring); 95062306a36Sopenharmony_ci new->process_keyring = key_get(old->process_keyring); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci security_transfer_creds(new, old); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci commit_creds(new); 95562306a36Sopenharmony_ci} 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci/* 95862306a36Sopenharmony_ci * Make sure that root's user and user-session keyrings exist. 95962306a36Sopenharmony_ci */ 96062306a36Sopenharmony_cistatic int __init init_root_keyring(void) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci return look_up_user_keyrings(NULL, NULL); 96362306a36Sopenharmony_ci} 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_cilate_initcall(init_root_keyring); 966