| /* 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 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; | 
 |  | 
 | } /* 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); | 
 | 	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; | 
 |  | 
 | } |