/* $Id$ */ /*** 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 #include #include #include #include #include #include #include #include #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", 128 }, { "aes", 192 } }; static const struct cipher_info *seppl_find_cipher(u8 id) { if (id < CIPHER_DICT_MAX) return &cipher_dict[id]; return NULL; } static 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); spin_unlock_bh(&keyring_lock); return key; } void seppl_release_key(struct seppl_key* key) { atomic_dec(&key->usage); } // 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); } static 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; key->key_ecb = NULL; key->tfm_ecb = NULL; atomic_set(&key->usage, 0); spin_lock_init(&key->iv_spinlock); if (!(key->tfm = crypto_alloc_tfm(cinfo->name, 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; /* Set up a random ecb key for making good IVs */ if (!(key->tfm_ecb = crypto_alloc_tfm("aes", CRYPTO_TFM_MODE_ECB))) { printk(KERN_ERR "SEPPL: Failed to load ecb cipher.\n"); goto cleanup; } if ( !(key->key_ecb = kmalloc( 128/8, GFP_KERNEL ))) { r = -ENOMEM; printk( KERN_ERR "SEPPL: kmalloc() failed #2a\n" ); goto cleanup; } get_random_bytes(key->key_ecb, 128/8); if (crypto_cipher_setkey(key->tfm_ecb, key->key_ecb, 128/8)) { printk(KERN_ERR "SEPPL: Failed to set ecb cipher key.\n"); goto cleanup; } 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); /* Free the data transform */ if (key->tfm) crypto_free_tfm(key->tfm); if (key->key) kfree(key->key); if (key->iv) kfree(key->iv); /* Free the IV transform */ if (key->tfm_ecb) crypto_free_tfm(key->tfm_ecb); if (key->key_ecb) kfree(key->key_ecb); kfree(key); } finish: if (locked) spin_unlock(&keyring_lock); return r; } static void seppl_clear_keyring(void) { unsigned n = 0; 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); crypto_free_tfm(key->tfm_ecb); kfree(key->key_ecb); k = key; key = key->next; kfree(k); continue; } else n++; prev = key; key = key->next; } spin_unlock(&keyring_lock); printk(KERN_INFO "SEPPL: Cleared keyring, %u keys remain.\n", n); } 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; 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_t(unsigned int, key->keysize, count)); e += d; count -= d; if (count <= 0) break; if (key->next == 0) if (eof) *eof = 1; } spin_unlock(&keyring_lock); 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; 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); 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 SEPPL "PACKAGE_VERSION", 2003,2004 by Lennart Poettering <"PACKAGE_BUGREPORT">.\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);