-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Ashwin Ganti
authored and
Greg Kroah-Hartman
committed
Apr 3, 2009
1 parent
f7bb19d
commit a55c30f
Showing
3 changed files
with
383 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
--- | ||
refs/heads/master: bea3e747f3ff1abe8d7f730bbf7d195f0e14d6ad | ||
refs/heads/master: 55643171de7ba429fbf2cb72fb1f2c6f2df0dcf3 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,347 @@ | ||
/* | ||
* Plan 9 style capability device implementation for the Linux Kernel | ||
* | ||
* Copyright 2008, 2009 Ashwin Ganti <ashwin.ganti@gmail.com> | ||
* | ||
* Released under the GPLv2 | ||
* | ||
*/ | ||
#include <linux/module.h> | ||
#include <linux/moduleparam.h> | ||
#include <linux/init.h> | ||
#include <linux/kernel.h> | ||
#include <linux/slab.h> | ||
#include <linux/fs.h> | ||
#include <linux/errno.h> | ||
#include <linux/types.h> | ||
#include <linux/proc_fs.h> | ||
#include <linux/fcntl.h> | ||
#include <linux/cdev.h> | ||
#include <linux/syscalls.h> | ||
#include <asm/system.h> | ||
#include <asm/uaccess.h> | ||
#include <linux/list.h> | ||
#include <linux/err.h> | ||
#include <linux/mm.h> | ||
#include <linux/string.h> | ||
#include <linux/crypto.h> | ||
#include <linux/highmem.h> | ||
#include <linux/jiffies.h> | ||
#include <linux/timex.h> | ||
#include <linux/interrupt.h> | ||
#include <linux/scatterlist.h> | ||
#include <linux/crypto.h> | ||
#include "p9auth.h" | ||
|
||
int cap_major = CAP_MAJOR; | ||
int cap_minor = 0; | ||
int cap_nr_devs = CAP_NR_DEVS; | ||
int cap_node_size = CAP_NODE_SIZE; | ||
|
||
module_param(cap_major, int, S_IRUGO); | ||
module_param(cap_minor, int, S_IRUGO); | ||
module_param(cap_nr_devs, int, S_IRUGO); | ||
|
||
MODULE_AUTHOR("Ashwin Ganti"); | ||
MODULE_LICENSE("GPL"); | ||
|
||
struct cap_dev *cap_devices; | ||
|
||
void hexdump(unsigned char *buf, unsigned int len) | ||
{ | ||
while (len--) | ||
printk("%02x", *buf++); | ||
printk("\n"); | ||
} | ||
|
||
int cap_trim(struct cap_dev *dev) | ||
{ | ||
struct cap_node *tmp; | ||
struct list_head *pos, *q; | ||
if (dev->head != NULL) { | ||
list_for_each_safe(pos, q, &(dev->head->list)) { | ||
tmp = list_entry(pos, struct cap_node, list); | ||
list_del(pos); | ||
kfree(tmp); | ||
} | ||
} | ||
return 0; | ||
} | ||
|
||
int cap_open(struct inode *inode, struct file *filp) | ||
{ | ||
struct cap_dev *dev; | ||
dev = container_of(inode->i_cdev, struct cap_dev, cdev); | ||
filp->private_data = dev; | ||
|
||
/* trim to 0 the length of the device if open was write-only */ | ||
if ((filp->f_flags & O_ACCMODE) == O_WRONLY) { | ||
if (down_interruptible(&dev->sem)) | ||
return -ERESTARTSYS; | ||
cap_trim(dev); | ||
up(&dev->sem); | ||
} | ||
/* initialise the head if it is NULL */ | ||
if (dev->head == NULL) { | ||
dev->head = | ||
(struct cap_node *) kmalloc(sizeof(struct cap_node), | ||
GFP_KERNEL); | ||
INIT_LIST_HEAD(&(dev->head->list)); | ||
} | ||
return 0; | ||
} | ||
|
||
int cap_release(struct inode *inode, struct file *filp) | ||
{ | ||
return 0; | ||
} | ||
|
||
ssize_t | ||
cap_write(struct file * filp, const char __user * buf, | ||
size_t count, loff_t * f_pos) | ||
{ | ||
struct cap_node *node_ptr, *tmp; | ||
struct list_head *pos; | ||
struct cap_dev *dev = filp->private_data; | ||
ssize_t retval = -ENOMEM; | ||
int len, target_int, source_int, flag = 0; | ||
char *user_buf, *user_buf_running, *source_user, *target_user, | ||
*rand_str, *hash_str, *result; | ||
|
||
if (down_interruptible(&dev->sem)) | ||
return -ERESTARTSYS; | ||
|
||
node_ptr = | ||
(struct cap_node *) kmalloc(sizeof(struct cap_node), | ||
GFP_KERNEL); | ||
user_buf = (char *) kmalloc(count, GFP_KERNEL); | ||
memset(user_buf, 0, count); | ||
|
||
if (copy_from_user(user_buf, buf, count)) { | ||
retval = -EFAULT; | ||
goto out; | ||
} | ||
|
||
/* If the minor number is 0 ( /dev/caphash ) then simply add the | ||
* hashed capability supplied by the user to the list of hashes | ||
*/ | ||
if (0 == iminor(filp->f_dentry->d_inode)) { | ||
printk(KERN_INFO "Capability being written to /dev/caphash : \n"); | ||
hexdump(user_buf, count); | ||
memcpy(node_ptr->data, user_buf, count); | ||
list_add(&(node_ptr->list), &(dev->head->list)); | ||
} else { | ||
/* break the supplied string into tokens with @ as the delimiter | ||
If the string is "user1@user2@randomstring" we need to split it | ||
and hash 'user1@user2' using 'randomstring' as the key | ||
*/ | ||
user_buf_running = kstrdup(user_buf, GFP_KERNEL); | ||
source_user = strsep(&user_buf_running, "@"); | ||
target_user = strsep(&user_buf_running, "@"); | ||
rand_str = strsep(&user_buf_running, "@"); | ||
|
||
/* hash the string user1@user2 with rand_str as the key */ | ||
len = strlen(source_user) + strlen(target_user) + 1; | ||
hash_str = (char *) kmalloc(len, GFP_KERNEL); | ||
memset(hash_str, 0, len); | ||
strcat(hash_str, source_user); | ||
strcat(hash_str, "@"); | ||
strcat(hash_str, target_user); | ||
|
||
printk(KERN_ALERT "the source user is %s \n", source_user); | ||
printk(KERN_ALERT "the target user is %s \n", target_user); | ||
|
||
result = | ||
cap_hash(hash_str, len, rand_str, strlen(rand_str)); | ||
if (NULL == result) { | ||
retval = -EFAULT; | ||
goto out; | ||
} | ||
memcpy(node_ptr->data, result, CAP_NODE_SIZE); | ||
/* Change the process's uid if the hash is present in the | ||
* list of hashes | ||
*/ | ||
list_for_each(pos, &(cap_devices->head->list)) { | ||
/* Change the user id of the process if the hashes match */ | ||
if (0 == | ||
memcmp(result, | ||
list_entry(pos, struct cap_node, | ||
list)->data, | ||
CAP_NODE_SIZE)) { | ||
target_int = (unsigned int) | ||
simple_strtol(target_user, NULL, 0); | ||
source_int = (unsigned int) | ||
simple_strtol(source_user, NULL, 0); | ||
flag = 1; | ||
|
||
/* Check whether the process writing to capuse is actually owned by | ||
* the source owner | ||
*/ | ||
if (source_int != current->uid) { | ||
printk(KERN_ALERT | ||
"Process is not owned by the source user of the capability.\n"); | ||
retval = -EFAULT; | ||
goto out; | ||
} | ||
/* What all id's need to be changed here? uid, euid, fsid, savedids ?? | ||
* Currently I am changing the effective user id | ||
* since most of the authorisation decisions are based on it | ||
*/ | ||
current->uid = (uid_t) target_int; | ||
current->euid = (uid_t) target_int; | ||
|
||
/* Remove the capability from the list and break */ | ||
tmp = | ||
list_entry(pos, struct cap_node, list); | ||
list_del(pos); | ||
kfree(tmp); | ||
break; | ||
} | ||
} | ||
if (0 == flag) { | ||
/* The capability is not present in the list of the hashes stored, hence return failure */ | ||
printk(KERN_ALERT | ||
"Invalid capabiliy written to /dev/capuse \n"); | ||
retval = -EFAULT; | ||
goto out; | ||
} | ||
} | ||
*f_pos += count; | ||
retval = count; | ||
/* update the size */ | ||
if (dev->size < *f_pos) | ||
dev->size = *f_pos; | ||
|
||
out: | ||
up(&dev->sem); | ||
return retval; | ||
} | ||
|
||
struct file_operations cap_fops = { | ||
.owner = THIS_MODULE, | ||
.write = cap_write, | ||
.open = cap_open, | ||
.release = cap_release, | ||
}; | ||
|
||
|
||
void cap_cleanup_module(void) | ||
{ | ||
int i; | ||
dev_t devno = MKDEV(cap_major, cap_minor); | ||
if (cap_devices) { | ||
for (i = 0; i < cap_nr_devs; i++) { | ||
cap_trim(cap_devices + i); | ||
cdev_del(&cap_devices[i].cdev); | ||
} | ||
kfree(cap_devices); | ||
} | ||
unregister_chrdev_region(devno, cap_nr_devs); | ||
|
||
} | ||
|
||
|
||
static void cap_setup_cdev(struct cap_dev *dev, int index) | ||
{ | ||
int err, devno = MKDEV(cap_major, cap_minor + index); | ||
cdev_init(&dev->cdev, &cap_fops); | ||
dev->cdev.owner = THIS_MODULE; | ||
dev->cdev.ops = &cap_fops; | ||
err = cdev_add(&dev->cdev, devno, 1); | ||
if (err) | ||
printk(KERN_NOTICE "Error %d adding cap%d", err, index); | ||
} | ||
|
||
|
||
int cap_init_module(void) | ||
{ | ||
int result, i; | ||
dev_t dev = 0; | ||
|
||
if (cap_major) { | ||
dev = MKDEV(cap_major, cap_minor); | ||
result = register_chrdev_region(dev, cap_nr_devs, "cap"); | ||
} else { | ||
result = alloc_chrdev_region(&dev, cap_minor, cap_nr_devs, | ||
"cap"); | ||
cap_major = MAJOR(dev); | ||
} | ||
|
||
if (result < 0) { | ||
printk(KERN_WARNING "cap: can't get major %d\n", | ||
cap_major); | ||
return result; | ||
} | ||
|
||
cap_devices = | ||
kmalloc(cap_nr_devs * sizeof(struct cap_dev), GFP_KERNEL); | ||
if (!cap_devices) { | ||
result = -ENOMEM; | ||
goto fail; | ||
} | ||
memset(cap_devices, 0, cap_nr_devs * sizeof(struct cap_dev)); | ||
|
||
/* Initialize each device. */ | ||
for (i = 0; i < cap_nr_devs; i++) { | ||
cap_devices[i].node_size = cap_node_size; | ||
init_MUTEX(&cap_devices[i].sem); | ||
cap_setup_cdev(&cap_devices[i], i); | ||
} | ||
|
||
return 0; | ||
|
||
fail: | ||
cap_cleanup_module(); | ||
return result; | ||
} | ||
|
||
module_init(cap_init_module); | ||
module_exit(cap_cleanup_module); | ||
|
||
char *cap_hash(char *plain_text, unsigned int plain_text_size, | ||
char *key, unsigned int key_size) | ||
{ | ||
struct scatterlist sg; | ||
char *result = (char *) kmalloc(MAX_DIGEST_SIZE, GFP_KERNEL); | ||
struct crypto_hash *tfm; | ||
struct hash_desc desc; | ||
int ret; | ||
|
||
tfm = crypto_alloc_hash("hmac(sha1)", 0, CRYPTO_ALG_ASYNC); | ||
if (IS_ERR(tfm)) { | ||
printk("failed to load transform for hmac(sha1): %ld\n", | ||
PTR_ERR(tfm)); | ||
kfree(result); | ||
return NULL; | ||
} | ||
|
||
desc.tfm = tfm; | ||
desc.flags = 0; | ||
|
||
memset(result, 0, MAX_DIGEST_SIZE); | ||
sg_set_buf(&sg, plain_text, plain_text_size); | ||
|
||
ret = crypto_hash_setkey(tfm, key, key_size); | ||
if (ret) { | ||
printk("setkey() failed ret=%d\n", ret); | ||
kfree(result); | ||
result = NULL; | ||
goto out; | ||
} | ||
|
||
ret = crypto_hash_digest(&desc, &sg, plain_text_size, result); | ||
if (ret) { | ||
printk("digest () failed ret=%d\n", ret); | ||
kfree(result); | ||
result = NULL; | ||
goto out; | ||
} | ||
|
||
printk("crypto hash digest size %d\n", | ||
crypto_hash_digestsize(tfm)); | ||
hexdump(result, MAX_DIGEST_SIZE); | ||
|
||
out: | ||
crypto_free_hash(tfm); | ||
return result; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
#ifndef CAP_MAJOR | ||
#define CAP_MAJOR 0 | ||
#endif | ||
|
||
#ifndef CAP_NR_DEVS | ||
#define CAP_NR_DEVS 2 /* caphash and capuse */ | ||
#endif | ||
|
||
#ifndef CAP_NODE_SIZE | ||
#define CAP_NODE_SIZE 20 | ||
#endif | ||
|
||
#define MAX_DIGEST_SIZE 20 | ||
|
||
struct cap_node { | ||
char data[CAP_NODE_SIZE]; | ||
struct list_head list; | ||
}; | ||
|
||
struct cap_dev { | ||
struct cap_node *head; | ||
int node_size; | ||
unsigned long size; | ||
struct semaphore sem; | ||
struct cdev cdev; | ||
}; | ||
|
||
extern int cap_major; | ||
extern int cap_nr_devs; | ||
extern int cap_node_size; | ||
|
||
int cap_trim(struct cap_dev *); | ||
ssize_t cap_write(struct file *, const char __user *, size_t, loff_t *); | ||
char *cap_hash(char *plain_text, unsigned int plain_text_size, char *key, unsigned int key_size); | ||
void hex_dump(unsigned char * buf, unsigned int len); |