1973c9f4fSDavid Howells /* procfs files for key database enumeration 21da177e4SLinus Torvalds * 31da177e4SLinus Torvalds * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. 41da177e4SLinus Torvalds * Written by David Howells (dhowells@redhat.com) 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 71da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 81da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 91da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 101da177e4SLinus Torvalds */ 111da177e4SLinus Torvalds 121da177e4SLinus Torvalds #include <linux/init.h> 131da177e4SLinus Torvalds #include <linux/sched.h> 141da177e4SLinus Torvalds #include <linux/fs.h> 151da177e4SLinus Torvalds #include <linux/proc_fs.h> 161da177e4SLinus Torvalds #include <linux/seq_file.h> 171da177e4SLinus Torvalds #include <asm/errno.h> 181da177e4SLinus Torvalds #include "internal.h" 191da177e4SLinus Torvalds 201da177e4SLinus Torvalds static void *proc_keys_start(struct seq_file *p, loff_t *_pos); 211da177e4SLinus Torvalds static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos); 221da177e4SLinus Torvalds static void proc_keys_stop(struct seq_file *p, void *v); 231da177e4SLinus Torvalds static int proc_keys_show(struct seq_file *m, void *v); 241da177e4SLinus Torvalds 251996a109SJan Engelhardt static const struct seq_operations proc_keys_ops = { 261da177e4SLinus Torvalds .start = proc_keys_start, 271da177e4SLinus Torvalds .next = proc_keys_next, 281da177e4SLinus Torvalds .stop = proc_keys_stop, 291da177e4SLinus Torvalds .show = proc_keys_show, 301da177e4SLinus Torvalds }; 311da177e4SLinus Torvalds 321da177e4SLinus Torvalds static void *proc_key_users_start(struct seq_file *p, loff_t *_pos); 331da177e4SLinus Torvalds static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos); 341da177e4SLinus Torvalds static void proc_key_users_stop(struct seq_file *p, void *v); 351da177e4SLinus Torvalds static int proc_key_users_show(struct seq_file *m, void *v); 361da177e4SLinus Torvalds 371996a109SJan Engelhardt static const struct seq_operations proc_key_users_ops = { 381da177e4SLinus Torvalds .start = proc_key_users_start, 391da177e4SLinus Torvalds .next = proc_key_users_next, 401da177e4SLinus Torvalds .stop = proc_key_users_stop, 411da177e4SLinus Torvalds .show = proc_key_users_show, 421da177e4SLinus Torvalds }; 431da177e4SLinus Torvalds 441da177e4SLinus Torvalds /* 45973c9f4fSDavid Howells * Declare the /proc files. 461da177e4SLinus Torvalds */ 471da177e4SLinus Torvalds static int __init key_proc_init(void) 481da177e4SLinus Torvalds { 491da177e4SLinus Torvalds struct proc_dir_entry *p; 501da177e4SLinus Torvalds 51fddda2b7SChristoph Hellwig p = proc_create_seq("keys", 0, NULL, &proc_keys_ops); 521da177e4SLinus Torvalds if (!p) 531da177e4SLinus Torvalds panic("Cannot create /proc/keys\n"); 541da177e4SLinus Torvalds 55fddda2b7SChristoph Hellwig p = proc_create_seq("key-users", 0, NULL, &proc_key_users_ops); 561da177e4SLinus Torvalds if (!p) 571da177e4SLinus Torvalds panic("Cannot create /proc/key-users\n"); 581da177e4SLinus Torvalds 591da177e4SLinus Torvalds return 0; 60a8b17ed0SDavid Howells } 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds __initcall(key_proc_init); 631da177e4SLinus Torvalds 641da177e4SLinus Torvalds /* 65973c9f4fSDavid Howells * Implement "/proc/keys" to provide a list of the keys on the system that 66973c9f4fSDavid Howells * grant View permission to the caller. 671da177e4SLinus Torvalds */ 689a56c2dbSEric W. Biederman static struct rb_node *key_serial_next(struct seq_file *p, struct rb_node *n) 69454804abSSerge E. Hallyn { 709a56c2dbSEric W. Biederman struct user_namespace *user_ns = seq_user_ns(p); 71ad73a717SSerge E. Hallyn 72ad73a717SSerge E. Hallyn n = rb_next(n); 73454804abSSerge E. Hallyn while (n) { 74454804abSSerge E. Hallyn struct key *key = rb_entry(n, struct key, serial_node); 759a56c2dbSEric W. Biederman if (kuid_has_mapping(user_ns, key->user->uid)) 76454804abSSerge E. Hallyn break; 77454804abSSerge E. Hallyn n = rb_next(n); 78454804abSSerge E. Hallyn } 79454804abSSerge E. Hallyn return n; 80454804abSSerge E. Hallyn } 81454804abSSerge E. Hallyn 829a56c2dbSEric W. Biederman static struct key *find_ge_key(struct seq_file *p, key_serial_t id) 83ad73a717SSerge E. Hallyn { 849a56c2dbSEric W. Biederman struct user_namespace *user_ns = seq_user_ns(p); 85ad73a717SSerge E. Hallyn struct rb_node *n = key_serial_tree.rb_node; 86ad73a717SSerge E. Hallyn struct key *minkey = NULL; 87ad73a717SSerge E. Hallyn 88ad73a717SSerge E. Hallyn while (n) { 89ad73a717SSerge E. Hallyn struct key *key = rb_entry(n, struct key, serial_node); 90ad73a717SSerge E. Hallyn if (id < key->serial) { 91ad73a717SSerge E. Hallyn if (!minkey || minkey->serial > key->serial) 92ad73a717SSerge E. Hallyn minkey = key; 93ad73a717SSerge E. Hallyn n = n->rb_left; 94ad73a717SSerge E. Hallyn } else if (id > key->serial) { 95ad73a717SSerge E. Hallyn n = n->rb_right; 96ad73a717SSerge E. Hallyn } else { 97ad73a717SSerge E. Hallyn minkey = key; 98ad73a717SSerge E. Hallyn break; 99ad73a717SSerge E. Hallyn } 100ad73a717SSerge E. Hallyn key = NULL; 101ad73a717SSerge E. Hallyn } 102ad73a717SSerge E. Hallyn 103ad73a717SSerge E. Hallyn if (!minkey) 104ad73a717SSerge E. Hallyn return NULL; 105ad73a717SSerge E. Hallyn 106ad73a717SSerge E. Hallyn for (;;) { 1079a56c2dbSEric W. Biederman if (kuid_has_mapping(user_ns, minkey->user->uid)) 108ad73a717SSerge E. Hallyn return minkey; 109ad73a717SSerge E. Hallyn n = rb_next(&minkey->serial_node); 110ad73a717SSerge E. Hallyn if (!n) 111ad73a717SSerge E. Hallyn return NULL; 112ad73a717SSerge E. Hallyn minkey = rb_entry(n, struct key, serial_node); 113ad73a717SSerge E. Hallyn } 1141da177e4SLinus Torvalds } 1151da177e4SLinus Torvalds 1161da177e4SLinus Torvalds static void *proc_keys_start(struct seq_file *p, loff_t *_pos) 11786abcf9cSJames Morris __acquires(key_serial_lock) 1181da177e4SLinus Torvalds { 119ad73a717SSerge E. Hallyn key_serial_t pos = *_pos; 120ad73a717SSerge E. Hallyn struct key *key; 1211da177e4SLinus Torvalds 1221da177e4SLinus Torvalds spin_lock(&key_serial_lock); 1231da177e4SLinus Torvalds 124ad73a717SSerge E. Hallyn if (*_pos > INT_MAX) 125ad73a717SSerge E. Hallyn return NULL; 1269a56c2dbSEric W. Biederman key = find_ge_key(p, pos); 127ad73a717SSerge E. Hallyn if (!key) 128ad73a717SSerge E. Hallyn return NULL; 129ad73a717SSerge E. Hallyn *_pos = key->serial; 130ad73a717SSerge E. Hallyn return &key->serial_node; 1311da177e4SLinus Torvalds } 1321da177e4SLinus Torvalds 133ad73a717SSerge E. Hallyn static inline key_serial_t key_node_serial(struct rb_node *n) 134ad73a717SSerge E. Hallyn { 135ad73a717SSerge E. Hallyn struct key *key = rb_entry(n, struct key, serial_node); 136ad73a717SSerge E. Hallyn return key->serial; 1371da177e4SLinus Torvalds } 1381da177e4SLinus Torvalds 1391da177e4SLinus Torvalds static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos) 1401da177e4SLinus Torvalds { 141ad73a717SSerge E. Hallyn struct rb_node *n; 1421da177e4SLinus Torvalds 1439a56c2dbSEric W. Biederman n = key_serial_next(p, v); 144ad73a717SSerge E. Hallyn if (n) 145ad73a717SSerge E. Hallyn *_pos = key_node_serial(n); 146ad73a717SSerge E. Hallyn return n; 1471da177e4SLinus Torvalds } 1481da177e4SLinus Torvalds 1491da177e4SLinus Torvalds static void proc_keys_stop(struct seq_file *p, void *v) 15086abcf9cSJames Morris __releases(key_serial_lock) 1511da177e4SLinus Torvalds { 1521da177e4SLinus Torvalds spin_unlock(&key_serial_lock); 1531da177e4SLinus Torvalds } 1541da177e4SLinus Torvalds 1551da177e4SLinus Torvalds static int proc_keys_show(struct seq_file *m, void *v) 1561da177e4SLinus Torvalds { 1571da177e4SLinus Torvalds struct rb_node *_p = v; 1581da177e4SLinus Torvalds struct key *key = rb_entry(_p, struct key, serial_node); 159ab5c69f0SEric Biggers unsigned long flags; 160927942aaSDavid Howells key_ref_t key_ref, skey_ref; 161074d5898SBaolin Wang time64_t now, expiry; 16203dab869SDavid Howells char xbuf[16]; 163363b02daSDavid Howells short state; 164074d5898SBaolin Wang u64 timo; 16506ec7be5SMichael LeMay int rc; 16606ec7be5SMichael LeMay 1674bdf0bc3SDavid Howells struct keyring_search_context ctx = { 168ede0fa98SEric Biggers .index_key = key->index_key, 1694aa68e07SEric Biggers .cred = m->file->f_cred, 17046291959SDavid Howells .match_data.cmp = lookup_user_key_possessed, 17146291959SDavid Howells .match_data.raw_data = key, 17246291959SDavid Howells .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, 173*dcf49dbcSDavid Howells .flags = (KEYRING_SEARCH_NO_STATE_CHECK | 174*dcf49dbcSDavid Howells KEYRING_SEARCH_RECURSE), 1754bdf0bc3SDavid Howells }; 1764bdf0bc3SDavid Howells 177927942aaSDavid Howells key_ref = make_key_ref(key, 0); 178927942aaSDavid Howells 179927942aaSDavid Howells /* determine if the key is possessed by this process (a test we can 180927942aaSDavid Howells * skip if the key does not indicate the possessor can view it 181927942aaSDavid Howells */ 182927942aaSDavid Howells if (key->perm & KEY_POS_VIEW) { 183e59428f7SDavid Howells rcu_read_lock(); 184e59428f7SDavid Howells skey_ref = search_cred_keyrings_rcu(&ctx); 185e59428f7SDavid Howells rcu_read_unlock(); 186927942aaSDavid Howells if (!IS_ERR(skey_ref)) { 187927942aaSDavid Howells key_ref_put(skey_ref); 188927942aaSDavid Howells key_ref = make_key_ref(key, 1); 189927942aaSDavid Howells } 190927942aaSDavid Howells } 191927942aaSDavid Howells 1924aa68e07SEric Biggers /* check whether the current task is allowed to view the key */ 193f5895943SDavid Howells rc = key_task_permission(key_ref, ctx.cred, KEY_NEED_VIEW); 19406ec7be5SMichael LeMay if (rc < 0) 19506ec7be5SMichael LeMay return 0; 1961da177e4SLinus Torvalds 197074d5898SBaolin Wang now = ktime_get_real_seconds(); 1981da177e4SLinus Torvalds 19976d8aeabSDavid Howells rcu_read_lock(); 2001da177e4SLinus Torvalds 2011da177e4SLinus Torvalds /* come up with a suitable timeout value */ 202ab5c69f0SEric Biggers expiry = READ_ONCE(key->expiry); 203ab5c69f0SEric Biggers if (expiry == 0) { 2041da177e4SLinus Torvalds memcpy(xbuf, "perm", 5); 205074d5898SBaolin Wang } else if (now >= expiry) { 2061da177e4SLinus Torvalds memcpy(xbuf, "expd", 5); 2077b1b9164SDavid Howells } else { 208074d5898SBaolin Wang timo = expiry - now; 2091da177e4SLinus Torvalds 2101da177e4SLinus Torvalds if (timo < 60) 211074d5898SBaolin Wang sprintf(xbuf, "%llus", timo); 2121da177e4SLinus Torvalds else if (timo < 60*60) 213074d5898SBaolin Wang sprintf(xbuf, "%llum", div_u64(timo, 60)); 2141da177e4SLinus Torvalds else if (timo < 60*60*24) 215074d5898SBaolin Wang sprintf(xbuf, "%lluh", div_u64(timo, 60 * 60)); 2161da177e4SLinus Torvalds else if (timo < 60*60*24*7) 217074d5898SBaolin Wang sprintf(xbuf, "%llud", div_u64(timo, 60 * 60 * 24)); 2181da177e4SLinus Torvalds else 219074d5898SBaolin Wang sprintf(xbuf, "%lluw", div_u64(timo, 60 * 60 * 24 * 7)); 2201da177e4SLinus Torvalds } 2211da177e4SLinus Torvalds 222363b02daSDavid Howells state = key_read_state(key); 223363b02daSDavid Howells 224ab5c69f0SEric Biggers #define showflag(FLAGS, LETTER, FLAG) \ 225ab5c69f0SEric Biggers ((FLAGS & (1 << FLAG)) ? LETTER : '-') 22676d8aeabSDavid Howells 227ab5c69f0SEric Biggers flags = READ_ONCE(key->flags); 228fd75815fSDavid Howells seq_printf(m, "%08x %c%c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ", 2291da177e4SLinus Torvalds key->serial, 230363b02daSDavid Howells state != KEY_IS_UNINSTANTIATED ? 'I' : '-', 231ab5c69f0SEric Biggers showflag(flags, 'R', KEY_FLAG_REVOKED), 232ab5c69f0SEric Biggers showflag(flags, 'D', KEY_FLAG_DEAD), 233ab5c69f0SEric Biggers showflag(flags, 'Q', KEY_FLAG_IN_QUOTA), 234ab5c69f0SEric Biggers showflag(flags, 'U', KEY_FLAG_USER_CONSTRUCT), 235363b02daSDavid Howells state < 0 ? 'N' : '-', 236ab5c69f0SEric Biggers showflag(flags, 'i', KEY_FLAG_INVALIDATED), 237fff29291SElena Reshetova refcount_read(&key->usage), 2381da177e4SLinus Torvalds xbuf, 2391da177e4SLinus Torvalds key->perm, 2409a56c2dbSEric W. Biederman from_kuid_munged(seq_user_ns(m), key->uid), 2419a56c2dbSEric W. Biederman from_kgid_munged(seq_user_ns(m), key->gid), 2421da177e4SLinus Torvalds key->type->name); 2431da177e4SLinus Torvalds 24476d8aeabSDavid Howells #undef showflag 24576d8aeabSDavid Howells 2461da177e4SLinus Torvalds if (key->type->describe) 2471da177e4SLinus Torvalds key->type->describe(key, m); 2481da177e4SLinus Torvalds seq_putc(m, '\n'); 2491da177e4SLinus Torvalds 25076d8aeabSDavid Howells rcu_read_unlock(); 2511da177e4SLinus Torvalds return 0; 2521da177e4SLinus Torvalds } 2531da177e4SLinus Torvalds 2549a56c2dbSEric W. Biederman static struct rb_node *__key_user_next(struct user_namespace *user_ns, struct rb_node *n) 255454804abSSerge E. Hallyn { 256454804abSSerge E. Hallyn while (n) { 257454804abSSerge E. Hallyn struct key_user *user = rb_entry(n, struct key_user, node); 2589a56c2dbSEric W. Biederman if (kuid_has_mapping(user_ns, user->uid)) 259454804abSSerge E. Hallyn break; 260454804abSSerge E. Hallyn n = rb_next(n); 261454804abSSerge E. Hallyn } 262454804abSSerge E. Hallyn return n; 263454804abSSerge E. Hallyn } 264454804abSSerge E. Hallyn 2659a56c2dbSEric W. Biederman static struct rb_node *key_user_next(struct user_namespace *user_ns, struct rb_node *n) 266454804abSSerge E. Hallyn { 2679a56c2dbSEric W. Biederman return __key_user_next(user_ns, rb_next(n)); 268454804abSSerge E. Hallyn } 269454804abSSerge E. Hallyn 2709a56c2dbSEric W. Biederman static struct rb_node *key_user_first(struct user_namespace *user_ns, struct rb_root *r) 271454804abSSerge E. Hallyn { 272454804abSSerge E. Hallyn struct rb_node *n = rb_first(r); 2739a56c2dbSEric W. Biederman return __key_user_next(user_ns, n); 274454804abSSerge E. Hallyn } 2757b1b9164SDavid Howells 2761da177e4SLinus Torvalds static void *proc_key_users_start(struct seq_file *p, loff_t *_pos) 27786abcf9cSJames Morris __acquires(key_user_lock) 2781da177e4SLinus Torvalds { 2791da177e4SLinus Torvalds struct rb_node *_p; 2801da177e4SLinus Torvalds loff_t pos = *_pos; 2811da177e4SLinus Torvalds 2821da177e4SLinus Torvalds spin_lock(&key_user_lock); 2831da177e4SLinus Torvalds 2849a56c2dbSEric W. Biederman _p = key_user_first(seq_user_ns(p), &key_user_tree); 2851da177e4SLinus Torvalds while (pos > 0 && _p) { 2861da177e4SLinus Torvalds pos--; 2879a56c2dbSEric W. Biederman _p = key_user_next(seq_user_ns(p), _p); 2881da177e4SLinus Torvalds } 2891da177e4SLinus Torvalds 2901da177e4SLinus Torvalds return _p; 2911da177e4SLinus Torvalds } 2921da177e4SLinus Torvalds 2931da177e4SLinus Torvalds static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos) 2941da177e4SLinus Torvalds { 2951da177e4SLinus Torvalds (*_pos)++; 2969a56c2dbSEric W. Biederman return key_user_next(seq_user_ns(p), (struct rb_node *)v); 2971da177e4SLinus Torvalds } 2981da177e4SLinus Torvalds 2991da177e4SLinus Torvalds static void proc_key_users_stop(struct seq_file *p, void *v) 30086abcf9cSJames Morris __releases(key_user_lock) 3011da177e4SLinus Torvalds { 3021da177e4SLinus Torvalds spin_unlock(&key_user_lock); 3031da177e4SLinus Torvalds } 3041da177e4SLinus Torvalds 3051da177e4SLinus Torvalds static int proc_key_users_show(struct seq_file *m, void *v) 3061da177e4SLinus Torvalds { 3071da177e4SLinus Torvalds struct rb_node *_p = v; 3081da177e4SLinus Torvalds struct key_user *user = rb_entry(_p, struct key_user, node); 3099a56c2dbSEric W. Biederman unsigned maxkeys = uid_eq(user->uid, GLOBAL_ROOT_UID) ? 3100b77f5bfSDavid Howells key_quota_root_maxkeys : key_quota_maxkeys; 3119a56c2dbSEric W. Biederman unsigned maxbytes = uid_eq(user->uid, GLOBAL_ROOT_UID) ? 3120b77f5bfSDavid Howells key_quota_root_maxbytes : key_quota_maxbytes; 3131da177e4SLinus Torvalds 3141da177e4SLinus Torvalds seq_printf(m, "%5u: %5d %d/%d %d/%d %d/%d\n", 3159a56c2dbSEric W. Biederman from_kuid_munged(seq_user_ns(m), user->uid), 316ddb99e11SElena Reshetova refcount_read(&user->usage), 3171da177e4SLinus Torvalds atomic_read(&user->nkeys), 3181da177e4SLinus Torvalds atomic_read(&user->nikeys), 3191da177e4SLinus Torvalds user->qnkeys, 3200b77f5bfSDavid Howells maxkeys, 3211da177e4SLinus Torvalds user->qnbytes, 3220b77f5bfSDavid Howells maxbytes); 3231da177e4SLinus Torvalds 3241da177e4SLinus Torvalds return 0; 3251da177e4SLinus Torvalds } 326