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>
34 #include <linux/sched.h>
35 #include <linux/cred.h>
42 #define CAP_NR_DEVS 2 /* caphash and capuse */
46 #define CAP_NODE_SIZE 20
49 #define MAX_DIGEST_SIZE 20
52 char data
[CAP_NODE_SIZE
];
53 struct list_head list
;
57 struct cap_node
*head
;
64 int cap_trim(struct cap_dev
*);
65 ssize_t
cap_write(struct file
*, const char __user
*, size_t, loff_t
*);
66 char *cap_hash(char *plain_text
, unsigned int plain_text_size
, char *key
, unsigned int key_size
);
67 void hex_dump(unsigned char * buf
, unsigned int len
);
69 int cap_major
= CAP_MAJOR
;
71 int cap_nr_devs
= CAP_NR_DEVS
;
72 int cap_node_size
= CAP_NODE_SIZE
;
74 module_param(cap_major
, int, S_IRUGO
);
75 module_param(cap_minor
, int, S_IRUGO
);
76 module_param(cap_nr_devs
, int, S_IRUGO
);
78 MODULE_AUTHOR("Ashwin Ganti");
79 MODULE_LICENSE("GPL");
81 struct cap_dev
*cap_devices
;
83 void hexdump(unsigned char *buf
, unsigned int len
)
86 printk("%02x", *buf
++);
90 int cap_trim(struct cap_dev
*dev
)
93 struct list_head
*pos
, *q
;
94 if (dev
->head
!= NULL
) {
95 list_for_each_safe(pos
, q
, &(dev
->head
->list
)) {
96 tmp
= list_entry(pos
, struct cap_node
, list
);
104 int cap_open(struct inode
*inode
, struct file
*filp
)
107 dev
= container_of(inode
->i_cdev
, struct cap_dev
, cdev
);
108 filp
->private_data
= dev
;
110 /* trim to 0 the length of the device if open was write-only */
111 if ((filp
->f_flags
& O_ACCMODE
) == O_WRONLY
) {
112 if (down_interruptible(&dev
->sem
))
117 /* initialise the head if it is NULL */
118 if (dev
->head
== NULL
) {
120 (struct cap_node
*) kmalloc(sizeof(struct cap_node
),
122 INIT_LIST_HEAD(&(dev
->head
->list
));
127 int cap_release(struct inode
*inode
, struct file
*filp
)
133 cap_write(struct file
* filp
, const char __user
* buf
,
134 size_t count
, loff_t
* f_pos
)
136 struct cap_node
*node_ptr
, *tmp
;
137 struct list_head
*pos
;
138 struct cap_dev
*dev
= filp
->private_data
;
139 ssize_t retval
= -ENOMEM
;
141 int len
, target_int
, source_int
, flag
= 0;
142 char *user_buf
, *user_buf_running
, *source_user
, *target_user
,
143 *rand_str
, *hash_str
, *result
;
145 if (down_interruptible(&dev
->sem
))
149 (struct cap_node
*) kmalloc(sizeof(struct cap_node
),
151 user_buf
= (char *) kmalloc(count
, GFP_KERNEL
);
152 memset(user_buf
, 0, count
);
154 if (copy_from_user(user_buf
, buf
, count
)) {
159 /* If the minor number is 0 ( /dev/caphash ) then simply add the
160 * hashed capability supplied by the user to the list of hashes
162 if (0 == iminor(filp
->f_dentry
->d_inode
)) {
163 printk(KERN_INFO
"Capability being written to /dev/caphash : \n");
164 hexdump(user_buf
, count
);
165 memcpy(node_ptr
->data
, user_buf
, count
);
166 list_add(&(node_ptr
->list
), &(dev
->head
->list
));
168 /* break the supplied string into tokens with @ as the delimiter
169 If the string is "user1@user2@randomstring" we need to split it
170 and hash 'user1@user2' using 'randomstring' as the key
172 user_buf_running
= kstrdup(user_buf
, GFP_KERNEL
);
173 source_user
= strsep(&user_buf_running
, "@");
174 target_user
= strsep(&user_buf_running
, "@");
175 rand_str
= strsep(&user_buf_running
, "@");
177 /* hash the string user1@user2 with rand_str as the key */
178 len
= strlen(source_user
) + strlen(target_user
) + 1;
179 hash_str
= (char *) kmalloc(len
, GFP_KERNEL
);
180 memset(hash_str
, 0, len
);
181 strcat(hash_str
, source_user
);
182 strcat(hash_str
, "@");
183 strcat(hash_str
, target_user
);
185 printk(KERN_ALERT
"the source user is %s \n", source_user
);
186 printk(KERN_ALERT
"the target user is %s \n", target_user
);
189 cap_hash(hash_str
, len
, rand_str
, strlen(rand_str
));
190 if (NULL
== result
) {
194 memcpy(node_ptr
->data
, result
, CAP_NODE_SIZE
);
195 /* Change the process's uid if the hash is present in the
198 list_for_each(pos
, &(cap_devices
->head
->list
)) {
199 /* Change the user id of the process if the hashes match */
202 list_entry(pos
, struct cap_node
,
205 target_int
= (unsigned int)
206 simple_strtol(target_user
, NULL
, 0);
207 source_int
= (unsigned int)
208 simple_strtol(source_user
, NULL
, 0);
211 /* Check whether the process writing to capuse is actually owned by
214 if (source_int
!= current_uid()) {
216 "Process is not owned by the source user of the capability.\n");
220 /* What all id's need to be changed here? uid, euid, fsid, savedids ??
221 * Currently I am changing the effective user id
222 * since most of the authorisation decisions are based on it
224 new = prepare_creds();
229 new->uid
= (uid_t
) target_int
;
230 new->euid
= (uid_t
) target_int
;
231 retval
= commit_creds(new);
235 /* Remove the capability from the list and break */
237 list_entry(pos
, struct cap_node
, list
);
244 /* The capability is not present in the list of the hashes stored, hence return failure */
246 "Invalid capabiliy written to /dev/capuse \n");
253 /* update the size */
254 if (dev
->size
< *f_pos
)
262 struct file_operations cap_fops
= {
263 .owner
= THIS_MODULE
,
266 .release
= cap_release
,
270 void cap_cleanup_module(void)
273 dev_t devno
= MKDEV(cap_major
, cap_minor
);
275 for (i
= 0; i
< cap_nr_devs
; i
++) {
276 cap_trim(cap_devices
+ i
);
277 cdev_del(&cap_devices
[i
].cdev
);
281 unregister_chrdev_region(devno
, cap_nr_devs
);
286 static void cap_setup_cdev(struct cap_dev
*dev
, int index
)
288 int err
, devno
= MKDEV(cap_major
, cap_minor
+ index
);
289 cdev_init(&dev
->cdev
, &cap_fops
);
290 dev
->cdev
.owner
= THIS_MODULE
;
291 dev
->cdev
.ops
= &cap_fops
;
292 err
= cdev_add(&dev
->cdev
, devno
, 1);
294 printk(KERN_NOTICE
"Error %d adding cap%d", err
, index
);
298 int cap_init_module(void)
304 dev
= MKDEV(cap_major
, cap_minor
);
305 result
= register_chrdev_region(dev
, cap_nr_devs
, "cap");
307 result
= alloc_chrdev_region(&dev
, cap_minor
, cap_nr_devs
,
309 cap_major
= MAJOR(dev
);
313 printk(KERN_WARNING
"cap: can't get major %d\n",
319 kmalloc(cap_nr_devs
* sizeof(struct cap_dev
), GFP_KERNEL
);
324 memset(cap_devices
, 0, cap_nr_devs
* sizeof(struct cap_dev
));
326 /* Initialize each device. */
327 for (i
= 0; i
< cap_nr_devs
; i
++) {
328 cap_devices
[i
].node_size
= cap_node_size
;
329 init_MUTEX(&cap_devices
[i
].sem
);
330 cap_setup_cdev(&cap_devices
[i
], i
);
336 cap_cleanup_module();
340 module_init(cap_init_module
);
341 module_exit(cap_cleanup_module
);
343 char *cap_hash(char *plain_text
, unsigned int plain_text_size
,
344 char *key
, unsigned int key_size
)
346 struct scatterlist sg
;
347 char *result
= (char *) kmalloc(MAX_DIGEST_SIZE
, GFP_KERNEL
);
348 struct crypto_hash
*tfm
;
349 struct hash_desc desc
;
352 tfm
= crypto_alloc_hash("hmac(sha1)", 0, CRYPTO_ALG_ASYNC
);
354 printk("failed to load transform for hmac(sha1): %ld\n",
363 memset(result
, 0, MAX_DIGEST_SIZE
);
364 sg_set_buf(&sg
, plain_text
, plain_text_size
);
366 ret
= crypto_hash_setkey(tfm
, key
, key_size
);
368 printk("setkey() failed ret=%d\n", ret
);
374 ret
= crypto_hash_digest(&desc
, &sg
, plain_text_size
, result
);
376 printk("digest () failed ret=%d\n", ret
);
382 printk("crypto hash digest size %d\n",
383 crypto_hash_digestsize(tfm
));
384 hexdump(result
, MAX_DIGEST_SIZE
);
387 crypto_free_hash(tfm
);