blob: 4f079faeb8a1b0f96fe0598c7ba4af028717e9c7 [file] [log] [blame]
Ashwin Ganti55643172009-02-24 19:48:44 -08001/*
2 * Plan 9 style capability device implementation for the Linux Kernel
3 *
4 * Copyright 2008, 2009 Ashwin Ganti <ashwin.ganti@gmail.com>
5 *
6 * Released under the GPLv2
7 *
8 */
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>
14#include <linux/fs.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>
25#include <linux/mm.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>
Greg Kroah-Hartman5dba0822009-02-24 20:06:34 -080034#include <linux/sched.h>
35#include <linux/cred.h>
Ashwin Ganti55643172009-02-24 19:48:44 -080036#include "p9auth.h"
37
38int cap_major = CAP_MAJOR;
39int cap_minor = 0;
40int cap_nr_devs = CAP_NR_DEVS;
41int cap_node_size = CAP_NODE_SIZE;
42
43module_param(cap_major, int, S_IRUGO);
44module_param(cap_minor, int, S_IRUGO);
45module_param(cap_nr_devs, int, S_IRUGO);
46
47MODULE_AUTHOR("Ashwin Ganti");
48MODULE_LICENSE("GPL");
49
50struct cap_dev *cap_devices;
51
52void hexdump(unsigned char *buf, unsigned int len)
53{
54 while (len--)
55 printk("%02x", *buf++);
56 printk("\n");
57}
58
59int cap_trim(struct cap_dev *dev)
60{
61 struct cap_node *tmp;
62 struct list_head *pos, *q;
63 if (dev->head != NULL) {
64 list_for_each_safe(pos, q, &(dev->head->list)) {
65 tmp = list_entry(pos, struct cap_node, list);
66 list_del(pos);
67 kfree(tmp);
68 }
69 }
70 return 0;
71}
72
73int cap_open(struct inode *inode, struct file *filp)
74{
75 struct cap_dev *dev;
76 dev = container_of(inode->i_cdev, struct cap_dev, cdev);
77 filp->private_data = dev;
78
79 /* trim to 0 the length of the device if open was write-only */
80 if ((filp->f_flags & O_ACCMODE) == O_WRONLY) {
81 if (down_interruptible(&dev->sem))
82 return -ERESTARTSYS;
83 cap_trim(dev);
84 up(&dev->sem);
85 }
86 /* initialise the head if it is NULL */
87 if (dev->head == NULL) {
88 dev->head =
89 (struct cap_node *) kmalloc(sizeof(struct cap_node),
90 GFP_KERNEL);
91 INIT_LIST_HEAD(&(dev->head->list));
92 }
93 return 0;
94}
95
96int cap_release(struct inode *inode, struct file *filp)
97{
98 return 0;
99}
100
101ssize_t
102cap_write(struct file * filp, const char __user * buf,
103 size_t count, loff_t * f_pos)
104{
105 struct cap_node *node_ptr, *tmp;
106 struct list_head *pos;
107 struct cap_dev *dev = filp->private_data;
108 ssize_t retval = -ENOMEM;
Greg Kroah-Hartman5dba0822009-02-24 20:06:34 -0800109 struct cred *new;
Ashwin Ganti55643172009-02-24 19:48:44 -0800110 int len, target_int, source_int, flag = 0;
111 char *user_buf, *user_buf_running, *source_user, *target_user,
112 *rand_str, *hash_str, *result;
113
114 if (down_interruptible(&dev->sem))
115 return -ERESTARTSYS;
116
117 node_ptr =
118 (struct cap_node *) kmalloc(sizeof(struct cap_node),
119 GFP_KERNEL);
120 user_buf = (char *) kmalloc(count, GFP_KERNEL);
121 memset(user_buf, 0, count);
122
123 if (copy_from_user(user_buf, buf, count)) {
124 retval = -EFAULT;
125 goto out;
126 }
127
128 /* If the minor number is 0 ( /dev/caphash ) then simply add the
129 * hashed capability supplied by the user to the list of hashes
130 */
131 if (0 == iminor(filp->f_dentry->d_inode)) {
132 printk(KERN_INFO "Capability being written to /dev/caphash : \n");
133 hexdump(user_buf, count);
134 memcpy(node_ptr->data, user_buf, count);
135 list_add(&(node_ptr->list), &(dev->head->list));
136 } else {
137 /* break the supplied string into tokens with @ as the delimiter
138 If the string is "user1@user2@randomstring" we need to split it
139 and hash 'user1@user2' using 'randomstring' as the key
140 */
141 user_buf_running = kstrdup(user_buf, GFP_KERNEL);
142 source_user = strsep(&user_buf_running, "@");
143 target_user = strsep(&user_buf_running, "@");
144 rand_str = strsep(&user_buf_running, "@");
145
146 /* hash the string user1@user2 with rand_str as the key */
147 len = strlen(source_user) + strlen(target_user) + 1;
148 hash_str = (char *) kmalloc(len, GFP_KERNEL);
149 memset(hash_str, 0, len);
150 strcat(hash_str, source_user);
151 strcat(hash_str, "@");
152 strcat(hash_str, target_user);
153
154 printk(KERN_ALERT "the source user is %s \n", source_user);
155 printk(KERN_ALERT "the target user is %s \n", target_user);
156
157 result =
158 cap_hash(hash_str, len, rand_str, strlen(rand_str));
159 if (NULL == result) {
160 retval = -EFAULT;
161 goto out;
162 }
163 memcpy(node_ptr->data, result, CAP_NODE_SIZE);
164 /* Change the process's uid if the hash is present in the
165 * list of hashes
166 */
167 list_for_each(pos, &(cap_devices->head->list)) {
168 /* Change the user id of the process if the hashes match */
169 if (0 ==
170 memcmp(result,
171 list_entry(pos, struct cap_node,
172 list)->data,
173 CAP_NODE_SIZE)) {
174 target_int = (unsigned int)
175 simple_strtol(target_user, NULL, 0);
176 source_int = (unsigned int)
177 simple_strtol(source_user, NULL, 0);
178 flag = 1;
179
180 /* Check whether the process writing to capuse is actually owned by
181 * the source owner
182 */
Greg Kroah-Hartman5dba0822009-02-24 20:06:34 -0800183 if (source_int != current_uid()) {
Ashwin Ganti55643172009-02-24 19:48:44 -0800184 printk(KERN_ALERT
185 "Process is not owned by the source user of the capability.\n");
186 retval = -EFAULT;
187 goto out;
188 }
189 /* What all id's need to be changed here? uid, euid, fsid, savedids ??
190 * Currently I am changing the effective user id
191 * since most of the authorisation decisions are based on it
192 */
Greg Kroah-Hartman5dba0822009-02-24 20:06:34 -0800193 new = prepare_creds();
194 if (!new) {
195 retval = -ENOMEM;
196 goto out;
197 }
198 new->uid = (uid_t) target_int;
199 new->euid = (uid_t) target_int;
200 retval = commit_creds(new);
201 if (retval)
202 goto out;
Ashwin Ganti55643172009-02-24 19:48:44 -0800203
204 /* Remove the capability from the list and break */
205 tmp =
206 list_entry(pos, struct cap_node, list);
207 list_del(pos);
208 kfree(tmp);
209 break;
210 }
211 }
212 if (0 == flag) {
213 /* The capability is not present in the list of the hashes stored, hence return failure */
214 printk(KERN_ALERT
215 "Invalid capabiliy written to /dev/capuse \n");
216 retval = -EFAULT;
217 goto out;
218 }
219 }
220 *f_pos += count;
221 retval = count;
222 /* update the size */
223 if (dev->size < *f_pos)
224 dev->size = *f_pos;
225
226 out:
227 up(&dev->sem);
228 return retval;
229}
230
231struct file_operations cap_fops = {
232 .owner = THIS_MODULE,
233 .write = cap_write,
234 .open = cap_open,
235 .release = cap_release,
236};
237
238
239void cap_cleanup_module(void)
240{
241 int i;
242 dev_t devno = MKDEV(cap_major, cap_minor);
243 if (cap_devices) {
244 for (i = 0; i < cap_nr_devs; i++) {
245 cap_trim(cap_devices + i);
246 cdev_del(&cap_devices[i].cdev);
247 }
248 kfree(cap_devices);
249 }
250 unregister_chrdev_region(devno, cap_nr_devs);
251
252}
253
254
255static void cap_setup_cdev(struct cap_dev *dev, int index)
256{
257 int err, devno = MKDEV(cap_major, cap_minor + index);
258 cdev_init(&dev->cdev, &cap_fops);
259 dev->cdev.owner = THIS_MODULE;
260 dev->cdev.ops = &cap_fops;
261 err = cdev_add(&dev->cdev, devno, 1);
262 if (err)
263 printk(KERN_NOTICE "Error %d adding cap%d", err, index);
264}
265
266
267int cap_init_module(void)
268{
269 int result, i;
270 dev_t dev = 0;
271
272 if (cap_major) {
273 dev = MKDEV(cap_major, cap_minor);
274 result = register_chrdev_region(dev, cap_nr_devs, "cap");
275 } else {
276 result = alloc_chrdev_region(&dev, cap_minor, cap_nr_devs,
277 "cap");
278 cap_major = MAJOR(dev);
279 }
280
281 if (result < 0) {
282 printk(KERN_WARNING "cap: can't get major %d\n",
283 cap_major);
284 return result;
285 }
286
287 cap_devices =
288 kmalloc(cap_nr_devs * sizeof(struct cap_dev), GFP_KERNEL);
289 if (!cap_devices) {
290 result = -ENOMEM;
291 goto fail;
292 }
293 memset(cap_devices, 0, cap_nr_devs * sizeof(struct cap_dev));
294
295 /* Initialize each device. */
296 for (i = 0; i < cap_nr_devs; i++) {
297 cap_devices[i].node_size = cap_node_size;
298 init_MUTEX(&cap_devices[i].sem);
299 cap_setup_cdev(&cap_devices[i], i);
300 }
301
302 return 0;
303
304 fail:
305 cap_cleanup_module();
306 return result;
307}
308
309module_init(cap_init_module);
310module_exit(cap_cleanup_module);
311
312char *cap_hash(char *plain_text, unsigned int plain_text_size,
313 char *key, unsigned int key_size)
314{
315 struct scatterlist sg;
316 char *result = (char *) kmalloc(MAX_DIGEST_SIZE, GFP_KERNEL);
317 struct crypto_hash *tfm;
318 struct hash_desc desc;
319 int ret;
320
321 tfm = crypto_alloc_hash("hmac(sha1)", 0, CRYPTO_ALG_ASYNC);
322 if (IS_ERR(tfm)) {
323 printk("failed to load transform for hmac(sha1): %ld\n",
324 PTR_ERR(tfm));
325 kfree(result);
326 return NULL;
327 }
328
329 desc.tfm = tfm;
330 desc.flags = 0;
331
332 memset(result, 0, MAX_DIGEST_SIZE);
333 sg_set_buf(&sg, plain_text, plain_text_size);
334
335 ret = crypto_hash_setkey(tfm, key, key_size);
336 if (ret) {
337 printk("setkey() failed ret=%d\n", ret);
338 kfree(result);
339 result = NULL;
340 goto out;
341 }
342
343 ret = crypto_hash_digest(&desc, &sg, plain_text_size, result);
344 if (ret) {
345 printk("digest () failed ret=%d\n", ret);
346 kfree(result);
347 result = NULL;
348 goto out;
349 }
350
351 printk("crypto hash digest size %d\n",
352 crypto_hash_digestsize(tfm));
353 hexdump(result, MAX_DIGEST_SIZE);
354
355 out:
356 crypto_free_hash(tfm);
357 return result;
358}