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]; 18506ec7be5SMichael LeMay int rc; 18606ec7be5SMichael LeMay 1874bdf0bc3SDavid Howells struct keyring_search_context ctx = { 1884bdf0bc3SDavid Howells .index_key.type = key->type, 1894bdf0bc3SDavid Howells .index_key.description = key->description, 1904bdf0bc3SDavid Howells .cred = current_cred(), 19146291959SDavid Howells .match_data.cmp = lookup_user_key_possessed, 19246291959SDavid Howells .match_data.raw_data = key, 19346291959SDavid Howells .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, 19446291959SDavid Howells .flags = KEYRING_SEARCH_NO_STATE_CHECK, 1954bdf0bc3SDavid Howells }; 1964bdf0bc3SDavid Howells 197927942aaSDavid Howells key_ref = make_key_ref(key, 0); 198927942aaSDavid Howells 199927942aaSDavid Howells /* determine if the key is possessed by this process (a test we can 200927942aaSDavid Howells * skip if the key does not indicate the possessor can view it 201927942aaSDavid Howells */ 202927942aaSDavid Howells if (key->perm & KEY_POS_VIEW) { 2034bdf0bc3SDavid Howells skey_ref = search_my_process_keyrings(&ctx); 204927942aaSDavid Howells if (!IS_ERR(skey_ref)) { 205927942aaSDavid Howells key_ref_put(skey_ref); 206927942aaSDavid Howells key_ref = make_key_ref(key, 1); 207927942aaSDavid Howells } 208927942aaSDavid Howells } 209927942aaSDavid Howells 21006ec7be5SMichael LeMay /* check whether the current task is allowed to view the key (assuming 211d84f4f99SDavid Howells * non-possession) 212d84f4f99SDavid Howells * - the caller holds a spinlock, and thus the RCU read lock, making our 213d84f4f99SDavid Howells * access to __current_cred() safe 214d84f4f99SDavid Howells */ 215f5895943SDavid Howells rc = key_task_permission(key_ref, ctx.cred, KEY_NEED_VIEW); 21606ec7be5SMichael LeMay if (rc < 0) 21706ec7be5SMichael LeMay return 0; 2181da177e4SLinus Torvalds 2191da177e4SLinus Torvalds now = current_kernel_time(); 2201da177e4SLinus Torvalds 22176d8aeabSDavid Howells rcu_read_lock(); 2221da177e4SLinus Torvalds 2231da177e4SLinus Torvalds /* come up with a suitable timeout value */ 2241da177e4SLinus Torvalds if (key->expiry == 0) { 2251da177e4SLinus Torvalds memcpy(xbuf, "perm", 5); 2267b1b9164SDavid Howells } else if (now.tv_sec >= key->expiry) { 2271da177e4SLinus Torvalds memcpy(xbuf, "expd", 5); 2287b1b9164SDavid Howells } else { 2291da177e4SLinus Torvalds timo = key->expiry - now.tv_sec; 2301da177e4SLinus Torvalds 2311da177e4SLinus Torvalds if (timo < 60) 2321da177e4SLinus Torvalds sprintf(xbuf, "%lus", timo); 2331da177e4SLinus Torvalds else if (timo < 60*60) 2341da177e4SLinus Torvalds sprintf(xbuf, "%lum", timo / 60); 2351da177e4SLinus Torvalds else if (timo < 60*60*24) 2361da177e4SLinus Torvalds sprintf(xbuf, "%luh", timo / (60*60)); 2371da177e4SLinus Torvalds else if (timo < 60*60*24*7) 2381da177e4SLinus Torvalds sprintf(xbuf, "%lud", timo / (60*60*24)); 2391da177e4SLinus Torvalds else 2401da177e4SLinus Torvalds sprintf(xbuf, "%luw", timo / (60*60*24*7)); 2411da177e4SLinus Torvalds } 2421da177e4SLinus Torvalds 24376d8aeabSDavid Howells #define showflag(KEY, LETTER, FLAG) \ 24476d8aeabSDavid Howells (test_bit(FLAG, &(KEY)->flags) ? LETTER : '-') 24576d8aeabSDavid Howells 246fd75815fSDavid Howells seq_printf(m, "%08x %c%c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ", 2471da177e4SLinus Torvalds key->serial, 24876d8aeabSDavid Howells showflag(key, 'I', KEY_FLAG_INSTANTIATED), 24976d8aeabSDavid Howells showflag(key, 'R', KEY_FLAG_REVOKED), 25076d8aeabSDavid Howells showflag(key, 'D', KEY_FLAG_DEAD), 25176d8aeabSDavid Howells showflag(key, 'Q', KEY_FLAG_IN_QUOTA), 25276d8aeabSDavid Howells showflag(key, 'U', KEY_FLAG_USER_CONSTRUCT), 25376d8aeabSDavid Howells showflag(key, 'N', KEY_FLAG_NEGATIVE), 254fd75815fSDavid Howells showflag(key, 'i', KEY_FLAG_INVALIDATED), 255fff29291SElena Reshetova refcount_read(&key->usage), 2561da177e4SLinus Torvalds xbuf, 2571da177e4SLinus Torvalds key->perm, 2589a56c2dbSEric W. Biederman from_kuid_munged(seq_user_ns(m), key->uid), 2599a56c2dbSEric W. Biederman from_kgid_munged(seq_user_ns(m), key->gid), 2601da177e4SLinus Torvalds key->type->name); 2611da177e4SLinus Torvalds 26276d8aeabSDavid Howells #undef showflag 26376d8aeabSDavid Howells 2641da177e4SLinus Torvalds if (key->type->describe) 2651da177e4SLinus Torvalds key->type->describe(key, m); 2661da177e4SLinus Torvalds seq_putc(m, '\n'); 2671da177e4SLinus Torvalds 26876d8aeabSDavid Howells rcu_read_unlock(); 2691da177e4SLinus Torvalds return 0; 2701da177e4SLinus Torvalds } 2711da177e4SLinus Torvalds 2729a56c2dbSEric W. Biederman static struct rb_node *__key_user_next(struct user_namespace *user_ns, struct rb_node *n) 273454804abSSerge E. Hallyn { 274454804abSSerge E. Hallyn while (n) { 275454804abSSerge E. Hallyn struct key_user *user = rb_entry(n, struct key_user, node); 2769a56c2dbSEric W. Biederman if (kuid_has_mapping(user_ns, user->uid)) 277454804abSSerge E. Hallyn break; 278454804abSSerge E. Hallyn n = rb_next(n); 279454804abSSerge E. Hallyn } 280454804abSSerge E. Hallyn return n; 281454804abSSerge E. Hallyn } 282454804abSSerge E. Hallyn 2839a56c2dbSEric W. Biederman static struct rb_node *key_user_next(struct user_namespace *user_ns, struct rb_node *n) 284454804abSSerge E. Hallyn { 2859a56c2dbSEric W. Biederman return __key_user_next(user_ns, rb_next(n)); 286454804abSSerge E. Hallyn } 287454804abSSerge E. Hallyn 2889a56c2dbSEric W. Biederman static struct rb_node *key_user_first(struct user_namespace *user_ns, struct rb_root *r) 289454804abSSerge E. Hallyn { 290454804abSSerge E. Hallyn struct rb_node *n = rb_first(r); 2919a56c2dbSEric W. Biederman return __key_user_next(user_ns, n); 292454804abSSerge E. Hallyn } 2937b1b9164SDavid Howells 2941da177e4SLinus Torvalds /* 295973c9f4fSDavid Howells * Implement "/proc/key-users" to provides a list of the key users and their 296973c9f4fSDavid Howells * quotas. 2971da177e4SLinus Torvalds */ 2981da177e4SLinus Torvalds static int proc_key_users_open(struct inode *inode, struct file *file) 2991da177e4SLinus Torvalds { 3001da177e4SLinus Torvalds return seq_open(file, &proc_key_users_ops); 3011da177e4SLinus Torvalds } 3021da177e4SLinus Torvalds 3031da177e4SLinus Torvalds static void *proc_key_users_start(struct seq_file *p, loff_t *_pos) 30486abcf9cSJames Morris __acquires(key_user_lock) 3051da177e4SLinus Torvalds { 3061da177e4SLinus Torvalds struct rb_node *_p; 3071da177e4SLinus Torvalds loff_t pos = *_pos; 3081da177e4SLinus Torvalds 3091da177e4SLinus Torvalds spin_lock(&key_user_lock); 3101da177e4SLinus Torvalds 3119a56c2dbSEric W. Biederman _p = key_user_first(seq_user_ns(p), &key_user_tree); 3121da177e4SLinus Torvalds while (pos > 0 && _p) { 3131da177e4SLinus Torvalds pos--; 3149a56c2dbSEric W. Biederman _p = key_user_next(seq_user_ns(p), _p); 3151da177e4SLinus Torvalds } 3161da177e4SLinus Torvalds 3171da177e4SLinus Torvalds return _p; 3181da177e4SLinus Torvalds } 3191da177e4SLinus Torvalds 3201da177e4SLinus Torvalds static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos) 3211da177e4SLinus Torvalds { 3221da177e4SLinus Torvalds (*_pos)++; 3239a56c2dbSEric W. Biederman return key_user_next(seq_user_ns(p), (struct rb_node *)v); 3241da177e4SLinus Torvalds } 3251da177e4SLinus Torvalds 3261da177e4SLinus Torvalds static void proc_key_users_stop(struct seq_file *p, void *v) 32786abcf9cSJames Morris __releases(key_user_lock) 3281da177e4SLinus Torvalds { 3291da177e4SLinus Torvalds spin_unlock(&key_user_lock); 3301da177e4SLinus Torvalds } 3311da177e4SLinus Torvalds 3321da177e4SLinus Torvalds static int proc_key_users_show(struct seq_file *m, void *v) 3331da177e4SLinus Torvalds { 3341da177e4SLinus Torvalds struct rb_node *_p = v; 3351da177e4SLinus Torvalds struct key_user *user = rb_entry(_p, struct key_user, node); 3369a56c2dbSEric W. Biederman unsigned maxkeys = uid_eq(user->uid, GLOBAL_ROOT_UID) ? 3370b77f5bfSDavid Howells key_quota_root_maxkeys : key_quota_maxkeys; 3389a56c2dbSEric W. Biederman unsigned maxbytes = uid_eq(user->uid, GLOBAL_ROOT_UID) ? 3390b77f5bfSDavid Howells key_quota_root_maxbytes : key_quota_maxbytes; 3401da177e4SLinus Torvalds 3411da177e4SLinus Torvalds seq_printf(m, "%5u: %5d %d/%d %d/%d %d/%d\n", 3429a56c2dbSEric W. Biederman from_kuid_munged(seq_user_ns(m), user->uid), 343*ddb99e11SElena Reshetova refcount_read(&user->usage), 3441da177e4SLinus Torvalds atomic_read(&user->nkeys), 3451da177e4SLinus Torvalds atomic_read(&user->nikeys), 3461da177e4SLinus Torvalds user->qnkeys, 3470b77f5bfSDavid Howells maxkeys, 3481da177e4SLinus Torvalds user->qnbytes, 3490b77f5bfSDavid Howells maxbytes); 3501da177e4SLinus Torvalds 3511da177e4SLinus Torvalds return 0; 3521da177e4SLinus Torvalds } 353