12874c5fdSThomas 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, 169dcf49dbcSDavid Howells .flags = (KEYRING_SEARCH_NO_STATE_CHECK | 170dcf49dbcSDavid Howells KEYRING_SEARCH_RECURSE), 1714bdf0bc3SDavid Howells }; 1724bdf0bc3SDavid Howells 173*028db3e2SLinus Torvalds key_ref = make_key_ref(key, 0); 174927942aaSDavid Howells 175927942aaSDavid Howells /* determine if the key is possessed by this process (a test we can 176927942aaSDavid Howells * skip if the key does not indicate the possessor can view it 177927942aaSDavid Howells */ 178*028db3e2SLinus Torvalds if (key->perm & KEY_POS_VIEW) { 179*028db3e2SLinus Torvalds rcu_read_lock(); 180e59428f7SDavid Howells skey_ref = search_cred_keyrings_rcu(&ctx); 181*028db3e2SLinus Torvalds rcu_read_unlock(); 182927942aaSDavid Howells if (!IS_ERR(skey_ref)) { 183927942aaSDavid Howells key_ref_put(skey_ref); 184927942aaSDavid Howells key_ref = make_key_ref(key, 1); 185927942aaSDavid Howells } 186927942aaSDavid Howells } 187927942aaSDavid Howells 1884aa68e07SEric Biggers /* check whether the current task is allowed to view the key */ 189f5895943SDavid Howells rc = key_task_permission(key_ref, ctx.cred, KEY_NEED_VIEW); 19006ec7be5SMichael LeMay if (rc < 0) 191*028db3e2SLinus Torvalds return 0; 1921da177e4SLinus Torvalds 193074d5898SBaolin Wang now = ktime_get_real_seconds(); 1941da177e4SLinus Torvalds 195*028db3e2SLinus Torvalds rcu_read_lock(); 196*028db3e2SLinus Torvalds 1971da177e4SLinus Torvalds /* come up with a suitable timeout value */ 198ab5c69f0SEric Biggers expiry = READ_ONCE(key->expiry); 199ab5c69f0SEric Biggers if (expiry == 0) { 2001da177e4SLinus Torvalds memcpy(xbuf, "perm", 5); 201074d5898SBaolin Wang } else if (now >= expiry) { 2021da177e4SLinus Torvalds memcpy(xbuf, "expd", 5); 2037b1b9164SDavid Howells } else { 204074d5898SBaolin Wang timo = expiry - now; 2051da177e4SLinus Torvalds 2061da177e4SLinus Torvalds if (timo < 60) 207074d5898SBaolin Wang sprintf(xbuf, "%llus", timo); 2081da177e4SLinus Torvalds else if (timo < 60*60) 209074d5898SBaolin Wang sprintf(xbuf, "%llum", div_u64(timo, 60)); 2101da177e4SLinus Torvalds else if (timo < 60*60*24) 211074d5898SBaolin Wang sprintf(xbuf, "%lluh", div_u64(timo, 60 * 60)); 2121da177e4SLinus Torvalds else if (timo < 60*60*24*7) 213074d5898SBaolin Wang sprintf(xbuf, "%llud", div_u64(timo, 60 * 60 * 24)); 2141da177e4SLinus Torvalds else 215074d5898SBaolin Wang sprintf(xbuf, "%lluw", div_u64(timo, 60 * 60 * 24 * 7)); 2161da177e4SLinus Torvalds } 2171da177e4SLinus Torvalds 218363b02daSDavid Howells state = key_read_state(key); 219363b02daSDavid Howells 220ab5c69f0SEric Biggers #define showflag(FLAGS, LETTER, FLAG) \ 221ab5c69f0SEric Biggers ((FLAGS & (1 << FLAG)) ? LETTER : '-') 22276d8aeabSDavid Howells 223ab5c69f0SEric Biggers flags = READ_ONCE(key->flags); 224fd75815fSDavid Howells seq_printf(m, "%08x %c%c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ", 2251da177e4SLinus Torvalds key->serial, 226363b02daSDavid Howells state != KEY_IS_UNINSTANTIATED ? 'I' : '-', 227ab5c69f0SEric Biggers showflag(flags, 'R', KEY_FLAG_REVOKED), 228ab5c69f0SEric Biggers showflag(flags, 'D', KEY_FLAG_DEAD), 229ab5c69f0SEric Biggers showflag(flags, 'Q', KEY_FLAG_IN_QUOTA), 230ab5c69f0SEric Biggers showflag(flags, 'U', KEY_FLAG_USER_CONSTRUCT), 231363b02daSDavid Howells state < 0 ? 'N' : '-', 232ab5c69f0SEric Biggers showflag(flags, 'i', KEY_FLAG_INVALIDATED), 233fff29291SElena Reshetova refcount_read(&key->usage), 2341da177e4SLinus Torvalds xbuf, 235*028db3e2SLinus Torvalds key->perm, 2369a56c2dbSEric W. Biederman from_kuid_munged(seq_user_ns(m), key->uid), 2379a56c2dbSEric W. Biederman from_kgid_munged(seq_user_ns(m), key->gid), 2381da177e4SLinus Torvalds key->type->name); 2391da177e4SLinus Torvalds 24076d8aeabSDavid Howells #undef showflag 24176d8aeabSDavid Howells 2421da177e4SLinus Torvalds if (key->type->describe) 2431da177e4SLinus Torvalds key->type->describe(key, m); 2441da177e4SLinus Torvalds seq_putc(m, '\n'); 2451da177e4SLinus Torvalds 246*028db3e2SLinus Torvalds rcu_read_unlock(); 2471da177e4SLinus Torvalds return 0; 2481da177e4SLinus Torvalds } 2491da177e4SLinus Torvalds 2509a56c2dbSEric W. Biederman static struct rb_node *__key_user_next(struct user_namespace *user_ns, struct rb_node *n) 251454804abSSerge E. Hallyn { 252454804abSSerge E. Hallyn while (n) { 253454804abSSerge E. Hallyn struct key_user *user = rb_entry(n, struct key_user, node); 2549a56c2dbSEric W. Biederman if (kuid_has_mapping(user_ns, user->uid)) 255454804abSSerge E. Hallyn break; 256454804abSSerge E. Hallyn n = rb_next(n); 257454804abSSerge E. Hallyn } 258454804abSSerge E. Hallyn return n; 259454804abSSerge E. Hallyn } 260454804abSSerge E. Hallyn 2619a56c2dbSEric W. Biederman static struct rb_node *key_user_next(struct user_namespace *user_ns, struct rb_node *n) 262454804abSSerge E. Hallyn { 2639a56c2dbSEric W. Biederman return __key_user_next(user_ns, rb_next(n)); 264454804abSSerge E. Hallyn } 265454804abSSerge E. Hallyn 2669a56c2dbSEric W. Biederman static struct rb_node *key_user_first(struct user_namespace *user_ns, struct rb_root *r) 267454804abSSerge E. Hallyn { 268454804abSSerge E. Hallyn struct rb_node *n = rb_first(r); 2699a56c2dbSEric W. Biederman return __key_user_next(user_ns, n); 270454804abSSerge E. Hallyn } 2717b1b9164SDavid Howells 2721da177e4SLinus Torvalds static void *proc_key_users_start(struct seq_file *p, loff_t *_pos) 27386abcf9cSJames Morris __acquires(key_user_lock) 2741da177e4SLinus Torvalds { 2751da177e4SLinus Torvalds struct rb_node *_p; 2761da177e4SLinus Torvalds loff_t pos = *_pos; 2771da177e4SLinus Torvalds 2781da177e4SLinus Torvalds spin_lock(&key_user_lock); 2791da177e4SLinus Torvalds 2809a56c2dbSEric W. Biederman _p = key_user_first(seq_user_ns(p), &key_user_tree); 2811da177e4SLinus Torvalds while (pos > 0 && _p) { 2821da177e4SLinus Torvalds pos--; 2839a56c2dbSEric W. Biederman _p = key_user_next(seq_user_ns(p), _p); 2841da177e4SLinus Torvalds } 2851da177e4SLinus Torvalds 2861da177e4SLinus Torvalds return _p; 2871da177e4SLinus Torvalds } 2881da177e4SLinus Torvalds 2891da177e4SLinus Torvalds static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos) 2901da177e4SLinus Torvalds { 2911da177e4SLinus Torvalds (*_pos)++; 2929a56c2dbSEric W. Biederman return key_user_next(seq_user_ns(p), (struct rb_node *)v); 2931da177e4SLinus Torvalds } 2941da177e4SLinus Torvalds 2951da177e4SLinus Torvalds static void proc_key_users_stop(struct seq_file *p, void *v) 29686abcf9cSJames Morris __releases(key_user_lock) 2971da177e4SLinus Torvalds { 2981da177e4SLinus Torvalds spin_unlock(&key_user_lock); 2991da177e4SLinus Torvalds } 3001da177e4SLinus Torvalds 3011da177e4SLinus Torvalds static int proc_key_users_show(struct seq_file *m, void *v) 3021da177e4SLinus Torvalds { 3031da177e4SLinus Torvalds struct rb_node *_p = v; 3041da177e4SLinus Torvalds struct key_user *user = rb_entry(_p, struct key_user, node); 3059a56c2dbSEric W. Biederman unsigned maxkeys = uid_eq(user->uid, GLOBAL_ROOT_UID) ? 3060b77f5bfSDavid Howells key_quota_root_maxkeys : key_quota_maxkeys; 3079a56c2dbSEric W. Biederman unsigned maxbytes = uid_eq(user->uid, GLOBAL_ROOT_UID) ? 3080b77f5bfSDavid Howells key_quota_root_maxbytes : key_quota_maxbytes; 3091da177e4SLinus Torvalds 3101da177e4SLinus Torvalds seq_printf(m, "%5u: %5d %d/%d %d/%d %d/%d\n", 3119a56c2dbSEric W. Biederman from_kuid_munged(seq_user_ns(m), user->uid), 312ddb99e11SElena Reshetova refcount_read(&user->usage), 3131da177e4SLinus Torvalds atomic_read(&user->nkeys), 3141da177e4SLinus Torvalds atomic_read(&user->nikeys), 3151da177e4SLinus Torvalds user->qnkeys, 3160b77f5bfSDavid Howells maxkeys, 3171da177e4SLinus Torvalds user->qnbytes, 3180b77f5bfSDavid Howells maxbytes); 3191da177e4SLinus Torvalds 3201da177e4SLinus Torvalds return 0; 3211da177e4SLinus Torvalds } 322