diff options
author | Ashwin Ganti <ashwin.ganti@gmail.com> | 2009-02-24 22:48:44 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-04-03 17:54:23 -0400 |
commit | 55643171de7ba429fbf2cb72fb1f2c6f2df0dcf3 (patch) | |
tree | a54bd3f44bb11ffd590219fb82ae440da5b5e7c3 /drivers/staging/p9auth | |
parent | bea3e747f3ff1abe8d7f730bbf7d195f0e14d6ad (diff) |
Staging: add p9auth driver
This is a driver that adds Plan 9 style capability device
implementation.
From: Ashwin Ganti <ashwin.ganti@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging/p9auth')
-rw-r--r-- | drivers/staging/p9auth/p9auth.c | 347 | ||||
-rw-r--r-- | drivers/staging/p9auth/p9auth.h | 35 |
2 files changed, 382 insertions, 0 deletions
diff --git a/drivers/staging/p9auth/p9auth.c b/drivers/staging/p9auth/p9auth.c new file mode 100644 index 00000000000..6704d97194a --- /dev/null +++ b/drivers/staging/p9auth/p9auth.c | |||
@@ -0,0 +1,347 @@ | |||
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/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> | ||
34 | #include "p9auth.h" | ||
35 | |||
36 | int cap_major = CAP_MAJOR; | ||
37 | int cap_minor = 0; | ||
38 | int cap_nr_devs = CAP_NR_DEVS; | ||
39 | int cap_node_size = CAP_NODE_SIZE; | ||
40 | |||
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); | ||
44 | |||
45 | MODULE_AUTHOR("Ashwin Ganti"); | ||
46 | MODULE_LICENSE("GPL"); | ||
47 | |||
48 | struct cap_dev *cap_devices; | ||
49 | |||
50 | void hexdump(unsigned char *buf, unsigned int len) | ||
51 | { | ||
52 | while (len--) | ||
53 | printk("%02x", *buf++); | ||
54 | printk("\n"); | ||
55 | } | ||
56 | |||
57 | int cap_trim(struct cap_dev *dev) | ||
58 | { | ||
59 | struct cap_node *tmp; | ||
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); | ||
64 | list_del(pos); | ||
65 | kfree(tmp); | ||
66 | } | ||
67 | } | ||
68 | return 0; | ||
69 | } | ||
70 | |||
71 | int cap_open(struct inode *inode, struct file *filp) | ||
72 | { | ||
73 | struct cap_dev *dev; | ||
74 | dev = container_of(inode->i_cdev, struct cap_dev, cdev); | ||
75 | filp->private_data = dev; | ||
76 | |||
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)) | ||
80 | return -ERESTARTSYS; | ||
81 | cap_trim(dev); | ||
82 | up(&dev->sem); | ||
83 | } | ||
84 | /* initialise the head if it is NULL */ | ||
85 | if (dev->head == NULL) { | ||
86 | dev->head = | ||
87 | (struct cap_node *) kmalloc(sizeof(struct cap_node), | ||
88 | GFP_KERNEL); | ||
89 | INIT_LIST_HEAD(&(dev->head->list)); | ||
90 | } | ||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | int cap_release(struct inode *inode, struct file *filp) | ||
95 | { | ||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | ssize_t | ||
100 | cap_write(struct file * filp, const char __user * buf, | ||
101 | size_t count, loff_t * f_pos) | ||
102 | { | ||
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; | ||
110 | |||
111 | if (down_interruptible(&dev->sem)) | ||
112 | return -ERESTARTSYS; | ||
113 | |||
114 | node_ptr = | ||
115 | (struct cap_node *) kmalloc(sizeof(struct cap_node), | ||
116 | GFP_KERNEL); | ||
117 | user_buf = (char *) kmalloc(count, GFP_KERNEL); | ||
118 | memset(user_buf, 0, count); | ||
119 | |||
120 | if (copy_from_user(user_buf, buf, count)) { | ||
121 | retval = -EFAULT; | ||
122 | goto out; | ||
123 | } | ||
124 | |||
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 | ||
127 | */ | ||
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)); | ||
133 | } else { | ||
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 | ||
137 | */ | ||
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, "@"); | ||
142 | |||
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); | ||
150 | |||
151 | printk(KERN_ALERT "the source user is %s \n", source_user); | ||
152 | printk(KERN_ALERT "the target user is %s \n", target_user); | ||
153 | |||
154 | result = | ||
155 | cap_hash(hash_str, len, rand_str, strlen(rand_str)); | ||
156 | if (NULL == result) { | ||
157 | retval = -EFAULT; | ||
158 | goto out; | ||
159 | } | ||
160 | memcpy(node_ptr->data, result, CAP_NODE_SIZE); | ||
161 | /* Change the process's uid if the hash is present in the | ||
162 | * list of hashes | ||
163 | */ | ||
164 | list_for_each(pos, &(cap_devices->head->list)) { | ||
165 | /* Change the user id of the process if the hashes match */ | ||
166 | if (0 == | ||
167 | memcmp(result, | ||
168 | list_entry(pos, struct cap_node, | ||
169 | list)->data, | ||
170 | CAP_NODE_SIZE)) { | ||
171 | target_int = (unsigned int) | ||
172 | simple_strtol(target_user, NULL, 0); | ||
173 | source_int = (unsigned int) | ||
174 | simple_strtol(source_user, NULL, 0); | ||
175 | flag = 1; | ||
176 | |||
177 | /* Check whether the process writing to capuse is actually owned by | ||
178 | * the source owner | ||
179 | */ | ||
180 | if (source_int != current->uid) { | ||
181 | printk(KERN_ALERT | ||
182 | "Process is not owned by the source user of the capability.\n"); | ||
183 | retval = -EFAULT; | ||
184 | goto out; | ||
185 | } | ||
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 | ||
189 | */ | ||
190 | current->uid = (uid_t) target_int; | ||
191 | current->euid = (uid_t) target_int; | ||
192 | |||
193 | /* Remove the capability from the list and break */ | ||
194 | tmp = | ||
195 | list_entry(pos, struct cap_node, list); | ||
196 | list_del(pos); | ||
197 | kfree(tmp); | ||
198 | break; | ||
199 | } | ||
200 | } | ||
201 | if (0 == flag) { | ||
202 | /* The capability is not present in the list of the hashes stored, hence return failure */ | ||
203 | printk(KERN_ALERT | ||
204 | "Invalid capabiliy written to /dev/capuse \n"); | ||
205 | retval = -EFAULT; | ||
206 | goto out; | ||
207 | } | ||
208 | } | ||
209 | *f_pos += count; | ||
210 | retval = count; | ||
211 | /* update the size */ | ||
212 | if (dev->size < *f_pos) | ||
213 | dev->size = *f_pos; | ||
214 | |||
215 | out: | ||
216 | up(&dev->sem); | ||
217 | return retval; | ||
218 | } | ||
219 | |||
220 | struct file_operations cap_fops = { | ||
221 | .owner = THIS_MODULE, | ||
222 | .write = cap_write, | ||
223 | .open = cap_open, | ||
224 | .release = cap_release, | ||
225 | }; | ||
226 | |||
227 | |||
228 | void cap_cleanup_module(void) | ||
229 | { | ||
230 | int i; | ||
231 | dev_t devno = MKDEV(cap_major, cap_minor); | ||
232 | if (cap_devices) { | ||
233 | for (i = 0; i < cap_nr_devs; i++) { | ||
234 | cap_trim(cap_devices + i); | ||
235 | cdev_del(&cap_devices[i].cdev); | ||
236 | } | ||
237 | kfree(cap_devices); | ||
238 | } | ||
239 | unregister_chrdev_region(devno, cap_nr_devs); | ||
240 | |||
241 | } | ||
242 | |||
243 | |||
244 | static void cap_setup_cdev(struct cap_dev *dev, int index) | ||
245 | { | ||
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); | ||
251 | if (err) | ||
252 | printk(KERN_NOTICE "Error %d adding cap%d", err, index); | ||
253 | } | ||
254 | |||
255 | |||
256 | int cap_init_module(void) | ||
257 | { | ||
258 | int result, i; | ||
259 | dev_t dev = 0; | ||
260 | |||
261 | if (cap_major) { | ||
262 | dev = MKDEV(cap_major, cap_minor); | ||
263 | result = register_chrdev_region(dev, cap_nr_devs, "cap"); | ||
264 | } else { | ||
265 | result = alloc_chrdev_region(&dev, cap_minor, cap_nr_devs, | ||
266 | "cap"); | ||
267 | cap_major = MAJOR(dev); | ||
268 | } | ||
269 | |||
270 | if (result < 0) { | ||
271 | printk(KERN_WARNING "cap: can't get major %d\n", | ||
272 | cap_major); | ||
273 | return result; | ||
274 | } | ||
275 | |||
276 | cap_devices = | ||
277 | kmalloc(cap_nr_devs * sizeof(struct cap_dev), GFP_KERNEL); | ||
278 | if (!cap_devices) { | ||
279 | result = -ENOMEM; | ||
280 | goto fail; | ||
281 | } | ||
282 | memset(cap_devices, 0, cap_nr_devs * sizeof(struct cap_dev)); | ||
283 | |||
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); | ||
289 | } | ||
290 | |||
291 | return 0; | ||
292 | |||
293 | fail: | ||
294 | cap_cleanup_module(); | ||
295 | return result; | ||
296 | } | ||
297 | |||
298 | module_init(cap_init_module); | ||
299 | module_exit(cap_cleanup_module); | ||
300 | |||
301 | char *cap_hash(char *plain_text, unsigned int plain_text_size, | ||
302 | char *key, unsigned int key_size) | ||
303 | { | ||
304 | struct scatterlist sg; | ||
305 | char *result = (char *) kmalloc(MAX_DIGEST_SIZE, GFP_KERNEL); | ||
306 | struct crypto_hash *tfm; | ||
307 | struct hash_desc desc; | ||
308 | int ret; | ||
309 | |||
310 | tfm = crypto_alloc_hash("hmac(sha1)", 0, CRYPTO_ALG_ASYNC); | ||
311 | if (IS_ERR(tfm)) { | ||
312 | printk("failed to load transform for hmac(sha1): %ld\n", | ||
313 | PTR_ERR(tfm)); | ||
314 | kfree(result); | ||
315 | return NULL; | ||
316 | } | ||
317 | |||
318 | desc.tfm = tfm; | ||
319 | desc.flags = 0; | ||
320 | |||
321 | memset(result, 0, MAX_DIGEST_SIZE); | ||
322 | sg_set_buf(&sg, plain_text, plain_text_size); | ||
323 | |||
324 | ret = crypto_hash_setkey(tfm, key, key_size); | ||
325 | if (ret) { | ||
326 | printk("setkey() failed ret=%d\n", ret); | ||
327 | kfree(result); | ||
328 | result = NULL; | ||
329 | goto out; | ||
330 | } | ||
331 | |||
332 | ret = crypto_hash_digest(&desc, &sg, plain_text_size, result); | ||
333 | if (ret) { | ||
334 | printk("digest () failed ret=%d\n", ret); | ||
335 | kfree(result); | ||
336 | result = NULL; | ||
337 | goto out; | ||
338 | } | ||
339 | |||
340 | printk("crypto hash digest size %d\n", | ||
341 | crypto_hash_digestsize(tfm)); | ||
342 | hexdump(result, MAX_DIGEST_SIZE); | ||
343 | |||
344 | out: | ||
345 | crypto_free_hash(tfm); | ||
346 | return result; | ||
347 | } | ||
diff --git a/drivers/staging/p9auth/p9auth.h b/drivers/staging/p9auth/p9auth.h new file mode 100644 index 00000000000..285d1d8c917 --- /dev/null +++ b/drivers/staging/p9auth/p9auth.h | |||
@@ -0,0 +1,35 @@ | |||
1 | #ifndef CAP_MAJOR | ||
2 | #define CAP_MAJOR 0 | ||
3 | #endif | ||
4 | |||
5 | #ifndef CAP_NR_DEVS | ||
6 | #define CAP_NR_DEVS 2 /* caphash and capuse */ | ||
7 | #endif | ||
8 | |||
9 | #ifndef CAP_NODE_SIZE | ||
10 | #define CAP_NODE_SIZE 20 | ||
11 | #endif | ||
12 | |||
13 | #define MAX_DIGEST_SIZE 20 | ||
14 | |||
15 | struct cap_node { | ||
16 | char data[CAP_NODE_SIZE]; | ||
17 | struct list_head list; | ||
18 | }; | ||
19 | |||
20 | struct cap_dev { | ||
21 | struct cap_node *head; | ||
22 | int node_size; | ||
23 | unsigned long size; | ||
24 | struct semaphore sem; | ||
25 | struct cdev cdev; | ||
26 | }; | ||
27 | |||
28 | extern int cap_major; | ||
29 | extern int cap_nr_devs; | ||
30 | extern int cap_node_size; | ||
31 | |||
32 | int cap_trim(struct cap_dev *); | ||
33 | ssize_t cap_write(struct file *, const char __user *, size_t, loff_t *); | ||
34 | char *cap_hash(char *plain_text, unsigned int plain_text_size, char *key, unsigned int key_size); | ||
35 | void hex_dump(unsigned char * buf, unsigned int len); | ||