blob: c1e5bc4f30ae9f3057694cb8c83c244f93607866 [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>
Greg Kroah-Hartman77dc1132009-02-24 20:21:55 -080021#include <linux/uaccess.h>
Ashwin Ganti55643172009-02-24 19:48:44 -080022#include <linux/list.h>
23#include <linux/err.h>
24#include <linux/mm.h>
25#include <linux/string.h>
26#include <linux/crypto.h>
27#include <linux/highmem.h>
28#include <linux/jiffies.h>
29#include <linux/timex.h>
30#include <linux/interrupt.h>
31#include <linux/scatterlist.h>
32#include <linux/crypto.h>
Greg Kroah-Hartman5dba0822009-02-24 20:06:34 -080033#include <linux/sched.h>
34#include <linux/cred.h>
Greg Kroah-Hartman77dc1132009-02-24 20:21:55 -080035#include <asm/system.h>
Greg Kroah-Hartman4bf04382009-02-24 20:11:39 -080036
37#ifndef CAP_MAJOR
38#define CAP_MAJOR 0
39#endif
40
41#ifndef CAP_NR_DEVS
42#define CAP_NR_DEVS 2 /* caphash and capuse */
43#endif
44
45#ifndef CAP_NODE_SIZE
46#define CAP_NODE_SIZE 20
47#endif
48
49#define MAX_DIGEST_SIZE 20
50
51struct cap_node {
52 char data[CAP_NODE_SIZE];
53 struct list_head list;
54};
55
56struct cap_dev {
57 struct cap_node *head;
58 int node_size;
59 unsigned long size;
60 struct semaphore sem;
61 struct cdev cdev;
62};
63
Greg Kroah-Hartman77dc1132009-02-24 20:21:55 -080064char *cap_hash(char *plain_text, unsigned int plain_text_size, char *key,
65 unsigned int key_size);
Ashwin Ganti55643172009-02-24 19:48:44 -080066
67int cap_major = CAP_MAJOR;
Greg Kroah-Hartman77dc1132009-02-24 20:21:55 -080068int cap_minor;
Ashwin Ganti55643172009-02-24 19:48:44 -080069int cap_nr_devs = CAP_NR_DEVS;
70int cap_node_size = CAP_NODE_SIZE;
71
72module_param(cap_major, int, S_IRUGO);
73module_param(cap_minor, int, S_IRUGO);
74module_param(cap_nr_devs, int, S_IRUGO);
75
76MODULE_AUTHOR("Ashwin Ganti");
77MODULE_LICENSE("GPL");
78
79struct cap_dev *cap_devices;
80
81void hexdump(unsigned char *buf, unsigned int len)
82{
83 while (len--)
84 printk("%02x", *buf++);
85 printk("\n");
86}
87
88int cap_trim(struct cap_dev *dev)
89{
90 struct cap_node *tmp;
91 struct list_head *pos, *q;
92 if (dev->head != NULL) {
93 list_for_each_safe(pos, q, &(dev->head->list)) {
94 tmp = list_entry(pos, struct cap_node, list);
95 list_del(pos);
96 kfree(tmp);
97 }
98 }
99 return 0;
100}
101
102int cap_open(struct inode *inode, struct file *filp)
103{
104 struct cap_dev *dev;
105 dev = container_of(inode->i_cdev, struct cap_dev, cdev);
106 filp->private_data = dev;
107
108 /* trim to 0 the length of the device if open was write-only */
109 if ((filp->f_flags & O_ACCMODE) == O_WRONLY) {
110 if (down_interruptible(&dev->sem))
111 return -ERESTARTSYS;
112 cap_trim(dev);
113 up(&dev->sem);
114 }
115 /* initialise the head if it is NULL */
116 if (dev->head == NULL) {
Greg Kroah-Hartman77dc1132009-02-24 20:21:55 -0800117 dev->head = kmalloc(sizeof(struct cap_node), GFP_KERNEL);
Ashwin Ganti55643172009-02-24 19:48:44 -0800118 INIT_LIST_HEAD(&(dev->head->list));
119 }
120 return 0;
121}
122
123int cap_release(struct inode *inode, struct file *filp)
124{
125 return 0;
126}
127
Greg Kroah-Hartman77dc1132009-02-24 20:21:55 -0800128ssize_t cap_write(struct file *filp, const char __user *buf, size_t count,
129 loff_t *f_pos)
Ashwin Ganti55643172009-02-24 19:48:44 -0800130{
131 struct cap_node *node_ptr, *tmp;
132 struct list_head *pos;
133 struct cap_dev *dev = filp->private_data;
134 ssize_t retval = -ENOMEM;
Greg Kroah-Hartman5dba0822009-02-24 20:06:34 -0800135 struct cred *new;
Ashwin Ganti55643172009-02-24 19:48:44 -0800136 int len, target_int, source_int, flag = 0;
137 char *user_buf, *user_buf_running, *source_user, *target_user,
138 *rand_str, *hash_str, *result;
139
140 if (down_interruptible(&dev->sem))
141 return -ERESTARTSYS;
142
Greg Kroah-Hartman77dc1132009-02-24 20:21:55 -0800143 node_ptr = kmalloc(sizeof(struct cap_node), GFP_KERNEL);
144 user_buf = kmalloc(count, GFP_KERNEL);
Ashwin Ganti55643172009-02-24 19:48:44 -0800145 memset(user_buf, 0, count);
146
147 if (copy_from_user(user_buf, buf, count)) {
148 retval = -EFAULT;
149 goto out;
150 }
151
Greg Kroah-Hartman77dc1132009-02-24 20:21:55 -0800152 /*
153 * If the minor number is 0 ( /dev/caphash ) then simply add the
Ashwin Ganti55643172009-02-24 19:48:44 -0800154 * hashed capability supplied by the user to the list of hashes
155 */
156 if (0 == iminor(filp->f_dentry->d_inode)) {
157 printk(KERN_INFO "Capability being written to /dev/caphash : \n");
158 hexdump(user_buf, count);
159 memcpy(node_ptr->data, user_buf, count);
160 list_add(&(node_ptr->list), &(dev->head->list));
161 } else {
Greg Kroah-Hartman77dc1132009-02-24 20:21:55 -0800162 /*
163 * break the supplied string into tokens with @ as the
164 * delimiter If the string is "user1@user2@randomstring" we
165 * need to split it and hash 'user1@user2' using 'randomstring'
166 * as the key.
Ashwin Ganti55643172009-02-24 19:48:44 -0800167 */
168 user_buf_running = kstrdup(user_buf, GFP_KERNEL);
169 source_user = strsep(&user_buf_running, "@");
170 target_user = strsep(&user_buf_running, "@");
171 rand_str = strsep(&user_buf_running, "@");
172
173 /* hash the string user1@user2 with rand_str as the key */
174 len = strlen(source_user) + strlen(target_user) + 1;
Greg Kroah-Hartman77dc1132009-02-24 20:21:55 -0800175 hash_str = kmalloc(len, GFP_KERNEL);
Ashwin Ganti55643172009-02-24 19:48:44 -0800176 memset(hash_str, 0, len);
177 strcat(hash_str, source_user);
178 strcat(hash_str, "@");
179 strcat(hash_str, target_user);
180
181 printk(KERN_ALERT "the source user is %s \n", source_user);
182 printk(KERN_ALERT "the target user is %s \n", target_user);
183
184 result =
185 cap_hash(hash_str, len, rand_str, strlen(rand_str));
186 if (NULL == result) {
187 retval = -EFAULT;
188 goto out;
189 }
190 memcpy(node_ptr->data, result, CAP_NODE_SIZE);
191 /* Change the process's uid if the hash is present in the
192 * list of hashes
193 */
194 list_for_each(pos, &(cap_devices->head->list)) {
Greg Kroah-Hartman77dc1132009-02-24 20:21:55 -0800195 /*
196 * Change the user id of the process if the hashes
197 * match
198 */
Ashwin Ganti55643172009-02-24 19:48:44 -0800199 if (0 ==
200 memcmp(result,
201 list_entry(pos, struct cap_node,
202 list)->data,
203 CAP_NODE_SIZE)) {
204 target_int = (unsigned int)
205 simple_strtol(target_user, NULL, 0);
206 source_int = (unsigned int)
207 simple_strtol(source_user, NULL, 0);
208 flag = 1;
209
Greg Kroah-Hartman77dc1132009-02-24 20:21:55 -0800210 /*
211 * Check whether the process writing to capuse
212 * is actually owned by the source owner
Ashwin Ganti55643172009-02-24 19:48:44 -0800213 */
Greg Kroah-Hartman5dba0822009-02-24 20:06:34 -0800214 if (source_int != current_uid()) {
Ashwin Ganti55643172009-02-24 19:48:44 -0800215 printk(KERN_ALERT
216 "Process is not owned by the source user of the capability.\n");
217 retval = -EFAULT;
218 goto out;
219 }
Greg Kroah-Hartman77dc1132009-02-24 20:21:55 -0800220 /*
221 * What all id's need to be changed here? uid,
222 * euid, fsid, savedids ?? Currently I am
223 * changing the effective user id since most of
224 * the authorisation decisions are based on it
Ashwin Ganti55643172009-02-24 19:48:44 -0800225 */
Greg Kroah-Hartman5dba0822009-02-24 20:06:34 -0800226 new = prepare_creds();
227 if (!new) {
228 retval = -ENOMEM;
229 goto out;
230 }
231 new->uid = (uid_t) target_int;
232 new->euid = (uid_t) target_int;
233 retval = commit_creds(new);
234 if (retval)
235 goto out;
Ashwin Ganti55643172009-02-24 19:48:44 -0800236
Greg Kroah-Hartman77dc1132009-02-24 20:21:55 -0800237 /*
238 * Remove the capability from the list and
239 * break
240 */
241 tmp = list_entry(pos, struct cap_node, list);
Ashwin Ganti55643172009-02-24 19:48:44 -0800242 list_del(pos);
243 kfree(tmp);
244 break;
245 }
246 }
247 if (0 == flag) {
Greg Kroah-Hartman77dc1132009-02-24 20:21:55 -0800248 /*
249 * The capability is not present in the list of the
250 * hashes stored, hence return failure
251 */
Ashwin Ganti55643172009-02-24 19:48:44 -0800252 printk(KERN_ALERT
253 "Invalid capabiliy written to /dev/capuse \n");
254 retval = -EFAULT;
255 goto out;
256 }
257 }
258 *f_pos += count;
259 retval = count;
260 /* update the size */
261 if (dev->size < *f_pos)
262 dev->size = *f_pos;
263
Greg Kroah-Hartman77dc1132009-02-24 20:21:55 -0800264out:
Ashwin Ganti55643172009-02-24 19:48:44 -0800265 up(&dev->sem);
266 return retval;
267}
268
Greg Kroah-Hartman77dc1132009-02-24 20:21:55 -0800269const struct file_operations cap_fops = {
Ashwin Ganti55643172009-02-24 19:48:44 -0800270 .owner = THIS_MODULE,
271 .write = cap_write,
272 .open = cap_open,
273 .release = cap_release,
274};
275
276
277void cap_cleanup_module(void)
278{
279 int i;
280 dev_t devno = MKDEV(cap_major, cap_minor);
281 if (cap_devices) {
282 for (i = 0; i < cap_nr_devs; i++) {
283 cap_trim(cap_devices + i);
284 cdev_del(&cap_devices[i].cdev);
285 }
286 kfree(cap_devices);
287 }
288 unregister_chrdev_region(devno, cap_nr_devs);
289
290}
291
292
293static void cap_setup_cdev(struct cap_dev *dev, int index)
294{
295 int err, devno = MKDEV(cap_major, cap_minor + index);
296 cdev_init(&dev->cdev, &cap_fops);
297 dev->cdev.owner = THIS_MODULE;
298 dev->cdev.ops = &cap_fops;
299 err = cdev_add(&dev->cdev, devno, 1);
300 if (err)
301 printk(KERN_NOTICE "Error %d adding cap%d", err, index);
302}
303
304
305int cap_init_module(void)
306{
307 int result, i;
308 dev_t dev = 0;
309
310 if (cap_major) {
311 dev = MKDEV(cap_major, cap_minor);
312 result = register_chrdev_region(dev, cap_nr_devs, "cap");
313 } else {
314 result = alloc_chrdev_region(&dev, cap_minor, cap_nr_devs,
315 "cap");
316 cap_major = MAJOR(dev);
317 }
318
319 if (result < 0) {
320 printk(KERN_WARNING "cap: can't get major %d\n",
321 cap_major);
322 return result;
323 }
324
325 cap_devices =
326 kmalloc(cap_nr_devs * sizeof(struct cap_dev), GFP_KERNEL);
327 if (!cap_devices) {
328 result = -ENOMEM;
329 goto fail;
330 }
331 memset(cap_devices, 0, cap_nr_devs * sizeof(struct cap_dev));
332
333 /* Initialize each device. */
334 for (i = 0; i < cap_nr_devs; i++) {
335 cap_devices[i].node_size = cap_node_size;
336 init_MUTEX(&cap_devices[i].sem);
337 cap_setup_cdev(&cap_devices[i], i);
338 }
339
340 return 0;
341
Greg Kroah-Hartman77dc1132009-02-24 20:21:55 -0800342fail:
Ashwin Ganti55643172009-02-24 19:48:44 -0800343 cap_cleanup_module();
344 return result;
345}
346
347module_init(cap_init_module);
348module_exit(cap_cleanup_module);
349
350char *cap_hash(char *plain_text, unsigned int plain_text_size,
351 char *key, unsigned int key_size)
352{
353 struct scatterlist sg;
Greg Kroah-Hartman77dc1132009-02-24 20:21:55 -0800354 char *result = kmalloc(MAX_DIGEST_SIZE, GFP_KERNEL);
Ashwin Ganti55643172009-02-24 19:48:44 -0800355 struct crypto_hash *tfm;
356 struct hash_desc desc;
357 int ret;
358
359 tfm = crypto_alloc_hash("hmac(sha1)", 0, CRYPTO_ALG_ASYNC);
360 if (IS_ERR(tfm)) {
Greg Kroah-Hartman77dc1132009-02-24 20:21:55 -0800361 printk(KERN_ERR
362 "failed to load transform for hmac(sha1): %ld\n",
Ashwin Ganti55643172009-02-24 19:48:44 -0800363 PTR_ERR(tfm));
364 kfree(result);
365 return NULL;
366 }
367
368 desc.tfm = tfm;
369 desc.flags = 0;
370
371 memset(result, 0, MAX_DIGEST_SIZE);
372 sg_set_buf(&sg, plain_text, plain_text_size);
373
374 ret = crypto_hash_setkey(tfm, key, key_size);
375 if (ret) {
Greg Kroah-Hartman77dc1132009-02-24 20:21:55 -0800376 printk(KERN_ERR "setkey() failed ret=%d\n", ret);
Ashwin Ganti55643172009-02-24 19:48:44 -0800377 kfree(result);
378 result = NULL;
379 goto out;
380 }
381
382 ret = crypto_hash_digest(&desc, &sg, plain_text_size, result);
383 if (ret) {
Greg Kroah-Hartman77dc1132009-02-24 20:21:55 -0800384 printk(KERN_ERR "digest () failed ret=%d\n", ret);
Ashwin Ganti55643172009-02-24 19:48:44 -0800385 kfree(result);
386 result = NULL;
387 goto out;
388 }
389
Greg Kroah-Hartman77dc1132009-02-24 20:21:55 -0800390 printk(KERN_DEBUG "crypto hash digest size %d\n",
Ashwin Ganti55643172009-02-24 19:48:44 -0800391 crypto_hash_digestsize(tfm));
392 hexdump(result, MAX_DIGEST_SIZE);
393
Greg Kroah-Hartman77dc1132009-02-24 20:21:55 -0800394out:
Ashwin Ganti55643172009-02-24 19:48:44 -0800395 crypto_free_hash(tfm);
396 return result;
397}