|  | /* procfs files for key database enumeration | 
|  | * | 
|  | * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. | 
|  | * Written by David Howells (dhowells@redhat.com) | 
|  | * | 
|  | * This program is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU General Public License | 
|  | * as published by the Free Software Foundation; either version | 
|  | * 2 of the License, or (at your option) any later version. | 
|  | */ | 
|  |  | 
|  | #include <linux/module.h> | 
|  | #include <linux/init.h> | 
|  | #include <linux/sched.h> | 
|  | #include <linux/fs.h> | 
|  | #include <linux/proc_fs.h> | 
|  | #include <linux/seq_file.h> | 
|  | #include <asm/errno.h> | 
|  | #include "internal.h" | 
|  |  | 
|  | #ifdef CONFIG_KEYS_DEBUG_PROC_KEYS | 
|  | static int proc_keys_open(struct inode *inode, struct file *file); | 
|  | static void *proc_keys_start(struct seq_file *p, loff_t *_pos); | 
|  | static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos); | 
|  | static void proc_keys_stop(struct seq_file *p, void *v); | 
|  | static int proc_keys_show(struct seq_file *m, void *v); | 
|  |  | 
|  | static const struct seq_operations proc_keys_ops = { | 
|  | .start	= proc_keys_start, | 
|  | .next	= proc_keys_next, | 
|  | .stop	= proc_keys_stop, | 
|  | .show	= proc_keys_show, | 
|  | }; | 
|  |  | 
|  | static const struct file_operations proc_keys_fops = { | 
|  | .open		= proc_keys_open, | 
|  | .read		= seq_read, | 
|  | .llseek		= seq_lseek, | 
|  | .release	= seq_release, | 
|  | }; | 
|  | #endif | 
|  |  | 
|  | static int proc_key_users_open(struct inode *inode, struct file *file); | 
|  | static void *proc_key_users_start(struct seq_file *p, loff_t *_pos); | 
|  | static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos); | 
|  | static void proc_key_users_stop(struct seq_file *p, void *v); | 
|  | static int proc_key_users_show(struct seq_file *m, void *v); | 
|  |  | 
|  | static const struct seq_operations proc_key_users_ops = { | 
|  | .start	= proc_key_users_start, | 
|  | .next	= proc_key_users_next, | 
|  | .stop	= proc_key_users_stop, | 
|  | .show	= proc_key_users_show, | 
|  | }; | 
|  |  | 
|  | static const struct file_operations proc_key_users_fops = { | 
|  | .open		= proc_key_users_open, | 
|  | .read		= seq_read, | 
|  | .llseek		= seq_lseek, | 
|  | .release	= seq_release, | 
|  | }; | 
|  |  | 
|  | /* | 
|  | * Declare the /proc files. | 
|  | */ | 
|  | static int __init key_proc_init(void) | 
|  | { | 
|  | struct proc_dir_entry *p; | 
|  |  | 
|  | #ifdef CONFIG_KEYS_DEBUG_PROC_KEYS | 
|  | p = proc_create("keys", 0, NULL, &proc_keys_fops); | 
|  | if (!p) | 
|  | panic("Cannot create /proc/keys\n"); | 
|  | #endif | 
|  |  | 
|  | p = proc_create("key-users", 0, NULL, &proc_key_users_fops); | 
|  | if (!p) | 
|  | panic("Cannot create /proc/key-users\n"); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | __initcall(key_proc_init); | 
|  |  | 
|  | /* | 
|  | * Implement "/proc/keys" to provide a list of the keys on the system that | 
|  | * grant View permission to the caller. | 
|  | */ | 
|  | #ifdef CONFIG_KEYS_DEBUG_PROC_KEYS | 
|  |  | 
|  | static struct rb_node *key_serial_next(struct rb_node *n) | 
|  | { | 
|  | struct user_namespace *user_ns = current_user_ns(); | 
|  |  | 
|  | n = rb_next(n); | 
|  | while (n) { | 
|  | struct key *key = rb_entry(n, struct key, serial_node); | 
|  | if (key->user->user_ns == user_ns) | 
|  | break; | 
|  | n = rb_next(n); | 
|  | } | 
|  | return n; | 
|  | } | 
|  |  | 
|  | static int proc_keys_open(struct inode *inode, struct file *file) | 
|  | { | 
|  | return seq_open(file, &proc_keys_ops); | 
|  | } | 
|  |  | 
|  | static struct key *find_ge_key(key_serial_t id) | 
|  | { | 
|  | struct user_namespace *user_ns = current_user_ns(); | 
|  | struct rb_node *n = key_serial_tree.rb_node; | 
|  | struct key *minkey = NULL; | 
|  |  | 
|  | while (n) { | 
|  | struct key *key = rb_entry(n, struct key, serial_node); | 
|  | if (id < key->serial) { | 
|  | if (!minkey || minkey->serial > key->serial) | 
|  | minkey = key; | 
|  | n = n->rb_left; | 
|  | } else if (id > key->serial) { | 
|  | n = n->rb_right; | 
|  | } else { | 
|  | minkey = key; | 
|  | break; | 
|  | } | 
|  | key = NULL; | 
|  | } | 
|  |  | 
|  | if (!minkey) | 
|  | return NULL; | 
|  |  | 
|  | for (;;) { | 
|  | if (minkey->user->user_ns == user_ns) | 
|  | return minkey; | 
|  | n = rb_next(&minkey->serial_node); | 
|  | if (!n) | 
|  | return NULL; | 
|  | minkey = rb_entry(n, struct key, serial_node); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void *proc_keys_start(struct seq_file *p, loff_t *_pos) | 
|  | __acquires(key_serial_lock) | 
|  | { | 
|  | key_serial_t pos = *_pos; | 
|  | struct key *key; | 
|  |  | 
|  | spin_lock(&key_serial_lock); | 
|  |  | 
|  | if (*_pos > INT_MAX) | 
|  | return NULL; | 
|  | key = find_ge_key(pos); | 
|  | if (!key) | 
|  | return NULL; | 
|  | *_pos = key->serial; | 
|  | return &key->serial_node; | 
|  | } | 
|  |  | 
|  | static inline key_serial_t key_node_serial(struct rb_node *n) | 
|  | { | 
|  | struct key *key = rb_entry(n, struct key, serial_node); | 
|  | return key->serial; | 
|  | } | 
|  |  | 
|  | static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos) | 
|  | { | 
|  | struct rb_node *n; | 
|  |  | 
|  | n = key_serial_next(v); | 
|  | if (n) | 
|  | *_pos = key_node_serial(n); | 
|  | return n; | 
|  | } | 
|  |  | 
|  | static void proc_keys_stop(struct seq_file *p, void *v) | 
|  | __releases(key_serial_lock) | 
|  | { | 
|  | spin_unlock(&key_serial_lock); | 
|  | } | 
|  |  | 
|  | static int proc_keys_show(struct seq_file *m, void *v) | 
|  | { | 
|  | const struct cred *cred = current_cred(); | 
|  | struct rb_node *_p = v; | 
|  | struct key *key = rb_entry(_p, struct key, serial_node); | 
|  | struct timespec now; | 
|  | unsigned long timo; | 
|  | key_ref_t key_ref, skey_ref; | 
|  | char xbuf[12]; | 
|  | int rc; | 
|  |  | 
|  | key_ref = make_key_ref(key, 0); | 
|  |  | 
|  | /* determine if the key is possessed by this process (a test we can | 
|  | * skip if the key does not indicate the possessor can view it | 
|  | */ | 
|  | if (key->perm & KEY_POS_VIEW) { | 
|  | skey_ref = search_my_process_keyrings(key->type, key, | 
|  | lookup_user_key_possessed, | 
|  | true, cred); | 
|  | if (!IS_ERR(skey_ref)) { | 
|  | key_ref_put(skey_ref); | 
|  | key_ref = make_key_ref(key, 1); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* check whether the current task is allowed to view the key (assuming | 
|  | * non-possession) | 
|  | * - the caller holds a spinlock, and thus the RCU read lock, making our | 
|  | *   access to __current_cred() safe | 
|  | */ | 
|  | rc = key_task_permission(key_ref, cred, KEY_VIEW); | 
|  | if (rc < 0) | 
|  | return 0; | 
|  |  | 
|  | now = current_kernel_time(); | 
|  |  | 
|  | rcu_read_lock(); | 
|  |  | 
|  | /* come up with a suitable timeout value */ | 
|  | if (key->expiry == 0) { | 
|  | memcpy(xbuf, "perm", 5); | 
|  | } else if (now.tv_sec >= key->expiry) { | 
|  | memcpy(xbuf, "expd", 5); | 
|  | } else { | 
|  | timo = key->expiry - now.tv_sec; | 
|  |  | 
|  | if (timo < 60) | 
|  | sprintf(xbuf, "%lus", timo); | 
|  | else if (timo < 60*60) | 
|  | sprintf(xbuf, "%lum", timo / 60); | 
|  | else if (timo < 60*60*24) | 
|  | sprintf(xbuf, "%luh", timo / (60*60)); | 
|  | else if (timo < 60*60*24*7) | 
|  | sprintf(xbuf, "%lud", timo / (60*60*24)); | 
|  | else | 
|  | sprintf(xbuf, "%luw", timo / (60*60*24*7)); | 
|  | } | 
|  |  | 
|  | #define showflag(KEY, LETTER, FLAG) \ | 
|  | (test_bit(FLAG,	&(KEY)->flags) ? LETTER : '-') | 
|  |  | 
|  | seq_printf(m, "%08x %c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ", | 
|  | key->serial, | 
|  | showflag(key, 'I', KEY_FLAG_INSTANTIATED), | 
|  | showflag(key, 'R', KEY_FLAG_REVOKED), | 
|  | showflag(key, 'D', KEY_FLAG_DEAD), | 
|  | showflag(key, 'Q', KEY_FLAG_IN_QUOTA), | 
|  | showflag(key, 'U', KEY_FLAG_USER_CONSTRUCT), | 
|  | showflag(key, 'N', KEY_FLAG_NEGATIVE), | 
|  | atomic_read(&key->usage), | 
|  | xbuf, | 
|  | key->perm, | 
|  | key->uid, | 
|  | key->gid, | 
|  | key->type->name); | 
|  |  | 
|  | #undef showflag | 
|  |  | 
|  | if (key->type->describe) | 
|  | key->type->describe(key, m); | 
|  | seq_putc(m, '\n'); | 
|  |  | 
|  | rcu_read_unlock(); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #endif /* CONFIG_KEYS_DEBUG_PROC_KEYS */ | 
|  |  | 
|  | static struct rb_node *__key_user_next(struct rb_node *n) | 
|  | { | 
|  | while (n) { | 
|  | struct key_user *user = rb_entry(n, struct key_user, node); | 
|  | if (user->user_ns == current_user_ns()) | 
|  | break; | 
|  | n = rb_next(n); | 
|  | } | 
|  | return n; | 
|  | } | 
|  |  | 
|  | static struct rb_node *key_user_next(struct rb_node *n) | 
|  | { | 
|  | return __key_user_next(rb_next(n)); | 
|  | } | 
|  |  | 
|  | static struct rb_node *key_user_first(struct rb_root *r) | 
|  | { | 
|  | struct rb_node *n = rb_first(r); | 
|  | return __key_user_next(n); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Implement "/proc/key-users" to provides a list of the key users and their | 
|  | * quotas. | 
|  | */ | 
|  | static int proc_key_users_open(struct inode *inode, struct file *file) | 
|  | { | 
|  | return seq_open(file, &proc_key_users_ops); | 
|  | } | 
|  |  | 
|  | static void *proc_key_users_start(struct seq_file *p, loff_t *_pos) | 
|  | __acquires(key_user_lock) | 
|  | { | 
|  | struct rb_node *_p; | 
|  | loff_t pos = *_pos; | 
|  |  | 
|  | spin_lock(&key_user_lock); | 
|  |  | 
|  | _p = key_user_first(&key_user_tree); | 
|  | while (pos > 0 && _p) { | 
|  | pos--; | 
|  | _p = key_user_next(_p); | 
|  | } | 
|  |  | 
|  | return _p; | 
|  | } | 
|  |  | 
|  | static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos) | 
|  | { | 
|  | (*_pos)++; | 
|  | return key_user_next((struct rb_node *)v); | 
|  | } | 
|  |  | 
|  | static void proc_key_users_stop(struct seq_file *p, void *v) | 
|  | __releases(key_user_lock) | 
|  | { | 
|  | spin_unlock(&key_user_lock); | 
|  | } | 
|  |  | 
|  | static int proc_key_users_show(struct seq_file *m, void *v) | 
|  | { | 
|  | struct rb_node *_p = v; | 
|  | struct key_user *user = rb_entry(_p, struct key_user, node); | 
|  | unsigned maxkeys = (user->uid == 0) ? | 
|  | key_quota_root_maxkeys : key_quota_maxkeys; | 
|  | unsigned maxbytes = (user->uid == 0) ? | 
|  | key_quota_root_maxbytes : key_quota_maxbytes; | 
|  |  | 
|  | seq_printf(m, "%5u: %5d %d/%d %d/%d %d/%d\n", | 
|  | user->uid, | 
|  | atomic_read(&user->usage), | 
|  | atomic_read(&user->nkeys), | 
|  | atomic_read(&user->nikeys), | 
|  | user->qnkeys, | 
|  | maxkeys, | 
|  | user->qnbytes, | 
|  | maxbytes); | 
|  |  | 
|  | return 0; | 
|  | } |