/* * Common routines for IPsec SA maintenance routines. * * Copyright (C) 1996, 1997 John Ioannidis. * Copyright (C) 1998, 1999, 2000, 2001, 2002 Richard Guy Briggs. * Copyright (C) 2012 Paul Wouters * Copyright (C) 2012 David McCullough * * 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 . * * 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. * * This is the file formerly known as "ipsec_xform.h" * */ #include #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38) && \ !defined(AUTOCONF_INCLUDED) #include #endif #include /* printk() */ #include "libreswan/ipsec_param.h" #include /* kmalloc() */ #include /* vmalloc() */ #include /* error codes */ #include /* size_t */ #include /* mark_bh */ #include /* struct device, and other headers */ #include /* eth_type_trans */ #include /* struct iphdr */ #include #include #include /* *lock* */ #include #include "libreswan/radij.h" #include "libreswan/ipsec_stats.h" #include "libreswan/ipsec_life.h" #include "libreswan/ipsec_sa.h" #include "libreswan/ipsec_xform.h" #include "libreswan/ipsec_encap.h" #include "libreswan/ipsec_radij.h" #include "libreswan/ipsec_xform.h" #include "libreswan/ipsec_ipe4.h" #include "libreswan/ipsec_ah.h" #include "libreswan/ipsec_esp.h" #include "libreswan/ipsec_ipip.h" #ifdef CONFIG_KLIPS_IPCOMP #include "libreswan/ipsec_ipcomp.h" #endif /* CONFIG_KLIPS_COMP */ #include #include #include "libreswan/ipsec_proto.h" #include "libreswan/ipsec_alg.h" #ifdef CONFIG_KLIPS_OCF # include "ipsec_ocf.h" #endif #define SENDERR(_x) do { error = -(_x); goto errlab; } while (0) struct ipsec_sa *ipsec_sadb_hash[SADB_HASHMOD]; DEFINE_SPINLOCK(tdb_lock); #ifdef IPSEC_SA_RECOUNT_DEBUG struct ipsec_sa *ipsec_sa_raw = NULL; #endif struct ipsec_sadb ipsec_sadb; /* the sub table must be narrower (or equal) in bits than the variable type in the main table to count the number of unused entries in it. */ typedef struct { int testSizeOf_refSubTable : ((sizeof(IPsecRefTableUnusedCount) * 8) < IPSEC_SA_REF_SUBTABLE_IDX_WIDTH ? -1 : 1); } dummy; /* The field where the saref will be hosted in the skb must be wide enough to accomodate the information it needs to store. */ typedef struct { int testSizeOf_refField : (IPSEC_SA_REF_HOST_FIELD_WIDTH < IPSEC_SA_REF_TABLE_IDX_WIDTH ? -1 : 1 ); } dummy2; #define IPS_HASH(said) (((said)->spi + (said)->dst.u.v4.sin_addr.s_addr + \ (said)->proto) % SADB_HASHMOD) static int ipsec_saref_verify_slot(IPsecSAref_t ref); static int ipsec_SArefSubTable_alloc(unsigned table); static int ipsec_saref_freelist_init(void); static IPsecSAref_t ipsec_SAref_alloc(int*erorr); /* pass in error var by pointer */ static int ipsec_SAref_recycle(void) { int table, i; int error = 0; int entry; int addone; ipsec_sadb.refFreeListHead = IPSEC_SAREF_NULL; ipsec_sadb.refFreeListTail = IPSEC_SAREF_NULL; KLIPS_PRINT(debug_xform, "klips_debug:ipsec_SAref_recycle: " "recycling, continuing from SAref=%d (0p%p), table=%d, entry=%d.\n", ipsec_sadb.refFreeListCont, (ipsec_sadb.refTable[IPsecSAref2table(ipsec_sadb. refFreeListCont)] != NULL) ? IPsecSAref2SA(ipsec_sadb.refFreeListCont) : NULL, IPsecSAref2table(ipsec_sadb.refFreeListCont), IPsecSAref2entry(ipsec_sadb.refFreeListCont)); /* add one additional table entry */ addone = 0; for (i = 0; i < IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES; i++) { if (ipsec_sadb.refFreeListCont == IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES * IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES) { KLIPS_PRINT(debug_xform, "klips_debug:ipsec_SAref_recycle: " "end of table reached, continuing at start..\n"); ipsec_sadb.refFreeListCont = IPSEC_SAREF_FIRST; } table = IPsecSAref2table(ipsec_sadb.refFreeListCont); if (ipsec_sadb.refTable[table] == NULL) { if (addone == 0) { addone = 1; error = ipsec_SArefSubTable_alloc(table); if (error) return error; else break; } } for (entry = IPsecSAref2entry(ipsec_sadb.refFreeListCont); entry < IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES; entry++) { if (ipsec_sadb.refTable[table]->entry[entry] == NULL) { ipsec_sadb.refFreeList[++ipsec_sadb. refFreeListTail] = IPsecSArefBuild(table, entry); if (ipsec_sadb.refFreeListTail == (IPSEC_SA_REF_FREELIST_NUM_ENTRIES - 1)) { ipsec_sadb.refFreeListHead = IPSEC_SAREF_FIRST; ipsec_sadb.refFreeListCont = ipsec_sadb.refFreeList[ ipsec_sadb. refFreeListTail ] + 1; KLIPS_PRINT(debug_xform, "klips_debug:ipsec_SAref_recycle: " "SArefFreeList refilled.\n"); return 0; } } ipsec_sadb.refFreeListCont++; } } if (ipsec_sadb.refFreeListTail == IPSEC_SAREF_NULL) { KLIPS_PRINT(debug_xform, "klips_debug:ipsec_SAref_recycle: " "out of room in the SArefTable.\n"); return -ENOSPC; } ipsec_sadb.refFreeListHead = IPSEC_SAREF_FIRST; ipsec_sadb.refFreeListCont = ipsec_sadb.refFreeList[ipsec_sadb.refFreeListTail] + 1; KLIPS_PRINT(debug_xform, "klips_debug:ipsec_SAref_recycle: " "SArefFreeList partly refilled to %d of %d.\n", ipsec_sadb.refFreeListTail, IPSEC_SA_REF_FREELIST_NUM_ENTRIES); return 0; } static int ipsec_SArefSubTable_alloc(unsigned table) { unsigned entry; struct IPsecSArefSubTable* SArefsub; KLIPS_PRINT(debug_xform, "klips_debug:ipsec_SArefSubTable_alloc: " "allocating %lu bytes for table %u of %u.\n", (unsigned long) IPSEC_SA_REF_SUBTABLE_SIZE, table, IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES); /* allocate another sub-table */ SArefsub = kmalloc(IPSEC_SA_REF_SUBTABLE_SIZE, GFP_ATOMIC); if (SArefsub == NULL) { KLIPS_PRINT(debug_xform, "klips_debug:ipsec_SArefSubTable_alloc: " "error allocating memory for table %u of %u!\n", table, IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES); return -ENOMEM; } /* add this sub-table to the main table */ ipsec_sadb.refTable[table] = SArefsub; /* initialise each element to NULL */ KLIPS_PRINT(debug_xform, "klips_debug:ipsec_SArefSubTable_alloc: " "initialising %u elements (2 ^ %u) of table %u.\n", IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES, IPSEC_SA_REF_SUBTABLE_IDX_WIDTH, table); for (entry = 0; entry < IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES; entry++) SArefsub->entry[entry] = NULL; return 0; } static int ipsec_saref_verify_slot(IPsecSAref_t ref) { int ref_table = IPsecSAref2table(ref); if (ipsec_sadb.refTable[ref_table] == NULL) return ipsec_SArefSubTable_alloc(ref_table); return 0; } int ipsec_saref_freelist_init(void) { int i; KLIPS_PRINT(debug_xform, "klips_debug:ipsec_saref_freelist_init: " "initialising %u elements of FreeList.\n", IPSEC_SA_REF_FREELIST_NUM_ENTRIES); for (i = 0; i < IPSEC_SA_REF_FREELIST_NUM_ENTRIES; i++) ipsec_sadb.refFreeList[i] = IPSEC_SAREF_NULL; ipsec_sadb.refFreeListHead = IPSEC_SAREF_NULL; ipsec_sadb.refFreeListCont = IPSEC_SAREF_FIRST; ipsec_sadb.refFreeListTail = IPSEC_SAREF_NULL; return 0; } int ipsec_sadb_init(void) { int error = 0; unsigned i; for (i = 0; i < SADB_HASHMOD; i++) ipsec_sadb_hash[i] = NULL; /* parts above are for the old style SADB hash table */ /* initialise SA reference table */ /* initialise the main table */ KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sadb_init: " "initialising main table of size %u (2 ^ %u).\n", IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES, IPSEC_SA_REF_MAINTABLE_IDX_WIDTH); { unsigned table; for (table = 0; table < IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES; table++) ipsec_sadb.refTable[table] = NULL; } /* allocate the first sub-table */ error = ipsec_SArefSubTable_alloc(0); if (error) return error; error = ipsec_saref_freelist_init(); return error; } static IPsecSAref_t ipsec_SAref_alloc(int*error) /* pass in error var by pointer */ { IPsecSAref_t SAref; KLIPS_PRINT(debug_xform, "ipsec_SAref_alloc: " "SAref requested... head=%d, cont=%d, tail=%d, listsize=%d.\n", ipsec_sadb.refFreeListHead, ipsec_sadb.refFreeListCont, ipsec_sadb.refFreeListTail, IPSEC_SA_REF_FREELIST_NUM_ENTRIES); if (ipsec_sadb.refFreeListHead == IPSEC_SAREF_NULL) { KLIPS_PRINT(debug_xform, "ipsec_SAref_alloc: " "FreeList empty, recycling...\n"); *error = ipsec_SAref_recycle(); if (*error) return IPSEC_SAREF_NULL; } SAref = ipsec_sadb.refFreeList[ipsec_sadb.refFreeListHead]; if (SAref == IPSEC_SAREF_NULL) { KLIPS_ERROR(debug_xform, "ipsec_SAref_alloc: " "unexpected error, refFreeListHead = %d points to invalid entry.\n", ipsec_sadb.refFreeListHead); *error = -ESPIPE; return IPSEC_SAREF_NULL; } KLIPS_PRINT(debug_xform, "ipsec_SAref_alloc: " "allocating SAref=%d, table=%u, entry=%u of %u.\n", SAref, IPsecSAref2table(SAref), IPsecSAref2entry(SAref), IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES * IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES); ipsec_sadb.refFreeList[ipsec_sadb.refFreeListHead] = IPSEC_SAREF_NULL; ipsec_sadb.refFreeListHead++; if (ipsec_sadb.refFreeListHead > ipsec_sadb.refFreeListTail) { KLIPS_PRINT(debug_xform, "ipsec_SAref_alloc: " "last FreeList entry allocated, resetting list head to empty.\n"); ipsec_sadb.refFreeListHead = IPSEC_SAREF_NULL; } return SAref; } #if 0 int ipsec_sa_print(struct ipsec_sa *ips) { char sa[SATOT_BUF]; size_t sa_len; printk(KERN_INFO "klips_debug: SA:"); if (ips == NULL) { printk("NULL\n"); return -ENOENT; } printk(" ref=%d", ips->ips_ref); printk(" refcount=%d", atomic_read(&ips->ips_refcount)); if (ips->ips_hnext != NULL) printk(" hnext=0p%p", ips->ips_hnext); if (ips->ips_next != NULL) printk(" next=0p%p", ips->ips_next); if (ips->ips_prev != NULL) printk(" prev=0p%p", ips->ips_prev); sa_len = KLIPS_SATOT(1, &ips->ips_said, 0, sa, sizeof(sa)); printk(" said=%s", sa_len ? sa : " (error)"); if (ips->ips_seq) printk(" seq=%u", ips->ips_seq); if (ips->ips_pid) printk(" pid=%u", ips->ips_pid); if (ips->ips_authalg) printk(" authalg=%u", ips->ips_authalg); if (ips->ips_encalg) printk(" encalg=%u", ips->ips_encalg); printk(" XFORM=%s%s%s", IPS_XFORM_NAME(ips)); if (ips->ips_replaywin) printk(" ooowin=%u", ips->ips_replaywin); if (ips->ips_flags) printk(" flags=%u", ips->ips_flags); if (ips->ips_addr_s) { char buf[SUBNETTOA_BUF]; sin_addrtot(ips->ips_addr_s, 0, buf, sizeof(buf)); printk(" src=%s", buf); } if (ips->ips_addr_d) { char buf[SUBNETTOA_BUF]; sin_addrtot(ips->ips_addr_s, 0, buf, sizeof(buf)); printk(" dst=%s", buf); } if (ips->ips_addr_p) { char buf[SUBNETTOA_BUF]; sin_addrtot(ips->ips_addr_p, 0, buf, sizeof(buf)); printk(" proxy=%s", buf); } if (ips->ips_key_bits_a) printk(" key_bits_a=%u", ips->ips_key_bits_a); if (ips->ips_key_bits_e) printk(" key_bits_e=%u", ips->ips_key_bits_e); printk("\n"); return 0; } #endif struct ipsec_sa*ipsec_sa_alloc(int*error) /* pass in error var by pointer */ { struct ipsec_sa* ips; if ((ips = kmalloc(sizeof(*ips), GFP_ATOMIC) ) == NULL) { KLIPS_PRINT(debug_xform, "ipsec_sa_alloc: " "memory allocation error\n"); *error = -ENOMEM; return NULL; } memset((caddr_t)ips, 0, sizeof(*ips)); #ifdef IPSEC_SA_RECOUNT_DEBUG ips->ips_raw = ipsec_sa_raw; ipsec_sa_raw = ips; #endif /* return with at least counter = 1 */ ipsec_sa_get(ips, IPSEC_REFALLOC); *error = 0; return ips; } void ipsec_sa_untern(struct ipsec_sa *ips) { IPsecSAref_t ref = ips->ips_ref; int error; /* verify that we are removing correct item! */ error = ipsec_saref_verify_slot(ref); if (error) return; if (IPsecSAref2SA(ref) == ips) { IPsecSAref2SA(ref) = NULL; ipsec_sa_put(ips, IPSEC_REFINTERN); } else { KLIPS_PRINT(debug_xform, "ipsec_sa_untern: " "ref=%u -> %p but untern'ing %p\n", ref, IPsecSAref2SA(ref), ips); } } int ipsec_sa_intern(struct ipsec_sa *ips) { int error = 0; IPsecSAref_t ref = ips->ips_ref; if (ref == IPSEC_SAREF_NULL) { ref = ipsec_SAref_alloc(&error); /* pass in error return by pointer */ KLIPS_PRINT(debug_xform, "ipsec_sa_intern: " "allocated ref=%u for sa %p\n", ref, ips); if (ref == IPSEC_SAREF_NULL) { KLIPS_PRINT(debug_xform, "ipsec_sa_intern: " "SAref allocation error\n"); return error; } ips->ips_ref = ref; } error = ipsec_saref_verify_slot(ref); if (error) return error; ipsec_sa_get(ips, IPSEC_REFINTERN); /* * if there is an existing SA at this reference, then free it * note, that nsa might == ips!. That's okay, we just incremented * the reference count above. */ { struct ipsec_sa *nsa = IPsecSAref2SA(ref); if (nsa) ipsec_sa_put(nsa, IPSEC_REFINTERN); } KLIPS_PRINT(debug_xform, "ipsec_sa_intern: " "SAref[%d]=%p\n", ips->ips_ref, ips); IPsecSAref2SA(ips->ips_ref) = ips; /* return OK */ return 0; } struct ipsec_sa *ipsec_sa_getbyid(ip_said *said, int type) { int hashval; struct ipsec_sa *ips; char sa[SATOT_BUF]; size_t sa_len; if (said == NULL) { KLIPS_PRINT(debug_xform, "ipsec_sa_getbyid: " "null pointer passed in!\n"); return NULL; } hashval = IPS_HASH(said); sa_len = KLIPS_SATOT(debug_xform, said, 0, sa, sizeof(sa)); KLIPS_PRINT(debug_xform, "ipsec_sa_getbyid: " "linked entry in ipsec_sa table for hash=%d of SA:%s requested.\n", hashval, sa_len ? sa : " (error)"); if ((ips = ipsec_sadb_hash[hashval]) == NULL) { KLIPS_PRINT(debug_xform, "ipsec_sa_getbyid: " "no entries in ipsec_sa table for hash=%d of SA:%s.\n", hashval, sa_len ? sa : " (error)"); return NULL; } for (; ips; ips = ips->ips_hnext) { if (ips->ips_said.spi == said->spi && ip_address_eq(&ips->ips_said.dst, &said->dst) && ips->ips_said.proto == said->proto) { ipsec_sa_get(ips, type); return ips; } } KLIPS_PRINT(debug_xform, "ipsec_sa_getbyid: " "no entry in linked list for hash=%d of SA:%s.\n", hashval, sa_len ? sa : " (error)"); return NULL; } struct ipsec_sa *ipsec_sa_getbyref(IPsecSAref_t ref, int type) { struct ipsec_sa *ips; struct IPsecSArefSubTable *st = ipsec_sadb.refTable[IPsecSAref2table(ref)]; if (st == NULL) return NULL; ips = st->entry[IPsecSAref2entry(ref)]; if (ips) ipsec_sa_get(ips, type); return ips; } void __ipsec_sa_put(struct ipsec_sa *ips, const char *func, int line, int type) { if (ips == NULL) { KLIPS_PRINT(debug_xform, "ipsec_sa_put: " "null pointer passed in!\n"); return; } if (debug_xform) { char sa[SATOT_BUF]; size_t sa_len; sa_len = KLIPS_SATOT(debug_xform, &ips->ips_said, 0, sa, sizeof(sa)); KLIPS_PRINT(debug_xform, "ipsec_sa_put: " "ipsec_sa %p SA:%s, ref:%d reference count (%d--) decremented by %s:%d.\n", ips, sa_len ? sa : " (error)", ips->ips_ref, atomic_read(&ips->ips_refcount), func, line); } #ifdef IPSEC_SA_RECOUNT_DEBUG if (type >= 0 && type < sizeof(ips->ips_track)) { unsigned long flags; local_irq_save(flags); if (ips->ips_track[type] == 0) printk("ipsec_sa_put: UNDERFLOW for %d @ %s %d\n", type, func, line); else ips->ips_track[type]--; local_irq_restore(flags); } else { printk("BAD BAD BAD @ %s %d\n", func, line); } #endif if (atomic_dec_and_test(&ips->ips_refcount)) { KLIPS_PRINT(debug_xform, "ipsec_sa_put: freeing %p\n", ips); /* it was zero */ ipsec_sa_wipe(ips); } } struct ipsec_sa *__ipsec_sa_get(struct ipsec_sa *ips, const char *func, int line, int type) { if (ips == NULL) return NULL; if (debug_xform) { char sa[SATOT_BUF]; size_t sa_len; sa_len = KLIPS_SATOT(debug_xform, &ips->ips_said, 0, sa, sizeof(sa)); KLIPS_PRINT(debug_xform, "ipsec_sa_get: " "ipsec_sa %p SA:%s, ref:%d reference count (%d++) incremented by %s:%d.\n", ips, sa_len ? sa : " (error)", ips->ips_ref, atomic_read(&ips->ips_refcount), func, line); } atomic_inc(&ips->ips_refcount); #ifdef IPSEC_SA_RECOUNT_DEBUG if (type >= 0 && type < sizeof(ips->ips_track)) { unsigned long flags; local_irq_save(flags); if (ips->ips_track[type] == 255) printk("ipsec_sa_get: OVERFLOW for %d @ %s %d\n", type, func, line); else ips->ips_track[type]++; local_irq_restore(flags); } else { printk("BAD BAD BAD @ %s %d\n", func, line); } #endif #if 0 /* * DAVIDM: if we include this code it means the SA is freed immediately * on creation and then reused ! Not sure why it is here. */ if (atomic_dec_and_test(&ips->ips_refcount)) { KLIPS_PRINT(debug_xform, "ipsec_sa_get: freeing %p\n", ips); /* it was zero */ ipsec_sa_wipe(ips); } #endif return ips; } /* The ipsec_sa table better *NOT* be locked before it is handed in, or SMP locks will happen */ int ipsec_sa_add(struct ipsec_sa *ips) { int error = 0; unsigned int hashval; if (ips == NULL) { KLIPS_PRINT(debug_xform, "klips_error:ipsec_sa_add: " "null pointer passed in!\n"); return -ENODATA; } hashval = IPS_HASH(&ips->ips_said); ipsec_sa_get(ips, IPSEC_REFSAADD); spin_lock_bh(&tdb_lock); ips->ips_hnext = ipsec_sadb_hash[hashval]; ipsec_sadb_hash[hashval] = ips; spin_unlock_bh(&tdb_lock); return error; } /* * remove it from the hash chain, decrementing hash count */ void ipsec_sa_rm(struct ipsec_sa *ips) { unsigned int hashval; char sa[SATOT_BUF]; size_t sa_len; if (ips == NULL) return; hashval = IPS_HASH(&ips->ips_said); sa_len = KLIPS_SATOT(debug_xform, &ips->ips_said, 0, sa, sizeof(sa)); KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sa_del: " "unhashing SA:%s (ref=%u), hashval=%d.\n", sa_len ? sa : " (error)", ips->ips_ref, hashval); if (ipsec_sadb_hash[hashval] == NULL) return; if (ips == ipsec_sadb_hash[hashval]) { ipsec_sadb_hash[hashval] = ipsec_sadb_hash[hashval]->ips_hnext; ips->ips_hnext = NULL; ipsec_sa_put(ips, IPSEC_REFSAADD); KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sa_del: " "successfully unhashed first ipsec_sa in chain.\n"); return; } else { struct ipsec_sa *ipstp; for (ipstp = ipsec_sadb_hash[hashval]; ipstp; ipstp = ipstp->ips_hnext) { if (ipstp->ips_hnext == ips) { ipstp->ips_hnext = ips->ips_hnext; ips->ips_hnext = NULL; ipsec_sa_put(ips, IPSEC_REFSAADD); KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sa_del: " "successfully unhashed link in ipsec_sa chain.\n"); return; } } } } #if 0 /* * The ipsec_sa table better be locked before it is handed in, * or races might happen. * * this routine assumes the SA has a refcount==0, and we free it. * we also assume that the pointers are already cleaned up. */ static int ipsec_sa_del(struct ipsec_sa *ips) { unsigned int hashval; struct ipsec_sa *ipstp; char sa[SATOT_BUF]; size_t sa_len; if (ips == NULL) { KLIPS_ERROR(debug_xform, "klips_error:ipsec_sa_del: " "null pointer passed in!\n"); return -ENODATA; } if (ips->ips_next) { struct ipsec_sa *in = ips->ips_next; ips->ips_next = NULL; ipsec_sa_put(in); } sa_len = KLIPS_SATOT(debug_xform, &ips->ips_said, 0, sa, sizeof(sa)); hashval = IPS_HASH(&ips->ips_said); KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sa_del: " "deleting SA:%s (ref=%u), hashval=%d.\n", sa_len ? sa : " (error)", ips->ips_ref, hashval); if (ipsec_sadb_hash[hashval] == NULL) { /* if this is NULL, then we can be sure that the SA was never * added to the SADB, so we just free it. */ KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sa_del: " "no entries in ipsec_sa table for hash=%d (ref=%u) of SA:%s.\n", hashval, ips->ips_ref, sa_len ? sa : " (error)"); return -ENOENT; } if (ips == ipsec_sadb_hash[hashval]) { ipsec_sadb_hash[hashval] = ipsec_sadb_hash[hashval]->ips_hnext; ips->ips_hnext = NULL; ipsec_sa_put(ips); KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sa_del: " "successfully deleted first ipsec_sa in chain.\n"); return 0; } else { for (ipstp = ipsec_sadb_hash[hashval]; ipstp; ipstp = ipstp->ips_hnext) { if (ipstp->ips_hnext == ips) { ipstp->ips_hnext = ips->ips_hnext; ips->ips_hnext = NULL; ipsec_sa_put(ips); KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sa_del: " "successfully deleted link in ipsec_sa chain.\n"); return 0; } } } KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sa_del: " "no entries in linked list for hash=%d of SA:%s.\n", hashval, sa_len ? sa : " (error)"); return -ENOENT; } #endif int ipsec_sadb_cleanup(__u8 proto) { unsigned i; int error = 0; struct ipsec_sa *ips; /* struct ipsec_sa *ipsnext, **ipsprev; */ /* char sa[SATOT_BUF]; */ /* size_t sa_len; */ KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sadb_cleanup: " "cleaning up proto=%d.\n", proto); spin_lock_bh(&tdb_lock); for (i = 0; i < SADB_HASHMOD; i++) { ips = ipsec_sadb_hash[i]; while (ips) { ipsec_sadb_hash[i] = ips->ips_hnext; ips->ips_hnext = NULL; ipsec_sa_put(ips, IPSEC_REFSAADD); ips = ipsec_sadb_hash[i]; } } /* errlab: */ spin_unlock_bh(&tdb_lock); #if IPSEC_SA_REF_CODE /* clean up SA reference table */ /* go through the ref table and clean out all the SAs */ KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sadb_cleanup: " "removing SAref entries and tables."); { unsigned table, entry; for (table = 0; table < IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES; table++) { KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sadb_cleanup: " "cleaning SAref table=%u.\n", table); if (ipsec_sadb.refTable[table] == NULL) { printk("\n"); KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sadb_cleanup: " "cleaned %u used refTables.\n", table); break; } for (entry = 0; entry < IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES; entry++) { if (ipsec_sadb.refTable[table]->entry[entry] != NULL) { struct ipsec_sa *sa1 = ipsec_sadb.refTable[table]-> entry[entry]; ipsec_sa_put(sa1, IPSEC_REFOTHER); ipsec_sadb.refTable[table]->entry[entry ] = NULL; } } } } #endif /* IPSEC_SA_REF_CODE */ return error; } int ipsec_sadb_free(void) { int error = 0; KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sadb_free: " "freeing SArefTable memory.\n"); /* clean up SA reference table */ /* go through the ref table and clean out all the SAs if any are left and free table memory */ KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sadb_free: " "removing SAref entries and tables.\n"); { unsigned table, entry; for (table = 0; table < IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES; table++) { KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sadb_free: " "removing SAref table=%u.\n", table); if (ipsec_sadb.refTable[table] == NULL) { KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sadb_free: " "removed %u used refTables.\n", table); break; } for (entry = 0; entry < IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES; entry++) { if (ipsec_sadb.refTable[table]->entry[entry] != NULL) { struct ipsec_sa *sa1 = ipsec_sadb.refTable[table]-> entry[entry]; BUG_ON(atomic_read(&sa1->ips_refcount) == 1); ipsec_sa_put(sa1, IPSEC_REFSAADD); ipsec_sadb.refTable[table]->entry[entry] = NULL; } } kfree(ipsec_sadb.refTable[table]); ipsec_sadb.refTable[table] = NULL; } } return error; } int ipsec_sa_wipe(struct ipsec_sa *ips) { if (ips == NULL) return -ENODATA; #if IPSEC_SA_REF_CODE /* remove me from the SArefTable */ if (debug_xform) { char sa[SATOT_BUF]; size_t sa_len; struct IPsecSArefSubTable *subtable = NULL; if (IPsecSAref2table(IPsecSA2SAref(ips)) < IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES && ipsec_sadb.refTable != NULL) subtable = ipsec_sadb.refTable[ IPsecSAref2table(IPsecSA2SAref(ips))]; sa_len = KLIPS_SATOT(debug_xform, &ips->ips_said, 0, sa, sizeof(sa)); KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sa_wipe: " "removing SA=%s(0p%p), SAref=%d, table=%d(0p%p), entry=%d from the refTable.\n", sa_len ? sa : " (error)", ips, ips->ips_ref, IPsecSAref2table(IPsecSA2SAref(ips)), subtable, subtable ? IPsecSAref2entry(IPsecSA2SAref(ips)) : 0); } if (ips->ips_ref != IPSEC_SAREF_NULL) { struct IPsecSArefSubTable *subtable = NULL; int ref_table = IPsecSAref2table(IPsecSA2SAref(ips)); int ref_entry = IPsecSAref2entry(IPsecSA2SAref(ips)); if (ref_table < IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES) { subtable = ipsec_sadb.refTable[ref_table]; if (subtable != NULL && subtable->entry[ref_entry] == ips) { subtable->entry[ref_entry] = NULL; } } ips->ips_ref = IPSEC_SAREF_NULL; } #endif /* IPSEC_SA_REF_CODE */ /* paranoid clean up */ if (ips->ips_addr_s != NULL) { memset((caddr_t)(ips->ips_addr_s), 0, ips->ips_addr_s_size); kfree(ips->ips_addr_s); } ips->ips_addr_s = NULL; if (ips->ips_addr_d != NULL) { memset((caddr_t)(ips->ips_addr_d), 0, ips->ips_addr_d_size); kfree(ips->ips_addr_d); } ips->ips_addr_d = NULL; if (ips->ips_addr_p != NULL) { memset((caddr_t)(ips->ips_addr_p), 0, ips->ips_addr_p_size); kfree(ips->ips_addr_p); } ips->ips_addr_p = NULL; if (ips->ips_natt_oa) { memset((caddr_t)(ips->ips_natt_oa), 0, ips->ips_natt_oa_size); kfree(ips->ips_natt_oa); } ips->ips_natt_oa = NULL; if (ips->ips_key_a != NULL) { memset((caddr_t)(ips->ips_key_a), 0, ips->ips_key_a_size); kfree(ips->ips_key_a); } ips->ips_key_a = NULL; if (ips->ips_key_e != NULL) { #ifdef CONFIG_KLIPS_ALG if (ips->ips_alg_enc && ips->ips_alg_enc->ixt_e_destroy_key) { ips->ips_alg_enc->ixt_e_destroy_key(ips->ips_alg_enc, ips->ips_key_e); } else #endif { memset((caddr_t)(ips->ips_key_e), 0, ips->ips_key_e_size); kfree(ips->ips_key_e); } } ips->ips_key_e = NULL; if (ips->ips_iv != NULL) { memset((caddr_t)(ips->ips_iv), 0, ips->ips_iv_size); kfree(ips->ips_iv); } ips->ips_iv = NULL; #ifdef CONFIG_KLIPS_OCF if (ips->ocf_in_use) ipsec_ocf_sa_free(ips); #endif if (ips->ips_ident_s.data != NULL) { memset((caddr_t)(ips->ips_ident_s.data), 0, ips->ips_ident_s.len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident)); kfree(ips->ips_ident_s.data); } ips->ips_ident_s.data = NULL; if (ips->ips_ident_d.data != NULL) { memset((caddr_t)(ips->ips_ident_d.data), 0, ips->ips_ident_d.len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident)); kfree(ips->ips_ident_d.data); } ips->ips_ident_d.data = NULL; #ifdef CONFIG_KLIPS_ALG if (ips->ips_alg_enc || ips->ips_alg_auth) ipsec_alg_sa_wipe(ips); ips->ips_alg_enc = NULL; ips->ips_alg_auth = NULL; #endif if (ips->ips_prev) ips->ips_prev->ips_next = ips->ips_next; if (ips->ips_next) { ips->ips_next->ips_prev = ips->ips_prev; ipsec_sa_put(ips->ips_next, IPSEC_REFALLOC); } ips->ips_next = NULL; ips->ips_prev = NULL; if (ips->ips_hnext) ipsec_sa_put(ips->ips_hnext, IPSEC_REFALLOC); ips->ips_hnext = NULL; BUG_ON(atomic_read(&ips->ips_refcount) != 0); #ifdef IPSEC_SA_RECOUNT_DEBUG if (ips == ipsec_sa_raw) { ipsec_sa_raw = ips->ips_raw; } else { struct ipsec_sa *raw = ipsec_sa_raw; while (raw) { if (raw->ips_raw == ips) { raw->ips_raw = ips->ips_raw; break; } raw = raw->ips_raw; } } #endif if (ips->ips_out != NULL) { ipsec_dev_put(ips->ips_out); ips->ips_out = NULL; } memset((caddr_t)ips, 0, sizeof(*ips)); kfree(ips); ips = NULL; return 0; } extern int sysctl_ipsec_debug_verbose; int ipsec_sa_init(struct ipsec_sa *ipsp) { int error = 0; char sa[SATOT_BUF]; size_t sa_len; #ifdef CONFIG_KLIPS_DEBUG char ipaddr_txt[ADDRTOA_BUF]; char ipaddr2_txt[ADDRTOA_BUF]; #endif #if defined (CONFIG_KLIPS_AUTH_HMAC_MD5) || \ defined (CONFIG_KLIPS_AUTH_HMAC_SHA1) unsigned char kb[AHMD596_BLKLEN]; int i; #endif if (ipsp == NULL) { KLIPS_PRINT(debug_pfkey, "ipsec_sa_init: " "ipsp is NULL, fatal\n"); SENDERR(EINVAL); } sa_len = KLIPS_SATOT(debug_pfkey, &ipsp->ips_said, 0, sa, sizeof(sa)); KLIPS_PRINT(debug_pfkey, "ipsec_sa_init: " "(pfkey defined) called for SA:%s\n", sa_len ? sa : " (error)"); KLIPS_PRINT(debug_pfkey, "ipsec_sa_init: " "calling init routine of %s%s%s\n", IPS_XFORM_NAME(ipsp)); switch (ipsp->ips_said.proto) { #ifdef CONFIG_KLIPS_IPIP case IPPROTO_IPIP: { ipsp->ips_xformfuncs = ipip_xform_funcs; #ifdef CONFIG_KLIPS_DEBUG sin_addrtot(ipsp->ips_addr_s, 0, ipaddr_txt, sizeof(ipaddr_txt)); sin_addrtot(ipsp->ips_addr_d, 0, ipaddr2_txt, sizeof(ipaddr2_txt)); KLIPS_PRINT(debug_pfkey, "ipsec_sa_init: " "(pfkey defined) IPIP ipsec_sa set for %s->%s.\n", ipaddr_txt, ipaddr2_txt); #endif } break; #endif /* !CONFIG_KLIPS_IPIP */ #ifdef CONFIG_KLIPS_AH case IPPROTO_AH: ipsp->ips_xformfuncs = ah_xform_funcs; #ifdef CONFIG_KLIPS_OCF if (ipsec_ocf_sa_init(ipsp, ipsp->ips_authalg, 0)) break; #endif switch (ipsp->ips_authalg) { # ifdef CONFIG_KLIPS_AUTH_HMAC_MD5 case AH_MD5: { unsigned char *akp; unsigned int aks; MD5_CTX *ictx; MD5_CTX *octx; if (ipsp->ips_key_bits_a != (AHMD596_KLEN * 8)) { KLIPS_PRINT(debug_pfkey, "ipsec_sa_init: " "incorrect key size: %d bits -- must be %d bits\n" /*octets (bytes)\n"*/, ipsp->ips_key_bits_a, AHMD596_KLEN * 8); SENDERR(EINVAL); } # if KLIPS_DIVULGE_HMAC_KEY KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, "ipsec_sa_init: " "hmac md5-96 key is 0x%08x %08x %08x %08x\n", ntohl(*(((__u32 *)ipsp->ips_key_a) + 0)), ntohl(*(((__u32 *)ipsp->ips_key_a) + 1)), ntohl(*(((__u32 *)ipsp->ips_key_a) + 2)), ntohl(*(((__u32 *)ipsp->ips_key_a) + 3))); # endif /* KLIPS_DIVULGE_HMAC_KEY */ ipsp->ips_auth_bits = AHMD596_ALEN * 8; /* save the pointer to the key material */ akp = ipsp->ips_key_a; aks = ipsp->ips_key_a_size; KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, "ipsec_sa_init: " "allocating %lu bytes for md5_ctx.\n", (unsigned long) sizeof(struct md5_ctx)); if ((ipsp->ips_key_a = (caddr_t) kmalloc(sizeof(struct md5_ctx), GFP_ATOMIC)) == NULL) { ipsp->ips_key_a = akp; SENDERR(ENOMEM); } ipsp->ips_key_a_size = sizeof(struct md5_ctx); for (i = 0; i < DIVUP(ipsp->ips_key_bits_a, 8); i++) kb[i] = akp[i] ^ HMAC_IPAD; for (; i < AHMD596_BLKLEN; i++) kb[i] = HMAC_IPAD; ictx = &(((struct md5_ctx*)(ipsp->ips_key_a))->ictx); osMD5Init(ictx); osMD5Update(ictx, kb, AHMD596_BLKLEN); for (i = 0; i < AHMD596_BLKLEN; i++) kb[i] ^= (HMAC_IPAD ^ HMAC_OPAD); octx = &(((struct md5_ctx*)(ipsp->ips_key_a))->octx); osMD5Init(octx); osMD5Update(octx, kb, AHMD596_BLKLEN); # if KLIPS_DIVULGE_HMAC_KEY KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, "ipsec_sa_init: " "MD5 ictx=0x%08x %08x %08x %08x octx=0x%08x %08x %08x %08x\n", ((__u32*)ictx)[0], ((__u32*)ictx)[1], ((__u32*)ictx)[2], ((__u32*)ictx)[3], ((__u32*)octx)[0], ((__u32*)octx)[1], ((__u32*)octx)[2], ((__u32*)octx)[3] ); # endif /* KLIPS_DIVULGE_HMAC_KEY */ /* zero key buffer -- paranoid */ memset(akp, 0, aks); kfree(akp); } break; # endif /* CONFIG_KLIPS_AUTH_HMAC_MD5 */ # ifdef CONFIG_KLIPS_AUTH_HMAC_SHA1 case AH_SHA: { unsigned char *akp; unsigned int aks; SHA1_CTX *ictx; SHA1_CTX *octx; if (ipsp->ips_key_bits_a != (AHSHA196_KLEN * 8)) { KLIPS_PRINT(debug_pfkey, "ipsec_sa_init: " "incorrect key size: %d bits -- must be %d bits\n" /*octets (bytes)\n"*/, ipsp->ips_key_bits_a, AHSHA196_KLEN * 8); SENDERR(EINVAL); } # if KLIPS_DIVULGE_HMAC_KEY KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, "ipsec_sa_init: " "hmac sha1-96 key is 0x%08x %08x %08x %08x\n", ntohl(*(((__u32 *)ipsp->ips_key_a) + 0)), ntohl(*(((__u32 *)ipsp->ips_key_a) + 1)), ntohl(*(((__u32 *)ipsp->ips_key_a) + 2)), ntohl(*(((__u32 *)ipsp->ips_key_a) + 3))); # endif /* KLIPS_DIVULGE_HMAC_KEY */ ipsp->ips_auth_bits = AHSHA196_ALEN * 8; /* save the pointer to the key material */ akp = ipsp->ips_key_a; aks = ipsp->ips_key_a_size; KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, "ipsec_sa_init: " "allocating %lu bytes for sha1_ctx.\n", (unsigned long) sizeof(struct sha1_ctx)); if ((ipsp->ips_key_a = (caddr_t) kmalloc(sizeof(struct sha1_ctx), GFP_ATOMIC)) == NULL) { ipsp->ips_key_a = akp; SENDERR(ENOMEM); } ipsp->ips_key_a_size = sizeof(struct sha1_ctx); for (i = 0; i < DIVUP(ipsp->ips_key_bits_a, 8); i++) kb[i] = akp[i] ^ HMAC_IPAD; for (; i < AHMD596_BLKLEN; i++) kb[i] = HMAC_IPAD; ictx = &(((struct sha1_ctx*)(ipsp->ips_key_a))->ictx); SHA1Init(ictx); SHA1Update(ictx, kb, AHSHA196_BLKLEN); for (i = 0; i < AHSHA196_BLKLEN; i++) kb[i] ^= (HMAC_IPAD ^ HMAC_OPAD); octx = &(((struct sha1_ctx*)(ipsp->ips_key_a))->octx); SHA1Init(octx); SHA1Update(octx, kb, AHSHA196_BLKLEN); # if KLIPS_DIVULGE_HMAC_KEY KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, "ipsec_sa_init: " "SHA1 ictx=0x%08x %08x %08x %08x octx=0x%08x %08x %08x %08x\n", ((__u32*)ictx)[0], ((__u32*)ictx)[1], ((__u32*)ictx)[2], ((__u32*)ictx)[3], ((__u32*)octx)[0], ((__u32*)octx)[1], ((__u32*)octx)[2], ((__u32*)octx)[3] ); # endif /* KLIPS_DIVULGE_HMAC_KEY */ /* zero key buffer -- paranoid */ memset(akp, 0, aks); kfree(akp); } break; # endif /* CONFIG_KLIPS_AUTH_HMAC_SHA1 */ default: KLIPS_PRINT(debug_pfkey, "ipsec_sa_init: " "authalg=%d support not available in the kernel", ipsp->ips_authalg); SENDERR(EINVAL); } break; #endif /* CONFIG_KLIPS_AH */ #ifdef CONFIG_KLIPS_ESP case IPPROTO_ESP: ipsp->ips_xformfuncs = esp_xform_funcs; { #ifdef CONFIG_KLIPS_OCF if (ipsec_ocf_sa_init(ipsp, ipsp->ips_authalg, ipsp->ips_encalg)) break; #endif #ifdef CONFIG_KLIPS_ALG error = ipsec_alg_enc_key_create(ipsp); if (error < 0) SENDERR(-error); error = ipsec_alg_auth_key_create(ipsp); if ((error < 0) && (error != -EPROTO)) SENDERR(-error); if (error == -EPROTO) { /* perform manual key generation, ignore this particular error */ error = 0; #endif /* CONFIG_KLIPS_ALG */ switch (ipsp->ips_authalg) { #if defined (CONFIG_KLIPS_AUTH_HMAC_MD5) || \ defined (CONFIG_KLIPS_AUTH_HMAC_SHA1) unsigned char *akp; unsigned int aks; #endif # ifdef CONFIG_KLIPS_AUTH_HMAC_MD5 case AH_MD5: { MD5_CTX *ictx; MD5_CTX *octx; if (ipsp->ips_key_bits_a != (AHMD596_KLEN * 8)) { KLIPS_PRINT(debug_pfkey, "ipsec_sa_init: " "incorrect authorisation key size: %d bits -- must be %d bits\n" /*octets (bytes)\n"*/, ipsp->ips_key_bits_a, AHMD596_KLEN * 8); SENDERR(EINVAL); } # if KLIPS_DIVULGE_HMAC_KEY KLIPS_PRINT( debug_pfkey && sysctl_ipsec_debug_verbose, "ipsec_sa_init: " "hmac md5-96 key is 0x%08x %08x %08x %08x\n", ntohl(*(((__u32 *)(ipsp->ips_key_a)) + 0)), ntohl(*(((__u32 *)(ipsp->ips_key_a)) + 1)), ntohl(*(((__u32 *)(ipsp->ips_key_a)) + 2)), ntohl(*(((__u32 *)(ipsp->ips_key_a)) + 3))); # endif /* KLIPS_DIVULGE_HMAC_KEY */ ipsp->ips_auth_bits = AHMD596_ALEN * 8; /* save the pointer to the key material */ akp = ipsp->ips_key_a; aks = ipsp->ips_key_a_size; KLIPS_PRINT( debug_pfkey && sysctl_ipsec_debug_verbose, "ipsec_sa_init: " "allocating %lu bytes for md5_ctx.\n", (unsigned long) sizeof(struct md5_ctx)); if ((ipsp->ips_key_a = (caddr_t) kmalloc(sizeof(struct md5_ctx), GFP_ATOMIC)) == NULL) { ipsp->ips_key_a = akp; SENDERR(ENOMEM); } ipsp->ips_key_a_size = sizeof(struct md5_ctx); for (i = 0; i < DIVUP(ipsp->ips_key_bits_a, 8); i++) kb[i] = akp[i] ^ HMAC_IPAD; for (; i < AHMD596_BLKLEN; i++) kb[i] = HMAC_IPAD; ictx = &(((struct md5_ctx*)(ipsp->ips_key_a)) ->ictx); osMD5Init(ictx); osMD5Update(ictx, kb, AHMD596_BLKLEN); for (i = 0; i < AHMD596_BLKLEN; i++) kb[i] ^= (HMAC_IPAD ^ HMAC_OPAD); octx = &(((struct md5_ctx*)(ipsp->ips_key_a)) ->octx); osMD5Init(octx); osMD5Update(octx, kb, AHMD596_BLKLEN); # if KLIPS_DIVULGE_HMAC_KEY KLIPS_PRINT( debug_pfkey && sysctl_ipsec_debug_verbose, "ipsec_sa_init: " "MD5 ictx=0x%08x %08x %08x %08x octx=0x%08x %08x %08x %08x\n", ((__u32*)ictx)[0], ((__u32*)ictx)[1], ((__u32*)ictx)[2], ((__u32*)ictx)[3], ((__u32*)octx)[0], ((__u32*)octx)[1], ((__u32*)octx)[2], ((__u32*)octx)[3] ); # endif /* KLIPS_DIVULGE_HMAC_KEY */ /* paranoid */ memset(akp, 0, aks); kfree(akp); break; } # endif /* CONFIG_KLIPS_AUTH_HMAC_MD5 */ # ifdef CONFIG_KLIPS_AUTH_HMAC_SHA1 case AH_SHA: { SHA1_CTX *ictx; SHA1_CTX *octx; if (ipsp->ips_key_bits_a != (AHSHA196_KLEN * 8)) { KLIPS_PRINT(debug_pfkey, "ipsec_sa_init: " "incorrect authorisation key size: %d bits -- must be %d bits\n" /*octets (bytes)\n"*/, ipsp->ips_key_bits_a, AHSHA196_KLEN * 8); SENDERR(EINVAL); } # if KLIPS_DIVULGE_HMAC_KEY KLIPS_PRINT( debug_pfkey && sysctl_ipsec_debug_verbose, "ipsec_sa_init: " "hmac sha1-96 key is 0x%08x %08x %08x %08x\n", ntohl(*(((__u32 *)ipsp->ips_key_a) + 0)), ntohl(*(((__u32 *)ipsp->ips_key_a) + 1)), ntohl(*(((__u32 *)ipsp->ips_key_a) + 2)), ntohl(*(((__u32 *)ipsp->ips_key_a) + 3))); # endif /* KLIPS_DIVULGE_HMAC_KEY */ ipsp->ips_auth_bits = AHSHA196_ALEN * 8; /* save the pointer to the key material */ akp = ipsp->ips_key_a; aks = ipsp->ips_key_a_size; KLIPS_PRINT( debug_pfkey && sysctl_ipsec_debug_verbose, "ipsec_sa_init: " "allocating %lu bytes for sha1_ctx.\n", (unsigned long) sizeof(struct sha1_ctx)); if ((ipsp->ips_key_a = (caddr_t) kmalloc(sizeof(struct sha1_ctx), GFP_ATOMIC)) == NULL) { ipsp->ips_key_a = akp; SENDERR(ENOMEM); } ipsp->ips_key_a_size = sizeof(struct sha1_ctx); for (i = 0; i < DIVUP(ipsp->ips_key_bits_a, 8); i++) kb[i] = akp[i] ^ HMAC_IPAD; for (; i < AHMD596_BLKLEN; i++) kb[i] = HMAC_IPAD; ictx = &(((struct sha1_ctx*)(ipsp->ips_key_a)) ->ictx); SHA1Init(ictx); SHA1Update(ictx, kb, AHSHA196_BLKLEN); for (i = 0; i < AHSHA196_BLKLEN; i++) kb[i] ^= (HMAC_IPAD ^ HMAC_OPAD); octx = &((struct sha1_ctx*)(ipsp->ips_key_a)) ->octx; SHA1Init(octx); SHA1Update(octx, kb, AHSHA196_BLKLEN); # if KLIPS_DIVULGE_HMAC_KEY KLIPS_PRINT( debug_pfkey && sysctl_ipsec_debug_verbose, "ipsec_sa_init: " "SHA1 ictx=0x%08x %08x %08x %08x octx=0x%08x %08x %08x %08x\n", ((__u32*)ictx)[0], ((__u32*)ictx)[1], ((__u32*)ictx)[2], ((__u32*)ictx)[3], ((__u32*)octx)[0], ((__u32*)octx)[1], ((__u32*)octx)[2], ((__u32*)octx)[3] ); # endif /* KLIPS_DIVULGE_HMAC_KEY */ memset(akp, 0, aks); kfree(akp); break; } # endif /* CONFIG_KLIPS_AUTH_HMAC_SHA1 */ case AH_NONE: break; default: KLIPS_PRINT(debug_pfkey, "ipsec_sa_init: " "authalg=%d support not available in the kernel.\n", ipsp->ips_authalg); SENDERR(EINVAL); } #ifdef CONFIG_KLIPS_ALG /* closure of the -EPROTO condition above */ } #endif ipsp->ips_iv_size = ipsp->ips_alg_enc->ixt_common.ixt_support. ias_ivlen / 8; /* Create IV */ if (ipsp->ips_iv_size) { if ((ipsp->ips_iv = (caddr_t) kmalloc(ipsp->ips_iv_size, GFP_ATOMIC)) == NULL) SENDERR(ENOMEM); prng_bytes(&ipsec_prng, (char *)ipsp->ips_iv, ipsp->ips_iv_size); ipsp->ips_iv_bits = ipsp->ips_iv_size * 8; } } break; #endif /* !CONFIG_KLIPS_ESP */ #ifdef CONFIG_KLIPS_IPCOMP case IPPROTO_COMP: ipsp->ips_xformfuncs = ipcomp_xform_funcs; ipsp->ips_comp_adapt_tries = 0; ipsp->ips_comp_adapt_skip = 0; ipsp->ips_comp_ratio_cbytes = 0; ipsp->ips_comp_ratio_dbytes = 0; #ifdef CONFIG_KLIPS_OCF if (ipsec_ocf_comp_sa_init(ipsp, ipsp->ips_encalg)) break; #endif ipsp->ips_comp_adapt_tries = 0; ipsp->ips_comp_adapt_skip = 0; ipsp->ips_comp_ratio_cbytes = 0; ipsp->ips_comp_ratio_dbytes = 0; break; #endif /* CONFIG_KLIPS_IPCOMP */ default: printk(KERN_ERR "KLIPS sa initialization: " "proto=%d unknown.\n", ipsp->ips_said.proto); SENDERR(EINVAL); } errlab: return error; }