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, 17346291959SDavid Howells .flags = KEYRING_SEARCH_NO_STATE_CHECK, 1744bdf0bc3SDavid Howells }; 1754bdf0bc3SDavid Howells 176927942aaSDavid Howells key_ref = make_key_ref(key, 0); 177927942aaSDavid Howells 178927942aaSDavid Howells /* determine if the key is possessed by this process (a test we can 179927942aaSDavid Howells * skip if the key does not indicate the possessor can view it 180927942aaSDavid Howells */ 181927942aaSDavid Howells if (key->perm & KEY_POS_VIEW) { 182*e59428f7SDavid Howells rcu_read_lock(); 183*e59428f7SDavid Howells skey_ref = search_cred_keyrings_rcu(&ctx); 184*e59428f7SDavid Howells rcu_read_unlock(); 185927942aaSDavid Howells if (!IS_ERR(skey_ref)) { 186927942aaSDavid Howells key_ref_put(skey_ref); 187927942aaSDavid Howells key_ref = make_key_ref(key, 1); 188927942aaSDavid Howells } 189927942aaSDavid Howells } 190927942aaSDavid Howells 1914aa68e07SEric Biggers /* check whether the current task is allowed to view the key */ 192f5895943SDavid Howells rc = key_task_permission(key_ref, ctx.cred, KEY_NEED_VIEW); 19306ec7be5SMichael LeMay if (rc < 0) 19406ec7be5SMichael LeMay return 0; 1951da177e4SLinus Torvalds 196074d5898SBaolin Wang now = ktime_get_real_seconds(); 1971da177e4SLinus Torvalds 19876d8aeabSDavid Howells rcu_read_lock(); 1991da177e4SLinus Torvalds 2001da177e4SLinus Torvalds /* come up with a suitable timeout value */ 201ab5c69f0SEric Biggers expiry = READ_ONCE(key->expiry); 202ab5c69f0SEric Biggers if (expiry == 0) { 2031da177e4SLinus Torvalds memcpy(xbuf, "perm", 5); 204074d5898SBaolin Wang } else if (now >= expiry) { 2051da177e4SLinus Torvalds memcpy(xbuf, "expd", 5); 2067b1b9164SDavid Howells } else { 207074d5898SBaolin Wang timo = expiry - now; 2081da177e4SLinus Torvalds 2091da177e4SLinus Torvalds if (timo < 60) 210074d5898SBaolin Wang sprintf(xbuf, "%llus", timo); 2111da177e4SLinus Torvalds else if (timo < 60*60) 212074d5898SBaolin Wang sprintf(xbuf, "%llum", div_u64(timo, 60)); 2131da177e4SLinus Torvalds else if (timo < 60*60*24) 214074d5898SBaolin Wang sprintf(xbuf, "%lluh", div_u64(timo, 60 * 60)); 2151da177e4SLinus Torvalds else if (timo < 60*60*24*7) 216074d5898SBaolin Wang sprintf(xbuf, "%llud", div_u64(timo, 60 * 60 * 24)); 2171da177e4SLinus Torvalds else 218074d5898SBaolin Wang sprintf(xbuf, "%lluw", div_u64(timo, 60 * 60 * 24 * 7)); 2191da177e4SLinus Torvalds } 2201da177e4SLinus Torvalds 221363b02daSDavid Howells state = key_read_state(key); 222363b02daSDavid Howells 223ab5c69f0SEric Biggers #define showflag(FLAGS, LETTER, FLAG) \ 224ab5c69f0SEric Biggers ((FLAGS & (1 << FLAG)) ? LETTER : '-') 22576d8aeabSDavid Howells 226ab5c69f0SEric Biggers flags = READ_ONCE(key->flags); 227fd75815fSDavid Howells seq_printf(m, "%08x %c%c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ", 2281da177e4SLinus Torvalds key->serial, 229363b02daSDavid Howells state != KEY_IS_UNINSTANTIATED ? 'I' : '-', 230ab5c69f0SEric Biggers showflag(flags, 'R', KEY_FLAG_REVOKED), 231ab5c69f0SEric Biggers showflag(flags, 'D', KEY_FLAG_DEAD), 232ab5c69f0SEric Biggers showflag(flags, 'Q', KEY_FLAG_IN_QUOTA), 233ab5c69f0SEric Biggers showflag(flags, 'U', KEY_FLAG_USER_CONSTRUCT), 234363b02daSDavid Howells state < 0 ? 'N' : '-', 235ab5c69f0SEric Biggers showflag(flags, 'i', KEY_FLAG_INVALIDATED), 236fff29291SElena Reshetova refcount_read(&key->usage), 2371da177e4SLinus Torvalds xbuf, 2381da177e4SLinus Torvalds key->perm, 2399a56c2dbSEric W. Biederman from_kuid_munged(seq_user_ns(m), key->uid), 2409a56c2dbSEric W. Biederman from_kgid_munged(seq_user_ns(m), key->gid), 2411da177e4SLinus Torvalds key->type->name); 2421da177e4SLinus Torvalds 24376d8aeabSDavid Howells #undef showflag 24476d8aeabSDavid Howells 2451da177e4SLinus Torvalds if (key->type->describe) 2461da177e4SLinus Torvalds key->type->describe(key, m); 2471da177e4SLinus Torvalds seq_putc(m, '\n'); 2481da177e4SLinus Torvalds 24976d8aeabSDavid Howells rcu_read_unlock(); 2501da177e4SLinus Torvalds return 0; 2511da177e4SLinus Torvalds } 2521da177e4SLinus Torvalds 2539a56c2dbSEric W. Biederman static struct rb_node *__key_user_next(struct user_namespace *user_ns, struct rb_node *n) 254454804abSSerge E. Hallyn { 255454804abSSerge E. Hallyn while (n) { 256454804abSSerge E. Hallyn struct key_user *user = rb_entry(n, struct key_user, node); 2579a56c2dbSEric W. Biederman if (kuid_has_mapping(user_ns, user->uid)) 258454804abSSerge E. Hallyn break; 259454804abSSerge E. Hallyn n = rb_next(n); 260454804abSSerge E. Hallyn } 261454804abSSerge E. Hallyn return n; 262454804abSSerge E. Hallyn } 263454804abSSerge E. Hallyn 2649a56c2dbSEric W. Biederman static struct rb_node *key_user_next(struct user_namespace *user_ns, struct rb_node *n) 265454804abSSerge E. Hallyn { 2669a56c2dbSEric W. Biederman return __key_user_next(user_ns, rb_next(n)); 267454804abSSerge E. Hallyn } 268454804abSSerge E. Hallyn 2699a56c2dbSEric W. Biederman static struct rb_node *key_user_first(struct user_namespace *user_ns, struct rb_root *r) 270454804abSSerge E. Hallyn { 271454804abSSerge E. Hallyn struct rb_node *n = rb_first(r); 2729a56c2dbSEric W. Biederman return __key_user_next(user_ns, n); 273454804abSSerge E. Hallyn } 2747b1b9164SDavid Howells 2751da177e4SLinus Torvalds static void *proc_key_users_start(struct seq_file *p, loff_t *_pos) 27686abcf9cSJames Morris __acquires(key_user_lock) 2771da177e4SLinus Torvalds { 2781da177e4SLinus Torvalds struct rb_node *_p; 2791da177e4SLinus Torvalds loff_t pos = *_pos; 2801da177e4SLinus Torvalds 2811da177e4SLinus Torvalds spin_lock(&key_user_lock); 2821da177e4SLinus Torvalds 2839a56c2dbSEric W. Biederman _p = key_user_first(seq_user_ns(p), &key_user_tree); 2841da177e4SLinus Torvalds while (pos > 0 && _p) { 2851da177e4SLinus Torvalds pos--; 2869a56c2dbSEric W. Biederman _p = key_user_next(seq_user_ns(p), _p); 2871da177e4SLinus Torvalds } 2881da177e4SLinus Torvalds 2891da177e4SLinus Torvalds return _p; 2901da177e4SLinus Torvalds } 2911da177e4SLinus Torvalds 2921da177e4SLinus Torvalds static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos) 2931da177e4SLinus Torvalds { 2941da177e4SLinus Torvalds (*_pos)++; 2959a56c2dbSEric W. Biederman return key_user_next(seq_user_ns(p), (struct rb_node *)v); 2961da177e4SLinus Torvalds } 2971da177e4SLinus Torvalds 2981da177e4SLinus Torvalds static void proc_key_users_stop(struct seq_file *p, void *v) 29986abcf9cSJames Morris __releases(key_user_lock) 3001da177e4SLinus Torvalds { 3011da177e4SLinus Torvalds spin_unlock(&key_user_lock); 3021da177e4SLinus Torvalds } 3031da177e4SLinus Torvalds 3041da177e4SLinus Torvalds static int proc_key_users_show(struct seq_file *m, void *v) 3051da177e4SLinus Torvalds { 3061da177e4SLinus Torvalds struct rb_node *_p = v; 3071da177e4SLinus Torvalds struct key_user *user = rb_entry(_p, struct key_user, node); 3089a56c2dbSEric W. Biederman unsigned maxkeys = uid_eq(user->uid, GLOBAL_ROOT_UID) ? 3090b77f5bfSDavid Howells key_quota_root_maxkeys : key_quota_maxkeys; 3109a56c2dbSEric W. Biederman unsigned maxbytes = uid_eq(user->uid, GLOBAL_ROOT_UID) ? 3110b77f5bfSDavid Howells key_quota_root_maxbytes : key_quota_maxbytes; 3121da177e4SLinus Torvalds 3131da177e4SLinus Torvalds seq_printf(m, "%5u: %5d %d/%d %d/%d %d/%d\n", 3149a56c2dbSEric W. Biederman from_kuid_munged(seq_user_ns(m), user->uid), 315ddb99e11SElena Reshetova refcount_read(&user->usage), 3161da177e4SLinus Torvalds atomic_read(&user->nkeys), 3171da177e4SLinus Torvalds atomic_read(&user->nikeys), 3181da177e4SLinus Torvalds user->qnkeys, 3190b77f5bfSDavid Howells maxkeys, 3201da177e4SLinus Torvalds user->qnbytes, 3210b77f5bfSDavid Howells maxbytes); 3221da177e4SLinus Torvalds 3231da177e4SLinus Torvalds return 0; 3241da177e4SLinus Torvalds } 325