1*2874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2973c9f4fSDavid Howells /* procfs files for key database enumeration 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. 51da177e4SLinus Torvalds * Written by David Howells (dhowells@redhat.com) 61da177e4SLinus Torvalds */ 71da177e4SLinus Torvalds 81da177e4SLinus Torvalds #include <linux/init.h> 91da177e4SLinus Torvalds #include <linux/sched.h> 101da177e4SLinus Torvalds #include <linux/fs.h> 111da177e4SLinus Torvalds #include <linux/proc_fs.h> 121da177e4SLinus Torvalds #include <linux/seq_file.h> 131da177e4SLinus Torvalds #include <asm/errno.h> 141da177e4SLinus Torvalds #include "internal.h" 151da177e4SLinus Torvalds 161da177e4SLinus Torvalds static void *proc_keys_start(struct seq_file *p, loff_t *_pos); 171da177e4SLinus Torvalds static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos); 181da177e4SLinus Torvalds static void proc_keys_stop(struct seq_file *p, void *v); 191da177e4SLinus Torvalds static int proc_keys_show(struct seq_file *m, void *v); 201da177e4SLinus Torvalds 211996a109SJan Engelhardt static const struct seq_operations proc_keys_ops = { 221da177e4SLinus Torvalds .start = proc_keys_start, 231da177e4SLinus Torvalds .next = proc_keys_next, 241da177e4SLinus Torvalds .stop = proc_keys_stop, 251da177e4SLinus Torvalds .show = proc_keys_show, 261da177e4SLinus Torvalds }; 271da177e4SLinus Torvalds 281da177e4SLinus Torvalds static void *proc_key_users_start(struct seq_file *p, loff_t *_pos); 291da177e4SLinus Torvalds static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos); 301da177e4SLinus Torvalds static void proc_key_users_stop(struct seq_file *p, void *v); 311da177e4SLinus Torvalds static int proc_key_users_show(struct seq_file *m, void *v); 321da177e4SLinus Torvalds 331996a109SJan Engelhardt static const struct seq_operations proc_key_users_ops = { 341da177e4SLinus Torvalds .start = proc_key_users_start, 351da177e4SLinus Torvalds .next = proc_key_users_next, 361da177e4SLinus Torvalds .stop = proc_key_users_stop, 371da177e4SLinus Torvalds .show = proc_key_users_show, 381da177e4SLinus Torvalds }; 391da177e4SLinus Torvalds 401da177e4SLinus Torvalds /* 41973c9f4fSDavid Howells * Declare the /proc files. 421da177e4SLinus Torvalds */ 431da177e4SLinus Torvalds static int __init key_proc_init(void) 441da177e4SLinus Torvalds { 451da177e4SLinus Torvalds struct proc_dir_entry *p; 461da177e4SLinus Torvalds 47fddda2b7SChristoph Hellwig p = proc_create_seq("keys", 0, NULL, &proc_keys_ops); 481da177e4SLinus Torvalds if (!p) 491da177e4SLinus Torvalds panic("Cannot create /proc/keys\n"); 501da177e4SLinus Torvalds 51fddda2b7SChristoph Hellwig p = proc_create_seq("key-users", 0, NULL, &proc_key_users_ops); 521da177e4SLinus Torvalds if (!p) 531da177e4SLinus Torvalds panic("Cannot create /proc/key-users\n"); 541da177e4SLinus Torvalds 551da177e4SLinus Torvalds return 0; 56a8b17ed0SDavid Howells } 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds __initcall(key_proc_init); 591da177e4SLinus Torvalds 601da177e4SLinus Torvalds /* 61973c9f4fSDavid Howells * Implement "/proc/keys" to provide a list of the keys on the system that 62973c9f4fSDavid Howells * grant View permission to the caller. 631da177e4SLinus Torvalds */ 649a56c2dbSEric W. Biederman static struct rb_node *key_serial_next(struct seq_file *p, struct rb_node *n) 65454804abSSerge E. Hallyn { 669a56c2dbSEric W. Biederman struct user_namespace *user_ns = seq_user_ns(p); 67ad73a717SSerge E. Hallyn 68ad73a717SSerge E. Hallyn n = rb_next(n); 69454804abSSerge E. Hallyn while (n) { 70454804abSSerge E. Hallyn struct key *key = rb_entry(n, struct key, serial_node); 719a56c2dbSEric W. Biederman if (kuid_has_mapping(user_ns, key->user->uid)) 72454804abSSerge E. Hallyn break; 73454804abSSerge E. Hallyn n = rb_next(n); 74454804abSSerge E. Hallyn } 75454804abSSerge E. Hallyn return n; 76454804abSSerge E. Hallyn } 77454804abSSerge E. Hallyn 789a56c2dbSEric W. Biederman static struct key *find_ge_key(struct seq_file *p, key_serial_t id) 79ad73a717SSerge E. Hallyn { 809a56c2dbSEric W. Biederman struct user_namespace *user_ns = seq_user_ns(p); 81ad73a717SSerge E. Hallyn struct rb_node *n = key_serial_tree.rb_node; 82ad73a717SSerge E. Hallyn struct key *minkey = NULL; 83ad73a717SSerge E. Hallyn 84ad73a717SSerge E. Hallyn while (n) { 85ad73a717SSerge E. Hallyn struct key *key = rb_entry(n, struct key, serial_node); 86ad73a717SSerge E. Hallyn if (id < key->serial) { 87ad73a717SSerge E. Hallyn if (!minkey || minkey->serial > key->serial) 88ad73a717SSerge E. Hallyn minkey = key; 89ad73a717SSerge E. Hallyn n = n->rb_left; 90ad73a717SSerge E. Hallyn } else if (id > key->serial) { 91ad73a717SSerge E. Hallyn n = n->rb_right; 92ad73a717SSerge E. Hallyn } else { 93ad73a717SSerge E. Hallyn minkey = key; 94ad73a717SSerge E. Hallyn break; 95ad73a717SSerge E. Hallyn } 96ad73a717SSerge E. Hallyn key = NULL; 97ad73a717SSerge E. Hallyn } 98ad73a717SSerge E. Hallyn 99ad73a717SSerge E. Hallyn if (!minkey) 100ad73a717SSerge E. Hallyn return NULL; 101ad73a717SSerge E. Hallyn 102ad73a717SSerge E. Hallyn for (;;) { 1039a56c2dbSEric W. Biederman if (kuid_has_mapping(user_ns, minkey->user->uid)) 104ad73a717SSerge E. Hallyn return minkey; 105ad73a717SSerge E. Hallyn n = rb_next(&minkey->serial_node); 106ad73a717SSerge E. Hallyn if (!n) 107ad73a717SSerge E. Hallyn return NULL; 108ad73a717SSerge E. Hallyn minkey = rb_entry(n, struct key, serial_node); 109ad73a717SSerge E. Hallyn } 1101da177e4SLinus Torvalds } 1111da177e4SLinus Torvalds 1121da177e4SLinus Torvalds static void *proc_keys_start(struct seq_file *p, loff_t *_pos) 11386abcf9cSJames Morris __acquires(key_serial_lock) 1141da177e4SLinus Torvalds { 115ad73a717SSerge E. Hallyn key_serial_t pos = *_pos; 116ad73a717SSerge E. Hallyn struct key *key; 1171da177e4SLinus Torvalds 1181da177e4SLinus Torvalds spin_lock(&key_serial_lock); 1191da177e4SLinus Torvalds 120ad73a717SSerge E. Hallyn if (*_pos > INT_MAX) 121ad73a717SSerge E. Hallyn return NULL; 1229a56c2dbSEric W. Biederman key = find_ge_key(p, pos); 123ad73a717SSerge E. Hallyn if (!key) 124ad73a717SSerge E. Hallyn return NULL; 125ad73a717SSerge E. Hallyn *_pos = key->serial; 126ad73a717SSerge E. Hallyn return &key->serial_node; 1271da177e4SLinus Torvalds } 1281da177e4SLinus Torvalds 129ad73a717SSerge E. Hallyn static inline key_serial_t key_node_serial(struct rb_node *n) 130ad73a717SSerge E. Hallyn { 131ad73a717SSerge E. Hallyn struct key *key = rb_entry(n, struct key, serial_node); 132ad73a717SSerge E. Hallyn return key->serial; 1331da177e4SLinus Torvalds } 1341da177e4SLinus Torvalds 1351da177e4SLinus Torvalds static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos) 1361da177e4SLinus Torvalds { 137ad73a717SSerge E. Hallyn struct rb_node *n; 1381da177e4SLinus Torvalds 1399a56c2dbSEric W. Biederman n = key_serial_next(p, v); 140ad73a717SSerge E. Hallyn if (n) 141ad73a717SSerge E. Hallyn *_pos = key_node_serial(n); 142ad73a717SSerge E. Hallyn return n; 1431da177e4SLinus Torvalds } 1441da177e4SLinus Torvalds 1451da177e4SLinus Torvalds static void proc_keys_stop(struct seq_file *p, void *v) 14686abcf9cSJames Morris __releases(key_serial_lock) 1471da177e4SLinus Torvalds { 1481da177e4SLinus Torvalds spin_unlock(&key_serial_lock); 1491da177e4SLinus Torvalds } 1501da177e4SLinus Torvalds 1511da177e4SLinus Torvalds static int proc_keys_show(struct seq_file *m, void *v) 1521da177e4SLinus Torvalds { 1531da177e4SLinus Torvalds struct rb_node *_p = v; 1541da177e4SLinus Torvalds struct key *key = rb_entry(_p, struct key, serial_node); 155ab5c69f0SEric Biggers unsigned long flags; 156927942aaSDavid Howells key_ref_t key_ref, skey_ref; 157074d5898SBaolin Wang time64_t now, expiry; 15803dab869SDavid Howells char xbuf[16]; 159363b02daSDavid Howells short state; 160074d5898SBaolin Wang u64 timo; 16106ec7be5SMichael LeMay int rc; 16206ec7be5SMichael LeMay 1634bdf0bc3SDavid Howells struct keyring_search_context ctx = { 164ede0fa98SEric Biggers .index_key = key->index_key, 1654aa68e07SEric Biggers .cred = m->file->f_cred, 16646291959SDavid Howells .match_data.cmp = lookup_user_key_possessed, 16746291959SDavid Howells .match_data.raw_data = key, 16846291959SDavid Howells .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, 16946291959SDavid Howells .flags = KEYRING_SEARCH_NO_STATE_CHECK, 1704bdf0bc3SDavid Howells }; 1714bdf0bc3SDavid Howells 172927942aaSDavid Howells key_ref = make_key_ref(key, 0); 173927942aaSDavid Howells 174927942aaSDavid Howells /* determine if the key is possessed by this process (a test we can 175927942aaSDavid Howells * skip if the key does not indicate the possessor can view it 176927942aaSDavid Howells */ 177927942aaSDavid Howells if (key->perm & KEY_POS_VIEW) { 1784bdf0bc3SDavid Howells skey_ref = search_my_process_keyrings(&ctx); 179927942aaSDavid Howells if (!IS_ERR(skey_ref)) { 180927942aaSDavid Howells key_ref_put(skey_ref); 181927942aaSDavid Howells key_ref = make_key_ref(key, 1); 182927942aaSDavid Howells } 183927942aaSDavid Howells } 184927942aaSDavid Howells 1854aa68e07SEric Biggers /* check whether the current task is allowed to view the key */ 186f5895943SDavid Howells rc = key_task_permission(key_ref, ctx.cred, KEY_NEED_VIEW); 18706ec7be5SMichael LeMay if (rc < 0) 18806ec7be5SMichael LeMay return 0; 1891da177e4SLinus Torvalds 190074d5898SBaolin Wang now = ktime_get_real_seconds(); 1911da177e4SLinus Torvalds 19276d8aeabSDavid Howells rcu_read_lock(); 1931da177e4SLinus Torvalds 1941da177e4SLinus Torvalds /* come up with a suitable timeout value */ 195ab5c69f0SEric Biggers expiry = READ_ONCE(key->expiry); 196ab5c69f0SEric Biggers if (expiry == 0) { 1971da177e4SLinus Torvalds memcpy(xbuf, "perm", 5); 198074d5898SBaolin Wang } else if (now >= expiry) { 1991da177e4SLinus Torvalds memcpy(xbuf, "expd", 5); 2007b1b9164SDavid Howells } else { 201074d5898SBaolin Wang timo = expiry - now; 2021da177e4SLinus Torvalds 2031da177e4SLinus Torvalds if (timo < 60) 204074d5898SBaolin Wang sprintf(xbuf, "%llus", timo); 2051da177e4SLinus Torvalds else if (timo < 60*60) 206074d5898SBaolin Wang sprintf(xbuf, "%llum", div_u64(timo, 60)); 2071da177e4SLinus Torvalds else if (timo < 60*60*24) 208074d5898SBaolin Wang sprintf(xbuf, "%lluh", div_u64(timo, 60 * 60)); 2091da177e4SLinus Torvalds else if (timo < 60*60*24*7) 210074d5898SBaolin Wang sprintf(xbuf, "%llud", div_u64(timo, 60 * 60 * 24)); 2111da177e4SLinus Torvalds else 212074d5898SBaolin Wang sprintf(xbuf, "%lluw", div_u64(timo, 60 * 60 * 24 * 7)); 2131da177e4SLinus Torvalds } 2141da177e4SLinus Torvalds 215363b02daSDavid Howells state = key_read_state(key); 216363b02daSDavid Howells 217ab5c69f0SEric Biggers #define showflag(FLAGS, LETTER, FLAG) \ 218ab5c69f0SEric Biggers ((FLAGS & (1 << FLAG)) ? LETTER : '-') 21976d8aeabSDavid Howells 220ab5c69f0SEric Biggers flags = READ_ONCE(key->flags); 221fd75815fSDavid Howells seq_printf(m, "%08x %c%c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ", 2221da177e4SLinus Torvalds key->serial, 223363b02daSDavid Howells state != KEY_IS_UNINSTANTIATED ? 'I' : '-', 224ab5c69f0SEric Biggers showflag(flags, 'R', KEY_FLAG_REVOKED), 225ab5c69f0SEric Biggers showflag(flags, 'D', KEY_FLAG_DEAD), 226ab5c69f0SEric Biggers showflag(flags, 'Q', KEY_FLAG_IN_QUOTA), 227ab5c69f0SEric Biggers showflag(flags, 'U', KEY_FLAG_USER_CONSTRUCT), 228363b02daSDavid Howells state < 0 ? 'N' : '-', 229ab5c69f0SEric Biggers showflag(flags, 'i', KEY_FLAG_INVALIDATED), 230fff29291SElena Reshetova refcount_read(&key->usage), 2311da177e4SLinus Torvalds xbuf, 2321da177e4SLinus Torvalds key->perm, 2339a56c2dbSEric W. Biederman from_kuid_munged(seq_user_ns(m), key->uid), 2349a56c2dbSEric W. Biederman from_kgid_munged(seq_user_ns(m), key->gid), 2351da177e4SLinus Torvalds key->type->name); 2361da177e4SLinus Torvalds 23776d8aeabSDavid Howells #undef showflag 23876d8aeabSDavid Howells 2391da177e4SLinus Torvalds if (key->type->describe) 2401da177e4SLinus Torvalds key->type->describe(key, m); 2411da177e4SLinus Torvalds seq_putc(m, '\n'); 2421da177e4SLinus Torvalds 24376d8aeabSDavid Howells rcu_read_unlock(); 2441da177e4SLinus Torvalds return 0; 2451da177e4SLinus Torvalds } 2461da177e4SLinus Torvalds 2479a56c2dbSEric W. Biederman static struct rb_node *__key_user_next(struct user_namespace *user_ns, struct rb_node *n) 248454804abSSerge E. Hallyn { 249454804abSSerge E. Hallyn while (n) { 250454804abSSerge E. Hallyn struct key_user *user = rb_entry(n, struct key_user, node); 2519a56c2dbSEric W. Biederman if (kuid_has_mapping(user_ns, user->uid)) 252454804abSSerge E. Hallyn break; 253454804abSSerge E. Hallyn n = rb_next(n); 254454804abSSerge E. Hallyn } 255454804abSSerge E. Hallyn return n; 256454804abSSerge E. Hallyn } 257454804abSSerge E. Hallyn 2589a56c2dbSEric W. Biederman static struct rb_node *key_user_next(struct user_namespace *user_ns, struct rb_node *n) 259454804abSSerge E. Hallyn { 2609a56c2dbSEric W. Biederman return __key_user_next(user_ns, rb_next(n)); 261454804abSSerge E. Hallyn } 262454804abSSerge E. Hallyn 2639a56c2dbSEric W. Biederman static struct rb_node *key_user_first(struct user_namespace *user_ns, struct rb_root *r) 264454804abSSerge E. Hallyn { 265454804abSSerge E. Hallyn struct rb_node *n = rb_first(r); 2669a56c2dbSEric W. Biederman return __key_user_next(user_ns, n); 267454804abSSerge E. Hallyn } 2687b1b9164SDavid Howells 2691da177e4SLinus Torvalds static void *proc_key_users_start(struct seq_file *p, loff_t *_pos) 27086abcf9cSJames Morris __acquires(key_user_lock) 2711da177e4SLinus Torvalds { 2721da177e4SLinus Torvalds struct rb_node *_p; 2731da177e4SLinus Torvalds loff_t pos = *_pos; 2741da177e4SLinus Torvalds 2751da177e4SLinus Torvalds spin_lock(&key_user_lock); 2761da177e4SLinus Torvalds 2779a56c2dbSEric W. Biederman _p = key_user_first(seq_user_ns(p), &key_user_tree); 2781da177e4SLinus Torvalds while (pos > 0 && _p) { 2791da177e4SLinus Torvalds pos--; 2809a56c2dbSEric W. Biederman _p = key_user_next(seq_user_ns(p), _p); 2811da177e4SLinus Torvalds } 2821da177e4SLinus Torvalds 2831da177e4SLinus Torvalds return _p; 2841da177e4SLinus Torvalds } 2851da177e4SLinus Torvalds 2861da177e4SLinus Torvalds static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos) 2871da177e4SLinus Torvalds { 2881da177e4SLinus Torvalds (*_pos)++; 2899a56c2dbSEric W. Biederman return key_user_next(seq_user_ns(p), (struct rb_node *)v); 2901da177e4SLinus Torvalds } 2911da177e4SLinus Torvalds 2921da177e4SLinus Torvalds static void proc_key_users_stop(struct seq_file *p, void *v) 29386abcf9cSJames Morris __releases(key_user_lock) 2941da177e4SLinus Torvalds { 2951da177e4SLinus Torvalds spin_unlock(&key_user_lock); 2961da177e4SLinus Torvalds } 2971da177e4SLinus Torvalds 2981da177e4SLinus Torvalds static int proc_key_users_show(struct seq_file *m, void *v) 2991da177e4SLinus Torvalds { 3001da177e4SLinus Torvalds struct rb_node *_p = v; 3011da177e4SLinus Torvalds struct key_user *user = rb_entry(_p, struct key_user, node); 3029a56c2dbSEric W. Biederman unsigned maxkeys = uid_eq(user->uid, GLOBAL_ROOT_UID) ? 3030b77f5bfSDavid Howells key_quota_root_maxkeys : key_quota_maxkeys; 3049a56c2dbSEric W. Biederman unsigned maxbytes = uid_eq(user->uid, GLOBAL_ROOT_UID) ? 3050b77f5bfSDavid Howells key_quota_root_maxbytes : key_quota_maxbytes; 3061da177e4SLinus Torvalds 3071da177e4SLinus Torvalds seq_printf(m, "%5u: %5d %d/%d %d/%d %d/%d\n", 3089a56c2dbSEric W. Biederman from_kuid_munged(seq_user_ns(m), user->uid), 309ddb99e11SElena Reshetova refcount_read(&user->usage), 3101da177e4SLinus Torvalds atomic_read(&user->nkeys), 3111da177e4SLinus Torvalds atomic_read(&user->nikeys), 3121da177e4SLinus Torvalds user->qnkeys, 3130b77f5bfSDavid Howells maxkeys, 3141da177e4SLinus Torvalds user->qnbytes, 3150b77f5bfSDavid Howells maxbytes); 3161da177e4SLinus Torvalds 3171da177e4SLinus Torvalds return 0; 3181da177e4SLinus Torvalds } 319