|  | /* proc.c: proc 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/slab.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 struct seq_operations proc_keys_ops = { | 
|  | .start	= proc_keys_start, | 
|  | .next	= proc_keys_next, | 
|  | .stop	= proc_keys_stop, | 
|  | .show	= proc_keys_show, | 
|  | }; | 
|  |  | 
|  | static 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 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 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 = create_proc_entry("keys", 0, NULL); | 
|  | if (!p) | 
|  | panic("Cannot create /proc/keys\n"); | 
|  |  | 
|  | p->proc_fops = &proc_keys_fops; | 
|  | #endif | 
|  |  | 
|  | p = create_proc_entry("key-users", 0, NULL); | 
|  | if (!p) | 
|  | panic("Cannot create /proc/key-users\n"); | 
|  |  | 
|  | p->proc_fops = &proc_key_users_fops; | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | } /* end key_proc_init() */ | 
|  |  | 
|  | __initcall(key_proc_init); | 
|  |  | 
|  | /*****************************************************************************/ | 
|  | /* | 
|  | * implement "/proc/keys" to provides a list of the keys on the system | 
|  | */ | 
|  | #ifdef CONFIG_KEYS_DEBUG_PROC_KEYS | 
|  |  | 
|  | static int proc_keys_open(struct inode *inode, struct file *file) | 
|  | { | 
|  | return seq_open(file, &proc_keys_ops); | 
|  |  | 
|  | } | 
|  |  | 
|  | static void *proc_keys_start(struct seq_file *p, loff_t *_pos) | 
|  | { | 
|  | struct rb_node *_p; | 
|  | loff_t pos = *_pos; | 
|  |  | 
|  | spin_lock(&key_serial_lock); | 
|  |  | 
|  | _p = rb_first(&key_serial_tree); | 
|  | while (pos > 0 && _p) { | 
|  | pos--; | 
|  | _p = rb_next(_p); | 
|  | } | 
|  |  | 
|  | return _p; | 
|  |  | 
|  | } | 
|  |  | 
|  | static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos) | 
|  | { | 
|  | (*_pos)++; | 
|  | return rb_next((struct rb_node *) v); | 
|  |  | 
|  | } | 
|  |  | 
|  | static void proc_keys_stop(struct seq_file *p, void *v) | 
|  | { | 
|  | spin_unlock(&key_serial_lock); | 
|  | } | 
|  |  | 
|  | static int proc_keys_show(struct seq_file *m, void *v) | 
|  | { | 
|  | struct rb_node *_p = v; | 
|  | struct key *key = rb_entry(_p, struct key, serial_node); | 
|  | struct timespec now; | 
|  | unsigned long timo; | 
|  | char xbuf[12]; | 
|  | int rc; | 
|  |  | 
|  | /* check whether the current task is allowed to view the key (assuming | 
|  | * non-possession) */ | 
|  | rc = key_task_permission(make_key_ref(key, 0), current, 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 */ | 
|  |  | 
|  | /*****************************************************************************/ | 
|  | /* | 
|  | * implement "/proc/key-users" to provides a list of the key users | 
|  | */ | 
|  | 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) | 
|  | { | 
|  | struct rb_node *_p; | 
|  | loff_t pos = *_pos; | 
|  |  | 
|  | spin_lock(&key_user_lock); | 
|  |  | 
|  | _p = rb_first(&key_user_tree); | 
|  | while (pos > 0 && _p) { | 
|  | pos--; | 
|  | _p = rb_next(_p); | 
|  | } | 
|  |  | 
|  | return _p; | 
|  |  | 
|  | } | 
|  |  | 
|  | static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos) | 
|  | { | 
|  | (*_pos)++; | 
|  | return rb_next((struct rb_node *) v); | 
|  |  | 
|  | } | 
|  |  | 
|  | static void proc_key_users_stop(struct seq_file *p, void *v) | 
|  | { | 
|  | 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); | 
|  |  | 
|  | 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, | 
|  | KEYQUOTA_MAX_KEYS, | 
|  | user->qnbytes, | 
|  | KEYQUOTA_MAX_BYTES | 
|  | ); | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | } |