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/module.h> 131da177e4SLinus Torvalds #include <linux/init.h> 141da177e4SLinus Torvalds #include <linux/sched.h> 151da177e4SLinus Torvalds #include <linux/fs.h> 161da177e4SLinus Torvalds #include <linux/proc_fs.h> 171da177e4SLinus Torvalds #include <linux/seq_file.h> 181da177e4SLinus Torvalds #include <asm/errno.h> 191da177e4SLinus Torvalds #include "internal.h" 201da177e4SLinus Torvalds 211da177e4SLinus Torvalds static int proc_keys_open(struct inode *inode, struct file *file); 221da177e4SLinus Torvalds static void *proc_keys_start(struct seq_file *p, loff_t *_pos); 231da177e4SLinus Torvalds static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos); 241da177e4SLinus Torvalds static void proc_keys_stop(struct seq_file *p, void *v); 251da177e4SLinus Torvalds static int proc_keys_show(struct seq_file *m, void *v); 261da177e4SLinus Torvalds 271996a109SJan Engelhardt static const struct seq_operations proc_keys_ops = { 281da177e4SLinus Torvalds .start = proc_keys_start, 291da177e4SLinus Torvalds .next = proc_keys_next, 301da177e4SLinus Torvalds .stop = proc_keys_stop, 311da177e4SLinus Torvalds .show = proc_keys_show, 321da177e4SLinus Torvalds }; 331da177e4SLinus Torvalds 349c2e08c5SArjan van de Ven static const struct file_operations proc_keys_fops = { 351da177e4SLinus Torvalds .open = proc_keys_open, 361da177e4SLinus Torvalds .read = seq_read, 371da177e4SLinus Torvalds .llseek = seq_lseek, 381da177e4SLinus Torvalds .release = seq_release, 391da177e4SLinus Torvalds }; 401da177e4SLinus Torvalds 411da177e4SLinus Torvalds static int proc_key_users_open(struct inode *inode, struct file *file); 421da177e4SLinus Torvalds static void *proc_key_users_start(struct seq_file *p, loff_t *_pos); 431da177e4SLinus Torvalds static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos); 441da177e4SLinus Torvalds static void proc_key_users_stop(struct seq_file *p, void *v); 451da177e4SLinus Torvalds static int proc_key_users_show(struct seq_file *m, void *v); 461da177e4SLinus Torvalds 471996a109SJan Engelhardt static const struct seq_operations proc_key_users_ops = { 481da177e4SLinus Torvalds .start = proc_key_users_start, 491da177e4SLinus Torvalds .next = proc_key_users_next, 501da177e4SLinus Torvalds .stop = proc_key_users_stop, 511da177e4SLinus Torvalds .show = proc_key_users_show, 521da177e4SLinus Torvalds }; 531da177e4SLinus Torvalds 549c2e08c5SArjan van de Ven static const struct file_operations proc_key_users_fops = { 551da177e4SLinus Torvalds .open = proc_key_users_open, 561da177e4SLinus Torvalds .read = seq_read, 571da177e4SLinus Torvalds .llseek = seq_lseek, 581da177e4SLinus Torvalds .release = seq_release, 591da177e4SLinus Torvalds }; 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds /* 62973c9f4fSDavid Howells * Declare the /proc files. 631da177e4SLinus Torvalds */ 641da177e4SLinus Torvalds static int __init key_proc_init(void) 651da177e4SLinus Torvalds { 661da177e4SLinus Torvalds struct proc_dir_entry *p; 671da177e4SLinus Torvalds 68da91d2efSAlexey Dobriyan p = proc_create("keys", 0, NULL, &proc_keys_fops); 691da177e4SLinus Torvalds if (!p) 701da177e4SLinus Torvalds panic("Cannot create /proc/keys\n"); 711da177e4SLinus Torvalds 72da91d2efSAlexey Dobriyan p = proc_create("key-users", 0, NULL, &proc_key_users_fops); 731da177e4SLinus Torvalds if (!p) 741da177e4SLinus Torvalds panic("Cannot create /proc/key-users\n"); 751da177e4SLinus Torvalds 761da177e4SLinus Torvalds return 0; 77a8b17ed0SDavid Howells } 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds __initcall(key_proc_init); 801da177e4SLinus Torvalds 811da177e4SLinus Torvalds /* 82973c9f4fSDavid Howells * Implement "/proc/keys" to provide a list of the keys on the system that 83973c9f4fSDavid Howells * grant View permission to the caller. 841da177e4SLinus Torvalds */ 859a56c2dbSEric W. Biederman static struct rb_node *key_serial_next(struct seq_file *p, struct rb_node *n) 86454804abSSerge E. Hallyn { 879a56c2dbSEric W. Biederman struct user_namespace *user_ns = seq_user_ns(p); 88ad73a717SSerge E. Hallyn 89ad73a717SSerge E. Hallyn n = rb_next(n); 90454804abSSerge E. Hallyn while (n) { 91454804abSSerge E. Hallyn struct key *key = rb_entry(n, struct key, serial_node); 929a56c2dbSEric W. Biederman if (kuid_has_mapping(user_ns, key->user->uid)) 93454804abSSerge E. Hallyn break; 94454804abSSerge E. Hallyn n = rb_next(n); 95454804abSSerge E. Hallyn } 96454804abSSerge E. Hallyn return n; 97454804abSSerge E. Hallyn } 98454804abSSerge E. Hallyn 991da177e4SLinus Torvalds static int proc_keys_open(struct inode *inode, struct file *file) 1001da177e4SLinus Torvalds { 1011da177e4SLinus Torvalds return seq_open(file, &proc_keys_ops); 102ad73a717SSerge E. Hallyn } 1031da177e4SLinus Torvalds 1049a56c2dbSEric W. Biederman static struct key *find_ge_key(struct seq_file *p, key_serial_t id) 105ad73a717SSerge E. Hallyn { 1069a56c2dbSEric W. Biederman struct user_namespace *user_ns = seq_user_ns(p); 107ad73a717SSerge E. Hallyn struct rb_node *n = key_serial_tree.rb_node; 108ad73a717SSerge E. Hallyn struct key *minkey = NULL; 109ad73a717SSerge E. Hallyn 110ad73a717SSerge E. Hallyn while (n) { 111ad73a717SSerge E. Hallyn struct key *key = rb_entry(n, struct key, serial_node); 112ad73a717SSerge E. Hallyn if (id < key->serial) { 113ad73a717SSerge E. Hallyn if (!minkey || minkey->serial > key->serial) 114ad73a717SSerge E. Hallyn minkey = key; 115ad73a717SSerge E. Hallyn n = n->rb_left; 116ad73a717SSerge E. Hallyn } else if (id > key->serial) { 117ad73a717SSerge E. Hallyn n = n->rb_right; 118ad73a717SSerge E. Hallyn } else { 119ad73a717SSerge E. Hallyn minkey = key; 120ad73a717SSerge E. Hallyn break; 121ad73a717SSerge E. Hallyn } 122ad73a717SSerge E. Hallyn key = NULL; 123ad73a717SSerge E. Hallyn } 124ad73a717SSerge E. Hallyn 125ad73a717SSerge E. Hallyn if (!minkey) 126ad73a717SSerge E. Hallyn return NULL; 127ad73a717SSerge E. Hallyn 128ad73a717SSerge E. Hallyn for (;;) { 1299a56c2dbSEric W. Biederman if (kuid_has_mapping(user_ns, minkey->user->uid)) 130ad73a717SSerge E. Hallyn return minkey; 131ad73a717SSerge E. Hallyn n = rb_next(&minkey->serial_node); 132ad73a717SSerge E. Hallyn if (!n) 133ad73a717SSerge E. Hallyn return NULL; 134ad73a717SSerge E. Hallyn minkey = rb_entry(n, struct key, serial_node); 135ad73a717SSerge E. Hallyn } 1361da177e4SLinus Torvalds } 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds static void *proc_keys_start(struct seq_file *p, loff_t *_pos) 13986abcf9cSJames Morris __acquires(key_serial_lock) 1401da177e4SLinus Torvalds { 141ad73a717SSerge E. Hallyn key_serial_t pos = *_pos; 142ad73a717SSerge E. Hallyn struct key *key; 1431da177e4SLinus Torvalds 1441da177e4SLinus Torvalds spin_lock(&key_serial_lock); 1451da177e4SLinus Torvalds 146ad73a717SSerge E. Hallyn if (*_pos > INT_MAX) 147ad73a717SSerge E. Hallyn return NULL; 1489a56c2dbSEric W. Biederman key = find_ge_key(p, pos); 149ad73a717SSerge E. Hallyn if (!key) 150ad73a717SSerge E. Hallyn return NULL; 151ad73a717SSerge E. Hallyn *_pos = key->serial; 152ad73a717SSerge E. Hallyn return &key->serial_node; 1531da177e4SLinus Torvalds } 1541da177e4SLinus Torvalds 155ad73a717SSerge E. Hallyn static inline key_serial_t key_node_serial(struct rb_node *n) 156ad73a717SSerge E. Hallyn { 157ad73a717SSerge E. Hallyn struct key *key = rb_entry(n, struct key, serial_node); 158ad73a717SSerge E. Hallyn return key->serial; 1591da177e4SLinus Torvalds } 1601da177e4SLinus Torvalds 1611da177e4SLinus Torvalds static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos) 1621da177e4SLinus Torvalds { 163ad73a717SSerge E. Hallyn struct rb_node *n; 1641da177e4SLinus Torvalds 1659a56c2dbSEric W. Biederman n = key_serial_next(p, v); 166ad73a717SSerge E. Hallyn if (n) 167ad73a717SSerge E. Hallyn *_pos = key_node_serial(n); 168ad73a717SSerge E. Hallyn return n; 1691da177e4SLinus Torvalds } 1701da177e4SLinus Torvalds 1711da177e4SLinus Torvalds static void proc_keys_stop(struct seq_file *p, void *v) 17286abcf9cSJames Morris __releases(key_serial_lock) 1731da177e4SLinus Torvalds { 1741da177e4SLinus Torvalds spin_unlock(&key_serial_lock); 1751da177e4SLinus Torvalds } 1761da177e4SLinus Torvalds 1771da177e4SLinus Torvalds static int proc_keys_show(struct seq_file *m, void *v) 1781da177e4SLinus Torvalds { 1791da177e4SLinus Torvalds struct rb_node *_p = v; 1801da177e4SLinus Torvalds struct key *key = rb_entry(_p, struct key, serial_node); 1811da177e4SLinus Torvalds struct timespec now; 1821da177e4SLinus Torvalds unsigned long timo; 183927942aaSDavid Howells key_ref_t key_ref, skey_ref; 18403dab869SDavid Howells char xbuf[16]; 185*363b02daSDavid Howells short state; 18606ec7be5SMichael LeMay int rc; 18706ec7be5SMichael LeMay 1884bdf0bc3SDavid Howells struct keyring_search_context ctx = { 1894bdf0bc3SDavid Howells .index_key.type = key->type, 1904bdf0bc3SDavid Howells .index_key.description = key->description, 1914aa68e07SEric Biggers .cred = m->file->f_cred, 19246291959SDavid Howells .match_data.cmp = lookup_user_key_possessed, 19346291959SDavid Howells .match_data.raw_data = key, 19446291959SDavid Howells .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, 19546291959SDavid Howells .flags = KEYRING_SEARCH_NO_STATE_CHECK, 1964bdf0bc3SDavid Howells }; 1974bdf0bc3SDavid Howells 198927942aaSDavid Howells key_ref = make_key_ref(key, 0); 199927942aaSDavid Howells 200927942aaSDavid Howells /* determine if the key is possessed by this process (a test we can 201927942aaSDavid Howells * skip if the key does not indicate the possessor can view it 202927942aaSDavid Howells */ 203927942aaSDavid Howells if (key->perm & KEY_POS_VIEW) { 2044bdf0bc3SDavid Howells skey_ref = search_my_process_keyrings(&ctx); 205927942aaSDavid Howells if (!IS_ERR(skey_ref)) { 206927942aaSDavid Howells key_ref_put(skey_ref); 207927942aaSDavid Howells key_ref = make_key_ref(key, 1); 208927942aaSDavid Howells } 209927942aaSDavid Howells } 210927942aaSDavid Howells 2114aa68e07SEric Biggers /* check whether the current task is allowed to view the key */ 212f5895943SDavid Howells rc = key_task_permission(key_ref, ctx.cred, KEY_NEED_VIEW); 21306ec7be5SMichael LeMay if (rc < 0) 21406ec7be5SMichael LeMay return 0; 2151da177e4SLinus Torvalds 2161da177e4SLinus Torvalds now = current_kernel_time(); 2171da177e4SLinus Torvalds 21876d8aeabSDavid Howells rcu_read_lock(); 2191da177e4SLinus Torvalds 2201da177e4SLinus Torvalds /* come up with a suitable timeout value */ 2211da177e4SLinus Torvalds if (key->expiry == 0) { 2221da177e4SLinus Torvalds memcpy(xbuf, "perm", 5); 2237b1b9164SDavid Howells } else if (now.tv_sec >= key->expiry) { 2241da177e4SLinus Torvalds memcpy(xbuf, "expd", 5); 2257b1b9164SDavid Howells } else { 2261da177e4SLinus Torvalds timo = key->expiry - now.tv_sec; 2271da177e4SLinus Torvalds 2281da177e4SLinus Torvalds if (timo < 60) 2291da177e4SLinus Torvalds sprintf(xbuf, "%lus", timo); 2301da177e4SLinus Torvalds else if (timo < 60*60) 2311da177e4SLinus Torvalds sprintf(xbuf, "%lum", timo / 60); 2321da177e4SLinus Torvalds else if (timo < 60*60*24) 2331da177e4SLinus Torvalds sprintf(xbuf, "%luh", timo / (60*60)); 2341da177e4SLinus Torvalds else if (timo < 60*60*24*7) 2351da177e4SLinus Torvalds sprintf(xbuf, "%lud", timo / (60*60*24)); 2361da177e4SLinus Torvalds else 2371da177e4SLinus Torvalds sprintf(xbuf, "%luw", timo / (60*60*24*7)); 2381da177e4SLinus Torvalds } 2391da177e4SLinus Torvalds 240*363b02daSDavid Howells state = key_read_state(key); 241*363b02daSDavid Howells 24276d8aeabSDavid Howells #define showflag(KEY, LETTER, FLAG) \ 24376d8aeabSDavid Howells (test_bit(FLAG, &(KEY)->flags) ? LETTER : '-') 24476d8aeabSDavid Howells 245fd75815fSDavid Howells seq_printf(m, "%08x %c%c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ", 2461da177e4SLinus Torvalds key->serial, 247*363b02daSDavid Howells state != KEY_IS_UNINSTANTIATED ? 'I' : '-', 24876d8aeabSDavid Howells showflag(key, 'R', KEY_FLAG_REVOKED), 24976d8aeabSDavid Howells showflag(key, 'D', KEY_FLAG_DEAD), 25076d8aeabSDavid Howells showflag(key, 'Q', KEY_FLAG_IN_QUOTA), 25176d8aeabSDavid Howells showflag(key, 'U', KEY_FLAG_USER_CONSTRUCT), 252*363b02daSDavid Howells state < 0 ? 'N' : '-', 253fd75815fSDavid Howells showflag(key, 'i', KEY_FLAG_INVALIDATED), 254fff29291SElena Reshetova refcount_read(&key->usage), 2551da177e4SLinus Torvalds xbuf, 2561da177e4SLinus Torvalds key->perm, 2579a56c2dbSEric W. Biederman from_kuid_munged(seq_user_ns(m), key->uid), 2589a56c2dbSEric W. Biederman from_kgid_munged(seq_user_ns(m), key->gid), 2591da177e4SLinus Torvalds key->type->name); 2601da177e4SLinus Torvalds 26176d8aeabSDavid Howells #undef showflag 26276d8aeabSDavid Howells 2631da177e4SLinus Torvalds if (key->type->describe) 2641da177e4SLinus Torvalds key->type->describe(key, m); 2651da177e4SLinus Torvalds seq_putc(m, '\n'); 2661da177e4SLinus Torvalds 26776d8aeabSDavid Howells rcu_read_unlock(); 2681da177e4SLinus Torvalds return 0; 2691da177e4SLinus Torvalds } 2701da177e4SLinus Torvalds 2719a56c2dbSEric W. Biederman static struct rb_node *__key_user_next(struct user_namespace *user_ns, struct rb_node *n) 272454804abSSerge E. Hallyn { 273454804abSSerge E. Hallyn while (n) { 274454804abSSerge E. Hallyn struct key_user *user = rb_entry(n, struct key_user, node); 2759a56c2dbSEric W. Biederman if (kuid_has_mapping(user_ns, user->uid)) 276454804abSSerge E. Hallyn break; 277454804abSSerge E. Hallyn n = rb_next(n); 278454804abSSerge E. Hallyn } 279454804abSSerge E. Hallyn return n; 280454804abSSerge E. Hallyn } 281454804abSSerge E. Hallyn 2829a56c2dbSEric W. Biederman static struct rb_node *key_user_next(struct user_namespace *user_ns, struct rb_node *n) 283454804abSSerge E. Hallyn { 2849a56c2dbSEric W. Biederman return __key_user_next(user_ns, rb_next(n)); 285454804abSSerge E. Hallyn } 286454804abSSerge E. Hallyn 2879a56c2dbSEric W. Biederman static struct rb_node *key_user_first(struct user_namespace *user_ns, struct rb_root *r) 288454804abSSerge E. Hallyn { 289454804abSSerge E. Hallyn struct rb_node *n = rb_first(r); 2909a56c2dbSEric W. Biederman return __key_user_next(user_ns, n); 291454804abSSerge E. Hallyn } 2927b1b9164SDavid Howells 2931da177e4SLinus Torvalds /* 294973c9f4fSDavid Howells * Implement "/proc/key-users" to provides a list of the key users and their 295973c9f4fSDavid Howells * quotas. 2961da177e4SLinus Torvalds */ 2971da177e4SLinus Torvalds static int proc_key_users_open(struct inode *inode, struct file *file) 2981da177e4SLinus Torvalds { 2991da177e4SLinus Torvalds return seq_open(file, &proc_key_users_ops); 3001da177e4SLinus Torvalds } 3011da177e4SLinus Torvalds 3021da177e4SLinus Torvalds static void *proc_key_users_start(struct seq_file *p, loff_t *_pos) 30386abcf9cSJames Morris __acquires(key_user_lock) 3041da177e4SLinus Torvalds { 3051da177e4SLinus Torvalds struct rb_node *_p; 3061da177e4SLinus Torvalds loff_t pos = *_pos; 3071da177e4SLinus Torvalds 3081da177e4SLinus Torvalds spin_lock(&key_user_lock); 3091da177e4SLinus Torvalds 3109a56c2dbSEric W. Biederman _p = key_user_first(seq_user_ns(p), &key_user_tree); 3111da177e4SLinus Torvalds while (pos > 0 && _p) { 3121da177e4SLinus Torvalds pos--; 3139a56c2dbSEric W. Biederman _p = key_user_next(seq_user_ns(p), _p); 3141da177e4SLinus Torvalds } 3151da177e4SLinus Torvalds 3161da177e4SLinus Torvalds return _p; 3171da177e4SLinus Torvalds } 3181da177e4SLinus Torvalds 3191da177e4SLinus Torvalds static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos) 3201da177e4SLinus Torvalds { 3211da177e4SLinus Torvalds (*_pos)++; 3229a56c2dbSEric W. Biederman return key_user_next(seq_user_ns(p), (struct rb_node *)v); 3231da177e4SLinus Torvalds } 3241da177e4SLinus Torvalds 3251da177e4SLinus Torvalds static void proc_key_users_stop(struct seq_file *p, void *v) 32686abcf9cSJames Morris __releases(key_user_lock) 3271da177e4SLinus Torvalds { 3281da177e4SLinus Torvalds spin_unlock(&key_user_lock); 3291da177e4SLinus Torvalds } 3301da177e4SLinus Torvalds 3311da177e4SLinus Torvalds static int proc_key_users_show(struct seq_file *m, void *v) 3321da177e4SLinus Torvalds { 3331da177e4SLinus Torvalds struct rb_node *_p = v; 3341da177e4SLinus Torvalds struct key_user *user = rb_entry(_p, struct key_user, node); 3359a56c2dbSEric W. Biederman unsigned maxkeys = uid_eq(user->uid, GLOBAL_ROOT_UID) ? 3360b77f5bfSDavid Howells key_quota_root_maxkeys : key_quota_maxkeys; 3379a56c2dbSEric W. Biederman unsigned maxbytes = uid_eq(user->uid, GLOBAL_ROOT_UID) ? 3380b77f5bfSDavid Howells key_quota_root_maxbytes : key_quota_maxbytes; 3391da177e4SLinus Torvalds 3401da177e4SLinus Torvalds seq_printf(m, "%5u: %5d %d/%d %d/%d %d/%d\n", 3419a56c2dbSEric W. Biederman from_kuid_munged(seq_user_ns(m), user->uid), 342ddb99e11SElena Reshetova refcount_read(&user->usage), 3431da177e4SLinus Torvalds atomic_read(&user->nkeys), 3441da177e4SLinus Torvalds atomic_read(&user->nikeys), 3451da177e4SLinus Torvalds user->qnkeys, 3460b77f5bfSDavid Howells maxkeys, 3471da177e4SLinus Torvalds user->qnbytes, 3480b77f5bfSDavid Howells maxbytes); 3491da177e4SLinus Torvalds 3501da177e4SLinus Torvalds return 0; 3511da177e4SLinus Torvalds } 352