diff options
Diffstat (limited to 'linux/net/ipsec/alg/ipsec_alg_cryptoapi.c')
-rw-r--r-- | linux/net/ipsec/alg/ipsec_alg_cryptoapi.c | 421 |
1 files changed, 421 insertions, 0 deletions
diff --git a/linux/net/ipsec/alg/ipsec_alg_cryptoapi.c b/linux/net/ipsec/alg/ipsec_alg_cryptoapi.c new file mode 100644 index 000000000..fc68094c2 --- /dev/null +++ b/linux/net/ipsec/alg/ipsec_alg_cryptoapi.c @@ -0,0 +1,421 @@ +/* + * ipsec_alg to linux cryptoapi GLUE + * + * Authors: CODE.ar TEAM + * Harpo MAxx <harpo@linuxmendoza.org.ar> + * JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar> + * Luciano Ruete <docemeses@softhome.net> + * + * $Id: ipsec_alg_cryptoapi.c,v 1.3 2004/09/17 18:57:30 as Exp $ + * + * This program 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program 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. + * + * Example usage: + * modinfo -p ipsec_cryptoapi (quite useful info, including supported algos) + * modprobe ipsec_cryptoapi + * modprobe ipsec_cryptoapi test=1 + * modprobe ipsec_cryptoapi excl=1 (exclusive cipher/algo) + * modprobe ipsec_cryptoapi noauto=1 aes=1 twofish=1 (only these ciphers) + * modprobe ipsec_cryptoapi aes=128,128 (force these keylens) + * modprobe ipsec_cryptoapi des_ede3=0 (everything but 3DES) + */ +#include <linux/config.h> +#include <linux/version.h> + +/* + * special case: ipsec core modular with this static algo inside: + * must avoid MODULE magic for this file + */ +#if CONFIG_IPSEC_MODULE && CONFIG_IPSEC_ALG_CRYPTOAPI +#undef MODULE +#endif + +#include <linux/module.h> +#include <linux/init.h> + +#include <linux/kernel.h> /* printk() */ +#include <linux/errno.h> /* error codes */ +#include <linux/types.h> /* size_t */ +#include <linux/string.h> + +/* Check if __exit is defined, if not null it */ +#ifndef __exit +#define __exit +#endif + +/* warn the innocent */ +#if !defined (CONFIG_CRYPTO) && !defined (CONFIG_CRYPTO_MODULE) +#warning "No linux CryptoAPI found, install 2.4.22+ or 2.6.x" +#define NO_CRYPTOAPI_SUPPORT +#endif +/* Low freeswan header coupling */ +#include "freeswan/ipsec_alg.h" + +#include <linux/crypto.h> +#ifdef CRYPTO_API_VERSION_CODE +#warning "Old CryptoAPI is not supported. Only linux-2.4.22+ or linux-2.6.x are supported" +#define NO_CRYPTOAPI_SUPPORT +#endif + +#ifdef NO_CRYPTOAPI_SUPPORT +#warning "Building an unusable module :P" +/* Catch old CryptoAPI by not allowing module to load */ +IPSEC_ALG_MODULE_INIT( ipsec_cryptoapi_init ) +{ + printk(KERN_WARNING "ipsec_cryptoapi.o was not built on stock Linux CryptoAPI (2.4.22+ or 2.6.x), not loading.\n"); + return -EINVAL; +} +#else +#include <asm/scatterlist.h> +#include <asm/pgtable.h> +#include <linux/mm.h> + +#define CIPHERNAME_AES "aes" +#define CIPHERNAME_3DES "des3_ede" +#define CIPHERNAME_BLOWFISH "blowfish" +#define CIPHERNAME_CAST "cast5" +#define CIPHERNAME_SERPENT "serpent" +#define CIPHERNAME_TWOFISH "twofish" + +#define ESP_3DES 3 +#define ESP_AES 12 +#define ESP_BLOWFISH 7 /* truly _constant_ :) */ +#define ESP_CAST 6 /* quite constant :) */ +#define ESP_SERPENT 252 /* from ipsec drafts */ +#define ESP_TWOFISH 253 /* from ipsec drafts */ + +#define AH_MD5 2 +#define AH_SHA 3 +#define DIGESTNAME_MD5 "md5" +#define DIGESTNAME_SHA1 "sha1" + +MODULE_AUTHOR("Juanjo Ciarlante, Harpo MAxx, Luciano Ruete"); +static int debug=0; +MODULE_PARM(debug, "i"); +static int test=0; +MODULE_PARM(test, "i"); +static int excl=0; +MODULE_PARM(excl, "i"); + +static int noauto = 0; +MODULE_PARM(noauto,"i"); +MODULE_PARM_DESC(noauto, "Dont try all known algos, just setup enabled ones"); + +static int des_ede3[] = {-1, -1}; +static int aes[] = {-1, -1}; +static int blowfish[] = {-1, -1}; +static int cast[] = {-1, -1}; +static int serpent[] = {-1, -1}; +static int twofish[] = {-1, -1}; + +MODULE_PARM(des_ede3,"1-2i"); +MODULE_PARM(aes,"1-2i"); +MODULE_PARM(blowfish,"1-2i"); +MODULE_PARM(cast,"1-2i"); +MODULE_PARM(serpent,"1-2i"); +MODULE_PARM(twofish,"1-2i"); +MODULE_PARM_DESC(des_ede3, "0: disable | 1: force_enable | min,max: dontuse"); +MODULE_PARM_DESC(aes, "0: disable | 1: force_enable | min,max: keybitlens"); +MODULE_PARM_DESC(blowfish, "0: disable | 1: force_enable | min,max: keybitlens"); +MODULE_PARM_DESC(cast, "0: disable | 1: force_enable | min,max: keybitlens"); +MODULE_PARM_DESC(serpent, "0: disable | 1: force_enable | min,max: keybitlens"); +MODULE_PARM_DESC(twofish, "0: disable | 1: force_enable | min,max: keybitlens"); + +struct ipsec_alg_capi_cipher { + const char *ciphername; /* cryptoapi's ciphername */ + unsigned blocksize; + unsigned short minbits; + unsigned short maxbits; + int *parm; /* lkm param for this cipher */ + struct ipsec_alg_enc alg; /* note it's not a pointer */ +}; +static struct ipsec_alg_capi_cipher alg_capi_carray[] = { + { CIPHERNAME_AES , 16, 128, 256, aes , { ixt_alg_id: ESP_AES, }}, + { CIPHERNAME_TWOFISH , 16, 128, 256, twofish, { ixt_alg_id: ESP_TWOFISH, }}, + { CIPHERNAME_SERPENT , 16, 128, 256, serpent, { ixt_alg_id: ESP_SERPENT, }}, + { CIPHERNAME_CAST , 8, 128, 128, cast , { ixt_alg_id: ESP_CAST, }}, + { CIPHERNAME_BLOWFISH , 8, 128, 448, blowfish,{ ixt_alg_id: ESP_BLOWFISH, }}, + { CIPHERNAME_3DES , 8, 192, 192, des_ede3,{ ixt_alg_id: ESP_3DES, }}, + { NULL, 0, 0, 0, NULL, {} } +}; +#ifdef NOT_YET +struct ipsec_alg_capi_digest { + const char *digestname; /* cryptoapi's digestname */ + struct digest_implementation *di; + struct ipsec_alg_auth alg; /* note it's not a pointer */ +}; +static struct ipsec_alg_capi_cipher alg_capi_darray[] = { + { DIGESTNAME_MD5, NULL, { ixt_alg_id: AH_MD5, }}, + { DIGESTNAME_SHA1, NULL, { ixt_alg_id: AH_SHA, }}, + { NULL, NULL, {} } +}; +#endif +/* + * "generic" linux cryptoapi setup_cipher() function + */ +int setup_cipher(const char *ciphername) +{ + return crypto_alg_available(ciphername, 0); +} + +/* + * setups ipsec_alg_capi_cipher "hyper" struct components, calling + * register_ipsec_alg for cointaned ipsec_alg object + */ +static void _capi_destroy_key (struct ipsec_alg_enc *alg, __u8 *key_e); +static __u8 * _capi_new_key (struct ipsec_alg_enc *alg, const __u8 *key, size_t keylen); +static int _capi_cbc_encrypt(struct ipsec_alg_enc *alg, __u8 * key_e, __u8 * in, int ilen, const __u8 * iv, int encrypt); + +static int +setup_ipsec_alg_capi_cipher(struct ipsec_alg_capi_cipher *cptr) +{ + int ret; + cptr->alg.ixt_version = IPSEC_ALG_VERSION; + cptr->alg.ixt_module = THIS_MODULE; + atomic_set (& cptr->alg.ixt_refcnt, 0); + strncpy (cptr->alg.ixt_name , cptr->ciphername, sizeof (cptr->alg.ixt_name)); + + cptr->alg.ixt_blocksize=cptr->blocksize; + cptr->alg.ixt_keyminbits=cptr->minbits; + cptr->alg.ixt_keymaxbits=cptr->maxbits; + cptr->alg.ixt_state = 0; + if (excl) cptr->alg.ixt_state |= IPSEC_ALG_ST_EXCL; + cptr->alg.ixt_e_keylen=cptr->alg.ixt_keymaxbits/8; + cptr->alg.ixt_e_ctx_size = 0; + cptr->alg.ixt_alg_type = IPSEC_ALG_TYPE_ENCRYPT; + cptr->alg.ixt_e_new_key = _capi_new_key; + cptr->alg.ixt_e_destroy_key = _capi_destroy_key; + cptr->alg.ixt_e_cbc_encrypt = _capi_cbc_encrypt; + cptr->alg.ixt_data = cptr; + + ret=register_ipsec_alg_enc(&cptr->alg); + printk("setup_ipsec_alg_capi_cipher(): " + "alg_type=%d alg_id=%d name=%s " + "keyminbits=%d keymaxbits=%d, ret=%d\n", + cptr->alg.ixt_alg_type, + cptr->alg.ixt_alg_id, + cptr->alg.ixt_name, + cptr->alg.ixt_keyminbits, + cptr->alg.ixt_keymaxbits, + ret); + return ret; +} +/* + * called in ipsec_sa_wipe() time, will destroy key contexts + * and do 1 unbind() + */ +static void +_capi_destroy_key (struct ipsec_alg_enc *alg, __u8 *key_e) +{ + struct crypto_tfm *tfm=(struct crypto_tfm*)key_e; + + if (debug > 0) + printk(KERN_DEBUG "klips_debug: _capi_destroy_key:" + "name=%s key_e=%p \n", + alg->ixt_name, key_e); + if (!key_e) { + printk(KERN_ERR "klips_debug: _capi_destroy_key:" + "name=%s NULL key_e!\n", + alg->ixt_name); + return; + } + crypto_free_tfm(tfm); +} + +/* + * create new key context, need alg->ixt_data to know which + * (of many) cipher inside this module is the target + */ +static __u8 * +_capi_new_key (struct ipsec_alg_enc *alg, const __u8 *key, size_t keylen) +{ + struct ipsec_alg_capi_cipher *cptr; + struct crypto_tfm *tfm=NULL; + + cptr = alg->ixt_data; + if (!cptr) { + printk(KERN_ERR "_capi_new_key(): " + "NULL ixt_data (?!) for \"%s\" algo\n" + , alg->ixt_name); + goto err; + } + if (debug > 0) + printk(KERN_DEBUG "klips_debug:_capi_new_key:" + "name=%s cptr=%p key=%p keysize=%d\n", + alg->ixt_name, cptr, key, keylen); + + /* + * alloc tfm + */ + tfm = crypto_alloc_tfm(cptr->ciphername, CRYPTO_TFM_MODE_CBC); + if (!tfm) { + printk(KERN_ERR "_capi_new_key(): " + "NULL tfm for \"%s\" cryptoapi (\"%s\") algo\n" + , alg->ixt_name, cptr->ciphername); + goto err; + } + if (crypto_cipher_setkey(tfm, key, keylen) < 0) { + printk(KERN_ERR "_capi_new_key(): " + "failed new_key() for \"%s\" cryptoapi algo (keylen=%d)\n" + , alg->ixt_name, keylen); + crypto_free_tfm(tfm); + tfm=NULL; + } +err: + if (debug > 0) + printk(KERN_DEBUG "klips_debug:_capi_new_key:" + "name=%s key=%p keylen=%d tfm=%p\n", + alg->ixt_name, key, keylen, tfm); + return (__u8 *) tfm; +} +/* + * core encryption function: will use cx->ci to call actual cipher's + * cbc function + */ +static int +_capi_cbc_encrypt(struct ipsec_alg_enc *alg, __u8 * key_e, __u8 * in, int ilen, const __u8 * iv, int encrypt) { + int error =0; + struct crypto_tfm *tfm=(struct crypto_tfm *)key_e; + struct scatterlist sg = { + .page = virt_to_page(in), + .offset = (unsigned long)(in) % PAGE_SIZE, + .length=ilen, + }; + if (debug > 1) + printk(KERN_DEBUG "klips_debug:_capi_cbc_encrypt:" + "key_e=%p " + "in=%p out=%p ilen=%d iv=%p encrypt=%d\n" + , key_e + , in, in, ilen, iv, encrypt); + crypto_cipher_set_iv(tfm, iv, crypto_tfm_alg_ivsize(tfm)); + if (encrypt) + error = crypto_cipher_encrypt (tfm, &sg, &sg, ilen); + else + error = crypto_cipher_decrypt (tfm, &sg, &sg, ilen); + if (debug > 1) + printk(KERN_DEBUG "klips_debug:_capi_cbc_encrypt:" + "error=%d\n" + , error); + return (error<0)? error : ilen; +} +/* + * main initialization loop: for each cipher in list, do + * 1) setup cryptoapi cipher else continue + * 2) register ipsec_alg object + */ +static int +setup_cipher_list (struct ipsec_alg_capi_cipher* clist) +{ + struct ipsec_alg_capi_cipher *cptr; + /* foreach cipher in list ... */ + for (cptr=clist;cptr->ciphername;cptr++) { + /* + * see if cipher has been disabled (0) or + * if noauto set and not enabled (1) + */ + if (cptr->parm[0] == 0 || (noauto && cptr->parm[0] < 0)) { + if (debug>0) + printk(KERN_INFO "setup_cipher_list(): " + "ciphername=%s skipped at user request: " + "noauto=%d parm[0]=%d parm[1]=%d\n" + , cptr->ciphername + , noauto + , cptr->parm[0] + , cptr->parm[1]); + continue; + } + /* + * use a local ci to avoid touching cptr->ci, + * if register ipsec_alg success then bind cipher + */ + if( setup_cipher(cptr->ciphername) ) { + if (debug > 0) + printk(KERN_DEBUG "klips_debug:" + "setup_cipher_list():" + "ciphername=%s found\n" + , cptr->ciphername); + if (setup_ipsec_alg_capi_cipher(cptr) == 0) { + + + } else { + printk(KERN_ERR "klips_debug:" + "setup_cipher_list():" + "ciphername=%s failed ipsec_alg_register\n" + , cptr->ciphername); + } + } else { + if (debug>0) + printk(KERN_INFO "setup_cipher_list(): lookup for ciphername=%s: not found \n", + cptr->ciphername); + } + } + return 0; +} +/* + * deregister ipsec_alg objects and unbind ciphers + */ +static int +unsetup_cipher_list (struct ipsec_alg_capi_cipher* clist) +{ + struct ipsec_alg_capi_cipher *cptr; + /* foreach cipher in list ... */ + for (cptr=clist;cptr->ciphername;cptr++) { + if (cptr->alg.ixt_state & IPSEC_ALG_ST_REGISTERED) { + unregister_ipsec_alg_enc(&cptr->alg); + } + } + return 0; +} +/* + * test loop for registered algos + */ +static int +test_cipher_list (struct ipsec_alg_capi_cipher* clist) +{ + int test_ret; + struct ipsec_alg_capi_cipher *cptr; + /* foreach cipher in list ... */ + for (cptr=clist;cptr->ciphername;cptr++) { + if (cptr->alg.ixt_state & IPSEC_ALG_ST_REGISTERED) { + test_ret=ipsec_alg_test( + cptr->alg.ixt_alg_type, + cptr->alg.ixt_alg_id, + test); + printk("test_cipher_list(alg_type=%d alg_id=%d): test_ret=%d\n", + cptr->alg.ixt_alg_type, + cptr->alg.ixt_alg_id, + test_ret); + } + } + return 0; +} + +IPSEC_ALG_MODULE_INIT( ipsec_cryptoapi_init ) +{ + int ret, test_ret; + if ((ret=setup_cipher_list(alg_capi_carray)) < 0) + return -EPROTONOSUPPORT; + if (ret==0 && test) { + test_ret=test_cipher_list(alg_capi_carray); + } + return ret; +} +IPSEC_ALG_MODULE_EXIT( ipsec_cryptoapi_fini ) +{ + unsetup_cipher_list(alg_capi_carray); + return; +} +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +EXPORT_NO_SYMBOLS; +#endif /* NO_CRYPTOAPI_SUPPORT */ |