summaryrefslogtreecommitdiffstats
path: root/kernel/seppl.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/seppl.c')
-rw-r--r--kernel/seppl.c429
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);
+