18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* procfs files for key database enumeration 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2004 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/fs.h> 118c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 128c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 138c2ecf20Sopenharmony_ci#include <asm/errno.h> 148c2ecf20Sopenharmony_ci#include "internal.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic void *proc_keys_start(struct seq_file *p, loff_t *_pos); 178c2ecf20Sopenharmony_cistatic void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos); 188c2ecf20Sopenharmony_cistatic void proc_keys_stop(struct seq_file *p, void *v); 198c2ecf20Sopenharmony_cistatic int proc_keys_show(struct seq_file *m, void *v); 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic const struct seq_operations proc_keys_ops = { 228c2ecf20Sopenharmony_ci .start = proc_keys_start, 238c2ecf20Sopenharmony_ci .next = proc_keys_next, 248c2ecf20Sopenharmony_ci .stop = proc_keys_stop, 258c2ecf20Sopenharmony_ci .show = proc_keys_show, 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic void *proc_key_users_start(struct seq_file *p, loff_t *_pos); 298c2ecf20Sopenharmony_cistatic void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos); 308c2ecf20Sopenharmony_cistatic void proc_key_users_stop(struct seq_file *p, void *v); 318c2ecf20Sopenharmony_cistatic int proc_key_users_show(struct seq_file *m, void *v); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic const struct seq_operations proc_key_users_ops = { 348c2ecf20Sopenharmony_ci .start = proc_key_users_start, 358c2ecf20Sopenharmony_ci .next = proc_key_users_next, 368c2ecf20Sopenharmony_ci .stop = proc_key_users_stop, 378c2ecf20Sopenharmony_ci .show = proc_key_users_show, 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* 418c2ecf20Sopenharmony_ci * Declare the /proc files. 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_cistatic int __init key_proc_init(void) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct proc_dir_entry *p; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci p = proc_create_seq("keys", 0, NULL, &proc_keys_ops); 488c2ecf20Sopenharmony_ci if (!p) 498c2ecf20Sopenharmony_ci panic("Cannot create /proc/keys\n"); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci p = proc_create_seq("key-users", 0, NULL, &proc_key_users_ops); 528c2ecf20Sopenharmony_ci if (!p) 538c2ecf20Sopenharmony_ci panic("Cannot create /proc/key-users\n"); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci return 0; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci__initcall(key_proc_init); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* 618c2ecf20Sopenharmony_ci * Implement "/proc/keys" to provide a list of the keys on the system that 628c2ecf20Sopenharmony_ci * grant View permission to the caller. 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_cistatic struct rb_node *key_serial_next(struct seq_file *p, struct rb_node *n) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct user_namespace *user_ns = seq_user_ns(p); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci n = rb_next(n); 698c2ecf20Sopenharmony_ci while (n) { 708c2ecf20Sopenharmony_ci struct key *key = rb_entry(n, struct key, serial_node); 718c2ecf20Sopenharmony_ci if (kuid_has_mapping(user_ns, key->user->uid)) 728c2ecf20Sopenharmony_ci break; 738c2ecf20Sopenharmony_ci n = rb_next(n); 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci return n; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic struct key *find_ge_key(struct seq_file *p, key_serial_t id) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct user_namespace *user_ns = seq_user_ns(p); 818c2ecf20Sopenharmony_ci struct rb_node *n = key_serial_tree.rb_node; 828c2ecf20Sopenharmony_ci struct key *minkey = NULL; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci while (n) { 858c2ecf20Sopenharmony_ci struct key *key = rb_entry(n, struct key, serial_node); 868c2ecf20Sopenharmony_ci if (id < key->serial) { 878c2ecf20Sopenharmony_ci if (!minkey || minkey->serial > key->serial) 888c2ecf20Sopenharmony_ci minkey = key; 898c2ecf20Sopenharmony_ci n = n->rb_left; 908c2ecf20Sopenharmony_ci } else if (id > key->serial) { 918c2ecf20Sopenharmony_ci n = n->rb_right; 928c2ecf20Sopenharmony_ci } else { 938c2ecf20Sopenharmony_ci minkey = key; 948c2ecf20Sopenharmony_ci break; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci key = NULL; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (!minkey) 1008c2ecf20Sopenharmony_ci return NULL; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci for (;;) { 1038c2ecf20Sopenharmony_ci if (kuid_has_mapping(user_ns, minkey->user->uid)) 1048c2ecf20Sopenharmony_ci return minkey; 1058c2ecf20Sopenharmony_ci n = rb_next(&minkey->serial_node); 1068c2ecf20Sopenharmony_ci if (!n) 1078c2ecf20Sopenharmony_ci return NULL; 1088c2ecf20Sopenharmony_ci minkey = rb_entry(n, struct key, serial_node); 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic void *proc_keys_start(struct seq_file *p, loff_t *_pos) 1138c2ecf20Sopenharmony_ci __acquires(key_serial_lock) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci key_serial_t pos = *_pos; 1168c2ecf20Sopenharmony_ci struct key *key; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci spin_lock(&key_serial_lock); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (*_pos > INT_MAX) 1218c2ecf20Sopenharmony_ci return NULL; 1228c2ecf20Sopenharmony_ci key = find_ge_key(p, pos); 1238c2ecf20Sopenharmony_ci if (!key) 1248c2ecf20Sopenharmony_ci return NULL; 1258c2ecf20Sopenharmony_ci *_pos = key->serial; 1268c2ecf20Sopenharmony_ci return &key->serial_node; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic inline key_serial_t key_node_serial(struct rb_node *n) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct key *key = rb_entry(n, struct key, serial_node); 1328c2ecf20Sopenharmony_ci return key->serial; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct rb_node *n; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci n = key_serial_next(p, v); 1408c2ecf20Sopenharmony_ci if (n) 1418c2ecf20Sopenharmony_ci *_pos = key_node_serial(n); 1428c2ecf20Sopenharmony_ci else 1438c2ecf20Sopenharmony_ci (*_pos)++; 1448c2ecf20Sopenharmony_ci return n; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic void proc_keys_stop(struct seq_file *p, void *v) 1488c2ecf20Sopenharmony_ci __releases(key_serial_lock) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci spin_unlock(&key_serial_lock); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic int proc_keys_show(struct seq_file *m, void *v) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct rb_node *_p = v; 1568c2ecf20Sopenharmony_ci struct key *key = rb_entry(_p, struct key, serial_node); 1578c2ecf20Sopenharmony_ci unsigned long flags; 1588c2ecf20Sopenharmony_ci key_ref_t key_ref, skey_ref; 1598c2ecf20Sopenharmony_ci time64_t now, expiry; 1608c2ecf20Sopenharmony_ci char xbuf[16]; 1618c2ecf20Sopenharmony_ci short state; 1628c2ecf20Sopenharmony_ci u64 timo; 1638c2ecf20Sopenharmony_ci int rc; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci struct keyring_search_context ctx = { 1668c2ecf20Sopenharmony_ci .index_key = key->index_key, 1678c2ecf20Sopenharmony_ci .cred = m->file->f_cred, 1688c2ecf20Sopenharmony_ci .match_data.cmp = lookup_user_key_possessed, 1698c2ecf20Sopenharmony_ci .match_data.raw_data = key, 1708c2ecf20Sopenharmony_ci .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, 1718c2ecf20Sopenharmony_ci .flags = (KEYRING_SEARCH_NO_STATE_CHECK | 1728c2ecf20Sopenharmony_ci KEYRING_SEARCH_RECURSE), 1738c2ecf20Sopenharmony_ci }; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci key_ref = make_key_ref(key, 0); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci /* determine if the key is possessed by this process (a test we can 1788c2ecf20Sopenharmony_ci * skip if the key does not indicate the possessor can view it 1798c2ecf20Sopenharmony_ci */ 1808c2ecf20Sopenharmony_ci if (key->perm & KEY_POS_VIEW) { 1818c2ecf20Sopenharmony_ci rcu_read_lock(); 1828c2ecf20Sopenharmony_ci skey_ref = search_cred_keyrings_rcu(&ctx); 1838c2ecf20Sopenharmony_ci rcu_read_unlock(); 1848c2ecf20Sopenharmony_ci if (!IS_ERR(skey_ref)) { 1858c2ecf20Sopenharmony_ci key_ref_put(skey_ref); 1868c2ecf20Sopenharmony_ci key_ref = make_key_ref(key, 1); 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* check whether the current task is allowed to view the key */ 1918c2ecf20Sopenharmony_ci rc = key_task_permission(key_ref, ctx.cred, KEY_NEED_VIEW); 1928c2ecf20Sopenharmony_ci if (rc < 0) 1938c2ecf20Sopenharmony_ci return 0; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci now = ktime_get_real_seconds(); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci rcu_read_lock(); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* come up with a suitable timeout value */ 2008c2ecf20Sopenharmony_ci expiry = READ_ONCE(key->expiry); 2018c2ecf20Sopenharmony_ci if (expiry == TIME64_MAX) { 2028c2ecf20Sopenharmony_ci memcpy(xbuf, "perm", 5); 2038c2ecf20Sopenharmony_ci } else if (now >= expiry) { 2048c2ecf20Sopenharmony_ci memcpy(xbuf, "expd", 5); 2058c2ecf20Sopenharmony_ci } else { 2068c2ecf20Sopenharmony_ci timo = expiry - now; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (timo < 60) 2098c2ecf20Sopenharmony_ci sprintf(xbuf, "%llus", timo); 2108c2ecf20Sopenharmony_ci else if (timo < 60*60) 2118c2ecf20Sopenharmony_ci sprintf(xbuf, "%llum", div_u64(timo, 60)); 2128c2ecf20Sopenharmony_ci else if (timo < 60*60*24) 2138c2ecf20Sopenharmony_ci sprintf(xbuf, "%lluh", div_u64(timo, 60 * 60)); 2148c2ecf20Sopenharmony_ci else if (timo < 60*60*24*7) 2158c2ecf20Sopenharmony_ci sprintf(xbuf, "%llud", div_u64(timo, 60 * 60 * 24)); 2168c2ecf20Sopenharmony_ci else 2178c2ecf20Sopenharmony_ci sprintf(xbuf, "%lluw", div_u64(timo, 60 * 60 * 24 * 7)); 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci state = key_read_state(key); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci#define showflag(FLAGS, LETTER, FLAG) \ 2238c2ecf20Sopenharmony_ci ((FLAGS & (1 << FLAG)) ? LETTER : '-') 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci flags = READ_ONCE(key->flags); 2268c2ecf20Sopenharmony_ci seq_printf(m, "%08x %c%c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ", 2278c2ecf20Sopenharmony_ci key->serial, 2288c2ecf20Sopenharmony_ci state != KEY_IS_UNINSTANTIATED ? 'I' : '-', 2298c2ecf20Sopenharmony_ci showflag(flags, 'R', KEY_FLAG_REVOKED), 2308c2ecf20Sopenharmony_ci showflag(flags, 'D', KEY_FLAG_DEAD), 2318c2ecf20Sopenharmony_ci showflag(flags, 'Q', KEY_FLAG_IN_QUOTA), 2328c2ecf20Sopenharmony_ci showflag(flags, 'U', KEY_FLAG_USER_CONSTRUCT), 2338c2ecf20Sopenharmony_ci state < 0 ? 'N' : '-', 2348c2ecf20Sopenharmony_ci showflag(flags, 'i', KEY_FLAG_INVALIDATED), 2358c2ecf20Sopenharmony_ci refcount_read(&key->usage), 2368c2ecf20Sopenharmony_ci xbuf, 2378c2ecf20Sopenharmony_ci key->perm, 2388c2ecf20Sopenharmony_ci from_kuid_munged(seq_user_ns(m), key->uid), 2398c2ecf20Sopenharmony_ci from_kgid_munged(seq_user_ns(m), key->gid), 2408c2ecf20Sopenharmony_ci key->type->name); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci#undef showflag 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (key->type->describe) 2458c2ecf20Sopenharmony_ci key->type->describe(key, m); 2468c2ecf20Sopenharmony_ci seq_putc(m, '\n'); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci rcu_read_unlock(); 2498c2ecf20Sopenharmony_ci return 0; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic struct rb_node *__key_user_next(struct user_namespace *user_ns, struct rb_node *n) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci while (n) { 2558c2ecf20Sopenharmony_ci struct key_user *user = rb_entry(n, struct key_user, node); 2568c2ecf20Sopenharmony_ci if (kuid_has_mapping(user_ns, user->uid)) 2578c2ecf20Sopenharmony_ci break; 2588c2ecf20Sopenharmony_ci n = rb_next(n); 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci return n; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic struct rb_node *key_user_next(struct user_namespace *user_ns, struct rb_node *n) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci return __key_user_next(user_ns, rb_next(n)); 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic struct rb_node *key_user_first(struct user_namespace *user_ns, struct rb_root *r) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct rb_node *n = rb_first(r); 2718c2ecf20Sopenharmony_ci return __key_user_next(user_ns, n); 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic void *proc_key_users_start(struct seq_file *p, loff_t *_pos) 2758c2ecf20Sopenharmony_ci __acquires(key_user_lock) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct rb_node *_p; 2788c2ecf20Sopenharmony_ci loff_t pos = *_pos; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci spin_lock(&key_user_lock); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci _p = key_user_first(seq_user_ns(p), &key_user_tree); 2838c2ecf20Sopenharmony_ci while (pos > 0 && _p) { 2848c2ecf20Sopenharmony_ci pos--; 2858c2ecf20Sopenharmony_ci _p = key_user_next(seq_user_ns(p), _p); 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci return _p; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci (*_pos)++; 2948c2ecf20Sopenharmony_ci return key_user_next(seq_user_ns(p), (struct rb_node *)v); 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic void proc_key_users_stop(struct seq_file *p, void *v) 2988c2ecf20Sopenharmony_ci __releases(key_user_lock) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci spin_unlock(&key_user_lock); 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic int proc_key_users_show(struct seq_file *m, void *v) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct rb_node *_p = v; 3068c2ecf20Sopenharmony_ci struct key_user *user = rb_entry(_p, struct key_user, node); 3078c2ecf20Sopenharmony_ci unsigned maxkeys = uid_eq(user->uid, GLOBAL_ROOT_UID) ? 3088c2ecf20Sopenharmony_ci key_quota_root_maxkeys : key_quota_maxkeys; 3098c2ecf20Sopenharmony_ci unsigned maxbytes = uid_eq(user->uid, GLOBAL_ROOT_UID) ? 3108c2ecf20Sopenharmony_ci key_quota_root_maxbytes : key_quota_maxbytes; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci seq_printf(m, "%5u: %5d %d/%d %d/%d %d/%d\n", 3138c2ecf20Sopenharmony_ci from_kuid_munged(seq_user_ns(m), user->uid), 3148c2ecf20Sopenharmony_ci refcount_read(&user->usage), 3158c2ecf20Sopenharmony_ci atomic_read(&user->nkeys), 3168c2ecf20Sopenharmony_ci atomic_read(&user->nikeys), 3178c2ecf20Sopenharmony_ci user->qnkeys, 3188c2ecf20Sopenharmony_ci maxkeys, 3198c2ecf20Sopenharmony_ci user->qnbytes, 3208c2ecf20Sopenharmony_ci maxbytes); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci return 0; 3238c2ecf20Sopenharmony_ci} 324