diff options
Diffstat (limited to 'linux/net/ipsec/ipsec_alg.c')
-rw-r--r-- | linux/net/ipsec/ipsec_alg.c | 927 |
1 files changed, 927 insertions, 0 deletions
diff --git a/linux/net/ipsec/ipsec_alg.c b/linux/net/ipsec/ipsec_alg.c new file mode 100644 index 000000000..c402b7e5b --- /dev/null +++ b/linux/net/ipsec/ipsec_alg.c @@ -0,0 +1,927 @@ +/* + * Modular extensions service and registration functions + * + * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar> + * + * Version: 0.8.1 + * + * $Id: ipsec_alg.c,v 1.4 2004/06/13 19:57:49 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. + * + */ +#ifdef CONFIG_IPSEC_ALG +#define __NO_VERSION__ +#include <linux/module.h> +#include <linux/kernel.h> /* printk() */ + +#include <linux/netdevice.h> /* struct device, and other headers */ +#include <linux/etherdevice.h> /* eth_type_trans */ +#include <linux/ip.h> /* struct iphdr */ +#include <linux/skbuff.h> +#include <linux/socket.h> +#include <linux/in.h> +#include <linux/types.h> +#include <linux/string.h> /* memcmp() */ +#include <linux/random.h> /* get_random_bytes() */ +#include <linux/errno.h> /* error codes */ +#ifdef SPINLOCK +# ifdef SPINLOCK_23 +# include <linux/spinlock.h> /* *lock* */ +# else /* SPINLOCK_23 */ +# include <asm/spinlock.h> /* *lock* */ +# endif /* SPINLOCK_23 */ +#endif /* SPINLOCK */ +#ifdef NET_21 +# include <asm/uaccess.h> +# include <linux/in6.h> +# define proto_priv cb +#endif /* NET21 */ +#include "freeswan/ipsec_param.h" +#include <freeswan.h> +#include "freeswan/ipsec_sa.h" +#include "freeswan/radij.h" +#include "freeswan/ipsec_encap.h" +#include "freeswan/ipsec_radij.h" +#include "freeswan/ipsec_xform.h" +#include "freeswan/ipsec_tunnel.h" +#include "freeswan/ipsec_rcv.h" +#if defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH) +# include "freeswan/ipsec_ah.h" +#endif /* defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH) */ +#ifdef CONFIG_IPSEC_ESP +# include "freeswan/ipsec_esp.h" +#endif /* !CONFIG_IPSEC_ESP */ +#ifdef CONFIG_IPSEC_IPCOMP +# include "freeswan/ipcomp.h" +#endif /* CONFIG_IPSEC_COMP */ + +#include <pfkeyv2.h> +#include <pfkey.h> + +#include "freeswan/ipsec_alg.h" + +#ifndef CONFIG_IPSEC_ALG +#error This file _MUST_ be compiled with CONFIG_IPSEC_ALG enabled ! +#endif +#if SADB_EALG_MAX < 255 +#warning Compiling with limited ESP support ( SADB_EALG_MAX < 256 ) +#endif + +static rwlock_t ipsec_alg_lock = RW_LOCK_UNLOCKED; +#define IPSEC_ALG_HASHSZ 16 /* must be power of 2, even 2^0=1 */ +static struct list_head ipsec_alg_hash_table[IPSEC_ALG_HASHSZ]; + +/* Old gcc's will fail here */ +#define barf_out(fmt, args...) do { printk(KERN_ERR "%s: (%s) " fmt, __FUNCTION__, ixt->ixt_name , ## args)\ + ; goto out; } while(0) + +/* + * Must be already protected by lock + */ +static void __ipsec_alg_usage_inc(struct ipsec_alg *ixt) { + if (ixt->ixt_module) + __MOD_INC_USE_COUNT(ixt->ixt_module); + atomic_inc(&ixt->ixt_refcnt); +} +static void __ipsec_alg_usage_dec(struct ipsec_alg *ixt) { + atomic_dec(&ixt->ixt_refcnt); + if (ixt->ixt_module) + __MOD_DEC_USE_COUNT(ixt->ixt_module); +} +/* + * simple hash function, optimized for 0-hash (1 list) special + * case + */ +#if IPSEC_ALG_HASHSZ > 1 +static inline unsigned ipsec_alg_hashfn(int alg_type, int alg_id) { + return ((alg_type^alg_id)&(IPSEC_ALG_HASHSZ-1)); +} +#else +#define ipsec_alg_hashfn(x,y) (0) +#endif + +/***************************************************************** + * + * INTERNAL table handling: insert, delete, find + * + *****************************************************************/ + +/* + * hash table initialization, called from ipsec_alg_init() + */ +static void ipsec_alg_hash_init(void) { + struct list_head *head = ipsec_alg_hash_table; + int i = IPSEC_ALG_HASHSZ; + do { + INIT_LIST_HEAD(head); + head++; + i--; + } while (i); +} +/* + * hash list lookup by {alg_type, alg_id} and table head, + * must be already protected by lock + */ +static struct ipsec_alg *__ipsec_alg_find(unsigned alg_type, unsigned alg_id, struct list_head * head) { + struct list_head *p; + struct ipsec_alg *ixt=NULL; + for (p=head->next; p!=head; p=p->next) { + ixt = list_entry(p, struct ipsec_alg, ixt_list); + if (ixt->ixt_alg_type == alg_type && ixt->ixt_alg_id==alg_id) { + goto out; + } + } + ixt=NULL; +out: + return ixt; +} +/* + * inserts (in front) a new entry in hash table, + * called from ipsec_alg_register() when new algorithm is registered. + */ +static int ipsec_alg_insert(struct ipsec_alg *ixt) { + int ret=-EINVAL; + unsigned hashval=ipsec_alg_hashfn(ixt->ixt_alg_type, ixt->ixt_alg_id); + struct list_head *head= ipsec_alg_hash_table + hashval; + struct ipsec_alg *ixt_cur; + /* new element must be virgin ... */ + if (ixt->ixt_list.next != &ixt->ixt_list || + ixt->ixt_list.prev != &ixt->ixt_list) { + printk(KERN_ERR "ipsec_alg_insert: ixt object \"%s\" " + "list head not initialized\n", + ixt->ixt_name); + return ret; + } + write_lock_bh(&ipsec_alg_lock); + ixt_cur = __ipsec_alg_find(ixt->ixt_alg_type, ixt->ixt_alg_id, head); + /* if previous (current) ipsec_alg found check excl flag of _anyone_ */ + if (ixt_cur && ((ixt->ixt_state|ixt_cur->ixt_state) & IPSEC_ALG_ST_EXCL)) + barf_out("ipsec_alg for alg_type=%d, alg_id=%d already exist. " + "Not loaded (ret=%d).\n", + ixt->ixt_alg_type, + ixt->ixt_alg_id, ret=-EEXIST); + list_add(&ixt->ixt_list, head); + ixt->ixt_state |= IPSEC_ALG_ST_REGISTERED; + ret=0; +out: + write_unlock_bh(&ipsec_alg_lock); + return ret; +} +/* + * deletes an existing entry in hash table, + * called from ipsec_alg_unregister() when algorithm is unregistered. + */ +static int ipsec_alg_delete(struct ipsec_alg *ixt) { + write_lock_bh(&ipsec_alg_lock); + list_del(&ixt->ixt_list); + write_unlock_bh(&ipsec_alg_lock); + return 0; +} +/* + * here @user context (read-only when @kernel bh context) + * -> no bh disabling + * + * called from ipsec_sa_init() -> ipsec_alg_sa_init() + */ +static struct ipsec_alg *ipsec_alg_get(int alg_type, int alg_id) { + unsigned hashval=ipsec_alg_hashfn(alg_type, alg_id); + struct list_head *head= ipsec_alg_hash_table + hashval; + struct ipsec_alg *ixt; + read_lock(&ipsec_alg_lock); + ixt=__ipsec_alg_find(alg_type, alg_id, head); + if (ixt) __ipsec_alg_usage_inc(ixt); + read_unlock(&ipsec_alg_lock); + return ixt; +} + +static void ipsec_alg_put(struct ipsec_alg *ixt) { + __ipsec_alg_usage_dec((struct ipsec_alg *)ixt); +} + +/***************************************************************** + * + * INTERFACE for ENC services: key creation, encrypt function + * + *****************************************************************/ + +/* + * main encrypt service entry point + * called from ipsec_rcv() with encrypt=IPSEC_ALG_DECRYPT and + * ipsec_tunnel_start_xmit with encrypt=IPSEC_ALG_ENCRYPT + */ +int ipsec_alg_esp_encrypt(struct ipsec_sa *sa_p, __u8 * idat, int ilen, const __u8 * iv, int encrypt) { + int ret; + struct ipsec_alg_enc *ixt_e=sa_p->ips_alg_enc; + KLIPS_PRINT(debug_rcv||debug_tunnel, + "klips_debug:ipsec_alg_esp_encrypt: " + "entering with encalg=%d, ixt_e=%p\n", + sa_p->ips_encalg, ixt_e); + if (!ixt_e) { + KLIPS_PRINT(debug_rcv||debug_tunnel, + "klips_debug:ipsec_alg_esp_encrypt: " + "NULL ipsec_alg_enc object\n"); + return -1; + } + KLIPS_PRINT(debug_rcv||debug_tunnel, + "klips_debug:ipsec_alg_esp_encrypt: " + "calling cbc_encrypt encalg=%d " + "ips_key_e=%p idat=%p ilen=%d iv=%p, encrypt=%d\n", + sa_p->ips_encalg, + sa_p->ips_key_e, idat, ilen, iv, encrypt); + ret=ixt_e->ixt_e_cbc_encrypt(ixt_e, sa_p->ips_key_e, idat, ilen, iv, encrypt); + KLIPS_PRINT(debug_rcv||debug_tunnel, + "klips_debug:ipsec_alg_esp_encrypt: " + "returned ret=%d\n", + ret); + return ret; +} +/* + * encryption key context creation function + * called from pfkey_v2_parser.c:pfkey_ips_init() + */ +int ipsec_alg_enc_key_create(struct ipsec_sa *sa_p) { + int ret=-EINVAL; + int keyminbits, keymaxbits; + caddr_t ekp; + struct ipsec_alg_enc *ixt_e=sa_p->ips_alg_enc; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:ipsec_alg_enc_key_create: " + "entering with encalg=%d ixt_e=%p\n", + sa_p->ips_encalg, ixt_e); + if (!ixt_e) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:ipsec_alg_enc_key_create: " + "NULL ipsec_alg_enc object\n"); + return -EPROTO; + } + + /* + * grRRR... DES 7bits jurassic stuff ... f*ckk --jjo + */ + switch(ixt_e->ixt_alg_id) { + case ESP_3DES: + keyminbits=keymaxbits=192;break; + case ESP_DES: + keyminbits=keymaxbits=64;break; + default: + keyminbits=ixt_e->ixt_keyminbits; + keymaxbits=ixt_e->ixt_keymaxbits; + } + if(sa_p->ips_key_bits_e<keyminbits || + sa_p->ips_key_bits_e>keymaxbits) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:ipsec_alg_enc_key_create: " + "incorrect encryption key size for id=%d: %d bits -- " + "must be between %d,%d bits\n" /*octets (bytes)\n"*/, + ixt_e->ixt_alg_id, + sa_p->ips_key_bits_e, keyminbits, keymaxbits); + ret=-EINVAL; + goto ixt_out; + } + /* save encryption key pointer */ + ekp = sa_p->ips_key_e; + + + if (ixt_e->ixt_e_new_key) { + sa_p->ips_key_e = ixt_e->ixt_e_new_key(ixt_e, + ekp, sa_p->ips_key_bits_e/8); + ret = (sa_p->ips_key_e)? 0 : -EINVAL; + } else { + if((sa_p->ips_key_e = (caddr_t) + kmalloc((sa_p->ips_key_e_size = ixt_e->ixt_e_ctx_size), + GFP_ATOMIC)) == NULL) { + ret=-ENOMEM; + goto ixt_out; + } + /* zero-out key_e */ + memset(sa_p->ips_key_e, 0, sa_p->ips_key_e_size); + + /* I cast here to allow more decoupling in alg module */ + KLIPS_PRINT(debug_pfkey, + "klips_debug:ipsec_alg_enc_key_create: about to call:" + "set_key(key_e=%p, ekp=%p, key_size=%d)\n", + (caddr_t)sa_p->ips_key_e, ekp, sa_p->ips_key_bits_e/8); + ret = ixt_e->ixt_e_set_key(ixt_e, (caddr_t)sa_p->ips_key_e, ekp, sa_p->ips_key_bits_e/8); + } + /* paranoid */ + memset(ekp, 0, sa_p->ips_key_bits_e/8); + kfree(ekp); +ixt_out: + return ret; +} + +/*************************************************************** + * + * INTERFACE for AUTH services: key creation, hash functions + * + ***************************************************************/ + +/* + * auth key context creation function + * called from pfkey_v2_parser.c:pfkey_ips_init() + */ +int ipsec_alg_auth_key_create(struct ipsec_sa *sa_p) { + int ret=-EINVAL; + struct ipsec_alg_auth *ixt_a=sa_p->ips_alg_auth; + int keyminbits, keymaxbits; + unsigned char *akp; + unsigned int aks; + KLIPS_PRINT(debug_pfkey, + "klips_debug:ipsec_alg_auth_key_create: " + "entering with authalg=%d ixt_a=%p\n", + sa_p->ips_authalg, ixt_a); + if (!ixt_a) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:ipsec_alg_auth_key_create: " + "NULL ipsec_alg_auth object\n"); + return -EPROTO; + } + keyminbits=ixt_a->ixt_keyminbits; + keymaxbits=ixt_a->ixt_keymaxbits; + if(sa_p->ips_key_bits_a<keyminbits || sa_p->ips_key_bits_a>keymaxbits) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:ipsec_alg_auth_key_create: incorrect auth" + "key size: %d bits -- must be between %d,%d bits\n"/*octets (bytes)\n"*/, + sa_p->ips_key_bits_a, keyminbits, keymaxbits); + ret=-EINVAL; + goto ixt_out; + } + /* save auth key pointer */ + sa_p->ips_auth_bits = ixt_a->ixt_a_keylen * 8; /* XXX XXX */ + akp = sa_p->ips_key_a; + aks = sa_p->ips_key_a_size; + + /* will hold: 2 ctx and a blocksize buffer: kb */ + sa_p->ips_key_a_size = ixt_a->ixt_a_ctx_size; + if((sa_p->ips_key_a = + (caddr_t) kmalloc(sa_p->ips_key_a_size, GFP_ATOMIC)) == NULL) { + ret=-ENOMEM; + goto ixt_out; + } + ixt_a->ixt_a_hmac_set_key(ixt_a, sa_p->ips_key_a, akp, sa_p->ips_key_bits_a/8); /* XXX XXX */ + ret=0; + memset(akp, 0, aks); + kfree(akp); + +ixt_out: + return ret; +} +int ipsec_alg_sa_esp_hash(const struct ipsec_sa *sa_p, const __u8 *espp, int len, __u8 *hash, int hashlen) { + struct ipsec_alg_auth *ixt_a=sa_p->ips_alg_auth; + if (!ixt_a) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:ipsec_sa_esp_hash: " + "NULL ipsec_alg_auth object\n"); + return -EPROTO; + } + KLIPS_PRINT(debug_tunnel|debug_rcv, + "klips_debug:ipsec_sa_esp_hash: " + "hashing %p (%d bytes) to %p (%d bytes)\n", + espp, len, + hash, hashlen); + ixt_a->ixt_a_hmac_hash(ixt_a, + sa_p->ips_key_a, + espp, len, + hash, hashlen); + return 0; +} + +/*************************************************************** + * + * INTERFACE for module loading,testing, and unloading + * + ***************************************************************/ + +/* validation for registering (enc) module */ +static int check_enc(struct ipsec_alg_enc *ixt) { + int ret=-EINVAL; + if (ixt->ixt_alg_id==0 || ixt->ixt_alg_id > SADB_EALG_MAX) + barf_out("invalid alg_id=%d >= %d\n", ixt->ixt_alg_id, SADB_EALG_MAX); + if (ixt->ixt_blocksize==0) /* || ixt->ixt_blocksize%2) need for ESP_NULL */ + barf_out(KERN_ERR "invalid blocksize=%d\n", ixt->ixt_blocksize); + if (ixt->ixt_keyminbits==0 && ixt->ixt_keymaxbits==0 && ixt->ixt_e_keylen==0) + goto zero_key_ok; + if (ixt->ixt_keyminbits==0) + barf_out(KERN_ERR "invalid keyminbits=%d\n", ixt->ixt_keyminbits); + if (ixt->ixt_keymaxbits==0) + barf_out(KERN_ERR "invalid keymaxbits=%d\n", ixt->ixt_keymaxbits); + if (ixt->ixt_e_keylen==0) + barf_out(KERN_ERR "invalid keysize=%d\n", ixt->ixt_e_keylen); +zero_key_ok: + if (ixt->ixt_e_ctx_size==0 && ixt->ixt_e_new_key == NULL) + barf_out(KERN_ERR "invalid key_e_size=%d and ixt_e_new_key=NULL\n", ixt->ixt_e_ctx_size); + if (ixt->ixt_e_cbc_encrypt==NULL) + barf_out(KERN_ERR "e_cbc_encrypt() must be not NULL\n"); + ret=0; +out: + return ret; +} + +/* validation for registering (auth) module */ +static int check_auth(struct ipsec_alg_auth *ixt) { + int ret=-EINVAL; + if (ixt->ixt_alg_id==0 || ixt->ixt_alg_id > SADB_AALG_MAX) + barf_out("invalid alg_id=%d > %d (SADB_AALG_MAX)\n", ixt->ixt_alg_id, SADB_AALG_MAX); + if (ixt->ixt_blocksize==0 || ixt->ixt_blocksize%2) + barf_out(KERN_ERR "invalid blocksize=%d\n", ixt->ixt_blocksize); + if (ixt->ixt_blocksize>AH_BLKLEN_MAX) + barf_out(KERN_ERR "sorry blocksize=%d > %d. " + "Please increase AH_BLKLEN_MAX and recompile\n", + ixt->ixt_blocksize, + AH_BLKLEN_MAX); + if (ixt->ixt_keyminbits==0 && ixt->ixt_keymaxbits==0 && ixt->ixt_a_keylen==0) + goto zero_key_ok; + if (ixt->ixt_keyminbits==0) + barf_out(KERN_ERR "invalid keyminbits=%d\n", ixt->ixt_keyminbits); + if (ixt->ixt_keymaxbits==0) + barf_out(KERN_ERR "invalid keymaxbits=%d\n", ixt->ixt_keymaxbits); + if (ixt->ixt_keymaxbits!=ixt->ixt_keyminbits) + barf_out(KERN_ERR "keymaxbits must equal keyminbits (not sure).\n"); + if (ixt->ixt_a_keylen==0) + barf_out(KERN_ERR "invalid keysize=%d\n", ixt->ixt_a_keylen); +zero_key_ok: + if (ixt->ixt_a_ctx_size==0) + barf_out(KERN_ERR "invalid a_ctx_size=%d\n", ixt->ixt_a_ctx_size); + if (ixt->ixt_a_hmac_set_key==NULL) + barf_out(KERN_ERR "a_hmac_set_key() must be not NULL\n"); + if (ixt->ixt_a_hmac_hash==NULL) + barf_out(KERN_ERR "a_hmac_hash() must be not NULL\n"); + ret=0; +out: + return ret; +} + +/* + * Generic (enc, auth) registration entry point + */ +int register_ipsec_alg(struct ipsec_alg *ixt) { + int ret=-EINVAL; + /* Validation */ + if (ixt==NULL) + barf_out("NULL ipsec_alg object passed\n"); + if ((ixt->ixt_version&0xffffff00) != (IPSEC_ALG_VERSION&0xffffff00)) + barf_out("incorrect version: %d.%d.%d-%d, " + "must be %d.%d.%d[-%d]\n", + IPSEC_ALG_VERSION_QUAD(ixt->ixt_version), + IPSEC_ALG_VERSION_QUAD(IPSEC_ALG_VERSION)); + switch(ixt->ixt_alg_type) { + case IPSEC_ALG_TYPE_AUTH: + if ((ret=check_auth((struct ipsec_alg_auth *)ixt)<0)) + goto out; + break; + case IPSEC_ALG_TYPE_ENCRYPT: + if ((ret=check_enc((struct ipsec_alg_enc *)ixt)<0)) + goto out; + /* + * Adapted two lines below: + * ivlen == 0 is possible (NULL enc has blocksize==1) + * + * fixed NULL support by David De Reu <DeReu@tComLabs.com> + */ + if (ixt->ixt_ivlen == 0 && ixt->ixt_blocksize > 1) + ixt->ixt_ivlen = ixt->ixt_blocksize*8; + break; + default: + barf_out("alg_type=%d not supported\n", ixt->ixt_alg_type); + } + INIT_LIST_HEAD(&ixt->ixt_list); + ret = ipsec_alg_insert(ixt); + if (ret<0) + barf_out(KERN_WARNING "ipsec_alg for alg_id=%d failed." + "Not loaded (ret=%d).\n", + ixt->ixt_alg_id, ret); + + ret = pfkey_list_insert_supported((struct supported *)&ixt->ixt_support, &(pfkey_supported_list[SADB_SATYPE_ESP])); + if (ret==0) { + ixt->ixt_state |= IPSEC_ALG_ST_SUPP; + /* send register event to userspace */ + pfkey_register_reply(SADB_SATYPE_ESP, NULL); + } else + printk(KERN_ERR "pfkey_list_insert_supported returned %d. " + "Loading anyway.\n", ret); + ret=0; +out: + return ret; +} + +/* + * unregister ipsec_alg object from own tables, if + * success => calls pfkey_list_remove_supported() + */ +int unregister_ipsec_alg(struct ipsec_alg *ixt) { + int ret= -EINVAL; + switch(ixt->ixt_alg_type) { + case IPSEC_ALG_TYPE_AUTH: + case IPSEC_ALG_TYPE_ENCRYPT: + break; + default: + /* this is not a typo :) */ + barf_out("frog found in list (\"%s\"): ixt_p=NULL\n", + ixt->ixt_name); + } + + ret=ipsec_alg_delete(ixt); + if (ixt->ixt_state&IPSEC_ALG_ST_SUPP) { + ixt->ixt_state &= ~IPSEC_ALG_ST_SUPP; + pfkey_list_remove_supported((struct supported *)&ixt->ixt_support, &(pfkey_supported_list[SADB_SATYPE_ESP])); + /* send register event to userspace */ + pfkey_register_reply(SADB_SATYPE_ESP, NULL); + } + +out: + return ret; +} +/* + * Must be called from user context + * used at module load type for testing algo implementation + */ +static int ipsec_alg_test_encrypt(int enc_alg, int test) { + int ret; + caddr_t buf = NULL; + int iv_size, keysize, key_e_size; + struct ipsec_alg_enc *ixt_e; + void *tmp_key_e = NULL; + #define BUFSZ 1024 + #define MARGIN 0 + #define test_enc (buf+MARGIN) + #define test_dec (test_enc+BUFSZ+MARGIN) + #define test_tmp (test_dec+BUFSZ+MARGIN) + #define test_key_e (test_tmp+BUFSZ+MARGIN) + #define test_iv (test_key_e+key_e_size+MARGIN) + #define test_key (test_iv+iv_size+MARGIN) + #define test_size (BUFSZ*3+key_e_size+iv_size+keysize+MARGIN*7) + ixt_e=(struct ipsec_alg_enc *)ipsec_alg_get(IPSEC_ALG_TYPE_ENCRYPT, enc_alg); + if (ixt_e==NULL) { + KLIPS_PRINT(1, + "klips_debug: ipsec_alg_test_encrypt: " + "encalg=%d object not found\n", + enc_alg); + ret=-EINVAL; + goto out; + } + iv_size=ixt_e->ixt_ivlen / 8; + key_e_size=ixt_e->ixt_e_ctx_size; + keysize=ixt_e->ixt_e_keylen; + KLIPS_PRINT(1, + "klips_debug: ipsec_alg_test_encrypt: " + "enc_alg=%d blocksize=%d key_e_size=%d keysize=%d\n", + enc_alg, iv_size, key_e_size, keysize); + if ((buf=kmalloc (test_size, GFP_KERNEL)) == NULL) { + ret= -ENOMEM; + goto out; + } + get_random_bytes(test_key, keysize); + get_random_bytes(test_iv, iv_size); + if (ixt_e->ixt_e_new_key) { + tmp_key_e = ixt_e->ixt_e_new_key(ixt_e, test_key, keysize); + ret = tmp_key_e ? 0 : -EINVAL; + } else { + tmp_key_e = test_key_e; + ret = ixt_e->ixt_e_set_key(ixt_e, test_key_e, test_key, keysize); + } + if (ret < 0) + goto out; + get_random_bytes(test_enc, BUFSZ); + memcpy(test_tmp, test_enc, BUFSZ); + ret=ixt_e->ixt_e_cbc_encrypt(ixt_e, tmp_key_e, test_enc, BUFSZ, test_iv, 1); + printk(KERN_INFO + "klips_info: ipsec_alg_test_encrypt: " + "cbc_encrypt=1 ret=%d\n", + ret); + ret=memcmp(test_enc, test_tmp, BUFSZ); + printk(KERN_INFO + "klips_info: ipsec_alg_test_encrypt: " + "memcmp(enc, tmp) ret=%d: %s\n", ret, + ret!=0? "OK. (encr->DIFFers)" : "FAIL! (encr->SAME)" ); + memcpy(test_dec, test_enc, BUFSZ); + ret=ixt_e->ixt_e_cbc_encrypt(ixt_e, tmp_key_e, test_dec, BUFSZ, test_iv, 0); + printk(KERN_INFO + "klips_info: ipsec_alg_test_encrypt: " + "cbc_encrypt=0 ret=%d\n", ret); + ret=memcmp(test_dec, test_tmp, BUFSZ); + printk(KERN_INFO + "klips_info: ipsec_alg_test_encrypt: " + "memcmp(dec,tmp) ret=%d: %s\n", ret, + ret==0? "OK. (encr->decr->SAME)" : "FAIL! (encr->decr->DIFFers)" ); + { + /* Shamelessly taken from drivers/md sources O:) */ + unsigned long now; + int i, count, max=0; + int encrypt, speed; + for (encrypt=0; encrypt <2;encrypt ++) { + for (i = 0; i < 5; i++) { + now = jiffies; + count = 0; + while (jiffies == now) { + mb(); + ixt_e->ixt_e_cbc_encrypt(ixt_e, + tmp_key_e, test_tmp, + BUFSZ, test_iv, encrypt); + mb(); + count++; + mb(); + } + if (count > max) + max = count; + } + speed = max * (HZ * BUFSZ / 1024); + printk(KERN_INFO + "klips_info: ipsec_alg_test_encrypt: " + "%s %s speed=%d KB/s\n", + ixt_e->ixt_name, + encrypt? "encrypt": "decrypt", speed); + } + } +out: + if (tmp_key_e && ixt_e->ixt_e_destroy_key) ixt_e->ixt_e_destroy_key(ixt_e, tmp_key_e); + if (buf) kfree(buf); + if (ixt_e) ipsec_alg_put((struct ipsec_alg *)ixt_e); + return ret; + #undef test_enc + #undef test_dec + #undef test_tmp + #undef test_key_e + #undef test_iv + #undef test_key + #undef test_size +} +/* + * Must be called from user context + * used at module load type for testing algo implementation + */ +static int ipsec_alg_test_auth(int auth_alg, int test) { + int ret; + caddr_t buf = NULL; + int blocksize, keysize, key_a_size; + struct ipsec_alg_auth *ixt_a; + #define BUFSZ 1024 + #define MARGIN 0 + #define test_auth (buf+MARGIN) + #define test_key_a (test_auth+BUFSZ+MARGIN) + #define test_key (test_key_a+key_a_size+MARGIN) + #define test_hash (test_key+keysize+MARGIN) + #define test_size (BUFSZ+key_a_size+keysize+AHHMAC_HASHLEN+MARGIN*4) + ixt_a=(struct ipsec_alg_auth *)ipsec_alg_get(IPSEC_ALG_TYPE_AUTH, auth_alg); + if (ixt_a==NULL) { + KLIPS_PRINT(1, + "klips_debug: ipsec_alg_test_auth: " + "encalg=%d object not found\n", + auth_alg); + ret=-EINVAL; + goto out; + } + blocksize=ixt_a->ixt_blocksize; + key_a_size=ixt_a->ixt_a_ctx_size; + keysize=ixt_a->ixt_a_keylen; + KLIPS_PRINT(1, + "klips_debug: ipsec_alg_test_auth: " + "auth_alg=%d blocksize=%d key_a_size=%d keysize=%d\n", + auth_alg, blocksize, key_a_size, keysize); + if ((buf=kmalloc (test_size, GFP_KERNEL)) == NULL) { + ret= -ENOMEM; + goto out; + } + get_random_bytes(test_key, keysize); + ret = ixt_a->ixt_a_hmac_set_key(ixt_a, test_key_a, test_key, keysize); + if (ret < 0 ) + goto out; + get_random_bytes(test_auth, BUFSZ); + ret=ixt_a->ixt_a_hmac_hash(ixt_a, test_key_a, test_auth, BUFSZ, test_hash, AHHMAC_HASHLEN); + printk(KERN_INFO + "klips_info: ipsec_alg_test_auth: " + "ret=%d\n", ret); + { + /* Shamelessly taken from drivers/md sources O:) */ + unsigned long now; + int i, count, max=0; + int speed; + for (i = 0; i < 5; i++) { + now = jiffies; + count = 0; + while (jiffies == now) { + mb(); + ixt_a->ixt_a_hmac_hash(ixt_a, test_key_a, test_auth, BUFSZ, test_hash, AHHMAC_HASHLEN); + mb(); + count++; + mb(); + } + if (count > max) + max = count; + } + speed = max * (HZ * BUFSZ / 1024); + printk(KERN_INFO + "klips_info: ipsec_alg_test_auth: " + "%s hash speed=%d KB/s\n", + ixt_a->ixt_name, + speed); + } +out: + if (buf) kfree(buf); + if (ixt_a) ipsec_alg_put((struct ipsec_alg *)ixt_a); + return ret; + #undef test_auth + #undef test_key_a + #undef test_key + #undef test_hash + #undef test_size +} +int ipsec_alg_test(unsigned alg_type, unsigned alg_id, int test) { + switch(alg_type) { + case IPSEC_ALG_TYPE_ENCRYPT: + return ipsec_alg_test_encrypt(alg_id, test); + break; + case IPSEC_ALG_TYPE_AUTH: + return ipsec_alg_test_auth(alg_id, test); + break; + } + printk(KERN_ERR "klips_info: ipsec_alg_test() called incorrectly: " + "alg_type=%d alg_id=%d\n", + alg_type, alg_id); + return -EINVAL; +} +int ipsec_alg_init(void) { + KLIPS_PRINT(1, "klips_info:ipsec_alg_init: " + "KLIPS alg v=%d.%d.%d-%d (EALG_MAX=%d, AALG_MAX=%d)\n", + IPSEC_ALG_VERSION_QUAD(IPSEC_ALG_VERSION), + SADB_EALG_MAX, SADB_AALG_MAX); + /* Initialize tables */ + write_lock_bh(&ipsec_alg_lock); + ipsec_alg_hash_init(); + write_unlock_bh(&ipsec_alg_lock); + /* Initialize static algos */ + KLIPS_PRINT(1, "klips_info:ipsec_alg_init: " + "calling ipsec_alg_static_init()\n"); + ipsec_alg_static_init(); + return 0; +} + +/********************************************** + * + * INTERFACE for ipsec_sa init and wipe + * + **********************************************/ + +/* + * Called from pluto -> pfkey_v2_parser.c:pfkey_ipsec_sa_init() + */ +int ipsec_alg_sa_init(struct ipsec_sa *sa_p) { + struct ipsec_alg_enc *ixt_e; + struct ipsec_alg_auth *ixt_a; + + /* Only ESP for now ... */ + if (sa_p->ips_said.proto != IPPROTO_ESP) + return -EPROTONOSUPPORT; + KLIPS_PRINT(debug_pfkey, "klips_debug: ipsec_alg_sa_init() :" + "entering for encalg=%d, authalg=%d\n", + sa_p->ips_encalg, sa_p->ips_authalg); + if ((ixt_e=(struct ipsec_alg_enc *) + ipsec_alg_get(IPSEC_ALG_TYPE_ENCRYPT, sa_p->ips_encalg))) { + KLIPS_PRINT(debug_pfkey, + "klips_debug: ipsec_alg_sa_init() :" + "found ipsec_alg (ixt_e=%p) for encalg=%d\n", + ixt_e, sa_p->ips_encalg); + sa_p->ips_alg_enc=ixt_e; + } + if ((ixt_a=(struct ipsec_alg_auth *) + ipsec_alg_get(IPSEC_ALG_TYPE_AUTH, sa_p->ips_authalg))) { + KLIPS_PRINT(debug_pfkey, + "klips_debug: ipsec_alg_sa_init() :" + "found ipsec_alg (ixt_a=%p) for auth=%d\n", + ixt_a, sa_p->ips_authalg); + sa_p->ips_alg_auth=ixt_a; + } + return 0; +} + +/* + * Called from pluto -> ipsec_sa.c:ipsec_sa_delchain() + */ +int ipsec_alg_sa_wipe(struct ipsec_sa *sa_p) { + struct ipsec_alg *ixt; + if ((ixt=(struct ipsec_alg *)sa_p->ips_alg_enc)) { + KLIPS_PRINT(debug_pfkey, "klips_debug: ipsec_alg_sa_wipe() :" + "unlinking for encalg=%d\n", + ixt->ixt_alg_id); + ipsec_alg_put(ixt); + } + if ((ixt=(struct ipsec_alg *)sa_p->ips_alg_auth)) { + KLIPS_PRINT(debug_pfkey, "klips_debug: ipsec_alg_sa_wipe() :" + "unlinking for authalg=%d\n", + ixt->ixt_alg_id); + ipsec_alg_put(ixt); + } + return 0; +} + +IPSEC_PROCFS_DEBUG_NO_STATIC +int +ipsec_xform_get_info(char *buffer, + char **start, + off_t offset, + int length IPSEC_PROC_LAST_ARG) +{ + int len = 0; + off_t begin = 0; + int i; + struct list_head *head; + struct ipsec_alg *ixt; + + KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS, + "klips_debug:ipsec_tncfg_get_info: " + "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n", + buffer, + *start, + (int)offset, + length); + + for(i = 0, head = ipsec_alg_hash_table; i< IPSEC_ALG_HASHSZ; i++, head++) + { + struct list_head *p; + for (p=head->next; p!=head; p=p->next) + { + ixt = list_entry(p, struct ipsec_alg, ixt_list); + len += ipsec_snprintf(buffer+len, length-len, + "VERSION=%d TYPE=%d ID=%d NAME=%s REFCNT=%d ", + ixt->ixt_version, ixt->ixt_alg_type, ixt->ixt_alg_id, + ixt->ixt_name, ixt->ixt_refcnt); + + len += ipsec_snprintf(buffer+len, length-len, + "STATE=%08x BLOCKSIZE=%d IVLEN=%d KEYMINBITS=%d KEYMAXBITS=%d ", + ixt->ixt_state, ixt->ixt_blocksize, + ixt->ixt_ivlen, ixt->ixt_keyminbits, ixt->ixt_keymaxbits); + + len += ipsec_snprintf(buffer+len, length-len, + "IVLEN=%d KEYMINBITS=%d KEYMAXBITS=%d ", + ixt->ixt_ivlen, ixt->ixt_keyminbits, ixt->ixt_keymaxbits); + + switch(ixt->ixt_alg_type) + { + case IPSEC_ALG_TYPE_AUTH: + { + struct ipsec_alg_auth *auth = (struct ipsec_alg_auth *)ixt; + + len += ipsec_snprintf(buffer+len, length-len, + "KEYLEN=%d CTXSIZE=%d AUTHLEN=%d ", + auth->ixt_a_keylen, auth->ixt_a_ctx_size, + auth->ixt_a_authlen); + break; + } + case IPSEC_ALG_TYPE_ENCRYPT: + { + struct ipsec_alg_enc *enc = (struct ipsec_alg_enc *)ixt; + len += ipsec_snprintf(buffer+len, length-len, + "KEYLEN=%d CTXSIZE=%d ", + enc->ixt_e_keylen, enc->ixt_e_ctx_size); + + break; + } + } + + len += ipsec_snprintf(buffer+len, length-len, "\n"); + } + } + + *start = buffer + (offset - begin); /* Start of wanted data */ + len -= (offset - begin); /* Start slop */ + if (len > length) + len = length; + return len; +} + +/* + * As the author of this module, I ONLY ALLOW using it from + * GPL (or same LICENSE TERMS as kernel source) modules. + * + * In respect to hardware crypto engines this means: + * * Closed-source device drivers ARE NOT ALLOWED to use + * this interface. + * * Closed-source VHDL/Verilog firmware running on + * the crypto hardware device IS ALLOWED to use this interface + * via a GPL (or same LICENSE TERMS as kernel source) device driver. + * --Juan Jose Ciarlante 20/03/2002 (thanks RGB for the correct wording) + */ + +/* + * These symbols can only be used from GPL modules + * for now, I'm disabling this because it creates false + * symbol problems for old modutils. + */ + +/* #ifndef EXPORT_SYMBOL_GPL */ +#undef EXPORT_SYMBOL_GPL +#define EXPORT_SYMBOL_GPL EXPORT_SYMBOL +/* #endif */ +EXPORT_SYMBOL_GPL(register_ipsec_alg); +EXPORT_SYMBOL_GPL(unregister_ipsec_alg); +EXPORT_SYMBOL_GPL(ipsec_alg_test); +#endif /* CONFIG_IPSEC_ALG */ |