Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.32-rc4 407 lines 9.4 kB view raw
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 41struct cap_node { 42 char data[CAP_NODE_SIZE]; 43 struct list_head list; 44}; 45 46struct 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 54static int cap_major = CAP_MAJOR; 55static int cap_minor; 56static int cap_nr_devs = CAP_NR_DEVS; 57static int cap_node_size = CAP_NODE_SIZE; 58 59module_param(cap_major, int, S_IRUGO); 60module_param(cap_minor, int, S_IRUGO); 61module_param(cap_nr_devs, int, S_IRUGO); 62 63MODULE_AUTHOR("Ashwin Ganti"); 64MODULE_LICENSE("GPL"); 65 66static struct cap_dev *cap_devices; 67 68static void hexdump(unsigned char *buf, unsigned int len) 69{ 70 while (len--) 71 printk("%02x", *buf++); 72 printk("\n"); 73} 74 75static 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 123out: 124 crypto_free_hash(tfm); 125 return result; 126} 127 128static 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 142static 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 163static int cap_release(struct inode *inode, struct file *filp) 164{ 165 return 0; 166} 167 168static 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 321out: 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 330static const struct file_operations cap_fops = { 331 .owner = THIS_MODULE, 332 .write = cap_write, 333 .open = cap_open, 334 .release = cap_release, 335}; 336 337static 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 352static 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 363static 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 399fail: 400 cap_cleanup_module(); 401 return result; 402} 403 404module_init(cap_init_module); 405module_exit(cap_cleanup_module); 406 407