linux/drivers/staging/p9auth/p9auth.c
<<
>>
Prefs
   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
 408