Files
strongswan-ext/linux/net/ipsec/alg/ipsec_alg_cryptoapi.c
T
Martin Willi 997358a6c4 - import of strongswan-2.7.0
- applied patch for charon
2006-04-28 07:14:48 +00:00

422 lines
12 KiB
C

/*
* 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 */