Logo Search packages:      
Sourcecode: openswan version File versions

ipsec_alg_cryptoapi.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>
 * 
 * ipsec_alg_cryptoapi.c,v 1.1.2.1 2003/11/21 18:12:23 jjo 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 "openswan/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     /* truely _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,  96, 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 */

Generated by  Doxygen 1.6.0   Back to index