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/init.h>
10 #include <linux/kernel.h>
11 #include <linux/moduleparam.h>
12 #include <linux/slab.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>
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>
32 #define CAP_NR_DEVS 2 /* caphash and capuse */
36 #define CAP_NODE_SIZE 20
39 #define MAX_DIGEST_SIZE 20
42 char data
[CAP_NODE_SIZE
];
43 struct list_head list
;
47 struct cap_node
*head
;
54 static int cap_major
= CAP_MAJOR
;
56 static int cap_nr_devs
= CAP_NR_DEVS
;
57 static int cap_node_size
= CAP_NODE_SIZE
;
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
);
63 MODULE_AUTHOR("Ashwin Ganti");
64 MODULE_LICENSE("GPL");
66 static struct cap_dev
*cap_devices
;
68 static void hexdump(unsigned char *buf
, unsigned int len
)
71 printk("%02x", *buf
++);
75 static char *cap_hash(char *plain_text
, unsigned int plain_text_size
,
76 char *key
, unsigned int key_size
)
78 struct scatterlist sg
;
80 struct crypto_hash
*tfm
;
81 struct hash_desc desc
;
84 tfm
= crypto_alloc_hash("hmac(sha1)", 0, CRYPTO_ALG_ASYNC
);
87 "failed to load transform for hmac(sha1): %ld\n",
95 result
= kzalloc(MAX_DIGEST_SIZE
, GFP_KERNEL
);
97 printk(KERN_ERR
"out of memory!\n");
101 sg_set_buf(&sg
, plain_text
, plain_text_size
);
103 ret
= crypto_hash_setkey(tfm
, key
, key_size
);
105 printk(KERN_ERR
"setkey() failed ret=%d\n", ret
);
111 ret
= crypto_hash_digest(&desc
, &sg
, plain_text_size
, result
);
113 printk(KERN_ERR
"digest () failed ret=%d\n", ret
);
119 printk(KERN_DEBUG
"crypto hash digest size %d\n",
120 crypto_hash_digestsize(tfm
));
121 hexdump(result
, MAX_DIGEST_SIZE
);
124 crypto_free_hash(tfm
);
128 static int cap_trim(struct cap_dev
*dev
)
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
);
142 static int cap_open(struct inode
*inode
, struct file
*filp
)
145 dev
= container_of(inode
->i_cdev
, struct cap_dev
, cdev
);
146 filp
->private_data
= dev
;
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
))
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
));
163 static int cap_release(struct inode
*inode
, struct file
*filp
)
168 static ssize_t
cap_write(struct file
*filp
, const char __user
*buf
,
169 size_t count
, loff_t
*f_pos
)
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
;
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
;
180 if (down_interruptible(&dev
->sem
))
183 user_buf_running
= NULL
;
185 node_ptr
= kmalloc(sizeof(struct cap_node
), GFP_KERNEL
);
186 user_buf
= kzalloc(count
, GFP_KERNEL
);
187 if (!node_ptr
|| !user_buf
)
190 if (copy_from_user(user_buf
, buf
, count
)) {
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
199 if (0 == iminor(filp
->f_dentry
->d_inode
)) {
200 if (count
> CAP_NODE_SIZE
) {
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
));
210 if (!cap_devices
[0].head
||
211 list_empty(&(cap_devices
[0].head
->list
))) {
216 * break the supplied string into tokens with @ as the
217 * delimiter If the string is "user1@user2@randomstring" we
218 * need to split it and hash 'user1@user2' using 'randomstring'
221 user_buf_running
= kstrdup(user_buf
, GFP_KERNEL
);
222 source_user
= strsep(&user_buf_running
, "@");
223 target_user
= strsep(&user_buf_running
, "@");
224 rand_str
= strsep(&user_buf_running
, "@");
225 if (!source_user
|| !target_user
|| !rand_str
) {
230 /* hash the string user1@user2 with rand_str as the key */
231 len
= strlen(source_user
) + strlen(target_user
) + 1;
232 hash_str
= kzalloc(len
, GFP_KERNEL
);
233 strcat(hash_str
, source_user
);
234 strcat(hash_str
, "@");
235 strcat(hash_str
, target_user
);
237 printk(KERN_ALERT
"the source user is %s \n", source_user
);
238 printk(KERN_ALERT
"the target user is %s \n", target_user
);
240 result
= cap_hash(hash_str
, len
, rand_str
, strlen(rand_str
));
241 if (NULL
== result
) {
245 memcpy(node_ptr
->data
, result
, CAP_NODE_SIZE
); /* why? */
246 /* Change the process's uid if the hash is present in the
249 list_for_each(pos
, &(cap_devices
->head
->list
)) {
251 * Change the user id of the process if the hashes
256 list_entry(pos
, struct cap_node
,
259 target_int
= (unsigned int)
260 simple_strtol(target_user
, NULL
, 0);
261 source_int
= (unsigned int)
262 simple_strtol(source_user
, NULL
, 0);
266 * Check whether the process writing to capuse
267 * is actually owned by the source owner
269 if (source_int
!= current_uid()) {
271 "Process is not owned by the source user of the capability.\n");
276 * What all id's need to be changed here? uid,
277 * euid, fsid, savedids ?? Currently I am
278 * changing the effective user id since most of
279 * the authorisation decisions are based on it
281 new = prepare_creds();
286 new->uid
= (uid_t
) target_int
;
287 new->euid
= (uid_t
) target_int
;
288 retval
= commit_creds(new);
293 * Remove the capability from the list and
296 tmp
= list_entry(pos
, struct cap_node
, list
);
304 * The capability is not present in the list of the
305 * hashes stored, hence return failure
308 "Invalid capabiliy written to /dev/capuse \n");
315 /* update the size */
316 if (dev
->size
< *f_pos
)
322 kfree(user_buf_running
);
328 static const struct file_operations cap_fops
= {
329 .owner
= THIS_MODULE
,
332 .release
= cap_release
,
335 static void cap_cleanup_module(void)
338 dev_t devno
= MKDEV(cap_major
, cap_minor
);
340 for (i
= 0; i
< cap_nr_devs
; i
++) {
341 cap_trim(cap_devices
+ i
);
342 cdev_del(&cap_devices
[i
].cdev
);
346 unregister_chrdev_region(devno
, cap_nr_devs
);
350 static void cap_setup_cdev(struct cap_dev
*dev
, int index
)
352 int err
, devno
= MKDEV(cap_major
, cap_minor
+ index
);
353 cdev_init(&dev
->cdev
, &cap_fops
);
354 dev
->cdev
.owner
= THIS_MODULE
;
355 dev
->cdev
.ops
= &cap_fops
;
356 err
= cdev_add(&dev
->cdev
, devno
, 1);
358 printk(KERN_NOTICE
"Error %d adding cap%d", err
, index
);
361 static int cap_init_module(void)
367 dev
= MKDEV(cap_major
, cap_minor
);
368 result
= register_chrdev_region(dev
, cap_nr_devs
, "cap");
370 result
= alloc_chrdev_region(&dev
, cap_minor
, cap_nr_devs
,
372 cap_major
= MAJOR(dev
);
376 printk(KERN_WARNING
"cap: can't get major %d\n",
381 cap_devices
= kzalloc(cap_nr_devs
* sizeof(struct cap_dev
),
388 /* Initialize each device. */
389 for (i
= 0; i
< cap_nr_devs
; i
++) {
390 cap_devices
[i
].node_size
= cap_node_size
;
391 init_MUTEX(&cap_devices
[i
].sem
);
392 cap_setup_cdev(&cap_devices
[i
], i
);
398 cap_cleanup_module();
402 module_init(cap_init_module
);
403 module_exit(cap_cleanup_module
);