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