2 * Plan 9 style capability device implementation for the Linux Kernel
4 * Copyright 2008, 2009 Ashwin Ganti <ashwin.ganti@gmail.com>
6 * Released under the GPLv2
9 #include <linux/module.h>
10 #include <linux/moduleparam.h>
11 #include <linux/init.h>
12 #include <linux/kernel.h>
13 #include <linux/slab.h>
15 #include <linux/errno.h>
16 #include <linux/types.h>
17 #include <linux/proc_fs.h>
18 #include <linux/fcntl.h>
19 #include <linux/cdev.h>
20 #include <linux/syscalls.h>
21 #include <asm/system.h>
22 #include <asm/uaccess.h>
23 #include <linux/list.h>
24 #include <linux/err.h>
26 #include <linux/string.h>
27 #include <linux/crypto.h>
28 #include <linux/highmem.h>
29 #include <linux/jiffies.h>
30 #include <linux/timex.h>
31 #include <linux/interrupt.h>
32 #include <linux/scatterlist.h>
33 #include <linux/crypto.h>
36 int cap_major
= CAP_MAJOR
;
38 int cap_nr_devs
= CAP_NR_DEVS
;
39 int cap_node_size
= CAP_NODE_SIZE
;
41 module_param(cap_major
, int, S_IRUGO
);
42 module_param(cap_minor
, int, S_IRUGO
);
43 module_param(cap_nr_devs
, int, S_IRUGO
);
45 MODULE_AUTHOR("Ashwin Ganti");
46 MODULE_LICENSE("GPL");
48 struct cap_dev
*cap_devices
;
50 void hexdump(unsigned char *buf
, unsigned int len
)
53 printk("%02x", *buf
++);
57 int cap_trim(struct cap_dev
*dev
)
60 struct list_head
*pos
, *q
;
61 if (dev
->head
!= NULL
) {
62 list_for_each_safe(pos
, q
, &(dev
->head
->list
)) {
63 tmp
= list_entry(pos
, struct cap_node
, list
);
71 int cap_open(struct inode
*inode
, struct file
*filp
)
74 dev
= container_of(inode
->i_cdev
, struct cap_dev
, cdev
);
75 filp
->private_data
= dev
;
77 /* trim to 0 the length of the device if open was write-only */
78 if ((filp
->f_flags
& O_ACCMODE
) == O_WRONLY
) {
79 if (down_interruptible(&dev
->sem
))
84 /* initialise the head if it is NULL */
85 if (dev
->head
== NULL
) {
87 (struct cap_node
*) kmalloc(sizeof(struct cap_node
),
89 INIT_LIST_HEAD(&(dev
->head
->list
));
94 int cap_release(struct inode
*inode
, struct file
*filp
)
100 cap_write(struct file
* filp
, const char __user
* buf
,
101 size_t count
, loff_t
* f_pos
)
103 struct cap_node
*node_ptr
, *tmp
;
104 struct list_head
*pos
;
105 struct cap_dev
*dev
= filp
->private_data
;
106 ssize_t retval
= -ENOMEM
;
107 int len
, target_int
, source_int
, flag
= 0;
108 char *user_buf
, *user_buf_running
, *source_user
, *target_user
,
109 *rand_str
, *hash_str
, *result
;
111 if (down_interruptible(&dev
->sem
))
115 (struct cap_node
*) kmalloc(sizeof(struct cap_node
),
117 user_buf
= (char *) kmalloc(count
, GFP_KERNEL
);
118 memset(user_buf
, 0, count
);
120 if (copy_from_user(user_buf
, buf
, count
)) {
125 /* If the minor number is 0 ( /dev/caphash ) then simply add the
126 * hashed capability supplied by the user to the list of hashes
128 if (0 == iminor(filp
->f_dentry
->d_inode
)) {
129 printk(KERN_INFO
"Capability being written to /dev/caphash : \n");
130 hexdump(user_buf
, count
);
131 memcpy(node_ptr
->data
, user_buf
, count
);
132 list_add(&(node_ptr
->list
), &(dev
->head
->list
));
134 /* break the supplied string into tokens with @ as the delimiter
135 If the string is "user1@user2@randomstring" we need to split it
136 and hash 'user1@user2' using 'randomstring' as the key
138 user_buf_running
= kstrdup(user_buf
, GFP_KERNEL
);
139 source_user
= strsep(&user_buf_running
, "@");
140 target_user
= strsep(&user_buf_running
, "@");
141 rand_str
= strsep(&user_buf_running
, "@");
143 /* hash the string user1@user2 with rand_str as the key */
144 len
= strlen(source_user
) + strlen(target_user
) + 1;
145 hash_str
= (char *) kmalloc(len
, GFP_KERNEL
);
146 memset(hash_str
, 0, len
);
147 strcat(hash_str
, source_user
);
148 strcat(hash_str
, "@");
149 strcat(hash_str
, target_user
);
151 printk(KERN_ALERT
"the source user is %s \n", source_user
);
152 printk(KERN_ALERT
"the target user is %s \n", target_user
);
155 cap_hash(hash_str
, len
, rand_str
, strlen(rand_str
));
156 if (NULL
== result
) {
160 memcpy(node_ptr
->data
, result
, CAP_NODE_SIZE
);
161 /* Change the process's uid if the hash is present in the
164 list_for_each(pos
, &(cap_devices
->head
->list
)) {
165 /* Change the user id of the process if the hashes match */
168 list_entry(pos
, struct cap_node
,
171 target_int
= (unsigned int)
172 simple_strtol(target_user
, NULL
, 0);
173 source_int
= (unsigned int)
174 simple_strtol(source_user
, NULL
, 0);
177 /* Check whether the process writing to capuse is actually owned by
180 if (source_int
!= current
->uid
) {
182 "Process is not owned by the source user of the capability.\n");
186 /* What all id's need to be changed here? uid, euid, fsid, savedids ??
187 * Currently I am changing the effective user id
188 * since most of the authorisation decisions are based on it
190 current
->uid
= (uid_t
) target_int
;
191 current
->euid
= (uid_t
) target_int
;
193 /* Remove the capability from the list and break */
195 list_entry(pos
, struct cap_node
, list
);
202 /* The capability is not present in the list of the hashes stored, hence return failure */
204 "Invalid capabiliy written to /dev/capuse \n");
211 /* update the size */
212 if (dev
->size
< *f_pos
)
220 struct file_operations cap_fops
= {
221 .owner
= THIS_MODULE
,
224 .release
= cap_release
,
228 void cap_cleanup_module(void)
231 dev_t devno
= MKDEV(cap_major
, cap_minor
);
233 for (i
= 0; i
< cap_nr_devs
; i
++) {
234 cap_trim(cap_devices
+ i
);
235 cdev_del(&cap_devices
[i
].cdev
);
239 unregister_chrdev_region(devno
, cap_nr_devs
);
244 static void cap_setup_cdev(struct cap_dev
*dev
, int index
)
246 int err
, devno
= MKDEV(cap_major
, cap_minor
+ index
);
247 cdev_init(&dev
->cdev
, &cap_fops
);
248 dev
->cdev
.owner
= THIS_MODULE
;
249 dev
->cdev
.ops
= &cap_fops
;
250 err
= cdev_add(&dev
->cdev
, devno
, 1);
252 printk(KERN_NOTICE
"Error %d adding cap%d", err
, index
);
256 int cap_init_module(void)
262 dev
= MKDEV(cap_major
, cap_minor
);
263 result
= register_chrdev_region(dev
, cap_nr_devs
, "cap");
265 result
= alloc_chrdev_region(&dev
, cap_minor
, cap_nr_devs
,
267 cap_major
= MAJOR(dev
);
271 printk(KERN_WARNING
"cap: can't get major %d\n",
277 kmalloc(cap_nr_devs
* sizeof(struct cap_dev
), GFP_KERNEL
);
282 memset(cap_devices
, 0, cap_nr_devs
* sizeof(struct cap_dev
));
284 /* Initialize each device. */
285 for (i
= 0; i
< cap_nr_devs
; i
++) {
286 cap_devices
[i
].node_size
= cap_node_size
;
287 init_MUTEX(&cap_devices
[i
].sem
);
288 cap_setup_cdev(&cap_devices
[i
], i
);
294 cap_cleanup_module();
298 module_init(cap_init_module
);
299 module_exit(cap_cleanup_module
);
301 char *cap_hash(char *plain_text
, unsigned int plain_text_size
,
302 char *key
, unsigned int key_size
)
304 struct scatterlist sg
;
305 char *result
= (char *) kmalloc(MAX_DIGEST_SIZE
, GFP_KERNEL
);
306 struct crypto_hash
*tfm
;
307 struct hash_desc desc
;
310 tfm
= crypto_alloc_hash("hmac(sha1)", 0, CRYPTO_ALG_ASYNC
);
312 printk("failed to load transform for hmac(sha1): %ld\n",
321 memset(result
, 0, MAX_DIGEST_SIZE
);
322 sg_set_buf(&sg
, plain_text
, plain_text_size
);
324 ret
= crypto_hash_setkey(tfm
, key
, key_size
);
326 printk("setkey() failed ret=%d\n", ret
);
332 ret
= crypto_hash_digest(&desc
, &sg
, plain_text_size
, result
);
334 printk("digest () failed ret=%d\n", ret
);
340 printk("crypto hash digest size %d\n",
341 crypto_hash_digestsize(tfm
));
342 hexdump(result
, MAX_DIGEST_SIZE
);
345 crypto_free_hash(tfm
);