key.c revision 194062
1105197Ssam/* $FreeBSD: head/sys/netipsec/key.c 194062 2009-06-12 15:44:35Z vanhu $ */ 2105197Ssam/* $KAME: key.c,v 1.191 2001/06/27 10:46:49 sakane Exp $ */ 3105197Ssam 4139823Simp/*- 5105197Ssam * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 6105197Ssam * All rights reserved. 7105197Ssam * 8105197Ssam * Redistribution and use in source and binary forms, with or without 9105197Ssam * modification, are permitted provided that the following conditions 10105197Ssam * are met: 11105197Ssam * 1. Redistributions of source code must retain the above copyright 12105197Ssam * notice, this list of conditions and the following disclaimer. 13105197Ssam * 2. Redistributions in binary form must reproduce the above copyright 14105197Ssam * notice, this list of conditions and the following disclaimer in the 15105197Ssam * documentation and/or other materials provided with the distribution. 16105197Ssam * 3. Neither the name of the project nor the names of its contributors 17105197Ssam * may be used to endorse or promote products derived from this software 18105197Ssam * without specific prior written permission. 19105197Ssam * 20105197Ssam * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21105197Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22105197Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23105197Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24105197Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25105197Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26105197Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27105197Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28105197Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29105197Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30105197Ssam * SUCH DAMAGE. 31105197Ssam */ 32105197Ssam 33105197Ssam/* 34105197Ssam * This code is referd to RFC 2367 35105197Ssam */ 36105197Ssam 37105197Ssam#include "opt_inet.h" 38105197Ssam#include "opt_inet6.h" 39105197Ssam#include "opt_ipsec.h" 40105197Ssam 41105197Ssam#include <sys/types.h> 42105197Ssam#include <sys/param.h> 43105197Ssam#include <sys/systm.h> 44105197Ssam#include <sys/kernel.h> 45119643Ssam#include <sys/lock.h> 46119643Ssam#include <sys/mutex.h> 47105197Ssam#include <sys/mbuf.h> 48105197Ssam#include <sys/domain.h> 49105197Ssam#include <sys/protosw.h> 50105197Ssam#include <sys/malloc.h> 51105197Ssam#include <sys/socket.h> 52105197Ssam#include <sys/socketvar.h> 53105197Ssam#include <sys/sysctl.h> 54105197Ssam#include <sys/errno.h> 55105197Ssam#include <sys/proc.h> 56105197Ssam#include <sys/queue.h> 57158767Spjd#include <sys/refcount.h> 58105197Ssam#include <sys/syslog.h> 59183550Szec#include <sys/vimage.h> 60105197Ssam 61105197Ssam#include <net/if.h> 62105197Ssam#include <net/route.h> 63105197Ssam#include <net/raw_cb.h> 64105197Ssam 65105197Ssam#include <netinet/in.h> 66105197Ssam#include <netinet/in_systm.h> 67105197Ssam#include <netinet/ip.h> 68105197Ssam#include <netinet/in_var.h> 69105197Ssam 70105197Ssam#ifdef INET6 71105197Ssam#include <netinet/ip6.h> 72105197Ssam#include <netinet6/in6_var.h> 73105197Ssam#include <netinet6/ip6_var.h> 74105197Ssam#endif /* INET6 */ 75105197Ssam 76105197Ssam#ifdef INET 77105197Ssam#include <netinet/in_pcb.h> 78185571Sbz#include <netinet/vinet.h> 79105197Ssam#endif 80105197Ssam#ifdef INET6 81105197Ssam#include <netinet6/in6_pcb.h> 82185571Sbz#include <netinet6/vinet6.h> 83105197Ssam#endif /* INET6 */ 84105197Ssam 85105197Ssam#include <net/pfkeyv2.h> 86105197Ssam#include <netipsec/keydb.h> 87105197Ssam#include <netipsec/key.h> 88105197Ssam#include <netipsec/keysock.h> 89105197Ssam#include <netipsec/key_debug.h> 90105197Ssam 91105197Ssam#include <netipsec/ipsec.h> 92105197Ssam#ifdef INET6 93105197Ssam#include <netipsec/ipsec6.h> 94105197Ssam#endif 95105197Ssam 96105197Ssam#include <netipsec/xform.h> 97105197Ssam 98105197Ssam#include <machine/stdarg.h> 99105197Ssam 100105197Ssam/* randomness */ 101105197Ssam#include <sys/random.h> 102181803Sbz#include <sys/vimage.h> 103105197Ssam 104105197Ssam#define FULLMASK 0xff 105105197Ssam#define _BITS(bytes) ((bytes) << 3) 106105197Ssam 107105197Ssam/* 108105197Ssam * Note on SA reference counting: 109105197Ssam * - SAs that are not in DEAD state will have (total external reference + 1) 110105197Ssam * following value in reference count field. they cannot be freed and are 111105197Ssam * referenced from SA header. 112105197Ssam * - SAs that are in DEAD state will have (total external reference) 113105197Ssam * in reference count field. they are ready to be freed. reference from 114105197Ssam * SA header will be removed in key_delsav(), when the reference count 115105197Ssam * field hits 0 (= no external reference other than from SA header. 116105197Ssam */ 117105197Ssam 118185088Szec#ifdef VIMAGE_GLOBALS 119185088Szecu_int32_t key_debug_level; 120185088Szecstatic u_int key_spi_trycnt; 121185088Szecstatic u_int32_t key_spi_minval; 122185088Szecstatic u_int32_t key_spi_maxval; 123185088Szecstatic u_int32_t policy_id; 124185088Szecstatic u_int key_int_random; 125185088Szecstatic u_int key_larval_lifetime; 126185088Szecstatic int key_blockacq_count; 127185088Szecstatic int key_blockacq_lifetime; 128185088Szecstatic int key_preferred_oldsa; 129105197Ssam 130185088Szecstatic u_int32_t acq_seq; 131105197Ssam 132185088Szecstatic int ipsec_esp_keymin; 133185088Szecstatic int ipsec_esp_auth; 134185088Szecstatic int ipsec_ah_keymin; 135185088Szec 136105197Ssamstatic LIST_HEAD(_sptree, secpolicy) sptree[IPSEC_DIR_MAX]; /* SPD */ 137185088Szecstatic LIST_HEAD(_sahtree, secashead) sahtree; /* SAD */ 138185088Szecstatic LIST_HEAD(_regtree, secreg) regtree[SADB_SATYPE_MAX + 1]; 139185088Szecstatic LIST_HEAD(_acqtree, secacq) acqtree; /* acquiring list */ 140185088Szecstatic LIST_HEAD(_spacqtree, secspacq) spacqtree; /* SP acquiring list */ 141185088Szec#endif /* VIMAGE_GLOBALS */ 142185088Szec 143119643Ssamstatic struct mtx sptree_lock; 144120585Ssam#define SPTREE_LOCK_INIT() \ 145120585Ssam mtx_init(&sptree_lock, "sptree", \ 146120585Ssam "fast ipsec security policy database", MTX_DEF) 147120585Ssam#define SPTREE_LOCK_DESTROY() mtx_destroy(&sptree_lock) 148120585Ssam#define SPTREE_LOCK() mtx_lock(&sptree_lock) 149120585Ssam#define SPTREE_UNLOCK() mtx_unlock(&sptree_lock) 150120585Ssam#define SPTREE_LOCK_ASSERT() mtx_assert(&sptree_lock, MA_OWNED) 151120585Ssam 152119643Ssamstatic struct mtx sahtree_lock; 153120585Ssam#define SAHTREE_LOCK_INIT() \ 154120585Ssam mtx_init(&sahtree_lock, "sahtree", \ 155120585Ssam "fast ipsec security association database", MTX_DEF) 156120585Ssam#define SAHTREE_LOCK_DESTROY() mtx_destroy(&sahtree_lock) 157120585Ssam#define SAHTREE_LOCK() mtx_lock(&sahtree_lock) 158120585Ssam#define SAHTREE_UNLOCK() mtx_unlock(&sahtree_lock) 159120585Ssam#define SAHTREE_LOCK_ASSERT() mtx_assert(&sahtree_lock, MA_OWNED) 160120585Ssam 161119643Ssam /* registed list */ 162119643Ssamstatic struct mtx regtree_lock; 163120585Ssam#define REGTREE_LOCK_INIT() \ 164120585Ssam mtx_init(®tree_lock, "regtree", "fast ipsec regtree", MTX_DEF) 165120585Ssam#define REGTREE_LOCK_DESTROY() mtx_destroy(®tree_lock) 166120585Ssam#define REGTREE_LOCK() mtx_lock(®tree_lock) 167120585Ssam#define REGTREE_UNLOCK() mtx_unlock(®tree_lock) 168120585Ssam#define REGTREE_LOCK_ASSERT() mtx_assert(®tree_lock, MA_OWNED) 169120585Ssam 170119643Ssamstatic struct mtx acq_lock; 171120585Ssam#define ACQ_LOCK_INIT() \ 172120585Ssam mtx_init(&acq_lock, "acqtree", "fast ipsec acquire list", MTX_DEF) 173120585Ssam#define ACQ_LOCK_DESTROY() mtx_destroy(&acq_lock) 174120585Ssam#define ACQ_LOCK() mtx_lock(&acq_lock) 175120585Ssam#define ACQ_UNLOCK() mtx_unlock(&acq_lock) 176120585Ssam#define ACQ_LOCK_ASSERT() mtx_assert(&acq_lock, MA_OWNED) 177120585Ssam 178119643Ssamstatic struct mtx spacq_lock; 179120585Ssam#define SPACQ_LOCK_INIT() \ 180120585Ssam mtx_init(&spacq_lock, "spacqtree", \ 181120585Ssam "fast ipsec security policy acquire list", MTX_DEF) 182120585Ssam#define SPACQ_LOCK_DESTROY() mtx_destroy(&spacq_lock) 183120585Ssam#define SPACQ_LOCK() mtx_lock(&spacq_lock) 184120585Ssam#define SPACQ_UNLOCK() mtx_unlock(&spacq_lock) 185120585Ssam#define SPACQ_LOCK_ASSERT() mtx_assert(&spacq_lock, MA_OWNED) 186105197Ssam 187105197Ssam/* search order for SAs */ 188128856Ssamstatic const u_int saorder_state_valid_prefer_old[] = { 189105197Ssam SADB_SASTATE_DYING, SADB_SASTATE_MATURE, 190105197Ssam}; 191128856Ssamstatic const u_int saorder_state_valid_prefer_new[] = { 192128856Ssam SADB_SASTATE_MATURE, SADB_SASTATE_DYING, 193128856Ssam}; 194185348Szecstatic const u_int saorder_state_alive[] = { 195105197Ssam /* except DEAD */ 196105197Ssam SADB_SASTATE_MATURE, SADB_SASTATE_DYING, SADB_SASTATE_LARVAL 197105197Ssam}; 198185348Szecstatic const u_int saorder_state_any[] = { 199105197Ssam SADB_SASTATE_MATURE, SADB_SASTATE_DYING, 200105197Ssam SADB_SASTATE_LARVAL, SADB_SASTATE_DEAD 201105197Ssam}; 202105197Ssam 203105197Ssamstatic const int minsize[] = { 204105197Ssam sizeof(struct sadb_msg), /* SADB_EXT_RESERVED */ 205105197Ssam sizeof(struct sadb_sa), /* SADB_EXT_SA */ 206105197Ssam sizeof(struct sadb_lifetime), /* SADB_EXT_LIFETIME_CURRENT */ 207105197Ssam sizeof(struct sadb_lifetime), /* SADB_EXT_LIFETIME_HARD */ 208105197Ssam sizeof(struct sadb_lifetime), /* SADB_EXT_LIFETIME_SOFT */ 209105197Ssam sizeof(struct sadb_address), /* SADB_EXT_ADDRESS_SRC */ 210105197Ssam sizeof(struct sadb_address), /* SADB_EXT_ADDRESS_DST */ 211105197Ssam sizeof(struct sadb_address), /* SADB_EXT_ADDRESS_PROXY */ 212105197Ssam sizeof(struct sadb_key), /* SADB_EXT_KEY_AUTH */ 213105197Ssam sizeof(struct sadb_key), /* SADB_EXT_KEY_ENCRYPT */ 214105197Ssam sizeof(struct sadb_ident), /* SADB_EXT_IDENTITY_SRC */ 215105197Ssam sizeof(struct sadb_ident), /* SADB_EXT_IDENTITY_DST */ 216105197Ssam sizeof(struct sadb_sens), /* SADB_EXT_SENSITIVITY */ 217105197Ssam sizeof(struct sadb_prop), /* SADB_EXT_PROPOSAL */ 218105197Ssam sizeof(struct sadb_supported), /* SADB_EXT_SUPPORTED_AUTH */ 219105197Ssam sizeof(struct sadb_supported), /* SADB_EXT_SUPPORTED_ENCRYPT */ 220105197Ssam sizeof(struct sadb_spirange), /* SADB_EXT_SPIRANGE */ 221105197Ssam 0, /* SADB_X_EXT_KMPRIVATE */ 222105197Ssam sizeof(struct sadb_x_policy), /* SADB_X_EXT_POLICY */ 223105197Ssam sizeof(struct sadb_x_sa2), /* SADB_X_SA2 */ 224194062Svanhu sizeof(struct sadb_x_nat_t_type),/* SADB_X_EXT_NAT_T_TYPE */ 225194062Svanhu sizeof(struct sadb_x_nat_t_port),/* SADB_X_EXT_NAT_T_SPORT */ 226194062Svanhu sizeof(struct sadb_x_nat_t_port),/* SADB_X_EXT_NAT_T_DPORT */ 227194062Svanhu sizeof(struct sadb_address), /* SADB_X_EXT_NAT_T_OAI */ 228194062Svanhu sizeof(struct sadb_address), /* SADB_X_EXT_NAT_T_OAR */ 229194062Svanhu sizeof(struct sadb_x_nat_t_frag),/* SADB_X_EXT_NAT_T_FRAG */ 230105197Ssam}; 231105197Ssamstatic const int maxsize[] = { 232105197Ssam sizeof(struct sadb_msg), /* SADB_EXT_RESERVED */ 233105197Ssam sizeof(struct sadb_sa), /* SADB_EXT_SA */ 234105197Ssam sizeof(struct sadb_lifetime), /* SADB_EXT_LIFETIME_CURRENT */ 235105197Ssam sizeof(struct sadb_lifetime), /* SADB_EXT_LIFETIME_HARD */ 236105197Ssam sizeof(struct sadb_lifetime), /* SADB_EXT_LIFETIME_SOFT */ 237105197Ssam 0, /* SADB_EXT_ADDRESS_SRC */ 238105197Ssam 0, /* SADB_EXT_ADDRESS_DST */ 239105197Ssam 0, /* SADB_EXT_ADDRESS_PROXY */ 240105197Ssam 0, /* SADB_EXT_KEY_AUTH */ 241105197Ssam 0, /* SADB_EXT_KEY_ENCRYPT */ 242105197Ssam 0, /* SADB_EXT_IDENTITY_SRC */ 243105197Ssam 0, /* SADB_EXT_IDENTITY_DST */ 244105197Ssam 0, /* SADB_EXT_SENSITIVITY */ 245105197Ssam 0, /* SADB_EXT_PROPOSAL */ 246105197Ssam 0, /* SADB_EXT_SUPPORTED_AUTH */ 247105197Ssam 0, /* SADB_EXT_SUPPORTED_ENCRYPT */ 248105197Ssam sizeof(struct sadb_spirange), /* SADB_EXT_SPIRANGE */ 249105197Ssam 0, /* SADB_X_EXT_KMPRIVATE */ 250105197Ssam 0, /* SADB_X_EXT_POLICY */ 251105197Ssam sizeof(struct sadb_x_sa2), /* SADB_X_SA2 */ 252194062Svanhu sizeof(struct sadb_x_nat_t_type),/* SADB_X_EXT_NAT_T_TYPE */ 253194062Svanhu sizeof(struct sadb_x_nat_t_port),/* SADB_X_EXT_NAT_T_SPORT */ 254194062Svanhu sizeof(struct sadb_x_nat_t_port),/* SADB_X_EXT_NAT_T_DPORT */ 255194062Svanhu 0, /* SADB_X_EXT_NAT_T_OAI */ 256194062Svanhu 0, /* SADB_X_EXT_NAT_T_OAR */ 257194062Svanhu sizeof(struct sadb_x_nat_t_frag),/* SADB_X_EXT_NAT_T_FRAG */ 258105197Ssam}; 259105197Ssam 260105197Ssam#ifdef SYSCTL_DECL 261105197SsamSYSCTL_DECL(_net_key); 262105197Ssam#endif 263105197Ssam 264183550SzecSYSCTL_V_INT(V_NET, vnet_ipsec,_net_key, KEYCTL_DEBUG_LEVEL, debug, 265183550Szec CTLFLAG_RW, key_debug_level, 0, ""); 266105197Ssam 267105197Ssam/* max count of trial for the decision of spi value */ 268183550SzecSYSCTL_V_INT(V_NET, vnet_ipsec,_net_key, KEYCTL_SPI_TRY, spi_trycnt, 269183550Szec CTLFLAG_RW, key_spi_trycnt, 0, ""); 270105197Ssam 271105197Ssam/* minimum spi value to allocate automatically. */ 272183550SzecSYSCTL_V_INT(V_NET, vnet_ipsec, _net_key, KEYCTL_SPI_MIN_VALUE, 273183550Szec spi_minval, CTLFLAG_RW, key_spi_minval, 0, ""); 274105197Ssam 275105197Ssam/* maximun spi value to allocate automatically. */ 276183550SzecSYSCTL_V_INT(V_NET, vnet_ipsec, _net_key, KEYCTL_SPI_MAX_VALUE, 277183550Szec spi_maxval, CTLFLAG_RW, key_spi_maxval, 0, ""); 278105197Ssam 279105197Ssam/* interval to initialize randseed */ 280183550SzecSYSCTL_V_INT(V_NET, vnet_ipsec, _net_key, KEYCTL_RANDOM_INT, 281183550Szec int_random, CTLFLAG_RW, key_int_random, 0, ""); 282105197Ssam 283105197Ssam/* lifetime for larval SA */ 284183550SzecSYSCTL_V_INT(V_NET, vnet_ipsec, _net_key, KEYCTL_LARVAL_LIFETIME, 285183550Szec larval_lifetime, CTLFLAG_RW, key_larval_lifetime, 0, ""); 286105197Ssam 287105197Ssam/* counter for blocking to send SADB_ACQUIRE to IKEd */ 288183550SzecSYSCTL_V_INT(V_NET, vnet_ipsec, _net_key, KEYCTL_BLOCKACQ_COUNT, 289183550Szec blockacq_count, CTLFLAG_RW, key_blockacq_count, 0, ""); 290105197Ssam 291105197Ssam/* lifetime for blocking to send SADB_ACQUIRE to IKEd */ 292183550SzecSYSCTL_V_INT(V_NET, vnet_ipsec, _net_key, KEYCTL_BLOCKACQ_LIFETIME, 293183550Szec blockacq_lifetime, CTLFLAG_RW, key_blockacq_lifetime, 0, ""); 294105197Ssam 295105197Ssam/* ESP auth */ 296183550SzecSYSCTL_V_INT(V_NET, vnet_ipsec, _net_key, KEYCTL_ESP_AUTH, esp_auth, 297183550Szec CTLFLAG_RW, ipsec_esp_auth, 0, ""); 298105197Ssam 299105197Ssam/* minimum ESP key length */ 300183550SzecSYSCTL_V_INT(V_NET, vnet_ipsec, _net_key, KEYCTL_ESP_KEYMIN, 301183550Szec esp_keymin, CTLFLAG_RW, ipsec_esp_keymin, 0, ""); 302105197Ssam 303105197Ssam/* minimum AH key length */ 304183550SzecSYSCTL_V_INT(V_NET, vnet_ipsec, _net_key, KEYCTL_AH_KEYMIN, ah_keymin, 305183550Szec CTLFLAG_RW, ipsec_ah_keymin, 0, ""); 306105197Ssam 307105197Ssam/* perfered old SA rather than new SA */ 308183550SzecSYSCTL_V_INT(V_NET, vnet_ipsec, _net_key, KEYCTL_PREFERED_OLDSA, 309183550Szec preferred_oldsa, CTLFLAG_RW, key_preferred_oldsa, 0, ""); 310105197Ssam 311105197Ssam#define __LIST_CHAINED(elm) \ 312105197Ssam (!((elm)->chain.le_next == NULL && (elm)->chain.le_prev == NULL)) 313105197Ssam#define LIST_INSERT_TAIL(head, elm, type, field) \ 314105197Ssamdo {\ 315105197Ssam struct type *curelm = LIST_FIRST(head); \ 316105197Ssam if (curelm == NULL) {\ 317105197Ssam LIST_INSERT_HEAD(head, elm, field); \ 318105197Ssam } else { \ 319105197Ssam while (LIST_NEXT(curelm, field)) \ 320105197Ssam curelm = LIST_NEXT(curelm, field);\ 321105197Ssam LIST_INSERT_AFTER(curelm, elm, field);\ 322105197Ssam }\ 323105197Ssam} while (0) 324105197Ssam 325105197Ssam#define KEY_CHKSASTATE(head, sav, name) \ 326105197Ssamdo { \ 327105197Ssam if ((head) != (sav)) { \ 328105197Ssam ipseclog((LOG_DEBUG, "%s: state mismatched (TREE=%d SA=%d)\n", \ 329105197Ssam (name), (head), (sav))); \ 330105197Ssam continue; \ 331105197Ssam } \ 332105197Ssam} while (0) 333105197Ssam 334105197Ssam#define KEY_CHKSPDIR(head, sp, name) \ 335105197Ssamdo { \ 336105197Ssam if ((head) != (sp)) { \ 337105197Ssam ipseclog((LOG_DEBUG, "%s: direction mismatched (TREE=%d SP=%d), " \ 338105197Ssam "anyway continue.\n", \ 339105197Ssam (name), (head), (sp))); \ 340105197Ssam } \ 341105197Ssam} while (0) 342105197Ssam 343119643SsamMALLOC_DEFINE(M_IPSEC_SA, "secasvar", "ipsec security association"); 344119643SsamMALLOC_DEFINE(M_IPSEC_SAH, "sahead", "ipsec sa head"); 345119643SsamMALLOC_DEFINE(M_IPSEC_SP, "ipsecpolicy", "ipsec security policy"); 346119643SsamMALLOC_DEFINE(M_IPSEC_SR, "ipsecrequest", "ipsec security request"); 347119643SsamMALLOC_DEFINE(M_IPSEC_MISC, "ipsec-misc", "ipsec miscellaneous"); 348119643SsamMALLOC_DEFINE(M_IPSEC_SAQ, "ipsec-saq", "ipsec sa acquire"); 349119643SsamMALLOC_DEFINE(M_IPSEC_SAR, "ipsec-reg", "ipsec sa acquire"); 350105197Ssam 351105197Ssam/* 352105197Ssam * set parameters into secpolicyindex buffer. 353105197Ssam * Must allocate secpolicyindex buffer passed to this function. 354105197Ssam */ 355105197Ssam#define KEY_SETSECSPIDX(_dir, s, d, ps, pd, ulp, idx) \ 356105197Ssamdo { \ 357105197Ssam bzero((idx), sizeof(struct secpolicyindex)); \ 358105197Ssam (idx)->dir = (_dir); \ 359105197Ssam (idx)->prefs = (ps); \ 360105197Ssam (idx)->prefd = (pd); \ 361105197Ssam (idx)->ul_proto = (ulp); \ 362105197Ssam bcopy((s), &(idx)->src, ((const struct sockaddr *)(s))->sa_len); \ 363105197Ssam bcopy((d), &(idx)->dst, ((const struct sockaddr *)(d))->sa_len); \ 364105197Ssam} while (0) 365105197Ssam 366105197Ssam/* 367105197Ssam * set parameters into secasindex buffer. 368105197Ssam * Must allocate secasindex buffer before calling this function. 369105197Ssam */ 370105197Ssam#define KEY_SETSECASIDX(p, m, r, s, d, idx) \ 371105197Ssamdo { \ 372105197Ssam bzero((idx), sizeof(struct secasindex)); \ 373105197Ssam (idx)->proto = (p); \ 374105197Ssam (idx)->mode = (m); \ 375105197Ssam (idx)->reqid = (r); \ 376105197Ssam bcopy((s), &(idx)->src, ((const struct sockaddr *)(s))->sa_len); \ 377105197Ssam bcopy((d), &(idx)->dst, ((const struct sockaddr *)(d))->sa_len); \ 378105197Ssam} while (0) 379105197Ssam 380105197Ssam/* key statistics */ 381105197Ssamstruct _keystat { 382105197Ssam u_long getspi_count; /* the avarage of count to try to get new SPI */ 383105197Ssam} keystat; 384105197Ssam 385105197Ssamstruct sadb_msghdr { 386105197Ssam struct sadb_msg *msg; 387105197Ssam struct sadb_ext *ext[SADB_EXT_MAX + 1]; 388105197Ssam int extoff[SADB_EXT_MAX + 1]; 389105197Ssam int extlen[SADB_EXT_MAX + 1]; 390105197Ssam}; 391105197Ssam 392105197Ssamstatic struct secasvar *key_allocsa_policy __P((const struct secasindex *)); 393105197Ssamstatic void key_freesp_so __P((struct secpolicy **)); 394105197Ssamstatic struct secasvar *key_do_allocsa_policy __P((struct secashead *, u_int)); 395105197Ssamstatic void key_delsp __P((struct secpolicy *)); 396105197Ssamstatic struct secpolicy *key_getsp __P((struct secpolicyindex *)); 397119643Ssamstatic void _key_delsp(struct secpolicy *sp); 398105197Ssamstatic struct secpolicy *key_getspbyid __P((u_int32_t)); 399105197Ssamstatic u_int32_t key_newreqid __P((void)); 400105197Ssamstatic struct mbuf *key_gather_mbuf __P((struct mbuf *, 401105197Ssam const struct sadb_msghdr *, int, int, ...)); 402105197Ssamstatic int key_spdadd __P((struct socket *, struct mbuf *, 403105197Ssam const struct sadb_msghdr *)); 404105197Ssamstatic u_int32_t key_getnewspid __P((void)); 405105197Ssamstatic int key_spddelete __P((struct socket *, struct mbuf *, 406105197Ssam const struct sadb_msghdr *)); 407105197Ssamstatic int key_spddelete2 __P((struct socket *, struct mbuf *, 408105197Ssam const struct sadb_msghdr *)); 409105197Ssamstatic int key_spdget __P((struct socket *, struct mbuf *, 410105197Ssam const struct sadb_msghdr *)); 411105197Ssamstatic int key_spdflush __P((struct socket *, struct mbuf *, 412105197Ssam const struct sadb_msghdr *)); 413105197Ssamstatic int key_spddump __P((struct socket *, struct mbuf *, 414105197Ssam const struct sadb_msghdr *)); 415105197Ssamstatic struct mbuf *key_setdumpsp __P((struct secpolicy *, 416105197Ssam u_int8_t, u_int32_t, u_int32_t)); 417105197Ssamstatic u_int key_getspreqmsglen __P((struct secpolicy *)); 418105197Ssamstatic int key_spdexpire __P((struct secpolicy *)); 419105197Ssamstatic struct secashead *key_newsah __P((struct secasindex *)); 420105197Ssamstatic void key_delsah __P((struct secashead *)); 421105197Ssamstatic struct secasvar *key_newsav __P((struct mbuf *, 422105197Ssam const struct sadb_msghdr *, struct secashead *, int *, 423105197Ssam const char*, int)); 424105197Ssam#define KEY_NEWSAV(m, sadb, sah, e) \ 425105197Ssam key_newsav(m, sadb, sah, e, __FILE__, __LINE__) 426105197Ssamstatic void key_delsav __P((struct secasvar *)); 427105197Ssamstatic struct secashead *key_getsah __P((struct secasindex *)); 428105197Ssamstatic struct secasvar *key_checkspidup __P((struct secasindex *, u_int32_t)); 429105197Ssamstatic struct secasvar *key_getsavbyspi __P((struct secashead *, u_int32_t)); 430105197Ssamstatic int key_setsaval __P((struct secasvar *, struct mbuf *, 431105197Ssam const struct sadb_msghdr *)); 432105197Ssamstatic int key_mature __P((struct secasvar *)); 433105197Ssamstatic struct mbuf *key_setdumpsa __P((struct secasvar *, u_int8_t, 434105197Ssam u_int8_t, u_int32_t, u_int32_t)); 435105197Ssamstatic struct mbuf *key_setsadbmsg __P((u_int8_t, u_int16_t, u_int8_t, 436105197Ssam u_int32_t, pid_t, u_int16_t)); 437105197Ssamstatic struct mbuf *key_setsadbsa __P((struct secasvar *)); 438105197Ssamstatic struct mbuf *key_setsadbaddr __P((u_int16_t, 439105197Ssam const struct sockaddr *, u_int8_t, u_int16_t)); 440194062Svanhu#ifdef IPSEC_NAT_T 441194062Svanhustatic struct mbuf *key_setsadbxport(u_int16_t, u_int16_t); 442194062Svanhustatic struct mbuf *key_setsadbxtype(u_int16_t); 443194062Svanhu#endif 444194062Svanhustatic void key_porttosaddr(struct sockaddr *, u_int16_t); 445194062Svanhu#define KEY_PORTTOSADDR(saddr, port) \ 446194062Svanhu key_porttosaddr((struct sockaddr *)(saddr), (port)) 447105197Ssamstatic struct mbuf *key_setsadbxsa2 __P((u_int8_t, u_int32_t, u_int32_t)); 448105197Ssamstatic struct mbuf *key_setsadbxpolicy __P((u_int16_t, u_int8_t, 449105197Ssam u_int32_t)); 450157123Sgnnstatic struct seckey *key_dup_keymsg(const struct sadb_key *, u_int, 451157123Sgnn struct malloc_type *); 452157123Sgnnstatic struct seclifetime *key_dup_lifemsg(const struct sadb_lifetime *src, 453157123Sgnn struct malloc_type *type); 454105197Ssam#ifdef INET6 455105197Ssamstatic int key_ismyaddr6 __P((struct sockaddr_in6 *)); 456105197Ssam#endif 457105197Ssam 458105197Ssam/* flags for key_cmpsaidx() */ 459105197Ssam#define CMP_HEAD 1 /* protocol, addresses. */ 460105197Ssam#define CMP_MODE_REQID 2 /* additionally HEAD, reqid, mode. */ 461105197Ssam#define CMP_REQID 3 /* additionally HEAD, reaid. */ 462105197Ssam#define CMP_EXACTLY 4 /* all elements. */ 463105197Ssamstatic int key_cmpsaidx 464105197Ssam __P((const struct secasindex *, const struct secasindex *, int)); 465105197Ssam 466105197Ssamstatic int key_cmpspidx_exactly 467105197Ssam __P((struct secpolicyindex *, struct secpolicyindex *)); 468105197Ssamstatic int key_cmpspidx_withmask 469105197Ssam __P((struct secpolicyindex *, struct secpolicyindex *)); 470105197Ssamstatic int key_sockaddrcmp __P((const struct sockaddr *, const struct sockaddr *, int)); 471105197Ssamstatic int key_bbcmp __P((const void *, const void *, u_int)); 472105197Ssamstatic u_int16_t key_satype2proto __P((u_int8_t)); 473105197Ssamstatic u_int8_t key_proto2satype __P((u_int16_t)); 474105197Ssam 475105197Ssamstatic int key_getspi __P((struct socket *, struct mbuf *, 476105197Ssam const struct sadb_msghdr *)); 477105197Ssamstatic u_int32_t key_do_getnewspi __P((struct sadb_spirange *, 478105197Ssam struct secasindex *)); 479105197Ssamstatic int key_update __P((struct socket *, struct mbuf *, 480105197Ssam const struct sadb_msghdr *)); 481105197Ssam#ifdef IPSEC_DOSEQCHECK 482105197Ssamstatic struct secasvar *key_getsavbyseq __P((struct secashead *, u_int32_t)); 483105197Ssam#endif 484105197Ssamstatic int key_add __P((struct socket *, struct mbuf *, 485105197Ssam const struct sadb_msghdr *)); 486105197Ssamstatic int key_setident __P((struct secashead *, struct mbuf *, 487105197Ssam const struct sadb_msghdr *)); 488105197Ssamstatic struct mbuf *key_getmsgbuf_x1 __P((struct mbuf *, 489105197Ssam const struct sadb_msghdr *)); 490105197Ssamstatic int key_delete __P((struct socket *, struct mbuf *, 491105197Ssam const struct sadb_msghdr *)); 492105197Ssamstatic int key_get __P((struct socket *, struct mbuf *, 493105197Ssam const struct sadb_msghdr *)); 494105197Ssam 495105197Ssamstatic void key_getcomb_setlifetime __P((struct sadb_comb *)); 496105197Ssamstatic struct mbuf *key_getcomb_esp __P((void)); 497105197Ssamstatic struct mbuf *key_getcomb_ah __P((void)); 498105197Ssamstatic struct mbuf *key_getcomb_ipcomp __P((void)); 499105197Ssamstatic struct mbuf *key_getprop __P((const struct secasindex *)); 500105197Ssam 501105197Ssamstatic int key_acquire __P((const struct secasindex *, struct secpolicy *)); 502105197Ssamstatic struct secacq *key_newacq __P((const struct secasindex *)); 503105197Ssamstatic struct secacq *key_getacq __P((const struct secasindex *)); 504105197Ssamstatic struct secacq *key_getacqbyseq __P((u_int32_t)); 505105197Ssamstatic struct secspacq *key_newspacq __P((struct secpolicyindex *)); 506105197Ssamstatic struct secspacq *key_getspacq __P((struct secpolicyindex *)); 507105197Ssamstatic int key_acquire2 __P((struct socket *, struct mbuf *, 508105197Ssam const struct sadb_msghdr *)); 509105197Ssamstatic int key_register __P((struct socket *, struct mbuf *, 510105197Ssam const struct sadb_msghdr *)); 511105197Ssamstatic int key_expire __P((struct secasvar *)); 512105197Ssamstatic int key_flush __P((struct socket *, struct mbuf *, 513105197Ssam const struct sadb_msghdr *)); 514105197Ssamstatic int key_dump __P((struct socket *, struct mbuf *, 515105197Ssam const struct sadb_msghdr *)); 516105197Ssamstatic int key_promisc __P((struct socket *, struct mbuf *, 517105197Ssam const struct sadb_msghdr *)); 518105197Ssamstatic int key_senderror __P((struct socket *, struct mbuf *, int)); 519105197Ssamstatic int key_validate_ext __P((const struct sadb_ext *, int)); 520105197Ssamstatic int key_align __P((struct mbuf *, struct sadb_msghdr *)); 521157123Sgnnstatic struct mbuf *key_setlifetime(struct seclifetime *src, 522157123Sgnn u_int16_t exttype); 523157123Sgnnstatic struct mbuf *key_setkey(struct seckey *src, u_int16_t exttype); 524157123Sgnn 525105197Ssam#if 0 526105197Ssamstatic const char *key_getfqdn __P((void)); 527105197Ssamstatic const char *key_getuserfqdn __P((void)); 528105197Ssam#endif 529105197Ssamstatic void key_sa_chgstate __P((struct secasvar *, u_int8_t)); 530105197Ssamstatic struct mbuf *key_alloc_mbuf __P((int)); 531105197Ssam 532158767Spjdstatic __inline void 533158767Spjdsa_initref(struct secasvar *sav) 534158767Spjd{ 535105197Ssam 536158767Spjd refcount_init(&sav->refcnt, 1); 537158767Spjd} 538158767Spjdstatic __inline void 539158767Spjdsa_addref(struct secasvar *sav) 540158767Spjd{ 541158767Spjd 542158767Spjd refcount_acquire(&sav->refcnt); 543158767Spjd IPSEC_ASSERT(sav->refcnt != 0, ("SA refcnt overflow")); 544158767Spjd} 545158767Spjdstatic __inline int 546158767Spjdsa_delref(struct secasvar *sav) 547158767Spjd{ 548158767Spjd 549158767Spjd IPSEC_ASSERT(sav->refcnt > 0, ("SA refcnt underflow")); 550158767Spjd return (refcount_release(&sav->refcnt)); 551158767Spjd} 552158767Spjd 553105197Ssam#define SP_ADDREF(p) do { \ 554105197Ssam (p)->refcnt++; \ 555120585Ssam IPSEC_ASSERT((p)->refcnt != 0, ("SP refcnt overflow")); \ 556105197Ssam} while (0) 557105197Ssam#define SP_DELREF(p) do { \ 558120585Ssam IPSEC_ASSERT((p)->refcnt > 0, ("SP refcnt underflow")); \ 559105197Ssam (p)->refcnt--; \ 560105197Ssam} while (0) 561135947Ssam 562105197Ssam 563105197Ssam/* 564135947Ssam * Update the refcnt while holding the SPTREE lock. 565135947Ssam */ 566135947Ssamvoid 567135947Ssamkey_addref(struct secpolicy *sp) 568135947Ssam{ 569135947Ssam SPTREE_LOCK(); 570135947Ssam SP_ADDREF(sp); 571135947Ssam SPTREE_UNLOCK(); 572135947Ssam} 573135947Ssam 574135947Ssam/* 575105197Ssam * Return 0 when there are known to be no SP's for the specified 576105197Ssam * direction. Otherwise return 1. This is used by IPsec code 577105197Ssam * to optimize performance. 578105197Ssam */ 579105197Ssamint 580105197Ssamkey_havesp(u_int dir) 581105197Ssam{ 582183550Szec INIT_VNET_IPSEC(curvnet); 583183550Szec 584105197Ssam return (dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND ? 585181803Sbz LIST_FIRST(&V_sptree[dir]) != NULL : 1); 586105197Ssam} 587105197Ssam 588105197Ssam/* %%% IPsec policy management */ 589105197Ssam/* 590105197Ssam * allocating a SP for OUTBOUND or INBOUND packet. 591105197Ssam * Must call key_freesp() later. 592105197Ssam * OUT: NULL: not found 593105197Ssam * others: found and return the pointer. 594105197Ssam */ 595105197Ssamstruct secpolicy * 596105197Ssamkey_allocsp(struct secpolicyindex *spidx, u_int dir, const char* where, int tag) 597105197Ssam{ 598183550Szec INIT_VNET_IPSEC(curvnet); 599105197Ssam struct secpolicy *sp; 600105197Ssam 601120585Ssam IPSEC_ASSERT(spidx != NULL, ("null spidx")); 602120585Ssam IPSEC_ASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND, 603120585Ssam ("invalid direction %u", dir)); 604105197Ssam 605105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 606120585Ssam printf("DP %s from %s:%u\n", __func__, where, tag)); 607105197Ssam 608105197Ssam /* get a SP entry */ 609105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DATA, 610105197Ssam printf("*** objects\n"); 611105197Ssam kdebug_secpolicyindex(spidx)); 612105197Ssam 613120585Ssam SPTREE_LOCK(); 614181803Sbz LIST_FOREACH(sp, &V_sptree[dir], chain) { 615105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DATA, 616105197Ssam printf("*** in SPD\n"); 617105197Ssam kdebug_secpolicyindex(&sp->spidx)); 618105197Ssam 619105197Ssam if (sp->state == IPSEC_SPSTATE_DEAD) 620105197Ssam continue; 621105197Ssam if (key_cmpspidx_withmask(&sp->spidx, spidx)) 622105197Ssam goto found; 623105197Ssam } 624105197Ssam sp = NULL; 625105197Ssamfound: 626105197Ssam if (sp) { 627105197Ssam /* sanity check */ 628120585Ssam KEY_CHKSPDIR(sp->spidx.dir, dir, __func__); 629105197Ssam 630105197Ssam /* found a SPD entry */ 631105197Ssam sp->lastused = time_second; 632105197Ssam SP_ADDREF(sp); 633105197Ssam } 634120585Ssam SPTREE_UNLOCK(); 635105197Ssam 636105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 637120585Ssam printf("DP %s return SP:%p (ID=%u) refcnt %u\n", __func__, 638105197Ssam sp, sp ? sp->id : 0, sp ? sp->refcnt : 0)); 639105197Ssam return sp; 640105197Ssam} 641105197Ssam 642105197Ssam/* 643105197Ssam * allocating a SP for OUTBOUND or INBOUND packet. 644105197Ssam * Must call key_freesp() later. 645105197Ssam * OUT: NULL: not found 646105197Ssam * others: found and return the pointer. 647105197Ssam */ 648105197Ssamstruct secpolicy * 649105197Ssamkey_allocsp2(u_int32_t spi, 650105197Ssam union sockaddr_union *dst, 651105197Ssam u_int8_t proto, 652105197Ssam u_int dir, 653105197Ssam const char* where, int tag) 654105197Ssam{ 655183550Szec INIT_VNET_IPSEC(curvnet); 656105197Ssam struct secpolicy *sp; 657105197Ssam 658120585Ssam IPSEC_ASSERT(dst != NULL, ("null dst")); 659120585Ssam IPSEC_ASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND, 660120585Ssam ("invalid direction %u", dir)); 661105197Ssam 662105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 663120585Ssam printf("DP %s from %s:%u\n", __func__, where, tag)); 664105197Ssam 665105197Ssam /* get a SP entry */ 666105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DATA, 667105197Ssam printf("*** objects\n"); 668105197Ssam printf("spi %u proto %u dir %u\n", spi, proto, dir); 669105197Ssam kdebug_sockaddr(&dst->sa)); 670105197Ssam 671120585Ssam SPTREE_LOCK(); 672181803Sbz LIST_FOREACH(sp, &V_sptree[dir], chain) { 673105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DATA, 674105197Ssam printf("*** in SPD\n"); 675105197Ssam kdebug_secpolicyindex(&sp->spidx)); 676105197Ssam 677105197Ssam if (sp->state == IPSEC_SPSTATE_DEAD) 678105197Ssam continue; 679105197Ssam /* compare simple values, then dst address */ 680105197Ssam if (sp->spidx.ul_proto != proto) 681105197Ssam continue; 682105197Ssam /* NB: spi's must exist and match */ 683105197Ssam if (!sp->req || !sp->req->sav || sp->req->sav->spi != spi) 684105197Ssam continue; 685105197Ssam if (key_sockaddrcmp(&sp->spidx.dst.sa, &dst->sa, 1) == 0) 686105197Ssam goto found; 687105197Ssam } 688105197Ssam sp = NULL; 689105197Ssamfound: 690105197Ssam if (sp) { 691105197Ssam /* sanity check */ 692120585Ssam KEY_CHKSPDIR(sp->spidx.dir, dir, __func__); 693105197Ssam 694105197Ssam /* found a SPD entry */ 695105197Ssam sp->lastused = time_second; 696105197Ssam SP_ADDREF(sp); 697105197Ssam } 698120585Ssam SPTREE_UNLOCK(); 699105197Ssam 700105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 701120585Ssam printf("DP %s return SP:%p (ID=%u) refcnt %u\n", __func__, 702105197Ssam sp, sp ? sp->id : 0, sp ? sp->refcnt : 0)); 703105197Ssam return sp; 704105197Ssam} 705105197Ssam 706191599Sbz#if 0 707105197Ssam/* 708105197Ssam * return a policy that matches this particular inbound packet. 709105197Ssam * XXX slow 710105197Ssam */ 711105197Ssamstruct secpolicy * 712105197Ssamkey_gettunnel(const struct sockaddr *osrc, 713105197Ssam const struct sockaddr *odst, 714105197Ssam const struct sockaddr *isrc, 715105197Ssam const struct sockaddr *idst, 716105197Ssam const char* where, int tag) 717105197Ssam{ 718183550Szec INIT_VNET_IPSEC(curvnet); 719105197Ssam struct secpolicy *sp; 720105197Ssam const int dir = IPSEC_DIR_INBOUND; 721105197Ssam struct ipsecrequest *r1, *r2, *p; 722105197Ssam struct secpolicyindex spidx; 723105197Ssam 724105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 725120585Ssam printf("DP %s from %s:%u\n", __func__, where, tag)); 726105197Ssam 727105197Ssam if (isrc->sa_family != idst->sa_family) { 728120585Ssam ipseclog((LOG_ERR, "%s: protocol family mismatched %d != %d\n.", 729120585Ssam __func__, isrc->sa_family, idst->sa_family)); 730105197Ssam sp = NULL; 731105197Ssam goto done; 732105197Ssam } 733105197Ssam 734120585Ssam SPTREE_LOCK(); 735181803Sbz LIST_FOREACH(sp, &V_sptree[dir], chain) { 736105197Ssam if (sp->state == IPSEC_SPSTATE_DEAD) 737105197Ssam continue; 738105197Ssam 739105197Ssam r1 = r2 = NULL; 740105197Ssam for (p = sp->req; p; p = p->next) { 741105197Ssam if (p->saidx.mode != IPSEC_MODE_TUNNEL) 742105197Ssam continue; 743105197Ssam 744105197Ssam r1 = r2; 745105197Ssam r2 = p; 746105197Ssam 747105197Ssam if (!r1) { 748105197Ssam /* here we look at address matches only */ 749105197Ssam spidx = sp->spidx; 750105197Ssam if (isrc->sa_len > sizeof(spidx.src) || 751105197Ssam idst->sa_len > sizeof(spidx.dst)) 752105197Ssam continue; 753105197Ssam bcopy(isrc, &spidx.src, isrc->sa_len); 754105197Ssam bcopy(idst, &spidx.dst, idst->sa_len); 755105197Ssam if (!key_cmpspidx_withmask(&sp->spidx, &spidx)) 756105197Ssam continue; 757105197Ssam } else { 758105197Ssam if (key_sockaddrcmp(&r1->saidx.src.sa, isrc, 0) || 759105197Ssam key_sockaddrcmp(&r1->saidx.dst.sa, idst, 0)) 760105197Ssam continue; 761105197Ssam } 762105197Ssam 763105197Ssam if (key_sockaddrcmp(&r2->saidx.src.sa, osrc, 0) || 764105197Ssam key_sockaddrcmp(&r2->saidx.dst.sa, odst, 0)) 765105197Ssam continue; 766105197Ssam 767105197Ssam goto found; 768105197Ssam } 769105197Ssam } 770105197Ssam sp = NULL; 771105197Ssamfound: 772105197Ssam if (sp) { 773105197Ssam sp->lastused = time_second; 774105197Ssam SP_ADDREF(sp); 775105197Ssam } 776120585Ssam SPTREE_UNLOCK(); 777105197Ssamdone: 778105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 779120585Ssam printf("DP %s return SP:%p (ID=%u) refcnt %u\n", __func__, 780105197Ssam sp, sp ? sp->id : 0, sp ? sp->refcnt : 0)); 781105197Ssam return sp; 782105197Ssam} 783191599Sbz#endif 784105197Ssam 785105197Ssam/* 786105197Ssam * allocating an SA entry for an *OUTBOUND* packet. 787105197Ssam * checking each request entries in SP, and acquire an SA if need. 788105197Ssam * OUT: 0: there are valid requests. 789105197Ssam * ENOENT: policy may be valid, but SA with REQUIRE is on acquiring. 790105197Ssam */ 791105197Ssamint 792105197Ssamkey_checkrequest(struct ipsecrequest *isr, const struct secasindex *saidx) 793105197Ssam{ 794183550Szec INIT_VNET_IPSEC(curvnet); 795105197Ssam u_int level; 796105197Ssam int error; 797105197Ssam 798120585Ssam IPSEC_ASSERT(isr != NULL, ("null isr")); 799120585Ssam IPSEC_ASSERT(saidx != NULL, ("null saidx")); 800120585Ssam IPSEC_ASSERT(saidx->mode == IPSEC_MODE_TRANSPORT || 801105197Ssam saidx->mode == IPSEC_MODE_TUNNEL, 802120585Ssam ("unexpected policy %u", saidx->mode)); 803105197Ssam 804105197Ssam /* 805105197Ssam * XXX guard against protocol callbacks from the crypto 806105197Ssam * thread as they reference ipsecrequest.sav which we 807105197Ssam * temporarily null out below. Need to rethink how we 808105197Ssam * handle bundled SA's in the callback thread. 809105197Ssam */ 810120585Ssam IPSECREQUEST_LOCK_ASSERT(isr); 811119643Ssam 812119643Ssam /* get current level */ 813119643Ssam level = ipsec_get_reqlevel(isr); 814105197Ssam#if 0 815105197Ssam /* 816105197Ssam * We do allocate new SA only if the state of SA in the holder is 817105197Ssam * SADB_SASTATE_DEAD. The SA for outbound must be the oldest. 818105197Ssam */ 819105197Ssam if (isr->sav != NULL) { 820105197Ssam if (isr->sav->sah == NULL) 821120585Ssam panic("%s: sah is null.\n", __func__); 822105197Ssam if (isr->sav == (struct secasvar *)LIST_FIRST( 823105197Ssam &isr->sav->sah->savtree[SADB_SASTATE_DEAD])) { 824105197Ssam KEY_FREESAV(&isr->sav); 825105197Ssam isr->sav = NULL; 826105197Ssam } 827105197Ssam } 828105197Ssam#else 829105197Ssam /* 830105197Ssam * we free any SA stashed in the IPsec request because a different 831105197Ssam * SA may be involved each time this request is checked, either 832105197Ssam * because new SAs are being configured, or this request is 833105197Ssam * associated with an unconnected datagram socket, or this request 834105197Ssam * is associated with a system default policy. 835105197Ssam * 836105197Ssam * The operation may have negative impact to performance. We may 837105197Ssam * want to check cached SA carefully, rather than picking new SA 838105197Ssam * every time. 839105197Ssam */ 840105197Ssam if (isr->sav != NULL) { 841105197Ssam KEY_FREESAV(&isr->sav); 842105197Ssam isr->sav = NULL; 843105197Ssam } 844105197Ssam#endif 845105197Ssam 846105197Ssam /* 847105197Ssam * new SA allocation if no SA found. 848105197Ssam * key_allocsa_policy should allocate the oldest SA available. 849105197Ssam * See key_do_allocsa_policy(), and draft-jenkins-ipsec-rekeying-03.txt. 850105197Ssam */ 851105197Ssam if (isr->sav == NULL) 852105197Ssam isr->sav = key_allocsa_policy(saidx); 853105197Ssam 854105197Ssam /* When there is SA. */ 855105197Ssam if (isr->sav != NULL) { 856105197Ssam if (isr->sav->state != SADB_SASTATE_MATURE && 857105197Ssam isr->sav->state != SADB_SASTATE_DYING) 858105197Ssam return EINVAL; 859105197Ssam return 0; 860105197Ssam } 861105197Ssam 862105197Ssam /* there is no SA */ 863105197Ssam error = key_acquire(saidx, isr->sp); 864105197Ssam if (error != 0) { 865105197Ssam /* XXX What should I do ? */ 866120585Ssam ipseclog((LOG_DEBUG, "%s: error %d returned from key_acquire\n", 867120585Ssam __func__, error)); 868105197Ssam return error; 869105197Ssam } 870105197Ssam 871105197Ssam if (level != IPSEC_LEVEL_REQUIRE) { 872105197Ssam /* XXX sigh, the interface to this routine is botched */ 873120585Ssam IPSEC_ASSERT(isr->sav == NULL, ("unexpected SA")); 874105197Ssam return 0; 875105197Ssam } else { 876105197Ssam return ENOENT; 877105197Ssam } 878105197Ssam} 879105197Ssam 880105197Ssam/* 881105197Ssam * allocating a SA for policy entry from SAD. 882105197Ssam * NOTE: searching SAD of aliving state. 883105197Ssam * OUT: NULL: not found. 884105197Ssam * others: found and return the pointer. 885105197Ssam */ 886105197Ssamstatic struct secasvar * 887105197Ssamkey_allocsa_policy(const struct secasindex *saidx) 888105197Ssam{ 889128856Ssam#define N(a) _ARRAYLEN(a) 890183550Szec INIT_VNET_IPSEC(curvnet); 891105197Ssam struct secashead *sah; 892105197Ssam struct secasvar *sav; 893128856Ssam u_int stateidx, arraysize; 894128856Ssam const u_int *state_valid; 895105197Ssam 896120585Ssam SAHTREE_LOCK(); 897181803Sbz LIST_FOREACH(sah, &V_sahtree, chain) { 898105197Ssam if (sah->state == SADB_SASTATE_DEAD) 899105197Ssam continue; 900119643Ssam if (key_cmpsaidx(&sah->saidx, saidx, CMP_MODE_REQID)) { 901181803Sbz if (V_key_preferred_oldsa) { 902128856Ssam state_valid = saorder_state_valid_prefer_old; 903128856Ssam arraysize = N(saorder_state_valid_prefer_old); 904128856Ssam } else { 905128856Ssam state_valid = saorder_state_valid_prefer_new; 906128856Ssam arraysize = N(saorder_state_valid_prefer_new); 907128856Ssam } 908120585Ssam SAHTREE_UNLOCK(); 909105197Ssam goto found; 910119643Ssam } 911105197Ssam } 912120585Ssam SAHTREE_UNLOCK(); 913105197Ssam 914105197Ssam return NULL; 915105197Ssam 916105197Ssam found: 917105197Ssam /* search valid state */ 918128856Ssam for (stateidx = 0; stateidx < arraysize; stateidx++) { 919128856Ssam sav = key_do_allocsa_policy(sah, state_valid[stateidx]); 920105197Ssam if (sav != NULL) 921105197Ssam return sav; 922105197Ssam } 923105197Ssam 924105197Ssam return NULL; 925128856Ssam#undef N 926105197Ssam} 927105197Ssam 928105197Ssam/* 929105197Ssam * searching SAD with direction, protocol, mode and state. 930105197Ssam * called by key_allocsa_policy(). 931105197Ssam * OUT: 932105197Ssam * NULL : not found 933105197Ssam * others : found, pointer to a SA. 934105197Ssam */ 935105197Ssamstatic struct secasvar * 936105197Ssamkey_do_allocsa_policy(struct secashead *sah, u_int state) 937105197Ssam{ 938183550Szec INIT_VNET_IPSEC(curvnet); 939105197Ssam struct secasvar *sav, *nextsav, *candidate, *d; 940105197Ssam 941105197Ssam /* initilize */ 942105197Ssam candidate = NULL; 943105197Ssam 944120585Ssam SAHTREE_LOCK(); 945105197Ssam for (sav = LIST_FIRST(&sah->savtree[state]); 946105197Ssam sav != NULL; 947105197Ssam sav = nextsav) { 948105197Ssam 949105197Ssam nextsav = LIST_NEXT(sav, chain); 950105197Ssam 951105197Ssam /* sanity check */ 952120585Ssam KEY_CHKSASTATE(sav->state, state, __func__); 953105197Ssam 954105197Ssam /* initialize */ 955105197Ssam if (candidate == NULL) { 956105197Ssam candidate = sav; 957105197Ssam continue; 958105197Ssam } 959105197Ssam 960105197Ssam /* Which SA is the better ? */ 961105197Ssam 962120585Ssam IPSEC_ASSERT(candidate->lft_c != NULL, 963120585Ssam ("null candidate lifetime")); 964120585Ssam IPSEC_ASSERT(sav->lft_c != NULL, ("null sav lifetime")); 965105197Ssam 966105197Ssam /* What the best method is to compare ? */ 967181803Sbz if (V_key_preferred_oldsa) { 968157123Sgnn if (candidate->lft_c->addtime > 969157123Sgnn sav->lft_c->addtime) { 970105197Ssam candidate = sav; 971105197Ssam } 972105197Ssam continue; 973105197Ssam /*NOTREACHED*/ 974105197Ssam } 975105197Ssam 976125876Sguido /* preferred new sa rather than old sa */ 977157123Sgnn if (candidate->lft_c->addtime < 978157123Sgnn sav->lft_c->addtime) { 979105197Ssam d = candidate; 980105197Ssam candidate = sav; 981105197Ssam } else 982105197Ssam d = sav; 983105197Ssam 984105197Ssam /* 985105197Ssam * prepared to delete the SA when there is more 986105197Ssam * suitable candidate and the lifetime of the SA is not 987105197Ssam * permanent. 988105197Ssam */ 989177553Sbz if (d->lft_h->addtime != 0) { 990105197Ssam struct mbuf *m, *result; 991125508Ssam u_int8_t satype; 992105197Ssam 993105197Ssam key_sa_chgstate(d, SADB_SASTATE_DEAD); 994105197Ssam 995120585Ssam IPSEC_ASSERT(d->refcnt > 0, ("bogus ref count")); 996125508Ssam 997125508Ssam satype = key_proto2satype(d->sah->saidx.proto); 998125508Ssam if (satype == 0) 999125508Ssam goto msgfail; 1000125508Ssam 1001105197Ssam m = key_setsadbmsg(SADB_DELETE, 0, 1002125508Ssam satype, 0, 0, d->refcnt - 1); 1003105197Ssam if (!m) 1004105197Ssam goto msgfail; 1005105197Ssam result = m; 1006105197Ssam 1007105197Ssam /* set sadb_address for saidx's. */ 1008105197Ssam m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC, 1009105197Ssam &d->sah->saidx.src.sa, 1010105197Ssam d->sah->saidx.src.sa.sa_len << 3, 1011105197Ssam IPSEC_ULPROTO_ANY); 1012105197Ssam if (!m) 1013105197Ssam goto msgfail; 1014105197Ssam m_cat(result, m); 1015105197Ssam 1016105197Ssam /* set sadb_address for saidx's. */ 1017105197Ssam m = key_setsadbaddr(SADB_EXT_ADDRESS_DST, 1018128860Ssam &d->sah->saidx.dst.sa, 1019128860Ssam d->sah->saidx.dst.sa.sa_len << 3, 1020105197Ssam IPSEC_ULPROTO_ANY); 1021105197Ssam if (!m) 1022105197Ssam goto msgfail; 1023105197Ssam m_cat(result, m); 1024105197Ssam 1025105197Ssam /* create SA extension */ 1026105197Ssam m = key_setsadbsa(d); 1027105197Ssam if (!m) 1028105197Ssam goto msgfail; 1029105197Ssam m_cat(result, m); 1030105197Ssam 1031105197Ssam if (result->m_len < sizeof(struct sadb_msg)) { 1032105197Ssam result = m_pullup(result, 1033105197Ssam sizeof(struct sadb_msg)); 1034105197Ssam if (result == NULL) 1035105197Ssam goto msgfail; 1036105197Ssam } 1037105197Ssam 1038105197Ssam result->m_pkthdr.len = 0; 1039105197Ssam for (m = result; m; m = m->m_next) 1040105197Ssam result->m_pkthdr.len += m->m_len; 1041105197Ssam mtod(result, struct sadb_msg *)->sadb_msg_len = 1042105197Ssam PFKEY_UNIT64(result->m_pkthdr.len); 1043105197Ssam 1044105197Ssam if (key_sendup_mbuf(NULL, result, 1045105197Ssam KEY_SENDUP_REGISTERED)) 1046105197Ssam goto msgfail; 1047105197Ssam msgfail: 1048105197Ssam KEY_FREESAV(&d); 1049105197Ssam } 1050105197Ssam } 1051105197Ssam if (candidate) { 1052158767Spjd sa_addref(candidate); 1053105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 1054120585Ssam printf("DP %s cause refcnt++:%d SA:%p\n", 1055120585Ssam __func__, candidate->refcnt, candidate)); 1056105197Ssam } 1057120585Ssam SAHTREE_UNLOCK(); 1058119643Ssam 1059105197Ssam return candidate; 1060105197Ssam} 1061105197Ssam 1062105197Ssam/* 1063105197Ssam * allocating a usable SA entry for a *INBOUND* packet. 1064105197Ssam * Must call key_freesav() later. 1065105197Ssam * OUT: positive: pointer to a usable sav (i.e. MATURE or DYING state). 1066105197Ssam * NULL: not found, or error occured. 1067105197Ssam * 1068105197Ssam * In the comparison, no source address is used--for RFC2401 conformance. 1069105197Ssam * To quote, from section 4.1: 1070105197Ssam * A security association is uniquely identified by a triple consisting 1071105197Ssam * of a Security Parameter Index (SPI), an IP Destination Address, and a 1072105197Ssam * security protocol (AH or ESP) identifier. 1073105197Ssam * Note that, however, we do need to keep source address in IPsec SA. 1074105197Ssam * IKE specification and PF_KEY specification do assume that we 1075105197Ssam * keep source address in IPsec SA. We see a tricky situation here. 1076105197Ssam */ 1077105197Ssamstruct secasvar * 1078105197Ssamkey_allocsa( 1079105197Ssam union sockaddr_union *dst, 1080105197Ssam u_int proto, 1081105197Ssam u_int32_t spi, 1082105197Ssam const char* where, int tag) 1083105197Ssam{ 1084183550Szec INIT_VNET_IPSEC(curvnet); 1085105197Ssam struct secashead *sah; 1086105197Ssam struct secasvar *sav; 1087128856Ssam u_int stateidx, arraysize, state; 1088128856Ssam const u_int *saorder_state_valid; 1089194062Svanhu int chkport; 1090105197Ssam 1091120585Ssam IPSEC_ASSERT(dst != NULL, ("null dst address")); 1092105197Ssam 1093105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 1094120585Ssam printf("DP %s from %s:%u\n", __func__, where, tag)); 1095105197Ssam 1096194062Svanhu#ifdef IPSEC_NAT_T 1097194062Svanhu chkport = (dst->sa.sa_family == AF_INET && 1098194062Svanhu dst->sa.sa_len == sizeof(struct sockaddr_in) && 1099194062Svanhu dst->sin.sin_port != 0); 1100194062Svanhu#else 1101194062Svanhu chkport = 0; 1102194062Svanhu#endif 1103194062Svanhu 1104105197Ssam /* 1105105197Ssam * searching SAD. 1106105197Ssam * XXX: to be checked internal IP header somewhere. Also when 1107105197Ssam * IPsec tunnel packet is received. But ESP tunnel mode is 1108105197Ssam * encrypted so we can't check internal IP header. 1109105197Ssam */ 1110120585Ssam SAHTREE_LOCK(); 1111181803Sbz if (V_key_preferred_oldsa) { 1112128856Ssam saorder_state_valid = saorder_state_valid_prefer_old; 1113128856Ssam arraysize = _ARRAYLEN(saorder_state_valid_prefer_old); 1114128856Ssam } else { 1115128856Ssam saorder_state_valid = saorder_state_valid_prefer_new; 1116128856Ssam arraysize = _ARRAYLEN(saorder_state_valid_prefer_new); 1117128856Ssam } 1118181803Sbz LIST_FOREACH(sah, &V_sahtree, chain) { 1119105197Ssam /* search valid state */ 1120128856Ssam for (stateidx = 0; stateidx < arraysize; stateidx++) { 1121105197Ssam state = saorder_state_valid[stateidx]; 1122105197Ssam LIST_FOREACH(sav, &sah->savtree[state], chain) { 1123105197Ssam /* sanity check */ 1124120585Ssam KEY_CHKSASTATE(sav->state, state, __func__); 1125105197Ssam /* do not return entries w/ unusable state */ 1126105197Ssam if (sav->state != SADB_SASTATE_MATURE && 1127105197Ssam sav->state != SADB_SASTATE_DYING) 1128105197Ssam continue; 1129105197Ssam if (proto != sav->sah->saidx.proto) 1130105197Ssam continue; 1131105197Ssam if (spi != sav->spi) 1132105197Ssam continue; 1133105197Ssam#if 0 /* don't check src */ 1134105197Ssam /* check src address */ 1135194062Svanhu if (key_sockaddrcmp(&src->sa, &sav->sah->saidx.src.sa, chkport) != 0) 1136105197Ssam continue; 1137105197Ssam#endif 1138105197Ssam /* check dst address */ 1139194062Svanhu if (key_sockaddrcmp(&dst->sa, &sav->sah->saidx.dst.sa, chkport) != 0) 1140105197Ssam continue; 1141158767Spjd sa_addref(sav); 1142105197Ssam goto done; 1143105197Ssam } 1144105197Ssam } 1145105197Ssam } 1146105197Ssam sav = NULL; 1147105197Ssamdone: 1148120585Ssam SAHTREE_UNLOCK(); 1149105197Ssam 1150105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 1151120585Ssam printf("DP %s return SA:%p; refcnt %u\n", __func__, 1152105197Ssam sav, sav ? sav->refcnt : 0)); 1153105197Ssam return sav; 1154105197Ssam} 1155105197Ssam 1156105197Ssam/* 1157105197Ssam * Must be called after calling key_allocsp(). 1158105197Ssam * For both the packet without socket and key_freeso(). 1159105197Ssam */ 1160105197Ssamvoid 1161105197Ssam_key_freesp(struct secpolicy **spp, const char* where, int tag) 1162105197Ssam{ 1163183550Szec INIT_VNET_IPSEC(curvnet); 1164105197Ssam struct secpolicy *sp = *spp; 1165105197Ssam 1166120585Ssam IPSEC_ASSERT(sp != NULL, ("null sp")); 1167105197Ssam 1168120585Ssam SPTREE_LOCK(); 1169105197Ssam SP_DELREF(sp); 1170105197Ssam 1171105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 1172120585Ssam printf("DP %s SP:%p (ID=%u) from %s:%u; refcnt now %u\n", 1173120585Ssam __func__, sp, sp->id, where, tag, sp->refcnt)); 1174105197Ssam 1175105197Ssam if (sp->refcnt == 0) { 1176105197Ssam *spp = NULL; 1177105197Ssam key_delsp(sp); 1178105197Ssam } 1179120585Ssam SPTREE_UNLOCK(); 1180105197Ssam} 1181105197Ssam 1182105197Ssam/* 1183105197Ssam * Must be called after calling key_allocsp(). 1184105197Ssam * For the packet with socket. 1185105197Ssam */ 1186105197Ssamvoid 1187105197Ssamkey_freeso(struct socket *so) 1188105197Ssam{ 1189183550Szec INIT_VNET_IPSEC(curvnet); 1190120585Ssam IPSEC_ASSERT(so != NULL, ("null so")); 1191105197Ssam 1192105197Ssam switch (so->so_proto->pr_domain->dom_family) { 1193186141Sbz#if defined(INET) || defined(INET6) 1194105197Ssam#ifdef INET 1195105197Ssam case PF_INET: 1196105197Ssam#endif 1197105197Ssam#ifdef INET6 1198105197Ssam case PF_INET6: 1199186141Sbz#endif 1200105197Ssam { 1201186141Sbz struct inpcb *pcb = sotoinpcb(so); 1202105197Ssam 1203105197Ssam /* Does it have a PCB ? */ 1204105197Ssam if (pcb == NULL) 1205105197Ssam return; 1206105197Ssam key_freesp_so(&pcb->inp_sp->sp_in); 1207105197Ssam key_freesp_so(&pcb->inp_sp->sp_out); 1208105197Ssam } 1209105197Ssam break; 1210186141Sbz#endif /* INET || INET6 */ 1211105197Ssam default: 1212120585Ssam ipseclog((LOG_DEBUG, "%s: unknown address family=%d.\n", 1213120585Ssam __func__, so->so_proto->pr_domain->dom_family)); 1214105197Ssam return; 1215105197Ssam } 1216105197Ssam} 1217105197Ssam 1218105197Ssamstatic void 1219105197Ssamkey_freesp_so(struct secpolicy **sp) 1220105197Ssam{ 1221120585Ssam IPSEC_ASSERT(sp != NULL && *sp != NULL, ("null sp")); 1222105197Ssam 1223105197Ssam if ((*sp)->policy == IPSEC_POLICY_ENTRUST || 1224105197Ssam (*sp)->policy == IPSEC_POLICY_BYPASS) 1225105197Ssam return; 1226105197Ssam 1227120585Ssam IPSEC_ASSERT((*sp)->policy == IPSEC_POLICY_IPSEC, 1228120585Ssam ("invalid policy %u", (*sp)->policy)); 1229105197Ssam KEY_FREESP(sp); 1230105197Ssam} 1231105197Ssam 1232105197Ssam/* 1233105197Ssam * Must be called after calling key_allocsa(). 1234105197Ssam * This function is called by key_freesp() to free some SA allocated 1235105197Ssam * for a policy. 1236105197Ssam */ 1237105197Ssamvoid 1238105197Ssamkey_freesav(struct secasvar **psav, const char* where, int tag) 1239105197Ssam{ 1240183550Szec INIT_VNET_IPSEC(curvnet); 1241105197Ssam struct secasvar *sav = *psav; 1242105197Ssam 1243120585Ssam IPSEC_ASSERT(sav != NULL, ("null sav")); 1244105197Ssam 1245158767Spjd if (sa_delref(sav)) { 1246158767Spjd KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 1247158767Spjd printf("DP %s SA:%p (SPI %u) from %s:%u; refcnt now %u\n", 1248158767Spjd __func__, sav, ntohl(sav->spi), where, tag, sav->refcnt)); 1249105197Ssam *psav = NULL; 1250105197Ssam key_delsav(sav); 1251158767Spjd } else { 1252158767Spjd KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 1253158767Spjd printf("DP %s SA:%p (SPI %u) from %s:%u; refcnt now %u\n", 1254158767Spjd __func__, sav, ntohl(sav->spi), where, tag, sav->refcnt)); 1255105197Ssam } 1256105197Ssam} 1257105197Ssam 1258105197Ssam/* %%% SPD management */ 1259105197Ssam/* 1260105197Ssam * free security policy entry. 1261105197Ssam */ 1262105197Ssamstatic void 1263105197Ssamkey_delsp(struct secpolicy *sp) 1264105197Ssam{ 1265119643Ssam struct ipsecrequest *isr, *nextisr; 1266105197Ssam 1267120585Ssam IPSEC_ASSERT(sp != NULL, ("null sp")); 1268120585Ssam SPTREE_LOCK_ASSERT(); 1269105197Ssam 1270105197Ssam sp->state = IPSEC_SPSTATE_DEAD; 1271105197Ssam 1272120585Ssam IPSEC_ASSERT(sp->refcnt == 0, 1273120585Ssam ("SP with references deleted (refcnt %u)", sp->refcnt)); 1274105197Ssam 1275105197Ssam /* remove from SP index */ 1276105197Ssam if (__LIST_CHAINED(sp)) 1277105197Ssam LIST_REMOVE(sp, chain); 1278105197Ssam 1279119643Ssam for (isr = sp->req; isr != NULL; isr = nextisr) { 1280105197Ssam if (isr->sav != NULL) { 1281105197Ssam KEY_FREESAV(&isr->sav); 1282105197Ssam isr->sav = NULL; 1283105197Ssam } 1284105197Ssam 1285105197Ssam nextisr = isr->next; 1286119643Ssam ipsec_delisr(isr); 1287105197Ssam } 1288119643Ssam _key_delsp(sp); 1289105197Ssam} 1290105197Ssam 1291105197Ssam/* 1292105197Ssam * search SPD 1293105197Ssam * OUT: NULL : not found 1294105197Ssam * others : found, pointer to a SP. 1295105197Ssam */ 1296105197Ssamstatic struct secpolicy * 1297105197Ssamkey_getsp(struct secpolicyindex *spidx) 1298105197Ssam{ 1299183550Szec INIT_VNET_IPSEC(curvnet); 1300105197Ssam struct secpolicy *sp; 1301105197Ssam 1302120585Ssam IPSEC_ASSERT(spidx != NULL, ("null spidx")); 1303105197Ssam 1304120585Ssam SPTREE_LOCK(); 1305181803Sbz LIST_FOREACH(sp, &V_sptree[spidx->dir], chain) { 1306105197Ssam if (sp->state == IPSEC_SPSTATE_DEAD) 1307105197Ssam continue; 1308105197Ssam if (key_cmpspidx_exactly(spidx, &sp->spidx)) { 1309105197Ssam SP_ADDREF(sp); 1310119643Ssam break; 1311105197Ssam } 1312105197Ssam } 1313120585Ssam SPTREE_UNLOCK(); 1314105197Ssam 1315119643Ssam return sp; 1316105197Ssam} 1317105197Ssam 1318105197Ssam/* 1319105197Ssam * get SP by index. 1320105197Ssam * OUT: NULL : not found 1321105197Ssam * others : found, pointer to a SP. 1322105197Ssam */ 1323105197Ssamstatic struct secpolicy * 1324105197Ssamkey_getspbyid(u_int32_t id) 1325105197Ssam{ 1326183550Szec INIT_VNET_IPSEC(curvnet); 1327105197Ssam struct secpolicy *sp; 1328105197Ssam 1329120585Ssam SPTREE_LOCK(); 1330181803Sbz LIST_FOREACH(sp, &V_sptree[IPSEC_DIR_INBOUND], chain) { 1331105197Ssam if (sp->state == IPSEC_SPSTATE_DEAD) 1332105197Ssam continue; 1333105197Ssam if (sp->id == id) { 1334105197Ssam SP_ADDREF(sp); 1335119643Ssam goto done; 1336105197Ssam } 1337105197Ssam } 1338105197Ssam 1339181803Sbz LIST_FOREACH(sp, &V_sptree[IPSEC_DIR_OUTBOUND], chain) { 1340105197Ssam if (sp->state == IPSEC_SPSTATE_DEAD) 1341105197Ssam continue; 1342105197Ssam if (sp->id == id) { 1343105197Ssam SP_ADDREF(sp); 1344119643Ssam goto done; 1345105197Ssam } 1346105197Ssam } 1347119643Ssamdone: 1348120585Ssam SPTREE_UNLOCK(); 1349105197Ssam 1350119643Ssam return sp; 1351105197Ssam} 1352105197Ssam 1353105197Ssamstruct secpolicy * 1354105197Ssamkey_newsp(const char* where, int tag) 1355105197Ssam{ 1356183550Szec INIT_VNET_IPSEC(curvnet); 1357105197Ssam struct secpolicy *newsp = NULL; 1358105197Ssam 1359105197Ssam newsp = (struct secpolicy *) 1360119643Ssam malloc(sizeof(struct secpolicy), M_IPSEC_SP, M_NOWAIT|M_ZERO); 1361105197Ssam if (newsp) { 1362120585Ssam SECPOLICY_LOCK_INIT(newsp); 1363105197Ssam newsp->refcnt = 1; 1364105197Ssam newsp->req = NULL; 1365105197Ssam } 1366105197Ssam 1367105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 1368120585Ssam printf("DP %s from %s:%u return SP:%p\n", __func__, 1369105197Ssam where, tag, newsp)); 1370105197Ssam return newsp; 1371105197Ssam} 1372105197Ssam 1373119643Ssamstatic void 1374119643Ssam_key_delsp(struct secpolicy *sp) 1375119643Ssam{ 1376120585Ssam SECPOLICY_LOCK_DESTROY(sp); 1377119643Ssam free(sp, M_IPSEC_SP); 1378119643Ssam} 1379119643Ssam 1380105197Ssam/* 1381105197Ssam * create secpolicy structure from sadb_x_policy structure. 1382105197Ssam * NOTE: `state', `secpolicyindex' in secpolicy structure are not set, 1383105197Ssam * so must be set properly later. 1384105197Ssam */ 1385105197Ssamstruct secpolicy * 1386105197Ssamkey_msg2sp(xpl0, len, error) 1387105197Ssam struct sadb_x_policy *xpl0; 1388105197Ssam size_t len; 1389105197Ssam int *error; 1390105197Ssam{ 1391183550Szec INIT_VNET_IPSEC(curvnet); 1392105197Ssam struct secpolicy *newsp; 1393105197Ssam 1394120585Ssam IPSEC_ASSERT(xpl0 != NULL, ("null xpl0")); 1395127972Spjd IPSEC_ASSERT(len >= sizeof(*xpl0), ("policy too short: %zu", len)); 1396120585Ssam 1397105197Ssam if (len != PFKEY_EXTLEN(xpl0)) { 1398120585Ssam ipseclog((LOG_DEBUG, "%s: Invalid msg length.\n", __func__)); 1399105197Ssam *error = EINVAL; 1400105197Ssam return NULL; 1401105197Ssam } 1402105197Ssam 1403105197Ssam if ((newsp = KEY_NEWSP()) == NULL) { 1404105197Ssam *error = ENOBUFS; 1405105197Ssam return NULL; 1406105197Ssam } 1407105197Ssam 1408105197Ssam newsp->spidx.dir = xpl0->sadb_x_policy_dir; 1409105197Ssam newsp->policy = xpl0->sadb_x_policy_type; 1410105197Ssam 1411105197Ssam /* check policy */ 1412105197Ssam switch (xpl0->sadb_x_policy_type) { 1413105197Ssam case IPSEC_POLICY_DISCARD: 1414105197Ssam case IPSEC_POLICY_NONE: 1415105197Ssam case IPSEC_POLICY_ENTRUST: 1416105197Ssam case IPSEC_POLICY_BYPASS: 1417105197Ssam newsp->req = NULL; 1418105197Ssam break; 1419105197Ssam 1420105197Ssam case IPSEC_POLICY_IPSEC: 1421105197Ssam { 1422105197Ssam int tlen; 1423105197Ssam struct sadb_x_ipsecrequest *xisr; 1424105197Ssam struct ipsecrequest **p_isr = &newsp->req; 1425105197Ssam 1426105197Ssam /* validity check */ 1427105197Ssam if (PFKEY_EXTLEN(xpl0) < sizeof(*xpl0)) { 1428120585Ssam ipseclog((LOG_DEBUG, "%s: Invalid msg length.\n", 1429120585Ssam __func__)); 1430105197Ssam KEY_FREESP(&newsp); 1431105197Ssam *error = EINVAL; 1432105197Ssam return NULL; 1433105197Ssam } 1434105197Ssam 1435105197Ssam tlen = PFKEY_EXTLEN(xpl0) - sizeof(*xpl0); 1436105197Ssam xisr = (struct sadb_x_ipsecrequest *)(xpl0 + 1); 1437105197Ssam 1438105197Ssam while (tlen > 0) { 1439105197Ssam /* length check */ 1440105197Ssam if (xisr->sadb_x_ipsecrequest_len < sizeof(*xisr)) { 1441120585Ssam ipseclog((LOG_DEBUG, "%s: invalid ipsecrequest " 1442120585Ssam "length.\n", __func__)); 1443105197Ssam KEY_FREESP(&newsp); 1444105197Ssam *error = EINVAL; 1445105197Ssam return NULL; 1446105197Ssam } 1447105197Ssam 1448105197Ssam /* allocate request buffer */ 1449119643Ssam /* NB: data structure is zero'd */ 1450119643Ssam *p_isr = ipsec_newisr(); 1451105197Ssam if ((*p_isr) == NULL) { 1452105197Ssam ipseclog((LOG_DEBUG, 1453120585Ssam "%s: No more memory.\n", __func__)); 1454105197Ssam KEY_FREESP(&newsp); 1455105197Ssam *error = ENOBUFS; 1456105197Ssam return NULL; 1457105197Ssam } 1458105197Ssam 1459105197Ssam /* set values */ 1460105197Ssam switch (xisr->sadb_x_ipsecrequest_proto) { 1461105197Ssam case IPPROTO_ESP: 1462105197Ssam case IPPROTO_AH: 1463105197Ssam case IPPROTO_IPCOMP: 1464105197Ssam break; 1465105197Ssam default: 1466105197Ssam ipseclog((LOG_DEBUG, 1467120585Ssam "%s: invalid proto type=%u\n", __func__, 1468105197Ssam xisr->sadb_x_ipsecrequest_proto)); 1469105197Ssam KEY_FREESP(&newsp); 1470105197Ssam *error = EPROTONOSUPPORT; 1471105197Ssam return NULL; 1472105197Ssam } 1473105197Ssam (*p_isr)->saidx.proto = xisr->sadb_x_ipsecrequest_proto; 1474105197Ssam 1475105197Ssam switch (xisr->sadb_x_ipsecrequest_mode) { 1476105197Ssam case IPSEC_MODE_TRANSPORT: 1477105197Ssam case IPSEC_MODE_TUNNEL: 1478105197Ssam break; 1479105197Ssam case IPSEC_MODE_ANY: 1480105197Ssam default: 1481105197Ssam ipseclog((LOG_DEBUG, 1482120585Ssam "%s: invalid mode=%u\n", __func__, 1483105197Ssam xisr->sadb_x_ipsecrequest_mode)); 1484105197Ssam KEY_FREESP(&newsp); 1485105197Ssam *error = EINVAL; 1486105197Ssam return NULL; 1487105197Ssam } 1488105197Ssam (*p_isr)->saidx.mode = xisr->sadb_x_ipsecrequest_mode; 1489105197Ssam 1490105197Ssam switch (xisr->sadb_x_ipsecrequest_level) { 1491105197Ssam case IPSEC_LEVEL_DEFAULT: 1492105197Ssam case IPSEC_LEVEL_USE: 1493105197Ssam case IPSEC_LEVEL_REQUIRE: 1494105197Ssam break; 1495105197Ssam case IPSEC_LEVEL_UNIQUE: 1496105197Ssam /* validity check */ 1497105197Ssam /* 1498105197Ssam * If range violation of reqid, kernel will 1499105197Ssam * update it, don't refuse it. 1500105197Ssam */ 1501105197Ssam if (xisr->sadb_x_ipsecrequest_reqid 1502105197Ssam > IPSEC_MANUAL_REQID_MAX) { 1503105197Ssam ipseclog((LOG_DEBUG, 1504120585Ssam "%s: reqid=%d range " 1505105197Ssam "violation, updated by kernel.\n", 1506120585Ssam __func__, 1507105197Ssam xisr->sadb_x_ipsecrequest_reqid)); 1508105197Ssam xisr->sadb_x_ipsecrequest_reqid = 0; 1509105197Ssam } 1510105197Ssam 1511105197Ssam /* allocate new reqid id if reqid is zero. */ 1512105197Ssam if (xisr->sadb_x_ipsecrequest_reqid == 0) { 1513105197Ssam u_int32_t reqid; 1514105197Ssam if ((reqid = key_newreqid()) == 0) { 1515105197Ssam KEY_FREESP(&newsp); 1516105197Ssam *error = ENOBUFS; 1517105197Ssam return NULL; 1518105197Ssam } 1519105197Ssam (*p_isr)->saidx.reqid = reqid; 1520105197Ssam xisr->sadb_x_ipsecrequest_reqid = reqid; 1521105197Ssam } else { 1522105197Ssam /* set it for manual keying. */ 1523105197Ssam (*p_isr)->saidx.reqid = 1524105197Ssam xisr->sadb_x_ipsecrequest_reqid; 1525105197Ssam } 1526105197Ssam break; 1527105197Ssam 1528105197Ssam default: 1529120585Ssam ipseclog((LOG_DEBUG, "%s: invalid level=%u\n", 1530120585Ssam __func__, 1531105197Ssam xisr->sadb_x_ipsecrequest_level)); 1532105197Ssam KEY_FREESP(&newsp); 1533105197Ssam *error = EINVAL; 1534105197Ssam return NULL; 1535105197Ssam } 1536105197Ssam (*p_isr)->level = xisr->sadb_x_ipsecrequest_level; 1537105197Ssam 1538105197Ssam /* set IP addresses if there */ 1539105197Ssam if (xisr->sadb_x_ipsecrequest_len > sizeof(*xisr)) { 1540105197Ssam struct sockaddr *paddr; 1541105197Ssam 1542105197Ssam paddr = (struct sockaddr *)(xisr + 1); 1543105197Ssam 1544105197Ssam /* validity check */ 1545105197Ssam if (paddr->sa_len 1546105197Ssam > sizeof((*p_isr)->saidx.src)) { 1547120585Ssam ipseclog((LOG_DEBUG, "%s: invalid " 1548120585Ssam "request address length.\n", 1549120585Ssam __func__)); 1550105197Ssam KEY_FREESP(&newsp); 1551105197Ssam *error = EINVAL; 1552105197Ssam return NULL; 1553105197Ssam } 1554105197Ssam bcopy(paddr, &(*p_isr)->saidx.src, 1555105197Ssam paddr->sa_len); 1556105197Ssam 1557105197Ssam paddr = (struct sockaddr *)((caddr_t)paddr 1558105197Ssam + paddr->sa_len); 1559105197Ssam 1560105197Ssam /* validity check */ 1561105197Ssam if (paddr->sa_len 1562105197Ssam > sizeof((*p_isr)->saidx.dst)) { 1563120585Ssam ipseclog((LOG_DEBUG, "%s: invalid " 1564120585Ssam "request address length.\n", 1565120585Ssam __func__)); 1566105197Ssam KEY_FREESP(&newsp); 1567105197Ssam *error = EINVAL; 1568105197Ssam return NULL; 1569105197Ssam } 1570105197Ssam bcopy(paddr, &(*p_isr)->saidx.dst, 1571105197Ssam paddr->sa_len); 1572105197Ssam } 1573105197Ssam 1574105197Ssam (*p_isr)->sp = newsp; 1575105197Ssam 1576105197Ssam /* initialization for the next. */ 1577105197Ssam p_isr = &(*p_isr)->next; 1578105197Ssam tlen -= xisr->sadb_x_ipsecrequest_len; 1579105197Ssam 1580105197Ssam /* validity check */ 1581105197Ssam if (tlen < 0) { 1582120585Ssam ipseclog((LOG_DEBUG, "%s: becoming tlen < 0.\n", 1583120585Ssam __func__)); 1584105197Ssam KEY_FREESP(&newsp); 1585105197Ssam *error = EINVAL; 1586105197Ssam return NULL; 1587105197Ssam } 1588105197Ssam 1589105197Ssam xisr = (struct sadb_x_ipsecrequest *)((caddr_t)xisr 1590105197Ssam + xisr->sadb_x_ipsecrequest_len); 1591105197Ssam } 1592105197Ssam } 1593105197Ssam break; 1594105197Ssam default: 1595120585Ssam ipseclog((LOG_DEBUG, "%s: invalid policy type.\n", __func__)); 1596105197Ssam KEY_FREESP(&newsp); 1597105197Ssam *error = EINVAL; 1598105197Ssam return NULL; 1599105197Ssam } 1600105197Ssam 1601105197Ssam *error = 0; 1602105197Ssam return newsp; 1603105197Ssam} 1604105197Ssam 1605105197Ssamstatic u_int32_t 1606105197Ssamkey_newreqid() 1607105197Ssam{ 1608105197Ssam static u_int32_t auto_reqid = IPSEC_MANUAL_REQID_MAX + 1; 1609105197Ssam 1610105197Ssam auto_reqid = (auto_reqid == ~0 1611105197Ssam ? IPSEC_MANUAL_REQID_MAX + 1 : auto_reqid + 1); 1612105197Ssam 1613105197Ssam /* XXX should be unique check */ 1614105197Ssam 1615105197Ssam return auto_reqid; 1616105197Ssam} 1617105197Ssam 1618105197Ssam/* 1619105197Ssam * copy secpolicy struct to sadb_x_policy structure indicated. 1620105197Ssam */ 1621105197Ssamstruct mbuf * 1622105197Ssamkey_sp2msg(sp) 1623105197Ssam struct secpolicy *sp; 1624105197Ssam{ 1625105197Ssam struct sadb_x_policy *xpl; 1626105197Ssam int tlen; 1627105197Ssam caddr_t p; 1628105197Ssam struct mbuf *m; 1629105197Ssam 1630120585Ssam IPSEC_ASSERT(sp != NULL, ("null policy")); 1631105197Ssam 1632105197Ssam tlen = key_getspreqmsglen(sp); 1633105197Ssam 1634105197Ssam m = key_alloc_mbuf(tlen); 1635105197Ssam if (!m || m->m_next) { /*XXX*/ 1636105197Ssam if (m) 1637105197Ssam m_freem(m); 1638105197Ssam return NULL; 1639105197Ssam } 1640105197Ssam 1641105197Ssam m->m_len = tlen; 1642105197Ssam m->m_next = NULL; 1643105197Ssam xpl = mtod(m, struct sadb_x_policy *); 1644105197Ssam bzero(xpl, tlen); 1645105197Ssam 1646105197Ssam xpl->sadb_x_policy_len = PFKEY_UNIT64(tlen); 1647105197Ssam xpl->sadb_x_policy_exttype = SADB_X_EXT_POLICY; 1648105197Ssam xpl->sadb_x_policy_type = sp->policy; 1649105197Ssam xpl->sadb_x_policy_dir = sp->spidx.dir; 1650105197Ssam xpl->sadb_x_policy_id = sp->id; 1651105197Ssam p = (caddr_t)xpl + sizeof(*xpl); 1652105197Ssam 1653105197Ssam /* if is the policy for ipsec ? */ 1654105197Ssam if (sp->policy == IPSEC_POLICY_IPSEC) { 1655105197Ssam struct sadb_x_ipsecrequest *xisr; 1656105197Ssam struct ipsecrequest *isr; 1657105197Ssam 1658105197Ssam for (isr = sp->req; isr != NULL; isr = isr->next) { 1659105197Ssam 1660105197Ssam xisr = (struct sadb_x_ipsecrequest *)p; 1661105197Ssam 1662105197Ssam xisr->sadb_x_ipsecrequest_proto = isr->saidx.proto; 1663105197Ssam xisr->sadb_x_ipsecrequest_mode = isr->saidx.mode; 1664105197Ssam xisr->sadb_x_ipsecrequest_level = isr->level; 1665105197Ssam xisr->sadb_x_ipsecrequest_reqid = isr->saidx.reqid; 1666105197Ssam 1667105197Ssam p += sizeof(*xisr); 1668105197Ssam bcopy(&isr->saidx.src, p, isr->saidx.src.sa.sa_len); 1669105197Ssam p += isr->saidx.src.sa.sa_len; 1670105197Ssam bcopy(&isr->saidx.dst, p, isr->saidx.dst.sa.sa_len); 1671105197Ssam p += isr->saidx.src.sa.sa_len; 1672105197Ssam 1673105197Ssam xisr->sadb_x_ipsecrequest_len = 1674105197Ssam PFKEY_ALIGN8(sizeof(*xisr) 1675105197Ssam + isr->saidx.src.sa.sa_len 1676105197Ssam + isr->saidx.dst.sa.sa_len); 1677105197Ssam } 1678105197Ssam } 1679105197Ssam 1680105197Ssam return m; 1681105197Ssam} 1682105197Ssam 1683105197Ssam/* m will not be freed nor modified */ 1684105197Ssamstatic struct mbuf * 1685105197Ssam#ifdef __STDC__ 1686105197Ssamkey_gather_mbuf(struct mbuf *m, const struct sadb_msghdr *mhp, 1687105197Ssam int ndeep, int nitem, ...) 1688105197Ssam#else 1689105197Ssamkey_gather_mbuf(m, mhp, ndeep, nitem, va_alist) 1690105197Ssam struct mbuf *m; 1691105197Ssam const struct sadb_msghdr *mhp; 1692105197Ssam int ndeep; 1693105197Ssam int nitem; 1694105197Ssam va_dcl 1695105197Ssam#endif 1696105197Ssam{ 1697105197Ssam va_list ap; 1698105197Ssam int idx; 1699105197Ssam int i; 1700105197Ssam struct mbuf *result = NULL, *n; 1701105197Ssam int len; 1702105197Ssam 1703120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 1704120585Ssam IPSEC_ASSERT(mhp != NULL, ("null msghdr")); 1705105197Ssam 1706105197Ssam va_start(ap, nitem); 1707105197Ssam for (i = 0; i < nitem; i++) { 1708105197Ssam idx = va_arg(ap, int); 1709105197Ssam if (idx < 0 || idx > SADB_EXT_MAX) 1710105197Ssam goto fail; 1711105197Ssam /* don't attempt to pull empty extension */ 1712105197Ssam if (idx == SADB_EXT_RESERVED && mhp->msg == NULL) 1713105197Ssam continue; 1714105197Ssam if (idx != SADB_EXT_RESERVED && 1715105197Ssam (mhp->ext[idx] == NULL || mhp->extlen[idx] == 0)) 1716105197Ssam continue; 1717105197Ssam 1718105197Ssam if (idx == SADB_EXT_RESERVED) { 1719105197Ssam len = PFKEY_ALIGN8(sizeof(struct sadb_msg)); 1720120585Ssam 1721120585Ssam IPSEC_ASSERT(len <= MHLEN, ("header too big %u", len)); 1722120585Ssam 1723111119Simp MGETHDR(n, M_DONTWAIT, MT_DATA); 1724105197Ssam if (!n) 1725105197Ssam goto fail; 1726105197Ssam n->m_len = len; 1727105197Ssam n->m_next = NULL; 1728105197Ssam m_copydata(m, 0, sizeof(struct sadb_msg), 1729105197Ssam mtod(n, caddr_t)); 1730105197Ssam } else if (i < ndeep) { 1731105197Ssam len = mhp->extlen[idx]; 1732105197Ssam n = key_alloc_mbuf(len); 1733105197Ssam if (!n || n->m_next) { /*XXX*/ 1734105197Ssam if (n) 1735105197Ssam m_freem(n); 1736105197Ssam goto fail; 1737105197Ssam } 1738105197Ssam m_copydata(m, mhp->extoff[idx], mhp->extlen[idx], 1739105197Ssam mtod(n, caddr_t)); 1740105197Ssam } else { 1741105197Ssam n = m_copym(m, mhp->extoff[idx], mhp->extlen[idx], 1742111119Simp M_DONTWAIT); 1743105197Ssam } 1744105197Ssam if (n == NULL) 1745105197Ssam goto fail; 1746105197Ssam 1747105197Ssam if (result) 1748105197Ssam m_cat(result, n); 1749105197Ssam else 1750105197Ssam result = n; 1751105197Ssam } 1752105197Ssam va_end(ap); 1753105197Ssam 1754105197Ssam if ((result->m_flags & M_PKTHDR) != 0) { 1755105197Ssam result->m_pkthdr.len = 0; 1756105197Ssam for (n = result; n; n = n->m_next) 1757105197Ssam result->m_pkthdr.len += n->m_len; 1758105197Ssam } 1759105197Ssam 1760105197Ssam return result; 1761105197Ssam 1762105197Ssamfail: 1763105197Ssam m_freem(result); 1764105197Ssam return NULL; 1765105197Ssam} 1766105197Ssam 1767105197Ssam/* 1768105197Ssam * SADB_X_SPDADD, SADB_X_SPDSETIDX or SADB_X_SPDUPDATE processing 1769108533Sschweikh * add an entry to SP database, when received 1770105197Ssam * <base, address(SD), (lifetime(H),) policy> 1771105197Ssam * from the user(?). 1772105197Ssam * Adding to SP database, 1773105197Ssam * and send 1774105197Ssam * <base, address(SD), (lifetime(H),) policy> 1775105197Ssam * to the socket which was send. 1776105197Ssam * 1777105197Ssam * SPDADD set a unique policy entry. 1778105197Ssam * SPDSETIDX like SPDADD without a part of policy requests. 1779105197Ssam * SPDUPDATE replace a unique policy entry. 1780105197Ssam * 1781105197Ssam * m will always be freed. 1782105197Ssam */ 1783105197Ssamstatic int 1784105197Ssamkey_spdadd(so, m, mhp) 1785105197Ssam struct socket *so; 1786105197Ssam struct mbuf *m; 1787105197Ssam const struct sadb_msghdr *mhp; 1788105197Ssam{ 1789183550Szec INIT_VNET_IPSEC(curvnet); 1790105197Ssam struct sadb_address *src0, *dst0; 1791105197Ssam struct sadb_x_policy *xpl0, *xpl; 1792105197Ssam struct sadb_lifetime *lft = NULL; 1793105197Ssam struct secpolicyindex spidx; 1794105197Ssam struct secpolicy *newsp; 1795105197Ssam int error; 1796105197Ssam 1797120585Ssam IPSEC_ASSERT(so != NULL, ("null socket")); 1798120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 1799120585Ssam IPSEC_ASSERT(mhp != NULL, ("null msghdr")); 1800120585Ssam IPSEC_ASSERT(mhp->msg != NULL, ("null msg")); 1801105197Ssam 1802105197Ssam if (mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL || 1803105197Ssam mhp->ext[SADB_EXT_ADDRESS_DST] == NULL || 1804105197Ssam mhp->ext[SADB_X_EXT_POLICY] == NULL) { 1805105197Ssam ipseclog((LOG_DEBUG, "key_spdadd: invalid message is passed.\n")); 1806105197Ssam return key_senderror(so, m, EINVAL); 1807105197Ssam } 1808105197Ssam if (mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) || 1809105197Ssam mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address) || 1810105197Ssam mhp->extlen[SADB_X_EXT_POLICY] < sizeof(struct sadb_x_policy)) { 1811120585Ssam ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n", 1812120585Ssam __func__)); 1813105197Ssam return key_senderror(so, m, EINVAL); 1814105197Ssam } 1815105197Ssam if (mhp->ext[SADB_EXT_LIFETIME_HARD] != NULL) { 1816105197Ssam if (mhp->extlen[SADB_EXT_LIFETIME_HARD] 1817105197Ssam < sizeof(struct sadb_lifetime)) { 1818120585Ssam ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n", 1819120585Ssam __func__)); 1820105197Ssam return key_senderror(so, m, EINVAL); 1821105197Ssam } 1822105197Ssam lft = (struct sadb_lifetime *)mhp->ext[SADB_EXT_LIFETIME_HARD]; 1823105197Ssam } 1824105197Ssam 1825105197Ssam src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC]; 1826105197Ssam dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST]; 1827105197Ssam xpl0 = (struct sadb_x_policy *)mhp->ext[SADB_X_EXT_POLICY]; 1828105197Ssam 1829194062Svanhu /* 1830194062Svanhu * Note: do not parse SADB_X_EXT_NAT_T_* here: 1831194062Svanhu * we are processing traffic endpoints. 1832194062Svanhu */ 1833194062Svanhu 1834105197Ssam /* make secindex */ 1835105197Ssam /* XXX boundary check against sa_len */ 1836105197Ssam KEY_SETSECSPIDX(xpl0->sadb_x_policy_dir, 1837105197Ssam src0 + 1, 1838105197Ssam dst0 + 1, 1839105197Ssam src0->sadb_address_prefixlen, 1840105197Ssam dst0->sadb_address_prefixlen, 1841105197Ssam src0->sadb_address_proto, 1842105197Ssam &spidx); 1843105197Ssam 1844105197Ssam /* checking the direciton. */ 1845105197Ssam switch (xpl0->sadb_x_policy_dir) { 1846105197Ssam case IPSEC_DIR_INBOUND: 1847105197Ssam case IPSEC_DIR_OUTBOUND: 1848105197Ssam break; 1849105197Ssam default: 1850120585Ssam ipseclog((LOG_DEBUG, "%s: Invalid SP direction.\n", __func__)); 1851105197Ssam mhp->msg->sadb_msg_errno = EINVAL; 1852105197Ssam return 0; 1853105197Ssam } 1854105197Ssam 1855105197Ssam /* check policy */ 1856105197Ssam /* key_spdadd() accepts DISCARD, NONE and IPSEC. */ 1857105197Ssam if (xpl0->sadb_x_policy_type == IPSEC_POLICY_ENTRUST 1858105197Ssam || xpl0->sadb_x_policy_type == IPSEC_POLICY_BYPASS) { 1859120585Ssam ipseclog((LOG_DEBUG, "%s: Invalid policy type.\n", __func__)); 1860105197Ssam return key_senderror(so, m, EINVAL); 1861105197Ssam } 1862105197Ssam 1863105197Ssam /* policy requests are mandatory when action is ipsec. */ 1864105197Ssam if (mhp->msg->sadb_msg_type != SADB_X_SPDSETIDX 1865105197Ssam && xpl0->sadb_x_policy_type == IPSEC_POLICY_IPSEC 1866105197Ssam && mhp->extlen[SADB_X_EXT_POLICY] <= sizeof(*xpl0)) { 1867120585Ssam ipseclog((LOG_DEBUG, "%s: some policy requests part required\n", 1868120585Ssam __func__)); 1869105197Ssam return key_senderror(so, m, EINVAL); 1870105197Ssam } 1871105197Ssam 1872105197Ssam /* 1873105197Ssam * checking there is SP already or not. 1874105197Ssam * SPDUPDATE doesn't depend on whether there is a SP or not. 1875105197Ssam * If the type is either SPDADD or SPDSETIDX AND a SP is found, 1876105197Ssam * then error. 1877105197Ssam */ 1878105197Ssam newsp = key_getsp(&spidx); 1879105197Ssam if (mhp->msg->sadb_msg_type == SADB_X_SPDUPDATE) { 1880105197Ssam if (newsp) { 1881105197Ssam newsp->state = IPSEC_SPSTATE_DEAD; 1882105197Ssam KEY_FREESP(&newsp); 1883105197Ssam } 1884105197Ssam } else { 1885105197Ssam if (newsp != NULL) { 1886105197Ssam KEY_FREESP(&newsp); 1887120585Ssam ipseclog((LOG_DEBUG, "%s: a SP entry exists already.\n", 1888120585Ssam __func__)); 1889105197Ssam return key_senderror(so, m, EEXIST); 1890105197Ssam } 1891105197Ssam } 1892105197Ssam 1893105197Ssam /* allocation new SP entry */ 1894105197Ssam if ((newsp = key_msg2sp(xpl0, PFKEY_EXTLEN(xpl0), &error)) == NULL) { 1895105197Ssam return key_senderror(so, m, error); 1896105197Ssam } 1897105197Ssam 1898105197Ssam if ((newsp->id = key_getnewspid()) == 0) { 1899119643Ssam _key_delsp(newsp); 1900105197Ssam return key_senderror(so, m, ENOBUFS); 1901105197Ssam } 1902105197Ssam 1903105197Ssam /* XXX boundary check against sa_len */ 1904105197Ssam KEY_SETSECSPIDX(xpl0->sadb_x_policy_dir, 1905105197Ssam src0 + 1, 1906105197Ssam dst0 + 1, 1907105197Ssam src0->sadb_address_prefixlen, 1908105197Ssam dst0->sadb_address_prefixlen, 1909105197Ssam src0->sadb_address_proto, 1910105197Ssam &newsp->spidx); 1911105197Ssam 1912105197Ssam /* sanity check on addr pair */ 1913105197Ssam if (((struct sockaddr *)(src0 + 1))->sa_family != 1914105197Ssam ((struct sockaddr *)(dst0+ 1))->sa_family) { 1915119643Ssam _key_delsp(newsp); 1916105197Ssam return key_senderror(so, m, EINVAL); 1917105197Ssam } 1918105197Ssam if (((struct sockaddr *)(src0 + 1))->sa_len != 1919105197Ssam ((struct sockaddr *)(dst0+ 1))->sa_len) { 1920119643Ssam _key_delsp(newsp); 1921105197Ssam return key_senderror(so, m, EINVAL); 1922105197Ssam } 1923105197Ssam#if 1 1924105197Ssam if (newsp->req && newsp->req->saidx.src.sa.sa_family) { 1925105197Ssam struct sockaddr *sa; 1926105197Ssam sa = (struct sockaddr *)(src0 + 1); 1927105197Ssam if (sa->sa_family != newsp->req->saidx.src.sa.sa_family) { 1928119643Ssam _key_delsp(newsp); 1929105197Ssam return key_senderror(so, m, EINVAL); 1930105197Ssam } 1931105197Ssam } 1932105197Ssam if (newsp->req && newsp->req->saidx.dst.sa.sa_family) { 1933105197Ssam struct sockaddr *sa; 1934105197Ssam sa = (struct sockaddr *)(dst0 + 1); 1935105197Ssam if (sa->sa_family != newsp->req->saidx.dst.sa.sa_family) { 1936119643Ssam _key_delsp(newsp); 1937105197Ssam return key_senderror(so, m, EINVAL); 1938105197Ssam } 1939105197Ssam } 1940105197Ssam#endif 1941105197Ssam 1942105197Ssam newsp->created = time_second; 1943105197Ssam newsp->lastused = newsp->created; 1944105197Ssam newsp->lifetime = lft ? lft->sadb_lifetime_addtime : 0; 1945105197Ssam newsp->validtime = lft ? lft->sadb_lifetime_usetime : 0; 1946105197Ssam 1947105197Ssam newsp->refcnt = 1; /* do not reclaim until I say I do */ 1948105197Ssam newsp->state = IPSEC_SPSTATE_ALIVE; 1949181803Sbz LIST_INSERT_TAIL(&V_sptree[newsp->spidx.dir], newsp, secpolicy, chain); 1950105197Ssam 1951105197Ssam /* delete the entry in spacqtree */ 1952105197Ssam if (mhp->msg->sadb_msg_type == SADB_X_SPDUPDATE) { 1953119643Ssam struct secspacq *spacq = key_getspacq(&spidx); 1954119643Ssam if (spacq != NULL) { 1955105197Ssam /* reset counter in order to deletion by timehandler. */ 1956105197Ssam spacq->created = time_second; 1957105197Ssam spacq->count = 0; 1958120585Ssam SPACQ_UNLOCK(); 1959105197Ssam } 1960105197Ssam } 1961105197Ssam 1962105197Ssam { 1963105197Ssam struct mbuf *n, *mpolicy; 1964105197Ssam struct sadb_msg *newmsg; 1965105197Ssam int off; 1966105197Ssam 1967194062Svanhu /* 1968194062Svanhu * Note: do not send SADB_X_EXT_NAT_T_* here: 1969194062Svanhu * we are sending traffic endpoints. 1970194062Svanhu */ 1971194062Svanhu 1972105197Ssam /* create new sadb_msg to reply. */ 1973105197Ssam if (lft) { 1974105197Ssam n = key_gather_mbuf(m, mhp, 2, 5, SADB_EXT_RESERVED, 1975105197Ssam SADB_X_EXT_POLICY, SADB_EXT_LIFETIME_HARD, 1976105197Ssam SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST); 1977105197Ssam } else { 1978105197Ssam n = key_gather_mbuf(m, mhp, 2, 4, SADB_EXT_RESERVED, 1979105197Ssam SADB_X_EXT_POLICY, 1980105197Ssam SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST); 1981105197Ssam } 1982105197Ssam if (!n) 1983105197Ssam return key_senderror(so, m, ENOBUFS); 1984105197Ssam 1985105197Ssam if (n->m_len < sizeof(*newmsg)) { 1986105197Ssam n = m_pullup(n, sizeof(*newmsg)); 1987105197Ssam if (!n) 1988105197Ssam return key_senderror(so, m, ENOBUFS); 1989105197Ssam } 1990105197Ssam newmsg = mtod(n, struct sadb_msg *); 1991105197Ssam newmsg->sadb_msg_errno = 0; 1992105197Ssam newmsg->sadb_msg_len = PFKEY_UNIT64(n->m_pkthdr.len); 1993105197Ssam 1994105197Ssam off = 0; 1995105197Ssam mpolicy = m_pulldown(n, PFKEY_ALIGN8(sizeof(struct sadb_msg)), 1996105197Ssam sizeof(*xpl), &off); 1997105197Ssam if (mpolicy == NULL) { 1998105197Ssam /* n is already freed */ 1999105197Ssam return key_senderror(so, m, ENOBUFS); 2000105197Ssam } 2001105197Ssam xpl = (struct sadb_x_policy *)(mtod(mpolicy, caddr_t) + off); 2002105197Ssam if (xpl->sadb_x_policy_exttype != SADB_X_EXT_POLICY) { 2003105197Ssam m_freem(n); 2004105197Ssam return key_senderror(so, m, EINVAL); 2005105197Ssam } 2006105197Ssam xpl->sadb_x_policy_id = newsp->id; 2007105197Ssam 2008105197Ssam m_freem(m); 2009105197Ssam return key_sendup_mbuf(so, n, KEY_SENDUP_ALL); 2010105197Ssam } 2011105197Ssam} 2012105197Ssam 2013105197Ssam/* 2014105197Ssam * get new policy id. 2015105197Ssam * OUT: 2016105197Ssam * 0: failure. 2017105197Ssam * others: success. 2018105197Ssam */ 2019105197Ssamstatic u_int32_t 2020105197Ssamkey_getnewspid() 2021105197Ssam{ 2022183550Szec INIT_VNET_IPSEC(curvnet); 2023105197Ssam u_int32_t newid = 0; 2024181803Sbz int count = V_key_spi_trycnt; /* XXX */ 2025105197Ssam struct secpolicy *sp; 2026105197Ssam 2027105197Ssam /* when requesting to allocate spi ranged */ 2028105197Ssam while (count--) { 2029181803Sbz newid = (V_policy_id = (V_policy_id == ~0 ? 1 : V_policy_id + 1)); 2030105197Ssam 2031105197Ssam if ((sp = key_getspbyid(newid)) == NULL) 2032105197Ssam break; 2033105197Ssam 2034105197Ssam KEY_FREESP(&sp); 2035105197Ssam } 2036105197Ssam 2037105197Ssam if (count == 0 || newid == 0) { 2038120585Ssam ipseclog((LOG_DEBUG, "%s: to allocate policy id is failed.\n", 2039120585Ssam __func__)); 2040105197Ssam return 0; 2041105197Ssam } 2042105197Ssam 2043105197Ssam return newid; 2044105197Ssam} 2045105197Ssam 2046105197Ssam/* 2047105197Ssam * SADB_SPDDELETE processing 2048105197Ssam * receive 2049105197Ssam * <base, address(SD), policy(*)> 2050105197Ssam * from the user(?), and set SADB_SASTATE_DEAD, 2051105197Ssam * and send, 2052105197Ssam * <base, address(SD), policy(*)> 2053105197Ssam * to the ikmpd. 2054105197Ssam * policy(*) including direction of policy. 2055105197Ssam * 2056105197Ssam * m will always be freed. 2057105197Ssam */ 2058105197Ssamstatic int 2059105197Ssamkey_spddelete(so, m, mhp) 2060105197Ssam struct socket *so; 2061105197Ssam struct mbuf *m; 2062105197Ssam const struct sadb_msghdr *mhp; 2063105197Ssam{ 2064183550Szec INIT_VNET_IPSEC(curvnet); 2065105197Ssam struct sadb_address *src0, *dst0; 2066105197Ssam struct sadb_x_policy *xpl0; 2067105197Ssam struct secpolicyindex spidx; 2068105197Ssam struct secpolicy *sp; 2069105197Ssam 2070120585Ssam IPSEC_ASSERT(so != NULL, ("null so")); 2071120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 2072120585Ssam IPSEC_ASSERT(mhp != NULL, ("null msghdr")); 2073120585Ssam IPSEC_ASSERT(mhp->msg != NULL, ("null msg")); 2074105197Ssam 2075105197Ssam if (mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL || 2076105197Ssam mhp->ext[SADB_EXT_ADDRESS_DST] == NULL || 2077105197Ssam mhp->ext[SADB_X_EXT_POLICY] == NULL) { 2078120585Ssam ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n", 2079120585Ssam __func__)); 2080105197Ssam return key_senderror(so, m, EINVAL); 2081105197Ssam } 2082105197Ssam if (mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) || 2083105197Ssam mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address) || 2084105197Ssam mhp->extlen[SADB_X_EXT_POLICY] < sizeof(struct sadb_x_policy)) { 2085120585Ssam ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n", 2086120585Ssam __func__)); 2087105197Ssam return key_senderror(so, m, EINVAL); 2088105197Ssam } 2089105197Ssam 2090105197Ssam src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC]; 2091105197Ssam dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST]; 2092105197Ssam xpl0 = (struct sadb_x_policy *)mhp->ext[SADB_X_EXT_POLICY]; 2093105197Ssam 2094194062Svanhu /* 2095194062Svanhu * Note: do not parse SADB_X_EXT_NAT_T_* here: 2096194062Svanhu * we are processing traffic endpoints. 2097194062Svanhu */ 2098194062Svanhu 2099105197Ssam /* make secindex */ 2100105197Ssam /* XXX boundary check against sa_len */ 2101105197Ssam KEY_SETSECSPIDX(xpl0->sadb_x_policy_dir, 2102105197Ssam src0 + 1, 2103105197Ssam dst0 + 1, 2104105197Ssam src0->sadb_address_prefixlen, 2105105197Ssam dst0->sadb_address_prefixlen, 2106105197Ssam src0->sadb_address_proto, 2107105197Ssam &spidx); 2108105197Ssam 2109105197Ssam /* checking the direciton. */ 2110105197Ssam switch (xpl0->sadb_x_policy_dir) { 2111105197Ssam case IPSEC_DIR_INBOUND: 2112105197Ssam case IPSEC_DIR_OUTBOUND: 2113105197Ssam break; 2114105197Ssam default: 2115120585Ssam ipseclog((LOG_DEBUG, "%s: Invalid SP direction.\n", __func__)); 2116105197Ssam return key_senderror(so, m, EINVAL); 2117105197Ssam } 2118105197Ssam 2119105197Ssam /* Is there SP in SPD ? */ 2120105197Ssam if ((sp = key_getsp(&spidx)) == NULL) { 2121120585Ssam ipseclog((LOG_DEBUG, "%s: no SP found.\n", __func__)); 2122105197Ssam return key_senderror(so, m, EINVAL); 2123105197Ssam } 2124105197Ssam 2125105197Ssam /* save policy id to buffer to be returned. */ 2126105197Ssam xpl0->sadb_x_policy_id = sp->id; 2127105197Ssam 2128105197Ssam sp->state = IPSEC_SPSTATE_DEAD; 2129105197Ssam KEY_FREESP(&sp); 2130105197Ssam 2131105197Ssam { 2132105197Ssam struct mbuf *n; 2133105197Ssam struct sadb_msg *newmsg; 2134105197Ssam 2135194062Svanhu /* 2136194062Svanhu * Note: do not send SADB_X_EXT_NAT_T_* here: 2137194062Svanhu * we are sending traffic endpoints. 2138194062Svanhu */ 2139194062Svanhu 2140105197Ssam /* create new sadb_msg to reply. */ 2141105197Ssam n = key_gather_mbuf(m, mhp, 1, 4, SADB_EXT_RESERVED, 2142105197Ssam SADB_X_EXT_POLICY, SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST); 2143105197Ssam if (!n) 2144105197Ssam return key_senderror(so, m, ENOBUFS); 2145105197Ssam 2146105197Ssam newmsg = mtod(n, struct sadb_msg *); 2147105197Ssam newmsg->sadb_msg_errno = 0; 2148105197Ssam newmsg->sadb_msg_len = PFKEY_UNIT64(n->m_pkthdr.len); 2149105197Ssam 2150105197Ssam m_freem(m); 2151105197Ssam return key_sendup_mbuf(so, n, KEY_SENDUP_ALL); 2152105197Ssam } 2153105197Ssam} 2154105197Ssam 2155105197Ssam/* 2156105197Ssam * SADB_SPDDELETE2 processing 2157105197Ssam * receive 2158105197Ssam * <base, policy(*)> 2159105197Ssam * from the user(?), and set SADB_SASTATE_DEAD, 2160105197Ssam * and send, 2161105197Ssam * <base, policy(*)> 2162105197Ssam * to the ikmpd. 2163105197Ssam * policy(*) including direction of policy. 2164105197Ssam * 2165105197Ssam * m will always be freed. 2166105197Ssam */ 2167105197Ssamstatic int 2168105197Ssamkey_spddelete2(so, m, mhp) 2169105197Ssam struct socket *so; 2170105197Ssam struct mbuf *m; 2171105197Ssam const struct sadb_msghdr *mhp; 2172105197Ssam{ 2173183550Szec INIT_VNET_IPSEC(curvnet); 2174105197Ssam u_int32_t id; 2175105197Ssam struct secpolicy *sp; 2176105197Ssam 2177120585Ssam IPSEC_ASSERT(so != NULL, ("null socket")); 2178120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 2179120585Ssam IPSEC_ASSERT(mhp != NULL, ("null msghdr")); 2180120585Ssam IPSEC_ASSERT(mhp->msg != NULL, ("null msg")); 2181105197Ssam 2182105197Ssam if (mhp->ext[SADB_X_EXT_POLICY] == NULL || 2183105197Ssam mhp->extlen[SADB_X_EXT_POLICY] < sizeof(struct sadb_x_policy)) { 2184120585Ssam ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n", __func__)); 2185170803Sbz return key_senderror(so, m, EINVAL); 2186105197Ssam } 2187105197Ssam 2188105197Ssam id = ((struct sadb_x_policy *)mhp->ext[SADB_X_EXT_POLICY])->sadb_x_policy_id; 2189105197Ssam 2190105197Ssam /* Is there SP in SPD ? */ 2191105197Ssam if ((sp = key_getspbyid(id)) == NULL) { 2192120585Ssam ipseclog((LOG_DEBUG, "%s: no SP found id:%u.\n", __func__, id)); 2193170803Sbz return key_senderror(so, m, EINVAL); 2194105197Ssam } 2195105197Ssam 2196105197Ssam sp->state = IPSEC_SPSTATE_DEAD; 2197105197Ssam KEY_FREESP(&sp); 2198105197Ssam 2199105197Ssam { 2200105197Ssam struct mbuf *n, *nn; 2201105197Ssam struct sadb_msg *newmsg; 2202105197Ssam int off, len; 2203105197Ssam 2204105197Ssam /* create new sadb_msg to reply. */ 2205105197Ssam len = PFKEY_ALIGN8(sizeof(struct sadb_msg)); 2206105197Ssam 2207111119Simp MGETHDR(n, M_DONTWAIT, MT_DATA); 2208105197Ssam if (n && len > MHLEN) { 2209111119Simp MCLGET(n, M_DONTWAIT); 2210105197Ssam if ((n->m_flags & M_EXT) == 0) { 2211105197Ssam m_freem(n); 2212105197Ssam n = NULL; 2213105197Ssam } 2214105197Ssam } 2215105197Ssam if (!n) 2216105197Ssam return key_senderror(so, m, ENOBUFS); 2217105197Ssam 2218105197Ssam n->m_len = len; 2219105197Ssam n->m_next = NULL; 2220105197Ssam off = 0; 2221105197Ssam 2222105197Ssam m_copydata(m, 0, sizeof(struct sadb_msg), mtod(n, caddr_t) + off); 2223105197Ssam off += PFKEY_ALIGN8(sizeof(struct sadb_msg)); 2224105197Ssam 2225120585Ssam IPSEC_ASSERT(off == len, ("length inconsistency (off %u len %u)", 2226120585Ssam off, len)); 2227105197Ssam 2228105197Ssam n->m_next = m_copym(m, mhp->extoff[SADB_X_EXT_POLICY], 2229111119Simp mhp->extlen[SADB_X_EXT_POLICY], M_DONTWAIT); 2230105197Ssam if (!n->m_next) { 2231105197Ssam m_freem(n); 2232105197Ssam return key_senderror(so, m, ENOBUFS); 2233105197Ssam } 2234105197Ssam 2235105197Ssam n->m_pkthdr.len = 0; 2236105197Ssam for (nn = n; nn; nn = nn->m_next) 2237105197Ssam n->m_pkthdr.len += nn->m_len; 2238105197Ssam 2239105197Ssam newmsg = mtod(n, struct sadb_msg *); 2240105197Ssam newmsg->sadb_msg_errno = 0; 2241105197Ssam newmsg->sadb_msg_len = PFKEY_UNIT64(n->m_pkthdr.len); 2242105197Ssam 2243105197Ssam m_freem(m); 2244105197Ssam return key_sendup_mbuf(so, n, KEY_SENDUP_ALL); 2245105197Ssam } 2246105197Ssam} 2247105197Ssam 2248105197Ssam/* 2249105197Ssam * SADB_X_GET processing 2250105197Ssam * receive 2251105197Ssam * <base, policy(*)> 2252105197Ssam * from the user(?), 2253105197Ssam * and send, 2254105197Ssam * <base, address(SD), policy> 2255105197Ssam * to the ikmpd. 2256105197Ssam * policy(*) including direction of policy. 2257105197Ssam * 2258105197Ssam * m will always be freed. 2259105197Ssam */ 2260105197Ssamstatic int 2261105197Ssamkey_spdget(so, m, mhp) 2262105197Ssam struct socket *so; 2263105197Ssam struct mbuf *m; 2264105197Ssam const struct sadb_msghdr *mhp; 2265105197Ssam{ 2266183550Szec INIT_VNET_IPSEC(curvnet); 2267105197Ssam u_int32_t id; 2268105197Ssam struct secpolicy *sp; 2269105197Ssam struct mbuf *n; 2270105197Ssam 2271120585Ssam IPSEC_ASSERT(so != NULL, ("null socket")); 2272120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 2273120585Ssam IPSEC_ASSERT(mhp != NULL, ("null msghdr")); 2274120585Ssam IPSEC_ASSERT(mhp->msg != NULL, ("null msg")); 2275105197Ssam 2276105197Ssam if (mhp->ext[SADB_X_EXT_POLICY] == NULL || 2277105197Ssam mhp->extlen[SADB_X_EXT_POLICY] < sizeof(struct sadb_x_policy)) { 2278120585Ssam ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n", 2279120585Ssam __func__)); 2280105197Ssam return key_senderror(so, m, EINVAL); 2281105197Ssam } 2282105197Ssam 2283105197Ssam id = ((struct sadb_x_policy *)mhp->ext[SADB_X_EXT_POLICY])->sadb_x_policy_id; 2284105197Ssam 2285105197Ssam /* Is there SP in SPD ? */ 2286105197Ssam if ((sp = key_getspbyid(id)) == NULL) { 2287120585Ssam ipseclog((LOG_DEBUG, "%s: no SP found id:%u.\n", __func__, id)); 2288105197Ssam return key_senderror(so, m, ENOENT); 2289105197Ssam } 2290105197Ssam 2291105197Ssam n = key_setdumpsp(sp, SADB_X_SPDGET, 0, mhp->msg->sadb_msg_pid); 2292105197Ssam if (n != NULL) { 2293105197Ssam m_freem(m); 2294105197Ssam return key_sendup_mbuf(so, n, KEY_SENDUP_ONE); 2295105197Ssam } else 2296105197Ssam return key_senderror(so, m, ENOBUFS); 2297105197Ssam} 2298105197Ssam 2299105197Ssam/* 2300105197Ssam * SADB_X_SPDACQUIRE processing. 2301105197Ssam * Acquire policy and SA(s) for a *OUTBOUND* packet. 2302105197Ssam * send 2303105197Ssam * <base, policy(*)> 2304105197Ssam * to KMD, and expect to receive 2305105197Ssam * <base> with SADB_X_SPDACQUIRE if error occured, 2306105197Ssam * or 2307105197Ssam * <base, policy> 2308105197Ssam * with SADB_X_SPDUPDATE from KMD by PF_KEY. 2309105197Ssam * policy(*) is without policy requests. 2310105197Ssam * 2311105197Ssam * 0 : succeed 2312105197Ssam * others: error number 2313105197Ssam */ 2314105197Ssamint 2315105197Ssamkey_spdacquire(sp) 2316105197Ssam struct secpolicy *sp; 2317105197Ssam{ 2318183550Szec INIT_VNET_IPSEC(curvnet); 2319105197Ssam struct mbuf *result = NULL, *m; 2320105197Ssam struct secspacq *newspacq; 2321105197Ssam 2322120585Ssam IPSEC_ASSERT(sp != NULL, ("null secpolicy")); 2323120585Ssam IPSEC_ASSERT(sp->req == NULL, ("policy exists")); 2324120585Ssam IPSEC_ASSERT(sp->policy == IPSEC_POLICY_IPSEC, 2325120585Ssam ("policy not IPSEC %u", sp->policy)); 2326105197Ssam 2327108533Sschweikh /* Get an entry to check whether sent message or not. */ 2328119643Ssam newspacq = key_getspacq(&sp->spidx); 2329119643Ssam if (newspacq != NULL) { 2330181803Sbz if (V_key_blockacq_count < newspacq->count) { 2331105197Ssam /* reset counter and do send message. */ 2332105197Ssam newspacq->count = 0; 2333105197Ssam } else { 2334105197Ssam /* increment counter and do nothing. */ 2335105197Ssam newspacq->count++; 2336105197Ssam return 0; 2337105197Ssam } 2338120585Ssam SPACQ_UNLOCK(); 2339105197Ssam } else { 2340105197Ssam /* make new entry for blocking to send SADB_ACQUIRE. */ 2341119643Ssam newspacq = key_newspacq(&sp->spidx); 2342119643Ssam if (newspacq == NULL) 2343105197Ssam return ENOBUFS; 2344105197Ssam } 2345105197Ssam 2346105197Ssam /* create new sadb_msg to reply. */ 2347105197Ssam m = key_setsadbmsg(SADB_X_SPDACQUIRE, 0, 0, 0, 0, 0); 2348170805Sbz if (!m) 2349170805Sbz return ENOBUFS; 2350170805Sbz 2351105197Ssam result = m; 2352105197Ssam 2353105197Ssam result->m_pkthdr.len = 0; 2354105197Ssam for (m = result; m; m = m->m_next) 2355105197Ssam result->m_pkthdr.len += m->m_len; 2356105197Ssam 2357105197Ssam mtod(result, struct sadb_msg *)->sadb_msg_len = 2358105197Ssam PFKEY_UNIT64(result->m_pkthdr.len); 2359105197Ssam 2360105197Ssam return key_sendup_mbuf(NULL, m, KEY_SENDUP_REGISTERED); 2361105197Ssam} 2362105197Ssam 2363105197Ssam/* 2364105197Ssam * SADB_SPDFLUSH processing 2365105197Ssam * receive 2366105197Ssam * <base> 2367105197Ssam * from the user, and free all entries in secpctree. 2368105197Ssam * and send, 2369105197Ssam * <base> 2370105197Ssam * to the user. 2371105197Ssam * NOTE: what to do is only marking SADB_SASTATE_DEAD. 2372105197Ssam * 2373105197Ssam * m will always be freed. 2374105197Ssam */ 2375105197Ssamstatic int 2376105197Ssamkey_spdflush(so, m, mhp) 2377105197Ssam struct socket *so; 2378105197Ssam struct mbuf *m; 2379105197Ssam const struct sadb_msghdr *mhp; 2380105197Ssam{ 2381183550Szec INIT_VNET_IPSEC(curvnet); 2382105197Ssam struct sadb_msg *newmsg; 2383105197Ssam struct secpolicy *sp; 2384105197Ssam u_int dir; 2385105197Ssam 2386120585Ssam IPSEC_ASSERT(so != NULL, ("null socket")); 2387120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 2388120585Ssam IPSEC_ASSERT(mhp != NULL, ("null msghdr")); 2389120585Ssam IPSEC_ASSERT(mhp->msg != NULL, ("null msg")); 2390105197Ssam 2391105197Ssam if (m->m_len != PFKEY_ALIGN8(sizeof(struct sadb_msg))) 2392105197Ssam return key_senderror(so, m, EINVAL); 2393105197Ssam 2394105197Ssam for (dir = 0; dir < IPSEC_DIR_MAX; dir++) { 2395120585Ssam SPTREE_LOCK(); 2396181803Sbz LIST_FOREACH(sp, &V_sptree[dir], chain) 2397105197Ssam sp->state = IPSEC_SPSTATE_DEAD; 2398120585Ssam SPTREE_UNLOCK(); 2399105197Ssam } 2400105197Ssam 2401105197Ssam if (sizeof(struct sadb_msg) > m->m_len + M_TRAILINGSPACE(m)) { 2402120585Ssam ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__)); 2403105197Ssam return key_senderror(so, m, ENOBUFS); 2404105197Ssam } 2405105197Ssam 2406105197Ssam if (m->m_next) 2407105197Ssam m_freem(m->m_next); 2408105197Ssam m->m_next = NULL; 2409105197Ssam m->m_pkthdr.len = m->m_len = PFKEY_ALIGN8(sizeof(struct sadb_msg)); 2410105197Ssam newmsg = mtod(m, struct sadb_msg *); 2411105197Ssam newmsg->sadb_msg_errno = 0; 2412105197Ssam newmsg->sadb_msg_len = PFKEY_UNIT64(m->m_pkthdr.len); 2413105197Ssam 2414105197Ssam return key_sendup_mbuf(so, m, KEY_SENDUP_ALL); 2415105197Ssam} 2416105197Ssam 2417105197Ssam/* 2418105197Ssam * SADB_SPDDUMP processing 2419105197Ssam * receive 2420105197Ssam * <base> 2421105197Ssam * from the user, and dump all SP leaves 2422105197Ssam * and send, 2423105197Ssam * <base> ..... 2424105197Ssam * to the ikmpd. 2425105197Ssam * 2426105197Ssam * m will always be freed. 2427105197Ssam */ 2428105197Ssamstatic int 2429105197Ssamkey_spddump(so, m, mhp) 2430105197Ssam struct socket *so; 2431105197Ssam struct mbuf *m; 2432105197Ssam const struct sadb_msghdr *mhp; 2433105197Ssam{ 2434183550Szec INIT_VNET_IPSEC(curvnet); 2435105197Ssam struct secpolicy *sp; 2436105197Ssam int cnt; 2437105197Ssam u_int dir; 2438105197Ssam struct mbuf *n; 2439105197Ssam 2440120585Ssam IPSEC_ASSERT(so != NULL, ("null socket")); 2441120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 2442120585Ssam IPSEC_ASSERT(mhp != NULL, ("null msghdr")); 2443120585Ssam IPSEC_ASSERT(mhp->msg != NULL, ("null msg")); 2444105197Ssam 2445105197Ssam /* search SPD entry and get buffer size. */ 2446105197Ssam cnt = 0; 2447192882Svanhu SPTREE_LOCK(); 2448105197Ssam for (dir = 0; dir < IPSEC_DIR_MAX; dir++) { 2449181803Sbz LIST_FOREACH(sp, &V_sptree[dir], chain) { 2450105197Ssam cnt++; 2451105197Ssam } 2452105197Ssam } 2453105197Ssam 2454192882Svanhu if (cnt == 0) { 2455192882Svanhu SPTREE_UNLOCK(); 2456105197Ssam return key_senderror(so, m, ENOENT); 2457192882Svanhu } 2458105197Ssam 2459105197Ssam for (dir = 0; dir < IPSEC_DIR_MAX; dir++) { 2460181803Sbz LIST_FOREACH(sp, &V_sptree[dir], chain) { 2461105197Ssam --cnt; 2462105197Ssam n = key_setdumpsp(sp, SADB_X_SPDDUMP, cnt, 2463105197Ssam mhp->msg->sadb_msg_pid); 2464105197Ssam 2465105197Ssam if (n) 2466105197Ssam key_sendup_mbuf(so, n, KEY_SENDUP_ONE); 2467105197Ssam } 2468105197Ssam } 2469105197Ssam 2470192882Svanhu SPTREE_UNLOCK(); 2471105197Ssam m_freem(m); 2472105197Ssam return 0; 2473105197Ssam} 2474105197Ssam 2475105197Ssamstatic struct mbuf * 2476189004Srdivackykey_setdumpsp(struct secpolicy *sp, u_int8_t type, u_int32_t seq, u_int32_t pid) 2477105197Ssam{ 2478105197Ssam struct mbuf *result = NULL, *m; 2479181330Svanhu struct seclifetime lt; 2480105197Ssam 2481105197Ssam m = key_setsadbmsg(type, 0, SADB_SATYPE_UNSPEC, seq, pid, sp->refcnt); 2482105197Ssam if (!m) 2483105197Ssam goto fail; 2484105197Ssam result = m; 2485105197Ssam 2486194062Svanhu /* 2487194062Svanhu * Note: do not send SADB_X_EXT_NAT_T_* here: 2488194062Svanhu * we are sending traffic endpoints. 2489194062Svanhu */ 2490105197Ssam m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC, 2491105197Ssam &sp->spidx.src.sa, sp->spidx.prefs, 2492105197Ssam sp->spidx.ul_proto); 2493105197Ssam if (!m) 2494105197Ssam goto fail; 2495105197Ssam m_cat(result, m); 2496105197Ssam 2497105197Ssam m = key_setsadbaddr(SADB_EXT_ADDRESS_DST, 2498105197Ssam &sp->spidx.dst.sa, sp->spidx.prefd, 2499105197Ssam sp->spidx.ul_proto); 2500105197Ssam if (!m) 2501105197Ssam goto fail; 2502105197Ssam m_cat(result, m); 2503105197Ssam 2504105197Ssam m = key_sp2msg(sp); 2505105197Ssam if (!m) 2506105197Ssam goto fail; 2507105197Ssam m_cat(result, m); 2508105197Ssam 2509181330Svanhu if(sp->lifetime){ 2510181330Svanhu lt.addtime=sp->created; 2511181330Svanhu lt.usetime= sp->lastused; 2512181330Svanhu m = key_setlifetime(<, SADB_EXT_LIFETIME_CURRENT); 2513181330Svanhu if (!m) 2514181330Svanhu goto fail; 2515181330Svanhu m_cat(result, m); 2516181330Svanhu 2517181330Svanhu lt.addtime=sp->lifetime; 2518181330Svanhu lt.usetime= sp->validtime; 2519181330Svanhu m = key_setlifetime(<, SADB_EXT_LIFETIME_HARD); 2520181330Svanhu if (!m) 2521181330Svanhu goto fail; 2522181330Svanhu m_cat(result, m); 2523181330Svanhu } 2524181330Svanhu 2525105197Ssam if ((result->m_flags & M_PKTHDR) == 0) 2526105197Ssam goto fail; 2527105197Ssam 2528105197Ssam if (result->m_len < sizeof(struct sadb_msg)) { 2529105197Ssam result = m_pullup(result, sizeof(struct sadb_msg)); 2530105197Ssam if (result == NULL) 2531105197Ssam goto fail; 2532105197Ssam } 2533105197Ssam 2534105197Ssam result->m_pkthdr.len = 0; 2535105197Ssam for (m = result; m; m = m->m_next) 2536105197Ssam result->m_pkthdr.len += m->m_len; 2537105197Ssam 2538105197Ssam mtod(result, struct sadb_msg *)->sadb_msg_len = 2539105197Ssam PFKEY_UNIT64(result->m_pkthdr.len); 2540105197Ssam 2541105197Ssam return result; 2542105197Ssam 2543105197Ssamfail: 2544105197Ssam m_freem(result); 2545105197Ssam return NULL; 2546105197Ssam} 2547105197Ssam 2548105197Ssam/* 2549105197Ssam * get PFKEY message length for security policy and request. 2550105197Ssam */ 2551105197Ssamstatic u_int 2552105197Ssamkey_getspreqmsglen(sp) 2553105197Ssam struct secpolicy *sp; 2554105197Ssam{ 2555105197Ssam u_int tlen; 2556105197Ssam 2557105197Ssam tlen = sizeof(struct sadb_x_policy); 2558105197Ssam 2559105197Ssam /* if is the policy for ipsec ? */ 2560105197Ssam if (sp->policy != IPSEC_POLICY_IPSEC) 2561105197Ssam return tlen; 2562105197Ssam 2563105197Ssam /* get length of ipsec requests */ 2564105197Ssam { 2565105197Ssam struct ipsecrequest *isr; 2566105197Ssam int len; 2567105197Ssam 2568105197Ssam for (isr = sp->req; isr != NULL; isr = isr->next) { 2569105197Ssam len = sizeof(struct sadb_x_ipsecrequest) 2570105197Ssam + isr->saidx.src.sa.sa_len 2571105197Ssam + isr->saidx.dst.sa.sa_len; 2572105197Ssam 2573105197Ssam tlen += PFKEY_ALIGN8(len); 2574105197Ssam } 2575105197Ssam } 2576105197Ssam 2577105197Ssam return tlen; 2578105197Ssam} 2579105197Ssam 2580105197Ssam/* 2581105197Ssam * SADB_SPDEXPIRE processing 2582105197Ssam * send 2583105197Ssam * <base, address(SD), lifetime(CH), policy> 2584105197Ssam * to KMD by PF_KEY. 2585105197Ssam * 2586105197Ssam * OUT: 0 : succeed 2587105197Ssam * others : error number 2588105197Ssam */ 2589105197Ssamstatic int 2590105197Ssamkey_spdexpire(sp) 2591105197Ssam struct secpolicy *sp; 2592105197Ssam{ 2593105197Ssam struct mbuf *result = NULL, *m; 2594105197Ssam int len; 2595105197Ssam int error = -1; 2596105197Ssam struct sadb_lifetime *lt; 2597105197Ssam 2598105197Ssam /* XXX: Why do we lock ? */ 2599105197Ssam 2600120585Ssam IPSEC_ASSERT(sp != NULL, ("null secpolicy")); 2601105197Ssam 2602105197Ssam /* set msg header */ 2603105197Ssam m = key_setsadbmsg(SADB_X_SPDEXPIRE, 0, 0, 0, 0, 0); 2604105197Ssam if (!m) { 2605105197Ssam error = ENOBUFS; 2606105197Ssam goto fail; 2607105197Ssam } 2608105197Ssam result = m; 2609105197Ssam 2610105197Ssam /* create lifetime extension (current and hard) */ 2611105197Ssam len = PFKEY_ALIGN8(sizeof(*lt)) * 2; 2612105197Ssam m = key_alloc_mbuf(len); 2613105197Ssam if (!m || m->m_next) { /*XXX*/ 2614105197Ssam if (m) 2615105197Ssam m_freem(m); 2616105197Ssam error = ENOBUFS; 2617105197Ssam goto fail; 2618105197Ssam } 2619105197Ssam bzero(mtod(m, caddr_t), len); 2620105197Ssam lt = mtod(m, struct sadb_lifetime *); 2621105197Ssam lt->sadb_lifetime_len = PFKEY_UNIT64(sizeof(struct sadb_lifetime)); 2622105197Ssam lt->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT; 2623105197Ssam lt->sadb_lifetime_allocations = 0; 2624105197Ssam lt->sadb_lifetime_bytes = 0; 2625105197Ssam lt->sadb_lifetime_addtime = sp->created; 2626105197Ssam lt->sadb_lifetime_usetime = sp->lastused; 2627105197Ssam lt = (struct sadb_lifetime *)(mtod(m, caddr_t) + len / 2); 2628105197Ssam lt->sadb_lifetime_len = PFKEY_UNIT64(sizeof(struct sadb_lifetime)); 2629105197Ssam lt->sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD; 2630105197Ssam lt->sadb_lifetime_allocations = 0; 2631105197Ssam lt->sadb_lifetime_bytes = 0; 2632105197Ssam lt->sadb_lifetime_addtime = sp->lifetime; 2633105197Ssam lt->sadb_lifetime_usetime = sp->validtime; 2634105197Ssam m_cat(result, m); 2635105197Ssam 2636194062Svanhu /* 2637194062Svanhu * Note: do not send SADB_X_EXT_NAT_T_* here: 2638194062Svanhu * we are sending traffic endpoints. 2639194062Svanhu */ 2640194062Svanhu 2641105197Ssam /* set sadb_address for source */ 2642105197Ssam m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC, 2643105197Ssam &sp->spidx.src.sa, 2644105197Ssam sp->spidx.prefs, sp->spidx.ul_proto); 2645105197Ssam if (!m) { 2646105197Ssam error = ENOBUFS; 2647105197Ssam goto fail; 2648105197Ssam } 2649105197Ssam m_cat(result, m); 2650105197Ssam 2651105197Ssam /* set sadb_address for destination */ 2652105197Ssam m = key_setsadbaddr(SADB_EXT_ADDRESS_DST, 2653105197Ssam &sp->spidx.dst.sa, 2654105197Ssam sp->spidx.prefd, sp->spidx.ul_proto); 2655105197Ssam if (!m) { 2656105197Ssam error = ENOBUFS; 2657105197Ssam goto fail; 2658105197Ssam } 2659105197Ssam m_cat(result, m); 2660105197Ssam 2661105197Ssam /* set secpolicy */ 2662105197Ssam m = key_sp2msg(sp); 2663105197Ssam if (!m) { 2664105197Ssam error = ENOBUFS; 2665105197Ssam goto fail; 2666105197Ssam } 2667105197Ssam m_cat(result, m); 2668105197Ssam 2669105197Ssam if ((result->m_flags & M_PKTHDR) == 0) { 2670105197Ssam error = EINVAL; 2671105197Ssam goto fail; 2672105197Ssam } 2673105197Ssam 2674105197Ssam if (result->m_len < sizeof(struct sadb_msg)) { 2675105197Ssam result = m_pullup(result, sizeof(struct sadb_msg)); 2676105197Ssam if (result == NULL) { 2677105197Ssam error = ENOBUFS; 2678105197Ssam goto fail; 2679105197Ssam } 2680105197Ssam } 2681105197Ssam 2682105197Ssam result->m_pkthdr.len = 0; 2683105197Ssam for (m = result; m; m = m->m_next) 2684105197Ssam result->m_pkthdr.len += m->m_len; 2685105197Ssam 2686105197Ssam mtod(result, struct sadb_msg *)->sadb_msg_len = 2687105197Ssam PFKEY_UNIT64(result->m_pkthdr.len); 2688105197Ssam 2689105197Ssam return key_sendup_mbuf(NULL, result, KEY_SENDUP_REGISTERED); 2690105197Ssam 2691105197Ssam fail: 2692105197Ssam if (result) 2693105197Ssam m_freem(result); 2694105197Ssam return error; 2695105197Ssam} 2696105197Ssam 2697105197Ssam/* %%% SAD management */ 2698105197Ssam/* 2699105197Ssam * allocating a memory for new SA head, and copy from the values of mhp. 2700105197Ssam * OUT: NULL : failure due to the lack of memory. 2701105197Ssam * others : pointer to new SA head. 2702105197Ssam */ 2703105197Ssamstatic struct secashead * 2704105197Ssamkey_newsah(saidx) 2705105197Ssam struct secasindex *saidx; 2706105197Ssam{ 2707183550Szec INIT_VNET_IPSEC(curvnet); 2708105197Ssam struct secashead *newsah; 2709105197Ssam 2710120585Ssam IPSEC_ASSERT(saidx != NULL, ("null saidx")); 2711105197Ssam 2712119643Ssam newsah = malloc(sizeof(struct secashead), M_IPSEC_SAH, M_NOWAIT|M_ZERO); 2713105197Ssam if (newsah != NULL) { 2714105197Ssam int i; 2715105197Ssam for (i = 0; i < sizeof(newsah->savtree)/sizeof(newsah->savtree[0]); i++) 2716105197Ssam LIST_INIT(&newsah->savtree[i]); 2717105197Ssam newsah->saidx = *saidx; 2718105197Ssam 2719105197Ssam /* add to saidxtree */ 2720105197Ssam newsah->state = SADB_SASTATE_MATURE; 2721119643Ssam 2722120585Ssam SAHTREE_LOCK(); 2723181803Sbz LIST_INSERT_HEAD(&V_sahtree, newsah, chain); 2724120585Ssam SAHTREE_UNLOCK(); 2725105197Ssam } 2726105197Ssam return(newsah); 2727105197Ssam} 2728105197Ssam 2729105197Ssam/* 2730105197Ssam * delete SA index and all SA registerd. 2731105197Ssam */ 2732105197Ssamstatic void 2733105197Ssamkey_delsah(sah) 2734105197Ssam struct secashead *sah; 2735105197Ssam{ 2736183550Szec INIT_VNET_IPSEC(curvnet); 2737105197Ssam struct secasvar *sav, *nextsav; 2738120585Ssam u_int stateidx; 2739105197Ssam int zombie = 0; 2740105197Ssam 2741120585Ssam IPSEC_ASSERT(sah != NULL, ("NULL sah")); 2742120585Ssam SAHTREE_LOCK_ASSERT(); 2743105197Ssam 2744105197Ssam /* searching all SA registerd in the secindex. */ 2745105197Ssam for (stateidx = 0; 2746185348Szec stateidx < _ARRAYLEN(saorder_state_any); 2747105197Ssam stateidx++) { 2748185348Szec u_int state = saorder_state_any[stateidx]; 2749120585Ssam LIST_FOREACH_SAFE(sav, &sah->savtree[state], chain, nextsav) { 2750105197Ssam if (sav->refcnt == 0) { 2751105197Ssam /* sanity check */ 2752120585Ssam KEY_CHKSASTATE(state, sav->state, __func__); 2753190071Svanhu /* 2754190071Svanhu * do NOT call KEY_FREESAV here: 2755190071Svanhu * it will only delete the sav if refcnt == 1, 2756189962Svanhu * where we already know that refcnt == 0 2757189962Svanhu */ 2758189962Svanhu key_delsav(sav); 2759105197Ssam } else { 2760105197Ssam /* give up to delete this sa */ 2761105197Ssam zombie++; 2762105197Ssam } 2763105197Ssam } 2764105197Ssam } 2765120585Ssam if (!zombie) { /* delete only if there are savs */ 2766120585Ssam /* remove from tree of SA index */ 2767120585Ssam if (__LIST_CHAINED(sah)) 2768120585Ssam LIST_REMOVE(sah, chain); 2769120585Ssam if (sah->sa_route.ro_rt) { 2770120585Ssam RTFREE(sah->sa_route.ro_rt); 2771120585Ssam sah->sa_route.ro_rt = (struct rtentry *)NULL; 2772120585Ssam } 2773120585Ssam free(sah, M_IPSEC_SAH); 2774105197Ssam } 2775105197Ssam} 2776105197Ssam 2777105197Ssam/* 2778105197Ssam * allocating a new SA with LARVAL state. key_add() and key_getspi() call, 2779105197Ssam * and copy the values of mhp into new buffer. 2780105197Ssam * When SAD message type is GETSPI: 2781105197Ssam * to set sequence number from acq_seq++, 2782105197Ssam * to set zero to SPI. 2783105197Ssam * not to call key_setsava(). 2784105197Ssam * OUT: NULL : fail 2785105197Ssam * others : pointer to new secasvar. 2786105197Ssam * 2787105197Ssam * does not modify mbuf. does not free mbuf on error. 2788105197Ssam */ 2789105197Ssamstatic struct secasvar * 2790105197Ssamkey_newsav(m, mhp, sah, errp, where, tag) 2791105197Ssam struct mbuf *m; 2792105197Ssam const struct sadb_msghdr *mhp; 2793105197Ssam struct secashead *sah; 2794105197Ssam int *errp; 2795105197Ssam const char* where; 2796105197Ssam int tag; 2797105197Ssam{ 2798183550Szec INIT_VNET_IPSEC(curvnet); 2799105197Ssam struct secasvar *newsav; 2800105197Ssam const struct sadb_sa *xsa; 2801105197Ssam 2802120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 2803120585Ssam IPSEC_ASSERT(mhp != NULL, ("null msghdr")); 2804120585Ssam IPSEC_ASSERT(mhp->msg != NULL, ("null msg")); 2805120585Ssam IPSEC_ASSERT(sah != NULL, ("null secashead")); 2806105197Ssam 2807119643Ssam newsav = malloc(sizeof(struct secasvar), M_IPSEC_SA, M_NOWAIT|M_ZERO); 2808105197Ssam if (newsav == NULL) { 2809120585Ssam ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__)); 2810105197Ssam *errp = ENOBUFS; 2811105197Ssam goto done; 2812105197Ssam } 2813105197Ssam 2814105197Ssam switch (mhp->msg->sadb_msg_type) { 2815105197Ssam case SADB_GETSPI: 2816105197Ssam newsav->spi = 0; 2817105197Ssam 2818105197Ssam#ifdef IPSEC_DOSEQCHECK 2819105197Ssam /* sync sequence number */ 2820105197Ssam if (mhp->msg->sadb_msg_seq == 0) 2821105197Ssam newsav->seq = 2822181803Sbz (V_acq_seq = (V_acq_seq == ~0 ? 1 : ++V_acq_seq)); 2823105197Ssam else 2824105197Ssam#endif 2825105197Ssam newsav->seq = mhp->msg->sadb_msg_seq; 2826105197Ssam break; 2827105197Ssam 2828105197Ssam case SADB_ADD: 2829105197Ssam /* sanity check */ 2830105197Ssam if (mhp->ext[SADB_EXT_SA] == NULL) { 2831119643Ssam free(newsav, M_IPSEC_SA); 2832119643Ssam newsav = NULL; 2833120585Ssam ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n", 2834120585Ssam __func__)); 2835105197Ssam *errp = EINVAL; 2836105197Ssam goto done; 2837105197Ssam } 2838105197Ssam xsa = (const struct sadb_sa *)mhp->ext[SADB_EXT_SA]; 2839105197Ssam newsav->spi = xsa->sadb_sa_spi; 2840105197Ssam newsav->seq = mhp->msg->sadb_msg_seq; 2841105197Ssam break; 2842105197Ssam default: 2843119643Ssam free(newsav, M_IPSEC_SA); 2844119643Ssam newsav = NULL; 2845105197Ssam *errp = EINVAL; 2846105197Ssam goto done; 2847105197Ssam } 2848105197Ssam 2849119643Ssam 2850105197Ssam /* copy sav values */ 2851105197Ssam if (mhp->msg->sadb_msg_type != SADB_GETSPI) { 2852105197Ssam *errp = key_setsaval(newsav, m, mhp); 2853105197Ssam if (*errp) { 2854119643Ssam free(newsav, M_IPSEC_SA); 2855119643Ssam newsav = NULL; 2856105197Ssam goto done; 2857105197Ssam } 2858105197Ssam } 2859105197Ssam 2860120585Ssam SECASVAR_LOCK_INIT(newsav); 2861119643Ssam 2862105197Ssam /* reset created */ 2863105197Ssam newsav->created = time_second; 2864105197Ssam newsav->pid = mhp->msg->sadb_msg_pid; 2865105197Ssam 2866105197Ssam /* add to satree */ 2867105197Ssam newsav->sah = sah; 2868158767Spjd sa_initref(newsav); 2869105197Ssam newsav->state = SADB_SASTATE_LARVAL; 2870119643Ssam 2871119643Ssam /* XXX locking??? */ 2872105197Ssam LIST_INSERT_TAIL(&sah->savtree[SADB_SASTATE_LARVAL], newsav, 2873105197Ssam secasvar, chain); 2874105197Ssamdone: 2875105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 2876120585Ssam printf("DP %s from %s:%u return SP:%p\n", __func__, 2877105197Ssam where, tag, newsav)); 2878105197Ssam 2879105197Ssam return newsav; 2880105197Ssam} 2881105197Ssam 2882105197Ssam/* 2883105197Ssam * free() SA variable entry. 2884105197Ssam */ 2885105197Ssamstatic void 2886119643Ssamkey_cleansav(struct secasvar *sav) 2887105197Ssam{ 2888117051Ssam /* 2889117051Ssam * Cleanup xform state. Note that zeroize'ing causes the 2890117051Ssam * keys to be cleared; otherwise we must do it ourself. 2891117051Ssam */ 2892117051Ssam if (sav->tdb_xform != NULL) { 2893117051Ssam sav->tdb_xform->xf_zeroize(sav); 2894117051Ssam sav->tdb_xform = NULL; 2895117051Ssam } else { 2896120585Ssam KASSERT(sav->iv == NULL, ("iv but no xform")); 2897117051Ssam if (sav->key_auth != NULL) 2898157123Sgnn bzero(sav->key_auth->key_data, _KEYLEN(sav->key_auth)); 2899117051Ssam if (sav->key_enc != NULL) 2900157123Sgnn bzero(sav->key_enc->key_data, _KEYLEN(sav->key_enc)); 2901117051Ssam } 2902105197Ssam if (sav->key_auth != NULL) { 2903157123Sgnn if (sav->key_auth->key_data != NULL) 2904157123Sgnn free(sav->key_auth->key_data, M_IPSEC_MISC); 2905119643Ssam free(sav->key_auth, M_IPSEC_MISC); 2906105197Ssam sav->key_auth = NULL; 2907105197Ssam } 2908105197Ssam if (sav->key_enc != NULL) { 2909157123Sgnn if (sav->key_enc->key_data != NULL) 2910157123Sgnn free(sav->key_enc->key_data, M_IPSEC_MISC); 2911119643Ssam free(sav->key_enc, M_IPSEC_MISC); 2912105197Ssam sav->key_enc = NULL; 2913105197Ssam } 2914105197Ssam if (sav->sched) { 2915105197Ssam bzero(sav->sched, sav->schedlen); 2916119643Ssam free(sav->sched, M_IPSEC_MISC); 2917105197Ssam sav->sched = NULL; 2918105197Ssam } 2919105197Ssam if (sav->replay != NULL) { 2920119643Ssam free(sav->replay, M_IPSEC_MISC); 2921105197Ssam sav->replay = NULL; 2922105197Ssam } 2923105197Ssam if (sav->lft_c != NULL) { 2924119643Ssam free(sav->lft_c, M_IPSEC_MISC); 2925105197Ssam sav->lft_c = NULL; 2926105197Ssam } 2927105197Ssam if (sav->lft_h != NULL) { 2928119643Ssam free(sav->lft_h, M_IPSEC_MISC); 2929105197Ssam sav->lft_h = NULL; 2930105197Ssam } 2931105197Ssam if (sav->lft_s != NULL) { 2932119643Ssam free(sav->lft_s, M_IPSEC_MISC); 2933105197Ssam sav->lft_s = NULL; 2934105197Ssam } 2935119643Ssam} 2936105197Ssam 2937119643Ssam/* 2938119643Ssam * free() SA variable entry. 2939119643Ssam */ 2940119643Ssamstatic void 2941119643Ssamkey_delsav(sav) 2942119643Ssam struct secasvar *sav; 2943119643Ssam{ 2944120585Ssam IPSEC_ASSERT(sav != NULL, ("null sav")); 2945120585Ssam IPSEC_ASSERT(sav->refcnt == 0, ("reference count %u > 0", sav->refcnt)); 2946105197Ssam 2947119643Ssam /* remove from SA header */ 2948119643Ssam if (__LIST_CHAINED(sav)) 2949119643Ssam LIST_REMOVE(sav, chain); 2950119643Ssam key_cleansav(sav); 2951120585Ssam SECASVAR_LOCK_DESTROY(sav); 2952119643Ssam free(sav, M_IPSEC_SA); 2953105197Ssam} 2954105197Ssam 2955105197Ssam/* 2956105197Ssam * search SAD. 2957105197Ssam * OUT: 2958105197Ssam * NULL : not found 2959105197Ssam * others : found, pointer to a SA. 2960105197Ssam */ 2961105197Ssamstatic struct secashead * 2962105197Ssamkey_getsah(saidx) 2963105197Ssam struct secasindex *saidx; 2964105197Ssam{ 2965183550Szec INIT_VNET_IPSEC(curvnet); 2966105197Ssam struct secashead *sah; 2967105197Ssam 2968120585Ssam SAHTREE_LOCK(); 2969181803Sbz LIST_FOREACH(sah, &V_sahtree, chain) { 2970105197Ssam if (sah->state == SADB_SASTATE_DEAD) 2971105197Ssam continue; 2972105197Ssam if (key_cmpsaidx(&sah->saidx, saidx, CMP_REQID)) 2973119643Ssam break; 2974105197Ssam } 2975120585Ssam SAHTREE_UNLOCK(); 2976105197Ssam 2977119643Ssam return sah; 2978105197Ssam} 2979105197Ssam 2980105197Ssam/* 2981105197Ssam * check not to be duplicated SPI. 2982105197Ssam * NOTE: this function is too slow due to searching all SAD. 2983105197Ssam * OUT: 2984105197Ssam * NULL : not found 2985105197Ssam * others : found, pointer to a SA. 2986105197Ssam */ 2987105197Ssamstatic struct secasvar * 2988105197Ssamkey_checkspidup(saidx, spi) 2989105197Ssam struct secasindex *saidx; 2990105197Ssam u_int32_t spi; 2991105197Ssam{ 2992183550Szec INIT_VNET_IPSEC(curvnet); 2993105197Ssam struct secashead *sah; 2994105197Ssam struct secasvar *sav; 2995105197Ssam 2996105197Ssam /* check address family */ 2997105197Ssam if (saidx->src.sa.sa_family != saidx->dst.sa.sa_family) { 2998120585Ssam ipseclog((LOG_DEBUG, "%s: address family mismatched.\n", 2999120585Ssam __func__)); 3000105197Ssam return NULL; 3001105197Ssam } 3002105197Ssam 3003119643Ssam sav = NULL; 3004105197Ssam /* check all SAD */ 3005120585Ssam SAHTREE_LOCK(); 3006181803Sbz LIST_FOREACH(sah, &V_sahtree, chain) { 3007105197Ssam if (!key_ismyaddr((struct sockaddr *)&sah->saidx.dst)) 3008105197Ssam continue; 3009105197Ssam sav = key_getsavbyspi(sah, spi); 3010105197Ssam if (sav != NULL) 3011119643Ssam break; 3012105197Ssam } 3013120585Ssam SAHTREE_UNLOCK(); 3014105197Ssam 3015119643Ssam return sav; 3016105197Ssam} 3017105197Ssam 3018105197Ssam/* 3019105197Ssam * search SAD litmited alive SA, protocol, SPI. 3020105197Ssam * OUT: 3021105197Ssam * NULL : not found 3022105197Ssam * others : found, pointer to a SA. 3023105197Ssam */ 3024105197Ssamstatic struct secasvar * 3025105197Ssamkey_getsavbyspi(sah, spi) 3026105197Ssam struct secashead *sah; 3027105197Ssam u_int32_t spi; 3028105197Ssam{ 3029183550Szec INIT_VNET_IPSEC(curvnet); 3030105197Ssam struct secasvar *sav; 3031105197Ssam u_int stateidx, state; 3032105197Ssam 3033119643Ssam sav = NULL; 3034120585Ssam SAHTREE_LOCK_ASSERT(); 3035105197Ssam /* search all status */ 3036105197Ssam for (stateidx = 0; 3037185348Szec stateidx < _ARRAYLEN(saorder_state_alive); 3038105197Ssam stateidx++) { 3039105197Ssam 3040185348Szec state = saorder_state_alive[stateidx]; 3041105197Ssam LIST_FOREACH(sav, &sah->savtree[state], chain) { 3042105197Ssam 3043105197Ssam /* sanity check */ 3044105197Ssam if (sav->state != state) { 3045120585Ssam ipseclog((LOG_DEBUG, "%s: " 3046105197Ssam "invalid sav->state (queue: %d SA: %d)\n", 3047120585Ssam __func__, state, sav->state)); 3048105197Ssam continue; 3049105197Ssam } 3050105197Ssam 3051105197Ssam if (sav->spi == spi) 3052128859Ssam return sav; 3053105197Ssam } 3054105197Ssam } 3055105197Ssam 3056128859Ssam return NULL; 3057105197Ssam} 3058105197Ssam 3059105197Ssam/* 3060105197Ssam * copy SA values from PF_KEY message except *SPI, SEQ, PID, STATE and TYPE*. 3061105197Ssam * You must update these if need. 3062105197Ssam * OUT: 0: success. 3063105197Ssam * !0: failure. 3064105197Ssam * 3065105197Ssam * does not modify mbuf. does not free mbuf on error. 3066105197Ssam */ 3067105197Ssamstatic int 3068105197Ssamkey_setsaval(sav, m, mhp) 3069105197Ssam struct secasvar *sav; 3070105197Ssam struct mbuf *m; 3071105197Ssam const struct sadb_msghdr *mhp; 3072105197Ssam{ 3073183550Szec INIT_VNET_IPSEC(curvnet); 3074105197Ssam int error = 0; 3075105197Ssam 3076120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 3077120585Ssam IPSEC_ASSERT(mhp != NULL, ("null msghdr")); 3078120585Ssam IPSEC_ASSERT(mhp->msg != NULL, ("null msg")); 3079105197Ssam 3080105197Ssam /* initialization */ 3081105197Ssam sav->replay = NULL; 3082105197Ssam sav->key_auth = NULL; 3083105197Ssam sav->key_enc = NULL; 3084105197Ssam sav->sched = NULL; 3085105197Ssam sav->schedlen = 0; 3086105197Ssam sav->iv = NULL; 3087105197Ssam sav->lft_c = NULL; 3088105197Ssam sav->lft_h = NULL; 3089105197Ssam sav->lft_s = NULL; 3090105197Ssam sav->tdb_xform = NULL; /* transform */ 3091105197Ssam sav->tdb_encalgxform = NULL; /* encoding algorithm */ 3092105197Ssam sav->tdb_authalgxform = NULL; /* authentication algorithm */ 3093105197Ssam sav->tdb_compalgxform = NULL; /* compression algorithm */ 3094194062Svanhu /* Initialize even if NAT-T not compiled in: */ 3095194062Svanhu sav->natt_type = 0; 3096194062Svanhu sav->natt_esp_frag_len = 0; 3097105197Ssam 3098105197Ssam /* SA */ 3099105197Ssam if (mhp->ext[SADB_EXT_SA] != NULL) { 3100105197Ssam const struct sadb_sa *sa0; 3101105197Ssam 3102105197Ssam sa0 = (const struct sadb_sa *)mhp->ext[SADB_EXT_SA]; 3103105197Ssam if (mhp->extlen[SADB_EXT_SA] < sizeof(*sa0)) { 3104105197Ssam error = EINVAL; 3105105197Ssam goto fail; 3106105197Ssam } 3107105197Ssam 3108105197Ssam sav->alg_auth = sa0->sadb_sa_auth; 3109105197Ssam sav->alg_enc = sa0->sadb_sa_encrypt; 3110105197Ssam sav->flags = sa0->sadb_sa_flags; 3111105197Ssam 3112105197Ssam /* replay window */ 3113105197Ssam if ((sa0->sadb_sa_flags & SADB_X_EXT_OLD) == 0) { 3114105197Ssam sav->replay = (struct secreplay *) 3115119643Ssam malloc(sizeof(struct secreplay)+sa0->sadb_sa_replay, M_IPSEC_MISC, M_NOWAIT|M_ZERO); 3116105197Ssam if (sav->replay == NULL) { 3117120585Ssam ipseclog((LOG_DEBUG, "%s: No more memory.\n", 3118120585Ssam __func__)); 3119105197Ssam error = ENOBUFS; 3120105197Ssam goto fail; 3121105197Ssam } 3122105197Ssam if (sa0->sadb_sa_replay != 0) 3123105197Ssam sav->replay->bitmap = (caddr_t)(sav->replay+1); 3124105197Ssam sav->replay->wsize = sa0->sadb_sa_replay; 3125105197Ssam } 3126105197Ssam } 3127105197Ssam 3128105197Ssam /* Authentication keys */ 3129105197Ssam if (mhp->ext[SADB_EXT_KEY_AUTH] != NULL) { 3130105197Ssam const struct sadb_key *key0; 3131105197Ssam int len; 3132105197Ssam 3133105197Ssam key0 = (const struct sadb_key *)mhp->ext[SADB_EXT_KEY_AUTH]; 3134105197Ssam len = mhp->extlen[SADB_EXT_KEY_AUTH]; 3135105197Ssam 3136105197Ssam error = 0; 3137105197Ssam if (len < sizeof(*key0)) { 3138105197Ssam error = EINVAL; 3139105197Ssam goto fail; 3140105197Ssam } 3141105197Ssam switch (mhp->msg->sadb_msg_satype) { 3142105197Ssam case SADB_SATYPE_AH: 3143105197Ssam case SADB_SATYPE_ESP: 3144125680Sbms case SADB_X_SATYPE_TCPSIGNATURE: 3145105197Ssam if (len == PFKEY_ALIGN8(sizeof(struct sadb_key)) && 3146105197Ssam sav->alg_auth != SADB_X_AALG_NULL) 3147105197Ssam error = EINVAL; 3148105197Ssam break; 3149105197Ssam case SADB_X_SATYPE_IPCOMP: 3150105197Ssam default: 3151105197Ssam error = EINVAL; 3152105197Ssam break; 3153105197Ssam } 3154105197Ssam if (error) { 3155120585Ssam ipseclog((LOG_DEBUG, "%s: invalid key_auth values.\n", 3156120585Ssam __func__)); 3157105197Ssam goto fail; 3158105197Ssam } 3159105197Ssam 3160157123Sgnn sav->key_auth = (struct seckey *)key_dup_keymsg(key0, len, 3161157123Sgnn M_IPSEC_MISC); 3162157123Sgnn if (sav->key_auth == NULL ) { 3163157123Sgnn ipseclog((LOG_DEBUG, "%s: No more memory.\n", 3164157123Sgnn __func__)); 3165105197Ssam error = ENOBUFS; 3166105197Ssam goto fail; 3167105197Ssam } 3168105197Ssam } 3169105197Ssam 3170105197Ssam /* Encryption key */ 3171105197Ssam if (mhp->ext[SADB_EXT_KEY_ENCRYPT] != NULL) { 3172105197Ssam const struct sadb_key *key0; 3173105197Ssam int len; 3174105197Ssam 3175105197Ssam key0 = (const struct sadb_key *)mhp->ext[SADB_EXT_KEY_ENCRYPT]; 3176105197Ssam len = mhp->extlen[SADB_EXT_KEY_ENCRYPT]; 3177105197Ssam 3178105197Ssam error = 0; 3179105197Ssam if (len < sizeof(*key0)) { 3180105197Ssam error = EINVAL; 3181105197Ssam goto fail; 3182105197Ssam } 3183105197Ssam switch (mhp->msg->sadb_msg_satype) { 3184105197Ssam case SADB_SATYPE_ESP: 3185105197Ssam if (len == PFKEY_ALIGN8(sizeof(struct sadb_key)) && 3186105197Ssam sav->alg_enc != SADB_EALG_NULL) { 3187105197Ssam error = EINVAL; 3188105197Ssam break; 3189105197Ssam } 3190157123Sgnn sav->key_enc = (struct seckey *)key_dup_keymsg(key0, 3191157123Sgnn len, 3192157123Sgnn M_IPSEC_MISC); 3193105197Ssam if (sav->key_enc == NULL) { 3194120585Ssam ipseclog((LOG_DEBUG, "%s: No more memory.\n", 3195120585Ssam __func__)); 3196105197Ssam error = ENOBUFS; 3197105197Ssam goto fail; 3198105197Ssam } 3199105197Ssam break; 3200105197Ssam case SADB_X_SATYPE_IPCOMP: 3201105197Ssam if (len != PFKEY_ALIGN8(sizeof(struct sadb_key))) 3202105197Ssam error = EINVAL; 3203105197Ssam sav->key_enc = NULL; /*just in case*/ 3204105197Ssam break; 3205105197Ssam case SADB_SATYPE_AH: 3206125680Sbms case SADB_X_SATYPE_TCPSIGNATURE: 3207105197Ssam default: 3208105197Ssam error = EINVAL; 3209105197Ssam break; 3210105197Ssam } 3211105197Ssam if (error) { 3212120585Ssam ipseclog((LOG_DEBUG, "%s: invalid key_enc value.\n", 3213120585Ssam __func__)); 3214105197Ssam goto fail; 3215105197Ssam } 3216105197Ssam } 3217105197Ssam 3218105197Ssam /* set iv */ 3219105197Ssam sav->ivlen = 0; 3220105197Ssam 3221105197Ssam switch (mhp->msg->sadb_msg_satype) { 3222105197Ssam case SADB_SATYPE_AH: 3223105197Ssam error = xform_init(sav, XF_AH); 3224105197Ssam break; 3225105197Ssam case SADB_SATYPE_ESP: 3226105197Ssam error = xform_init(sav, XF_ESP); 3227105197Ssam break; 3228105197Ssam case SADB_X_SATYPE_IPCOMP: 3229105197Ssam error = xform_init(sav, XF_IPCOMP); 3230105197Ssam break; 3231125680Sbms case SADB_X_SATYPE_TCPSIGNATURE: 3232125680Sbms error = xform_init(sav, XF_TCPSIGNATURE); 3233125680Sbms break; 3234105197Ssam } 3235105197Ssam if (error) { 3236120585Ssam ipseclog((LOG_DEBUG, "%s: unable to initialize SA type %u.\n", 3237120585Ssam __func__, mhp->msg->sadb_msg_satype)); 3238105197Ssam goto fail; 3239105197Ssam } 3240105197Ssam 3241105197Ssam /* reset created */ 3242105197Ssam sav->created = time_second; 3243105197Ssam 3244105197Ssam /* make lifetime for CURRENT */ 3245176743Sbz sav->lft_c = malloc(sizeof(struct seclifetime), M_IPSEC_MISC, M_NOWAIT); 3246105197Ssam if (sav->lft_c == NULL) { 3247120585Ssam ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__)); 3248105197Ssam error = ENOBUFS; 3249105197Ssam goto fail; 3250105197Ssam } 3251105197Ssam 3252157123Sgnn sav->lft_c->allocations = 0; 3253157123Sgnn sav->lft_c->bytes = 0; 3254157123Sgnn sav->lft_c->addtime = time_second; 3255157123Sgnn sav->lft_c->usetime = 0; 3256105197Ssam 3257105197Ssam /* lifetimes for HARD and SOFT */ 3258105197Ssam { 3259105197Ssam const struct sadb_lifetime *lft0; 3260105197Ssam 3261105197Ssam lft0 = (struct sadb_lifetime *)mhp->ext[SADB_EXT_LIFETIME_HARD]; 3262105197Ssam if (lft0 != NULL) { 3263105197Ssam if (mhp->extlen[SADB_EXT_LIFETIME_HARD] < sizeof(*lft0)) { 3264105197Ssam error = EINVAL; 3265105197Ssam goto fail; 3266105197Ssam } 3267157123Sgnn sav->lft_h = key_dup_lifemsg(lft0, M_IPSEC_MISC); 3268105197Ssam if (sav->lft_h == NULL) { 3269120585Ssam ipseclog((LOG_DEBUG, "%s: No more memory.\n",__func__)); 3270105197Ssam error = ENOBUFS; 3271105197Ssam goto fail; 3272105197Ssam } 3273105197Ssam /* to be initialize ? */ 3274105197Ssam } 3275105197Ssam 3276105197Ssam lft0 = (struct sadb_lifetime *)mhp->ext[SADB_EXT_LIFETIME_SOFT]; 3277105197Ssam if (lft0 != NULL) { 3278105197Ssam if (mhp->extlen[SADB_EXT_LIFETIME_SOFT] < sizeof(*lft0)) { 3279105197Ssam error = EINVAL; 3280105197Ssam goto fail; 3281105197Ssam } 3282157123Sgnn sav->lft_s = key_dup_lifemsg(lft0, M_IPSEC_MISC); 3283105197Ssam if (sav->lft_s == NULL) { 3284120585Ssam ipseclog((LOG_DEBUG, "%s: No more memory.\n",__func__)); 3285105197Ssam error = ENOBUFS; 3286105197Ssam goto fail; 3287105197Ssam } 3288105197Ssam /* to be initialize ? */ 3289105197Ssam } 3290105197Ssam } 3291105197Ssam 3292105197Ssam return 0; 3293105197Ssam 3294105197Ssam fail: 3295105197Ssam /* initialization */ 3296119643Ssam key_cleansav(sav); 3297105197Ssam 3298105197Ssam return error; 3299105197Ssam} 3300105197Ssam 3301105197Ssam/* 3302105197Ssam * validation with a secasvar entry, and set SADB_SATYPE_MATURE. 3303105197Ssam * OUT: 0: valid 3304105197Ssam * other: errno 3305105197Ssam */ 3306105197Ssamstatic int 3307119643Ssamkey_mature(struct secasvar *sav) 3308105197Ssam{ 3309183550Szec INIT_VNET_IPSEC(curvnet); 3310105197Ssam int error; 3311105197Ssam 3312105197Ssam /* check SPI value */ 3313105197Ssam switch (sav->sah->saidx.proto) { 3314105197Ssam case IPPROTO_ESP: 3315105197Ssam case IPPROTO_AH: 3316170823Sbz /* 3317170823Sbz * RFC 4302, 2.4. Security Parameters Index (SPI), SPI values 3318170823Sbz * 1-255 reserved by IANA for future use, 3319170823Sbz * 0 for implementation specific, local use. 3320170823Sbz */ 3321170823Sbz if (ntohl(sav->spi) <= 255) { 3322120585Ssam ipseclog((LOG_DEBUG, "%s: illegal range of SPI %u.\n", 3323120585Ssam __func__, (u_int32_t)ntohl(sav->spi))); 3324105197Ssam return EINVAL; 3325105197Ssam } 3326105197Ssam break; 3327105197Ssam } 3328105197Ssam 3329105197Ssam /* check satype */ 3330105197Ssam switch (sav->sah->saidx.proto) { 3331105197Ssam case IPPROTO_ESP: 3332105197Ssam /* check flags */ 3333105197Ssam if ((sav->flags & (SADB_X_EXT_OLD|SADB_X_EXT_DERIV)) == 3334105197Ssam (SADB_X_EXT_OLD|SADB_X_EXT_DERIV)) { 3335120585Ssam ipseclog((LOG_DEBUG, "%s: invalid flag (derived) " 3336120585Ssam "given to old-esp.\n", __func__)); 3337105197Ssam return EINVAL; 3338105197Ssam } 3339105197Ssam error = xform_init(sav, XF_ESP); 3340105197Ssam break; 3341105197Ssam case IPPROTO_AH: 3342105197Ssam /* check flags */ 3343105197Ssam if (sav->flags & SADB_X_EXT_DERIV) { 3344120585Ssam ipseclog((LOG_DEBUG, "%s: invalid flag (derived) " 3345120585Ssam "given to AH SA.\n", __func__)); 3346105197Ssam return EINVAL; 3347105197Ssam } 3348105197Ssam if (sav->alg_enc != SADB_EALG_NONE) { 3349120585Ssam ipseclog((LOG_DEBUG, "%s: protocol and algorithm " 3350120585Ssam "mismated.\n", __func__)); 3351105197Ssam return(EINVAL); 3352105197Ssam } 3353105197Ssam error = xform_init(sav, XF_AH); 3354105197Ssam break; 3355105197Ssam case IPPROTO_IPCOMP: 3356105197Ssam if (sav->alg_auth != SADB_AALG_NONE) { 3357120585Ssam ipseclog((LOG_DEBUG, "%s: protocol and algorithm " 3358120585Ssam "mismated.\n", __func__)); 3359105197Ssam return(EINVAL); 3360105197Ssam } 3361105197Ssam if ((sav->flags & SADB_X_EXT_RAWCPI) == 0 3362105197Ssam && ntohl(sav->spi) >= 0x10000) { 3363120585Ssam ipseclog((LOG_DEBUG, "%s: invalid cpi for IPComp.\n", 3364120585Ssam __func__)); 3365105197Ssam return(EINVAL); 3366105197Ssam } 3367105197Ssam error = xform_init(sav, XF_IPCOMP); 3368105197Ssam break; 3369125680Sbms case IPPROTO_TCP: 3370125680Sbms if (sav->alg_enc != SADB_EALG_NONE) { 3371125680Sbms ipseclog((LOG_DEBUG, "%s: protocol and algorithm " 3372125680Sbms "mismated.\n", __func__)); 3373125680Sbms return(EINVAL); 3374125680Sbms } 3375125680Sbms error = xform_init(sav, XF_TCPSIGNATURE); 3376125680Sbms break; 3377105197Ssam default: 3378120585Ssam ipseclog((LOG_DEBUG, "%s: Invalid satype.\n", __func__)); 3379105197Ssam error = EPROTONOSUPPORT; 3380105197Ssam break; 3381105197Ssam } 3382119643Ssam if (error == 0) { 3383120585Ssam SAHTREE_LOCK(); 3384105197Ssam key_sa_chgstate(sav, SADB_SASTATE_MATURE); 3385120585Ssam SAHTREE_UNLOCK(); 3386119643Ssam } 3387105197Ssam return (error); 3388105197Ssam} 3389105197Ssam 3390105197Ssam/* 3391105197Ssam * subroutine for SADB_GET and SADB_DUMP. 3392105197Ssam */ 3393105197Ssamstatic struct mbuf * 3394189004Srdivackykey_setdumpsa(struct secasvar *sav, u_int8_t type, u_int8_t satype, 3395189004Srdivacky u_int32_t seq, u_int32_t pid) 3396105197Ssam{ 3397105197Ssam struct mbuf *result = NULL, *tres = NULL, *m; 3398105197Ssam int i; 3399105197Ssam int dumporder[] = { 3400105197Ssam SADB_EXT_SA, SADB_X_EXT_SA2, 3401105197Ssam SADB_EXT_LIFETIME_HARD, SADB_EXT_LIFETIME_SOFT, 3402105197Ssam SADB_EXT_LIFETIME_CURRENT, SADB_EXT_ADDRESS_SRC, 3403105197Ssam SADB_EXT_ADDRESS_DST, SADB_EXT_ADDRESS_PROXY, SADB_EXT_KEY_AUTH, 3404105197Ssam SADB_EXT_KEY_ENCRYPT, SADB_EXT_IDENTITY_SRC, 3405105197Ssam SADB_EXT_IDENTITY_DST, SADB_EXT_SENSITIVITY, 3406194062Svanhu#ifdef IPSEC_NAT_T 3407194062Svanhu SADB_X_EXT_NAT_T_TYPE, 3408194062Svanhu SADB_X_EXT_NAT_T_SPORT, SADB_X_EXT_NAT_T_DPORT, 3409194062Svanhu SADB_X_EXT_NAT_T_OAI, SADB_X_EXT_NAT_T_OAR, 3410194062Svanhu SADB_X_EXT_NAT_T_FRAG, 3411194062Svanhu#endif 3412105197Ssam }; 3413105197Ssam 3414105197Ssam m = key_setsadbmsg(type, 0, satype, seq, pid, sav->refcnt); 3415105197Ssam if (m == NULL) 3416105197Ssam goto fail; 3417105197Ssam result = m; 3418105197Ssam 3419105197Ssam for (i = sizeof(dumporder)/sizeof(dumporder[0]) - 1; i >= 0; i--) { 3420105197Ssam m = NULL; 3421105197Ssam switch (dumporder[i]) { 3422105197Ssam case SADB_EXT_SA: 3423105197Ssam m = key_setsadbsa(sav); 3424105197Ssam if (!m) 3425105197Ssam goto fail; 3426105197Ssam break; 3427105197Ssam 3428105197Ssam case SADB_X_EXT_SA2: 3429105197Ssam m = key_setsadbxsa2(sav->sah->saidx.mode, 3430105197Ssam sav->replay ? sav->replay->count : 0, 3431105197Ssam sav->sah->saidx.reqid); 3432105197Ssam if (!m) 3433105197Ssam goto fail; 3434105197Ssam break; 3435105197Ssam 3436105197Ssam case SADB_EXT_ADDRESS_SRC: 3437105197Ssam m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC, 3438105197Ssam &sav->sah->saidx.src.sa, 3439105197Ssam FULLMASK, IPSEC_ULPROTO_ANY); 3440105197Ssam if (!m) 3441105197Ssam goto fail; 3442105197Ssam break; 3443105197Ssam 3444105197Ssam case SADB_EXT_ADDRESS_DST: 3445105197Ssam m = key_setsadbaddr(SADB_EXT_ADDRESS_DST, 3446105197Ssam &sav->sah->saidx.dst.sa, 3447105197Ssam FULLMASK, IPSEC_ULPROTO_ANY); 3448105197Ssam if (!m) 3449105197Ssam goto fail; 3450105197Ssam break; 3451105197Ssam 3452105197Ssam case SADB_EXT_KEY_AUTH: 3453105197Ssam if (!sav->key_auth) 3454105197Ssam continue; 3455157123Sgnn m = key_setkey(sav->key_auth, SADB_EXT_KEY_AUTH); 3456157123Sgnn if (!m) 3457157123Sgnn goto fail; 3458105197Ssam break; 3459105197Ssam 3460105197Ssam case SADB_EXT_KEY_ENCRYPT: 3461105197Ssam if (!sav->key_enc) 3462105197Ssam continue; 3463157123Sgnn m = key_setkey(sav->key_enc, SADB_EXT_KEY_ENCRYPT); 3464157123Sgnn if (!m) 3465157123Sgnn goto fail; 3466105197Ssam break; 3467105197Ssam 3468105197Ssam case SADB_EXT_LIFETIME_CURRENT: 3469105197Ssam if (!sav->lft_c) 3470105197Ssam continue; 3471157123Sgnn m = key_setlifetime(sav->lft_c, 3472157123Sgnn SADB_EXT_LIFETIME_CURRENT); 3473157123Sgnn if (!m) 3474157123Sgnn goto fail; 3475105197Ssam break; 3476105197Ssam 3477105197Ssam case SADB_EXT_LIFETIME_HARD: 3478105197Ssam if (!sav->lft_h) 3479105197Ssam continue; 3480157123Sgnn m = key_setlifetime(sav->lft_h, 3481157123Sgnn SADB_EXT_LIFETIME_HARD); 3482157123Sgnn if (!m) 3483157123Sgnn goto fail; 3484105197Ssam break; 3485105197Ssam 3486105197Ssam case SADB_EXT_LIFETIME_SOFT: 3487105197Ssam if (!sav->lft_s) 3488105197Ssam continue; 3489177554Sbz m = key_setlifetime(sav->lft_s, 3490157123Sgnn SADB_EXT_LIFETIME_SOFT); 3491157123Sgnn 3492157123Sgnn if (!m) 3493157123Sgnn goto fail; 3494105197Ssam break; 3495105197Ssam 3496194062Svanhu#ifdef IPSEC_NAT_T 3497194062Svanhu case SADB_X_EXT_NAT_T_TYPE: 3498194062Svanhu m = key_setsadbxtype(sav->natt_type); 3499194062Svanhu if (!m) 3500194062Svanhu goto fail; 3501194062Svanhu break; 3502194062Svanhu 3503194062Svanhu case SADB_X_EXT_NAT_T_DPORT: 3504194062Svanhu m = key_setsadbxport( 3505194062Svanhu KEY_PORTFROMSADDR(&sav->sah->saidx.dst), 3506194062Svanhu SADB_X_EXT_NAT_T_DPORT); 3507194062Svanhu if (!m) 3508194062Svanhu goto fail; 3509194062Svanhu break; 3510194062Svanhu 3511194062Svanhu case SADB_X_EXT_NAT_T_SPORT: 3512194062Svanhu m = key_setsadbxport( 3513194062Svanhu KEY_PORTFROMSADDR(&sav->sah->saidx.src), 3514194062Svanhu SADB_X_EXT_NAT_T_SPORT); 3515194062Svanhu if (!m) 3516194062Svanhu goto fail; 3517194062Svanhu break; 3518194062Svanhu 3519194062Svanhu case SADB_X_EXT_NAT_T_OAI: 3520194062Svanhu case SADB_X_EXT_NAT_T_OAR: 3521194062Svanhu case SADB_X_EXT_NAT_T_FRAG: 3522194062Svanhu /* We do not (yet) support those. */ 3523194062Svanhu continue; 3524194062Svanhu#endif 3525194062Svanhu 3526105197Ssam case SADB_EXT_ADDRESS_PROXY: 3527105197Ssam case SADB_EXT_IDENTITY_SRC: 3528105197Ssam case SADB_EXT_IDENTITY_DST: 3529105197Ssam /* XXX: should we brought from SPD ? */ 3530105197Ssam case SADB_EXT_SENSITIVITY: 3531105197Ssam default: 3532105197Ssam continue; 3533105197Ssam } 3534105197Ssam 3535157123Sgnn if (!m) 3536105197Ssam goto fail; 3537105197Ssam if (tres) 3538105197Ssam m_cat(m, tres); 3539105197Ssam tres = m; 3540157123Sgnn 3541105197Ssam } 3542105197Ssam 3543105197Ssam m_cat(result, tres); 3544105197Ssam if (result->m_len < sizeof(struct sadb_msg)) { 3545105197Ssam result = m_pullup(result, sizeof(struct sadb_msg)); 3546105197Ssam if (result == NULL) 3547105197Ssam goto fail; 3548105197Ssam } 3549105197Ssam 3550105197Ssam result->m_pkthdr.len = 0; 3551105197Ssam for (m = result; m; m = m->m_next) 3552105197Ssam result->m_pkthdr.len += m->m_len; 3553105197Ssam 3554105197Ssam mtod(result, struct sadb_msg *)->sadb_msg_len = 3555105197Ssam PFKEY_UNIT64(result->m_pkthdr.len); 3556105197Ssam 3557105197Ssam return result; 3558105197Ssam 3559105197Ssamfail: 3560105197Ssam m_freem(result); 3561105197Ssam m_freem(tres); 3562105197Ssam return NULL; 3563105197Ssam} 3564105197Ssam 3565105197Ssam/* 3566105197Ssam * set data into sadb_msg. 3567105197Ssam */ 3568105197Ssamstatic struct mbuf * 3569189004Srdivackykey_setsadbmsg(u_int8_t type, u_int16_t tlen, u_int8_t satype, u_int32_t seq, 3570189004Srdivacky pid_t pid, u_int16_t reserved) 3571105197Ssam{ 3572105197Ssam struct mbuf *m; 3573105197Ssam struct sadb_msg *p; 3574105197Ssam int len; 3575105197Ssam 3576105197Ssam len = PFKEY_ALIGN8(sizeof(struct sadb_msg)); 3577105197Ssam if (len > MCLBYTES) 3578105197Ssam return NULL; 3579111119Simp MGETHDR(m, M_DONTWAIT, MT_DATA); 3580105197Ssam if (m && len > MHLEN) { 3581111119Simp MCLGET(m, M_DONTWAIT); 3582105197Ssam if ((m->m_flags & M_EXT) == 0) { 3583105197Ssam m_freem(m); 3584105197Ssam m = NULL; 3585105197Ssam } 3586105197Ssam } 3587105197Ssam if (!m) 3588105197Ssam return NULL; 3589105197Ssam m->m_pkthdr.len = m->m_len = len; 3590105197Ssam m->m_next = NULL; 3591105197Ssam 3592105197Ssam p = mtod(m, struct sadb_msg *); 3593105197Ssam 3594105197Ssam bzero(p, len); 3595105197Ssam p->sadb_msg_version = PF_KEY_V2; 3596105197Ssam p->sadb_msg_type = type; 3597105197Ssam p->sadb_msg_errno = 0; 3598105197Ssam p->sadb_msg_satype = satype; 3599105197Ssam p->sadb_msg_len = PFKEY_UNIT64(tlen); 3600105197Ssam p->sadb_msg_reserved = reserved; 3601105197Ssam p->sadb_msg_seq = seq; 3602105197Ssam p->sadb_msg_pid = (u_int32_t)pid; 3603105197Ssam 3604105197Ssam return m; 3605105197Ssam} 3606105197Ssam 3607105197Ssam/* 3608105197Ssam * copy secasvar data into sadb_address. 3609105197Ssam */ 3610105197Ssamstatic struct mbuf * 3611105197Ssamkey_setsadbsa(sav) 3612105197Ssam struct secasvar *sav; 3613105197Ssam{ 3614105197Ssam struct mbuf *m; 3615105197Ssam struct sadb_sa *p; 3616105197Ssam int len; 3617105197Ssam 3618105197Ssam len = PFKEY_ALIGN8(sizeof(struct sadb_sa)); 3619105197Ssam m = key_alloc_mbuf(len); 3620105197Ssam if (!m || m->m_next) { /*XXX*/ 3621105197Ssam if (m) 3622105197Ssam m_freem(m); 3623105197Ssam return NULL; 3624105197Ssam } 3625105197Ssam 3626105197Ssam p = mtod(m, struct sadb_sa *); 3627105197Ssam 3628105197Ssam bzero(p, len); 3629105197Ssam p->sadb_sa_len = PFKEY_UNIT64(len); 3630105197Ssam p->sadb_sa_exttype = SADB_EXT_SA; 3631105197Ssam p->sadb_sa_spi = sav->spi; 3632105197Ssam p->sadb_sa_replay = (sav->replay != NULL ? sav->replay->wsize : 0); 3633105197Ssam p->sadb_sa_state = sav->state; 3634105197Ssam p->sadb_sa_auth = sav->alg_auth; 3635105197Ssam p->sadb_sa_encrypt = sav->alg_enc; 3636105197Ssam p->sadb_sa_flags = sav->flags; 3637105197Ssam 3638105197Ssam return m; 3639105197Ssam} 3640105197Ssam 3641105197Ssam/* 3642105197Ssam * set data into sadb_address. 3643105197Ssam */ 3644105197Ssamstatic struct mbuf * 3645189004Srdivackykey_setsadbaddr(u_int16_t exttype, const struct sockaddr *saddr, u_int8_t prefixlen, u_int16_t ul_proto) 3646105197Ssam{ 3647105197Ssam struct mbuf *m; 3648105197Ssam struct sadb_address *p; 3649105197Ssam size_t len; 3650105197Ssam 3651105197Ssam len = PFKEY_ALIGN8(sizeof(struct sadb_address)) + 3652105197Ssam PFKEY_ALIGN8(saddr->sa_len); 3653105197Ssam m = key_alloc_mbuf(len); 3654105197Ssam if (!m || m->m_next) { /*XXX*/ 3655105197Ssam if (m) 3656105197Ssam m_freem(m); 3657105197Ssam return NULL; 3658105197Ssam } 3659105197Ssam 3660105197Ssam p = mtod(m, struct sadb_address *); 3661105197Ssam 3662105197Ssam bzero(p, len); 3663105197Ssam p->sadb_address_len = PFKEY_UNIT64(len); 3664105197Ssam p->sadb_address_exttype = exttype; 3665105197Ssam p->sadb_address_proto = ul_proto; 3666105197Ssam if (prefixlen == FULLMASK) { 3667105197Ssam switch (saddr->sa_family) { 3668105197Ssam case AF_INET: 3669105197Ssam prefixlen = sizeof(struct in_addr) << 3; 3670105197Ssam break; 3671105197Ssam case AF_INET6: 3672105197Ssam prefixlen = sizeof(struct in6_addr) << 3; 3673105197Ssam break; 3674105197Ssam default: 3675105197Ssam ; /*XXX*/ 3676105197Ssam } 3677105197Ssam } 3678105197Ssam p->sadb_address_prefixlen = prefixlen; 3679105197Ssam p->sadb_address_reserved = 0; 3680105197Ssam 3681105197Ssam bcopy(saddr, 3682105197Ssam mtod(m, caddr_t) + PFKEY_ALIGN8(sizeof(struct sadb_address)), 3683105197Ssam saddr->sa_len); 3684105197Ssam 3685105197Ssam return m; 3686105197Ssam} 3687105197Ssam 3688105197Ssam/* 3689105197Ssam * set data into sadb_x_sa2. 3690105197Ssam */ 3691105197Ssamstatic struct mbuf * 3692189004Srdivackykey_setsadbxsa2(u_int8_t mode, u_int32_t seq, u_int32_t reqid) 3693105197Ssam{ 3694105197Ssam struct mbuf *m; 3695105197Ssam struct sadb_x_sa2 *p; 3696105197Ssam size_t len; 3697105197Ssam 3698105197Ssam len = PFKEY_ALIGN8(sizeof(struct sadb_x_sa2)); 3699105197Ssam m = key_alloc_mbuf(len); 3700105197Ssam if (!m || m->m_next) { /*XXX*/ 3701105197Ssam if (m) 3702105197Ssam m_freem(m); 3703105197Ssam return NULL; 3704105197Ssam } 3705105197Ssam 3706105197Ssam p = mtod(m, struct sadb_x_sa2 *); 3707105197Ssam 3708105197Ssam bzero(p, len); 3709105197Ssam p->sadb_x_sa2_len = PFKEY_UNIT64(len); 3710105197Ssam p->sadb_x_sa2_exttype = SADB_X_EXT_SA2; 3711105197Ssam p->sadb_x_sa2_mode = mode; 3712105197Ssam p->sadb_x_sa2_reserved1 = 0; 3713105197Ssam p->sadb_x_sa2_reserved2 = 0; 3714105197Ssam p->sadb_x_sa2_sequence = seq; 3715105197Ssam p->sadb_x_sa2_reqid = reqid; 3716105197Ssam 3717105197Ssam return m; 3718105197Ssam} 3719105197Ssam 3720194062Svanhu#ifdef IPSEC_NAT_T 3721105197Ssam/* 3722194062Svanhu * Set a type in sadb_x_nat_t_type. 3723194062Svanhu */ 3724194062Svanhustatic struct mbuf * 3725194062Svanhukey_setsadbxtype(u_int16_t type) 3726194062Svanhu{ 3727194062Svanhu struct mbuf *m; 3728194062Svanhu size_t len; 3729194062Svanhu struct sadb_x_nat_t_type *p; 3730194062Svanhu 3731194062Svanhu len = PFKEY_ALIGN8(sizeof(struct sadb_x_nat_t_type)); 3732194062Svanhu 3733194062Svanhu m = key_alloc_mbuf(len); 3734194062Svanhu if (!m || m->m_next) { /*XXX*/ 3735194062Svanhu if (m) 3736194062Svanhu m_freem(m); 3737194062Svanhu return (NULL); 3738194062Svanhu } 3739194062Svanhu 3740194062Svanhu p = mtod(m, struct sadb_x_nat_t_type *); 3741194062Svanhu 3742194062Svanhu bzero(p, len); 3743194062Svanhu p->sadb_x_nat_t_type_len = PFKEY_UNIT64(len); 3744194062Svanhu p->sadb_x_nat_t_type_exttype = SADB_X_EXT_NAT_T_TYPE; 3745194062Svanhu p->sadb_x_nat_t_type_type = type; 3746194062Svanhu 3747194062Svanhu return (m); 3748194062Svanhu} 3749194062Svanhu/* 3750194062Svanhu * Set a port in sadb_x_nat_t_port. 3751194062Svanhu * In contrast to default RFC 2367 behaviour, port is in network byte order. 3752194062Svanhu */ 3753194062Svanhustatic struct mbuf * 3754194062Svanhukey_setsadbxport(u_int16_t port, u_int16_t type) 3755194062Svanhu{ 3756194062Svanhu struct mbuf *m; 3757194062Svanhu size_t len; 3758194062Svanhu struct sadb_x_nat_t_port *p; 3759194062Svanhu 3760194062Svanhu len = PFKEY_ALIGN8(sizeof(struct sadb_x_nat_t_port)); 3761194062Svanhu 3762194062Svanhu m = key_alloc_mbuf(len); 3763194062Svanhu if (!m || m->m_next) { /*XXX*/ 3764194062Svanhu if (m) 3765194062Svanhu m_freem(m); 3766194062Svanhu return (NULL); 3767194062Svanhu } 3768194062Svanhu 3769194062Svanhu p = mtod(m, struct sadb_x_nat_t_port *); 3770194062Svanhu 3771194062Svanhu bzero(p, len); 3772194062Svanhu p->sadb_x_nat_t_port_len = PFKEY_UNIT64(len); 3773194062Svanhu p->sadb_x_nat_t_port_exttype = type; 3774194062Svanhu p->sadb_x_nat_t_port_port = port; 3775194062Svanhu 3776194062Svanhu return (m); 3777194062Svanhu} 3778194062Svanhu 3779194062Svanhu/* 3780194062Svanhu * Get port from sockaddr. Port is in network byte order. 3781194062Svanhu */ 3782194062Svanhuu_int16_t 3783194062Svanhukey_portfromsaddr(struct sockaddr *sa) 3784194062Svanhu{ 3785194062Svanhu INIT_VNET_IPSEC(curvnet); 3786194062Svanhu 3787194062Svanhu switch (sa->sa_family) { 3788194062Svanhu#ifdef INET 3789194062Svanhu case AF_INET: 3790194062Svanhu return ((struct sockaddr_in *)sa)->sin_port; 3791194062Svanhu#endif 3792194062Svanhu#ifdef INET6 3793194062Svanhu case AF_INET6: 3794194062Svanhu return ((struct sockaddr_in6 *)sa)->sin6_port; 3795194062Svanhu#endif 3796194062Svanhu } 3797194062Svanhu KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 3798194062Svanhu printf("DP %s unexpected address family %d\n", 3799194062Svanhu __func__, sa->sa_family)); 3800194062Svanhu return (0); 3801194062Svanhu} 3802194062Svanhu#endif /* IPSEC_NAT_T */ 3803194062Svanhu 3804194062Svanhu/* 3805194062Svanhu * Set port in struct sockaddr. Port is in network byte order. 3806194062Svanhu */ 3807194062Svanhustatic void 3808194062Svanhukey_porttosaddr(struct sockaddr *sa, u_int16_t port) 3809194062Svanhu{ 3810194062Svanhu INIT_VNET_IPSEC(curvnet); 3811194062Svanhu 3812194062Svanhu switch (sa->sa_family) { 3813194062Svanhu#ifdef INET 3814194062Svanhu case AF_INET: 3815194062Svanhu ((struct sockaddr_in *)sa)->sin_port = port; 3816194062Svanhu break; 3817194062Svanhu#endif 3818194062Svanhu#ifdef INET6 3819194062Svanhu case AF_INET6: 3820194062Svanhu ((struct sockaddr_in6 *)sa)->sin6_port = port; 3821194062Svanhu break; 3822194062Svanhu#endif 3823194062Svanhu default: 3824194062Svanhu ipseclog((LOG_DEBUG, "%s: unexpected address family %d.\n", 3825194062Svanhu __func__, sa->sa_family)); 3826194062Svanhu break; 3827194062Svanhu } 3828194062Svanhu} 3829194062Svanhu 3830194062Svanhu/* 3831105197Ssam * set data into sadb_x_policy 3832105197Ssam */ 3833105197Ssamstatic struct mbuf * 3834189004Srdivackykey_setsadbxpolicy(u_int16_t type, u_int8_t dir, u_int32_t id) 3835105197Ssam{ 3836105197Ssam struct mbuf *m; 3837105197Ssam struct sadb_x_policy *p; 3838105197Ssam size_t len; 3839105197Ssam 3840105197Ssam len = PFKEY_ALIGN8(sizeof(struct sadb_x_policy)); 3841105197Ssam m = key_alloc_mbuf(len); 3842105197Ssam if (!m || m->m_next) { /*XXX*/ 3843105197Ssam if (m) 3844105197Ssam m_freem(m); 3845105197Ssam return NULL; 3846105197Ssam } 3847105197Ssam 3848105197Ssam p = mtod(m, struct sadb_x_policy *); 3849105197Ssam 3850105197Ssam bzero(p, len); 3851105197Ssam p->sadb_x_policy_len = PFKEY_UNIT64(len); 3852105197Ssam p->sadb_x_policy_exttype = SADB_X_EXT_POLICY; 3853105197Ssam p->sadb_x_policy_type = type; 3854105197Ssam p->sadb_x_policy_dir = dir; 3855105197Ssam p->sadb_x_policy_id = id; 3856105197Ssam 3857105197Ssam return m; 3858105197Ssam} 3859105197Ssam 3860105197Ssam/* %%% utilities */ 3861157123Sgnn/* Take a key message (sadb_key) from the socket and turn it into one 3862157123Sgnn * of the kernel's key structures (seckey). 3863157123Sgnn * 3864157123Sgnn * IN: pointer to the src 3865157123Sgnn * OUT: NULL no more memory 3866105197Ssam */ 3867157123Sgnnstruct seckey * 3868157123Sgnnkey_dup_keymsg(const struct sadb_key *src, u_int len, 3869157123Sgnn struct malloc_type *type) 3870105197Ssam{ 3871183550Szec INIT_VNET_IPSEC(curvnet); 3872157123Sgnn struct seckey *dst; 3873157123Sgnn dst = (struct seckey *)malloc(sizeof(struct seckey), type, M_NOWAIT); 3874157123Sgnn if (dst != NULL) { 3875157123Sgnn dst->bits = src->sadb_key_bits; 3876157123Sgnn dst->key_data = (char *)malloc(len, type, M_NOWAIT); 3877157123Sgnn if (dst->key_data != NULL) { 3878157123Sgnn bcopy((const char *)src + sizeof(struct sadb_key), 3879157123Sgnn dst->key_data, len); 3880157123Sgnn } else { 3881157123Sgnn ipseclog((LOG_DEBUG, "%s: No more memory.\n", 3882157123Sgnn __func__)); 3883157123Sgnn free(dst, type); 3884157123Sgnn dst = NULL; 3885157123Sgnn } 3886157123Sgnn } else { 3887157123Sgnn ipseclog((LOG_DEBUG, "%s: No more memory.\n", 3888157123Sgnn __func__)); 3889105197Ssam 3890157123Sgnn } 3891157123Sgnn return dst; 3892157123Sgnn} 3893157123Sgnn 3894157123Sgnn/* Take a lifetime message (sadb_lifetime) passed in on a socket and 3895157123Sgnn * turn it into one of the kernel's lifetime structures (seclifetime). 3896157123Sgnn * 3897157123Sgnn * IN: pointer to the destination, source and malloc type 3898157123Sgnn * OUT: NULL, no more memory 3899157123Sgnn */ 3900157123Sgnn 3901157123Sgnnstatic struct seclifetime * 3902157123Sgnnkey_dup_lifemsg(const struct sadb_lifetime *src, 3903157123Sgnn struct malloc_type *type) 3904157123Sgnn{ 3905183550Szec INIT_VNET_IPSEC(curvnet); 3906157123Sgnn struct seclifetime *dst = NULL; 3907157123Sgnn 3908157123Sgnn dst = (struct seclifetime *)malloc(sizeof(struct seclifetime), 3909157123Sgnn type, M_NOWAIT); 3910157123Sgnn if (dst == NULL) { 3911119643Ssam /* XXX counter */ 3912120585Ssam ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__)); 3913157123Sgnn } else { 3914157123Sgnn dst->allocations = src->sadb_lifetime_allocations; 3915157123Sgnn dst->bytes = src->sadb_lifetime_bytes; 3916157123Sgnn dst->addtime = src->sadb_lifetime_addtime; 3917157123Sgnn dst->usetime = src->sadb_lifetime_usetime; 3918157123Sgnn } 3919157123Sgnn return dst; 3920105197Ssam} 3921105197Ssam 3922105197Ssam/* compare my own address 3923105197Ssam * OUT: 1: true, i.e. my address. 3924105197Ssam * 0: false 3925105197Ssam */ 3926105197Ssamint 3927105197Ssamkey_ismyaddr(sa) 3928105197Ssam struct sockaddr *sa; 3929105197Ssam{ 3930105197Ssam#ifdef INET 3931183550Szec INIT_VNET_INET(curvnet); 3932105197Ssam struct sockaddr_in *sin; 3933105197Ssam struct in_ifaddr *ia; 3934105197Ssam#endif 3935105197Ssam 3936120585Ssam IPSEC_ASSERT(sa != NULL, ("null sockaddr")); 3937105197Ssam 3938105197Ssam switch (sa->sa_family) { 3939105197Ssam#ifdef INET 3940105197Ssam case AF_INET: 3941105197Ssam sin = (struct sockaddr_in *)sa; 3942181803Sbz for (ia = V_in_ifaddrhead.tqh_first; ia; 3943105197Ssam ia = ia->ia_link.tqe_next) 3944105197Ssam { 3945105197Ssam if (sin->sin_family == ia->ia_addr.sin_family && 3946105197Ssam sin->sin_len == ia->ia_addr.sin_len && 3947105197Ssam sin->sin_addr.s_addr == ia->ia_addr.sin_addr.s_addr) 3948105197Ssam { 3949105197Ssam return 1; 3950105197Ssam } 3951105197Ssam } 3952105197Ssam break; 3953105197Ssam#endif 3954105197Ssam#ifdef INET6 3955105197Ssam case AF_INET6: 3956105197Ssam return key_ismyaddr6((struct sockaddr_in6 *)sa); 3957105197Ssam#endif 3958105197Ssam } 3959105197Ssam 3960105197Ssam return 0; 3961105197Ssam} 3962105197Ssam 3963105197Ssam#ifdef INET6 3964105197Ssam/* 3965105197Ssam * compare my own address for IPv6. 3966105197Ssam * 1: ours 3967105197Ssam * 0: other 3968105197Ssam * NOTE: derived ip6_input() in KAME. This is necessary to modify more. 3969105197Ssam */ 3970105197Ssam#include <netinet6/in6_var.h> 3971105197Ssam 3972105197Ssamstatic int 3973105197Ssamkey_ismyaddr6(sin6) 3974105197Ssam struct sockaddr_in6 *sin6; 3975105197Ssam{ 3976183550Szec INIT_VNET_INET6(curvnet); 3977105197Ssam struct in6_ifaddr *ia; 3978191663Sbms#if 0 3979105197Ssam struct in6_multi *in6m; 3980191663Sbms#endif 3981105197Ssam 3982181803Sbz for (ia = V_in6_ifaddr; ia; ia = ia->ia_next) { 3983105197Ssam if (key_sockaddrcmp((struct sockaddr *)&sin6, 3984105197Ssam (struct sockaddr *)&ia->ia_addr, 0) == 0) 3985105197Ssam return 1; 3986105197Ssam 3987191663Sbms#if 0 3988105197Ssam /* 3989105197Ssam * XXX Multicast 3990105197Ssam * XXX why do we care about multlicast here while we don't care 3991105197Ssam * about IPv4 multicast?? 3992105197Ssam * XXX scope 3993105197Ssam */ 3994105197Ssam in6m = NULL; 3995105197Ssam IN6_LOOKUP_MULTI(sin6->sin6_addr, ia->ia_ifp, in6m); 3996105197Ssam if (in6m) 3997105197Ssam return 1; 3998191663Sbms#endif 3999105197Ssam } 4000105197Ssam 4001105197Ssam /* loopback, just for safety */ 4002105197Ssam if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) 4003105197Ssam return 1; 4004105197Ssam 4005105197Ssam return 0; 4006105197Ssam} 4007105197Ssam#endif /*INET6*/ 4008105197Ssam 4009105197Ssam/* 4010105197Ssam * compare two secasindex structure. 4011105197Ssam * flag can specify to compare 2 saidxes. 4012105197Ssam * compare two secasindex structure without both mode and reqid. 4013105197Ssam * don't compare port. 4014105197Ssam * IN: 4015105197Ssam * saidx0: source, it can be in SAD. 4016105197Ssam * saidx1: object. 4017105197Ssam * OUT: 4018105197Ssam * 1 : equal 4019105197Ssam * 0 : not equal 4020105197Ssam */ 4021105197Ssamstatic int 4022105197Ssamkey_cmpsaidx( 4023105197Ssam const struct secasindex *saidx0, 4024105197Ssam const struct secasindex *saidx1, 4025105197Ssam int flag) 4026105197Ssam{ 4027194062Svanhu int chkport = 0; 4028194062Svanhu 4029105197Ssam /* sanity */ 4030105197Ssam if (saidx0 == NULL && saidx1 == NULL) 4031105197Ssam return 1; 4032105197Ssam 4033105197Ssam if (saidx0 == NULL || saidx1 == NULL) 4034105197Ssam return 0; 4035105197Ssam 4036105197Ssam if (saidx0->proto != saidx1->proto) 4037105197Ssam return 0; 4038105197Ssam 4039105197Ssam if (flag == CMP_EXACTLY) { 4040105197Ssam if (saidx0->mode != saidx1->mode) 4041105197Ssam return 0; 4042105197Ssam if (saidx0->reqid != saidx1->reqid) 4043105197Ssam return 0; 4044105197Ssam if (bcmp(&saidx0->src, &saidx1->src, saidx0->src.sa.sa_len) != 0 || 4045105197Ssam bcmp(&saidx0->dst, &saidx1->dst, saidx0->dst.sa.sa_len) != 0) 4046105197Ssam return 0; 4047105197Ssam } else { 4048105197Ssam 4049105197Ssam /* CMP_MODE_REQID, CMP_REQID, CMP_HEAD */ 4050105197Ssam if (flag == CMP_MODE_REQID 4051105197Ssam ||flag == CMP_REQID) { 4052105197Ssam /* 4053105197Ssam * If reqid of SPD is non-zero, unique SA is required. 4054105197Ssam * The result must be of same reqid in this case. 4055105197Ssam */ 4056105197Ssam if (saidx1->reqid != 0 && saidx0->reqid != saidx1->reqid) 4057105197Ssam return 0; 4058105197Ssam } 4059105197Ssam 4060105197Ssam if (flag == CMP_MODE_REQID) { 4061105197Ssam if (saidx0->mode != IPSEC_MODE_ANY 4062105197Ssam && saidx0->mode != saidx1->mode) 4063105197Ssam return 0; 4064105197Ssam } 4065105197Ssam 4066194062Svanhu#ifdef IPSEC_NAT_T 4067194062Svanhu /* 4068194062Svanhu * If NAT-T is enabled, check ports for tunnel mode. 4069194062Svanhu * Do not check ports if they are set to zero in the SPD. 4070194062Svanhu * Also do not do it for transport mode, as there is no 4071194062Svanhu * port information available in the SP. 4072194062Svanhu */ 4073194062Svanhu if (saidx1->mode == IPSEC_MODE_TUNNEL && 4074194062Svanhu saidx1->src.sa.sa_family == AF_INET && 4075194062Svanhu saidx1->dst.sa.sa_family == AF_INET && 4076194062Svanhu ((const struct sockaddr_in *)(&saidx1->src))->sin_port && 4077194062Svanhu ((const struct sockaddr_in *)(&saidx1->dst))->sin_port) 4078194062Svanhu chkport = 1; 4079194062Svanhu#endif /* IPSEC_NAT_T */ 4080194062Svanhu 4081194062Svanhu if (key_sockaddrcmp(&saidx0->src.sa, &saidx1->src.sa, chkport) != 0) { 4082105197Ssam return 0; 4083105197Ssam } 4084194062Svanhu if (key_sockaddrcmp(&saidx0->dst.sa, &saidx1->dst.sa, chkport) != 0) { 4085105197Ssam return 0; 4086105197Ssam } 4087105197Ssam } 4088105197Ssam 4089105197Ssam return 1; 4090105197Ssam} 4091105197Ssam 4092105197Ssam/* 4093105197Ssam * compare two secindex structure exactly. 4094105197Ssam * IN: 4095105197Ssam * spidx0: source, it is often in SPD. 4096105197Ssam * spidx1: object, it is often from PFKEY message. 4097105197Ssam * OUT: 4098105197Ssam * 1 : equal 4099105197Ssam * 0 : not equal 4100105197Ssam */ 4101105197Ssamstatic int 4102105197Ssamkey_cmpspidx_exactly( 4103105197Ssam struct secpolicyindex *spidx0, 4104105197Ssam struct secpolicyindex *spidx1) 4105105197Ssam{ 4106105197Ssam /* sanity */ 4107105197Ssam if (spidx0 == NULL && spidx1 == NULL) 4108105197Ssam return 1; 4109105197Ssam 4110105197Ssam if (spidx0 == NULL || spidx1 == NULL) 4111105197Ssam return 0; 4112105197Ssam 4113105197Ssam if (spidx0->prefs != spidx1->prefs 4114105197Ssam || spidx0->prefd != spidx1->prefd 4115105197Ssam || spidx0->ul_proto != spidx1->ul_proto) 4116105197Ssam return 0; 4117105197Ssam 4118105197Ssam return key_sockaddrcmp(&spidx0->src.sa, &spidx1->src.sa, 1) == 0 && 4119105197Ssam key_sockaddrcmp(&spidx0->dst.sa, &spidx1->dst.sa, 1) == 0; 4120105197Ssam} 4121105197Ssam 4122105197Ssam/* 4123105197Ssam * compare two secindex structure with mask. 4124105197Ssam * IN: 4125105197Ssam * spidx0: source, it is often in SPD. 4126105197Ssam * spidx1: object, it is often from IP header. 4127105197Ssam * OUT: 4128105197Ssam * 1 : equal 4129105197Ssam * 0 : not equal 4130105197Ssam */ 4131105197Ssamstatic int 4132105197Ssamkey_cmpspidx_withmask( 4133105197Ssam struct secpolicyindex *spidx0, 4134105197Ssam struct secpolicyindex *spidx1) 4135105197Ssam{ 4136105197Ssam /* sanity */ 4137105197Ssam if (spidx0 == NULL && spidx1 == NULL) 4138105197Ssam return 1; 4139105197Ssam 4140105197Ssam if (spidx0 == NULL || spidx1 == NULL) 4141105197Ssam return 0; 4142105197Ssam 4143105197Ssam if (spidx0->src.sa.sa_family != spidx1->src.sa.sa_family || 4144105197Ssam spidx0->dst.sa.sa_family != spidx1->dst.sa.sa_family || 4145105197Ssam spidx0->src.sa.sa_len != spidx1->src.sa.sa_len || 4146105197Ssam spidx0->dst.sa.sa_len != spidx1->dst.sa.sa_len) 4147105197Ssam return 0; 4148105197Ssam 4149105197Ssam /* if spidx.ul_proto == IPSEC_ULPROTO_ANY, ignore. */ 4150105197Ssam if (spidx0->ul_proto != (u_int16_t)IPSEC_ULPROTO_ANY 4151105197Ssam && spidx0->ul_proto != spidx1->ul_proto) 4152105197Ssam return 0; 4153105197Ssam 4154105197Ssam switch (spidx0->src.sa.sa_family) { 4155105197Ssam case AF_INET: 4156105197Ssam if (spidx0->src.sin.sin_port != IPSEC_PORT_ANY 4157105197Ssam && spidx0->src.sin.sin_port != spidx1->src.sin.sin_port) 4158105197Ssam return 0; 4159105197Ssam if (!key_bbcmp(&spidx0->src.sin.sin_addr, 4160105197Ssam &spidx1->src.sin.sin_addr, spidx0->prefs)) 4161105197Ssam return 0; 4162105197Ssam break; 4163105197Ssam case AF_INET6: 4164105197Ssam if (spidx0->src.sin6.sin6_port != IPSEC_PORT_ANY 4165105197Ssam && spidx0->src.sin6.sin6_port != spidx1->src.sin6.sin6_port) 4166105197Ssam return 0; 4167105197Ssam /* 4168105197Ssam * scope_id check. if sin6_scope_id is 0, we regard it 4169105197Ssam * as a wildcard scope, which matches any scope zone ID. 4170105197Ssam */ 4171105197Ssam if (spidx0->src.sin6.sin6_scope_id && 4172105197Ssam spidx1->src.sin6.sin6_scope_id && 4173105197Ssam spidx0->src.sin6.sin6_scope_id != spidx1->src.sin6.sin6_scope_id) 4174105197Ssam return 0; 4175105197Ssam if (!key_bbcmp(&spidx0->src.sin6.sin6_addr, 4176105197Ssam &spidx1->src.sin6.sin6_addr, spidx0->prefs)) 4177105197Ssam return 0; 4178105197Ssam break; 4179105197Ssam default: 4180105197Ssam /* XXX */ 4181105197Ssam if (bcmp(&spidx0->src, &spidx1->src, spidx0->src.sa.sa_len) != 0) 4182105197Ssam return 0; 4183105197Ssam break; 4184105197Ssam } 4185105197Ssam 4186105197Ssam switch (spidx0->dst.sa.sa_family) { 4187105197Ssam case AF_INET: 4188105197Ssam if (spidx0->dst.sin.sin_port != IPSEC_PORT_ANY 4189105197Ssam && spidx0->dst.sin.sin_port != spidx1->dst.sin.sin_port) 4190105197Ssam return 0; 4191105197Ssam if (!key_bbcmp(&spidx0->dst.sin.sin_addr, 4192105197Ssam &spidx1->dst.sin.sin_addr, spidx0->prefd)) 4193105197Ssam return 0; 4194105197Ssam break; 4195105197Ssam case AF_INET6: 4196105197Ssam if (spidx0->dst.sin6.sin6_port != IPSEC_PORT_ANY 4197105197Ssam && spidx0->dst.sin6.sin6_port != spidx1->dst.sin6.sin6_port) 4198105197Ssam return 0; 4199105197Ssam /* 4200105197Ssam * scope_id check. if sin6_scope_id is 0, we regard it 4201105197Ssam * as a wildcard scope, which matches any scope zone ID. 4202105197Ssam */ 4203130928Sbms if (spidx0->dst.sin6.sin6_scope_id && 4204130928Sbms spidx1->dst.sin6.sin6_scope_id && 4205105197Ssam spidx0->dst.sin6.sin6_scope_id != spidx1->dst.sin6.sin6_scope_id) 4206105197Ssam return 0; 4207105197Ssam if (!key_bbcmp(&spidx0->dst.sin6.sin6_addr, 4208105197Ssam &spidx1->dst.sin6.sin6_addr, spidx0->prefd)) 4209105197Ssam return 0; 4210105197Ssam break; 4211105197Ssam default: 4212105197Ssam /* XXX */ 4213105197Ssam if (bcmp(&spidx0->dst, &spidx1->dst, spidx0->dst.sa.sa_len) != 0) 4214105197Ssam return 0; 4215105197Ssam break; 4216105197Ssam } 4217105197Ssam 4218105197Ssam /* XXX Do we check other field ? e.g. flowinfo */ 4219105197Ssam 4220105197Ssam return 1; 4221105197Ssam} 4222105197Ssam 4223105197Ssam/* returns 0 on match */ 4224105197Ssamstatic int 4225105197Ssamkey_sockaddrcmp( 4226105197Ssam const struct sockaddr *sa1, 4227105197Ssam const struct sockaddr *sa2, 4228105197Ssam int port) 4229105197Ssam{ 4230105197Ssam#ifdef satosin 4231105197Ssam#undef satosin 4232105197Ssam#endif 4233105197Ssam#define satosin(s) ((const struct sockaddr_in *)s) 4234105197Ssam#ifdef satosin6 4235105197Ssam#undef satosin6 4236105197Ssam#endif 4237105197Ssam#define satosin6(s) ((const struct sockaddr_in6 *)s) 4238105197Ssam if (sa1->sa_family != sa2->sa_family || sa1->sa_len != sa2->sa_len) 4239105197Ssam return 1; 4240105197Ssam 4241105197Ssam switch (sa1->sa_family) { 4242105197Ssam case AF_INET: 4243105197Ssam if (sa1->sa_len != sizeof(struct sockaddr_in)) 4244105197Ssam return 1; 4245105197Ssam if (satosin(sa1)->sin_addr.s_addr != 4246105197Ssam satosin(sa2)->sin_addr.s_addr) { 4247105197Ssam return 1; 4248105197Ssam } 4249105197Ssam if (port && satosin(sa1)->sin_port != satosin(sa2)->sin_port) 4250105197Ssam return 1; 4251105197Ssam break; 4252105197Ssam case AF_INET6: 4253105197Ssam if (sa1->sa_len != sizeof(struct sockaddr_in6)) 4254105197Ssam return 1; /*EINVAL*/ 4255105197Ssam if (satosin6(sa1)->sin6_scope_id != 4256105197Ssam satosin6(sa2)->sin6_scope_id) { 4257105197Ssam return 1; 4258105197Ssam } 4259105197Ssam if (!IN6_ARE_ADDR_EQUAL(&satosin6(sa1)->sin6_addr, 4260105197Ssam &satosin6(sa2)->sin6_addr)) { 4261105197Ssam return 1; 4262105197Ssam } 4263105197Ssam if (port && 4264105197Ssam satosin6(sa1)->sin6_port != satosin6(sa2)->sin6_port) { 4265105197Ssam return 1; 4266105197Ssam } 4267170120Sbz break; 4268105197Ssam default: 4269105197Ssam if (bcmp(sa1, sa2, sa1->sa_len) != 0) 4270105197Ssam return 1; 4271105197Ssam break; 4272105197Ssam } 4273105197Ssam 4274105197Ssam return 0; 4275105197Ssam#undef satosin 4276105197Ssam#undef satosin6 4277105197Ssam} 4278105197Ssam 4279105197Ssam/* 4280105197Ssam * compare two buffers with mask. 4281105197Ssam * IN: 4282105197Ssam * addr1: source 4283105197Ssam * addr2: object 4284105197Ssam * bits: Number of bits to compare 4285105197Ssam * OUT: 4286105197Ssam * 1 : equal 4287105197Ssam * 0 : not equal 4288105197Ssam */ 4289105197Ssamstatic int 4290105197Ssamkey_bbcmp(const void *a1, const void *a2, u_int bits) 4291105197Ssam{ 4292105197Ssam const unsigned char *p1 = a1; 4293105197Ssam const unsigned char *p2 = a2; 4294105197Ssam 4295105197Ssam /* XXX: This could be considerably faster if we compare a word 4296105197Ssam * at a time, but it is complicated on LSB Endian machines */ 4297105197Ssam 4298105197Ssam /* Handle null pointers */ 4299105197Ssam if (p1 == NULL || p2 == NULL) 4300105197Ssam return (p1 == p2); 4301105197Ssam 4302105197Ssam while (bits >= 8) { 4303105197Ssam if (*p1++ != *p2++) 4304105197Ssam return 0; 4305105197Ssam bits -= 8; 4306105197Ssam } 4307105197Ssam 4308105197Ssam if (bits > 0) { 4309105197Ssam u_int8_t mask = ~((1<<(8-bits))-1); 4310105197Ssam if ((*p1 & mask) != (*p2 & mask)) 4311105197Ssam return 0; 4312105197Ssam } 4313105197Ssam return 1; /* Match! */ 4314105197Ssam} 4315105197Ssam 4316119643Ssamstatic void 4317119643Ssamkey_flush_spd(time_t now) 4318105197Ssam{ 4319183550Szec INIT_VNET_IPSEC(curvnet); 4320120585Ssam static u_int16_t sptree_scangen = 0; 4321120585Ssam u_int16_t gen = sptree_scangen++; 4322120585Ssam struct secpolicy *sp; 4323105197Ssam u_int dir; 4324105197Ssam 4325105197Ssam /* SPD */ 4326105197Ssam for (dir = 0; dir < IPSEC_DIR_MAX; dir++) { 4327120585Ssamrestart: 4328120585Ssam SPTREE_LOCK(); 4329181803Sbz LIST_FOREACH(sp, &V_sptree[dir], chain) { 4330120585Ssam if (sp->scangen == gen) /* previously handled */ 4331120585Ssam continue; 4332120585Ssam sp->scangen = gen; 4333192880Svanhu if (sp->state == IPSEC_SPSTATE_DEAD && 4334192880Svanhu sp->refcnt == 1) { 4335192880Svanhu /* 4336192880Svanhu * Ensure that we only decrease refcnt once, 4337192880Svanhu * when we're the last consumer. 4338192880Svanhu * Directly call SP_DELREF/key_delsp instead 4339192880Svanhu * of KEY_FREESP to avoid unlocking/relocking 4340192880Svanhu * SPTREE_LOCK before key_delsp: may refcnt 4341192880Svanhu * be increased again during that time ? 4342192880Svanhu * NB: also clean entries created by 4343192880Svanhu * key_spdflush 4344192880Svanhu */ 4345192880Svanhu SP_DELREF(sp); 4346192880Svanhu key_delsp(sp); 4347120585Ssam SPTREE_UNLOCK(); 4348120585Ssam goto restart; 4349105197Ssam } 4350105197Ssam if (sp->lifetime == 0 && sp->validtime == 0) 4351105197Ssam continue; 4352105197Ssam if ((sp->lifetime && now - sp->created > sp->lifetime) 4353105197Ssam || (sp->validtime && now - sp->lastused > sp->validtime)) { 4354105197Ssam sp->state = IPSEC_SPSTATE_DEAD; 4355120585Ssam SPTREE_UNLOCK(); 4356105197Ssam key_spdexpire(sp); 4357120585Ssam goto restart; 4358105197Ssam } 4359105197Ssam } 4360120585Ssam SPTREE_UNLOCK(); 4361105197Ssam } 4362119643Ssam} 4363105197Ssam 4364119643Ssamstatic void 4365119643Ssamkey_flush_sad(time_t now) 4366119643Ssam{ 4367183550Szec INIT_VNET_IPSEC(curvnet); 4368105197Ssam struct secashead *sah, *nextsah; 4369105197Ssam struct secasvar *sav, *nextsav; 4370105197Ssam 4371119643Ssam /* SAD */ 4372120585Ssam SAHTREE_LOCK(); 4373181803Sbz LIST_FOREACH_SAFE(sah, &V_sahtree, chain, nextsah) { 4374105197Ssam /* if sah has been dead, then delete it and process next sah. */ 4375105197Ssam if (sah->state == SADB_SASTATE_DEAD) { 4376105197Ssam key_delsah(sah); 4377105197Ssam continue; 4378105197Ssam } 4379105197Ssam 4380105197Ssam /* if LARVAL entry doesn't become MATURE, delete it. */ 4381120585Ssam LIST_FOREACH_SAFE(sav, &sah->savtree[SADB_SASTATE_LARVAL], chain, nextsav) { 4382190071Svanhu /* Need to also check refcnt for a larval SA ??? */ 4383181803Sbz if (now - sav->created > V_key_larval_lifetime) 4384105197Ssam KEY_FREESAV(&sav); 4385105197Ssam } 4386105197Ssam 4387105197Ssam /* 4388105197Ssam * check MATURE entry to start to send expire message 4389105197Ssam * whether or not. 4390105197Ssam */ 4391120585Ssam LIST_FOREACH_SAFE(sav, &sah->savtree[SADB_SASTATE_MATURE], chain, nextsav) { 4392105197Ssam /* we don't need to check. */ 4393105197Ssam if (sav->lft_s == NULL) 4394105197Ssam continue; 4395105197Ssam 4396105197Ssam /* sanity check */ 4397105197Ssam if (sav->lft_c == NULL) { 4398120585Ssam ipseclog((LOG_DEBUG,"%s: there is no CURRENT " 4399120585Ssam "time, why?\n", __func__)); 4400105197Ssam continue; 4401105197Ssam } 4402105197Ssam 4403105197Ssam /* check SOFT lifetime */ 4404157123Sgnn if (sav->lft_s->addtime != 0 && 4405157123Sgnn now - sav->created > sav->lft_s->addtime) { 4406189406Svanhu key_sa_chgstate(sav, SADB_SASTATE_DYING); 4407190075Svanhu /* 4408190323Svanhu * Actually, only send expire message if 4409190323Svanhu * SA has been used, as it was done before, 4410190323Svanhu * but should we always send such message, 4411190323Svanhu * and let IKE daemon decide if it should be 4412190323Svanhu * renegotiated or not ? 4413190323Svanhu * XXX expire message will actually NOT be 4414190323Svanhu * sent if SA is only used after soft 4415190323Svanhu * lifetime has been reached, see below 4416190323Svanhu * (DYING state) 4417105197Ssam */ 4418189406Svanhu if (sav->lft_c->usetime != 0) 4419105197Ssam key_expire(sav); 4420105197Ssam } 4421105197Ssam /* check SOFT lifetime by bytes */ 4422105197Ssam /* 4423105197Ssam * XXX I don't know the way to delete this SA 4424105197Ssam * when new SA is installed. Caution when it's 4425105197Ssam * installed too big lifetime by time. 4426105197Ssam */ 4427157123Sgnn else if (sav->lft_s->bytes != 0 && 4428157123Sgnn sav->lft_s->bytes < sav->lft_c->bytes) { 4429105197Ssam 4430105197Ssam key_sa_chgstate(sav, SADB_SASTATE_DYING); 4431105197Ssam /* 4432105197Ssam * XXX If we keep to send expire 4433105197Ssam * message in the status of 4434105197Ssam * DYING. Do remove below code. 4435105197Ssam */ 4436105197Ssam key_expire(sav); 4437105197Ssam } 4438105197Ssam } 4439105197Ssam 4440105197Ssam /* check DYING entry to change status to DEAD. */ 4441120585Ssam LIST_FOREACH_SAFE(sav, &sah->savtree[SADB_SASTATE_DYING], chain, nextsav) { 4442105197Ssam /* we don't need to check. */ 4443105197Ssam if (sav->lft_h == NULL) 4444105197Ssam continue; 4445105197Ssam 4446105197Ssam /* sanity check */ 4447105197Ssam if (sav->lft_c == NULL) { 4448120585Ssam ipseclog((LOG_DEBUG, "%s: there is no CURRENT " 4449120585Ssam "time, why?\n", __func__)); 4450105197Ssam continue; 4451105197Ssam } 4452105197Ssam 4453157123Sgnn if (sav->lft_h->addtime != 0 && 4454157123Sgnn now - sav->created > sav->lft_h->addtime) { 4455105197Ssam key_sa_chgstate(sav, SADB_SASTATE_DEAD); 4456105197Ssam KEY_FREESAV(&sav); 4457105197Ssam } 4458105197Ssam#if 0 /* XXX Should we keep to send expire message until HARD lifetime ? */ 4459105197Ssam else if (sav->lft_s != NULL 4460157123Sgnn && sav->lft_s->addtime != 0 4461157123Sgnn && now - sav->created > sav->lft_s->addtime) { 4462105197Ssam /* 4463105197Ssam * XXX: should be checked to be 4464105197Ssam * installed the valid SA. 4465105197Ssam */ 4466105197Ssam 4467105197Ssam /* 4468105197Ssam * If there is no SA then sending 4469105197Ssam * expire message. 4470105197Ssam */ 4471105197Ssam key_expire(sav); 4472105197Ssam } 4473105197Ssam#endif 4474105197Ssam /* check HARD lifetime by bytes */ 4475157123Sgnn else if (sav->lft_h->bytes != 0 && 4476157123Sgnn sav->lft_h->bytes < sav->lft_c->bytes) { 4477105197Ssam key_sa_chgstate(sav, SADB_SASTATE_DEAD); 4478105197Ssam KEY_FREESAV(&sav); 4479105197Ssam } 4480105197Ssam } 4481105197Ssam 4482105197Ssam /* delete entry in DEAD */ 4483120585Ssam LIST_FOREACH_SAFE(sav, &sah->savtree[SADB_SASTATE_DEAD], chain, nextsav) { 4484105197Ssam /* sanity check */ 4485105197Ssam if (sav->state != SADB_SASTATE_DEAD) { 4486120585Ssam ipseclog((LOG_DEBUG, "%s: invalid sav->state " 4487120585Ssam "(queue: %d SA: %d): kill it anyway\n", 4488120585Ssam __func__, 4489105197Ssam SADB_SASTATE_DEAD, sav->state)); 4490105197Ssam } 4491105197Ssam /* 4492105197Ssam * do not call key_freesav() here. 4493105197Ssam * sav should already be freed, and sav->refcnt 4494105197Ssam * shows other references to sav 4495105197Ssam * (such as from SPD). 4496105197Ssam */ 4497105197Ssam } 4498105197Ssam } 4499120585Ssam SAHTREE_UNLOCK(); 4500119643Ssam} 4501105197Ssam 4502119643Ssamstatic void 4503119643Ssamkey_flush_acq(time_t now) 4504119643Ssam{ 4505183550Szec INIT_VNET_IPSEC(curvnet); 4506105197Ssam struct secacq *acq, *nextacq; 4507105197Ssam 4508119643Ssam /* ACQ tree */ 4509120585Ssam ACQ_LOCK(); 4510181803Sbz for (acq = LIST_FIRST(&V_acqtree); acq != NULL; acq = nextacq) { 4511105197Ssam nextacq = LIST_NEXT(acq, chain); 4512181803Sbz if (now - acq->created > V_key_blockacq_lifetime 4513105197Ssam && __LIST_CHAINED(acq)) { 4514105197Ssam LIST_REMOVE(acq, chain); 4515119643Ssam free(acq, M_IPSEC_SAQ); 4516105197Ssam } 4517105197Ssam } 4518120585Ssam ACQ_UNLOCK(); 4519119643Ssam} 4520105197Ssam 4521119643Ssamstatic void 4522119643Ssamkey_flush_spacq(time_t now) 4523119643Ssam{ 4524183550Szec INIT_VNET_IPSEC(curvnet); 4525105197Ssam struct secspacq *acq, *nextacq; 4526105197Ssam 4527119643Ssam /* SP ACQ tree */ 4528120585Ssam SPACQ_LOCK(); 4529181803Sbz for (acq = LIST_FIRST(&V_spacqtree); acq != NULL; acq = nextacq) { 4530105197Ssam nextacq = LIST_NEXT(acq, chain); 4531181803Sbz if (now - acq->created > V_key_blockacq_lifetime 4532105197Ssam && __LIST_CHAINED(acq)) { 4533105197Ssam LIST_REMOVE(acq, chain); 4534119643Ssam free(acq, M_IPSEC_SAQ); 4535105197Ssam } 4536105197Ssam } 4537120585Ssam SPACQ_UNLOCK(); 4538119643Ssam} 4539105197Ssam 4540119643Ssam/* 4541119643Ssam * time handler. 4542119643Ssam * scanning SPD and SAD to check status for each entries, 4543119643Ssam * and do to remove or to expire. 4544119643Ssam * XXX: year 2038 problem may remain. 4545119643Ssam */ 4546119643Ssamvoid 4547119643Ssamkey_timehandler(void) 4548119643Ssam{ 4549183550Szec VNET_ITERATOR_DECL(vnet_iter); 4550119643Ssam time_t now = time_second; 4551105197Ssam 4552185348Szec VNET_LIST_RLOCK(); 4553183550Szec VNET_FOREACH(vnet_iter) { 4554183550Szec CURVNET_SET(vnet_iter); 4555183550Szec key_flush_spd(now); 4556183550Szec key_flush_sad(now); 4557183550Szec key_flush_acq(now); 4558183550Szec key_flush_spacq(now); 4559183550Szec CURVNET_RESTORE(); 4560183550Szec } 4561185348Szec VNET_LIST_RUNLOCK(); 4562119643Ssam 4563105197Ssam#ifndef IPSEC_DEBUG2 4564105197Ssam /* do exchange to tick time !! */ 4565105197Ssam (void)timeout((void *)key_timehandler, (void *)0, hz); 4566105197Ssam#endif /* IPSEC_DEBUG2 */ 4567105197Ssam} 4568105197Ssam 4569105197Ssamu_long 4570105197Ssamkey_random() 4571105197Ssam{ 4572105197Ssam u_long value; 4573105197Ssam 4574105197Ssam key_randomfill(&value, sizeof(value)); 4575105197Ssam return value; 4576105197Ssam} 4577105197Ssam 4578105197Ssamvoid 4579105197Ssamkey_randomfill(p, l) 4580105197Ssam void *p; 4581105197Ssam size_t l; 4582105197Ssam{ 4583105197Ssam size_t n; 4584105197Ssam u_long v; 4585105197Ssam static int warn = 1; 4586105197Ssam 4587105197Ssam n = 0; 4588105197Ssam n = (size_t)read_random(p, (u_int)l); 4589105197Ssam /* last resort */ 4590105197Ssam while (n < l) { 4591105197Ssam v = random(); 4592105197Ssam bcopy(&v, (u_int8_t *)p + n, 4593105197Ssam l - n < sizeof(v) ? l - n : sizeof(v)); 4594105197Ssam n += sizeof(v); 4595105197Ssam 4596105197Ssam if (warn) { 4597105197Ssam printf("WARNING: pseudo-random number generator " 4598105197Ssam "used for IPsec processing\n"); 4599105197Ssam warn = 0; 4600105197Ssam } 4601105197Ssam } 4602105197Ssam} 4603105197Ssam 4604105197Ssam/* 4605105197Ssam * map SADB_SATYPE_* to IPPROTO_*. 4606105197Ssam * if satype == SADB_SATYPE then satype is mapped to ~0. 4607105197Ssam * OUT: 4608105197Ssam * 0: invalid satype. 4609105197Ssam */ 4610105197Ssamstatic u_int16_t 4611189004Srdivackykey_satype2proto(u_int8_t satype) 4612105197Ssam{ 4613105197Ssam switch (satype) { 4614105197Ssam case SADB_SATYPE_UNSPEC: 4615105197Ssam return IPSEC_PROTO_ANY; 4616105197Ssam case SADB_SATYPE_AH: 4617105197Ssam return IPPROTO_AH; 4618105197Ssam case SADB_SATYPE_ESP: 4619105197Ssam return IPPROTO_ESP; 4620105197Ssam case SADB_X_SATYPE_IPCOMP: 4621105197Ssam return IPPROTO_IPCOMP; 4622125680Sbms case SADB_X_SATYPE_TCPSIGNATURE: 4623125680Sbms return IPPROTO_TCP; 4624105197Ssam default: 4625105197Ssam return 0; 4626105197Ssam } 4627105197Ssam /* NOTREACHED */ 4628105197Ssam} 4629105197Ssam 4630105197Ssam/* 4631105197Ssam * map IPPROTO_* to SADB_SATYPE_* 4632105197Ssam * OUT: 4633105197Ssam * 0: invalid protocol type. 4634105197Ssam */ 4635105197Ssamstatic u_int8_t 4636189004Srdivackykey_proto2satype(u_int16_t proto) 4637105197Ssam{ 4638105197Ssam switch (proto) { 4639105197Ssam case IPPROTO_AH: 4640105197Ssam return SADB_SATYPE_AH; 4641105197Ssam case IPPROTO_ESP: 4642105197Ssam return SADB_SATYPE_ESP; 4643105197Ssam case IPPROTO_IPCOMP: 4644105197Ssam return SADB_X_SATYPE_IPCOMP; 4645125680Sbms case IPPROTO_TCP: 4646125680Sbms return SADB_X_SATYPE_TCPSIGNATURE; 4647105197Ssam default: 4648105197Ssam return 0; 4649105197Ssam } 4650105197Ssam /* NOTREACHED */ 4651105197Ssam} 4652105197Ssam 4653105197Ssam/* %%% PF_KEY */ 4654105197Ssam/* 4655105197Ssam * SADB_GETSPI processing is to receive 4656105197Ssam * <base, (SA2), src address, dst address, (SPI range)> 4657105197Ssam * from the IKMPd, to assign a unique spi value, to hang on the INBOUND 4658105197Ssam * tree with the status of LARVAL, and send 4659105197Ssam * <base, SA(*), address(SD)> 4660105197Ssam * to the IKMPd. 4661105197Ssam * 4662105197Ssam * IN: mhp: pointer to the pointer to each header. 4663105197Ssam * OUT: NULL if fail. 4664105197Ssam * other if success, return pointer to the message to send. 4665105197Ssam */ 4666105197Ssamstatic int 4667105197Ssamkey_getspi(so, m, mhp) 4668105197Ssam struct socket *so; 4669105197Ssam struct mbuf *m; 4670105197Ssam const struct sadb_msghdr *mhp; 4671105197Ssam{ 4672183550Szec INIT_VNET_IPSEC(curvnet); 4673105197Ssam struct sadb_address *src0, *dst0; 4674105197Ssam struct secasindex saidx; 4675105197Ssam struct secashead *newsah; 4676105197Ssam struct secasvar *newsav; 4677105197Ssam u_int8_t proto; 4678105197Ssam u_int32_t spi; 4679105197Ssam u_int8_t mode; 4680105197Ssam u_int32_t reqid; 4681105197Ssam int error; 4682105197Ssam 4683120585Ssam IPSEC_ASSERT(so != NULL, ("null socket")); 4684120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 4685120585Ssam IPSEC_ASSERT(mhp != NULL, ("null msghdr")); 4686120585Ssam IPSEC_ASSERT(mhp->msg != NULL, ("null msg")); 4687105197Ssam 4688105197Ssam if (mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL || 4689105197Ssam mhp->ext[SADB_EXT_ADDRESS_DST] == NULL) { 4690120585Ssam ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n", 4691120585Ssam __func__)); 4692105197Ssam return key_senderror(so, m, EINVAL); 4693105197Ssam } 4694105197Ssam if (mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) || 4695105197Ssam mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address)) { 4696120585Ssam ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n", 4697120585Ssam __func__)); 4698105197Ssam return key_senderror(so, m, EINVAL); 4699105197Ssam } 4700105197Ssam if (mhp->ext[SADB_X_EXT_SA2] != NULL) { 4701105197Ssam mode = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_mode; 4702105197Ssam reqid = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_reqid; 4703105197Ssam } else { 4704105197Ssam mode = IPSEC_MODE_ANY; 4705105197Ssam reqid = 0; 4706105197Ssam } 4707105197Ssam 4708105197Ssam src0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_SRC]); 4709105197Ssam dst0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_DST]); 4710105197Ssam 4711105197Ssam /* map satype to proto */ 4712105197Ssam if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) { 4713120585Ssam ipseclog((LOG_DEBUG, "%s: invalid satype is passed.\n", 4714120585Ssam __func__)); 4715105197Ssam return key_senderror(so, m, EINVAL); 4716105197Ssam } 4717105197Ssam 4718194062Svanhu /* 4719194062Svanhu * Make sure the port numbers are zero. 4720194062Svanhu * In case of NAT-T we will update them later if needed. 4721194062Svanhu */ 4722105197Ssam switch (((struct sockaddr *)(src0 + 1))->sa_family) { 4723105197Ssam case AF_INET: 4724105197Ssam if (((struct sockaddr *)(src0 + 1))->sa_len != 4725105197Ssam sizeof(struct sockaddr_in)) 4726105197Ssam return key_senderror(so, m, EINVAL); 4727105197Ssam ((struct sockaddr_in *)(src0 + 1))->sin_port = 0; 4728105197Ssam break; 4729105197Ssam case AF_INET6: 4730105197Ssam if (((struct sockaddr *)(src0 + 1))->sa_len != 4731105197Ssam sizeof(struct sockaddr_in6)) 4732105197Ssam return key_senderror(so, m, EINVAL); 4733105197Ssam ((struct sockaddr_in6 *)(src0 + 1))->sin6_port = 0; 4734105197Ssam break; 4735105197Ssam default: 4736105197Ssam ; /*???*/ 4737105197Ssam } 4738105197Ssam switch (((struct sockaddr *)(dst0 + 1))->sa_family) { 4739105197Ssam case AF_INET: 4740105197Ssam if (((struct sockaddr *)(dst0 + 1))->sa_len != 4741105197Ssam sizeof(struct sockaddr_in)) 4742105197Ssam return key_senderror(so, m, EINVAL); 4743105197Ssam ((struct sockaddr_in *)(dst0 + 1))->sin_port = 0; 4744105197Ssam break; 4745105197Ssam case AF_INET6: 4746105197Ssam if (((struct sockaddr *)(dst0 + 1))->sa_len != 4747105197Ssam sizeof(struct sockaddr_in6)) 4748105197Ssam return key_senderror(so, m, EINVAL); 4749105197Ssam ((struct sockaddr_in6 *)(dst0 + 1))->sin6_port = 0; 4750105197Ssam break; 4751105197Ssam default: 4752105197Ssam ; /*???*/ 4753105197Ssam } 4754105197Ssam 4755105197Ssam /* XXX boundary check against sa_len */ 4756105197Ssam KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx); 4757105197Ssam 4758194062Svanhu#ifdef IPSEC_NAT_T 4759194062Svanhu /* 4760194062Svanhu * Handle NAT-T info if present. 4761194062Svanhu * We made sure the port numbers are zero above, so we do 4762194062Svanhu * not have to worry in case we do not update them. 4763194062Svanhu */ 4764194062Svanhu if (mhp->ext[SADB_X_EXT_NAT_T_OAI] != NULL) 4765194062Svanhu ipseclog((LOG_DEBUG, "%s: NAT-T OAi present\n", __func__)); 4766194062Svanhu if (mhp->ext[SADB_X_EXT_NAT_T_OAR] != NULL) 4767194062Svanhu ipseclog((LOG_DEBUG, "%s: NAT-T OAr present\n", __func__)); 4768194062Svanhu 4769194062Svanhu if (mhp->ext[SADB_X_EXT_NAT_T_TYPE] != NULL && 4770194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_SPORT] != NULL && 4771194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_DPORT] != NULL) { 4772194062Svanhu struct sadb_x_nat_t_type *type; 4773194062Svanhu struct sadb_x_nat_t_port *sport, *dport; 4774194062Svanhu 4775194062Svanhu if (mhp->extlen[SADB_X_EXT_NAT_T_TYPE] < sizeof(*type) || 4776194062Svanhu mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport) || 4777194062Svanhu mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport)) { 4778194062Svanhu ipseclog((LOG_DEBUG, "%s: invalid nat-t message " 4779194062Svanhu "passed.\n", __func__)); 4780194062Svanhu return key_senderror(so, m, EINVAL); 4781194062Svanhu } 4782194062Svanhu 4783194062Svanhu sport = (struct sadb_x_nat_t_port *) 4784194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_SPORT]; 4785194062Svanhu dport = (struct sadb_x_nat_t_port *) 4786194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_DPORT]; 4787194062Svanhu 4788194062Svanhu if (sport) 4789194062Svanhu KEY_PORTTOSADDR(&saidx.src, sport->sadb_x_nat_t_port_port); 4790194062Svanhu if (dport) 4791194062Svanhu KEY_PORTTOSADDR(&saidx.dst, dport->sadb_x_nat_t_port_port); 4792194062Svanhu } 4793194062Svanhu#endif 4794194062Svanhu 4795105197Ssam /* SPI allocation */ 4796105197Ssam spi = key_do_getnewspi((struct sadb_spirange *)mhp->ext[SADB_EXT_SPIRANGE], 4797105197Ssam &saidx); 4798105197Ssam if (spi == 0) 4799105197Ssam return key_senderror(so, m, EINVAL); 4800105197Ssam 4801105197Ssam /* get a SA index */ 4802105197Ssam if ((newsah = key_getsah(&saidx)) == NULL) { 4803105197Ssam /* create a new SA index */ 4804105197Ssam if ((newsah = key_newsah(&saidx)) == NULL) { 4805120585Ssam ipseclog((LOG_DEBUG, "%s: No more memory.\n",__func__)); 4806105197Ssam return key_senderror(so, m, ENOBUFS); 4807105197Ssam } 4808105197Ssam } 4809105197Ssam 4810105197Ssam /* get a new SA */ 4811105197Ssam /* XXX rewrite */ 4812105197Ssam newsav = KEY_NEWSAV(m, mhp, newsah, &error); 4813105197Ssam if (newsav == NULL) { 4814105197Ssam /* XXX don't free new SA index allocated in above. */ 4815105197Ssam return key_senderror(so, m, error); 4816105197Ssam } 4817105197Ssam 4818105197Ssam /* set spi */ 4819105197Ssam newsav->spi = htonl(spi); 4820105197Ssam 4821105197Ssam /* delete the entry in acqtree */ 4822105197Ssam if (mhp->msg->sadb_msg_seq != 0) { 4823105197Ssam struct secacq *acq; 4824105197Ssam if ((acq = key_getacqbyseq(mhp->msg->sadb_msg_seq)) != NULL) { 4825105197Ssam /* reset counter in order to deletion by timehandler. */ 4826105197Ssam acq->created = time_second; 4827105197Ssam acq->count = 0; 4828105197Ssam } 4829105197Ssam } 4830105197Ssam 4831105197Ssam { 4832105197Ssam struct mbuf *n, *nn; 4833105197Ssam struct sadb_sa *m_sa; 4834105197Ssam struct sadb_msg *newmsg; 4835105197Ssam int off, len; 4836105197Ssam 4837105197Ssam /* create new sadb_msg to reply. */ 4838105197Ssam len = PFKEY_ALIGN8(sizeof(struct sadb_msg)) + 4839105197Ssam PFKEY_ALIGN8(sizeof(struct sadb_sa)); 4840105197Ssam 4841111119Simp MGETHDR(n, M_DONTWAIT, MT_DATA); 4842105197Ssam if (len > MHLEN) { 4843111119Simp MCLGET(n, M_DONTWAIT); 4844105197Ssam if ((n->m_flags & M_EXT) == 0) { 4845105197Ssam m_freem(n); 4846105197Ssam n = NULL; 4847105197Ssam } 4848105197Ssam } 4849105197Ssam if (!n) 4850105197Ssam return key_senderror(so, m, ENOBUFS); 4851105197Ssam 4852105197Ssam n->m_len = len; 4853105197Ssam n->m_next = NULL; 4854105197Ssam off = 0; 4855105197Ssam 4856105197Ssam m_copydata(m, 0, sizeof(struct sadb_msg), mtod(n, caddr_t) + off); 4857105197Ssam off += PFKEY_ALIGN8(sizeof(struct sadb_msg)); 4858105197Ssam 4859105197Ssam m_sa = (struct sadb_sa *)(mtod(n, caddr_t) + off); 4860105197Ssam m_sa->sadb_sa_len = PFKEY_UNIT64(sizeof(struct sadb_sa)); 4861105197Ssam m_sa->sadb_sa_exttype = SADB_EXT_SA; 4862105197Ssam m_sa->sadb_sa_spi = htonl(spi); 4863105197Ssam off += PFKEY_ALIGN8(sizeof(struct sadb_sa)); 4864105197Ssam 4865120585Ssam IPSEC_ASSERT(off == len, 4866120585Ssam ("length inconsistency (off %u len %u)", off, len)); 4867105197Ssam 4868105197Ssam n->m_next = key_gather_mbuf(m, mhp, 0, 2, SADB_EXT_ADDRESS_SRC, 4869105197Ssam SADB_EXT_ADDRESS_DST); 4870105197Ssam if (!n->m_next) { 4871105197Ssam m_freem(n); 4872105197Ssam return key_senderror(so, m, ENOBUFS); 4873105197Ssam } 4874105197Ssam 4875105197Ssam if (n->m_len < sizeof(struct sadb_msg)) { 4876105197Ssam n = m_pullup(n, sizeof(struct sadb_msg)); 4877105197Ssam if (n == NULL) 4878105197Ssam return key_sendup_mbuf(so, m, KEY_SENDUP_ONE); 4879105197Ssam } 4880105197Ssam 4881105197Ssam n->m_pkthdr.len = 0; 4882105197Ssam for (nn = n; nn; nn = nn->m_next) 4883105197Ssam n->m_pkthdr.len += nn->m_len; 4884105197Ssam 4885105197Ssam newmsg = mtod(n, struct sadb_msg *); 4886105197Ssam newmsg->sadb_msg_seq = newsav->seq; 4887105197Ssam newmsg->sadb_msg_errno = 0; 4888105197Ssam newmsg->sadb_msg_len = PFKEY_UNIT64(n->m_pkthdr.len); 4889105197Ssam 4890105197Ssam m_freem(m); 4891105197Ssam return key_sendup_mbuf(so, n, KEY_SENDUP_ONE); 4892105197Ssam } 4893105197Ssam} 4894105197Ssam 4895105197Ssam/* 4896105197Ssam * allocating new SPI 4897105197Ssam * called by key_getspi(). 4898105197Ssam * OUT: 4899105197Ssam * 0: failure. 4900105197Ssam * others: success. 4901105197Ssam */ 4902105197Ssamstatic u_int32_t 4903105197Ssamkey_do_getnewspi(spirange, saidx) 4904105197Ssam struct sadb_spirange *spirange; 4905105197Ssam struct secasindex *saidx; 4906105197Ssam{ 4907183550Szec INIT_VNET_IPSEC(curvnet); 4908105197Ssam u_int32_t newspi; 4909105197Ssam u_int32_t min, max; 4910181803Sbz int count = V_key_spi_trycnt; 4911105197Ssam 4912105197Ssam /* set spi range to allocate */ 4913105197Ssam if (spirange != NULL) { 4914105197Ssam min = spirange->sadb_spirange_min; 4915105197Ssam max = spirange->sadb_spirange_max; 4916105197Ssam } else { 4917181803Sbz min = V_key_spi_minval; 4918181803Sbz max = V_key_spi_maxval; 4919105197Ssam } 4920105197Ssam /* IPCOMP needs 2-byte SPI */ 4921105197Ssam if (saidx->proto == IPPROTO_IPCOMP) { 4922105197Ssam u_int32_t t; 4923105197Ssam if (min >= 0x10000) 4924105197Ssam min = 0xffff; 4925105197Ssam if (max >= 0x10000) 4926105197Ssam max = 0xffff; 4927105197Ssam if (min > max) { 4928105197Ssam t = min; min = max; max = t; 4929105197Ssam } 4930105197Ssam } 4931105197Ssam 4932105197Ssam if (min == max) { 4933105197Ssam if (key_checkspidup(saidx, min) != NULL) { 4934120585Ssam ipseclog((LOG_DEBUG, "%s: SPI %u exists already.\n", 4935120585Ssam __func__, min)); 4936105197Ssam return 0; 4937105197Ssam } 4938105197Ssam 4939105197Ssam count--; /* taking one cost. */ 4940105197Ssam newspi = min; 4941105197Ssam 4942105197Ssam } else { 4943105197Ssam 4944105197Ssam /* init SPI */ 4945105197Ssam newspi = 0; 4946105197Ssam 4947105197Ssam /* when requesting to allocate spi ranged */ 4948105197Ssam while (count--) { 4949105197Ssam /* generate pseudo-random SPI value ranged. */ 4950105197Ssam newspi = min + (key_random() % (max - min + 1)); 4951105197Ssam 4952105197Ssam if (key_checkspidup(saidx, newspi) == NULL) 4953105197Ssam break; 4954105197Ssam } 4955105197Ssam 4956105197Ssam if (count == 0 || newspi == 0) { 4957120585Ssam ipseclog((LOG_DEBUG, "%s: to allocate spi is failed.\n", 4958120585Ssam __func__)); 4959105197Ssam return 0; 4960105197Ssam } 4961105197Ssam } 4962105197Ssam 4963105197Ssam /* statistics */ 4964105197Ssam keystat.getspi_count = 4965181803Sbz (keystat.getspi_count + V_key_spi_trycnt - count) / 2; 4966105197Ssam 4967105197Ssam return newspi; 4968105197Ssam} 4969105197Ssam 4970105197Ssam/* 4971105197Ssam * SADB_UPDATE processing 4972105197Ssam * receive 4973105197Ssam * <base, SA, (SA2), (lifetime(HSC),) address(SD), (address(P),) 4974105197Ssam * key(AE), (identity(SD),) (sensitivity)> 4975105197Ssam * from the ikmpd, and update a secasvar entry whose status is SADB_SASTATE_LARVAL. 4976105197Ssam * and send 4977105197Ssam * <base, SA, (SA2), (lifetime(HSC),) address(SD), (address(P),) 4978105197Ssam * (identity(SD),) (sensitivity)> 4979105197Ssam * to the ikmpd. 4980105197Ssam * 4981105197Ssam * m will always be freed. 4982105197Ssam */ 4983105197Ssamstatic int 4984105197Ssamkey_update(so, m, mhp) 4985105197Ssam struct socket *so; 4986105197Ssam struct mbuf *m; 4987105197Ssam const struct sadb_msghdr *mhp; 4988105197Ssam{ 4989183550Szec INIT_VNET_IPSEC(curvnet); 4990105197Ssam struct sadb_sa *sa0; 4991105197Ssam struct sadb_address *src0, *dst0; 4992194062Svanhu#ifdef IPSEC_NAT_T 4993194062Svanhu struct sadb_x_nat_t_type *type; 4994194062Svanhu struct sadb_address *iaddr, *raddr; 4995194062Svanhu struct sadb_x_nat_t_frag *frag; 4996194062Svanhu#endif 4997105197Ssam struct secasindex saidx; 4998105197Ssam struct secashead *sah; 4999105197Ssam struct secasvar *sav; 5000105197Ssam u_int16_t proto; 5001105197Ssam u_int8_t mode; 5002105197Ssam u_int32_t reqid; 5003105197Ssam int error; 5004105197Ssam 5005120585Ssam IPSEC_ASSERT(so != NULL, ("null socket")); 5006120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 5007120585Ssam IPSEC_ASSERT(mhp != NULL, ("null msghdr")); 5008120585Ssam IPSEC_ASSERT(mhp->msg != NULL, ("null msg")); 5009105197Ssam 5010105197Ssam /* map satype to proto */ 5011105197Ssam if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) { 5012120585Ssam ipseclog((LOG_DEBUG, "%s: invalid satype is passed.\n", 5013120585Ssam __func__)); 5014105197Ssam return key_senderror(so, m, EINVAL); 5015105197Ssam } 5016105197Ssam 5017105197Ssam if (mhp->ext[SADB_EXT_SA] == NULL || 5018105197Ssam mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL || 5019105197Ssam mhp->ext[SADB_EXT_ADDRESS_DST] == NULL || 5020105197Ssam (mhp->msg->sadb_msg_satype == SADB_SATYPE_ESP && 5021105197Ssam mhp->ext[SADB_EXT_KEY_ENCRYPT] == NULL) || 5022105197Ssam (mhp->msg->sadb_msg_satype == SADB_SATYPE_AH && 5023105197Ssam mhp->ext[SADB_EXT_KEY_AUTH] == NULL) || 5024105197Ssam (mhp->ext[SADB_EXT_LIFETIME_HARD] != NULL && 5025105197Ssam mhp->ext[SADB_EXT_LIFETIME_SOFT] == NULL) || 5026105197Ssam (mhp->ext[SADB_EXT_LIFETIME_HARD] == NULL && 5027105197Ssam mhp->ext[SADB_EXT_LIFETIME_SOFT] != NULL)) { 5028120585Ssam ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n", 5029120585Ssam __func__)); 5030105197Ssam return key_senderror(so, m, EINVAL); 5031105197Ssam } 5032105197Ssam if (mhp->extlen[SADB_EXT_SA] < sizeof(struct sadb_sa) || 5033105197Ssam mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) || 5034105197Ssam mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address)) { 5035120585Ssam ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n", 5036120585Ssam __func__)); 5037105197Ssam return key_senderror(so, m, EINVAL); 5038105197Ssam } 5039105197Ssam if (mhp->ext[SADB_X_EXT_SA2] != NULL) { 5040105197Ssam mode = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_mode; 5041105197Ssam reqid = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_reqid; 5042105197Ssam } else { 5043105197Ssam mode = IPSEC_MODE_ANY; 5044105197Ssam reqid = 0; 5045105197Ssam } 5046105197Ssam /* XXX boundary checking for other extensions */ 5047105197Ssam 5048105197Ssam sa0 = (struct sadb_sa *)mhp->ext[SADB_EXT_SA]; 5049105197Ssam src0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_SRC]); 5050105197Ssam dst0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_DST]); 5051105197Ssam 5052105197Ssam /* XXX boundary check against sa_len */ 5053105197Ssam KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx); 5054105197Ssam 5055194062Svanhu /* 5056194062Svanhu * Make sure the port numbers are zero. 5057194062Svanhu * In case of NAT-T we will update them later if needed. 5058194062Svanhu */ 5059194062Svanhu KEY_PORTTOSADDR(&saidx.src, 0); 5060194062Svanhu KEY_PORTTOSADDR(&saidx.dst, 0); 5061194062Svanhu 5062194062Svanhu#ifdef IPSEC_NAT_T 5063194062Svanhu /* 5064194062Svanhu * Handle NAT-T info if present. 5065194062Svanhu */ 5066194062Svanhu if (mhp->ext[SADB_X_EXT_NAT_T_TYPE] != NULL && 5067194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_SPORT] != NULL && 5068194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_DPORT] != NULL) { 5069194062Svanhu struct sadb_x_nat_t_port *sport, *dport; 5070194062Svanhu 5071194062Svanhu if (mhp->extlen[SADB_X_EXT_NAT_T_TYPE] < sizeof(*type) || 5072194062Svanhu mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport) || 5073194062Svanhu mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport)) { 5074194062Svanhu ipseclog((LOG_DEBUG, "%s: invalid message.\n", 5075194062Svanhu __func__)); 5076194062Svanhu return key_senderror(so, m, EINVAL); 5077194062Svanhu } 5078194062Svanhu 5079194062Svanhu type = (struct sadb_x_nat_t_type *) 5080194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_TYPE]; 5081194062Svanhu sport = (struct sadb_x_nat_t_port *) 5082194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_SPORT]; 5083194062Svanhu dport = (struct sadb_x_nat_t_port *) 5084194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_DPORT]; 5085194062Svanhu 5086194062Svanhu if (sport) 5087194062Svanhu KEY_PORTTOSADDR(&saidx.src, 5088194062Svanhu sport->sadb_x_nat_t_port_port); 5089194062Svanhu if (dport) 5090194062Svanhu KEY_PORTTOSADDR(&saidx.dst, 5091194062Svanhu dport->sadb_x_nat_t_port_port); 5092194062Svanhu } else { 5093194062Svanhu type = 0; 5094194062Svanhu } 5095194062Svanhu if (mhp->ext[SADB_X_EXT_NAT_T_OAI] != NULL && 5096194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_OAR] != NULL) { 5097194062Svanhu if (mhp->extlen[SADB_X_EXT_NAT_T_OAI] < sizeof(*iaddr) || 5098194062Svanhu mhp->extlen[SADB_X_EXT_NAT_T_OAR] < sizeof(*raddr)) { 5099194062Svanhu ipseclog((LOG_DEBUG, "%s: invalid message\n", 5100194062Svanhu __func__)); 5101194062Svanhu return key_senderror(so, m, EINVAL); 5102194062Svanhu } 5103194062Svanhu iaddr = (struct sadb_address *)mhp->ext[SADB_X_EXT_NAT_T_OAI]; 5104194062Svanhu raddr = (struct sadb_address *)mhp->ext[SADB_X_EXT_NAT_T_OAR]; 5105194062Svanhu ipseclog((LOG_DEBUG, "%s: NAT-T OAi/r present\n", __func__)); 5106194062Svanhu } else { 5107194062Svanhu iaddr = raddr = NULL; 5108194062Svanhu } 5109194062Svanhu if (mhp->ext[SADB_X_EXT_NAT_T_FRAG] != NULL) { 5110194062Svanhu if (mhp->extlen[SADB_X_EXT_NAT_T_FRAG] < sizeof(*frag)) { 5111194062Svanhu ipseclog((LOG_DEBUG, "%s: invalid message\n", 5112194062Svanhu __func__)); 5113194062Svanhu return key_senderror(so, m, EINVAL); 5114194062Svanhu } 5115194062Svanhu frag = (struct sadb_x_nat_t_frag *) 5116194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_FRAG]; 5117194062Svanhu } else { 5118194062Svanhu frag = 0; 5119194062Svanhu } 5120194062Svanhu#endif 5121194062Svanhu 5122105197Ssam /* get a SA header */ 5123105197Ssam if ((sah = key_getsah(&saidx)) == NULL) { 5124120585Ssam ipseclog((LOG_DEBUG, "%s: no SA index found.\n", __func__)); 5125105197Ssam return key_senderror(so, m, ENOENT); 5126105197Ssam } 5127105197Ssam 5128105197Ssam /* set spidx if there */ 5129105197Ssam /* XXX rewrite */ 5130105197Ssam error = key_setident(sah, m, mhp); 5131105197Ssam if (error) 5132105197Ssam return key_senderror(so, m, error); 5133105197Ssam 5134105197Ssam /* find a SA with sequence number. */ 5135105197Ssam#ifdef IPSEC_DOSEQCHECK 5136105197Ssam if (mhp->msg->sadb_msg_seq != 0 5137105197Ssam && (sav = key_getsavbyseq(sah, mhp->msg->sadb_msg_seq)) == NULL) { 5138120585Ssam ipseclog((LOG_DEBUG, "%s: no larval SA with sequence %u " 5139120585Ssam "exists.\n", __func__, mhp->msg->sadb_msg_seq)); 5140105197Ssam return key_senderror(so, m, ENOENT); 5141105197Ssam } 5142105197Ssam#else 5143120585Ssam SAHTREE_LOCK(); 5144120585Ssam sav = key_getsavbyspi(sah, sa0->sadb_sa_spi); 5145120585Ssam SAHTREE_UNLOCK(); 5146120585Ssam if (sav == NULL) { 5147120585Ssam ipseclog((LOG_DEBUG, "%s: no such a SA found (spi:%u)\n", 5148120585Ssam __func__, (u_int32_t)ntohl(sa0->sadb_sa_spi))); 5149105197Ssam return key_senderror(so, m, EINVAL); 5150105197Ssam } 5151105197Ssam#endif 5152105197Ssam 5153105197Ssam /* validity check */ 5154105197Ssam if (sav->sah->saidx.proto != proto) { 5155120585Ssam ipseclog((LOG_DEBUG, "%s: protocol mismatched " 5156120585Ssam "(DB=%u param=%u)\n", __func__, 5157120585Ssam sav->sah->saidx.proto, proto)); 5158105197Ssam return key_senderror(so, m, EINVAL); 5159105197Ssam } 5160105197Ssam#ifdef IPSEC_DOSEQCHECK 5161105197Ssam if (sav->spi != sa0->sadb_sa_spi) { 5162120585Ssam ipseclog((LOG_DEBUG, "%s: SPI mismatched (DB:%u param:%u)\n", 5163120585Ssam __func__, 5164105197Ssam (u_int32_t)ntohl(sav->spi), 5165105197Ssam (u_int32_t)ntohl(sa0->sadb_sa_spi))); 5166105197Ssam return key_senderror(so, m, EINVAL); 5167105197Ssam } 5168105197Ssam#endif 5169105197Ssam if (sav->pid != mhp->msg->sadb_msg_pid) { 5170120585Ssam ipseclog((LOG_DEBUG, "%s: pid mismatched (DB:%u param:%u)\n", 5171120585Ssam __func__, sav->pid, mhp->msg->sadb_msg_pid)); 5172105197Ssam return key_senderror(so, m, EINVAL); 5173105197Ssam } 5174105197Ssam 5175105197Ssam /* copy sav values */ 5176105197Ssam error = key_setsaval(sav, m, mhp); 5177105197Ssam if (error) { 5178105197Ssam KEY_FREESAV(&sav); 5179105197Ssam return key_senderror(so, m, error); 5180105197Ssam } 5181105197Ssam 5182105197Ssam /* check SA values to be mature. */ 5183105197Ssam if ((mhp->msg->sadb_msg_errno = key_mature(sav)) != 0) { 5184105197Ssam KEY_FREESAV(&sav); 5185105197Ssam return key_senderror(so, m, 0); 5186105197Ssam } 5187105197Ssam 5188194062Svanhu#ifdef IPSEC_NAT_T 5189194062Svanhu /* 5190194062Svanhu * Handle more NAT-T info if present, 5191194062Svanhu * now that we have a sav to fill. 5192194062Svanhu */ 5193194062Svanhu if (type) 5194194062Svanhu sav->natt_type = type->sadb_x_nat_t_type_type; 5195194062Svanhu 5196194062Svanhu#if 0 5197194062Svanhu /* 5198194062Svanhu * In case SADB_X_EXT_NAT_T_FRAG was not given, leave it at 0. 5199194062Svanhu * We should actually check for a minimum MTU here, if we 5200194062Svanhu * want to support it in ip_output. 5201194062Svanhu */ 5202194062Svanhu if (frag) 5203194062Svanhu sav->natt_esp_frag_len = frag->sadb_x_nat_t_frag_fraglen; 5204194062Svanhu#endif 5205194062Svanhu#endif 5206194062Svanhu 5207105197Ssam { 5208105197Ssam struct mbuf *n; 5209105197Ssam 5210105197Ssam /* set msg buf from mhp */ 5211105197Ssam n = key_getmsgbuf_x1(m, mhp); 5212105197Ssam if (n == NULL) { 5213120585Ssam ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__)); 5214105197Ssam return key_senderror(so, m, ENOBUFS); 5215105197Ssam } 5216105197Ssam 5217105197Ssam m_freem(m); 5218105197Ssam return key_sendup_mbuf(so, n, KEY_SENDUP_ALL); 5219105197Ssam } 5220105197Ssam} 5221105197Ssam 5222105197Ssam/* 5223105197Ssam * search SAD with sequence for a SA which state is SADB_SASTATE_LARVAL. 5224105197Ssam * only called by key_update(). 5225105197Ssam * OUT: 5226105197Ssam * NULL : not found 5227105197Ssam * others : found, pointer to a SA. 5228105197Ssam */ 5229105197Ssam#ifdef IPSEC_DOSEQCHECK 5230105197Ssamstatic struct secasvar * 5231105197Ssamkey_getsavbyseq(sah, seq) 5232105197Ssam struct secashead *sah; 5233105197Ssam u_int32_t seq; 5234105197Ssam{ 5235105197Ssam struct secasvar *sav; 5236105197Ssam u_int state; 5237105197Ssam 5238105197Ssam state = SADB_SASTATE_LARVAL; 5239105197Ssam 5240105197Ssam /* search SAD with sequence number ? */ 5241105197Ssam LIST_FOREACH(sav, &sah->savtree[state], chain) { 5242105197Ssam 5243120585Ssam KEY_CHKSASTATE(state, sav->state, __func__); 5244105197Ssam 5245105197Ssam if (sav->seq == seq) { 5246158767Spjd sa_addref(sav); 5247105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 5248120585Ssam printf("DP %s cause refcnt++:%d SA:%p\n", 5249120585Ssam __func__, sav->refcnt, sav)); 5250105197Ssam return sav; 5251105197Ssam } 5252105197Ssam } 5253105197Ssam 5254105197Ssam return NULL; 5255105197Ssam} 5256105197Ssam#endif 5257105197Ssam 5258105197Ssam/* 5259105197Ssam * SADB_ADD processing 5260108533Sschweikh * add an entry to SA database, when received 5261105197Ssam * <base, SA, (SA2), (lifetime(HSC),) address(SD), (address(P),) 5262105197Ssam * key(AE), (identity(SD),) (sensitivity)> 5263105197Ssam * from the ikmpd, 5264105197Ssam * and send 5265105197Ssam * <base, SA, (SA2), (lifetime(HSC),) address(SD), (address(P),) 5266105197Ssam * (identity(SD),) (sensitivity)> 5267105197Ssam * to the ikmpd. 5268105197Ssam * 5269105197Ssam * IGNORE identity and sensitivity messages. 5270105197Ssam * 5271105197Ssam * m will always be freed. 5272105197Ssam */ 5273105197Ssamstatic int 5274105197Ssamkey_add(so, m, mhp) 5275105197Ssam struct socket *so; 5276105197Ssam struct mbuf *m; 5277105197Ssam const struct sadb_msghdr *mhp; 5278105197Ssam{ 5279183550Szec INIT_VNET_IPSEC(curvnet); 5280105197Ssam struct sadb_sa *sa0; 5281105197Ssam struct sadb_address *src0, *dst0; 5282194062Svanhu#ifdef IPSEC_NAT_T 5283194062Svanhu struct sadb_x_nat_t_type *type; 5284194062Svanhu struct sadb_address *iaddr, *raddr; 5285194062Svanhu struct sadb_x_nat_t_frag *frag; 5286194062Svanhu#endif 5287105197Ssam struct secasindex saidx; 5288105197Ssam struct secashead *newsah; 5289105197Ssam struct secasvar *newsav; 5290105197Ssam u_int16_t proto; 5291105197Ssam u_int8_t mode; 5292105197Ssam u_int32_t reqid; 5293105197Ssam int error; 5294105197Ssam 5295120585Ssam IPSEC_ASSERT(so != NULL, ("null socket")); 5296120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 5297120585Ssam IPSEC_ASSERT(mhp != NULL, ("null msghdr")); 5298120585Ssam IPSEC_ASSERT(mhp->msg != NULL, ("null msg")); 5299105197Ssam 5300105197Ssam /* map satype to proto */ 5301105197Ssam if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) { 5302120585Ssam ipseclog((LOG_DEBUG, "%s: invalid satype is passed.\n", 5303120585Ssam __func__)); 5304105197Ssam return key_senderror(so, m, EINVAL); 5305105197Ssam } 5306105197Ssam 5307105197Ssam if (mhp->ext[SADB_EXT_SA] == NULL || 5308105197Ssam mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL || 5309105197Ssam mhp->ext[SADB_EXT_ADDRESS_DST] == NULL || 5310105197Ssam (mhp->msg->sadb_msg_satype == SADB_SATYPE_ESP && 5311105197Ssam mhp->ext[SADB_EXT_KEY_ENCRYPT] == NULL) || 5312105197Ssam (mhp->msg->sadb_msg_satype == SADB_SATYPE_AH && 5313105197Ssam mhp->ext[SADB_EXT_KEY_AUTH] == NULL) || 5314105197Ssam (mhp->ext[SADB_EXT_LIFETIME_HARD] != NULL && 5315105197Ssam mhp->ext[SADB_EXT_LIFETIME_SOFT] == NULL) || 5316105197Ssam (mhp->ext[SADB_EXT_LIFETIME_HARD] == NULL && 5317105197Ssam mhp->ext[SADB_EXT_LIFETIME_SOFT] != NULL)) { 5318120585Ssam ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n", 5319120585Ssam __func__)); 5320105197Ssam return key_senderror(so, m, EINVAL); 5321105197Ssam } 5322105197Ssam if (mhp->extlen[SADB_EXT_SA] < sizeof(struct sadb_sa) || 5323105197Ssam mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) || 5324105197Ssam mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address)) { 5325105197Ssam /* XXX need more */ 5326120585Ssam ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n", 5327120585Ssam __func__)); 5328105197Ssam return key_senderror(so, m, EINVAL); 5329105197Ssam } 5330105197Ssam if (mhp->ext[SADB_X_EXT_SA2] != NULL) { 5331105197Ssam mode = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_mode; 5332105197Ssam reqid = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_reqid; 5333105197Ssam } else { 5334105197Ssam mode = IPSEC_MODE_ANY; 5335105197Ssam reqid = 0; 5336105197Ssam } 5337105197Ssam 5338105197Ssam sa0 = (struct sadb_sa *)mhp->ext[SADB_EXT_SA]; 5339105197Ssam src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC]; 5340105197Ssam dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST]; 5341105197Ssam 5342105197Ssam /* XXX boundary check against sa_len */ 5343105197Ssam KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx); 5344105197Ssam 5345194062Svanhu /* 5346194062Svanhu * Make sure the port numbers are zero. 5347194062Svanhu * In case of NAT-T we will update them later if needed. 5348194062Svanhu */ 5349194062Svanhu KEY_PORTTOSADDR(&saidx.src, 0); 5350194062Svanhu KEY_PORTTOSADDR(&saidx.dst, 0); 5351194062Svanhu 5352194062Svanhu#ifdef IPSEC_NAT_T 5353194062Svanhu /* 5354194062Svanhu * Handle NAT-T info if present. 5355194062Svanhu */ 5356194062Svanhu if (mhp->ext[SADB_X_EXT_NAT_T_TYPE] != NULL && 5357194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_SPORT] != NULL && 5358194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_DPORT] != NULL) { 5359194062Svanhu struct sadb_x_nat_t_port *sport, *dport; 5360194062Svanhu 5361194062Svanhu if (mhp->extlen[SADB_X_EXT_NAT_T_TYPE] < sizeof(*type) || 5362194062Svanhu mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport) || 5363194062Svanhu mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport)) { 5364194062Svanhu ipseclog((LOG_DEBUG, "%s: invalid message.\n", 5365194062Svanhu __func__)); 5366194062Svanhu return key_senderror(so, m, EINVAL); 5367194062Svanhu } 5368194062Svanhu 5369194062Svanhu type = (struct sadb_x_nat_t_type *) 5370194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_TYPE]; 5371194062Svanhu sport = (struct sadb_x_nat_t_port *) 5372194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_SPORT]; 5373194062Svanhu dport = (struct sadb_x_nat_t_port *) 5374194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_DPORT]; 5375194062Svanhu 5376194062Svanhu if (sport) 5377194062Svanhu KEY_PORTTOSADDR(&saidx.src, 5378194062Svanhu sport->sadb_x_nat_t_port_port); 5379194062Svanhu if (dport) 5380194062Svanhu KEY_PORTTOSADDR(&saidx.dst, 5381194062Svanhu dport->sadb_x_nat_t_port_port); 5382194062Svanhu } else { 5383194062Svanhu type = 0; 5384194062Svanhu } 5385194062Svanhu if (mhp->ext[SADB_X_EXT_NAT_T_OAI] != NULL && 5386194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_OAR] != NULL) { 5387194062Svanhu if (mhp->extlen[SADB_X_EXT_NAT_T_OAI] < sizeof(*iaddr) || 5388194062Svanhu mhp->extlen[SADB_X_EXT_NAT_T_OAR] < sizeof(*raddr)) { 5389194062Svanhu ipseclog((LOG_DEBUG, "%s: invalid message\n", 5390194062Svanhu __func__)); 5391194062Svanhu return key_senderror(so, m, EINVAL); 5392194062Svanhu } 5393194062Svanhu iaddr = (struct sadb_address *)mhp->ext[SADB_X_EXT_NAT_T_OAI]; 5394194062Svanhu raddr = (struct sadb_address *)mhp->ext[SADB_X_EXT_NAT_T_OAR]; 5395194062Svanhu ipseclog((LOG_DEBUG, "%s: NAT-T OAi/r present\n", __func__)); 5396194062Svanhu } else { 5397194062Svanhu iaddr = raddr = NULL; 5398194062Svanhu } 5399194062Svanhu if (mhp->ext[SADB_X_EXT_NAT_T_FRAG] != NULL) { 5400194062Svanhu if (mhp->extlen[SADB_X_EXT_NAT_T_FRAG] < sizeof(*frag)) { 5401194062Svanhu ipseclog((LOG_DEBUG, "%s: invalid message\n", 5402194062Svanhu __func__)); 5403194062Svanhu return key_senderror(so, m, EINVAL); 5404194062Svanhu } 5405194062Svanhu frag = (struct sadb_x_nat_t_frag *) 5406194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_FRAG]; 5407194062Svanhu } else { 5408194062Svanhu frag = 0; 5409194062Svanhu } 5410194062Svanhu#endif 5411194062Svanhu 5412105197Ssam /* get a SA header */ 5413105197Ssam if ((newsah = key_getsah(&saidx)) == NULL) { 5414105197Ssam /* create a new SA header */ 5415105197Ssam if ((newsah = key_newsah(&saidx)) == NULL) { 5416120585Ssam ipseclog((LOG_DEBUG, "%s: No more memory.\n",__func__)); 5417105197Ssam return key_senderror(so, m, ENOBUFS); 5418105197Ssam } 5419105197Ssam } 5420105197Ssam 5421105197Ssam /* set spidx if there */ 5422105197Ssam /* XXX rewrite */ 5423105197Ssam error = key_setident(newsah, m, mhp); 5424105197Ssam if (error) { 5425105197Ssam return key_senderror(so, m, error); 5426105197Ssam } 5427105197Ssam 5428105197Ssam /* create new SA entry. */ 5429105197Ssam /* We can create new SA only if SPI is differenct. */ 5430120585Ssam SAHTREE_LOCK(); 5431120585Ssam newsav = key_getsavbyspi(newsah, sa0->sadb_sa_spi); 5432120585Ssam SAHTREE_UNLOCK(); 5433120585Ssam if (newsav != NULL) { 5434120585Ssam ipseclog((LOG_DEBUG, "%s: SA already exists.\n", __func__)); 5435105197Ssam return key_senderror(so, m, EEXIST); 5436105197Ssam } 5437105197Ssam newsav = KEY_NEWSAV(m, mhp, newsah, &error); 5438105197Ssam if (newsav == NULL) { 5439105197Ssam return key_senderror(so, m, error); 5440105197Ssam } 5441105197Ssam 5442105197Ssam /* check SA values to be mature. */ 5443105197Ssam if ((error = key_mature(newsav)) != 0) { 5444105197Ssam KEY_FREESAV(&newsav); 5445105197Ssam return key_senderror(so, m, error); 5446105197Ssam } 5447105197Ssam 5448194062Svanhu#ifdef IPSEC_NAT_T 5449105197Ssam /* 5450194062Svanhu * Handle more NAT-T info if present, 5451194062Svanhu * now that we have a sav to fill. 5452194062Svanhu */ 5453194062Svanhu if (type) 5454194062Svanhu newsav->natt_type = type->sadb_x_nat_t_type_type; 5455194062Svanhu 5456194062Svanhu#if 0 5457194062Svanhu /* 5458194062Svanhu * In case SADB_X_EXT_NAT_T_FRAG was not given, leave it at 0. 5459194062Svanhu * We should actually check for a minimum MTU here, if we 5460194062Svanhu * want to support it in ip_output. 5461194062Svanhu */ 5462194062Svanhu if (frag) 5463194062Svanhu newsav->natt_esp_frag_len = frag->sadb_x_nat_t_frag_fraglen; 5464194062Svanhu#endif 5465194062Svanhu#endif 5466194062Svanhu 5467194062Svanhu /* 5468105197Ssam * don't call key_freesav() here, as we would like to keep the SA 5469105197Ssam * in the database on success. 5470105197Ssam */ 5471105197Ssam 5472105197Ssam { 5473105197Ssam struct mbuf *n; 5474105197Ssam 5475105197Ssam /* set msg buf from mhp */ 5476105197Ssam n = key_getmsgbuf_x1(m, mhp); 5477105197Ssam if (n == NULL) { 5478120585Ssam ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__)); 5479105197Ssam return key_senderror(so, m, ENOBUFS); 5480105197Ssam } 5481105197Ssam 5482105197Ssam m_freem(m); 5483105197Ssam return key_sendup_mbuf(so, n, KEY_SENDUP_ALL); 5484105197Ssam } 5485105197Ssam} 5486105197Ssam 5487105197Ssam/* m is retained */ 5488105197Ssamstatic int 5489105197Ssamkey_setident(sah, m, mhp) 5490105197Ssam struct secashead *sah; 5491105197Ssam struct mbuf *m; 5492105197Ssam const struct sadb_msghdr *mhp; 5493105197Ssam{ 5494183550Szec INIT_VNET_IPSEC(curvnet); 5495105197Ssam const struct sadb_ident *idsrc, *iddst; 5496105197Ssam int idsrclen, iddstlen; 5497105197Ssam 5498120585Ssam IPSEC_ASSERT(sah != NULL, ("null secashead")); 5499120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 5500120585Ssam IPSEC_ASSERT(mhp != NULL, ("null msghdr")); 5501120585Ssam IPSEC_ASSERT(mhp->msg != NULL, ("null msg")); 5502105197Ssam 5503105197Ssam /* don't make buffer if not there */ 5504105197Ssam if (mhp->ext[SADB_EXT_IDENTITY_SRC] == NULL && 5505105197Ssam mhp->ext[SADB_EXT_IDENTITY_DST] == NULL) { 5506105197Ssam sah->idents = NULL; 5507105197Ssam sah->identd = NULL; 5508105197Ssam return 0; 5509105197Ssam } 5510105197Ssam 5511105197Ssam if (mhp->ext[SADB_EXT_IDENTITY_SRC] == NULL || 5512105197Ssam mhp->ext[SADB_EXT_IDENTITY_DST] == NULL) { 5513120585Ssam ipseclog((LOG_DEBUG, "%s: invalid identity.\n", __func__)); 5514105197Ssam return EINVAL; 5515105197Ssam } 5516105197Ssam 5517105197Ssam idsrc = (const struct sadb_ident *)mhp->ext[SADB_EXT_IDENTITY_SRC]; 5518105197Ssam iddst = (const struct sadb_ident *)mhp->ext[SADB_EXT_IDENTITY_DST]; 5519105197Ssam idsrclen = mhp->extlen[SADB_EXT_IDENTITY_SRC]; 5520105197Ssam iddstlen = mhp->extlen[SADB_EXT_IDENTITY_DST]; 5521105197Ssam 5522105197Ssam /* validity check */ 5523105197Ssam if (idsrc->sadb_ident_type != iddst->sadb_ident_type) { 5524120585Ssam ipseclog((LOG_DEBUG, "%s: ident type mismatch.\n", __func__)); 5525105197Ssam return EINVAL; 5526105197Ssam } 5527105197Ssam 5528105197Ssam switch (idsrc->sadb_ident_type) { 5529105197Ssam case SADB_IDENTTYPE_PREFIX: 5530105197Ssam case SADB_IDENTTYPE_FQDN: 5531105197Ssam case SADB_IDENTTYPE_USERFQDN: 5532105197Ssam default: 5533105197Ssam /* XXX do nothing */ 5534105197Ssam sah->idents = NULL; 5535105197Ssam sah->identd = NULL; 5536105197Ssam return 0; 5537105197Ssam } 5538105197Ssam 5539105197Ssam /* make structure */ 5540157123Sgnn sah->idents = malloc(sizeof(struct secident), M_IPSEC_MISC, M_NOWAIT); 5541105197Ssam if (sah->idents == NULL) { 5542120585Ssam ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__)); 5543105197Ssam return ENOBUFS; 5544105197Ssam } 5545157123Sgnn sah->identd = malloc(sizeof(struct secident), M_IPSEC_MISC, M_NOWAIT); 5546105197Ssam if (sah->identd == NULL) { 5547119643Ssam free(sah->idents, M_IPSEC_MISC); 5548105197Ssam sah->idents = NULL; 5549120585Ssam ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__)); 5550105197Ssam return ENOBUFS; 5551105197Ssam } 5552157123Sgnn sah->idents->type = idsrc->sadb_ident_type; 5553157123Sgnn sah->idents->id = idsrc->sadb_ident_id; 5554105197Ssam 5555157123Sgnn sah->identd->type = iddst->sadb_ident_type; 5556157123Sgnn sah->identd->id = iddst->sadb_ident_id; 5557157123Sgnn 5558105197Ssam return 0; 5559105197Ssam} 5560105197Ssam 5561105197Ssam/* 5562105197Ssam * m will not be freed on return. 5563105197Ssam * it is caller's responsibility to free the result. 5564105197Ssam */ 5565105197Ssamstatic struct mbuf * 5566105197Ssamkey_getmsgbuf_x1(m, mhp) 5567105197Ssam struct mbuf *m; 5568105197Ssam const struct sadb_msghdr *mhp; 5569105197Ssam{ 5570105197Ssam struct mbuf *n; 5571105197Ssam 5572120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 5573120585Ssam IPSEC_ASSERT(mhp != NULL, ("null msghdr")); 5574120585Ssam IPSEC_ASSERT(mhp->msg != NULL, ("null msg")); 5575105197Ssam 5576105197Ssam /* create new sadb_msg to reply. */ 5577105197Ssam n = key_gather_mbuf(m, mhp, 1, 9, SADB_EXT_RESERVED, 5578105197Ssam SADB_EXT_SA, SADB_X_EXT_SA2, 5579105197Ssam SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST, 5580105197Ssam SADB_EXT_LIFETIME_HARD, SADB_EXT_LIFETIME_SOFT, 5581105197Ssam SADB_EXT_IDENTITY_SRC, SADB_EXT_IDENTITY_DST); 5582105197Ssam if (!n) 5583105197Ssam return NULL; 5584105197Ssam 5585105197Ssam if (n->m_len < sizeof(struct sadb_msg)) { 5586105197Ssam n = m_pullup(n, sizeof(struct sadb_msg)); 5587105197Ssam if (n == NULL) 5588105197Ssam return NULL; 5589105197Ssam } 5590105197Ssam mtod(n, struct sadb_msg *)->sadb_msg_errno = 0; 5591105197Ssam mtod(n, struct sadb_msg *)->sadb_msg_len = 5592105197Ssam PFKEY_UNIT64(n->m_pkthdr.len); 5593105197Ssam 5594105197Ssam return n; 5595105197Ssam} 5596105197Ssam 5597105197Ssamstatic int key_delete_all __P((struct socket *, struct mbuf *, 5598105197Ssam const struct sadb_msghdr *, u_int16_t)); 5599105197Ssam 5600105197Ssam/* 5601105197Ssam * SADB_DELETE processing 5602105197Ssam * receive 5603105197Ssam * <base, SA(*), address(SD)> 5604105197Ssam * from the ikmpd, and set SADB_SASTATE_DEAD, 5605105197Ssam * and send, 5606105197Ssam * <base, SA(*), address(SD)> 5607105197Ssam * to the ikmpd. 5608105197Ssam * 5609105197Ssam * m will always be freed. 5610105197Ssam */ 5611105197Ssamstatic int 5612105197Ssamkey_delete(so, m, mhp) 5613105197Ssam struct socket *so; 5614105197Ssam struct mbuf *m; 5615105197Ssam const struct sadb_msghdr *mhp; 5616105197Ssam{ 5617183550Szec INIT_VNET_IPSEC(curvnet); 5618105197Ssam struct sadb_sa *sa0; 5619105197Ssam struct sadb_address *src0, *dst0; 5620105197Ssam struct secasindex saidx; 5621105197Ssam struct secashead *sah; 5622105197Ssam struct secasvar *sav = NULL; 5623105197Ssam u_int16_t proto; 5624105197Ssam 5625120585Ssam IPSEC_ASSERT(so != NULL, ("null socket")); 5626120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 5627120585Ssam IPSEC_ASSERT(mhp != NULL, ("null msghdr")); 5628120585Ssam IPSEC_ASSERT(mhp->msg != NULL, ("null msg")); 5629105197Ssam 5630105197Ssam /* map satype to proto */ 5631105197Ssam if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) { 5632120585Ssam ipseclog((LOG_DEBUG, "%s: invalid satype is passed.\n", 5633120585Ssam __func__)); 5634105197Ssam return key_senderror(so, m, EINVAL); 5635105197Ssam } 5636105197Ssam 5637105197Ssam if (mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL || 5638105197Ssam mhp->ext[SADB_EXT_ADDRESS_DST] == NULL) { 5639120585Ssam ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n", 5640120585Ssam __func__)); 5641105197Ssam return key_senderror(so, m, EINVAL); 5642105197Ssam } 5643105197Ssam 5644105197Ssam if (mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) || 5645105197Ssam mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address)) { 5646120585Ssam ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n", 5647120585Ssam __func__)); 5648105197Ssam return key_senderror(so, m, EINVAL); 5649105197Ssam } 5650105197Ssam 5651105197Ssam if (mhp->ext[SADB_EXT_SA] == NULL) { 5652105197Ssam /* 5653105197Ssam * Caller wants us to delete all non-LARVAL SAs 5654105197Ssam * that match the src/dst. This is used during 5655105197Ssam * IKE INITIAL-CONTACT. 5656105197Ssam */ 5657120585Ssam ipseclog((LOG_DEBUG, "%s: doing delete all.\n", __func__)); 5658105197Ssam return key_delete_all(so, m, mhp, proto); 5659105197Ssam } else if (mhp->extlen[SADB_EXT_SA] < sizeof(struct sadb_sa)) { 5660120585Ssam ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n", 5661120585Ssam __func__)); 5662105197Ssam return key_senderror(so, m, EINVAL); 5663105197Ssam } 5664105197Ssam 5665105197Ssam sa0 = (struct sadb_sa *)mhp->ext[SADB_EXT_SA]; 5666105197Ssam src0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_SRC]); 5667105197Ssam dst0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_DST]); 5668105197Ssam 5669105197Ssam /* XXX boundary check against sa_len */ 5670105197Ssam KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); 5671105197Ssam 5672194062Svanhu /* 5673194062Svanhu * Make sure the port numbers are zero. 5674194062Svanhu * In case of NAT-T we will update them later if needed. 5675194062Svanhu */ 5676194062Svanhu KEY_PORTTOSADDR(&saidx.src, 0); 5677194062Svanhu KEY_PORTTOSADDR(&saidx.dst, 0); 5678194062Svanhu 5679194062Svanhu#ifdef IPSEC_NAT_T 5680194062Svanhu /* 5681194062Svanhu * Handle NAT-T info if present. 5682194062Svanhu */ 5683194062Svanhu if (mhp->ext[SADB_X_EXT_NAT_T_SPORT] != NULL && 5684194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_DPORT] != NULL) { 5685194062Svanhu struct sadb_x_nat_t_port *sport, *dport; 5686194062Svanhu 5687194062Svanhu if (mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport) || 5688194062Svanhu mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport)) { 5689194062Svanhu ipseclog((LOG_DEBUG, "%s: invalid message.\n", 5690194062Svanhu __func__)); 5691194062Svanhu return key_senderror(so, m, EINVAL); 5692194062Svanhu } 5693194062Svanhu 5694194062Svanhu sport = (struct sadb_x_nat_t_port *) 5695194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_SPORT]; 5696194062Svanhu dport = (struct sadb_x_nat_t_port *) 5697194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_DPORT]; 5698194062Svanhu 5699194062Svanhu if (sport) 5700194062Svanhu KEY_PORTTOSADDR(&saidx.src, 5701194062Svanhu sport->sadb_x_nat_t_port_port); 5702194062Svanhu if (dport) 5703194062Svanhu KEY_PORTTOSADDR(&saidx.dst, 5704194062Svanhu dport->sadb_x_nat_t_port_port); 5705194062Svanhu } 5706194062Svanhu#endif 5707194062Svanhu 5708105197Ssam /* get a SA header */ 5709120585Ssam SAHTREE_LOCK(); 5710181803Sbz LIST_FOREACH(sah, &V_sahtree, chain) { 5711105197Ssam if (sah->state == SADB_SASTATE_DEAD) 5712105197Ssam continue; 5713105197Ssam if (key_cmpsaidx(&sah->saidx, &saidx, CMP_HEAD) == 0) 5714105197Ssam continue; 5715105197Ssam 5716105197Ssam /* get a SA with SPI. */ 5717105197Ssam sav = key_getsavbyspi(sah, sa0->sadb_sa_spi); 5718105197Ssam if (sav) 5719105197Ssam break; 5720105197Ssam } 5721105197Ssam if (sah == NULL) { 5722120585Ssam SAHTREE_UNLOCK(); 5723120585Ssam ipseclog((LOG_DEBUG, "%s: no SA found.\n", __func__)); 5724105197Ssam return key_senderror(so, m, ENOENT); 5725105197Ssam } 5726105197Ssam 5727105197Ssam key_sa_chgstate(sav, SADB_SASTATE_DEAD); 5728120585Ssam SAHTREE_UNLOCK(); 5729105197Ssam KEY_FREESAV(&sav); 5730105197Ssam 5731105197Ssam { 5732105197Ssam struct mbuf *n; 5733105197Ssam struct sadb_msg *newmsg; 5734105197Ssam 5735105197Ssam /* create new sadb_msg to reply. */ 5736194062Svanhu /* XXX-BZ NAT-T extensions? */ 5737105197Ssam n = key_gather_mbuf(m, mhp, 1, 4, SADB_EXT_RESERVED, 5738105197Ssam SADB_EXT_SA, SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST); 5739105197Ssam if (!n) 5740105197Ssam return key_senderror(so, m, ENOBUFS); 5741105197Ssam 5742105197Ssam if (n->m_len < sizeof(struct sadb_msg)) { 5743105197Ssam n = m_pullup(n, sizeof(struct sadb_msg)); 5744105197Ssam if (n == NULL) 5745105197Ssam return key_senderror(so, m, ENOBUFS); 5746105197Ssam } 5747105197Ssam newmsg = mtod(n, struct sadb_msg *); 5748105197Ssam newmsg->sadb_msg_errno = 0; 5749105197Ssam newmsg->sadb_msg_len = PFKEY_UNIT64(n->m_pkthdr.len); 5750105197Ssam 5751105197Ssam m_freem(m); 5752105197Ssam return key_sendup_mbuf(so, n, KEY_SENDUP_ALL); 5753105197Ssam } 5754105197Ssam} 5755105197Ssam 5756105197Ssam/* 5757105197Ssam * delete all SAs for src/dst. Called from key_delete(). 5758105197Ssam */ 5759105197Ssamstatic int 5760189004Srdivackykey_delete_all(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp, 5761189004Srdivacky u_int16_t proto) 5762105197Ssam{ 5763183550Szec INIT_VNET_IPSEC(curvnet); 5764105197Ssam struct sadb_address *src0, *dst0; 5765105197Ssam struct secasindex saidx; 5766105197Ssam struct secashead *sah; 5767105197Ssam struct secasvar *sav, *nextsav; 5768105197Ssam u_int stateidx, state; 5769105197Ssam 5770105197Ssam src0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_SRC]); 5771105197Ssam dst0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_DST]); 5772105197Ssam 5773105197Ssam /* XXX boundary check against sa_len */ 5774105197Ssam KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); 5775105197Ssam 5776194062Svanhu /* 5777194062Svanhu * Make sure the port numbers are zero. 5778194062Svanhu * In case of NAT-T we will update them later if needed. 5779194062Svanhu */ 5780194062Svanhu KEY_PORTTOSADDR(&saidx.src, 0); 5781194062Svanhu KEY_PORTTOSADDR(&saidx.dst, 0); 5782194062Svanhu 5783194062Svanhu#ifdef IPSEC_NAT_T 5784194062Svanhu /* 5785194062Svanhu * Handle NAT-T info if present. 5786194062Svanhu */ 5787194062Svanhu 5788194062Svanhu if (mhp->ext[SADB_X_EXT_NAT_T_SPORT] != NULL && 5789194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_DPORT] != NULL) { 5790194062Svanhu struct sadb_x_nat_t_port *sport, *dport; 5791194062Svanhu 5792194062Svanhu if (mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport) || 5793194062Svanhu mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport)) { 5794194062Svanhu ipseclog((LOG_DEBUG, "%s: invalid message.\n", 5795194062Svanhu __func__)); 5796194062Svanhu return key_senderror(so, m, EINVAL); 5797194062Svanhu } 5798194062Svanhu 5799194062Svanhu sport = (struct sadb_x_nat_t_port *) 5800194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_SPORT]; 5801194062Svanhu dport = (struct sadb_x_nat_t_port *) 5802194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_DPORT]; 5803194062Svanhu 5804194062Svanhu if (sport) 5805194062Svanhu KEY_PORTTOSADDR(&saidx.src, 5806194062Svanhu sport->sadb_x_nat_t_port_port); 5807194062Svanhu if (dport) 5808194062Svanhu KEY_PORTTOSADDR(&saidx.dst, 5809194062Svanhu dport->sadb_x_nat_t_port_port); 5810194062Svanhu } 5811194062Svanhu#endif 5812194062Svanhu 5813120585Ssam SAHTREE_LOCK(); 5814181803Sbz LIST_FOREACH(sah, &V_sahtree, chain) { 5815105197Ssam if (sah->state == SADB_SASTATE_DEAD) 5816105197Ssam continue; 5817105197Ssam if (key_cmpsaidx(&sah->saidx, &saidx, CMP_HEAD) == 0) 5818105197Ssam continue; 5819105197Ssam 5820105197Ssam /* Delete all non-LARVAL SAs. */ 5821105197Ssam for (stateidx = 0; 5822185348Szec stateidx < _ARRAYLEN(saorder_state_alive); 5823105197Ssam stateidx++) { 5824185348Szec state = saorder_state_alive[stateidx]; 5825105197Ssam if (state == SADB_SASTATE_LARVAL) 5826105197Ssam continue; 5827105197Ssam for (sav = LIST_FIRST(&sah->savtree[state]); 5828105197Ssam sav != NULL; sav = nextsav) { 5829105197Ssam nextsav = LIST_NEXT(sav, chain); 5830105197Ssam /* sanity check */ 5831105197Ssam if (sav->state != state) { 5832120585Ssam ipseclog((LOG_DEBUG, "%s: invalid " 5833120585Ssam "sav->state (queue %d SA %d)\n", 5834120585Ssam __func__, state, sav->state)); 5835105197Ssam continue; 5836105197Ssam } 5837105197Ssam 5838105197Ssam key_sa_chgstate(sav, SADB_SASTATE_DEAD); 5839105197Ssam KEY_FREESAV(&sav); 5840105197Ssam } 5841105197Ssam } 5842105197Ssam } 5843120585Ssam SAHTREE_UNLOCK(); 5844105197Ssam { 5845105197Ssam struct mbuf *n; 5846105197Ssam struct sadb_msg *newmsg; 5847105197Ssam 5848105197Ssam /* create new sadb_msg to reply. */ 5849194062Svanhu /* XXX-BZ NAT-T extensions? */ 5850105197Ssam n = key_gather_mbuf(m, mhp, 1, 3, SADB_EXT_RESERVED, 5851105197Ssam SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST); 5852105197Ssam if (!n) 5853105197Ssam return key_senderror(so, m, ENOBUFS); 5854105197Ssam 5855105197Ssam if (n->m_len < sizeof(struct sadb_msg)) { 5856105197Ssam n = m_pullup(n, sizeof(struct sadb_msg)); 5857105197Ssam if (n == NULL) 5858105197Ssam return key_senderror(so, m, ENOBUFS); 5859105197Ssam } 5860105197Ssam newmsg = mtod(n, struct sadb_msg *); 5861105197Ssam newmsg->sadb_msg_errno = 0; 5862105197Ssam newmsg->sadb_msg_len = PFKEY_UNIT64(n->m_pkthdr.len); 5863105197Ssam 5864105197Ssam m_freem(m); 5865105197Ssam return key_sendup_mbuf(so, n, KEY_SENDUP_ALL); 5866105197Ssam } 5867105197Ssam} 5868105197Ssam 5869105197Ssam/* 5870105197Ssam * SADB_GET processing 5871105197Ssam * receive 5872105197Ssam * <base, SA(*), address(SD)> 5873105197Ssam * from the ikmpd, and get a SP and a SA to respond, 5874105197Ssam * and send, 5875105197Ssam * <base, SA, (lifetime(HSC),) address(SD), (address(P),) key(AE), 5876105197Ssam * (identity(SD),) (sensitivity)> 5877105197Ssam * to the ikmpd. 5878105197Ssam * 5879105197Ssam * m will always be freed. 5880105197Ssam */ 5881105197Ssamstatic int 5882105197Ssamkey_get(so, m, mhp) 5883105197Ssam struct socket *so; 5884105197Ssam struct mbuf *m; 5885105197Ssam const struct sadb_msghdr *mhp; 5886105197Ssam{ 5887183550Szec INIT_VNET_IPSEC(curvnet); 5888105197Ssam struct sadb_sa *sa0; 5889105197Ssam struct sadb_address *src0, *dst0; 5890105197Ssam struct secasindex saidx; 5891105197Ssam struct secashead *sah; 5892105197Ssam struct secasvar *sav = NULL; 5893105197Ssam u_int16_t proto; 5894105197Ssam 5895120585Ssam IPSEC_ASSERT(so != NULL, ("null socket")); 5896120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 5897120585Ssam IPSEC_ASSERT(mhp != NULL, ("null msghdr")); 5898120585Ssam IPSEC_ASSERT(mhp->msg != NULL, ("null msg")); 5899105197Ssam 5900105197Ssam /* map satype to proto */ 5901105197Ssam if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) { 5902120585Ssam ipseclog((LOG_DEBUG, "%s: invalid satype is passed.\n", 5903120585Ssam __func__)); 5904105197Ssam return key_senderror(so, m, EINVAL); 5905105197Ssam } 5906105197Ssam 5907105197Ssam if (mhp->ext[SADB_EXT_SA] == NULL || 5908105197Ssam mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL || 5909105197Ssam mhp->ext[SADB_EXT_ADDRESS_DST] == NULL) { 5910120585Ssam ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n", 5911120585Ssam __func__)); 5912105197Ssam return key_senderror(so, m, EINVAL); 5913105197Ssam } 5914105197Ssam if (mhp->extlen[SADB_EXT_SA] < sizeof(struct sadb_sa) || 5915105197Ssam mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) || 5916105197Ssam mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address)) { 5917120585Ssam ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n", 5918120585Ssam __func__)); 5919105197Ssam return key_senderror(so, m, EINVAL); 5920105197Ssam } 5921105197Ssam 5922105197Ssam sa0 = (struct sadb_sa *)mhp->ext[SADB_EXT_SA]; 5923105197Ssam src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC]; 5924105197Ssam dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST]; 5925105197Ssam 5926105197Ssam /* XXX boundary check against sa_len */ 5927105197Ssam KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); 5928105197Ssam 5929194062Svanhu /* 5930194062Svanhu * Make sure the port numbers are zero. 5931194062Svanhu * In case of NAT-T we will update them later if needed. 5932194062Svanhu */ 5933194062Svanhu KEY_PORTTOSADDR(&saidx.src, 0); 5934194062Svanhu KEY_PORTTOSADDR(&saidx.dst, 0); 5935194062Svanhu 5936194062Svanhu#ifdef IPSEC_NAT_T 5937194062Svanhu /* 5938194062Svanhu * Handle NAT-T info if present. 5939194062Svanhu */ 5940194062Svanhu 5941194062Svanhu if (mhp->ext[SADB_X_EXT_NAT_T_SPORT] != NULL && 5942194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_DPORT] != NULL) { 5943194062Svanhu struct sadb_x_nat_t_port *sport, *dport; 5944194062Svanhu 5945194062Svanhu if (mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport) || 5946194062Svanhu mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport)) { 5947194062Svanhu ipseclog((LOG_DEBUG, "%s: invalid message.\n", 5948194062Svanhu __func__)); 5949194062Svanhu return key_senderror(so, m, EINVAL); 5950194062Svanhu } 5951194062Svanhu 5952194062Svanhu sport = (struct sadb_x_nat_t_port *) 5953194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_SPORT]; 5954194062Svanhu dport = (struct sadb_x_nat_t_port *) 5955194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_DPORT]; 5956194062Svanhu 5957194062Svanhu if (sport) 5958194062Svanhu KEY_PORTTOSADDR(&saidx.src, 5959194062Svanhu sport->sadb_x_nat_t_port_port); 5960194062Svanhu if (dport) 5961194062Svanhu KEY_PORTTOSADDR(&saidx.dst, 5962194062Svanhu dport->sadb_x_nat_t_port_port); 5963194062Svanhu } 5964194062Svanhu#endif 5965194062Svanhu 5966105197Ssam /* get a SA header */ 5967120585Ssam SAHTREE_LOCK(); 5968181803Sbz LIST_FOREACH(sah, &V_sahtree, chain) { 5969105197Ssam if (sah->state == SADB_SASTATE_DEAD) 5970105197Ssam continue; 5971105197Ssam if (key_cmpsaidx(&sah->saidx, &saidx, CMP_HEAD) == 0) 5972105197Ssam continue; 5973105197Ssam 5974105197Ssam /* get a SA with SPI. */ 5975105197Ssam sav = key_getsavbyspi(sah, sa0->sadb_sa_spi); 5976105197Ssam if (sav) 5977105197Ssam break; 5978105197Ssam } 5979120585Ssam SAHTREE_UNLOCK(); 5980105197Ssam if (sah == NULL) { 5981120585Ssam ipseclog((LOG_DEBUG, "%s: no SA found.\n", __func__)); 5982105197Ssam return key_senderror(so, m, ENOENT); 5983105197Ssam } 5984105197Ssam 5985105197Ssam { 5986105197Ssam struct mbuf *n; 5987105197Ssam u_int8_t satype; 5988105197Ssam 5989105197Ssam /* map proto to satype */ 5990105197Ssam if ((satype = key_proto2satype(sah->saidx.proto)) == 0) { 5991120585Ssam ipseclog((LOG_DEBUG, "%s: there was invalid proto in SAD.\n", 5992120585Ssam __func__)); 5993105197Ssam return key_senderror(so, m, EINVAL); 5994105197Ssam } 5995105197Ssam 5996105197Ssam /* create new sadb_msg to reply. */ 5997105197Ssam n = key_setdumpsa(sav, SADB_GET, satype, mhp->msg->sadb_msg_seq, 5998105197Ssam mhp->msg->sadb_msg_pid); 5999105197Ssam if (!n) 6000105197Ssam return key_senderror(so, m, ENOBUFS); 6001105197Ssam 6002105197Ssam m_freem(m); 6003105197Ssam return key_sendup_mbuf(so, n, KEY_SENDUP_ONE); 6004105197Ssam } 6005105197Ssam} 6006105197Ssam 6007105197Ssam/* XXX make it sysctl-configurable? */ 6008105197Ssamstatic void 6009105197Ssamkey_getcomb_setlifetime(comb) 6010105197Ssam struct sadb_comb *comb; 6011105197Ssam{ 6012105197Ssam 6013105197Ssam comb->sadb_comb_soft_allocations = 1; 6014105197Ssam comb->sadb_comb_hard_allocations = 1; 6015105197Ssam comb->sadb_comb_soft_bytes = 0; 6016105197Ssam comb->sadb_comb_hard_bytes = 0; 6017105197Ssam comb->sadb_comb_hard_addtime = 86400; /* 1 day */ 6018105197Ssam comb->sadb_comb_soft_addtime = comb->sadb_comb_soft_addtime * 80 / 100; 6019105197Ssam comb->sadb_comb_soft_usetime = 28800; /* 8 hours */ 6020105197Ssam comb->sadb_comb_hard_usetime = comb->sadb_comb_hard_usetime * 80 / 100; 6021105197Ssam} 6022105197Ssam 6023105197Ssam/* 6024105197Ssam * XXX reorder combinations by preference 6025105197Ssam * XXX no idea if the user wants ESP authentication or not 6026105197Ssam */ 6027105197Ssamstatic struct mbuf * 6028105197Ssamkey_getcomb_esp() 6029105197Ssam{ 6030183550Szec INIT_VNET_IPSEC(curvnet); 6031105197Ssam struct sadb_comb *comb; 6032105197Ssam struct enc_xform *algo; 6033105197Ssam struct mbuf *result = NULL, *m, *n; 6034105197Ssam int encmin; 6035105197Ssam int i, off, o; 6036105197Ssam int totlen; 6037105197Ssam const int l = PFKEY_ALIGN8(sizeof(struct sadb_comb)); 6038105197Ssam 6039105197Ssam m = NULL; 6040105197Ssam for (i = 1; i <= SADB_EALG_MAX; i++) { 6041105197Ssam algo = esp_algorithm_lookup(i); 6042105197Ssam if (algo == NULL) 6043105197Ssam continue; 6044105197Ssam 6045105197Ssam /* discard algorithms with key size smaller than system min */ 6046181803Sbz if (_BITS(algo->maxkey) < V_ipsec_esp_keymin) 6047105197Ssam continue; 6048181803Sbz if (_BITS(algo->minkey) < V_ipsec_esp_keymin) 6049181803Sbz encmin = V_ipsec_esp_keymin; 6050105197Ssam else 6051105197Ssam encmin = _BITS(algo->minkey); 6052105197Ssam 6053181803Sbz if (V_ipsec_esp_auth) 6054105197Ssam m = key_getcomb_ah(); 6055105197Ssam else { 6056120585Ssam IPSEC_ASSERT(l <= MLEN, 6057120585Ssam ("l=%u > MLEN=%lu", l, (u_long) MLEN)); 6058111119Simp MGET(m, M_DONTWAIT, MT_DATA); 6059105197Ssam if (m) { 6060105197Ssam M_ALIGN(m, l); 6061105197Ssam m->m_len = l; 6062105197Ssam m->m_next = NULL; 6063105197Ssam bzero(mtod(m, caddr_t), m->m_len); 6064105197Ssam } 6065105197Ssam } 6066105197Ssam if (!m) 6067105197Ssam goto fail; 6068105197Ssam 6069105197Ssam totlen = 0; 6070105197Ssam for (n = m; n; n = n->m_next) 6071105197Ssam totlen += n->m_len; 6072120585Ssam IPSEC_ASSERT((totlen % l) == 0, ("totlen=%u, l=%u", totlen, l)); 6073105197Ssam 6074105197Ssam for (off = 0; off < totlen; off += l) { 6075105197Ssam n = m_pulldown(m, off, l, &o); 6076105197Ssam if (!n) { 6077105197Ssam /* m is already freed */ 6078105197Ssam goto fail; 6079105197Ssam } 6080105197Ssam comb = (struct sadb_comb *)(mtod(n, caddr_t) + o); 6081105197Ssam bzero(comb, sizeof(*comb)); 6082105197Ssam key_getcomb_setlifetime(comb); 6083105197Ssam comb->sadb_comb_encrypt = i; 6084105197Ssam comb->sadb_comb_encrypt_minbits = encmin; 6085105197Ssam comb->sadb_comb_encrypt_maxbits = _BITS(algo->maxkey); 6086105197Ssam } 6087105197Ssam 6088105197Ssam if (!result) 6089105197Ssam result = m; 6090105197Ssam else 6091105197Ssam m_cat(result, m); 6092105197Ssam } 6093105197Ssam 6094105197Ssam return result; 6095105197Ssam 6096105197Ssam fail: 6097105197Ssam if (result) 6098105197Ssam m_freem(result); 6099105197Ssam return NULL; 6100105197Ssam} 6101105197Ssam 6102105197Ssamstatic void 6103105197Ssamkey_getsizes_ah( 6104105197Ssam const struct auth_hash *ah, 6105105197Ssam int alg, 6106105197Ssam u_int16_t* min, 6107105197Ssam u_int16_t* max) 6108105197Ssam{ 6109183550Szec INIT_VNET_IPSEC(curvnet); 6110183550Szec 6111105197Ssam *min = *max = ah->keysize; 6112105197Ssam if (ah->keysize == 0) { 6113105197Ssam /* 6114105197Ssam * Transform takes arbitrary key size but algorithm 6115105197Ssam * key size is restricted. Enforce this here. 6116105197Ssam */ 6117105197Ssam switch (alg) { 6118105197Ssam case SADB_X_AALG_MD5: *min = *max = 16; break; 6119105197Ssam case SADB_X_AALG_SHA: *min = *max = 20; break; 6120105197Ssam case SADB_X_AALG_NULL: *min = 1; *max = 256; break; 6121105197Ssam default: 6122120585Ssam DPRINTF(("%s: unknown AH algorithm %u\n", 6123120585Ssam __func__, alg)); 6124105197Ssam break; 6125105197Ssam } 6126105197Ssam } 6127105197Ssam} 6128105197Ssam 6129105197Ssam/* 6130105197Ssam * XXX reorder combinations by preference 6131105197Ssam */ 6132105197Ssamstatic struct mbuf * 6133105197Ssamkey_getcomb_ah() 6134105197Ssam{ 6135183550Szec INIT_VNET_IPSEC(curvnet); 6136105197Ssam struct sadb_comb *comb; 6137105197Ssam struct auth_hash *algo; 6138105197Ssam struct mbuf *m; 6139105197Ssam u_int16_t minkeysize, maxkeysize; 6140105197Ssam int i; 6141105197Ssam const int l = PFKEY_ALIGN8(sizeof(struct sadb_comb)); 6142105197Ssam 6143105197Ssam m = NULL; 6144105197Ssam for (i = 1; i <= SADB_AALG_MAX; i++) { 6145105197Ssam#if 1 6146105197Ssam /* we prefer HMAC algorithms, not old algorithms */ 6147105197Ssam if (i != SADB_AALG_SHA1HMAC && i != SADB_AALG_MD5HMAC) 6148105197Ssam continue; 6149105197Ssam#endif 6150105197Ssam algo = ah_algorithm_lookup(i); 6151105197Ssam if (!algo) 6152105197Ssam continue; 6153105197Ssam key_getsizes_ah(algo, i, &minkeysize, &maxkeysize); 6154105197Ssam /* discard algorithms with key size smaller than system min */ 6155181803Sbz if (_BITS(minkeysize) < V_ipsec_ah_keymin) 6156105197Ssam continue; 6157105197Ssam 6158105197Ssam if (!m) { 6159120585Ssam IPSEC_ASSERT(l <= MLEN, 6160120585Ssam ("l=%u > MLEN=%lu", l, (u_long) MLEN)); 6161111119Simp MGET(m, M_DONTWAIT, MT_DATA); 6162105197Ssam if (m) { 6163105197Ssam M_ALIGN(m, l); 6164105197Ssam m->m_len = l; 6165105197Ssam m->m_next = NULL; 6166105197Ssam } 6167105197Ssam } else 6168111119Simp M_PREPEND(m, l, M_DONTWAIT); 6169105197Ssam if (!m) 6170105197Ssam return NULL; 6171105197Ssam 6172105197Ssam comb = mtod(m, struct sadb_comb *); 6173105197Ssam bzero(comb, sizeof(*comb)); 6174105197Ssam key_getcomb_setlifetime(comb); 6175105197Ssam comb->sadb_comb_auth = i; 6176105197Ssam comb->sadb_comb_auth_minbits = _BITS(minkeysize); 6177105197Ssam comb->sadb_comb_auth_maxbits = _BITS(maxkeysize); 6178105197Ssam } 6179105197Ssam 6180105197Ssam return m; 6181105197Ssam} 6182105197Ssam 6183105197Ssam/* 6184105197Ssam * not really an official behavior. discussed in pf_key@inner.net in Sep2000. 6185105197Ssam * XXX reorder combinations by preference 6186105197Ssam */ 6187105197Ssamstatic struct mbuf * 6188105197Ssamkey_getcomb_ipcomp() 6189105197Ssam{ 6190105197Ssam struct sadb_comb *comb; 6191105197Ssam struct comp_algo *algo; 6192105197Ssam struct mbuf *m; 6193105197Ssam int i; 6194105197Ssam const int l = PFKEY_ALIGN8(sizeof(struct sadb_comb)); 6195105197Ssam 6196105197Ssam m = NULL; 6197105197Ssam for (i = 1; i <= SADB_X_CALG_MAX; i++) { 6198105197Ssam algo = ipcomp_algorithm_lookup(i); 6199105197Ssam if (!algo) 6200105197Ssam continue; 6201105197Ssam 6202105197Ssam if (!m) { 6203120585Ssam IPSEC_ASSERT(l <= MLEN, 6204120585Ssam ("l=%u > MLEN=%lu", l, (u_long) MLEN)); 6205111119Simp MGET(m, M_DONTWAIT, MT_DATA); 6206105197Ssam if (m) { 6207105197Ssam M_ALIGN(m, l); 6208105197Ssam m->m_len = l; 6209105197Ssam m->m_next = NULL; 6210105197Ssam } 6211105197Ssam } else 6212111119Simp M_PREPEND(m, l, M_DONTWAIT); 6213105197Ssam if (!m) 6214105197Ssam return NULL; 6215105197Ssam 6216105197Ssam comb = mtod(m, struct sadb_comb *); 6217105197Ssam bzero(comb, sizeof(*comb)); 6218105197Ssam key_getcomb_setlifetime(comb); 6219105197Ssam comb->sadb_comb_encrypt = i; 6220105197Ssam /* what should we set into sadb_comb_*_{min,max}bits? */ 6221105197Ssam } 6222105197Ssam 6223105197Ssam return m; 6224105197Ssam} 6225105197Ssam 6226105197Ssam/* 6227105197Ssam * XXX no way to pass mode (transport/tunnel) to userland 6228105197Ssam * XXX replay checking? 6229105197Ssam * XXX sysctl interface to ipsec_{ah,esp}_keymin 6230105197Ssam */ 6231105197Ssamstatic struct mbuf * 6232105197Ssamkey_getprop(saidx) 6233105197Ssam const struct secasindex *saidx; 6234105197Ssam{ 6235105197Ssam struct sadb_prop *prop; 6236105197Ssam struct mbuf *m, *n; 6237105197Ssam const int l = PFKEY_ALIGN8(sizeof(struct sadb_prop)); 6238105197Ssam int totlen; 6239105197Ssam 6240105197Ssam switch (saidx->proto) { 6241105197Ssam case IPPROTO_ESP: 6242105197Ssam m = key_getcomb_esp(); 6243105197Ssam break; 6244105197Ssam case IPPROTO_AH: 6245105197Ssam m = key_getcomb_ah(); 6246105197Ssam break; 6247105197Ssam case IPPROTO_IPCOMP: 6248105197Ssam m = key_getcomb_ipcomp(); 6249105197Ssam break; 6250105197Ssam default: 6251105197Ssam return NULL; 6252105197Ssam } 6253105197Ssam 6254105197Ssam if (!m) 6255105197Ssam return NULL; 6256111119Simp M_PREPEND(m, l, M_DONTWAIT); 6257105197Ssam if (!m) 6258105197Ssam return NULL; 6259105197Ssam 6260105197Ssam totlen = 0; 6261105197Ssam for (n = m; n; n = n->m_next) 6262105197Ssam totlen += n->m_len; 6263105197Ssam 6264105197Ssam prop = mtod(m, struct sadb_prop *); 6265105197Ssam bzero(prop, sizeof(*prop)); 6266105197Ssam prop->sadb_prop_len = PFKEY_UNIT64(totlen); 6267105197Ssam prop->sadb_prop_exttype = SADB_EXT_PROPOSAL; 6268105197Ssam prop->sadb_prop_replay = 32; /* XXX */ 6269105197Ssam 6270105197Ssam return m; 6271105197Ssam} 6272105197Ssam 6273105197Ssam/* 6274105197Ssam * SADB_ACQUIRE processing called by key_checkrequest() and key_acquire2(). 6275105197Ssam * send 6276105197Ssam * <base, SA, address(SD), (address(P)), x_policy, 6277105197Ssam * (identity(SD),) (sensitivity,) proposal> 6278105197Ssam * to KMD, and expect to receive 6279105197Ssam * <base> with SADB_ACQUIRE if error occured, 6280105197Ssam * or 6281105197Ssam * <base, src address, dst address, (SPI range)> with SADB_GETSPI 6282105197Ssam * from KMD by PF_KEY. 6283105197Ssam * 6284105197Ssam * XXX x_policy is outside of RFC2367 (KAME extension). 6285105197Ssam * XXX sensitivity is not supported. 6286105197Ssam * XXX for ipcomp, RFC2367 does not define how to fill in proposal. 6287105197Ssam * see comment for key_getcomb_ipcomp(). 6288105197Ssam * 6289105197Ssam * OUT: 6290105197Ssam * 0 : succeed 6291105197Ssam * others: error number 6292105197Ssam */ 6293105197Ssamstatic int 6294105197Ssamkey_acquire(const struct secasindex *saidx, struct secpolicy *sp) 6295105197Ssam{ 6296183550Szec INIT_VNET_IPSEC(curvnet); 6297105197Ssam struct mbuf *result = NULL, *m; 6298105197Ssam struct secacq *newacq; 6299105197Ssam u_int8_t satype; 6300105197Ssam int error = -1; 6301105197Ssam u_int32_t seq; 6302105197Ssam 6303120585Ssam IPSEC_ASSERT(saidx != NULL, ("null saidx")); 6304105197Ssam satype = key_proto2satype(saidx->proto); 6305120585Ssam IPSEC_ASSERT(satype != 0, ("null satype, protocol %u", saidx->proto)); 6306105197Ssam 6307105197Ssam /* 6308105197Ssam * We never do anything about acquirng SA. There is anather 6309105197Ssam * solution that kernel blocks to send SADB_ACQUIRE message until 6310105197Ssam * getting something message from IKEd. In later case, to be 6311105197Ssam * managed with ACQUIRING list. 6312105197Ssam */ 6313108533Sschweikh /* Get an entry to check whether sending message or not. */ 6314105197Ssam if ((newacq = key_getacq(saidx)) != NULL) { 6315181803Sbz if (V_key_blockacq_count < newacq->count) { 6316105197Ssam /* reset counter and do send message. */ 6317105197Ssam newacq->count = 0; 6318105197Ssam } else { 6319105197Ssam /* increment counter and do nothing. */ 6320105197Ssam newacq->count++; 6321105197Ssam return 0; 6322105197Ssam } 6323105197Ssam } else { 6324105197Ssam /* make new entry for blocking to send SADB_ACQUIRE. */ 6325105197Ssam if ((newacq = key_newacq(saidx)) == NULL) 6326105197Ssam return ENOBUFS; 6327105197Ssam } 6328105197Ssam 6329105197Ssam 6330105197Ssam seq = newacq->seq; 6331105197Ssam m = key_setsadbmsg(SADB_ACQUIRE, 0, satype, seq, 0, 0); 6332105197Ssam if (!m) { 6333105197Ssam error = ENOBUFS; 6334105197Ssam goto fail; 6335105197Ssam } 6336105197Ssam result = m; 6337105197Ssam 6338194062Svanhu /* 6339194062Svanhu * No SADB_X_EXT_NAT_T_* here: we do not know 6340194062Svanhu * anything related to NAT-T at this time. 6341194062Svanhu */ 6342194062Svanhu 6343105197Ssam /* set sadb_address for saidx's. */ 6344105197Ssam m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC, 6345105197Ssam &saidx->src.sa, FULLMASK, IPSEC_ULPROTO_ANY); 6346105197Ssam if (!m) { 6347105197Ssam error = ENOBUFS; 6348105197Ssam goto fail; 6349105197Ssam } 6350105197Ssam m_cat(result, m); 6351105197Ssam 6352105197Ssam m = key_setsadbaddr(SADB_EXT_ADDRESS_DST, 6353105197Ssam &saidx->dst.sa, FULLMASK, IPSEC_ULPROTO_ANY); 6354105197Ssam if (!m) { 6355105197Ssam error = ENOBUFS; 6356105197Ssam goto fail; 6357105197Ssam } 6358105197Ssam m_cat(result, m); 6359105197Ssam 6360105197Ssam /* XXX proxy address (optional) */ 6361105197Ssam 6362105197Ssam /* set sadb_x_policy */ 6363105197Ssam if (sp) { 6364105197Ssam m = key_setsadbxpolicy(sp->policy, sp->spidx.dir, sp->id); 6365105197Ssam if (!m) { 6366105197Ssam error = ENOBUFS; 6367105197Ssam goto fail; 6368105197Ssam } 6369105197Ssam m_cat(result, m); 6370105197Ssam } 6371105197Ssam 6372105197Ssam /* XXX identity (optional) */ 6373105197Ssam#if 0 6374105197Ssam if (idexttype && fqdn) { 6375105197Ssam /* create identity extension (FQDN) */ 6376105197Ssam struct sadb_ident *id; 6377105197Ssam int fqdnlen; 6378105197Ssam 6379105197Ssam fqdnlen = strlen(fqdn) + 1; /* +1 for terminating-NUL */ 6380105197Ssam id = (struct sadb_ident *)p; 6381105197Ssam bzero(id, sizeof(*id) + PFKEY_ALIGN8(fqdnlen)); 6382105197Ssam id->sadb_ident_len = PFKEY_UNIT64(sizeof(*id) + PFKEY_ALIGN8(fqdnlen)); 6383105197Ssam id->sadb_ident_exttype = idexttype; 6384105197Ssam id->sadb_ident_type = SADB_IDENTTYPE_FQDN; 6385105197Ssam bcopy(fqdn, id + 1, fqdnlen); 6386105197Ssam p += sizeof(struct sadb_ident) + PFKEY_ALIGN8(fqdnlen); 6387105197Ssam } 6388105197Ssam 6389105197Ssam if (idexttype) { 6390105197Ssam /* create identity extension (USERFQDN) */ 6391105197Ssam struct sadb_ident *id; 6392105197Ssam int userfqdnlen; 6393105197Ssam 6394105197Ssam if (userfqdn) { 6395105197Ssam /* +1 for terminating-NUL */ 6396105197Ssam userfqdnlen = strlen(userfqdn) + 1; 6397105197Ssam } else 6398105197Ssam userfqdnlen = 0; 6399105197Ssam id = (struct sadb_ident *)p; 6400105197Ssam bzero(id, sizeof(*id) + PFKEY_ALIGN8(userfqdnlen)); 6401105197Ssam id->sadb_ident_len = PFKEY_UNIT64(sizeof(*id) + PFKEY_ALIGN8(userfqdnlen)); 6402105197Ssam id->sadb_ident_exttype = idexttype; 6403105197Ssam id->sadb_ident_type = SADB_IDENTTYPE_USERFQDN; 6404105197Ssam /* XXX is it correct? */ 6405105197Ssam if (curproc && curproc->p_cred) 6406105197Ssam id->sadb_ident_id = curproc->p_cred->p_ruid; 6407105197Ssam if (userfqdn && userfqdnlen) 6408105197Ssam bcopy(userfqdn, id + 1, userfqdnlen); 6409105197Ssam p += sizeof(struct sadb_ident) + PFKEY_ALIGN8(userfqdnlen); 6410105197Ssam } 6411105197Ssam#endif 6412105197Ssam 6413105197Ssam /* XXX sensitivity (optional) */ 6414105197Ssam 6415105197Ssam /* create proposal/combination extension */ 6416105197Ssam m = key_getprop(saidx); 6417105197Ssam#if 0 6418105197Ssam /* 6419105197Ssam * spec conformant: always attach proposal/combination extension, 6420105197Ssam * the problem is that we have no way to attach it for ipcomp, 6421105197Ssam * due to the way sadb_comb is declared in RFC2367. 6422105197Ssam */ 6423105197Ssam if (!m) { 6424105197Ssam error = ENOBUFS; 6425105197Ssam goto fail; 6426105197Ssam } 6427105197Ssam m_cat(result, m); 6428105197Ssam#else 6429105197Ssam /* 6430105197Ssam * outside of spec; make proposal/combination extension optional. 6431105197Ssam */ 6432105197Ssam if (m) 6433105197Ssam m_cat(result, m); 6434105197Ssam#endif 6435105197Ssam 6436105197Ssam if ((result->m_flags & M_PKTHDR) == 0) { 6437105197Ssam error = EINVAL; 6438105197Ssam goto fail; 6439105197Ssam } 6440105197Ssam 6441105197Ssam if (result->m_len < sizeof(struct sadb_msg)) { 6442105197Ssam result = m_pullup(result, sizeof(struct sadb_msg)); 6443105197Ssam if (result == NULL) { 6444105197Ssam error = ENOBUFS; 6445105197Ssam goto fail; 6446105197Ssam } 6447105197Ssam } 6448105197Ssam 6449105197Ssam result->m_pkthdr.len = 0; 6450105197Ssam for (m = result; m; m = m->m_next) 6451105197Ssam result->m_pkthdr.len += m->m_len; 6452105197Ssam 6453105197Ssam mtod(result, struct sadb_msg *)->sadb_msg_len = 6454105197Ssam PFKEY_UNIT64(result->m_pkthdr.len); 6455105197Ssam 6456105197Ssam return key_sendup_mbuf(NULL, result, KEY_SENDUP_REGISTERED); 6457105197Ssam 6458105197Ssam fail: 6459105197Ssam if (result) 6460105197Ssam m_freem(result); 6461105197Ssam return error; 6462105197Ssam} 6463105197Ssam 6464105197Ssamstatic struct secacq * 6465105197Ssamkey_newacq(const struct secasindex *saidx) 6466105197Ssam{ 6467183550Szec INIT_VNET_IPSEC(curvnet); 6468105197Ssam struct secacq *newacq; 6469105197Ssam 6470105197Ssam /* get new entry */ 6471119643Ssam newacq = malloc(sizeof(struct secacq), M_IPSEC_SAQ, M_NOWAIT|M_ZERO); 6472105197Ssam if (newacq == NULL) { 6473120585Ssam ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__)); 6474105197Ssam return NULL; 6475105197Ssam } 6476105197Ssam 6477105197Ssam /* copy secindex */ 6478105197Ssam bcopy(saidx, &newacq->saidx, sizeof(newacq->saidx)); 6479181803Sbz newacq->seq = (V_acq_seq == ~0 ? 1 : ++V_acq_seq); 6480105197Ssam newacq->created = time_second; 6481105197Ssam newacq->count = 0; 6482105197Ssam 6483119643Ssam /* add to acqtree */ 6484120585Ssam ACQ_LOCK(); 6485181803Sbz LIST_INSERT_HEAD(&V_acqtree, newacq, chain); 6486120585Ssam ACQ_UNLOCK(); 6487119643Ssam 6488105197Ssam return newacq; 6489105197Ssam} 6490105197Ssam 6491105197Ssamstatic struct secacq * 6492105197Ssamkey_getacq(const struct secasindex *saidx) 6493105197Ssam{ 6494183550Szec INIT_VNET_IPSEC(curvnet); 6495105197Ssam struct secacq *acq; 6496105197Ssam 6497120585Ssam ACQ_LOCK(); 6498181803Sbz LIST_FOREACH(acq, &V_acqtree, chain) { 6499105197Ssam if (key_cmpsaidx(saidx, &acq->saidx, CMP_EXACTLY)) 6500119643Ssam break; 6501105197Ssam } 6502120585Ssam ACQ_UNLOCK(); 6503105197Ssam 6504119643Ssam return acq; 6505105197Ssam} 6506105197Ssam 6507105197Ssamstatic struct secacq * 6508105197Ssamkey_getacqbyseq(seq) 6509105197Ssam u_int32_t seq; 6510105197Ssam{ 6511183550Szec INIT_VNET_IPSEC(curvnet); 6512105197Ssam struct secacq *acq; 6513105197Ssam 6514120585Ssam ACQ_LOCK(); 6515181803Sbz LIST_FOREACH(acq, &V_acqtree, chain) { 6516105197Ssam if (acq->seq == seq) 6517119643Ssam break; 6518105197Ssam } 6519120585Ssam ACQ_UNLOCK(); 6520105197Ssam 6521119643Ssam return acq; 6522105197Ssam} 6523105197Ssam 6524105197Ssamstatic struct secspacq * 6525105197Ssamkey_newspacq(spidx) 6526105197Ssam struct secpolicyindex *spidx; 6527105197Ssam{ 6528183550Szec INIT_VNET_IPSEC(curvnet); 6529105197Ssam struct secspacq *acq; 6530105197Ssam 6531105197Ssam /* get new entry */ 6532119643Ssam acq = malloc(sizeof(struct secspacq), M_IPSEC_SAQ, M_NOWAIT|M_ZERO); 6533105197Ssam if (acq == NULL) { 6534120585Ssam ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__)); 6535105197Ssam return NULL; 6536105197Ssam } 6537105197Ssam 6538105197Ssam /* copy secindex */ 6539105197Ssam bcopy(spidx, &acq->spidx, sizeof(acq->spidx)); 6540105197Ssam acq->created = time_second; 6541105197Ssam acq->count = 0; 6542105197Ssam 6543119643Ssam /* add to spacqtree */ 6544120585Ssam SPACQ_LOCK(); 6545181803Sbz LIST_INSERT_HEAD(&V_spacqtree, acq, chain); 6546120585Ssam SPACQ_UNLOCK(); 6547119643Ssam 6548105197Ssam return acq; 6549105197Ssam} 6550105197Ssam 6551105197Ssamstatic struct secspacq * 6552105197Ssamkey_getspacq(spidx) 6553105197Ssam struct secpolicyindex *spidx; 6554105197Ssam{ 6555183550Szec INIT_VNET_IPSEC(curvnet); 6556105197Ssam struct secspacq *acq; 6557105197Ssam 6558120585Ssam SPACQ_LOCK(); 6559181803Sbz LIST_FOREACH(acq, &V_spacqtree, chain) { 6560119643Ssam if (key_cmpspidx_exactly(spidx, &acq->spidx)) { 6561119643Ssam /* NB: return holding spacq_lock */ 6562105197Ssam return acq; 6563119643Ssam } 6564105197Ssam } 6565120585Ssam SPACQ_UNLOCK(); 6566105197Ssam 6567105197Ssam return NULL; 6568105197Ssam} 6569105197Ssam 6570105197Ssam/* 6571105197Ssam * SADB_ACQUIRE processing, 6572105197Ssam * in first situation, is receiving 6573105197Ssam * <base> 6574105197Ssam * from the ikmpd, and clear sequence of its secasvar entry. 6575105197Ssam * 6576105197Ssam * In second situation, is receiving 6577105197Ssam * <base, address(SD), (address(P),) (identity(SD),) (sensitivity,) proposal> 6578105197Ssam * from a user land process, and return 6579105197Ssam * <base, address(SD), (address(P),) (identity(SD),) (sensitivity,) proposal> 6580105197Ssam * to the socket. 6581105197Ssam * 6582105197Ssam * m will always be freed. 6583105197Ssam */ 6584105197Ssamstatic int 6585105197Ssamkey_acquire2(so, m, mhp) 6586105197Ssam struct socket *so; 6587105197Ssam struct mbuf *m; 6588105197Ssam const struct sadb_msghdr *mhp; 6589105197Ssam{ 6590183550Szec INIT_VNET_IPSEC(curvnet); 6591105197Ssam const struct sadb_address *src0, *dst0; 6592105197Ssam struct secasindex saidx; 6593105197Ssam struct secashead *sah; 6594105197Ssam u_int16_t proto; 6595105197Ssam int error; 6596105197Ssam 6597120585Ssam IPSEC_ASSERT(so != NULL, ("null socket")); 6598120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 6599120585Ssam IPSEC_ASSERT(mhp != NULL, ("null msghdr")); 6600120585Ssam IPSEC_ASSERT(mhp->msg != NULL, ("null msg")); 6601105197Ssam 6602105197Ssam /* 6603105197Ssam * Error message from KMd. 6604105197Ssam * We assume that if error was occured in IKEd, the length of PFKEY 6605105197Ssam * message is equal to the size of sadb_msg structure. 6606105197Ssam * We do not raise error even if error occured in this function. 6607105197Ssam */ 6608105197Ssam if (mhp->msg->sadb_msg_len == PFKEY_UNIT64(sizeof(struct sadb_msg))) { 6609105197Ssam struct secacq *acq; 6610105197Ssam 6611105197Ssam /* check sequence number */ 6612105197Ssam if (mhp->msg->sadb_msg_seq == 0) { 6613120585Ssam ipseclog((LOG_DEBUG, "%s: must specify sequence " 6614120585Ssam "number.\n", __func__)); 6615105197Ssam m_freem(m); 6616105197Ssam return 0; 6617105197Ssam } 6618105197Ssam 6619105197Ssam if ((acq = key_getacqbyseq(mhp->msg->sadb_msg_seq)) == NULL) { 6620105197Ssam /* 6621105197Ssam * the specified larval SA is already gone, or we got 6622105197Ssam * a bogus sequence number. we can silently ignore it. 6623105197Ssam */ 6624105197Ssam m_freem(m); 6625105197Ssam return 0; 6626105197Ssam } 6627105197Ssam 6628105197Ssam /* reset acq counter in order to deletion by timehander. */ 6629105197Ssam acq->created = time_second; 6630105197Ssam acq->count = 0; 6631105197Ssam m_freem(m); 6632105197Ssam return 0; 6633105197Ssam } 6634105197Ssam 6635105197Ssam /* 6636105197Ssam * This message is from user land. 6637105197Ssam */ 6638105197Ssam 6639105197Ssam /* map satype to proto */ 6640105197Ssam if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) { 6641120585Ssam ipseclog((LOG_DEBUG, "%s: invalid satype is passed.\n", 6642120585Ssam __func__)); 6643105197Ssam return key_senderror(so, m, EINVAL); 6644105197Ssam } 6645105197Ssam 6646105197Ssam if (mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL || 6647105197Ssam mhp->ext[SADB_EXT_ADDRESS_DST] == NULL || 6648105197Ssam mhp->ext[SADB_EXT_PROPOSAL] == NULL) { 6649105197Ssam /* error */ 6650120585Ssam ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n", 6651120585Ssam __func__)); 6652105197Ssam return key_senderror(so, m, EINVAL); 6653105197Ssam } 6654105197Ssam if (mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) || 6655105197Ssam mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address) || 6656105197Ssam mhp->extlen[SADB_EXT_PROPOSAL] < sizeof(struct sadb_prop)) { 6657105197Ssam /* error */ 6658120585Ssam ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n", 6659120585Ssam __func__)); 6660105197Ssam return key_senderror(so, m, EINVAL); 6661105197Ssam } 6662105197Ssam 6663105197Ssam src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC]; 6664105197Ssam dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST]; 6665105197Ssam 6666105197Ssam /* XXX boundary check against sa_len */ 6667105197Ssam KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); 6668105197Ssam 6669194062Svanhu /* 6670194062Svanhu * Make sure the port numbers are zero. 6671194062Svanhu * In case of NAT-T we will update them later if needed. 6672194062Svanhu */ 6673194062Svanhu KEY_PORTTOSADDR(&saidx.src, 0); 6674194062Svanhu KEY_PORTTOSADDR(&saidx.dst, 0); 6675194062Svanhu 6676194062Svanhu#ifndef IPSEC_NAT_T 6677194062Svanhu /* 6678194062Svanhu * Handle NAT-T info if present. 6679194062Svanhu */ 6680194062Svanhu 6681194062Svanhu if (mhp->ext[SADB_X_EXT_NAT_T_SPORT] != NULL && 6682194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_DPORT] != NULL) { 6683194062Svanhu struct sadb_x_nat_t_port *sport, *dport; 6684194062Svanhu 6685194062Svanhu if (mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport) || 6686194062Svanhu mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport)) { 6687194062Svanhu ipseclog((LOG_DEBUG, "%s: invalid message.\n", 6688194062Svanhu __func__)); 6689194062Svanhu return key_senderror(so, m, EINVAL); 6690194062Svanhu } 6691194062Svanhu 6692194062Svanhu sport = (struct sadb_x_nat_t_port *) 6693194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_SPORT]; 6694194062Svanhu dport = (struct sadb_x_nat_t_port *) 6695194062Svanhu mhp->ext[SADB_X_EXT_NAT_T_DPORT]; 6696194062Svanhu 6697194062Svanhu if (sport) 6698194062Svanhu KEY_PORTTOSADDR(&saidx.src, 6699194062Svanhu sport->sadb_x_nat_t_port_port); 6700194062Svanhu if (dport) 6701194062Svanhu KEY_PORTTOSADDR(&saidx.dst, 6702194062Svanhu dport->sadb_x_nat_t_port_port); 6703194062Svanhu } 6704194062Svanhu#endif 6705194062Svanhu 6706105197Ssam /* get a SA index */ 6707120585Ssam SAHTREE_LOCK(); 6708181803Sbz LIST_FOREACH(sah, &V_sahtree, chain) { 6709105197Ssam if (sah->state == SADB_SASTATE_DEAD) 6710105197Ssam continue; 6711105197Ssam if (key_cmpsaidx(&sah->saidx, &saidx, CMP_MODE_REQID)) 6712105197Ssam break; 6713105197Ssam } 6714120585Ssam SAHTREE_UNLOCK(); 6715105197Ssam if (sah != NULL) { 6716120585Ssam ipseclog((LOG_DEBUG, "%s: a SA exists already.\n", __func__)); 6717105197Ssam return key_senderror(so, m, EEXIST); 6718105197Ssam } 6719105197Ssam 6720105197Ssam error = key_acquire(&saidx, NULL); 6721105197Ssam if (error != 0) { 6722120585Ssam ipseclog((LOG_DEBUG, "%s: error %d returned from key_acquire\n", 6723120585Ssam __func__, mhp->msg->sadb_msg_errno)); 6724105197Ssam return key_senderror(so, m, error); 6725105197Ssam } 6726105197Ssam 6727105197Ssam return key_sendup_mbuf(so, m, KEY_SENDUP_REGISTERED); 6728105197Ssam} 6729105197Ssam 6730105197Ssam/* 6731105197Ssam * SADB_REGISTER processing. 6732105197Ssam * If SATYPE_UNSPEC has been passed as satype, only return sabd_supported. 6733105197Ssam * receive 6734105197Ssam * <base> 6735105197Ssam * from the ikmpd, and register a socket to send PF_KEY messages, 6736105197Ssam * and send 6737105197Ssam * <base, supported> 6738105197Ssam * to KMD by PF_KEY. 6739105197Ssam * If socket is detached, must free from regnode. 6740105197Ssam * 6741105197Ssam * m will always be freed. 6742105197Ssam */ 6743105197Ssamstatic int 6744105197Ssamkey_register(so, m, mhp) 6745105197Ssam struct socket *so; 6746105197Ssam struct mbuf *m; 6747105197Ssam const struct sadb_msghdr *mhp; 6748105197Ssam{ 6749183550Szec INIT_VNET_IPSEC(curvnet); 6750105197Ssam struct secreg *reg, *newreg = 0; 6751105197Ssam 6752120585Ssam IPSEC_ASSERT(so != NULL, ("null socket")); 6753120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 6754120585Ssam IPSEC_ASSERT(mhp != NULL, ("null msghdr")); 6755120585Ssam IPSEC_ASSERT(mhp->msg != NULL, ("null msg")); 6756105197Ssam 6757105197Ssam /* check for invalid register message */ 6758181803Sbz if (mhp->msg->sadb_msg_satype >= sizeof(V_regtree)/sizeof(V_regtree[0])) 6759105197Ssam return key_senderror(so, m, EINVAL); 6760105197Ssam 6761105197Ssam /* When SATYPE_UNSPEC is specified, only return sabd_supported. */ 6762105197Ssam if (mhp->msg->sadb_msg_satype == SADB_SATYPE_UNSPEC) 6763105197Ssam goto setmsg; 6764105197Ssam 6765105197Ssam /* check whether existing or not */ 6766120585Ssam REGTREE_LOCK(); 6767181803Sbz LIST_FOREACH(reg, &V_regtree[mhp->msg->sadb_msg_satype], chain) { 6768105197Ssam if (reg->so == so) { 6769120585Ssam REGTREE_UNLOCK(); 6770120585Ssam ipseclog((LOG_DEBUG, "%s: socket exists already.\n", 6771120585Ssam __func__)); 6772105197Ssam return key_senderror(so, m, EEXIST); 6773105197Ssam } 6774105197Ssam } 6775105197Ssam 6776105197Ssam /* create regnode */ 6777119643Ssam newreg = malloc(sizeof(struct secreg), M_IPSEC_SAR, M_NOWAIT|M_ZERO); 6778105197Ssam if (newreg == NULL) { 6779120585Ssam REGTREE_UNLOCK(); 6780120585Ssam ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__)); 6781105197Ssam return key_senderror(so, m, ENOBUFS); 6782105197Ssam } 6783105197Ssam 6784105197Ssam newreg->so = so; 6785105197Ssam ((struct keycb *)sotorawcb(so))->kp_registered++; 6786105197Ssam 6787105197Ssam /* add regnode to regtree. */ 6788181803Sbz LIST_INSERT_HEAD(&V_regtree[mhp->msg->sadb_msg_satype], newreg, chain); 6789120585Ssam REGTREE_UNLOCK(); 6790105197Ssam 6791105197Ssam setmsg: 6792105197Ssam { 6793105197Ssam struct mbuf *n; 6794105197Ssam struct sadb_msg *newmsg; 6795105197Ssam struct sadb_supported *sup; 6796105197Ssam u_int len, alen, elen; 6797105197Ssam int off; 6798105197Ssam int i; 6799105197Ssam struct sadb_alg *alg; 6800105197Ssam 6801105197Ssam /* create new sadb_msg to reply. */ 6802105197Ssam alen = 0; 6803105197Ssam for (i = 1; i <= SADB_AALG_MAX; i++) { 6804105197Ssam if (ah_algorithm_lookup(i)) 6805105197Ssam alen += sizeof(struct sadb_alg); 6806105197Ssam } 6807105197Ssam if (alen) 6808105197Ssam alen += sizeof(struct sadb_supported); 6809105197Ssam elen = 0; 6810105197Ssam for (i = 1; i <= SADB_EALG_MAX; i++) { 6811105197Ssam if (esp_algorithm_lookup(i)) 6812105197Ssam elen += sizeof(struct sadb_alg); 6813105197Ssam } 6814105197Ssam if (elen) 6815105197Ssam elen += sizeof(struct sadb_supported); 6816105197Ssam 6817105197Ssam len = sizeof(struct sadb_msg) + alen + elen; 6818105197Ssam 6819105197Ssam if (len > MCLBYTES) 6820105197Ssam return key_senderror(so, m, ENOBUFS); 6821105197Ssam 6822111119Simp MGETHDR(n, M_DONTWAIT, MT_DATA); 6823105197Ssam if (len > MHLEN) { 6824111119Simp MCLGET(n, M_DONTWAIT); 6825105197Ssam if ((n->m_flags & M_EXT) == 0) { 6826105197Ssam m_freem(n); 6827105197Ssam n = NULL; 6828105197Ssam } 6829105197Ssam } 6830105197Ssam if (!n) 6831105197Ssam return key_senderror(so, m, ENOBUFS); 6832105197Ssam 6833105197Ssam n->m_pkthdr.len = n->m_len = len; 6834105197Ssam n->m_next = NULL; 6835105197Ssam off = 0; 6836105197Ssam 6837105197Ssam m_copydata(m, 0, sizeof(struct sadb_msg), mtod(n, caddr_t) + off); 6838105197Ssam newmsg = mtod(n, struct sadb_msg *); 6839105197Ssam newmsg->sadb_msg_errno = 0; 6840105197Ssam newmsg->sadb_msg_len = PFKEY_UNIT64(len); 6841105197Ssam off += PFKEY_ALIGN8(sizeof(struct sadb_msg)); 6842105197Ssam 6843105197Ssam /* for authentication algorithm */ 6844105197Ssam if (alen) { 6845105197Ssam sup = (struct sadb_supported *)(mtod(n, caddr_t) + off); 6846105197Ssam sup->sadb_supported_len = PFKEY_UNIT64(alen); 6847105197Ssam sup->sadb_supported_exttype = SADB_EXT_SUPPORTED_AUTH; 6848105197Ssam off += PFKEY_ALIGN8(sizeof(*sup)); 6849105197Ssam 6850105197Ssam for (i = 1; i <= SADB_AALG_MAX; i++) { 6851105197Ssam struct auth_hash *aalgo; 6852105197Ssam u_int16_t minkeysize, maxkeysize; 6853105197Ssam 6854105197Ssam aalgo = ah_algorithm_lookup(i); 6855105197Ssam if (!aalgo) 6856105197Ssam continue; 6857105197Ssam alg = (struct sadb_alg *)(mtod(n, caddr_t) + off); 6858105197Ssam alg->sadb_alg_id = i; 6859105197Ssam alg->sadb_alg_ivlen = 0; 6860105197Ssam key_getsizes_ah(aalgo, i, &minkeysize, &maxkeysize); 6861105197Ssam alg->sadb_alg_minbits = _BITS(minkeysize); 6862105197Ssam alg->sadb_alg_maxbits = _BITS(maxkeysize); 6863105197Ssam off += PFKEY_ALIGN8(sizeof(*alg)); 6864105197Ssam } 6865105197Ssam } 6866105197Ssam 6867105197Ssam /* for encryption algorithm */ 6868105197Ssam if (elen) { 6869105197Ssam sup = (struct sadb_supported *)(mtod(n, caddr_t) + off); 6870105197Ssam sup->sadb_supported_len = PFKEY_UNIT64(elen); 6871105197Ssam sup->sadb_supported_exttype = SADB_EXT_SUPPORTED_ENCRYPT; 6872105197Ssam off += PFKEY_ALIGN8(sizeof(*sup)); 6873105197Ssam 6874105197Ssam for (i = 1; i <= SADB_EALG_MAX; i++) { 6875105197Ssam struct enc_xform *ealgo; 6876105197Ssam 6877105197Ssam ealgo = esp_algorithm_lookup(i); 6878105197Ssam if (!ealgo) 6879105197Ssam continue; 6880105197Ssam alg = (struct sadb_alg *)(mtod(n, caddr_t) + off); 6881105197Ssam alg->sadb_alg_id = i; 6882105197Ssam alg->sadb_alg_ivlen = ealgo->blocksize; 6883105197Ssam alg->sadb_alg_minbits = _BITS(ealgo->minkey); 6884105197Ssam alg->sadb_alg_maxbits = _BITS(ealgo->maxkey); 6885105197Ssam off += PFKEY_ALIGN8(sizeof(struct sadb_alg)); 6886105197Ssam } 6887105197Ssam } 6888105197Ssam 6889120585Ssam IPSEC_ASSERT(off == len, 6890120585Ssam ("length assumption failed (off %u len %u)", off, len)); 6891105197Ssam 6892105197Ssam m_freem(m); 6893105197Ssam return key_sendup_mbuf(so, n, KEY_SENDUP_REGISTERED); 6894105197Ssam } 6895105197Ssam} 6896105197Ssam 6897105197Ssam/* 6898105197Ssam * free secreg entry registered. 6899105197Ssam * XXX: I want to do free a socket marked done SADB_RESIGER to socket. 6900105197Ssam */ 6901105197Ssamvoid 6902119643Ssamkey_freereg(struct socket *so) 6903105197Ssam{ 6904183550Szec INIT_VNET_IPSEC(curvnet); 6905105197Ssam struct secreg *reg; 6906105197Ssam int i; 6907105197Ssam 6908120585Ssam IPSEC_ASSERT(so != NULL, ("NULL so")); 6909105197Ssam 6910105197Ssam /* 6911105197Ssam * check whether existing or not. 6912105197Ssam * check all type of SA, because there is a potential that 6913105197Ssam * one socket is registered to multiple type of SA. 6914105197Ssam */ 6915120585Ssam REGTREE_LOCK(); 6916105197Ssam for (i = 0; i <= SADB_SATYPE_MAX; i++) { 6917181803Sbz LIST_FOREACH(reg, &V_regtree[i], chain) { 6918119643Ssam if (reg->so == so && __LIST_CHAINED(reg)) { 6919105197Ssam LIST_REMOVE(reg, chain); 6920119643Ssam free(reg, M_IPSEC_SAR); 6921105197Ssam break; 6922105197Ssam } 6923105197Ssam } 6924105197Ssam } 6925120585Ssam REGTREE_UNLOCK(); 6926105197Ssam} 6927105197Ssam 6928105197Ssam/* 6929105197Ssam * SADB_EXPIRE processing 6930105197Ssam * send 6931105197Ssam * <base, SA, SA2, lifetime(C and one of HS), address(SD)> 6932105197Ssam * to KMD by PF_KEY. 6933105197Ssam * NOTE: We send only soft lifetime extension. 6934105197Ssam * 6935105197Ssam * OUT: 0 : succeed 6936105197Ssam * others : error number 6937105197Ssam */ 6938105197Ssamstatic int 6939119643Ssamkey_expire(struct secasvar *sav) 6940105197Ssam{ 6941105197Ssam int s; 6942105197Ssam int satype; 6943105197Ssam struct mbuf *result = NULL, *m; 6944105197Ssam int len; 6945105197Ssam int error = -1; 6946105197Ssam struct sadb_lifetime *lt; 6947105197Ssam 6948105197Ssam /* XXX: Why do we lock ? */ 6949105197Ssam s = splnet(); /*called from softclock()*/ 6950105197Ssam 6951120585Ssam IPSEC_ASSERT (sav != NULL, ("null sav")); 6952120585Ssam IPSEC_ASSERT (sav->sah != NULL, ("null sa header")); 6953105197Ssam 6954105197Ssam /* set msg header */ 6955120585Ssam satype = key_proto2satype(sav->sah->saidx.proto); 6956120585Ssam IPSEC_ASSERT(satype != 0, ("invalid proto, satype %u", satype)); 6957105197Ssam m = key_setsadbmsg(SADB_EXPIRE, 0, satype, sav->seq, 0, sav->refcnt); 6958105197Ssam if (!m) { 6959105197Ssam error = ENOBUFS; 6960105197Ssam goto fail; 6961105197Ssam } 6962105197Ssam result = m; 6963105197Ssam 6964105197Ssam /* create SA extension */ 6965105197Ssam m = key_setsadbsa(sav); 6966105197Ssam if (!m) { 6967105197Ssam error = ENOBUFS; 6968105197Ssam goto fail; 6969105197Ssam } 6970105197Ssam m_cat(result, m); 6971105197Ssam 6972105197Ssam /* create SA extension */ 6973105197Ssam m = key_setsadbxsa2(sav->sah->saidx.mode, 6974105197Ssam sav->replay ? sav->replay->count : 0, 6975105197Ssam sav->sah->saidx.reqid); 6976105197Ssam if (!m) { 6977105197Ssam error = ENOBUFS; 6978105197Ssam goto fail; 6979105197Ssam } 6980105197Ssam m_cat(result, m); 6981105197Ssam 6982105197Ssam /* create lifetime extension (current and soft) */ 6983105197Ssam len = PFKEY_ALIGN8(sizeof(*lt)) * 2; 6984105197Ssam m = key_alloc_mbuf(len); 6985105197Ssam if (!m || m->m_next) { /*XXX*/ 6986105197Ssam if (m) 6987105197Ssam m_freem(m); 6988105197Ssam error = ENOBUFS; 6989105197Ssam goto fail; 6990105197Ssam } 6991105197Ssam bzero(mtod(m, caddr_t), len); 6992105197Ssam lt = mtod(m, struct sadb_lifetime *); 6993105197Ssam lt->sadb_lifetime_len = PFKEY_UNIT64(sizeof(struct sadb_lifetime)); 6994105197Ssam lt->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT; 6995157123Sgnn lt->sadb_lifetime_allocations = sav->lft_c->allocations; 6996157123Sgnn lt->sadb_lifetime_bytes = sav->lft_c->bytes; 6997157123Sgnn lt->sadb_lifetime_addtime = sav->lft_c->addtime; 6998157123Sgnn lt->sadb_lifetime_usetime = sav->lft_c->usetime; 6999105197Ssam lt = (struct sadb_lifetime *)(mtod(m, caddr_t) + len / 2); 7000176743Sbz lt->sadb_lifetime_len = PFKEY_UNIT64(sizeof(struct sadb_lifetime)); 7001176743Sbz lt->sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT; 7002176743Sbz lt->sadb_lifetime_allocations = sav->lft_s->allocations; 7003176743Sbz lt->sadb_lifetime_bytes = sav->lft_s->bytes; 7004176743Sbz lt->sadb_lifetime_addtime = sav->lft_s->addtime; 7005176743Sbz lt->sadb_lifetime_usetime = sav->lft_s->usetime; 7006105197Ssam m_cat(result, m); 7007105197Ssam 7008105197Ssam /* set sadb_address for source */ 7009105197Ssam m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC, 7010105197Ssam &sav->sah->saidx.src.sa, 7011105197Ssam FULLMASK, IPSEC_ULPROTO_ANY); 7012105197Ssam if (!m) { 7013105197Ssam error = ENOBUFS; 7014105197Ssam goto fail; 7015105197Ssam } 7016105197Ssam m_cat(result, m); 7017105197Ssam 7018105197Ssam /* set sadb_address for destination */ 7019105197Ssam m = key_setsadbaddr(SADB_EXT_ADDRESS_DST, 7020105197Ssam &sav->sah->saidx.dst.sa, 7021105197Ssam FULLMASK, IPSEC_ULPROTO_ANY); 7022105197Ssam if (!m) { 7023105197Ssam error = ENOBUFS; 7024105197Ssam goto fail; 7025105197Ssam } 7026105197Ssam m_cat(result, m); 7027105197Ssam 7028194062Svanhu /* 7029194062Svanhu * XXX-BZ Handle NAT-T extensions here. 7030194062Svanhu */ 7031194062Svanhu 7032105197Ssam if ((result->m_flags & M_PKTHDR) == 0) { 7033105197Ssam error = EINVAL; 7034105197Ssam goto fail; 7035105197Ssam } 7036105197Ssam 7037105197Ssam if (result->m_len < sizeof(struct sadb_msg)) { 7038105197Ssam result = m_pullup(result, sizeof(struct sadb_msg)); 7039105197Ssam if (result == NULL) { 7040105197Ssam error = ENOBUFS; 7041105197Ssam goto fail; 7042105197Ssam } 7043105197Ssam } 7044105197Ssam 7045105197Ssam result->m_pkthdr.len = 0; 7046105197Ssam for (m = result; m; m = m->m_next) 7047105197Ssam result->m_pkthdr.len += m->m_len; 7048105197Ssam 7049105197Ssam mtod(result, struct sadb_msg *)->sadb_msg_len = 7050105197Ssam PFKEY_UNIT64(result->m_pkthdr.len); 7051105197Ssam 7052105197Ssam splx(s); 7053105197Ssam return key_sendup_mbuf(NULL, result, KEY_SENDUP_REGISTERED); 7054105197Ssam 7055105197Ssam fail: 7056105197Ssam if (result) 7057105197Ssam m_freem(result); 7058105197Ssam splx(s); 7059105197Ssam return error; 7060105197Ssam} 7061105197Ssam 7062105197Ssam/* 7063105197Ssam * SADB_FLUSH processing 7064105197Ssam * receive 7065105197Ssam * <base> 7066105197Ssam * from the ikmpd, and free all entries in secastree. 7067105197Ssam * and send, 7068105197Ssam * <base> 7069105197Ssam * to the ikmpd. 7070105197Ssam * NOTE: to do is only marking SADB_SASTATE_DEAD. 7071105197Ssam * 7072105197Ssam * m will always be freed. 7073105197Ssam */ 7074105197Ssamstatic int 7075105197Ssamkey_flush(so, m, mhp) 7076105197Ssam struct socket *so; 7077105197Ssam struct mbuf *m; 7078105197Ssam const struct sadb_msghdr *mhp; 7079105197Ssam{ 7080183550Szec INIT_VNET_IPSEC(curvnet); 7081105197Ssam struct sadb_msg *newmsg; 7082105197Ssam struct secashead *sah, *nextsah; 7083105197Ssam struct secasvar *sav, *nextsav; 7084105197Ssam u_int16_t proto; 7085105197Ssam u_int8_t state; 7086105197Ssam u_int stateidx; 7087105197Ssam 7088120585Ssam IPSEC_ASSERT(so != NULL, ("null socket")); 7089120585Ssam IPSEC_ASSERT(mhp != NULL, ("null msghdr")); 7090120585Ssam IPSEC_ASSERT(mhp->msg != NULL, ("null msg")); 7091105197Ssam 7092105197Ssam /* map satype to proto */ 7093105197Ssam if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) { 7094120585Ssam ipseclog((LOG_DEBUG, "%s: invalid satype is passed.\n", 7095120585Ssam __func__)); 7096105197Ssam return key_senderror(so, m, EINVAL); 7097105197Ssam } 7098105197Ssam 7099105197Ssam /* no SATYPE specified, i.e. flushing all SA. */ 7100120585Ssam SAHTREE_LOCK(); 7101181803Sbz for (sah = LIST_FIRST(&V_sahtree); 7102105197Ssam sah != NULL; 7103105197Ssam sah = nextsah) { 7104105197Ssam nextsah = LIST_NEXT(sah, chain); 7105105197Ssam 7106105197Ssam if (mhp->msg->sadb_msg_satype != SADB_SATYPE_UNSPEC 7107105197Ssam && proto != sah->saidx.proto) 7108105197Ssam continue; 7109105197Ssam 7110105197Ssam for (stateidx = 0; 7111185348Szec stateidx < _ARRAYLEN(saorder_state_alive); 7112105197Ssam stateidx++) { 7113185348Szec state = saorder_state_any[stateidx]; 7114105197Ssam for (sav = LIST_FIRST(&sah->savtree[state]); 7115105197Ssam sav != NULL; 7116105197Ssam sav = nextsav) { 7117105197Ssam 7118105197Ssam nextsav = LIST_NEXT(sav, chain); 7119105197Ssam 7120105197Ssam key_sa_chgstate(sav, SADB_SASTATE_DEAD); 7121105197Ssam KEY_FREESAV(&sav); 7122105197Ssam } 7123105197Ssam } 7124105197Ssam 7125105197Ssam sah->state = SADB_SASTATE_DEAD; 7126105197Ssam } 7127120585Ssam SAHTREE_UNLOCK(); 7128105197Ssam 7129105197Ssam if (m->m_len < sizeof(struct sadb_msg) || 7130105197Ssam sizeof(struct sadb_msg) > m->m_len + M_TRAILINGSPACE(m)) { 7131120585Ssam ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__)); 7132105197Ssam return key_senderror(so, m, ENOBUFS); 7133105197Ssam } 7134105197Ssam 7135105197Ssam if (m->m_next) 7136105197Ssam m_freem(m->m_next); 7137105197Ssam m->m_next = NULL; 7138105197Ssam m->m_pkthdr.len = m->m_len = sizeof(struct sadb_msg); 7139105197Ssam newmsg = mtod(m, struct sadb_msg *); 7140105197Ssam newmsg->sadb_msg_errno = 0; 7141105197Ssam newmsg->sadb_msg_len = PFKEY_UNIT64(m->m_pkthdr.len); 7142105197Ssam 7143105197Ssam return key_sendup_mbuf(so, m, KEY_SENDUP_ALL); 7144105197Ssam} 7145105197Ssam 7146105197Ssam/* 7147105197Ssam * SADB_DUMP processing 7148105197Ssam * dump all entries including status of DEAD in SAD. 7149105197Ssam * receive 7150105197Ssam * <base> 7151105197Ssam * from the ikmpd, and dump all secasvar leaves 7152105197Ssam * and send, 7153105197Ssam * <base> ..... 7154105197Ssam * to the ikmpd. 7155105197Ssam * 7156105197Ssam * m will always be freed. 7157105197Ssam */ 7158105197Ssamstatic int 7159105197Ssamkey_dump(so, m, mhp) 7160105197Ssam struct socket *so; 7161105197Ssam struct mbuf *m; 7162105197Ssam const struct sadb_msghdr *mhp; 7163105197Ssam{ 7164183550Szec INIT_VNET_IPSEC(curvnet); 7165105197Ssam struct secashead *sah; 7166105197Ssam struct secasvar *sav; 7167105197Ssam u_int16_t proto; 7168105197Ssam u_int stateidx; 7169105197Ssam u_int8_t satype; 7170105197Ssam u_int8_t state; 7171105197Ssam int cnt; 7172105197Ssam struct sadb_msg *newmsg; 7173105197Ssam struct mbuf *n; 7174105197Ssam 7175120585Ssam IPSEC_ASSERT(so != NULL, ("null socket")); 7176120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 7177120585Ssam IPSEC_ASSERT(mhp != NULL, ("null msghdr")); 7178120585Ssam IPSEC_ASSERT(mhp->msg != NULL, ("null msg")); 7179105197Ssam 7180105197Ssam /* map satype to proto */ 7181105197Ssam if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) { 7182120585Ssam ipseclog((LOG_DEBUG, "%s: invalid satype is passed.\n", 7183120585Ssam __func__)); 7184105197Ssam return key_senderror(so, m, EINVAL); 7185105197Ssam } 7186105197Ssam 7187105197Ssam /* count sav entries to be sent to the userland. */ 7188105197Ssam cnt = 0; 7189120585Ssam SAHTREE_LOCK(); 7190181803Sbz LIST_FOREACH(sah, &V_sahtree, chain) { 7191105197Ssam if (mhp->msg->sadb_msg_satype != SADB_SATYPE_UNSPEC 7192105197Ssam && proto != sah->saidx.proto) 7193105197Ssam continue; 7194105197Ssam 7195105197Ssam for (stateidx = 0; 7196185348Szec stateidx < _ARRAYLEN(saorder_state_any); 7197105197Ssam stateidx++) { 7198185348Szec state = saorder_state_any[stateidx]; 7199105197Ssam LIST_FOREACH(sav, &sah->savtree[state], chain) { 7200105197Ssam cnt++; 7201105197Ssam } 7202105197Ssam } 7203105197Ssam } 7204105197Ssam 7205119643Ssam if (cnt == 0) { 7206120585Ssam SAHTREE_UNLOCK(); 7207105197Ssam return key_senderror(so, m, ENOENT); 7208119643Ssam } 7209105197Ssam 7210105197Ssam /* send this to the userland, one at a time. */ 7211105197Ssam newmsg = NULL; 7212181803Sbz LIST_FOREACH(sah, &V_sahtree, chain) { 7213105197Ssam if (mhp->msg->sadb_msg_satype != SADB_SATYPE_UNSPEC 7214105197Ssam && proto != sah->saidx.proto) 7215105197Ssam continue; 7216105197Ssam 7217105197Ssam /* map proto to satype */ 7218105197Ssam if ((satype = key_proto2satype(sah->saidx.proto)) == 0) { 7219120585Ssam SAHTREE_UNLOCK(); 7220120585Ssam ipseclog((LOG_DEBUG, "%s: there was invalid proto in " 7221120585Ssam "SAD.\n", __func__)); 7222105197Ssam return key_senderror(so, m, EINVAL); 7223105197Ssam } 7224105197Ssam 7225105197Ssam for (stateidx = 0; 7226185348Szec stateidx < _ARRAYLEN(saorder_state_any); 7227105197Ssam stateidx++) { 7228185348Szec state = saorder_state_any[stateidx]; 7229105197Ssam LIST_FOREACH(sav, &sah->savtree[state], chain) { 7230105197Ssam n = key_setdumpsa(sav, SADB_DUMP, satype, 7231105197Ssam --cnt, mhp->msg->sadb_msg_pid); 7232119643Ssam if (!n) { 7233120585Ssam SAHTREE_UNLOCK(); 7234105197Ssam return key_senderror(so, m, ENOBUFS); 7235119643Ssam } 7236105197Ssam key_sendup_mbuf(so, n, KEY_SENDUP_ONE); 7237105197Ssam } 7238105197Ssam } 7239105197Ssam } 7240120585Ssam SAHTREE_UNLOCK(); 7241105197Ssam 7242105197Ssam m_freem(m); 7243105197Ssam return 0; 7244105197Ssam} 7245105197Ssam 7246105197Ssam/* 7247105197Ssam * SADB_X_PROMISC processing 7248105197Ssam * 7249105197Ssam * m will always be freed. 7250105197Ssam */ 7251105197Ssamstatic int 7252105197Ssamkey_promisc(so, m, mhp) 7253105197Ssam struct socket *so; 7254105197Ssam struct mbuf *m; 7255105197Ssam const struct sadb_msghdr *mhp; 7256105197Ssam{ 7257105197Ssam int olen; 7258105197Ssam 7259120585Ssam IPSEC_ASSERT(so != NULL, ("null socket")); 7260120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 7261120585Ssam IPSEC_ASSERT(mhp != NULL, ("null msghdr")); 7262120585Ssam IPSEC_ASSERT(mhp->msg != NULL, ("null msg")); 7263105197Ssam 7264105197Ssam olen = PFKEY_UNUNIT64(mhp->msg->sadb_msg_len); 7265105197Ssam 7266105197Ssam if (olen < sizeof(struct sadb_msg)) { 7267105197Ssam#if 1 7268105197Ssam return key_senderror(so, m, EINVAL); 7269105197Ssam#else 7270105197Ssam m_freem(m); 7271105197Ssam return 0; 7272105197Ssam#endif 7273105197Ssam } else if (olen == sizeof(struct sadb_msg)) { 7274105197Ssam /* enable/disable promisc mode */ 7275105197Ssam struct keycb *kp; 7276105197Ssam 7277105197Ssam if ((kp = (struct keycb *)sotorawcb(so)) == NULL) 7278105197Ssam return key_senderror(so, m, EINVAL); 7279105197Ssam mhp->msg->sadb_msg_errno = 0; 7280105197Ssam switch (mhp->msg->sadb_msg_satype) { 7281105197Ssam case 0: 7282105197Ssam case 1: 7283105197Ssam kp->kp_promisc = mhp->msg->sadb_msg_satype; 7284105197Ssam break; 7285105197Ssam default: 7286105197Ssam return key_senderror(so, m, EINVAL); 7287105197Ssam } 7288105197Ssam 7289105197Ssam /* send the original message back to everyone */ 7290105197Ssam mhp->msg->sadb_msg_errno = 0; 7291105197Ssam return key_sendup_mbuf(so, m, KEY_SENDUP_ALL); 7292105197Ssam } else { 7293105197Ssam /* send packet as is */ 7294105197Ssam 7295105197Ssam m_adj(m, PFKEY_ALIGN8(sizeof(struct sadb_msg))); 7296105197Ssam 7297105197Ssam /* TODO: if sadb_msg_seq is specified, send to specific pid */ 7298105197Ssam return key_sendup_mbuf(so, m, KEY_SENDUP_ALL); 7299105197Ssam } 7300105197Ssam} 7301105197Ssam 7302105197Ssamstatic int (*key_typesw[]) __P((struct socket *, struct mbuf *, 7303105197Ssam const struct sadb_msghdr *)) = { 7304105197Ssam NULL, /* SADB_RESERVED */ 7305105197Ssam key_getspi, /* SADB_GETSPI */ 7306105197Ssam key_update, /* SADB_UPDATE */ 7307105197Ssam key_add, /* SADB_ADD */ 7308105197Ssam key_delete, /* SADB_DELETE */ 7309105197Ssam key_get, /* SADB_GET */ 7310105197Ssam key_acquire2, /* SADB_ACQUIRE */ 7311105197Ssam key_register, /* SADB_REGISTER */ 7312105197Ssam NULL, /* SADB_EXPIRE */ 7313105197Ssam key_flush, /* SADB_FLUSH */ 7314105197Ssam key_dump, /* SADB_DUMP */ 7315105197Ssam key_promisc, /* SADB_X_PROMISC */ 7316105197Ssam NULL, /* SADB_X_PCHANGE */ 7317105197Ssam key_spdadd, /* SADB_X_SPDUPDATE */ 7318105197Ssam key_spdadd, /* SADB_X_SPDADD */ 7319105197Ssam key_spddelete, /* SADB_X_SPDDELETE */ 7320105197Ssam key_spdget, /* SADB_X_SPDGET */ 7321105197Ssam NULL, /* SADB_X_SPDACQUIRE */ 7322105197Ssam key_spddump, /* SADB_X_SPDDUMP */ 7323105197Ssam key_spdflush, /* SADB_X_SPDFLUSH */ 7324105197Ssam key_spdadd, /* SADB_X_SPDSETIDX */ 7325105197Ssam NULL, /* SADB_X_SPDEXPIRE */ 7326105197Ssam key_spddelete2, /* SADB_X_SPDDELETE2 */ 7327105197Ssam}; 7328105197Ssam 7329105197Ssam/* 7330105197Ssam * parse sadb_msg buffer to process PFKEYv2, 7331105197Ssam * and create a data to response if needed. 7332105197Ssam * I think to be dealed with mbuf directly. 7333105197Ssam * IN: 7334105197Ssam * msgp : pointer to pointer to a received buffer pulluped. 7335105197Ssam * This is rewrited to response. 7336105197Ssam * so : pointer to socket. 7337105197Ssam * OUT: 7338105197Ssam * length for buffer to send to user process. 7339105197Ssam */ 7340105197Ssamint 7341105197Ssamkey_parse(m, so) 7342105197Ssam struct mbuf *m; 7343105197Ssam struct socket *so; 7344105197Ssam{ 7345183550Szec INIT_VNET_IPSEC(curvnet); 7346105197Ssam struct sadb_msg *msg; 7347105197Ssam struct sadb_msghdr mh; 7348105197Ssam u_int orglen; 7349105197Ssam int error; 7350105197Ssam int target; 7351105197Ssam 7352120585Ssam IPSEC_ASSERT(so != NULL, ("null socket")); 7353120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 7354105197Ssam 7355105197Ssam#if 0 /*kdebug_sadb assumes msg in linear buffer*/ 7356105197Ssam KEYDEBUG(KEYDEBUG_KEY_DUMP, 7357120585Ssam ipseclog((LOG_DEBUG, "%s: passed sadb_msg\n", __func__)); 7358105197Ssam kdebug_sadb(msg)); 7359105197Ssam#endif 7360105197Ssam 7361105197Ssam if (m->m_len < sizeof(struct sadb_msg)) { 7362105197Ssam m = m_pullup(m, sizeof(struct sadb_msg)); 7363105197Ssam if (!m) 7364105197Ssam return ENOBUFS; 7365105197Ssam } 7366105197Ssam msg = mtod(m, struct sadb_msg *); 7367105197Ssam orglen = PFKEY_UNUNIT64(msg->sadb_msg_len); 7368105197Ssam target = KEY_SENDUP_ONE; 7369105197Ssam 7370105197Ssam if ((m->m_flags & M_PKTHDR) == 0 || 7371105197Ssam m->m_pkthdr.len != m->m_pkthdr.len) { 7372120585Ssam ipseclog((LOG_DEBUG, "%s: invalid message length.\n",__func__)); 7373181803Sbz V_pfkeystat.out_invlen++; 7374105197Ssam error = EINVAL; 7375105197Ssam goto senderror; 7376105197Ssam } 7377105197Ssam 7378105197Ssam if (msg->sadb_msg_version != PF_KEY_V2) { 7379120585Ssam ipseclog((LOG_DEBUG, "%s: PF_KEY version %u is mismatched.\n", 7380120585Ssam __func__, msg->sadb_msg_version)); 7381181803Sbz V_pfkeystat.out_invver++; 7382105197Ssam error = EINVAL; 7383105197Ssam goto senderror; 7384105197Ssam } 7385105197Ssam 7386105197Ssam if (msg->sadb_msg_type > SADB_MAX) { 7387120585Ssam ipseclog((LOG_DEBUG, "%s: invalid type %u is passed.\n", 7388120585Ssam __func__, msg->sadb_msg_type)); 7389181803Sbz V_pfkeystat.out_invmsgtype++; 7390105197Ssam error = EINVAL; 7391105197Ssam goto senderror; 7392105197Ssam } 7393105197Ssam 7394105197Ssam /* for old-fashioned code - should be nuked */ 7395105197Ssam if (m->m_pkthdr.len > MCLBYTES) { 7396105197Ssam m_freem(m); 7397105197Ssam return ENOBUFS; 7398105197Ssam } 7399105197Ssam if (m->m_next) { 7400105197Ssam struct mbuf *n; 7401105197Ssam 7402111119Simp MGETHDR(n, M_DONTWAIT, MT_DATA); 7403105197Ssam if (n && m->m_pkthdr.len > MHLEN) { 7404111119Simp MCLGET(n, M_DONTWAIT); 7405105197Ssam if ((n->m_flags & M_EXT) == 0) { 7406105197Ssam m_free(n); 7407105197Ssam n = NULL; 7408105197Ssam } 7409105197Ssam } 7410105197Ssam if (!n) { 7411105197Ssam m_freem(m); 7412105197Ssam return ENOBUFS; 7413105197Ssam } 7414105197Ssam m_copydata(m, 0, m->m_pkthdr.len, mtod(n, caddr_t)); 7415105197Ssam n->m_pkthdr.len = n->m_len = m->m_pkthdr.len; 7416105197Ssam n->m_next = NULL; 7417105197Ssam m_freem(m); 7418105197Ssam m = n; 7419105197Ssam } 7420105197Ssam 7421105197Ssam /* align the mbuf chain so that extensions are in contiguous region. */ 7422105197Ssam error = key_align(m, &mh); 7423105197Ssam if (error) 7424105197Ssam return error; 7425105197Ssam 7426105197Ssam msg = mh.msg; 7427105197Ssam 7428105197Ssam /* check SA type */ 7429105197Ssam switch (msg->sadb_msg_satype) { 7430105197Ssam case SADB_SATYPE_UNSPEC: 7431105197Ssam switch (msg->sadb_msg_type) { 7432105197Ssam case SADB_GETSPI: 7433105197Ssam case SADB_UPDATE: 7434105197Ssam case SADB_ADD: 7435105197Ssam case SADB_DELETE: 7436105197Ssam case SADB_GET: 7437105197Ssam case SADB_ACQUIRE: 7438105197Ssam case SADB_EXPIRE: 7439120585Ssam ipseclog((LOG_DEBUG, "%s: must specify satype " 7440120585Ssam "when msg type=%u.\n", __func__, 7441120585Ssam msg->sadb_msg_type)); 7442181803Sbz V_pfkeystat.out_invsatype++; 7443105197Ssam error = EINVAL; 7444105197Ssam goto senderror; 7445105197Ssam } 7446105197Ssam break; 7447105197Ssam case SADB_SATYPE_AH: 7448105197Ssam case SADB_SATYPE_ESP: 7449105197Ssam case SADB_X_SATYPE_IPCOMP: 7450125680Sbms case SADB_X_SATYPE_TCPSIGNATURE: 7451105197Ssam switch (msg->sadb_msg_type) { 7452105197Ssam case SADB_X_SPDADD: 7453105197Ssam case SADB_X_SPDDELETE: 7454105197Ssam case SADB_X_SPDGET: 7455105197Ssam case SADB_X_SPDDUMP: 7456105197Ssam case SADB_X_SPDFLUSH: 7457105197Ssam case SADB_X_SPDSETIDX: 7458105197Ssam case SADB_X_SPDUPDATE: 7459105197Ssam case SADB_X_SPDDELETE2: 7460120585Ssam ipseclog((LOG_DEBUG, "%s: illegal satype=%u\n", 7461120585Ssam __func__, msg->sadb_msg_type)); 7462181803Sbz V_pfkeystat.out_invsatype++; 7463105197Ssam error = EINVAL; 7464105197Ssam goto senderror; 7465105197Ssam } 7466105197Ssam break; 7467105197Ssam case SADB_SATYPE_RSVP: 7468105197Ssam case SADB_SATYPE_OSPFV2: 7469105197Ssam case SADB_SATYPE_RIPV2: 7470105197Ssam case SADB_SATYPE_MIP: 7471120585Ssam ipseclog((LOG_DEBUG, "%s: type %u isn't supported.\n", 7472120585Ssam __func__, msg->sadb_msg_satype)); 7473181803Sbz V_pfkeystat.out_invsatype++; 7474105197Ssam error = EOPNOTSUPP; 7475105197Ssam goto senderror; 7476105197Ssam case 1: /* XXX: What does it do? */ 7477105197Ssam if (msg->sadb_msg_type == SADB_X_PROMISC) 7478105197Ssam break; 7479105197Ssam /*FALLTHROUGH*/ 7480105197Ssam default: 7481120585Ssam ipseclog((LOG_DEBUG, "%s: invalid type %u is passed.\n", 7482120585Ssam __func__, msg->sadb_msg_satype)); 7483181803Sbz V_pfkeystat.out_invsatype++; 7484105197Ssam error = EINVAL; 7485105197Ssam goto senderror; 7486105197Ssam } 7487105197Ssam 7488105197Ssam /* check field of upper layer protocol and address family */ 7489105197Ssam if (mh.ext[SADB_EXT_ADDRESS_SRC] != NULL 7490105197Ssam && mh.ext[SADB_EXT_ADDRESS_DST] != NULL) { 7491105197Ssam struct sadb_address *src0, *dst0; 7492105197Ssam u_int plen; 7493105197Ssam 7494105197Ssam src0 = (struct sadb_address *)(mh.ext[SADB_EXT_ADDRESS_SRC]); 7495105197Ssam dst0 = (struct sadb_address *)(mh.ext[SADB_EXT_ADDRESS_DST]); 7496105197Ssam 7497105197Ssam /* check upper layer protocol */ 7498105197Ssam if (src0->sadb_address_proto != dst0->sadb_address_proto) { 7499120585Ssam ipseclog((LOG_DEBUG, "%s: upper layer protocol " 7500120585Ssam "mismatched.\n", __func__)); 7501181803Sbz V_pfkeystat.out_invaddr++; 7502105197Ssam error = EINVAL; 7503105197Ssam goto senderror; 7504105197Ssam } 7505105197Ssam 7506105197Ssam /* check family */ 7507105197Ssam if (PFKEY_ADDR_SADDR(src0)->sa_family != 7508105197Ssam PFKEY_ADDR_SADDR(dst0)->sa_family) { 7509120585Ssam ipseclog((LOG_DEBUG, "%s: address family mismatched.\n", 7510120585Ssam __func__)); 7511181803Sbz V_pfkeystat.out_invaddr++; 7512105197Ssam error = EINVAL; 7513105197Ssam goto senderror; 7514105197Ssam } 7515105197Ssam if (PFKEY_ADDR_SADDR(src0)->sa_len != 7516105197Ssam PFKEY_ADDR_SADDR(dst0)->sa_len) { 7517120585Ssam ipseclog((LOG_DEBUG, "%s: address struct size " 7518120585Ssam "mismatched.\n", __func__)); 7519181803Sbz V_pfkeystat.out_invaddr++; 7520105197Ssam error = EINVAL; 7521105197Ssam goto senderror; 7522105197Ssam } 7523105197Ssam 7524105197Ssam switch (PFKEY_ADDR_SADDR(src0)->sa_family) { 7525105197Ssam case AF_INET: 7526105197Ssam if (PFKEY_ADDR_SADDR(src0)->sa_len != 7527105197Ssam sizeof(struct sockaddr_in)) { 7528181803Sbz V_pfkeystat.out_invaddr++; 7529105197Ssam error = EINVAL; 7530105197Ssam goto senderror; 7531105197Ssam } 7532105197Ssam break; 7533105197Ssam case AF_INET6: 7534105197Ssam if (PFKEY_ADDR_SADDR(src0)->sa_len != 7535105197Ssam sizeof(struct sockaddr_in6)) { 7536181803Sbz V_pfkeystat.out_invaddr++; 7537105197Ssam error = EINVAL; 7538105197Ssam goto senderror; 7539105197Ssam } 7540105197Ssam break; 7541105197Ssam default: 7542120585Ssam ipseclog((LOG_DEBUG, "%s: unsupported address family\n", 7543120585Ssam __func__)); 7544181803Sbz V_pfkeystat.out_invaddr++; 7545105197Ssam error = EAFNOSUPPORT; 7546105197Ssam goto senderror; 7547105197Ssam } 7548105197Ssam 7549105197Ssam switch (PFKEY_ADDR_SADDR(src0)->sa_family) { 7550105197Ssam case AF_INET: 7551105197Ssam plen = sizeof(struct in_addr) << 3; 7552105197Ssam break; 7553105197Ssam case AF_INET6: 7554105197Ssam plen = sizeof(struct in6_addr) << 3; 7555105197Ssam break; 7556105197Ssam default: 7557105197Ssam plen = 0; /*fool gcc*/ 7558105197Ssam break; 7559105197Ssam } 7560105197Ssam 7561105197Ssam /* check max prefix length */ 7562105197Ssam if (src0->sadb_address_prefixlen > plen || 7563105197Ssam dst0->sadb_address_prefixlen > plen) { 7564120585Ssam ipseclog((LOG_DEBUG, "%s: illegal prefixlen.\n", 7565120585Ssam __func__)); 7566181803Sbz V_pfkeystat.out_invaddr++; 7567105197Ssam error = EINVAL; 7568105197Ssam goto senderror; 7569105197Ssam } 7570105197Ssam 7571105197Ssam /* 7572105197Ssam * prefixlen == 0 is valid because there can be a case when 7573105197Ssam * all addresses are matched. 7574105197Ssam */ 7575105197Ssam } 7576105197Ssam 7577105197Ssam if (msg->sadb_msg_type >= sizeof(key_typesw)/sizeof(key_typesw[0]) || 7578105197Ssam key_typesw[msg->sadb_msg_type] == NULL) { 7579181803Sbz V_pfkeystat.out_invmsgtype++; 7580105197Ssam error = EINVAL; 7581105197Ssam goto senderror; 7582105197Ssam } 7583105197Ssam 7584105197Ssam return (*key_typesw[msg->sadb_msg_type])(so, m, &mh); 7585105197Ssam 7586105197Ssamsenderror: 7587105197Ssam msg->sadb_msg_errno = error; 7588105197Ssam return key_sendup_mbuf(so, m, target); 7589105197Ssam} 7590105197Ssam 7591105197Ssamstatic int 7592105197Ssamkey_senderror(so, m, code) 7593105197Ssam struct socket *so; 7594105197Ssam struct mbuf *m; 7595105197Ssam int code; 7596105197Ssam{ 7597105197Ssam struct sadb_msg *msg; 7598105197Ssam 7599120585Ssam IPSEC_ASSERT(m->m_len >= sizeof(struct sadb_msg), 7600120585Ssam ("mbuf too small, len %u", m->m_len)); 7601105197Ssam 7602105197Ssam msg = mtod(m, struct sadb_msg *); 7603105197Ssam msg->sadb_msg_errno = code; 7604105197Ssam return key_sendup_mbuf(so, m, KEY_SENDUP_ONE); 7605105197Ssam} 7606105197Ssam 7607105197Ssam/* 7608105197Ssam * set the pointer to each header into message buffer. 7609105197Ssam * m will be freed on error. 7610105197Ssam * XXX larger-than-MCLBYTES extension? 7611105197Ssam */ 7612105197Ssamstatic int 7613105197Ssamkey_align(m, mhp) 7614105197Ssam struct mbuf *m; 7615105197Ssam struct sadb_msghdr *mhp; 7616105197Ssam{ 7617183550Szec INIT_VNET_IPSEC(curvnet); 7618105197Ssam struct mbuf *n; 7619105197Ssam struct sadb_ext *ext; 7620105197Ssam size_t off, end; 7621105197Ssam int extlen; 7622105197Ssam int toff; 7623105197Ssam 7624120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 7625120585Ssam IPSEC_ASSERT(mhp != NULL, ("null msghdr")); 7626120585Ssam IPSEC_ASSERT(m->m_len >= sizeof(struct sadb_msg), 7627120585Ssam ("mbuf too small, len %u", m->m_len)); 7628105197Ssam 7629105197Ssam /* initialize */ 7630105197Ssam bzero(mhp, sizeof(*mhp)); 7631105197Ssam 7632105197Ssam mhp->msg = mtod(m, struct sadb_msg *); 7633105197Ssam mhp->ext[0] = (struct sadb_ext *)mhp->msg; /*XXX backward compat */ 7634105197Ssam 7635105197Ssam end = PFKEY_UNUNIT64(mhp->msg->sadb_msg_len); 7636105197Ssam extlen = end; /*just in case extlen is not updated*/ 7637105197Ssam for (off = sizeof(struct sadb_msg); off < end; off += extlen) { 7638105197Ssam n = m_pulldown(m, off, sizeof(struct sadb_ext), &toff); 7639105197Ssam if (!n) { 7640105197Ssam /* m is already freed */ 7641105197Ssam return ENOBUFS; 7642105197Ssam } 7643105197Ssam ext = (struct sadb_ext *)(mtod(n, caddr_t) + toff); 7644105197Ssam 7645105197Ssam /* set pointer */ 7646105197Ssam switch (ext->sadb_ext_type) { 7647105197Ssam case SADB_EXT_SA: 7648105197Ssam case SADB_EXT_ADDRESS_SRC: 7649105197Ssam case SADB_EXT_ADDRESS_DST: 7650105197Ssam case SADB_EXT_ADDRESS_PROXY: 7651105197Ssam case SADB_EXT_LIFETIME_CURRENT: 7652105197Ssam case SADB_EXT_LIFETIME_HARD: 7653105197Ssam case SADB_EXT_LIFETIME_SOFT: 7654105197Ssam case SADB_EXT_KEY_AUTH: 7655105197Ssam case SADB_EXT_KEY_ENCRYPT: 7656105197Ssam case SADB_EXT_IDENTITY_SRC: 7657105197Ssam case SADB_EXT_IDENTITY_DST: 7658105197Ssam case SADB_EXT_SENSITIVITY: 7659105197Ssam case SADB_EXT_PROPOSAL: 7660105197Ssam case SADB_EXT_SUPPORTED_AUTH: 7661105197Ssam case SADB_EXT_SUPPORTED_ENCRYPT: 7662105197Ssam case SADB_EXT_SPIRANGE: 7663105197Ssam case SADB_X_EXT_POLICY: 7664105197Ssam case SADB_X_EXT_SA2: 7665194062Svanhu#ifdef IPSEC_NAT_T 7666194062Svanhu case SADB_X_EXT_NAT_T_TYPE: 7667194062Svanhu case SADB_X_EXT_NAT_T_SPORT: 7668194062Svanhu case SADB_X_EXT_NAT_T_DPORT: 7669194062Svanhu case SADB_X_EXT_NAT_T_OAI: 7670194062Svanhu case SADB_X_EXT_NAT_T_OAR: 7671194062Svanhu case SADB_X_EXT_NAT_T_FRAG: 7672194062Svanhu#endif 7673105197Ssam /* duplicate check */ 7674105197Ssam /* 7675105197Ssam * XXX Are there duplication payloads of either 7676105197Ssam * KEY_AUTH or KEY_ENCRYPT ? 7677105197Ssam */ 7678105197Ssam if (mhp->ext[ext->sadb_ext_type] != NULL) { 7679120585Ssam ipseclog((LOG_DEBUG, "%s: duplicate ext_type " 7680120585Ssam "%u\n", __func__, ext->sadb_ext_type)); 7681105197Ssam m_freem(m); 7682181803Sbz V_pfkeystat.out_dupext++; 7683105197Ssam return EINVAL; 7684105197Ssam } 7685105197Ssam break; 7686105197Ssam default: 7687120585Ssam ipseclog((LOG_DEBUG, "%s: invalid ext_type %u\n", 7688120585Ssam __func__, ext->sadb_ext_type)); 7689105197Ssam m_freem(m); 7690181803Sbz V_pfkeystat.out_invexttype++; 7691105197Ssam return EINVAL; 7692105197Ssam } 7693105197Ssam 7694105197Ssam extlen = PFKEY_UNUNIT64(ext->sadb_ext_len); 7695105197Ssam 7696105197Ssam if (key_validate_ext(ext, extlen)) { 7697105197Ssam m_freem(m); 7698181803Sbz V_pfkeystat.out_invlen++; 7699105197Ssam return EINVAL; 7700105197Ssam } 7701105197Ssam 7702105197Ssam n = m_pulldown(m, off, extlen, &toff); 7703105197Ssam if (!n) { 7704105197Ssam /* m is already freed */ 7705105197Ssam return ENOBUFS; 7706105197Ssam } 7707105197Ssam ext = (struct sadb_ext *)(mtod(n, caddr_t) + toff); 7708105197Ssam 7709105197Ssam mhp->ext[ext->sadb_ext_type] = ext; 7710105197Ssam mhp->extoff[ext->sadb_ext_type] = off; 7711105197Ssam mhp->extlen[ext->sadb_ext_type] = extlen; 7712105197Ssam } 7713105197Ssam 7714105197Ssam if (off != end) { 7715105197Ssam m_freem(m); 7716181803Sbz V_pfkeystat.out_invlen++; 7717105197Ssam return EINVAL; 7718105197Ssam } 7719105197Ssam 7720105197Ssam return 0; 7721105197Ssam} 7722105197Ssam 7723105197Ssamstatic int 7724105197Ssamkey_validate_ext(ext, len) 7725105197Ssam const struct sadb_ext *ext; 7726105197Ssam int len; 7727105197Ssam{ 7728105197Ssam const struct sockaddr *sa; 7729105197Ssam enum { NONE, ADDR } checktype = NONE; 7730105197Ssam int baselen = 0; 7731105197Ssam const int sal = offsetof(struct sockaddr, sa_len) + sizeof(sa->sa_len); 7732105197Ssam 7733105197Ssam if (len != PFKEY_UNUNIT64(ext->sadb_ext_len)) 7734105197Ssam return EINVAL; 7735105197Ssam 7736105197Ssam /* if it does not match minimum/maximum length, bail */ 7737105197Ssam if (ext->sadb_ext_type >= sizeof(minsize) / sizeof(minsize[0]) || 7738105197Ssam ext->sadb_ext_type >= sizeof(maxsize) / sizeof(maxsize[0])) 7739105197Ssam return EINVAL; 7740105197Ssam if (!minsize[ext->sadb_ext_type] || len < minsize[ext->sadb_ext_type]) 7741105197Ssam return EINVAL; 7742105197Ssam if (maxsize[ext->sadb_ext_type] && len > maxsize[ext->sadb_ext_type]) 7743105197Ssam return EINVAL; 7744105197Ssam 7745105197Ssam /* more checks based on sadb_ext_type XXX need more */ 7746105197Ssam switch (ext->sadb_ext_type) { 7747105197Ssam case SADB_EXT_ADDRESS_SRC: 7748105197Ssam case SADB_EXT_ADDRESS_DST: 7749105197Ssam case SADB_EXT_ADDRESS_PROXY: 7750105197Ssam baselen = PFKEY_ALIGN8(sizeof(struct sadb_address)); 7751105197Ssam checktype = ADDR; 7752105197Ssam break; 7753105197Ssam case SADB_EXT_IDENTITY_SRC: 7754105197Ssam case SADB_EXT_IDENTITY_DST: 7755105197Ssam if (((const struct sadb_ident *)ext)->sadb_ident_type == 7756105197Ssam SADB_X_IDENTTYPE_ADDR) { 7757105197Ssam baselen = PFKEY_ALIGN8(sizeof(struct sadb_ident)); 7758105197Ssam checktype = ADDR; 7759105197Ssam } else 7760105197Ssam checktype = NONE; 7761105197Ssam break; 7762105197Ssam default: 7763105197Ssam checktype = NONE; 7764105197Ssam break; 7765105197Ssam } 7766105197Ssam 7767105197Ssam switch (checktype) { 7768105197Ssam case NONE: 7769105197Ssam break; 7770105197Ssam case ADDR: 7771105197Ssam sa = (const struct sockaddr *)(((const u_int8_t*)ext)+baselen); 7772105197Ssam if (len < baselen + sal) 7773105197Ssam return EINVAL; 7774105197Ssam if (baselen + PFKEY_ALIGN8(sa->sa_len) != len) 7775105197Ssam return EINVAL; 7776105197Ssam break; 7777105197Ssam } 7778105197Ssam 7779105197Ssam return 0; 7780105197Ssam} 7781105197Ssam 7782105197Ssamvoid 7783180086Sjuliankey_init(void) 7784105197Ssam{ 7785183550Szec INIT_VNET_IPSEC(curvnet); 7786105197Ssam int i; 7787105197Ssam 7788185088Szec V_key_debug_level = 0; 7789185088Szec V_key_spi_trycnt = 1000; 7790185088Szec V_key_spi_minval = 0x100; 7791185088Szec V_key_spi_maxval = 0x0fffffff; /* XXX */ 7792185088Szec V_policy_id = 0; 7793185088Szec V_key_int_random = 60; /*interval to initialize randseed,1(m)*/ 7794185088Szec V_key_larval_lifetime = 30; /* interval to expire acquiring, 30(s)*/ 7795185088Szec V_key_blockacq_count = 10; /* counter for blocking SADB_ACQUIRE.*/ 7796185088Szec V_key_blockacq_lifetime = 20; /* lifetime for blocking SADB_ACQUIRE.*/ 7797185088Szec V_key_preferred_oldsa = 1; /* preferred old sa rather than new sa*/ 7798185088Szec 7799185088Szec V_acq_seq = 0; 7800185088Szec 7801185088Szec V_ipsec_esp_keymin = 256; 7802185088Szec V_ipsec_esp_auth = 0; 7803185088Szec V_ipsec_ah_keymin = 128; 7804185088Szec 7805119643Ssam for (i = 0; i < IPSEC_DIR_MAX; i++) 7806181803Sbz LIST_INIT(&V_sptree[i]); 7807105197Ssam 7808181803Sbz LIST_INIT(&V_sahtree); 7809105197Ssam 7810119643Ssam for (i = 0; i <= SADB_SATYPE_MAX; i++) 7811181803Sbz LIST_INIT(&V_regtree[i]); 7812105197Ssam 7813181803Sbz LIST_INIT(&V_acqtree); 7814181803Sbz LIST_INIT(&V_spacqtree); 7815105197Ssam 7816105197Ssam /* system default */ 7817181803Sbz V_ip4_def_policy.policy = IPSEC_POLICY_NONE; 7818181803Sbz V_ip4_def_policy.refcnt++; /*never reclaim this*/ 7819105197Ssam 7820190787Szec if (!IS_DEFAULT_VNET(curvnet)) 7821190787Szec return; 7822190787Szec 7823190787Szec SPTREE_LOCK_INIT(); 7824190787Szec REGTREE_LOCK_INIT(); 7825190787Szec SAHTREE_LOCK_INIT(); 7826190787Szec ACQ_LOCK_INIT(); 7827190787Szec SPACQ_LOCK_INIT(); 7828190787Szec 7829105197Ssam#ifndef IPSEC_DEBUG2 7830105197Ssam timeout((void *)key_timehandler, (void *)0, hz); 7831105197Ssam#endif /*IPSEC_DEBUG2*/ 7832105197Ssam 7833105197Ssam /* initialize key statistics */ 7834105197Ssam keystat.getspi_count = 1; 7835105197Ssam 7836177173Sbz printf("IPsec: Initialized Security Association Processing.\n"); 7837193731Szec} 7838105197Ssam 7839193731Szec#ifdef VIMAGE 7840193731Szecvoid 7841193731Szeckey_destroy(void) 7842193731Szec{ 7843193731Szec INIT_VNET_IPSEC(curvnet); 7844193731Szec struct secpolicy *sp, *nextsp; 7845193731Szec struct secspacq *acq, *nextacq; 7846193731Szec struct secashead *sah, *nextsah; 7847193731Szec struct secreg *reg; 7848193731Szec int i; 7849193731Szec 7850193731Szec SPTREE_LOCK(); 7851193731Szec for (i = 0; i < IPSEC_DIR_MAX; i++) { 7852193731Szec for (sp = LIST_FIRST(&V_sptree[i]); 7853193731Szec sp != NULL; sp = nextsp) { 7854193731Szec nextsp = LIST_NEXT(sp, chain); 7855193731Szec if (__LIST_CHAINED(sp)) { 7856193731Szec LIST_REMOVE(sp, chain); 7857193731Szec free(sp, M_IPSEC_SP); 7858193731Szec } 7859193731Szec } 7860193731Szec } 7861193731Szec SPTREE_UNLOCK(); 7862193731Szec 7863193731Szec SAHTREE_LOCK(); 7864193731Szec for (sah = LIST_FIRST(&V_sahtree); sah != NULL; sah = nextsah) { 7865193731Szec nextsah = LIST_NEXT(sah, chain); 7866193731Szec if (__LIST_CHAINED(sah)) { 7867193731Szec LIST_REMOVE(sah, chain); 7868193731Szec free(sah, M_IPSEC_SAH); 7869193731Szec } 7870193731Szec } 7871193731Szec SAHTREE_UNLOCK(); 7872193731Szec 7873193731Szec REGTREE_LOCK(); 7874193731Szec for (i = 0; i <= SADB_SATYPE_MAX; i++) { 7875193731Szec LIST_FOREACH(reg, &V_regtree[i], chain) { 7876193731Szec if (__LIST_CHAINED(reg)) { 7877193731Szec LIST_REMOVE(reg, chain); 7878193731Szec free(reg, M_IPSEC_SAR); 7879193731Szec break; 7880193731Szec } 7881193731Szec } 7882193731Szec } 7883193731Szec REGTREE_UNLOCK(); 7884193731Szec 7885193731Szec ACQ_LOCK(); 7886193731Szec for (acq = LIST_FIRST(&V_spacqtree); acq != NULL; acq = nextacq) { 7887193731Szec nextacq = LIST_NEXT(acq, chain); 7888193731Szec if (__LIST_CHAINED(acq)) { 7889193731Szec LIST_REMOVE(acq, chain); 7890193731Szec free(acq, M_IPSEC_SAQ); 7891193731Szec } 7892193731Szec } 7893193731Szec ACQ_UNLOCK(); 7894193731Szec 7895193731Szec SPACQ_LOCK(); 7896193731Szec for (acq = LIST_FIRST(&V_spacqtree); acq != NULL; acq = nextacq) { 7897193731Szec nextacq = LIST_NEXT(acq, chain); 7898193731Szec if (__LIST_CHAINED(acq)) { 7899193731Szec LIST_REMOVE(acq, chain); 7900193731Szec free(acq, M_IPSEC_SAQ); 7901193731Szec } 7902193731Szec } 7903193731Szec SPACQ_UNLOCK(); 7904105197Ssam} 7905193731Szec#endif 7906105197Ssam 7907105197Ssam/* 7908105197Ssam * XXX: maybe This function is called after INBOUND IPsec processing. 7909105197Ssam * 7910105197Ssam * Special check for tunnel-mode packets. 7911105197Ssam * We must make some checks for consistency between inner and outer IP header. 7912105197Ssam * 7913105197Ssam * xxx more checks to be provided 7914105197Ssam */ 7915105197Ssamint 7916105197Ssamkey_checktunnelsanity(sav, family, src, dst) 7917105197Ssam struct secasvar *sav; 7918105197Ssam u_int family; 7919105197Ssam caddr_t src; 7920105197Ssam caddr_t dst; 7921105197Ssam{ 7922120585Ssam IPSEC_ASSERT(sav->sah != NULL, ("null SA header")); 7923105197Ssam 7924105197Ssam /* XXX: check inner IP header */ 7925105197Ssam 7926105197Ssam return 1; 7927105197Ssam} 7928105197Ssam 7929105197Ssam/* record data transfer on SA, and update timestamps */ 7930105197Ssamvoid 7931105197Ssamkey_sa_recordxfer(sav, m) 7932105197Ssam struct secasvar *sav; 7933105197Ssam struct mbuf *m; 7934105197Ssam{ 7935120585Ssam IPSEC_ASSERT(sav != NULL, ("Null secasvar")); 7936120585Ssam IPSEC_ASSERT(m != NULL, ("Null mbuf")); 7937105197Ssam if (!sav->lft_c) 7938105197Ssam return; 7939105197Ssam 7940105197Ssam /* 7941105197Ssam * XXX Currently, there is a difference of bytes size 7942105197Ssam * between inbound and outbound processing. 7943105197Ssam */ 7944157123Sgnn sav->lft_c->bytes += m->m_pkthdr.len; 7945105197Ssam /* to check bytes lifetime is done in key_timehandler(). */ 7946105197Ssam 7947105197Ssam /* 7948105197Ssam * We use the number of packets as the unit of 7949157123Sgnn * allocations. We increment the variable 7950105197Ssam * whenever {esp,ah}_{in,out}put is called. 7951105197Ssam */ 7952157123Sgnn sav->lft_c->allocations++; 7953105197Ssam /* XXX check for expires? */ 7954105197Ssam 7955105197Ssam /* 7956157123Sgnn * NOTE: We record CURRENT usetime by using wall clock, 7957105197Ssam * in seconds. HARD and SOFT lifetime are measured by the time 7958157123Sgnn * difference (again in seconds) from usetime. 7959105197Ssam * 7960105197Ssam * usetime 7961105197Ssam * v expire expire 7962105197Ssam * -----+-----+--------+---> t 7963105197Ssam * <--------------> HARD 7964105197Ssam * <-----> SOFT 7965105197Ssam */ 7966157123Sgnn sav->lft_c->usetime = time_second; 7967105197Ssam /* XXX check for expires? */ 7968105197Ssam 7969105197Ssam return; 7970105197Ssam} 7971105197Ssam 7972105197Ssam/* dumb version */ 7973105197Ssamvoid 7974105197Ssamkey_sa_routechange(dst) 7975105197Ssam struct sockaddr *dst; 7976105197Ssam{ 7977183550Szec INIT_VNET_IPSEC(curvnet); 7978105197Ssam struct secashead *sah; 7979105197Ssam struct route *ro; 7980105197Ssam 7981120585Ssam SAHTREE_LOCK(); 7982181803Sbz LIST_FOREACH(sah, &V_sahtree, chain) { 7983105197Ssam ro = &sah->sa_route; 7984105197Ssam if (ro->ro_rt && dst->sa_len == ro->ro_dst.sa_len 7985105197Ssam && bcmp(dst, &ro->ro_dst, dst->sa_len) == 0) { 7986105197Ssam RTFREE(ro->ro_rt); 7987105197Ssam ro->ro_rt = (struct rtentry *)NULL; 7988105197Ssam } 7989105197Ssam } 7990120585Ssam SAHTREE_UNLOCK(); 7991105197Ssam} 7992105197Ssam 7993105197Ssamstatic void 7994189004Srdivackykey_sa_chgstate(struct secasvar *sav, u_int8_t state) 7995105197Ssam{ 7996120585Ssam IPSEC_ASSERT(sav != NULL, ("NULL sav")); 7997120585Ssam SAHTREE_LOCK_ASSERT(); 7998105197Ssam 7999119643Ssam if (sav->state != state) { 8000119643Ssam if (__LIST_CHAINED(sav)) 8001119643Ssam LIST_REMOVE(sav, chain); 8002119643Ssam sav->state = state; 8003119643Ssam LIST_INSERT_HEAD(&sav->sah->savtree[state], sav, chain); 8004119643Ssam } 8005105197Ssam} 8006105197Ssam 8007105197Ssamvoid 8008105197Ssamkey_sa_stir_iv(sav) 8009105197Ssam struct secasvar *sav; 8010105197Ssam{ 8011105197Ssam 8012120585Ssam IPSEC_ASSERT(sav->iv != NULL, ("null IV")); 8013105197Ssam key_randomfill(sav->iv, sav->ivlen); 8014105197Ssam} 8015105197Ssam 8016105197Ssam/* XXX too much? */ 8017105197Ssamstatic struct mbuf * 8018105197Ssamkey_alloc_mbuf(l) 8019105197Ssam int l; 8020105197Ssam{ 8021105197Ssam struct mbuf *m = NULL, *n; 8022105197Ssam int len, t; 8023105197Ssam 8024105197Ssam len = l; 8025105197Ssam while (len > 0) { 8026111119Simp MGET(n, M_DONTWAIT, MT_DATA); 8027105197Ssam if (n && len > MLEN) 8028111119Simp MCLGET(n, M_DONTWAIT); 8029105197Ssam if (!n) { 8030105197Ssam m_freem(m); 8031105197Ssam return NULL; 8032105197Ssam } 8033105197Ssam 8034105197Ssam n->m_next = NULL; 8035105197Ssam n->m_len = 0; 8036105197Ssam n->m_len = M_TRAILINGSPACE(n); 8037105197Ssam /* use the bottom of mbuf, hoping we can prepend afterwards */ 8038105197Ssam if (n->m_len > len) { 8039105197Ssam t = (n->m_len - len) & ~(sizeof(long) - 1); 8040105197Ssam n->m_data += t; 8041105197Ssam n->m_len = len; 8042105197Ssam } 8043105197Ssam 8044105197Ssam len -= n->m_len; 8045105197Ssam 8046105197Ssam if (m) 8047105197Ssam m_cat(m, n); 8048105197Ssam else 8049105197Ssam m = n; 8050105197Ssam } 8051105197Ssam 8052105197Ssam return m; 8053105197Ssam} 8054157123Sgnn 8055157123Sgnn/* 8056157123Sgnn * Take one of the kernel's security keys and convert it into a PF_KEY 8057157123Sgnn * structure within an mbuf, suitable for sending up to a waiting 8058157123Sgnn * application in user land. 8059157123Sgnn * 8060157123Sgnn * IN: 8061157123Sgnn * src: A pointer to a kernel security key. 8062157123Sgnn * exttype: Which type of key this is. Refer to the PF_KEY data structures. 8063157123Sgnn * OUT: 8064157123Sgnn * a valid mbuf or NULL indicating an error 8065157123Sgnn * 8066157123Sgnn */ 8067157123Sgnn 8068157123Sgnnstatic struct mbuf * 8069157123Sgnnkey_setkey(struct seckey *src, u_int16_t exttype) 8070157123Sgnn{ 8071157123Sgnn struct mbuf *m; 8072157123Sgnn struct sadb_key *p; 8073170799Sbz int len; 8074157123Sgnn 8075157123Sgnn if (src == NULL) 8076157123Sgnn return NULL; 8077157123Sgnn 8078170799Sbz len = PFKEY_ALIGN8(sizeof(struct sadb_key) + _KEYLEN(src)); 8079157123Sgnn m = key_alloc_mbuf(len); 8080157123Sgnn if (m == NULL) 8081157123Sgnn return NULL; 8082157123Sgnn p = mtod(m, struct sadb_key *); 8083157123Sgnn bzero(p, len); 8084157123Sgnn p->sadb_key_len = PFKEY_UNIT64(len); 8085157123Sgnn p->sadb_key_exttype = exttype; 8086157123Sgnn p->sadb_key_bits = src->bits; 8087157123Sgnn bcopy(src->key_data, _KEYBUF(p), _KEYLEN(src)); 8088157123Sgnn 8089157123Sgnn return m; 8090157123Sgnn} 8091157123Sgnn 8092157123Sgnn/* 8093157123Sgnn * Take one of the kernel's lifetime data structures and convert it 8094157123Sgnn * into a PF_KEY structure within an mbuf, suitable for sending up to 8095157123Sgnn * a waiting application in user land. 8096157123Sgnn * 8097157123Sgnn * IN: 8098157123Sgnn * src: A pointer to a kernel lifetime structure. 8099157123Sgnn * exttype: Which type of lifetime this is. Refer to the PF_KEY 8100157123Sgnn * data structures for more information. 8101157123Sgnn * OUT: 8102157123Sgnn * a valid mbuf or NULL indicating an error 8103157123Sgnn * 8104157123Sgnn */ 8105157123Sgnn 8106157123Sgnnstatic struct mbuf * 8107157123Sgnnkey_setlifetime(struct seclifetime *src, u_int16_t exttype) 8108157123Sgnn{ 8109157123Sgnn struct mbuf *m = NULL; 8110157123Sgnn struct sadb_lifetime *p; 8111157123Sgnn int len = PFKEY_ALIGN8(sizeof(struct sadb_lifetime)); 8112157123Sgnn 8113157123Sgnn if (src == NULL) 8114157123Sgnn return NULL; 8115157123Sgnn 8116157123Sgnn m = key_alloc_mbuf(len); 8117157123Sgnn if (m == NULL) 8118157123Sgnn return m; 8119157123Sgnn p = mtod(m, struct sadb_lifetime *); 8120157123Sgnn 8121157123Sgnn bzero(p, len); 8122157123Sgnn p->sadb_lifetime_len = PFKEY_UNIT64(len); 8123157123Sgnn p->sadb_lifetime_exttype = exttype; 8124157123Sgnn p->sadb_lifetime_allocations = src->allocations; 8125157123Sgnn p->sadb_lifetime_bytes = src->bytes; 8126157123Sgnn p->sadb_lifetime_addtime = src->addtime; 8127157123Sgnn p->sadb_lifetime_usetime = src->usetime; 8128157123Sgnn 8129157123Sgnn return m; 8130157123Sgnn 8131157123Sgnn} 8132