diff options
author | Lennart Poettering <lennart@poettering.net> | 2003-11-05 10:44:19 +0000 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2003-11-05 10:44:19 +0000 |
commit | 7ec5099b5f5c79419a54a6cdedea618d98b60569 (patch) | |
tree | f1bed2425e622919435fe4580b15dbfddb3137bf /kernel/seppl.c | |
parent | 8bc528bfd9d8c17d25eec2ca1686513679ba3e55 (diff) |
Commit initial version
git-svn-id: file:///home/lennart/svn/public/seppl/trunk@2 91a2fd9b-5dcb-0310-a70a-d71e310228e6
Diffstat (limited to 'kernel/seppl.c')
-rw-r--r-- | kernel/seppl.c | 429 |
1 files changed, 429 insertions, 0 deletions
diff --git a/kernel/seppl.c b/kernel/seppl.c new file mode 100644 index 0000000..de59eb1 --- /dev/null +++ b/kernel/seppl.c @@ -0,0 +1,429 @@ +/* $Id: newmail.c 31 2003-10-22 22:59:07Z lennart $ */ + +/*** + This file is part of seppl + + seppl is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + seppl is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with seppl; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA +***/ + +#include <linux/module.h> +#include <linux/skbuff.h> +#include <linux/interrupt.h> +#include <linux/string.h> +#include <linux/random.h> +#include <linux/kmod.h> +#include <linux/proc_fs.h> +#include <asm/uaccess.h> + +#include "seppl.h" + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +#ifdef MODULE_DESCRIPTION +MODULE_DESCRIPTION ("SEPPL Key Manager"); +#endif +#ifdef MODULE_AUTHOR +MODULE_AUTHOR("Lennart Poettering <"PACKAGE_BUGREPORT">"); +#endif + +static struct seppl_key *keyring = NULL; +static spinlock_t keyring_lock = SPIN_LOCK_UNLOCKED; + +#define PROC_FILE_NAME "seppl_keyring" +static struct proc_dir_entry* proc_file = NULL; + + +struct cipher_info { + char *name; + int bits; +}; + +#define CIPHER_DICT_MAX 2 + +static const struct cipher_info cipher_dict[CIPHER_DICT_MAX] = { + { "aes-cbc", 128 }, + { "aes-cbc", 192 } +}; + +const struct cipher_info *seppl_find_cipher(u8 id) { + if (id < CIPHER_DICT_MAX) + return &cipher_dict[id]; + + return NULL; +} + +struct seppl_key* seppl_find_key(u8 algorithm, const char *name, int r) { + struct seppl_key *key = NULL, *l; + + for (l = keyring; l; l = l->next) + if (l->algorithm == algorithm && strncmp(l->name, name, sizeof(l->name)) == 0) + if (!r || atomic_read(&l->ready)) { + key = l; + break; + } + + return key; +} + +struct seppl_key* seppl_claim_key(u8 algorithm, const char *name) { + struct seppl_key *key = NULL; + + spin_lock_bh(&keyring_lock); // FIXME: BH? + + if ((key = seppl_find_key(algorithm, name, 1))) { + atomic_inc(&key->usage); + MOD_INC_USE_COUNT; + } + + spin_unlock_bh(&keyring_lock); + + return key; +} + +void seppl_release_key(struct seppl_key* key) { + atomic_dec(&key->usage); + MOD_DEC_USE_COUNT; +} + +// not spinlocked! +static void seppl_next_iv(struct seppl_key *key) { + unsigned int i, m = key->ivsize; + + for (i = 0; i < m ; i++) { + key->iv[i]++; + if (key->iv[i] != 0) break; + } +} + +void seppl_copy_iv(struct seppl_key *key, u8* iv) { + spin_lock_bh(&key->iv_spinlock); // FIXME: BH? + + memcpy(iv, key->iv, key->ivsize); + seppl_next_iv(key); + + spin_unlock_bh(&key->iv_spinlock); +} + +int seppl_add_key(u8 algorithm, const char *name, const u8 *key_data) { + struct seppl_key* key = NULL; + int r = -EINVAL; + int locked = 1; + + spin_lock(&keyring_lock); + + if ((key = seppl_find_key(algorithm, name, 0))) { + + if (!atomic_read(&key->ready)) { + printk(KERN_ERR "SEPPL: Tried to replace incomplete key.\n"); + goto finish; + } + + memcpy(key->key, key_data, key->keysize); + if (crypto_cipher_setkey(key->tfm, key->key, key->keysize)) { + printk(KERN_ERR "SEPPL: Failed to set cipher key.\n"); + goto finish; + } + + printk(KERN_INFO "SEPPL: Replaced key sucessfully.\n"); + + r = 0; + goto finish; + } else { + const struct cipher_info* cinfo; + + if (!(cinfo = seppl_find_cipher(algorithm))) { + printk(KERN_ERR "SEPPL: Unknown cipher\n"); + goto finish; + } + + if(!(key = kmalloc(sizeof(struct seppl_key), GFP_KERNEL))) { + r = -ENOMEM; + printk(KERN_ERR "SEPPL: kmalloc() failed\n"); + goto finish; + } + + key->algorithm = algorithm; + memset(key->name, 0, 7); + strncpy(key->name, name, 7); + atomic_set(&key->ready, 0); + key->next = keyring; + keyring = key; + + spin_unlock(&keyring_lock); + locked = 0; + + key->key = key->iv = NULL; + key->tfm = NULL; + + atomic_set(&key->usage, 0); + spin_lock_init(&key->iv_spinlock); + + if (!(key->tfm = crypto_alloc_tfm("aes", CRYPTO_TFM_MODE_CBC))) { + printk(KERN_ERR "SEPPL: Failed to load cipher.\n"); + goto cleanup; + } + + /* Fill in key */ + if (!(key->key = kmalloc(key->keysize = cinfo->bits/8, GFP_KERNEL))) { + r = -ENOMEM; + printk(KERN_ERR "SEPPL: kmalloc() failed #2\n"); + goto cleanup; + } + memcpy(key->key, key_data, key->keysize); + + if (crypto_cipher_setkey(key->tfm, key->key, key->keysize)) { + printk(KERN_ERR "SEPPL: Failed to set cipher key.\n"); + goto cleanup; + } + + /* Fill in iv */ + if (!(key->iv = kmalloc(key->ivsize = crypto_tfm_alg_ivsize(key->tfm), GFP_KERNEL))) { + r = -ENOMEM; + printk(KERN_ERR "SEPPL: kmalloc() failed #3\n"); + goto cleanup; + } + get_random_bytes(key->iv, key->ivsize); + + key->blocksize = crypto_tfm_alg_blocksize(key->tfm); + + proc_file->size += 8 + key->keysize; + atomic_set(&key->ready, 1); + + printk(KERN_INFO "SEPPL: Added key sucessfully.\n"); + + r = 0; + goto finish; + } + +cleanup: + + if (key) { + struct seppl_key *i; + + spin_lock(&keyring_lock); + + if (keyring == key) + keyring = key->next; + else { + for (i = keyring; i; i = i->next) { + if (i->next == key) { + i->next = key->next; + break; + } + } + } + + spin_unlock(&keyring_lock); + + if (key->tfm) + crypto_free_tfm(key->tfm); + + if (key->key) + kfree(key->key); + + if (key->iv) + kfree(key->key); + + kfree(key); + } + +finish: + + if (locked) + spin_unlock(&keyring_lock); + + return r; +} + +void seppl_clear_keyring(void) { + struct seppl_key *key, *prev; + + printk(KERN_INFO "SEPPL: Clearing keyring\n"); + + spin_lock(&keyring_lock); + + prev = NULL; + key = keyring; + while (key) { + if (atomic_read(&key->ready) && !atomic_read(&key->usage)) { + struct seppl_key *k; + + atomic_set(&key->ready, 0); + + proc_file->size -= 8 + key->keysize; + + if (prev) + prev->next = key->next; + else + keyring = key->next; + + crypto_free_tfm(key->tfm); + kfree(key->key); + kfree(key->iv); + + k = key; + key = key->next; + kfree(k); + + continue; + } + + prev = key; + key = key->next; + } + + spin_unlock(&keyring_lock); +} + +#ifndef MIN +#define MIN(a,b) ((a)>(b)?(b):(a)) +#endif + +static int seppl_proc_read_func(char* page, char** start, off_t off, int count, int* eof, void* data) { + struct seppl_key *key; + char *e = page; + int d = 0; + + MOD_INC_USE_COUNT; + spin_lock(&keyring_lock); + + for (key = keyring; key; key = key->next) { + if (count <= 0) break; + + *e = key->algorithm; + e ++; count--; + + if (count <= 0) break; + + memcpy(e, key->name, d = MIN(7, count)); + e += d; count -= d; + + if (count <= 0) break; + + memcpy(e, key->key, d = MIN(key->keysize, count)); + e += d; count -= d; + + if (count <= 0) break; + + if (key->next == 0) + if (eof) + *eof = 1; + } + + spin_unlock(&keyring_lock); + MOD_DEC_USE_COUNT; + + return e-page; +} + + +static int seppl_proc_write_func(struct file* file, const char* buffer, unsigned long count, void* data) { + int r = -EINVAL; + u8* buf = NULL; + MOD_INC_USE_COUNT; + + count = count > 10*1024 ? 10*1024 : count; + + if (!(buf = kmalloc(count, GFP_ATOMIC))) { + r = -ENOMEM; + goto finish; + } + + if (copy_from_user(buf, buffer, count)) + goto finish; + + if (strcmp((char*) buf, "clear\n") == 0) { + seppl_clear_keyring(); + r = strlen(buf); + } else { + unsigned long n = count; + u8* p = buf; + + while (n >= 8) { + const struct cipher_info* ci = seppl_find_cipher(*p); + int l, t; + + if (!ci) { + printk(KERN_ERR "SEPPL: Unknown cipher\n"); + goto finish; + } + + l = 8+ci->bits/8; + + if (n < l) { + printk(KERN_ERR "SEPPL: Key data too short\n"); + goto finish; + } + + if ((t = seppl_add_key(*p, p+1, p+8)) < 0) { + r = t; + goto finish; + } + + p += l; + n -= l; + } + + if (n) { + printk(KERN_ERR "SEPPL: Data too short (%li)\n", n); + goto finish; + } + + r = (int) count; + } + +finish: + + if (buf) + kfree(buf); + + MOD_DEC_USE_COUNT; + return r; +} + +static int __init init(void) { + if (!(proc_file = create_proc_entry(PROC_FILE_NAME, 0600, proc_net))) + return -ENOMEM; + + proc_file->read_proc = seppl_proc_read_func; + proc_file->write_proc = seppl_proc_write_func; + proc_file->owner = THIS_MODULE; + proc_file->size = 0; + + printk("SEPPL: Loaded.\n"); + + return 0; +} + +static void __exit fini(void) { + seppl_clear_keyring(); + + if (keyring) + printk("SEPPL: Something wicked happened.\n"); + + remove_proc_entry(PROC_FILE_NAME, proc_net); + + printk("SEPPL: Unloaded.\n"); +} + +module_init(init); +module_exit(fini); + +EXPORT_SYMBOL(seppl_copy_iv); +EXPORT_SYMBOL(seppl_claim_key); +EXPORT_SYMBOL(seppl_release_key); + |