18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* Userspace key control operations 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved. 58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/sched.h> 108c2ecf20Sopenharmony_ci#include <linux/sched/task.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/syscalls.h> 138c2ecf20Sopenharmony_ci#include <linux/key.h> 148c2ecf20Sopenharmony_ci#include <linux/keyctl.h> 158c2ecf20Sopenharmony_ci#include <linux/fs.h> 168c2ecf20Sopenharmony_ci#include <linux/capability.h> 178c2ecf20Sopenharmony_ci#include <linux/cred.h> 188c2ecf20Sopenharmony_ci#include <linux/string.h> 198c2ecf20Sopenharmony_ci#include <linux/err.h> 208c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 218c2ecf20Sopenharmony_ci#include <linux/security.h> 228c2ecf20Sopenharmony_ci#include <linux/uio.h> 238c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 248c2ecf20Sopenharmony_ci#include <keys/request_key_auth-type.h> 258c2ecf20Sopenharmony_ci#include "internal.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define KEY_MAX_DESC_SIZE 4096 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic const unsigned char keyrings_capabilities[2] = { 308c2ecf20Sopenharmony_ci [0] = (KEYCTL_CAPS0_CAPABILITIES | 318c2ecf20Sopenharmony_ci (IS_ENABLED(CONFIG_PERSISTENT_KEYRINGS) ? KEYCTL_CAPS0_PERSISTENT_KEYRINGS : 0) | 328c2ecf20Sopenharmony_ci (IS_ENABLED(CONFIG_KEY_DH_OPERATIONS) ? KEYCTL_CAPS0_DIFFIE_HELLMAN : 0) | 338c2ecf20Sopenharmony_ci (IS_ENABLED(CONFIG_ASYMMETRIC_KEY_TYPE) ? KEYCTL_CAPS0_PUBLIC_KEY : 0) | 348c2ecf20Sopenharmony_ci (IS_ENABLED(CONFIG_BIG_KEYS) ? KEYCTL_CAPS0_BIG_KEY : 0) | 358c2ecf20Sopenharmony_ci KEYCTL_CAPS0_INVALIDATE | 368c2ecf20Sopenharmony_ci KEYCTL_CAPS0_RESTRICT_KEYRING | 378c2ecf20Sopenharmony_ci KEYCTL_CAPS0_MOVE 388c2ecf20Sopenharmony_ci ), 398c2ecf20Sopenharmony_ci [1] = (KEYCTL_CAPS1_NS_KEYRING_NAME | 408c2ecf20Sopenharmony_ci KEYCTL_CAPS1_NS_KEY_TAG | 418c2ecf20Sopenharmony_ci (IS_ENABLED(CONFIG_KEY_NOTIFICATIONS) ? KEYCTL_CAPS1_NOTIFICATIONS : 0) 428c2ecf20Sopenharmony_ci ), 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic int key_get_type_from_user(char *type, 468c2ecf20Sopenharmony_ci const char __user *_type, 478c2ecf20Sopenharmony_ci unsigned len) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci int ret; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci ret = strncpy_from_user(type, _type, len); 528c2ecf20Sopenharmony_ci if (ret < 0) 538c2ecf20Sopenharmony_ci return ret; 548c2ecf20Sopenharmony_ci if (ret == 0 || ret >= len) 558c2ecf20Sopenharmony_ci return -EINVAL; 568c2ecf20Sopenharmony_ci if (type[0] == '.') 578c2ecf20Sopenharmony_ci return -EPERM; 588c2ecf20Sopenharmony_ci type[len - 1] = '\0'; 598c2ecf20Sopenharmony_ci return 0; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* 638c2ecf20Sopenharmony_ci * Extract the description of a new key from userspace and either add it as a 648c2ecf20Sopenharmony_ci * new key to the specified keyring or update a matching key in that keyring. 658c2ecf20Sopenharmony_ci * 668c2ecf20Sopenharmony_ci * If the description is NULL or an empty string, the key type is asked to 678c2ecf20Sopenharmony_ci * generate one from the payload. 688c2ecf20Sopenharmony_ci * 698c2ecf20Sopenharmony_ci * The keyring must be writable so that we can attach the key to it. 708c2ecf20Sopenharmony_ci * 718c2ecf20Sopenharmony_ci * If successful, the new key's serial number is returned, otherwise an error 728c2ecf20Sopenharmony_ci * code is returned. 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_ciSYSCALL_DEFINE5(add_key, const char __user *, _type, 758c2ecf20Sopenharmony_ci const char __user *, _description, 768c2ecf20Sopenharmony_ci const void __user *, _payload, 778c2ecf20Sopenharmony_ci size_t, plen, 788c2ecf20Sopenharmony_ci key_serial_t, ringid) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci key_ref_t keyring_ref, key_ref; 818c2ecf20Sopenharmony_ci char type[32], *description; 828c2ecf20Sopenharmony_ci void *payload; 838c2ecf20Sopenharmony_ci long ret; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci ret = -EINVAL; 868c2ecf20Sopenharmony_ci if (plen > 1024 * 1024 - 1) 878c2ecf20Sopenharmony_ci goto error; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* draw all the data into kernel space */ 908c2ecf20Sopenharmony_ci ret = key_get_type_from_user(type, _type, sizeof(type)); 918c2ecf20Sopenharmony_ci if (ret < 0) 928c2ecf20Sopenharmony_ci goto error; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci description = NULL; 958c2ecf20Sopenharmony_ci if (_description) { 968c2ecf20Sopenharmony_ci description = strndup_user(_description, KEY_MAX_DESC_SIZE); 978c2ecf20Sopenharmony_ci if (IS_ERR(description)) { 988c2ecf20Sopenharmony_ci ret = PTR_ERR(description); 998c2ecf20Sopenharmony_ci goto error; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci if (!*description) { 1028c2ecf20Sopenharmony_ci kfree(description); 1038c2ecf20Sopenharmony_ci description = NULL; 1048c2ecf20Sopenharmony_ci } else if ((description[0] == '.') && 1058c2ecf20Sopenharmony_ci (strncmp(type, "keyring", 7) == 0)) { 1068c2ecf20Sopenharmony_ci ret = -EPERM; 1078c2ecf20Sopenharmony_ci goto error2; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* pull the payload in if one was supplied */ 1128c2ecf20Sopenharmony_ci payload = NULL; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (plen) { 1158c2ecf20Sopenharmony_ci ret = -ENOMEM; 1168c2ecf20Sopenharmony_ci payload = kvmalloc(plen, GFP_KERNEL); 1178c2ecf20Sopenharmony_ci if (!payload) 1188c2ecf20Sopenharmony_ci goto error2; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci ret = -EFAULT; 1218c2ecf20Sopenharmony_ci if (copy_from_user(payload, _payload, plen) != 0) 1228c2ecf20Sopenharmony_ci goto error3; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* find the target keyring (which must be writable) */ 1268c2ecf20Sopenharmony_ci keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE); 1278c2ecf20Sopenharmony_ci if (IS_ERR(keyring_ref)) { 1288c2ecf20Sopenharmony_ci ret = PTR_ERR(keyring_ref); 1298c2ecf20Sopenharmony_ci goto error3; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* create or update the requested key and add it to the target 1338c2ecf20Sopenharmony_ci * keyring */ 1348c2ecf20Sopenharmony_ci key_ref = key_create_or_update(keyring_ref, type, description, 1358c2ecf20Sopenharmony_ci payload, plen, KEY_PERM_UNDEF, 1368c2ecf20Sopenharmony_ci KEY_ALLOC_IN_QUOTA); 1378c2ecf20Sopenharmony_ci if (!IS_ERR(key_ref)) { 1388c2ecf20Sopenharmony_ci ret = key_ref_to_ptr(key_ref)->serial; 1398c2ecf20Sopenharmony_ci key_ref_put(key_ref); 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci else { 1428c2ecf20Sopenharmony_ci ret = PTR_ERR(key_ref); 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci key_ref_put(keyring_ref); 1468c2ecf20Sopenharmony_ci error3: 1478c2ecf20Sopenharmony_ci kvfree_sensitive(payload, plen); 1488c2ecf20Sopenharmony_ci error2: 1498c2ecf20Sopenharmony_ci kfree(description); 1508c2ecf20Sopenharmony_ci error: 1518c2ecf20Sopenharmony_ci return ret; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* 1558c2ecf20Sopenharmony_ci * Search the process keyrings and keyring trees linked from those for a 1568c2ecf20Sopenharmony_ci * matching key. Keyrings must have appropriate Search permission to be 1578c2ecf20Sopenharmony_ci * searched. 1588c2ecf20Sopenharmony_ci * 1598c2ecf20Sopenharmony_ci * If a key is found, it will be attached to the destination keyring if there's 1608c2ecf20Sopenharmony_ci * one specified and the serial number of the key will be returned. 1618c2ecf20Sopenharmony_ci * 1628c2ecf20Sopenharmony_ci * If no key is found, /sbin/request-key will be invoked if _callout_info is 1638c2ecf20Sopenharmony_ci * non-NULL in an attempt to create a key. The _callout_info string will be 1648c2ecf20Sopenharmony_ci * passed to /sbin/request-key to aid with completing the request. If the 1658c2ecf20Sopenharmony_ci * _callout_info string is "" then it will be changed to "-". 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_ciSYSCALL_DEFINE4(request_key, const char __user *, _type, 1688c2ecf20Sopenharmony_ci const char __user *, _description, 1698c2ecf20Sopenharmony_ci const char __user *, _callout_info, 1708c2ecf20Sopenharmony_ci key_serial_t, destringid) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct key_type *ktype; 1738c2ecf20Sopenharmony_ci struct key *key; 1748c2ecf20Sopenharmony_ci key_ref_t dest_ref; 1758c2ecf20Sopenharmony_ci size_t callout_len; 1768c2ecf20Sopenharmony_ci char type[32], *description, *callout_info; 1778c2ecf20Sopenharmony_ci long ret; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* pull the type into kernel space */ 1808c2ecf20Sopenharmony_ci ret = key_get_type_from_user(type, _type, sizeof(type)); 1818c2ecf20Sopenharmony_ci if (ret < 0) 1828c2ecf20Sopenharmony_ci goto error; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* pull the description into kernel space */ 1858c2ecf20Sopenharmony_ci description = strndup_user(_description, KEY_MAX_DESC_SIZE); 1868c2ecf20Sopenharmony_ci if (IS_ERR(description)) { 1878c2ecf20Sopenharmony_ci ret = PTR_ERR(description); 1888c2ecf20Sopenharmony_ci goto error; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* pull the callout info into kernel space */ 1928c2ecf20Sopenharmony_ci callout_info = NULL; 1938c2ecf20Sopenharmony_ci callout_len = 0; 1948c2ecf20Sopenharmony_ci if (_callout_info) { 1958c2ecf20Sopenharmony_ci callout_info = strndup_user(_callout_info, PAGE_SIZE); 1968c2ecf20Sopenharmony_ci if (IS_ERR(callout_info)) { 1978c2ecf20Sopenharmony_ci ret = PTR_ERR(callout_info); 1988c2ecf20Sopenharmony_ci goto error2; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci callout_len = strlen(callout_info); 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* get the destination keyring if specified */ 2048c2ecf20Sopenharmony_ci dest_ref = NULL; 2058c2ecf20Sopenharmony_ci if (destringid) { 2068c2ecf20Sopenharmony_ci dest_ref = lookup_user_key(destringid, KEY_LOOKUP_CREATE, 2078c2ecf20Sopenharmony_ci KEY_NEED_WRITE); 2088c2ecf20Sopenharmony_ci if (IS_ERR(dest_ref)) { 2098c2ecf20Sopenharmony_ci ret = PTR_ERR(dest_ref); 2108c2ecf20Sopenharmony_ci goto error3; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* find the key type */ 2158c2ecf20Sopenharmony_ci ktype = key_type_lookup(type); 2168c2ecf20Sopenharmony_ci if (IS_ERR(ktype)) { 2178c2ecf20Sopenharmony_ci ret = PTR_ERR(ktype); 2188c2ecf20Sopenharmony_ci goto error4; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* do the search */ 2228c2ecf20Sopenharmony_ci key = request_key_and_link(ktype, description, NULL, callout_info, 2238c2ecf20Sopenharmony_ci callout_len, NULL, key_ref_to_ptr(dest_ref), 2248c2ecf20Sopenharmony_ci KEY_ALLOC_IN_QUOTA); 2258c2ecf20Sopenharmony_ci if (IS_ERR(key)) { 2268c2ecf20Sopenharmony_ci ret = PTR_ERR(key); 2278c2ecf20Sopenharmony_ci goto error5; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* wait for the key to finish being constructed */ 2318c2ecf20Sopenharmony_ci ret = wait_for_key_construction(key, 1); 2328c2ecf20Sopenharmony_ci if (ret < 0) 2338c2ecf20Sopenharmony_ci goto error6; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci ret = key->serial; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cierror6: 2388c2ecf20Sopenharmony_ci key_put(key); 2398c2ecf20Sopenharmony_cierror5: 2408c2ecf20Sopenharmony_ci key_type_put(ktype); 2418c2ecf20Sopenharmony_cierror4: 2428c2ecf20Sopenharmony_ci key_ref_put(dest_ref); 2438c2ecf20Sopenharmony_cierror3: 2448c2ecf20Sopenharmony_ci kfree(callout_info); 2458c2ecf20Sopenharmony_cierror2: 2468c2ecf20Sopenharmony_ci kfree(description); 2478c2ecf20Sopenharmony_cierror: 2488c2ecf20Sopenharmony_ci return ret; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci/* 2528c2ecf20Sopenharmony_ci * Get the ID of the specified process keyring. 2538c2ecf20Sopenharmony_ci * 2548c2ecf20Sopenharmony_ci * The requested keyring must have search permission to be found. 2558c2ecf20Sopenharmony_ci * 2568c2ecf20Sopenharmony_ci * If successful, the ID of the requested keyring will be returned. 2578c2ecf20Sopenharmony_ci */ 2588c2ecf20Sopenharmony_cilong keyctl_get_keyring_ID(key_serial_t id, int create) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci key_ref_t key_ref; 2618c2ecf20Sopenharmony_ci unsigned long lflags; 2628c2ecf20Sopenharmony_ci long ret; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci lflags = create ? KEY_LOOKUP_CREATE : 0; 2658c2ecf20Sopenharmony_ci key_ref = lookup_user_key(id, lflags, KEY_NEED_SEARCH); 2668c2ecf20Sopenharmony_ci if (IS_ERR(key_ref)) { 2678c2ecf20Sopenharmony_ci ret = PTR_ERR(key_ref); 2688c2ecf20Sopenharmony_ci goto error; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci ret = key_ref_to_ptr(key_ref)->serial; 2728c2ecf20Sopenharmony_ci key_ref_put(key_ref); 2738c2ecf20Sopenharmony_cierror: 2748c2ecf20Sopenharmony_ci return ret; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci/* 2788c2ecf20Sopenharmony_ci * Join a (named) session keyring. 2798c2ecf20Sopenharmony_ci * 2808c2ecf20Sopenharmony_ci * Create and join an anonymous session keyring or join a named session 2818c2ecf20Sopenharmony_ci * keyring, creating it if necessary. A named session keyring must have Search 2828c2ecf20Sopenharmony_ci * permission for it to be joined. Session keyrings without this permit will 2838c2ecf20Sopenharmony_ci * be skipped over. It is not permitted for userspace to create or join 2848c2ecf20Sopenharmony_ci * keyrings whose name begin with a dot. 2858c2ecf20Sopenharmony_ci * 2868c2ecf20Sopenharmony_ci * If successful, the ID of the joined session keyring will be returned. 2878c2ecf20Sopenharmony_ci */ 2888c2ecf20Sopenharmony_cilong keyctl_join_session_keyring(const char __user *_name) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci char *name; 2918c2ecf20Sopenharmony_ci long ret; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci /* fetch the name from userspace */ 2948c2ecf20Sopenharmony_ci name = NULL; 2958c2ecf20Sopenharmony_ci if (_name) { 2968c2ecf20Sopenharmony_ci name = strndup_user(_name, KEY_MAX_DESC_SIZE); 2978c2ecf20Sopenharmony_ci if (IS_ERR(name)) { 2988c2ecf20Sopenharmony_ci ret = PTR_ERR(name); 2998c2ecf20Sopenharmony_ci goto error; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci ret = -EPERM; 3038c2ecf20Sopenharmony_ci if (name[0] == '.') 3048c2ecf20Sopenharmony_ci goto error_name; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci /* join the session */ 3088c2ecf20Sopenharmony_ci ret = join_session_keyring(name); 3098c2ecf20Sopenharmony_cierror_name: 3108c2ecf20Sopenharmony_ci kfree(name); 3118c2ecf20Sopenharmony_cierror: 3128c2ecf20Sopenharmony_ci return ret; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci/* 3168c2ecf20Sopenharmony_ci * Update a key's data payload from the given data. 3178c2ecf20Sopenharmony_ci * 3188c2ecf20Sopenharmony_ci * The key must grant the caller Write permission and the key type must support 3198c2ecf20Sopenharmony_ci * updating for this to work. A negative key can be positively instantiated 3208c2ecf20Sopenharmony_ci * with this call. 3218c2ecf20Sopenharmony_ci * 3228c2ecf20Sopenharmony_ci * If successful, 0 will be returned. If the key type does not support 3238c2ecf20Sopenharmony_ci * updating, then -EOPNOTSUPP will be returned. 3248c2ecf20Sopenharmony_ci */ 3258c2ecf20Sopenharmony_cilong keyctl_update_key(key_serial_t id, 3268c2ecf20Sopenharmony_ci const void __user *_payload, 3278c2ecf20Sopenharmony_ci size_t plen) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci key_ref_t key_ref; 3308c2ecf20Sopenharmony_ci void *payload; 3318c2ecf20Sopenharmony_ci long ret; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci ret = -EINVAL; 3348c2ecf20Sopenharmony_ci if (plen > PAGE_SIZE) 3358c2ecf20Sopenharmony_ci goto error; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci /* pull the payload in if one was supplied */ 3388c2ecf20Sopenharmony_ci payload = NULL; 3398c2ecf20Sopenharmony_ci if (plen) { 3408c2ecf20Sopenharmony_ci ret = -ENOMEM; 3418c2ecf20Sopenharmony_ci payload = kvmalloc(plen, GFP_KERNEL); 3428c2ecf20Sopenharmony_ci if (!payload) 3438c2ecf20Sopenharmony_ci goto error; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci ret = -EFAULT; 3468c2ecf20Sopenharmony_ci if (copy_from_user(payload, _payload, plen) != 0) 3478c2ecf20Sopenharmony_ci goto error2; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* find the target key (which must be writable) */ 3518c2ecf20Sopenharmony_ci key_ref = lookup_user_key(id, 0, KEY_NEED_WRITE); 3528c2ecf20Sopenharmony_ci if (IS_ERR(key_ref)) { 3538c2ecf20Sopenharmony_ci ret = PTR_ERR(key_ref); 3548c2ecf20Sopenharmony_ci goto error2; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci /* update the key */ 3588c2ecf20Sopenharmony_ci ret = key_update(key_ref, payload, plen); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci key_ref_put(key_ref); 3618c2ecf20Sopenharmony_cierror2: 3628c2ecf20Sopenharmony_ci kvfree_sensitive(payload, plen); 3638c2ecf20Sopenharmony_cierror: 3648c2ecf20Sopenharmony_ci return ret; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci/* 3688c2ecf20Sopenharmony_ci * Revoke a key. 3698c2ecf20Sopenharmony_ci * 3708c2ecf20Sopenharmony_ci * The key must be grant the caller Write or Setattr permission for this to 3718c2ecf20Sopenharmony_ci * work. The key type should give up its quota claim when revoked. The key 3728c2ecf20Sopenharmony_ci * and any links to the key will be automatically garbage collected after a 3738c2ecf20Sopenharmony_ci * certain amount of time (/proc/sys/kernel/keys/gc_delay). 3748c2ecf20Sopenharmony_ci * 3758c2ecf20Sopenharmony_ci * Keys with KEY_FLAG_KEEP set should not be revoked. 3768c2ecf20Sopenharmony_ci * 3778c2ecf20Sopenharmony_ci * If successful, 0 is returned. 3788c2ecf20Sopenharmony_ci */ 3798c2ecf20Sopenharmony_cilong keyctl_revoke_key(key_serial_t id) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci key_ref_t key_ref; 3828c2ecf20Sopenharmony_ci struct key *key; 3838c2ecf20Sopenharmony_ci long ret; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci key_ref = lookup_user_key(id, 0, KEY_NEED_WRITE); 3868c2ecf20Sopenharmony_ci if (IS_ERR(key_ref)) { 3878c2ecf20Sopenharmony_ci ret = PTR_ERR(key_ref); 3888c2ecf20Sopenharmony_ci if (ret != -EACCES) 3898c2ecf20Sopenharmony_ci goto error; 3908c2ecf20Sopenharmony_ci key_ref = lookup_user_key(id, 0, KEY_NEED_SETATTR); 3918c2ecf20Sopenharmony_ci if (IS_ERR(key_ref)) { 3928c2ecf20Sopenharmony_ci ret = PTR_ERR(key_ref); 3938c2ecf20Sopenharmony_ci goto error; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci key = key_ref_to_ptr(key_ref); 3988c2ecf20Sopenharmony_ci ret = 0; 3998c2ecf20Sopenharmony_ci if (test_bit(KEY_FLAG_KEEP, &key->flags)) 4008c2ecf20Sopenharmony_ci ret = -EPERM; 4018c2ecf20Sopenharmony_ci else 4028c2ecf20Sopenharmony_ci key_revoke(key); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci key_ref_put(key_ref); 4058c2ecf20Sopenharmony_cierror: 4068c2ecf20Sopenharmony_ci return ret; 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci/* 4108c2ecf20Sopenharmony_ci * Invalidate a key. 4118c2ecf20Sopenharmony_ci * 4128c2ecf20Sopenharmony_ci * The key must be grant the caller Invalidate permission for this to work. 4138c2ecf20Sopenharmony_ci * The key and any links to the key will be automatically garbage collected 4148c2ecf20Sopenharmony_ci * immediately. 4158c2ecf20Sopenharmony_ci * 4168c2ecf20Sopenharmony_ci * Keys with KEY_FLAG_KEEP set should not be invalidated. 4178c2ecf20Sopenharmony_ci * 4188c2ecf20Sopenharmony_ci * If successful, 0 is returned. 4198c2ecf20Sopenharmony_ci */ 4208c2ecf20Sopenharmony_cilong keyctl_invalidate_key(key_serial_t id) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci key_ref_t key_ref; 4238c2ecf20Sopenharmony_ci struct key *key; 4248c2ecf20Sopenharmony_ci long ret; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci kenter("%d", id); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci key_ref = lookup_user_key(id, 0, KEY_NEED_SEARCH); 4298c2ecf20Sopenharmony_ci if (IS_ERR(key_ref)) { 4308c2ecf20Sopenharmony_ci ret = PTR_ERR(key_ref); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci /* Root is permitted to invalidate certain special keys */ 4338c2ecf20Sopenharmony_ci if (capable(CAP_SYS_ADMIN)) { 4348c2ecf20Sopenharmony_ci key_ref = lookup_user_key(id, 0, KEY_SYSADMIN_OVERRIDE); 4358c2ecf20Sopenharmony_ci if (IS_ERR(key_ref)) 4368c2ecf20Sopenharmony_ci goto error; 4378c2ecf20Sopenharmony_ci if (test_bit(KEY_FLAG_ROOT_CAN_INVAL, 4388c2ecf20Sopenharmony_ci &key_ref_to_ptr(key_ref)->flags)) 4398c2ecf20Sopenharmony_ci goto invalidate; 4408c2ecf20Sopenharmony_ci goto error_put; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci goto error; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ciinvalidate: 4478c2ecf20Sopenharmony_ci key = key_ref_to_ptr(key_ref); 4488c2ecf20Sopenharmony_ci ret = 0; 4498c2ecf20Sopenharmony_ci if (test_bit(KEY_FLAG_KEEP, &key->flags)) 4508c2ecf20Sopenharmony_ci ret = -EPERM; 4518c2ecf20Sopenharmony_ci else 4528c2ecf20Sopenharmony_ci key_invalidate(key); 4538c2ecf20Sopenharmony_cierror_put: 4548c2ecf20Sopenharmony_ci key_ref_put(key_ref); 4558c2ecf20Sopenharmony_cierror: 4568c2ecf20Sopenharmony_ci kleave(" = %ld", ret); 4578c2ecf20Sopenharmony_ci return ret; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci/* 4618c2ecf20Sopenharmony_ci * Clear the specified keyring, creating an empty process keyring if one of the 4628c2ecf20Sopenharmony_ci * special keyring IDs is used. 4638c2ecf20Sopenharmony_ci * 4648c2ecf20Sopenharmony_ci * The keyring must grant the caller Write permission and not have 4658c2ecf20Sopenharmony_ci * KEY_FLAG_KEEP set for this to work. If successful, 0 will be returned. 4668c2ecf20Sopenharmony_ci */ 4678c2ecf20Sopenharmony_cilong keyctl_keyring_clear(key_serial_t ringid) 4688c2ecf20Sopenharmony_ci{ 4698c2ecf20Sopenharmony_ci key_ref_t keyring_ref; 4708c2ecf20Sopenharmony_ci struct key *keyring; 4718c2ecf20Sopenharmony_ci long ret; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE); 4748c2ecf20Sopenharmony_ci if (IS_ERR(keyring_ref)) { 4758c2ecf20Sopenharmony_ci ret = PTR_ERR(keyring_ref); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci /* Root is permitted to invalidate certain special keyrings */ 4788c2ecf20Sopenharmony_ci if (capable(CAP_SYS_ADMIN)) { 4798c2ecf20Sopenharmony_ci keyring_ref = lookup_user_key(ringid, 0, 4808c2ecf20Sopenharmony_ci KEY_SYSADMIN_OVERRIDE); 4818c2ecf20Sopenharmony_ci if (IS_ERR(keyring_ref)) 4828c2ecf20Sopenharmony_ci goto error; 4838c2ecf20Sopenharmony_ci if (test_bit(KEY_FLAG_ROOT_CAN_CLEAR, 4848c2ecf20Sopenharmony_ci &key_ref_to_ptr(keyring_ref)->flags)) 4858c2ecf20Sopenharmony_ci goto clear; 4868c2ecf20Sopenharmony_ci goto error_put; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci goto error; 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ciclear: 4938c2ecf20Sopenharmony_ci keyring = key_ref_to_ptr(keyring_ref); 4948c2ecf20Sopenharmony_ci if (test_bit(KEY_FLAG_KEEP, &keyring->flags)) 4958c2ecf20Sopenharmony_ci ret = -EPERM; 4968c2ecf20Sopenharmony_ci else 4978c2ecf20Sopenharmony_ci ret = keyring_clear(keyring); 4988c2ecf20Sopenharmony_cierror_put: 4998c2ecf20Sopenharmony_ci key_ref_put(keyring_ref); 5008c2ecf20Sopenharmony_cierror: 5018c2ecf20Sopenharmony_ci return ret; 5028c2ecf20Sopenharmony_ci} 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci/* 5058c2ecf20Sopenharmony_ci * Create a link from a keyring to a key if there's no matching key in the 5068c2ecf20Sopenharmony_ci * keyring, otherwise replace the link to the matching key with a link to the 5078c2ecf20Sopenharmony_ci * new key. 5088c2ecf20Sopenharmony_ci * 5098c2ecf20Sopenharmony_ci * The key must grant the caller Link permission and the the keyring must grant 5108c2ecf20Sopenharmony_ci * the caller Write permission. Furthermore, if an additional link is created, 5118c2ecf20Sopenharmony_ci * the keyring's quota will be extended. 5128c2ecf20Sopenharmony_ci * 5138c2ecf20Sopenharmony_ci * If successful, 0 will be returned. 5148c2ecf20Sopenharmony_ci */ 5158c2ecf20Sopenharmony_cilong keyctl_keyring_link(key_serial_t id, key_serial_t ringid) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci key_ref_t keyring_ref, key_ref; 5188c2ecf20Sopenharmony_ci long ret; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE); 5218c2ecf20Sopenharmony_ci if (IS_ERR(keyring_ref)) { 5228c2ecf20Sopenharmony_ci ret = PTR_ERR(keyring_ref); 5238c2ecf20Sopenharmony_ci goto error; 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE, KEY_NEED_LINK); 5278c2ecf20Sopenharmony_ci if (IS_ERR(key_ref)) { 5288c2ecf20Sopenharmony_ci ret = PTR_ERR(key_ref); 5298c2ecf20Sopenharmony_ci goto error2; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci ret = key_link(key_ref_to_ptr(keyring_ref), key_ref_to_ptr(key_ref)); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci key_ref_put(key_ref); 5358c2ecf20Sopenharmony_cierror2: 5368c2ecf20Sopenharmony_ci key_ref_put(keyring_ref); 5378c2ecf20Sopenharmony_cierror: 5388c2ecf20Sopenharmony_ci return ret; 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci/* 5428c2ecf20Sopenharmony_ci * Unlink a key from a keyring. 5438c2ecf20Sopenharmony_ci * 5448c2ecf20Sopenharmony_ci * The keyring must grant the caller Write permission for this to work; the key 5458c2ecf20Sopenharmony_ci * itself need not grant the caller anything. If the last link to a key is 5468c2ecf20Sopenharmony_ci * removed then that key will be scheduled for destruction. 5478c2ecf20Sopenharmony_ci * 5488c2ecf20Sopenharmony_ci * Keys or keyrings with KEY_FLAG_KEEP set should not be unlinked. 5498c2ecf20Sopenharmony_ci * 5508c2ecf20Sopenharmony_ci * If successful, 0 will be returned. 5518c2ecf20Sopenharmony_ci */ 5528c2ecf20Sopenharmony_cilong keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci key_ref_t keyring_ref, key_ref; 5558c2ecf20Sopenharmony_ci struct key *keyring, *key; 5568c2ecf20Sopenharmony_ci long ret; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci keyring_ref = lookup_user_key(ringid, 0, KEY_NEED_WRITE); 5598c2ecf20Sopenharmony_ci if (IS_ERR(keyring_ref)) { 5608c2ecf20Sopenharmony_ci ret = PTR_ERR(keyring_ref); 5618c2ecf20Sopenharmony_ci goto error; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci key_ref = lookup_user_key(id, KEY_LOOKUP_PARTIAL, KEY_NEED_UNLINK); 5658c2ecf20Sopenharmony_ci if (IS_ERR(key_ref)) { 5668c2ecf20Sopenharmony_ci ret = PTR_ERR(key_ref); 5678c2ecf20Sopenharmony_ci goto error2; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci keyring = key_ref_to_ptr(keyring_ref); 5718c2ecf20Sopenharmony_ci key = key_ref_to_ptr(key_ref); 5728c2ecf20Sopenharmony_ci if (test_bit(KEY_FLAG_KEEP, &keyring->flags) && 5738c2ecf20Sopenharmony_ci test_bit(KEY_FLAG_KEEP, &key->flags)) 5748c2ecf20Sopenharmony_ci ret = -EPERM; 5758c2ecf20Sopenharmony_ci else 5768c2ecf20Sopenharmony_ci ret = key_unlink(keyring, key); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci key_ref_put(key_ref); 5798c2ecf20Sopenharmony_cierror2: 5808c2ecf20Sopenharmony_ci key_ref_put(keyring_ref); 5818c2ecf20Sopenharmony_cierror: 5828c2ecf20Sopenharmony_ci return ret; 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci/* 5868c2ecf20Sopenharmony_ci * Move a link to a key from one keyring to another, displacing any matching 5878c2ecf20Sopenharmony_ci * key from the destination keyring. 5888c2ecf20Sopenharmony_ci * 5898c2ecf20Sopenharmony_ci * The key must grant the caller Link permission and both keyrings must grant 5908c2ecf20Sopenharmony_ci * the caller Write permission. There must also be a link in the from keyring 5918c2ecf20Sopenharmony_ci * to the key. If both keyrings are the same, nothing is done. 5928c2ecf20Sopenharmony_ci * 5938c2ecf20Sopenharmony_ci * If successful, 0 will be returned. 5948c2ecf20Sopenharmony_ci */ 5958c2ecf20Sopenharmony_cilong keyctl_keyring_move(key_serial_t id, key_serial_t from_ringid, 5968c2ecf20Sopenharmony_ci key_serial_t to_ringid, unsigned int flags) 5978c2ecf20Sopenharmony_ci{ 5988c2ecf20Sopenharmony_ci key_ref_t key_ref, from_ref, to_ref; 5998c2ecf20Sopenharmony_ci long ret; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci if (flags & ~KEYCTL_MOVE_EXCL) 6028c2ecf20Sopenharmony_ci return -EINVAL; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE, KEY_NEED_LINK); 6058c2ecf20Sopenharmony_ci if (IS_ERR(key_ref)) 6068c2ecf20Sopenharmony_ci return PTR_ERR(key_ref); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci from_ref = lookup_user_key(from_ringid, 0, KEY_NEED_WRITE); 6098c2ecf20Sopenharmony_ci if (IS_ERR(from_ref)) { 6108c2ecf20Sopenharmony_ci ret = PTR_ERR(from_ref); 6118c2ecf20Sopenharmony_ci goto error2; 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci to_ref = lookup_user_key(to_ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE); 6158c2ecf20Sopenharmony_ci if (IS_ERR(to_ref)) { 6168c2ecf20Sopenharmony_ci ret = PTR_ERR(to_ref); 6178c2ecf20Sopenharmony_ci goto error3; 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci ret = key_move(key_ref_to_ptr(key_ref), key_ref_to_ptr(from_ref), 6218c2ecf20Sopenharmony_ci key_ref_to_ptr(to_ref), flags); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci key_ref_put(to_ref); 6248c2ecf20Sopenharmony_cierror3: 6258c2ecf20Sopenharmony_ci key_ref_put(from_ref); 6268c2ecf20Sopenharmony_cierror2: 6278c2ecf20Sopenharmony_ci key_ref_put(key_ref); 6288c2ecf20Sopenharmony_ci return ret; 6298c2ecf20Sopenharmony_ci} 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci/* 6328c2ecf20Sopenharmony_ci * Return a description of a key to userspace. 6338c2ecf20Sopenharmony_ci * 6348c2ecf20Sopenharmony_ci * The key must grant the caller View permission for this to work. 6358c2ecf20Sopenharmony_ci * 6368c2ecf20Sopenharmony_ci * If there's a buffer, we place up to buflen bytes of data into it formatted 6378c2ecf20Sopenharmony_ci * in the following way: 6388c2ecf20Sopenharmony_ci * 6398c2ecf20Sopenharmony_ci * type;uid;gid;perm;description<NUL> 6408c2ecf20Sopenharmony_ci * 6418c2ecf20Sopenharmony_ci * If successful, we return the amount of description available, irrespective 6428c2ecf20Sopenharmony_ci * of how much we may have copied into the buffer. 6438c2ecf20Sopenharmony_ci */ 6448c2ecf20Sopenharmony_cilong keyctl_describe_key(key_serial_t keyid, 6458c2ecf20Sopenharmony_ci char __user *buffer, 6468c2ecf20Sopenharmony_ci size_t buflen) 6478c2ecf20Sopenharmony_ci{ 6488c2ecf20Sopenharmony_ci struct key *key, *instkey; 6498c2ecf20Sopenharmony_ci key_ref_t key_ref; 6508c2ecf20Sopenharmony_ci char *infobuf; 6518c2ecf20Sopenharmony_ci long ret; 6528c2ecf20Sopenharmony_ci int desclen, infolen; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_NEED_VIEW); 6558c2ecf20Sopenharmony_ci if (IS_ERR(key_ref)) { 6568c2ecf20Sopenharmony_ci /* viewing a key under construction is permitted if we have the 6578c2ecf20Sopenharmony_ci * authorisation token handy */ 6588c2ecf20Sopenharmony_ci if (PTR_ERR(key_ref) == -EACCES) { 6598c2ecf20Sopenharmony_ci instkey = key_get_instantiation_authkey(keyid); 6608c2ecf20Sopenharmony_ci if (!IS_ERR(instkey)) { 6618c2ecf20Sopenharmony_ci key_put(instkey); 6628c2ecf20Sopenharmony_ci key_ref = lookup_user_key(keyid, 6638c2ecf20Sopenharmony_ci KEY_LOOKUP_PARTIAL, 6648c2ecf20Sopenharmony_ci KEY_AUTHTOKEN_OVERRIDE); 6658c2ecf20Sopenharmony_ci if (!IS_ERR(key_ref)) 6668c2ecf20Sopenharmony_ci goto okay; 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci ret = PTR_ERR(key_ref); 6718c2ecf20Sopenharmony_ci goto error; 6728c2ecf20Sopenharmony_ci } 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ciokay: 6758c2ecf20Sopenharmony_ci key = key_ref_to_ptr(key_ref); 6768c2ecf20Sopenharmony_ci desclen = strlen(key->description); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci /* calculate how much information we're going to return */ 6798c2ecf20Sopenharmony_ci ret = -ENOMEM; 6808c2ecf20Sopenharmony_ci infobuf = kasprintf(GFP_KERNEL, 6818c2ecf20Sopenharmony_ci "%s;%d;%d;%08x;", 6828c2ecf20Sopenharmony_ci key->type->name, 6838c2ecf20Sopenharmony_ci from_kuid_munged(current_user_ns(), key->uid), 6848c2ecf20Sopenharmony_ci from_kgid_munged(current_user_ns(), key->gid), 6858c2ecf20Sopenharmony_ci key->perm); 6868c2ecf20Sopenharmony_ci if (!infobuf) 6878c2ecf20Sopenharmony_ci goto error2; 6888c2ecf20Sopenharmony_ci infolen = strlen(infobuf); 6898c2ecf20Sopenharmony_ci ret = infolen + desclen + 1; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci /* consider returning the data */ 6928c2ecf20Sopenharmony_ci if (buffer && buflen >= ret) { 6938c2ecf20Sopenharmony_ci if (copy_to_user(buffer, infobuf, infolen) != 0 || 6948c2ecf20Sopenharmony_ci copy_to_user(buffer + infolen, key->description, 6958c2ecf20Sopenharmony_ci desclen + 1) != 0) 6968c2ecf20Sopenharmony_ci ret = -EFAULT; 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci kfree(infobuf); 7008c2ecf20Sopenharmony_cierror2: 7018c2ecf20Sopenharmony_ci key_ref_put(key_ref); 7028c2ecf20Sopenharmony_cierror: 7038c2ecf20Sopenharmony_ci return ret; 7048c2ecf20Sopenharmony_ci} 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci/* 7078c2ecf20Sopenharmony_ci * Search the specified keyring and any keyrings it links to for a matching 7088c2ecf20Sopenharmony_ci * key. Only keyrings that grant the caller Search permission will be searched 7098c2ecf20Sopenharmony_ci * (this includes the starting keyring). Only keys with Search permission can 7108c2ecf20Sopenharmony_ci * be found. 7118c2ecf20Sopenharmony_ci * 7128c2ecf20Sopenharmony_ci * If successful, the found key will be linked to the destination keyring if 7138c2ecf20Sopenharmony_ci * supplied and the key has Link permission, and the found key ID will be 7148c2ecf20Sopenharmony_ci * returned. 7158c2ecf20Sopenharmony_ci */ 7168c2ecf20Sopenharmony_cilong keyctl_keyring_search(key_serial_t ringid, 7178c2ecf20Sopenharmony_ci const char __user *_type, 7188c2ecf20Sopenharmony_ci const char __user *_description, 7198c2ecf20Sopenharmony_ci key_serial_t destringid) 7208c2ecf20Sopenharmony_ci{ 7218c2ecf20Sopenharmony_ci struct key_type *ktype; 7228c2ecf20Sopenharmony_ci key_ref_t keyring_ref, key_ref, dest_ref; 7238c2ecf20Sopenharmony_ci char type[32], *description; 7248c2ecf20Sopenharmony_ci long ret; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci /* pull the type and description into kernel space */ 7278c2ecf20Sopenharmony_ci ret = key_get_type_from_user(type, _type, sizeof(type)); 7288c2ecf20Sopenharmony_ci if (ret < 0) 7298c2ecf20Sopenharmony_ci goto error; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci description = strndup_user(_description, KEY_MAX_DESC_SIZE); 7328c2ecf20Sopenharmony_ci if (IS_ERR(description)) { 7338c2ecf20Sopenharmony_ci ret = PTR_ERR(description); 7348c2ecf20Sopenharmony_ci goto error; 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci /* get the keyring at which to begin the search */ 7388c2ecf20Sopenharmony_ci keyring_ref = lookup_user_key(ringid, 0, KEY_NEED_SEARCH); 7398c2ecf20Sopenharmony_ci if (IS_ERR(keyring_ref)) { 7408c2ecf20Sopenharmony_ci ret = PTR_ERR(keyring_ref); 7418c2ecf20Sopenharmony_ci goto error2; 7428c2ecf20Sopenharmony_ci } 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci /* get the destination keyring if specified */ 7458c2ecf20Sopenharmony_ci dest_ref = NULL; 7468c2ecf20Sopenharmony_ci if (destringid) { 7478c2ecf20Sopenharmony_ci dest_ref = lookup_user_key(destringid, KEY_LOOKUP_CREATE, 7488c2ecf20Sopenharmony_ci KEY_NEED_WRITE); 7498c2ecf20Sopenharmony_ci if (IS_ERR(dest_ref)) { 7508c2ecf20Sopenharmony_ci ret = PTR_ERR(dest_ref); 7518c2ecf20Sopenharmony_ci goto error3; 7528c2ecf20Sopenharmony_ci } 7538c2ecf20Sopenharmony_ci } 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci /* find the key type */ 7568c2ecf20Sopenharmony_ci ktype = key_type_lookup(type); 7578c2ecf20Sopenharmony_ci if (IS_ERR(ktype)) { 7588c2ecf20Sopenharmony_ci ret = PTR_ERR(ktype); 7598c2ecf20Sopenharmony_ci goto error4; 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci /* do the search */ 7638c2ecf20Sopenharmony_ci key_ref = keyring_search(keyring_ref, ktype, description, true); 7648c2ecf20Sopenharmony_ci if (IS_ERR(key_ref)) { 7658c2ecf20Sopenharmony_ci ret = PTR_ERR(key_ref); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci /* treat lack or presence of a negative key the same */ 7688c2ecf20Sopenharmony_ci if (ret == -EAGAIN) 7698c2ecf20Sopenharmony_ci ret = -ENOKEY; 7708c2ecf20Sopenharmony_ci goto error5; 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci /* link the resulting key to the destination keyring if we can */ 7748c2ecf20Sopenharmony_ci if (dest_ref) { 7758c2ecf20Sopenharmony_ci ret = key_permission(key_ref, KEY_NEED_LINK); 7768c2ecf20Sopenharmony_ci if (ret < 0) 7778c2ecf20Sopenharmony_ci goto error6; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci ret = key_link(key_ref_to_ptr(dest_ref), key_ref_to_ptr(key_ref)); 7808c2ecf20Sopenharmony_ci if (ret < 0) 7818c2ecf20Sopenharmony_ci goto error6; 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci ret = key_ref_to_ptr(key_ref)->serial; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_cierror6: 7878c2ecf20Sopenharmony_ci key_ref_put(key_ref); 7888c2ecf20Sopenharmony_cierror5: 7898c2ecf20Sopenharmony_ci key_type_put(ktype); 7908c2ecf20Sopenharmony_cierror4: 7918c2ecf20Sopenharmony_ci key_ref_put(dest_ref); 7928c2ecf20Sopenharmony_cierror3: 7938c2ecf20Sopenharmony_ci key_ref_put(keyring_ref); 7948c2ecf20Sopenharmony_cierror2: 7958c2ecf20Sopenharmony_ci kfree(description); 7968c2ecf20Sopenharmony_cierror: 7978c2ecf20Sopenharmony_ci return ret; 7988c2ecf20Sopenharmony_ci} 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci/* 8018c2ecf20Sopenharmony_ci * Call the read method 8028c2ecf20Sopenharmony_ci */ 8038c2ecf20Sopenharmony_cistatic long __keyctl_read_key(struct key *key, char *buffer, size_t buflen) 8048c2ecf20Sopenharmony_ci{ 8058c2ecf20Sopenharmony_ci long ret; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci down_read(&key->sem); 8088c2ecf20Sopenharmony_ci ret = key_validate(key); 8098c2ecf20Sopenharmony_ci if (ret == 0) 8108c2ecf20Sopenharmony_ci ret = key->type->read(key, buffer, buflen); 8118c2ecf20Sopenharmony_ci up_read(&key->sem); 8128c2ecf20Sopenharmony_ci return ret; 8138c2ecf20Sopenharmony_ci} 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci/* 8168c2ecf20Sopenharmony_ci * Read a key's payload. 8178c2ecf20Sopenharmony_ci * 8188c2ecf20Sopenharmony_ci * The key must either grant the caller Read permission, or it must grant the 8198c2ecf20Sopenharmony_ci * caller Search permission when searched for from the process keyrings. 8208c2ecf20Sopenharmony_ci * 8218c2ecf20Sopenharmony_ci * If successful, we place up to buflen bytes of data into the buffer, if one 8228c2ecf20Sopenharmony_ci * is provided, and return the amount of data that is available in the key, 8238c2ecf20Sopenharmony_ci * irrespective of how much we copied into the buffer. 8248c2ecf20Sopenharmony_ci */ 8258c2ecf20Sopenharmony_cilong keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen) 8268c2ecf20Sopenharmony_ci{ 8278c2ecf20Sopenharmony_ci struct key *key; 8288c2ecf20Sopenharmony_ci key_ref_t key_ref; 8298c2ecf20Sopenharmony_ci long ret; 8308c2ecf20Sopenharmony_ci char *key_data = NULL; 8318c2ecf20Sopenharmony_ci size_t key_data_len; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci /* find the key first */ 8348c2ecf20Sopenharmony_ci key_ref = lookup_user_key(keyid, 0, KEY_DEFER_PERM_CHECK); 8358c2ecf20Sopenharmony_ci if (IS_ERR(key_ref)) { 8368c2ecf20Sopenharmony_ci ret = -ENOKEY; 8378c2ecf20Sopenharmony_ci goto out; 8388c2ecf20Sopenharmony_ci } 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci key = key_ref_to_ptr(key_ref); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci ret = key_read_state(key); 8438c2ecf20Sopenharmony_ci if (ret < 0) 8448c2ecf20Sopenharmony_ci goto key_put_out; /* Negatively instantiated */ 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci /* see if we can read it directly */ 8478c2ecf20Sopenharmony_ci ret = key_permission(key_ref, KEY_NEED_READ); 8488c2ecf20Sopenharmony_ci if (ret == 0) 8498c2ecf20Sopenharmony_ci goto can_read_key; 8508c2ecf20Sopenharmony_ci if (ret != -EACCES) 8518c2ecf20Sopenharmony_ci goto key_put_out; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci /* we can't; see if it's searchable from this process's keyrings 8548c2ecf20Sopenharmony_ci * - we automatically take account of the fact that it may be 8558c2ecf20Sopenharmony_ci * dangling off an instantiation key 8568c2ecf20Sopenharmony_ci */ 8578c2ecf20Sopenharmony_ci if (!is_key_possessed(key_ref)) { 8588c2ecf20Sopenharmony_ci ret = -EACCES; 8598c2ecf20Sopenharmony_ci goto key_put_out; 8608c2ecf20Sopenharmony_ci } 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci /* the key is probably readable - now try to read it */ 8638c2ecf20Sopenharmony_cican_read_key: 8648c2ecf20Sopenharmony_ci if (!key->type->read) { 8658c2ecf20Sopenharmony_ci ret = -EOPNOTSUPP; 8668c2ecf20Sopenharmony_ci goto key_put_out; 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if (!buffer || !buflen) { 8708c2ecf20Sopenharmony_ci /* Get the key length from the read method */ 8718c2ecf20Sopenharmony_ci ret = __keyctl_read_key(key, NULL, 0); 8728c2ecf20Sopenharmony_ci goto key_put_out; 8738c2ecf20Sopenharmony_ci } 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci /* 8768c2ecf20Sopenharmony_ci * Read the data with the semaphore held (since we might sleep) 8778c2ecf20Sopenharmony_ci * to protect against the key being updated or revoked. 8788c2ecf20Sopenharmony_ci * 8798c2ecf20Sopenharmony_ci * Allocating a temporary buffer to hold the keys before 8808c2ecf20Sopenharmony_ci * transferring them to user buffer to avoid potential 8818c2ecf20Sopenharmony_ci * deadlock involving page fault and mmap_lock. 8828c2ecf20Sopenharmony_ci * 8838c2ecf20Sopenharmony_ci * key_data_len = (buflen <= PAGE_SIZE) 8848c2ecf20Sopenharmony_ci * ? buflen : actual length of key data 8858c2ecf20Sopenharmony_ci * 8868c2ecf20Sopenharmony_ci * This prevents allocating arbitrary large buffer which can 8878c2ecf20Sopenharmony_ci * be much larger than the actual key length. In the latter case, 8888c2ecf20Sopenharmony_ci * at least 2 passes of this loop is required. 8898c2ecf20Sopenharmony_ci */ 8908c2ecf20Sopenharmony_ci key_data_len = (buflen <= PAGE_SIZE) ? buflen : 0; 8918c2ecf20Sopenharmony_ci for (;;) { 8928c2ecf20Sopenharmony_ci if (key_data_len) { 8938c2ecf20Sopenharmony_ci key_data = kvmalloc(key_data_len, GFP_KERNEL); 8948c2ecf20Sopenharmony_ci if (!key_data) { 8958c2ecf20Sopenharmony_ci ret = -ENOMEM; 8968c2ecf20Sopenharmony_ci goto key_put_out; 8978c2ecf20Sopenharmony_ci } 8988c2ecf20Sopenharmony_ci } 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci ret = __keyctl_read_key(key, key_data, key_data_len); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci /* 9038c2ecf20Sopenharmony_ci * Read methods will just return the required length without 9048c2ecf20Sopenharmony_ci * any copying if the provided length isn't large enough. 9058c2ecf20Sopenharmony_ci */ 9068c2ecf20Sopenharmony_ci if (ret <= 0 || ret > buflen) 9078c2ecf20Sopenharmony_ci break; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci /* 9108c2ecf20Sopenharmony_ci * The key may change (unlikely) in between 2 consecutive 9118c2ecf20Sopenharmony_ci * __keyctl_read_key() calls. In this case, we reallocate 9128c2ecf20Sopenharmony_ci * a larger buffer and redo the key read when 9138c2ecf20Sopenharmony_ci * key_data_len < ret <= buflen. 9148c2ecf20Sopenharmony_ci */ 9158c2ecf20Sopenharmony_ci if (ret > key_data_len) { 9168c2ecf20Sopenharmony_ci if (unlikely(key_data)) 9178c2ecf20Sopenharmony_ci kvfree_sensitive(key_data, key_data_len); 9188c2ecf20Sopenharmony_ci key_data_len = ret; 9198c2ecf20Sopenharmony_ci continue; /* Allocate buffer */ 9208c2ecf20Sopenharmony_ci } 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci if (copy_to_user(buffer, key_data, ret)) 9238c2ecf20Sopenharmony_ci ret = -EFAULT; 9248c2ecf20Sopenharmony_ci break; 9258c2ecf20Sopenharmony_ci } 9268c2ecf20Sopenharmony_ci kvfree_sensitive(key_data, key_data_len); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_cikey_put_out: 9298c2ecf20Sopenharmony_ci key_put(key); 9308c2ecf20Sopenharmony_ciout: 9318c2ecf20Sopenharmony_ci return ret; 9328c2ecf20Sopenharmony_ci} 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci/* 9358c2ecf20Sopenharmony_ci * Change the ownership of a key 9368c2ecf20Sopenharmony_ci * 9378c2ecf20Sopenharmony_ci * The key must grant the caller Setattr permission for this to work, though 9388c2ecf20Sopenharmony_ci * the key need not be fully instantiated yet. For the UID to be changed, or 9398c2ecf20Sopenharmony_ci * for the GID to be changed to a group the caller is not a member of, the 9408c2ecf20Sopenharmony_ci * caller must have sysadmin capability. If either uid or gid is -1 then that 9418c2ecf20Sopenharmony_ci * attribute is not changed. 9428c2ecf20Sopenharmony_ci * 9438c2ecf20Sopenharmony_ci * If the UID is to be changed, the new user must have sufficient quota to 9448c2ecf20Sopenharmony_ci * accept the key. The quota deduction will be removed from the old user to 9458c2ecf20Sopenharmony_ci * the new user should the attribute be changed. 9468c2ecf20Sopenharmony_ci * 9478c2ecf20Sopenharmony_ci * If successful, 0 will be returned. 9488c2ecf20Sopenharmony_ci */ 9498c2ecf20Sopenharmony_cilong keyctl_chown_key(key_serial_t id, uid_t user, gid_t group) 9508c2ecf20Sopenharmony_ci{ 9518c2ecf20Sopenharmony_ci struct key_user *newowner, *zapowner = NULL; 9528c2ecf20Sopenharmony_ci struct key *key; 9538c2ecf20Sopenharmony_ci key_ref_t key_ref; 9548c2ecf20Sopenharmony_ci long ret; 9558c2ecf20Sopenharmony_ci kuid_t uid; 9568c2ecf20Sopenharmony_ci kgid_t gid; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci uid = make_kuid(current_user_ns(), user); 9598c2ecf20Sopenharmony_ci gid = make_kgid(current_user_ns(), group); 9608c2ecf20Sopenharmony_ci ret = -EINVAL; 9618c2ecf20Sopenharmony_ci if ((user != (uid_t) -1) && !uid_valid(uid)) 9628c2ecf20Sopenharmony_ci goto error; 9638c2ecf20Sopenharmony_ci if ((group != (gid_t) -1) && !gid_valid(gid)) 9648c2ecf20Sopenharmony_ci goto error; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci ret = 0; 9678c2ecf20Sopenharmony_ci if (user == (uid_t) -1 && group == (gid_t) -1) 9688c2ecf20Sopenharmony_ci goto error; 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL, 9718c2ecf20Sopenharmony_ci KEY_NEED_SETATTR); 9728c2ecf20Sopenharmony_ci if (IS_ERR(key_ref)) { 9738c2ecf20Sopenharmony_ci ret = PTR_ERR(key_ref); 9748c2ecf20Sopenharmony_ci goto error; 9758c2ecf20Sopenharmony_ci } 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci key = key_ref_to_ptr(key_ref); 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci /* make the changes with the locks held to prevent chown/chown races */ 9808c2ecf20Sopenharmony_ci ret = -EACCES; 9818c2ecf20Sopenharmony_ci down_write(&key->sem); 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci { 9848c2ecf20Sopenharmony_ci bool is_privileged_op = false; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci /* only the sysadmin can chown a key to some other UID */ 9878c2ecf20Sopenharmony_ci if (user != (uid_t) -1 && !uid_eq(key->uid, uid)) 9888c2ecf20Sopenharmony_ci is_privileged_op = true; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci /* only the sysadmin can set the key's GID to a group other 9918c2ecf20Sopenharmony_ci * than one of those that the current process subscribes to */ 9928c2ecf20Sopenharmony_ci if (group != (gid_t) -1 && !gid_eq(gid, key->gid) && !in_group_p(gid)) 9938c2ecf20Sopenharmony_ci is_privileged_op = true; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci if (is_privileged_op && !capable(CAP_SYS_ADMIN)) 9968c2ecf20Sopenharmony_ci goto error_put; 9978c2ecf20Sopenharmony_ci } 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci /* change the UID */ 10008c2ecf20Sopenharmony_ci if (user != (uid_t) -1 && !uid_eq(uid, key->uid)) { 10018c2ecf20Sopenharmony_ci ret = -ENOMEM; 10028c2ecf20Sopenharmony_ci newowner = key_user_lookup(uid); 10038c2ecf20Sopenharmony_ci if (!newowner) 10048c2ecf20Sopenharmony_ci goto error_put; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci /* transfer the quota burden to the new user */ 10078c2ecf20Sopenharmony_ci if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { 10088c2ecf20Sopenharmony_ci unsigned maxkeys = uid_eq(uid, GLOBAL_ROOT_UID) ? 10098c2ecf20Sopenharmony_ci key_quota_root_maxkeys : key_quota_maxkeys; 10108c2ecf20Sopenharmony_ci unsigned maxbytes = uid_eq(uid, GLOBAL_ROOT_UID) ? 10118c2ecf20Sopenharmony_ci key_quota_root_maxbytes : key_quota_maxbytes; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci spin_lock(&newowner->lock); 10148c2ecf20Sopenharmony_ci if (newowner->qnkeys + 1 > maxkeys || 10158c2ecf20Sopenharmony_ci newowner->qnbytes + key->quotalen > maxbytes || 10168c2ecf20Sopenharmony_ci newowner->qnbytes + key->quotalen < 10178c2ecf20Sopenharmony_ci newowner->qnbytes) 10188c2ecf20Sopenharmony_ci goto quota_overrun; 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci newowner->qnkeys++; 10218c2ecf20Sopenharmony_ci newowner->qnbytes += key->quotalen; 10228c2ecf20Sopenharmony_ci spin_unlock(&newowner->lock); 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci spin_lock(&key->user->lock); 10258c2ecf20Sopenharmony_ci key->user->qnkeys--; 10268c2ecf20Sopenharmony_ci key->user->qnbytes -= key->quotalen; 10278c2ecf20Sopenharmony_ci spin_unlock(&key->user->lock); 10288c2ecf20Sopenharmony_ci } 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci atomic_dec(&key->user->nkeys); 10318c2ecf20Sopenharmony_ci atomic_inc(&newowner->nkeys); 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci if (key->state != KEY_IS_UNINSTANTIATED) { 10348c2ecf20Sopenharmony_ci atomic_dec(&key->user->nikeys); 10358c2ecf20Sopenharmony_ci atomic_inc(&newowner->nikeys); 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci zapowner = key->user; 10398c2ecf20Sopenharmony_ci key->user = newowner; 10408c2ecf20Sopenharmony_ci key->uid = uid; 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci /* change the GID */ 10448c2ecf20Sopenharmony_ci if (group != (gid_t) -1) 10458c2ecf20Sopenharmony_ci key->gid = gid; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci notify_key(key, NOTIFY_KEY_SETATTR, 0); 10488c2ecf20Sopenharmony_ci ret = 0; 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_cierror_put: 10518c2ecf20Sopenharmony_ci up_write(&key->sem); 10528c2ecf20Sopenharmony_ci key_put(key); 10538c2ecf20Sopenharmony_ci if (zapowner) 10548c2ecf20Sopenharmony_ci key_user_put(zapowner); 10558c2ecf20Sopenharmony_cierror: 10568c2ecf20Sopenharmony_ci return ret; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ciquota_overrun: 10598c2ecf20Sopenharmony_ci spin_unlock(&newowner->lock); 10608c2ecf20Sopenharmony_ci zapowner = newowner; 10618c2ecf20Sopenharmony_ci ret = -EDQUOT; 10628c2ecf20Sopenharmony_ci goto error_put; 10638c2ecf20Sopenharmony_ci} 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci/* 10668c2ecf20Sopenharmony_ci * Change the permission mask on a key. 10678c2ecf20Sopenharmony_ci * 10688c2ecf20Sopenharmony_ci * The key must grant the caller Setattr permission for this to work, though 10698c2ecf20Sopenharmony_ci * the key need not be fully instantiated yet. If the caller does not have 10708c2ecf20Sopenharmony_ci * sysadmin capability, it may only change the permission on keys that it owns. 10718c2ecf20Sopenharmony_ci */ 10728c2ecf20Sopenharmony_cilong keyctl_setperm_key(key_serial_t id, key_perm_t perm) 10738c2ecf20Sopenharmony_ci{ 10748c2ecf20Sopenharmony_ci struct key *key; 10758c2ecf20Sopenharmony_ci key_ref_t key_ref; 10768c2ecf20Sopenharmony_ci long ret; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci ret = -EINVAL; 10798c2ecf20Sopenharmony_ci if (perm & ~(KEY_POS_ALL | KEY_USR_ALL | KEY_GRP_ALL | KEY_OTH_ALL)) 10808c2ecf20Sopenharmony_ci goto error; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL, 10838c2ecf20Sopenharmony_ci KEY_NEED_SETATTR); 10848c2ecf20Sopenharmony_ci if (IS_ERR(key_ref)) { 10858c2ecf20Sopenharmony_ci ret = PTR_ERR(key_ref); 10868c2ecf20Sopenharmony_ci goto error; 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci key = key_ref_to_ptr(key_ref); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci /* make the changes with the locks held to prevent chown/chmod races */ 10928c2ecf20Sopenharmony_ci ret = -EACCES; 10938c2ecf20Sopenharmony_ci down_write(&key->sem); 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci /* if we're not the sysadmin, we can only change a key that we own */ 10968c2ecf20Sopenharmony_ci if (uid_eq(key->uid, current_fsuid()) || capable(CAP_SYS_ADMIN)) { 10978c2ecf20Sopenharmony_ci key->perm = perm; 10988c2ecf20Sopenharmony_ci notify_key(key, NOTIFY_KEY_SETATTR, 0); 10998c2ecf20Sopenharmony_ci ret = 0; 11008c2ecf20Sopenharmony_ci } 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci up_write(&key->sem); 11038c2ecf20Sopenharmony_ci key_put(key); 11048c2ecf20Sopenharmony_cierror: 11058c2ecf20Sopenharmony_ci return ret; 11068c2ecf20Sopenharmony_ci} 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci/* 11098c2ecf20Sopenharmony_ci * Get the destination keyring for instantiation and check that the caller has 11108c2ecf20Sopenharmony_ci * Write permission on it. 11118c2ecf20Sopenharmony_ci */ 11128c2ecf20Sopenharmony_cistatic long get_instantiation_keyring(key_serial_t ringid, 11138c2ecf20Sopenharmony_ci struct request_key_auth *rka, 11148c2ecf20Sopenharmony_ci struct key **_dest_keyring) 11158c2ecf20Sopenharmony_ci{ 11168c2ecf20Sopenharmony_ci key_ref_t dkref; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci *_dest_keyring = NULL; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci /* just return a NULL pointer if we weren't asked to make a link */ 11218c2ecf20Sopenharmony_ci if (ringid == 0) 11228c2ecf20Sopenharmony_ci return 0; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci /* if a specific keyring is nominated by ID, then use that */ 11258c2ecf20Sopenharmony_ci if (ringid > 0) { 11268c2ecf20Sopenharmony_ci dkref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE); 11278c2ecf20Sopenharmony_ci if (IS_ERR(dkref)) 11288c2ecf20Sopenharmony_ci return PTR_ERR(dkref); 11298c2ecf20Sopenharmony_ci *_dest_keyring = key_ref_to_ptr(dkref); 11308c2ecf20Sopenharmony_ci return 0; 11318c2ecf20Sopenharmony_ci } 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci if (ringid == KEY_SPEC_REQKEY_AUTH_KEY) 11348c2ecf20Sopenharmony_ci return -EINVAL; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci /* otherwise specify the destination keyring recorded in the 11378c2ecf20Sopenharmony_ci * authorisation key (any KEY_SPEC_*_KEYRING) */ 11388c2ecf20Sopenharmony_ci if (ringid >= KEY_SPEC_REQUESTOR_KEYRING) { 11398c2ecf20Sopenharmony_ci *_dest_keyring = key_get(rka->dest_keyring); 11408c2ecf20Sopenharmony_ci return 0; 11418c2ecf20Sopenharmony_ci } 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci return -ENOKEY; 11448c2ecf20Sopenharmony_ci} 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci/* 11478c2ecf20Sopenharmony_ci * Change the request_key authorisation key on the current process. 11488c2ecf20Sopenharmony_ci */ 11498c2ecf20Sopenharmony_cistatic int keyctl_change_reqkey_auth(struct key *key) 11508c2ecf20Sopenharmony_ci{ 11518c2ecf20Sopenharmony_ci struct cred *new; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci new = prepare_creds(); 11548c2ecf20Sopenharmony_ci if (!new) 11558c2ecf20Sopenharmony_ci return -ENOMEM; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci key_put(new->request_key_auth); 11588c2ecf20Sopenharmony_ci new->request_key_auth = key_get(key); 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci return commit_creds(new); 11618c2ecf20Sopenharmony_ci} 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci/* 11648c2ecf20Sopenharmony_ci * Instantiate a key with the specified payload and link the key into the 11658c2ecf20Sopenharmony_ci * destination keyring if one is given. 11668c2ecf20Sopenharmony_ci * 11678c2ecf20Sopenharmony_ci * The caller must have the appropriate instantiation permit set for this to 11688c2ecf20Sopenharmony_ci * work (see keyctl_assume_authority). No other permissions are required. 11698c2ecf20Sopenharmony_ci * 11708c2ecf20Sopenharmony_ci * If successful, 0 will be returned. 11718c2ecf20Sopenharmony_ci */ 11728c2ecf20Sopenharmony_cistatic long keyctl_instantiate_key_common(key_serial_t id, 11738c2ecf20Sopenharmony_ci struct iov_iter *from, 11748c2ecf20Sopenharmony_ci key_serial_t ringid) 11758c2ecf20Sopenharmony_ci{ 11768c2ecf20Sopenharmony_ci const struct cred *cred = current_cred(); 11778c2ecf20Sopenharmony_ci struct request_key_auth *rka; 11788c2ecf20Sopenharmony_ci struct key *instkey, *dest_keyring; 11798c2ecf20Sopenharmony_ci size_t plen = from ? iov_iter_count(from) : 0; 11808c2ecf20Sopenharmony_ci void *payload; 11818c2ecf20Sopenharmony_ci long ret; 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci kenter("%d,,%zu,%d", id, plen, ringid); 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci if (!plen) 11868c2ecf20Sopenharmony_ci from = NULL; 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci ret = -EINVAL; 11898c2ecf20Sopenharmony_ci if (plen > 1024 * 1024 - 1) 11908c2ecf20Sopenharmony_ci goto error; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci /* the appropriate instantiation authorisation key must have been 11938c2ecf20Sopenharmony_ci * assumed before calling this */ 11948c2ecf20Sopenharmony_ci ret = -EPERM; 11958c2ecf20Sopenharmony_ci instkey = cred->request_key_auth; 11968c2ecf20Sopenharmony_ci if (!instkey) 11978c2ecf20Sopenharmony_ci goto error; 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci rka = instkey->payload.data[0]; 12008c2ecf20Sopenharmony_ci if (rka->target_key->serial != id) 12018c2ecf20Sopenharmony_ci goto error; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci /* pull the payload in if one was supplied */ 12048c2ecf20Sopenharmony_ci payload = NULL; 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci if (from) { 12078c2ecf20Sopenharmony_ci ret = -ENOMEM; 12088c2ecf20Sopenharmony_ci payload = kvmalloc(plen, GFP_KERNEL); 12098c2ecf20Sopenharmony_ci if (!payload) 12108c2ecf20Sopenharmony_ci goto error; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci ret = -EFAULT; 12138c2ecf20Sopenharmony_ci if (!copy_from_iter_full(payload, plen, from)) 12148c2ecf20Sopenharmony_ci goto error2; 12158c2ecf20Sopenharmony_ci } 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci /* find the destination keyring amongst those belonging to the 12188c2ecf20Sopenharmony_ci * requesting task */ 12198c2ecf20Sopenharmony_ci ret = get_instantiation_keyring(ringid, rka, &dest_keyring); 12208c2ecf20Sopenharmony_ci if (ret < 0) 12218c2ecf20Sopenharmony_ci goto error2; 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci /* instantiate the key and link it into a keyring */ 12248c2ecf20Sopenharmony_ci ret = key_instantiate_and_link(rka->target_key, payload, plen, 12258c2ecf20Sopenharmony_ci dest_keyring, instkey); 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci key_put(dest_keyring); 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci /* discard the assumed authority if it's just been disabled by 12308c2ecf20Sopenharmony_ci * instantiation of the key */ 12318c2ecf20Sopenharmony_ci if (ret == 0) 12328c2ecf20Sopenharmony_ci keyctl_change_reqkey_auth(NULL); 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_cierror2: 12358c2ecf20Sopenharmony_ci kvfree_sensitive(payload, plen); 12368c2ecf20Sopenharmony_cierror: 12378c2ecf20Sopenharmony_ci return ret; 12388c2ecf20Sopenharmony_ci} 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci/* 12418c2ecf20Sopenharmony_ci * Instantiate a key with the specified payload and link the key into the 12428c2ecf20Sopenharmony_ci * destination keyring if one is given. 12438c2ecf20Sopenharmony_ci * 12448c2ecf20Sopenharmony_ci * The caller must have the appropriate instantiation permit set for this to 12458c2ecf20Sopenharmony_ci * work (see keyctl_assume_authority). No other permissions are required. 12468c2ecf20Sopenharmony_ci * 12478c2ecf20Sopenharmony_ci * If successful, 0 will be returned. 12488c2ecf20Sopenharmony_ci */ 12498c2ecf20Sopenharmony_cilong keyctl_instantiate_key(key_serial_t id, 12508c2ecf20Sopenharmony_ci const void __user *_payload, 12518c2ecf20Sopenharmony_ci size_t plen, 12528c2ecf20Sopenharmony_ci key_serial_t ringid) 12538c2ecf20Sopenharmony_ci{ 12548c2ecf20Sopenharmony_ci if (_payload && plen) { 12558c2ecf20Sopenharmony_ci struct iovec iov; 12568c2ecf20Sopenharmony_ci struct iov_iter from; 12578c2ecf20Sopenharmony_ci int ret; 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci ret = import_single_range(WRITE, (void __user *)_payload, plen, 12608c2ecf20Sopenharmony_ci &iov, &from); 12618c2ecf20Sopenharmony_ci if (unlikely(ret)) 12628c2ecf20Sopenharmony_ci return ret; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci return keyctl_instantiate_key_common(id, &from, ringid); 12658c2ecf20Sopenharmony_ci } 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci return keyctl_instantiate_key_common(id, NULL, ringid); 12688c2ecf20Sopenharmony_ci} 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci/* 12718c2ecf20Sopenharmony_ci * Instantiate a key with the specified multipart payload and link the key into 12728c2ecf20Sopenharmony_ci * the destination keyring if one is given. 12738c2ecf20Sopenharmony_ci * 12748c2ecf20Sopenharmony_ci * The caller must have the appropriate instantiation permit set for this to 12758c2ecf20Sopenharmony_ci * work (see keyctl_assume_authority). No other permissions are required. 12768c2ecf20Sopenharmony_ci * 12778c2ecf20Sopenharmony_ci * If successful, 0 will be returned. 12788c2ecf20Sopenharmony_ci */ 12798c2ecf20Sopenharmony_cilong keyctl_instantiate_key_iov(key_serial_t id, 12808c2ecf20Sopenharmony_ci const struct iovec __user *_payload_iov, 12818c2ecf20Sopenharmony_ci unsigned ioc, 12828c2ecf20Sopenharmony_ci key_serial_t ringid) 12838c2ecf20Sopenharmony_ci{ 12848c2ecf20Sopenharmony_ci struct iovec iovstack[UIO_FASTIOV], *iov = iovstack; 12858c2ecf20Sopenharmony_ci struct iov_iter from; 12868c2ecf20Sopenharmony_ci long ret; 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci if (!_payload_iov) 12898c2ecf20Sopenharmony_ci ioc = 0; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci ret = import_iovec(WRITE, _payload_iov, ioc, 12928c2ecf20Sopenharmony_ci ARRAY_SIZE(iovstack), &iov, &from); 12938c2ecf20Sopenharmony_ci if (ret < 0) 12948c2ecf20Sopenharmony_ci return ret; 12958c2ecf20Sopenharmony_ci ret = keyctl_instantiate_key_common(id, &from, ringid); 12968c2ecf20Sopenharmony_ci kfree(iov); 12978c2ecf20Sopenharmony_ci return ret; 12988c2ecf20Sopenharmony_ci} 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci/* 13018c2ecf20Sopenharmony_ci * Negatively instantiate the key with the given timeout (in seconds) and link 13028c2ecf20Sopenharmony_ci * the key into the destination keyring if one is given. 13038c2ecf20Sopenharmony_ci * 13048c2ecf20Sopenharmony_ci * The caller must have the appropriate instantiation permit set for this to 13058c2ecf20Sopenharmony_ci * work (see keyctl_assume_authority). No other permissions are required. 13068c2ecf20Sopenharmony_ci * 13078c2ecf20Sopenharmony_ci * The key and any links to the key will be automatically garbage collected 13088c2ecf20Sopenharmony_ci * after the timeout expires. 13098c2ecf20Sopenharmony_ci * 13108c2ecf20Sopenharmony_ci * Negative keys are used to rate limit repeated request_key() calls by causing 13118c2ecf20Sopenharmony_ci * them to return -ENOKEY until the negative key expires. 13128c2ecf20Sopenharmony_ci * 13138c2ecf20Sopenharmony_ci * If successful, 0 will be returned. 13148c2ecf20Sopenharmony_ci */ 13158c2ecf20Sopenharmony_cilong keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid) 13168c2ecf20Sopenharmony_ci{ 13178c2ecf20Sopenharmony_ci return keyctl_reject_key(id, timeout, ENOKEY, ringid); 13188c2ecf20Sopenharmony_ci} 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci/* 13218c2ecf20Sopenharmony_ci * Negatively instantiate the key with the given timeout (in seconds) and error 13228c2ecf20Sopenharmony_ci * code and link the key into the destination keyring if one is given. 13238c2ecf20Sopenharmony_ci * 13248c2ecf20Sopenharmony_ci * The caller must have the appropriate instantiation permit set for this to 13258c2ecf20Sopenharmony_ci * work (see keyctl_assume_authority). No other permissions are required. 13268c2ecf20Sopenharmony_ci * 13278c2ecf20Sopenharmony_ci * The key and any links to the key will be automatically garbage collected 13288c2ecf20Sopenharmony_ci * after the timeout expires. 13298c2ecf20Sopenharmony_ci * 13308c2ecf20Sopenharmony_ci * Negative keys are used to rate limit repeated request_key() calls by causing 13318c2ecf20Sopenharmony_ci * them to return the specified error code until the negative key expires. 13328c2ecf20Sopenharmony_ci * 13338c2ecf20Sopenharmony_ci * If successful, 0 will be returned. 13348c2ecf20Sopenharmony_ci */ 13358c2ecf20Sopenharmony_cilong keyctl_reject_key(key_serial_t id, unsigned timeout, unsigned error, 13368c2ecf20Sopenharmony_ci key_serial_t ringid) 13378c2ecf20Sopenharmony_ci{ 13388c2ecf20Sopenharmony_ci const struct cred *cred = current_cred(); 13398c2ecf20Sopenharmony_ci struct request_key_auth *rka; 13408c2ecf20Sopenharmony_ci struct key *instkey, *dest_keyring; 13418c2ecf20Sopenharmony_ci long ret; 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci kenter("%d,%u,%u,%d", id, timeout, error, ringid); 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci /* must be a valid error code and mustn't be a kernel special */ 13468c2ecf20Sopenharmony_ci if (error <= 0 || 13478c2ecf20Sopenharmony_ci error >= MAX_ERRNO || 13488c2ecf20Sopenharmony_ci error == ERESTARTSYS || 13498c2ecf20Sopenharmony_ci error == ERESTARTNOINTR || 13508c2ecf20Sopenharmony_ci error == ERESTARTNOHAND || 13518c2ecf20Sopenharmony_ci error == ERESTART_RESTARTBLOCK) 13528c2ecf20Sopenharmony_ci return -EINVAL; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci /* the appropriate instantiation authorisation key must have been 13558c2ecf20Sopenharmony_ci * assumed before calling this */ 13568c2ecf20Sopenharmony_ci ret = -EPERM; 13578c2ecf20Sopenharmony_ci instkey = cred->request_key_auth; 13588c2ecf20Sopenharmony_ci if (!instkey) 13598c2ecf20Sopenharmony_ci goto error; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci rka = instkey->payload.data[0]; 13628c2ecf20Sopenharmony_ci if (rka->target_key->serial != id) 13638c2ecf20Sopenharmony_ci goto error; 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci /* find the destination keyring if present (which must also be 13668c2ecf20Sopenharmony_ci * writable) */ 13678c2ecf20Sopenharmony_ci ret = get_instantiation_keyring(ringid, rka, &dest_keyring); 13688c2ecf20Sopenharmony_ci if (ret < 0) 13698c2ecf20Sopenharmony_ci goto error; 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci /* instantiate the key and link it into a keyring */ 13728c2ecf20Sopenharmony_ci ret = key_reject_and_link(rka->target_key, timeout, error, 13738c2ecf20Sopenharmony_ci dest_keyring, instkey); 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci key_put(dest_keyring); 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci /* discard the assumed authority if it's just been disabled by 13788c2ecf20Sopenharmony_ci * instantiation of the key */ 13798c2ecf20Sopenharmony_ci if (ret == 0) 13808c2ecf20Sopenharmony_ci keyctl_change_reqkey_auth(NULL); 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_cierror: 13838c2ecf20Sopenharmony_ci return ret; 13848c2ecf20Sopenharmony_ci} 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci/* 13878c2ecf20Sopenharmony_ci * Read or set the default keyring in which request_key() will cache keys and 13888c2ecf20Sopenharmony_ci * return the old setting. 13898c2ecf20Sopenharmony_ci * 13908c2ecf20Sopenharmony_ci * If a thread or process keyring is specified then it will be created if it 13918c2ecf20Sopenharmony_ci * doesn't yet exist. The old setting will be returned if successful. 13928c2ecf20Sopenharmony_ci */ 13938c2ecf20Sopenharmony_cilong keyctl_set_reqkey_keyring(int reqkey_defl) 13948c2ecf20Sopenharmony_ci{ 13958c2ecf20Sopenharmony_ci struct cred *new; 13968c2ecf20Sopenharmony_ci int ret, old_setting; 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci old_setting = current_cred_xxx(jit_keyring); 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci if (reqkey_defl == KEY_REQKEY_DEFL_NO_CHANGE) 14018c2ecf20Sopenharmony_ci return old_setting; 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci new = prepare_creds(); 14048c2ecf20Sopenharmony_ci if (!new) 14058c2ecf20Sopenharmony_ci return -ENOMEM; 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci switch (reqkey_defl) { 14088c2ecf20Sopenharmony_ci case KEY_REQKEY_DEFL_THREAD_KEYRING: 14098c2ecf20Sopenharmony_ci ret = install_thread_keyring_to_cred(new); 14108c2ecf20Sopenharmony_ci if (ret < 0) 14118c2ecf20Sopenharmony_ci goto error; 14128c2ecf20Sopenharmony_ci goto set; 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci case KEY_REQKEY_DEFL_PROCESS_KEYRING: 14158c2ecf20Sopenharmony_ci ret = install_process_keyring_to_cred(new); 14168c2ecf20Sopenharmony_ci if (ret < 0) 14178c2ecf20Sopenharmony_ci goto error; 14188c2ecf20Sopenharmony_ci goto set; 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci case KEY_REQKEY_DEFL_DEFAULT: 14218c2ecf20Sopenharmony_ci case KEY_REQKEY_DEFL_SESSION_KEYRING: 14228c2ecf20Sopenharmony_ci case KEY_REQKEY_DEFL_USER_KEYRING: 14238c2ecf20Sopenharmony_ci case KEY_REQKEY_DEFL_USER_SESSION_KEYRING: 14248c2ecf20Sopenharmony_ci case KEY_REQKEY_DEFL_REQUESTOR_KEYRING: 14258c2ecf20Sopenharmony_ci goto set; 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci case KEY_REQKEY_DEFL_NO_CHANGE: 14288c2ecf20Sopenharmony_ci case KEY_REQKEY_DEFL_GROUP_KEYRING: 14298c2ecf20Sopenharmony_ci default: 14308c2ecf20Sopenharmony_ci ret = -EINVAL; 14318c2ecf20Sopenharmony_ci goto error; 14328c2ecf20Sopenharmony_ci } 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ciset: 14358c2ecf20Sopenharmony_ci new->jit_keyring = reqkey_defl; 14368c2ecf20Sopenharmony_ci commit_creds(new); 14378c2ecf20Sopenharmony_ci return old_setting; 14388c2ecf20Sopenharmony_cierror: 14398c2ecf20Sopenharmony_ci abort_creds(new); 14408c2ecf20Sopenharmony_ci return ret; 14418c2ecf20Sopenharmony_ci} 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci/* 14448c2ecf20Sopenharmony_ci * Set or clear the timeout on a key. 14458c2ecf20Sopenharmony_ci * 14468c2ecf20Sopenharmony_ci * Either the key must grant the caller Setattr permission or else the caller 14478c2ecf20Sopenharmony_ci * must hold an instantiation authorisation token for the key. 14488c2ecf20Sopenharmony_ci * 14498c2ecf20Sopenharmony_ci * The timeout is either 0 to clear the timeout, or a number of seconds from 14508c2ecf20Sopenharmony_ci * the current time. The key and any links to the key will be automatically 14518c2ecf20Sopenharmony_ci * garbage collected after the timeout expires. 14528c2ecf20Sopenharmony_ci * 14538c2ecf20Sopenharmony_ci * Keys with KEY_FLAG_KEEP set should not be timed out. 14548c2ecf20Sopenharmony_ci * 14558c2ecf20Sopenharmony_ci * If successful, 0 is returned. 14568c2ecf20Sopenharmony_ci */ 14578c2ecf20Sopenharmony_cilong keyctl_set_timeout(key_serial_t id, unsigned timeout) 14588c2ecf20Sopenharmony_ci{ 14598c2ecf20Sopenharmony_ci struct key *key, *instkey; 14608c2ecf20Sopenharmony_ci key_ref_t key_ref; 14618c2ecf20Sopenharmony_ci long ret; 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL, 14648c2ecf20Sopenharmony_ci KEY_NEED_SETATTR); 14658c2ecf20Sopenharmony_ci if (IS_ERR(key_ref)) { 14668c2ecf20Sopenharmony_ci /* setting the timeout on a key under construction is permitted 14678c2ecf20Sopenharmony_ci * if we have the authorisation token handy */ 14688c2ecf20Sopenharmony_ci if (PTR_ERR(key_ref) == -EACCES) { 14698c2ecf20Sopenharmony_ci instkey = key_get_instantiation_authkey(id); 14708c2ecf20Sopenharmony_ci if (!IS_ERR(instkey)) { 14718c2ecf20Sopenharmony_ci key_put(instkey); 14728c2ecf20Sopenharmony_ci key_ref = lookup_user_key(id, 14738c2ecf20Sopenharmony_ci KEY_LOOKUP_PARTIAL, 14748c2ecf20Sopenharmony_ci KEY_AUTHTOKEN_OVERRIDE); 14758c2ecf20Sopenharmony_ci if (!IS_ERR(key_ref)) 14768c2ecf20Sopenharmony_ci goto okay; 14778c2ecf20Sopenharmony_ci } 14788c2ecf20Sopenharmony_ci } 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci ret = PTR_ERR(key_ref); 14818c2ecf20Sopenharmony_ci goto error; 14828c2ecf20Sopenharmony_ci } 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ciokay: 14858c2ecf20Sopenharmony_ci key = key_ref_to_ptr(key_ref); 14868c2ecf20Sopenharmony_ci ret = 0; 14878c2ecf20Sopenharmony_ci if (test_bit(KEY_FLAG_KEEP, &key->flags)) { 14888c2ecf20Sopenharmony_ci ret = -EPERM; 14898c2ecf20Sopenharmony_ci } else { 14908c2ecf20Sopenharmony_ci key_set_timeout(key, timeout); 14918c2ecf20Sopenharmony_ci notify_key(key, NOTIFY_KEY_SETATTR, 0); 14928c2ecf20Sopenharmony_ci } 14938c2ecf20Sopenharmony_ci key_put(key); 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_cierror: 14968c2ecf20Sopenharmony_ci return ret; 14978c2ecf20Sopenharmony_ci} 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci/* 15008c2ecf20Sopenharmony_ci * Assume (or clear) the authority to instantiate the specified key. 15018c2ecf20Sopenharmony_ci * 15028c2ecf20Sopenharmony_ci * This sets the authoritative token currently in force for key instantiation. 15038c2ecf20Sopenharmony_ci * This must be done for a key to be instantiated. It has the effect of making 15048c2ecf20Sopenharmony_ci * available all the keys from the caller of the request_key() that created a 15058c2ecf20Sopenharmony_ci * key to request_key() calls made by the caller of this function. 15068c2ecf20Sopenharmony_ci * 15078c2ecf20Sopenharmony_ci * The caller must have the instantiation key in their process keyrings with a 15088c2ecf20Sopenharmony_ci * Search permission grant available to the caller. 15098c2ecf20Sopenharmony_ci * 15108c2ecf20Sopenharmony_ci * If the ID given is 0, then the setting will be cleared and 0 returned. 15118c2ecf20Sopenharmony_ci * 15128c2ecf20Sopenharmony_ci * If the ID given has a matching an authorisation key, then that key will be 15138c2ecf20Sopenharmony_ci * set and its ID will be returned. The authorisation key can be read to get 15148c2ecf20Sopenharmony_ci * the callout information passed to request_key(). 15158c2ecf20Sopenharmony_ci */ 15168c2ecf20Sopenharmony_cilong keyctl_assume_authority(key_serial_t id) 15178c2ecf20Sopenharmony_ci{ 15188c2ecf20Sopenharmony_ci struct key *authkey; 15198c2ecf20Sopenharmony_ci long ret; 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci /* special key IDs aren't permitted */ 15228c2ecf20Sopenharmony_ci ret = -EINVAL; 15238c2ecf20Sopenharmony_ci if (id < 0) 15248c2ecf20Sopenharmony_ci goto error; 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci /* we divest ourselves of authority if given an ID of 0 */ 15278c2ecf20Sopenharmony_ci if (id == 0) { 15288c2ecf20Sopenharmony_ci ret = keyctl_change_reqkey_auth(NULL); 15298c2ecf20Sopenharmony_ci goto error; 15308c2ecf20Sopenharmony_ci } 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci /* attempt to assume the authority temporarily granted to us whilst we 15338c2ecf20Sopenharmony_ci * instantiate the specified key 15348c2ecf20Sopenharmony_ci * - the authorisation key must be in the current task's keyrings 15358c2ecf20Sopenharmony_ci * somewhere 15368c2ecf20Sopenharmony_ci */ 15378c2ecf20Sopenharmony_ci authkey = key_get_instantiation_authkey(id); 15388c2ecf20Sopenharmony_ci if (IS_ERR(authkey)) { 15398c2ecf20Sopenharmony_ci ret = PTR_ERR(authkey); 15408c2ecf20Sopenharmony_ci goto error; 15418c2ecf20Sopenharmony_ci } 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci ret = keyctl_change_reqkey_auth(authkey); 15448c2ecf20Sopenharmony_ci if (ret == 0) 15458c2ecf20Sopenharmony_ci ret = authkey->serial; 15468c2ecf20Sopenharmony_ci key_put(authkey); 15478c2ecf20Sopenharmony_cierror: 15488c2ecf20Sopenharmony_ci return ret; 15498c2ecf20Sopenharmony_ci} 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci/* 15528c2ecf20Sopenharmony_ci * Get a key's the LSM security label. 15538c2ecf20Sopenharmony_ci * 15548c2ecf20Sopenharmony_ci * The key must grant the caller View permission for this to work. 15558c2ecf20Sopenharmony_ci * 15568c2ecf20Sopenharmony_ci * If there's a buffer, then up to buflen bytes of data will be placed into it. 15578c2ecf20Sopenharmony_ci * 15588c2ecf20Sopenharmony_ci * If successful, the amount of information available will be returned, 15598c2ecf20Sopenharmony_ci * irrespective of how much was copied (including the terminal NUL). 15608c2ecf20Sopenharmony_ci */ 15618c2ecf20Sopenharmony_cilong keyctl_get_security(key_serial_t keyid, 15628c2ecf20Sopenharmony_ci char __user *buffer, 15638c2ecf20Sopenharmony_ci size_t buflen) 15648c2ecf20Sopenharmony_ci{ 15658c2ecf20Sopenharmony_ci struct key *key, *instkey; 15668c2ecf20Sopenharmony_ci key_ref_t key_ref; 15678c2ecf20Sopenharmony_ci char *context; 15688c2ecf20Sopenharmony_ci long ret; 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_NEED_VIEW); 15718c2ecf20Sopenharmony_ci if (IS_ERR(key_ref)) { 15728c2ecf20Sopenharmony_ci if (PTR_ERR(key_ref) != -EACCES) 15738c2ecf20Sopenharmony_ci return PTR_ERR(key_ref); 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci /* viewing a key under construction is also permitted if we 15768c2ecf20Sopenharmony_ci * have the authorisation token handy */ 15778c2ecf20Sopenharmony_ci instkey = key_get_instantiation_authkey(keyid); 15788c2ecf20Sopenharmony_ci if (IS_ERR(instkey)) 15798c2ecf20Sopenharmony_ci return PTR_ERR(instkey); 15808c2ecf20Sopenharmony_ci key_put(instkey); 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, 15838c2ecf20Sopenharmony_ci KEY_AUTHTOKEN_OVERRIDE); 15848c2ecf20Sopenharmony_ci if (IS_ERR(key_ref)) 15858c2ecf20Sopenharmony_ci return PTR_ERR(key_ref); 15868c2ecf20Sopenharmony_ci } 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci key = key_ref_to_ptr(key_ref); 15898c2ecf20Sopenharmony_ci ret = security_key_getsecurity(key, &context); 15908c2ecf20Sopenharmony_ci if (ret == 0) { 15918c2ecf20Sopenharmony_ci /* if no information was returned, give userspace an empty 15928c2ecf20Sopenharmony_ci * string */ 15938c2ecf20Sopenharmony_ci ret = 1; 15948c2ecf20Sopenharmony_ci if (buffer && buflen > 0 && 15958c2ecf20Sopenharmony_ci copy_to_user(buffer, "", 1) != 0) 15968c2ecf20Sopenharmony_ci ret = -EFAULT; 15978c2ecf20Sopenharmony_ci } else if (ret > 0) { 15988c2ecf20Sopenharmony_ci /* return as much data as there's room for */ 15998c2ecf20Sopenharmony_ci if (buffer && buflen > 0) { 16008c2ecf20Sopenharmony_ci if (buflen > ret) 16018c2ecf20Sopenharmony_ci buflen = ret; 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci if (copy_to_user(buffer, context, buflen) != 0) 16048c2ecf20Sopenharmony_ci ret = -EFAULT; 16058c2ecf20Sopenharmony_ci } 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci kfree(context); 16088c2ecf20Sopenharmony_ci } 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci key_ref_put(key_ref); 16118c2ecf20Sopenharmony_ci return ret; 16128c2ecf20Sopenharmony_ci} 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci/* 16158c2ecf20Sopenharmony_ci * Attempt to install the calling process's session keyring on the process's 16168c2ecf20Sopenharmony_ci * parent process. 16178c2ecf20Sopenharmony_ci * 16188c2ecf20Sopenharmony_ci * The keyring must exist and must grant the caller LINK permission, and the 16198c2ecf20Sopenharmony_ci * parent process must be single-threaded and must have the same effective 16208c2ecf20Sopenharmony_ci * ownership as this process and mustn't be SUID/SGID. 16218c2ecf20Sopenharmony_ci * 16228c2ecf20Sopenharmony_ci * The keyring will be emplaced on the parent when it next resumes userspace. 16238c2ecf20Sopenharmony_ci * 16248c2ecf20Sopenharmony_ci * If successful, 0 will be returned. 16258c2ecf20Sopenharmony_ci */ 16268c2ecf20Sopenharmony_cilong keyctl_session_to_parent(void) 16278c2ecf20Sopenharmony_ci{ 16288c2ecf20Sopenharmony_ci struct task_struct *me, *parent; 16298c2ecf20Sopenharmony_ci const struct cred *mycred, *pcred; 16308c2ecf20Sopenharmony_ci struct callback_head *newwork, *oldwork; 16318c2ecf20Sopenharmony_ci key_ref_t keyring_r; 16328c2ecf20Sopenharmony_ci struct cred *cred; 16338c2ecf20Sopenharmony_ci int ret; 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci keyring_r = lookup_user_key(KEY_SPEC_SESSION_KEYRING, 0, KEY_NEED_LINK); 16368c2ecf20Sopenharmony_ci if (IS_ERR(keyring_r)) 16378c2ecf20Sopenharmony_ci return PTR_ERR(keyring_r); 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci ret = -ENOMEM; 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci /* our parent is going to need a new cred struct, a new tgcred struct 16428c2ecf20Sopenharmony_ci * and new security data, so we allocate them here to prevent ENOMEM in 16438c2ecf20Sopenharmony_ci * our parent */ 16448c2ecf20Sopenharmony_ci cred = cred_alloc_blank(); 16458c2ecf20Sopenharmony_ci if (!cred) 16468c2ecf20Sopenharmony_ci goto error_keyring; 16478c2ecf20Sopenharmony_ci newwork = &cred->rcu; 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci cred->session_keyring = key_ref_to_ptr(keyring_r); 16508c2ecf20Sopenharmony_ci keyring_r = NULL; 16518c2ecf20Sopenharmony_ci init_task_work(newwork, key_change_session_keyring); 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_ci me = current; 16548c2ecf20Sopenharmony_ci rcu_read_lock(); 16558c2ecf20Sopenharmony_ci write_lock_irq(&tasklist_lock); 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci ret = -EPERM; 16588c2ecf20Sopenharmony_ci oldwork = NULL; 16598c2ecf20Sopenharmony_ci parent = rcu_dereference_protected(me->real_parent, 16608c2ecf20Sopenharmony_ci lockdep_is_held(&tasklist_lock)); 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci /* the parent mustn't be init and mustn't be a kernel thread */ 16638c2ecf20Sopenharmony_ci if (parent->pid <= 1 || !parent->mm) 16648c2ecf20Sopenharmony_ci goto unlock; 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci /* the parent must be single threaded */ 16678c2ecf20Sopenharmony_ci if (!thread_group_empty(parent)) 16688c2ecf20Sopenharmony_ci goto unlock; 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci /* the parent and the child must have different session keyrings or 16718c2ecf20Sopenharmony_ci * there's no point */ 16728c2ecf20Sopenharmony_ci mycred = current_cred(); 16738c2ecf20Sopenharmony_ci pcred = __task_cred(parent); 16748c2ecf20Sopenharmony_ci if (mycred == pcred || 16758c2ecf20Sopenharmony_ci mycred->session_keyring == pcred->session_keyring) { 16768c2ecf20Sopenharmony_ci ret = 0; 16778c2ecf20Sopenharmony_ci goto unlock; 16788c2ecf20Sopenharmony_ci } 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci /* the parent must have the same effective ownership and mustn't be 16818c2ecf20Sopenharmony_ci * SUID/SGID */ 16828c2ecf20Sopenharmony_ci if (!uid_eq(pcred->uid, mycred->euid) || 16838c2ecf20Sopenharmony_ci !uid_eq(pcred->euid, mycred->euid) || 16848c2ecf20Sopenharmony_ci !uid_eq(pcred->suid, mycred->euid) || 16858c2ecf20Sopenharmony_ci !gid_eq(pcred->gid, mycred->egid) || 16868c2ecf20Sopenharmony_ci !gid_eq(pcred->egid, mycred->egid) || 16878c2ecf20Sopenharmony_ci !gid_eq(pcred->sgid, mycred->egid)) 16888c2ecf20Sopenharmony_ci goto unlock; 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci /* the keyrings must have the same UID */ 16918c2ecf20Sopenharmony_ci if ((pcred->session_keyring && 16928c2ecf20Sopenharmony_ci !uid_eq(pcred->session_keyring->uid, mycred->euid)) || 16938c2ecf20Sopenharmony_ci !uid_eq(mycred->session_keyring->uid, mycred->euid)) 16948c2ecf20Sopenharmony_ci goto unlock; 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci /* cancel an already pending keyring replacement */ 16978c2ecf20Sopenharmony_ci oldwork = task_work_cancel(parent, key_change_session_keyring); 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci /* the replacement session keyring is applied just prior to userspace 17008c2ecf20Sopenharmony_ci * restarting */ 17018c2ecf20Sopenharmony_ci ret = task_work_add(parent, newwork, TWA_RESUME); 17028c2ecf20Sopenharmony_ci if (!ret) 17038c2ecf20Sopenharmony_ci newwork = NULL; 17048c2ecf20Sopenharmony_ciunlock: 17058c2ecf20Sopenharmony_ci write_unlock_irq(&tasklist_lock); 17068c2ecf20Sopenharmony_ci rcu_read_unlock(); 17078c2ecf20Sopenharmony_ci if (oldwork) 17088c2ecf20Sopenharmony_ci put_cred(container_of(oldwork, struct cred, rcu)); 17098c2ecf20Sopenharmony_ci if (newwork) 17108c2ecf20Sopenharmony_ci put_cred(cred); 17118c2ecf20Sopenharmony_ci return ret; 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_cierror_keyring: 17148c2ecf20Sopenharmony_ci key_ref_put(keyring_r); 17158c2ecf20Sopenharmony_ci return ret; 17168c2ecf20Sopenharmony_ci} 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_ci/* 17198c2ecf20Sopenharmony_ci * Apply a restriction to a given keyring. 17208c2ecf20Sopenharmony_ci * 17218c2ecf20Sopenharmony_ci * The caller must have Setattr permission to change keyring restrictions. 17228c2ecf20Sopenharmony_ci * 17238c2ecf20Sopenharmony_ci * The requested type name may be a NULL pointer to reject all attempts 17248c2ecf20Sopenharmony_ci * to link to the keyring. In this case, _restriction must also be NULL. 17258c2ecf20Sopenharmony_ci * Otherwise, both _type and _restriction must be non-NULL. 17268c2ecf20Sopenharmony_ci * 17278c2ecf20Sopenharmony_ci * Returns 0 if successful. 17288c2ecf20Sopenharmony_ci */ 17298c2ecf20Sopenharmony_cilong keyctl_restrict_keyring(key_serial_t id, const char __user *_type, 17308c2ecf20Sopenharmony_ci const char __user *_restriction) 17318c2ecf20Sopenharmony_ci{ 17328c2ecf20Sopenharmony_ci key_ref_t key_ref; 17338c2ecf20Sopenharmony_ci char type[32]; 17348c2ecf20Sopenharmony_ci char *restriction = NULL; 17358c2ecf20Sopenharmony_ci long ret; 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci key_ref = lookup_user_key(id, 0, KEY_NEED_SETATTR); 17388c2ecf20Sopenharmony_ci if (IS_ERR(key_ref)) 17398c2ecf20Sopenharmony_ci return PTR_ERR(key_ref); 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci ret = -EINVAL; 17428c2ecf20Sopenharmony_ci if (_type) { 17438c2ecf20Sopenharmony_ci if (!_restriction) 17448c2ecf20Sopenharmony_ci goto error; 17458c2ecf20Sopenharmony_ci 17468c2ecf20Sopenharmony_ci ret = key_get_type_from_user(type, _type, sizeof(type)); 17478c2ecf20Sopenharmony_ci if (ret < 0) 17488c2ecf20Sopenharmony_ci goto error; 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci restriction = strndup_user(_restriction, PAGE_SIZE); 17518c2ecf20Sopenharmony_ci if (IS_ERR(restriction)) { 17528c2ecf20Sopenharmony_ci ret = PTR_ERR(restriction); 17538c2ecf20Sopenharmony_ci goto error; 17548c2ecf20Sopenharmony_ci } 17558c2ecf20Sopenharmony_ci } else { 17568c2ecf20Sopenharmony_ci if (_restriction) 17578c2ecf20Sopenharmony_ci goto error; 17588c2ecf20Sopenharmony_ci } 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci ret = keyring_restrict(key_ref, _type ? type : NULL, restriction); 17618c2ecf20Sopenharmony_ci kfree(restriction); 17628c2ecf20Sopenharmony_cierror: 17638c2ecf20Sopenharmony_ci key_ref_put(key_ref); 17648c2ecf20Sopenharmony_ci return ret; 17658c2ecf20Sopenharmony_ci} 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci#ifdef CONFIG_KEY_NOTIFICATIONS 17688c2ecf20Sopenharmony_ci/* 17698c2ecf20Sopenharmony_ci * Watch for changes to a key. 17708c2ecf20Sopenharmony_ci * 17718c2ecf20Sopenharmony_ci * The caller must have View permission to watch a key or keyring. 17728c2ecf20Sopenharmony_ci */ 17738c2ecf20Sopenharmony_cilong keyctl_watch_key(key_serial_t id, int watch_queue_fd, int watch_id) 17748c2ecf20Sopenharmony_ci{ 17758c2ecf20Sopenharmony_ci struct watch_queue *wqueue; 17768c2ecf20Sopenharmony_ci struct watch_list *wlist = NULL; 17778c2ecf20Sopenharmony_ci struct watch *watch = NULL; 17788c2ecf20Sopenharmony_ci struct key *key; 17798c2ecf20Sopenharmony_ci key_ref_t key_ref; 17808c2ecf20Sopenharmony_ci long ret; 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_ci if (watch_id < -1 || watch_id > 0xff) 17838c2ecf20Sopenharmony_ci return -EINVAL; 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE, KEY_NEED_VIEW); 17868c2ecf20Sopenharmony_ci if (IS_ERR(key_ref)) 17878c2ecf20Sopenharmony_ci return PTR_ERR(key_ref); 17888c2ecf20Sopenharmony_ci key = key_ref_to_ptr(key_ref); 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci wqueue = get_watch_queue(watch_queue_fd); 17918c2ecf20Sopenharmony_ci if (IS_ERR(wqueue)) { 17928c2ecf20Sopenharmony_ci ret = PTR_ERR(wqueue); 17938c2ecf20Sopenharmony_ci goto err_key; 17948c2ecf20Sopenharmony_ci } 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_ci if (watch_id >= 0) { 17978c2ecf20Sopenharmony_ci ret = -ENOMEM; 17988c2ecf20Sopenharmony_ci if (!key->watchers) { 17998c2ecf20Sopenharmony_ci wlist = kzalloc(sizeof(*wlist), GFP_KERNEL); 18008c2ecf20Sopenharmony_ci if (!wlist) 18018c2ecf20Sopenharmony_ci goto err_wqueue; 18028c2ecf20Sopenharmony_ci init_watch_list(wlist, NULL); 18038c2ecf20Sopenharmony_ci } 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci watch = kzalloc(sizeof(*watch), GFP_KERNEL); 18068c2ecf20Sopenharmony_ci if (!watch) 18078c2ecf20Sopenharmony_ci goto err_wlist; 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci init_watch(watch, wqueue); 18108c2ecf20Sopenharmony_ci watch->id = key->serial; 18118c2ecf20Sopenharmony_ci watch->info_id = (u32)watch_id << WATCH_INFO_ID__SHIFT; 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci ret = security_watch_key(key); 18148c2ecf20Sopenharmony_ci if (ret < 0) 18158c2ecf20Sopenharmony_ci goto err_watch; 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_ci down_write(&key->sem); 18188c2ecf20Sopenharmony_ci if (!key->watchers) { 18198c2ecf20Sopenharmony_ci key->watchers = wlist; 18208c2ecf20Sopenharmony_ci wlist = NULL; 18218c2ecf20Sopenharmony_ci } 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_ci ret = add_watch_to_object(watch, key->watchers); 18248c2ecf20Sopenharmony_ci up_write(&key->sem); 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_ci if (ret == 0) 18278c2ecf20Sopenharmony_ci watch = NULL; 18288c2ecf20Sopenharmony_ci } else { 18298c2ecf20Sopenharmony_ci ret = -EBADSLT; 18308c2ecf20Sopenharmony_ci if (key->watchers) { 18318c2ecf20Sopenharmony_ci down_write(&key->sem); 18328c2ecf20Sopenharmony_ci ret = remove_watch_from_object(key->watchers, 18338c2ecf20Sopenharmony_ci wqueue, key_serial(key), 18348c2ecf20Sopenharmony_ci false); 18358c2ecf20Sopenharmony_ci up_write(&key->sem); 18368c2ecf20Sopenharmony_ci } 18378c2ecf20Sopenharmony_ci } 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_cierr_watch: 18408c2ecf20Sopenharmony_ci kfree(watch); 18418c2ecf20Sopenharmony_cierr_wlist: 18428c2ecf20Sopenharmony_ci kfree(wlist); 18438c2ecf20Sopenharmony_cierr_wqueue: 18448c2ecf20Sopenharmony_ci put_watch_queue(wqueue); 18458c2ecf20Sopenharmony_cierr_key: 18468c2ecf20Sopenharmony_ci key_put(key); 18478c2ecf20Sopenharmony_ci return ret; 18488c2ecf20Sopenharmony_ci} 18498c2ecf20Sopenharmony_ci#endif /* CONFIG_KEY_NOTIFICATIONS */ 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci/* 18528c2ecf20Sopenharmony_ci * Get keyrings subsystem capabilities. 18538c2ecf20Sopenharmony_ci */ 18548c2ecf20Sopenharmony_cilong keyctl_capabilities(unsigned char __user *_buffer, size_t buflen) 18558c2ecf20Sopenharmony_ci{ 18568c2ecf20Sopenharmony_ci size_t size = buflen; 18578c2ecf20Sopenharmony_ci 18588c2ecf20Sopenharmony_ci if (size > 0) { 18598c2ecf20Sopenharmony_ci if (size > sizeof(keyrings_capabilities)) 18608c2ecf20Sopenharmony_ci size = sizeof(keyrings_capabilities); 18618c2ecf20Sopenharmony_ci if (copy_to_user(_buffer, keyrings_capabilities, size) != 0) 18628c2ecf20Sopenharmony_ci return -EFAULT; 18638c2ecf20Sopenharmony_ci if (size < buflen && 18648c2ecf20Sopenharmony_ci clear_user(_buffer + size, buflen - size) != 0) 18658c2ecf20Sopenharmony_ci return -EFAULT; 18668c2ecf20Sopenharmony_ci } 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci return sizeof(keyrings_capabilities); 18698c2ecf20Sopenharmony_ci} 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_ci/* 18728c2ecf20Sopenharmony_ci * The key control system call 18738c2ecf20Sopenharmony_ci */ 18748c2ecf20Sopenharmony_ciSYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, 18758c2ecf20Sopenharmony_ci unsigned long, arg4, unsigned long, arg5) 18768c2ecf20Sopenharmony_ci{ 18778c2ecf20Sopenharmony_ci switch (option) { 18788c2ecf20Sopenharmony_ci case KEYCTL_GET_KEYRING_ID: 18798c2ecf20Sopenharmony_ci return keyctl_get_keyring_ID((key_serial_t) arg2, 18808c2ecf20Sopenharmony_ci (int) arg3); 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci case KEYCTL_JOIN_SESSION_KEYRING: 18838c2ecf20Sopenharmony_ci return keyctl_join_session_keyring((const char __user *) arg2); 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci case KEYCTL_UPDATE: 18868c2ecf20Sopenharmony_ci return keyctl_update_key((key_serial_t) arg2, 18878c2ecf20Sopenharmony_ci (const void __user *) arg3, 18888c2ecf20Sopenharmony_ci (size_t) arg4); 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_ci case KEYCTL_REVOKE: 18918c2ecf20Sopenharmony_ci return keyctl_revoke_key((key_serial_t) arg2); 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci case KEYCTL_DESCRIBE: 18948c2ecf20Sopenharmony_ci return keyctl_describe_key((key_serial_t) arg2, 18958c2ecf20Sopenharmony_ci (char __user *) arg3, 18968c2ecf20Sopenharmony_ci (unsigned) arg4); 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci case KEYCTL_CLEAR: 18998c2ecf20Sopenharmony_ci return keyctl_keyring_clear((key_serial_t) arg2); 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci case KEYCTL_LINK: 19028c2ecf20Sopenharmony_ci return keyctl_keyring_link((key_serial_t) arg2, 19038c2ecf20Sopenharmony_ci (key_serial_t) arg3); 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci case KEYCTL_UNLINK: 19068c2ecf20Sopenharmony_ci return keyctl_keyring_unlink((key_serial_t) arg2, 19078c2ecf20Sopenharmony_ci (key_serial_t) arg3); 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_ci case KEYCTL_SEARCH: 19108c2ecf20Sopenharmony_ci return keyctl_keyring_search((key_serial_t) arg2, 19118c2ecf20Sopenharmony_ci (const char __user *) arg3, 19128c2ecf20Sopenharmony_ci (const char __user *) arg4, 19138c2ecf20Sopenharmony_ci (key_serial_t) arg5); 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci case KEYCTL_READ: 19168c2ecf20Sopenharmony_ci return keyctl_read_key((key_serial_t) arg2, 19178c2ecf20Sopenharmony_ci (char __user *) arg3, 19188c2ecf20Sopenharmony_ci (size_t) arg4); 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_ci case KEYCTL_CHOWN: 19218c2ecf20Sopenharmony_ci return keyctl_chown_key((key_serial_t) arg2, 19228c2ecf20Sopenharmony_ci (uid_t) arg3, 19238c2ecf20Sopenharmony_ci (gid_t) arg4); 19248c2ecf20Sopenharmony_ci 19258c2ecf20Sopenharmony_ci case KEYCTL_SETPERM: 19268c2ecf20Sopenharmony_ci return keyctl_setperm_key((key_serial_t) arg2, 19278c2ecf20Sopenharmony_ci (key_perm_t) arg3); 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ci case KEYCTL_INSTANTIATE: 19308c2ecf20Sopenharmony_ci return keyctl_instantiate_key((key_serial_t) arg2, 19318c2ecf20Sopenharmony_ci (const void __user *) arg3, 19328c2ecf20Sopenharmony_ci (size_t) arg4, 19338c2ecf20Sopenharmony_ci (key_serial_t) arg5); 19348c2ecf20Sopenharmony_ci 19358c2ecf20Sopenharmony_ci case KEYCTL_NEGATE: 19368c2ecf20Sopenharmony_ci return keyctl_negate_key((key_serial_t) arg2, 19378c2ecf20Sopenharmony_ci (unsigned) arg3, 19388c2ecf20Sopenharmony_ci (key_serial_t) arg4); 19398c2ecf20Sopenharmony_ci 19408c2ecf20Sopenharmony_ci case KEYCTL_SET_REQKEY_KEYRING: 19418c2ecf20Sopenharmony_ci return keyctl_set_reqkey_keyring(arg2); 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci case KEYCTL_SET_TIMEOUT: 19448c2ecf20Sopenharmony_ci return keyctl_set_timeout((key_serial_t) arg2, 19458c2ecf20Sopenharmony_ci (unsigned) arg3); 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ci case KEYCTL_ASSUME_AUTHORITY: 19488c2ecf20Sopenharmony_ci return keyctl_assume_authority((key_serial_t) arg2); 19498c2ecf20Sopenharmony_ci 19508c2ecf20Sopenharmony_ci case KEYCTL_GET_SECURITY: 19518c2ecf20Sopenharmony_ci return keyctl_get_security((key_serial_t) arg2, 19528c2ecf20Sopenharmony_ci (char __user *) arg3, 19538c2ecf20Sopenharmony_ci (size_t) arg4); 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci case KEYCTL_SESSION_TO_PARENT: 19568c2ecf20Sopenharmony_ci return keyctl_session_to_parent(); 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci case KEYCTL_REJECT: 19598c2ecf20Sopenharmony_ci return keyctl_reject_key((key_serial_t) arg2, 19608c2ecf20Sopenharmony_ci (unsigned) arg3, 19618c2ecf20Sopenharmony_ci (unsigned) arg4, 19628c2ecf20Sopenharmony_ci (key_serial_t) arg5); 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_ci case KEYCTL_INSTANTIATE_IOV: 19658c2ecf20Sopenharmony_ci return keyctl_instantiate_key_iov( 19668c2ecf20Sopenharmony_ci (key_serial_t) arg2, 19678c2ecf20Sopenharmony_ci (const struct iovec __user *) arg3, 19688c2ecf20Sopenharmony_ci (unsigned) arg4, 19698c2ecf20Sopenharmony_ci (key_serial_t) arg5); 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci case KEYCTL_INVALIDATE: 19728c2ecf20Sopenharmony_ci return keyctl_invalidate_key((key_serial_t) arg2); 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci case KEYCTL_GET_PERSISTENT: 19758c2ecf20Sopenharmony_ci return keyctl_get_persistent((uid_t)arg2, (key_serial_t)arg3); 19768c2ecf20Sopenharmony_ci 19778c2ecf20Sopenharmony_ci case KEYCTL_DH_COMPUTE: 19788c2ecf20Sopenharmony_ci return keyctl_dh_compute((struct keyctl_dh_params __user *) arg2, 19798c2ecf20Sopenharmony_ci (char __user *) arg3, (size_t) arg4, 19808c2ecf20Sopenharmony_ci (struct keyctl_kdf_params __user *) arg5); 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci case KEYCTL_RESTRICT_KEYRING: 19838c2ecf20Sopenharmony_ci return keyctl_restrict_keyring((key_serial_t) arg2, 19848c2ecf20Sopenharmony_ci (const char __user *) arg3, 19858c2ecf20Sopenharmony_ci (const char __user *) arg4); 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci case KEYCTL_PKEY_QUERY: 19888c2ecf20Sopenharmony_ci if (arg3 != 0) 19898c2ecf20Sopenharmony_ci return -EINVAL; 19908c2ecf20Sopenharmony_ci return keyctl_pkey_query((key_serial_t)arg2, 19918c2ecf20Sopenharmony_ci (const char __user *)arg4, 19928c2ecf20Sopenharmony_ci (struct keyctl_pkey_query __user *)arg5); 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_ci case KEYCTL_PKEY_ENCRYPT: 19958c2ecf20Sopenharmony_ci case KEYCTL_PKEY_DECRYPT: 19968c2ecf20Sopenharmony_ci case KEYCTL_PKEY_SIGN: 19978c2ecf20Sopenharmony_ci return keyctl_pkey_e_d_s( 19988c2ecf20Sopenharmony_ci option, 19998c2ecf20Sopenharmony_ci (const struct keyctl_pkey_params __user *)arg2, 20008c2ecf20Sopenharmony_ci (const char __user *)arg3, 20018c2ecf20Sopenharmony_ci (const void __user *)arg4, 20028c2ecf20Sopenharmony_ci (void __user *)arg5); 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ci case KEYCTL_PKEY_VERIFY: 20058c2ecf20Sopenharmony_ci return keyctl_pkey_verify( 20068c2ecf20Sopenharmony_ci (const struct keyctl_pkey_params __user *)arg2, 20078c2ecf20Sopenharmony_ci (const char __user *)arg3, 20088c2ecf20Sopenharmony_ci (const void __user *)arg4, 20098c2ecf20Sopenharmony_ci (const void __user *)arg5); 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_ci case KEYCTL_MOVE: 20128c2ecf20Sopenharmony_ci return keyctl_keyring_move((key_serial_t)arg2, 20138c2ecf20Sopenharmony_ci (key_serial_t)arg3, 20148c2ecf20Sopenharmony_ci (key_serial_t)arg4, 20158c2ecf20Sopenharmony_ci (unsigned int)arg5); 20168c2ecf20Sopenharmony_ci 20178c2ecf20Sopenharmony_ci case KEYCTL_CAPABILITIES: 20188c2ecf20Sopenharmony_ci return keyctl_capabilities((unsigned char __user *)arg2, (size_t)arg3); 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci case KEYCTL_WATCH_KEY: 20218c2ecf20Sopenharmony_ci return keyctl_watch_key((key_serial_t)arg2, (int)arg3, (int)arg4); 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci default: 20248c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 20258c2ecf20Sopenharmony_ci } 20268c2ecf20Sopenharmony_ci} 2027