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); 14286d32f9aSVasily Averin else 14386d32f9aSVasily Averin (*_pos)++; 144ad73a717SSerge E. Hallyn return n; 1451da177e4SLinus Torvalds } 1461da177e4SLinus Torvalds 1471da177e4SLinus Torvalds static void proc_keys_stop(struct seq_file *p, void *v) 14886abcf9cSJames Morris __releases(key_serial_lock) 1491da177e4SLinus Torvalds { 1501da177e4SLinus Torvalds spin_unlock(&key_serial_lock); 1511da177e4SLinus Torvalds } 1521da177e4SLinus Torvalds 1531da177e4SLinus Torvalds static int proc_keys_show(struct seq_file *m, void *v) 1541da177e4SLinus Torvalds { 1551da177e4SLinus Torvalds struct rb_node *_p = v; 1561da177e4SLinus Torvalds struct key *key = rb_entry(_p, struct key, serial_node); 157ab5c69f0SEric Biggers unsigned long flags; 158927942aaSDavid Howells key_ref_t key_ref, skey_ref; 159074d5898SBaolin Wang time64_t now, expiry; 16003dab869SDavid Howells char xbuf[16]; 161363b02daSDavid Howells short state; 162074d5898SBaolin Wang u64 timo; 16306ec7be5SMichael LeMay int rc; 16406ec7be5SMichael LeMay 1654bdf0bc3SDavid Howells struct keyring_search_context ctx = { 166ede0fa98SEric Biggers .index_key = key->index_key, 1674aa68e07SEric Biggers .cred = m->file->f_cred, 16846291959SDavid Howells .match_data.cmp = lookup_user_key_possessed, 16946291959SDavid Howells .match_data.raw_data = key, 17046291959SDavid Howells .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, 171dcf49dbcSDavid Howells .flags = (KEYRING_SEARCH_NO_STATE_CHECK | 172dcf49dbcSDavid Howells KEYRING_SEARCH_RECURSE), 1734bdf0bc3SDavid Howells }; 1744bdf0bc3SDavid Howells 175028db3e2SLinus Torvalds key_ref = make_key_ref(key, 0); 176927942aaSDavid Howells 177927942aaSDavid Howells /* determine if the key is possessed by this process (a test we can 178927942aaSDavid Howells * skip if the key does not indicate the possessor can view it 179927942aaSDavid Howells */ 180028db3e2SLinus Torvalds if (key->perm & KEY_POS_VIEW) { 181028db3e2SLinus Torvalds rcu_read_lock(); 182e59428f7SDavid Howells skey_ref = search_cred_keyrings_rcu(&ctx); 183028db3e2SLinus Torvalds rcu_read_unlock(); 184927942aaSDavid Howells if (!IS_ERR(skey_ref)) { 185927942aaSDavid Howells key_ref_put(skey_ref); 186927942aaSDavid Howells key_ref = make_key_ref(key, 1); 187927942aaSDavid Howells } 188927942aaSDavid Howells } 189927942aaSDavid Howells 1904aa68e07SEric Biggers /* check whether the current task is allowed to view the key */ 191f5895943SDavid Howells rc = key_task_permission(key_ref, ctx.cred, KEY_NEED_VIEW); 19206ec7be5SMichael LeMay if (rc < 0) 193028db3e2SLinus Torvalds return 0; 1941da177e4SLinus Torvalds 195074d5898SBaolin Wang now = ktime_get_real_seconds(); 1961da177e4SLinus Torvalds 197028db3e2SLinus Torvalds rcu_read_lock(); 198028db3e2SLinus Torvalds 1991da177e4SLinus Torvalds /* come up with a suitable timeout value */ 200ab5c69f0SEric Biggers expiry = READ_ONCE(key->expiry); 201*afc360e8SDavid Howells if (expiry == TIME64_MAX) { 2021da177e4SLinus Torvalds memcpy(xbuf, "perm", 5); 203074d5898SBaolin Wang } else if (now >= expiry) { 2041da177e4SLinus Torvalds memcpy(xbuf, "expd", 5); 2057b1b9164SDavid Howells } else { 206074d5898SBaolin Wang timo = expiry - now; 2071da177e4SLinus Torvalds 2081da177e4SLinus Torvalds if (timo < 60) 209074d5898SBaolin Wang sprintf(xbuf, "%llus", timo); 2101da177e4SLinus Torvalds else if (timo < 60*60) 211074d5898SBaolin Wang sprintf(xbuf, "%llum", div_u64(timo, 60)); 2121da177e4SLinus Torvalds else if (timo < 60*60*24) 213074d5898SBaolin Wang sprintf(xbuf, "%lluh", div_u64(timo, 60 * 60)); 2141da177e4SLinus Torvalds else if (timo < 60*60*24*7) 215074d5898SBaolin Wang sprintf(xbuf, "%llud", div_u64(timo, 60 * 60 * 24)); 2161da177e4SLinus Torvalds else 217074d5898SBaolin Wang sprintf(xbuf, "%lluw", div_u64(timo, 60 * 60 * 24 * 7)); 2181da177e4SLinus Torvalds } 2191da177e4SLinus Torvalds 220363b02daSDavid Howells state = key_read_state(key); 221363b02daSDavid Howells 222ab5c69f0SEric Biggers #define showflag(FLAGS, LETTER, FLAG) \ 223ab5c69f0SEric Biggers ((FLAGS & (1 << FLAG)) ? LETTER : '-') 22476d8aeabSDavid Howells 225ab5c69f0SEric Biggers flags = READ_ONCE(key->flags); 226fd75815fSDavid Howells seq_printf(m, "%08x %c%c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ", 2271da177e4SLinus Torvalds key->serial, 228363b02daSDavid Howells state != KEY_IS_UNINSTANTIATED ? 'I' : '-', 229ab5c69f0SEric Biggers showflag(flags, 'R', KEY_FLAG_REVOKED), 230ab5c69f0SEric Biggers showflag(flags, 'D', KEY_FLAG_DEAD), 231ab5c69f0SEric Biggers showflag(flags, 'Q', KEY_FLAG_IN_QUOTA), 232ab5c69f0SEric Biggers showflag(flags, 'U', KEY_FLAG_USER_CONSTRUCT), 233363b02daSDavid Howells state < 0 ? 'N' : '-', 234ab5c69f0SEric Biggers showflag(flags, 'i', KEY_FLAG_INVALIDATED), 235fff29291SElena Reshetova refcount_read(&key->usage), 2361da177e4SLinus Torvalds xbuf, 237028db3e2SLinus Torvalds key->perm, 2389a56c2dbSEric W. Biederman from_kuid_munged(seq_user_ns(m), key->uid), 2399a56c2dbSEric W. Biederman from_kgid_munged(seq_user_ns(m), key->gid), 2401da177e4SLinus Torvalds key->type->name); 2411da177e4SLinus Torvalds 24276d8aeabSDavid Howells #undef showflag 24376d8aeabSDavid Howells 2441da177e4SLinus Torvalds if (key->type->describe) 2451da177e4SLinus Torvalds key->type->describe(key, m); 2461da177e4SLinus Torvalds seq_putc(m, '\n'); 2471da177e4SLinus Torvalds 248028db3e2SLinus Torvalds rcu_read_unlock(); 2491da177e4SLinus Torvalds return 0; 2501da177e4SLinus Torvalds } 2511da177e4SLinus Torvalds 2529a56c2dbSEric W. Biederman static struct rb_node *__key_user_next(struct user_namespace *user_ns, struct rb_node *n) 253454804abSSerge E. Hallyn { 254454804abSSerge E. Hallyn while (n) { 255454804abSSerge E. Hallyn struct key_user *user = rb_entry(n, struct key_user, node); 2569a56c2dbSEric W. Biederman if (kuid_has_mapping(user_ns, user->uid)) 257454804abSSerge E. Hallyn break; 258454804abSSerge E. Hallyn n = rb_next(n); 259454804abSSerge E. Hallyn } 260454804abSSerge E. Hallyn return n; 261454804abSSerge E. Hallyn } 262454804abSSerge E. Hallyn 2639a56c2dbSEric W. Biederman static struct rb_node *key_user_next(struct user_namespace *user_ns, struct rb_node *n) 264454804abSSerge E. Hallyn { 2659a56c2dbSEric W. Biederman return __key_user_next(user_ns, rb_next(n)); 266454804abSSerge E. Hallyn } 267454804abSSerge E. Hallyn 2689a56c2dbSEric W. Biederman static struct rb_node *key_user_first(struct user_namespace *user_ns, struct rb_root *r) 269454804abSSerge E. Hallyn { 270454804abSSerge E. Hallyn struct rb_node *n = rb_first(r); 2719a56c2dbSEric W. Biederman return __key_user_next(user_ns, n); 272454804abSSerge E. Hallyn } 2737b1b9164SDavid Howells 2741da177e4SLinus Torvalds static void *proc_key_users_start(struct seq_file *p, loff_t *_pos) 27586abcf9cSJames Morris __acquires(key_user_lock) 2761da177e4SLinus Torvalds { 2771da177e4SLinus Torvalds struct rb_node *_p; 2781da177e4SLinus Torvalds loff_t pos = *_pos; 2791da177e4SLinus Torvalds 2801da177e4SLinus Torvalds spin_lock(&key_user_lock); 2811da177e4SLinus Torvalds 2829a56c2dbSEric W. Biederman _p = key_user_first(seq_user_ns(p), &key_user_tree); 2831da177e4SLinus Torvalds while (pos > 0 && _p) { 2841da177e4SLinus Torvalds pos--; 2859a56c2dbSEric W. Biederman _p = key_user_next(seq_user_ns(p), _p); 2861da177e4SLinus Torvalds } 2871da177e4SLinus Torvalds 2881da177e4SLinus Torvalds return _p; 2891da177e4SLinus Torvalds } 2901da177e4SLinus Torvalds 2911da177e4SLinus Torvalds static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos) 2921da177e4SLinus Torvalds { 2931da177e4SLinus Torvalds (*_pos)++; 2949a56c2dbSEric W. Biederman return key_user_next(seq_user_ns(p), (struct rb_node *)v); 2951da177e4SLinus Torvalds } 2961da177e4SLinus Torvalds 2971da177e4SLinus Torvalds static void proc_key_users_stop(struct seq_file *p, void *v) 29886abcf9cSJames Morris __releases(key_user_lock) 2991da177e4SLinus Torvalds { 3001da177e4SLinus Torvalds spin_unlock(&key_user_lock); 3011da177e4SLinus Torvalds } 3021da177e4SLinus Torvalds 3031da177e4SLinus Torvalds static int proc_key_users_show(struct seq_file *m, void *v) 3041da177e4SLinus Torvalds { 3051da177e4SLinus Torvalds struct rb_node *_p = v; 3061da177e4SLinus Torvalds struct key_user *user = rb_entry(_p, struct key_user, node); 3079a56c2dbSEric W. Biederman unsigned maxkeys = uid_eq(user->uid, GLOBAL_ROOT_UID) ? 3080b77f5bfSDavid Howells key_quota_root_maxkeys : key_quota_maxkeys; 3099a56c2dbSEric W. Biederman unsigned maxbytes = uid_eq(user->uid, GLOBAL_ROOT_UID) ? 3100b77f5bfSDavid Howells key_quota_root_maxbytes : key_quota_maxbytes; 3111da177e4SLinus Torvalds 3121da177e4SLinus Torvalds seq_printf(m, "%5u: %5d %d/%d %d/%d %d/%d\n", 3139a56c2dbSEric W. Biederman from_kuid_munged(seq_user_ns(m), user->uid), 314ddb99e11SElena Reshetova refcount_read(&user->usage), 3151da177e4SLinus Torvalds atomic_read(&user->nkeys), 3161da177e4SLinus Torvalds atomic_read(&user->nikeys), 3171da177e4SLinus Torvalds user->qnkeys, 3180b77f5bfSDavid Howells maxkeys, 3191da177e4SLinus Torvalds user->qnbytes, 3200b77f5bfSDavid Howells maxbytes); 3211da177e4SLinus Torvalds 3221da177e4SLinus Torvalds return 0; 3231da177e4SLinus Torvalds } 324