Print this page
Bayard's initial drop, needs finishing, or at least testing.
*** 19,28 ****
--- 19,29 ----
* CDDL HEADER END
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright (c) 2012 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/types.h>
#include <sys/stream.h>
#include <sys/stropts.h>
*** 70,90 ****
/*
* This source file contains Security Association Database (SADB) common
* routines. They are linked in with the AH module. Since AH has no chance
* of falling under export control, it was safe to link it in there.
*/
-
- static mblk_t *sadb_extended_acquire(ipsec_selector_t *, ipsec_policy_t *,
- ipsec_action_t *, boolean_t, uint32_t, uint32_t, sadb_sens_t *,
- netstack_t *);
static ipsa_t *sadb_torch_assoc(isaf_t *, ipsa_t *);
static void sadb_destroy_acqlist(iacqf_t **, uint_t, boolean_t,
netstack_t *);
static void sadb_destroy(sadb_t *, netstack_t *);
static mblk_t *sadb_sa2msg(ipsa_t *, sadb_msg_t *);
static ts_label_t *sadb_label_from_sens(sadb_sens_t *, uint64_t *);
static sadb_sens_t *sadb_make_sens_ext(ts_label_t *tsl, int *len);
static time_t sadb_add_time(time_t, uint64_t);
static void lifetime_fuzz(ipsa_t *);
static void age_pair_peer_list(templist_t *, sadb_t *, boolean_t);
static int get_ipsa_pair(ipsa_query_t *, ipsap_t *, int *);
--- 71,96 ----
/*
* This source file contains Security Association Database (SADB) common
* routines. They are linked in with the AH module. Since AH has no chance
* of falling under export control, it was safe to link it in there.
*/
static ipsa_t *sadb_torch_assoc(isaf_t *, ipsa_t *);
static void sadb_destroy_acqlist(iacqf_t **, uint_t, boolean_t,
netstack_t *);
static void sadb_destroy(sadb_t *, netstack_t *);
static mblk_t *sadb_sa2msg(ipsa_t *, sadb_msg_t *);
static ts_label_t *sadb_label_from_sens(sadb_sens_t *, uint64_t *);
static sadb_sens_t *sadb_make_sens_ext(ts_label_t *tsl, int *len);
+ /* Args named here, as the booleans can be hard to distinguish */
+ static mblk_t *sadb_construct_acqmsg(ipsacq_t *acqrec, ipsec_selector_t *sel,
+ ipsec_action_t *ap, ipsec_policy_t *pp, netstack_t *ns, sadb_sens_t *sens,
+ boolean_t need_esp, boolean_t tunnel_mode, boolean_t extended,
+ boolean_t with_prop);
+ static uint8_t *sadb_construct_eprop(const ipsec_action_t *,
+ const ipsec_policy_t *, netstack_t *, const uint8_t *, const uint8_t *);
+ static void sadb_insert_prop(sadb_prop_t *, const ipsec_action_t *,
+ netstack_t *, uint_t, boolean_t);
static time_t sadb_add_time(time_t, uint64_t);
static void lifetime_fuzz(ipsa_t *);
static void age_pair_peer_list(templist_t *, sadb_t *, boolean_t);
static int get_ipsa_pair(ipsa_query_t *, ipsap_t *, int *);
*** 97,106 ****
--- 103,119 ----
* ipsacq_maxpackets is defined here to make it tunable
* from /etc/system.
*/
extern uint64_t ipsacq_maxpackets;
+ /*
+ * Allocation size for sin_t/sin6_t in address extensions. We allocate IPv6
+ * because it's the larger of the two, and we roundup because the type isn't
+ * defined to guarantee 64-bit alignment.
+ */
+ #define SADB_SOCKADDR_SIZE (roundup(sizeof (sin6_t), sizeof (uint64_t)))
+
#define SET_EXPIRE(sa, delta, exp) { \
if (((sa)->ipsa_ ## delta) != 0) { \
(sa)->ipsa_ ## exp = sadb_add_time((sa)->ipsa_addtime, \
(sa)->ipsa_ ## delta); \
} \
*** 116,126 ****
--- 129,166 ----
(sa)->ipsa_ ## exp = \
MIN((sa)->ipsa_ ## exp, tmp); \
} \
}
+ /* Warning: watch for evaluation issues with complex args */
+ #define INITIALIZE_SAMSG(samsg, type) \
+ (samsg)->sadb_msg_version = PF_KEY_V2, \
+ (samsg)->sadb_msg_type = (type), \
+ (samsg)->sadb_msg_errno = 0, \
+ (samsg)->sadb_msg_reserved = 0
+ /* Warning: watch for evaluation issues with complex args */
+ #define ERRNO_SAMSG(samsg, errno) \
+ (samsg)->sadb_msg_len = SADB_8TO64(sizeof (*samsg)), \
+ (samsg)->sadb_msg_errno = (errno), \
+ (samsg)->sadb_x_msg_diagnostic = 0
+
+ /*
+ * Warning: watch for evaluation issues with complex args. This is a rough,
+ * conservative calculation (e.g. combined mode encr algs can perform both
+ * encr/auth and ipsecconf drops auth algs in combinations). This is
+ * nevertheless reasonable, given that the kernel doesn't make or guarantee
+ * optimizations reducing the combination space.
+ */
+ #define CALC_COMBS(limit, ipss, need_esp) { \
+ limit = (need_esp) ? \
+ (ipss)->ipsec_nalgs[IPSEC_ALG_AUTH] * \
+ (ipss)->ipsec_nalgs[IPSEC_ALG_ENCR] \
+ : (ipss)->ipsec_nalgs[IPSEC_ALG_AUTH]; \
+ ASSERT((limit) > 0); \
+ }
+
/* wrap the macro so we can pass it as a function pointer */
void
sadb_sa_refrele(void *target)
{
IPSA_REFRELE(((ipsa_t *)target));
*** 973,1056 ****
return (newbie);
}
/*
! * Initialize a SADB address extension at the address specified by addrext.
! * Return a pointer to the end of the new address extension.
*/
static uint8_t *
! sadb_make_addr_ext(uint8_t *start, uint8_t *end, uint16_t exttype,
sa_family_t af, uint32_t *addr, uint16_t port, uint8_t proto, int prefix)
{
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
! uint8_t *cur = start;
int addrext_len;
- int sin_len;
sadb_address_t *addrext = (sadb_address_t *)cur;
! if (cur == NULL)
! return (NULL);
cur += sizeof (*addrext);
if (cur > end)
return (NULL);
addrext->sadb_address_proto = proto;
addrext->sadb_address_prefixlen = prefix;
addrext->sadb_address_reserved = 0;
addrext->sadb_address_exttype = exttype;
switch (af) {
case AF_INET:
- sin = (struct sockaddr_in *)cur;
- sin_len = sizeof (*sin);
- cur += sin_len;
- if (cur > end)
- return (NULL);
-
sin->sin_family = af;
bzero(sin->sin_zero, sizeof (sin->sin_zero));
sin->sin_port = port;
IPSA_COPY_ADDR(&sin->sin_addr, addr, af);
break;
case AF_INET6:
- sin6 = (struct sockaddr_in6 *)cur;
- sin_len = sizeof (*sin6);
- cur += sin_len;
- if (cur > end)
- return (NULL);
-
bzero(sin6, sizeof (*sin6));
sin6->sin6_family = af;
sin6->sin6_port = port;
IPSA_COPY_ADDR(&sin6->sin6_addr, addr, af);
break;
}
! addrext_len = roundup(cur - start, sizeof (uint64_t));
! addrext->sadb_address_len = SADB_8TO64(addrext_len);
! cur = start + addrext_len;
! if (cur > end)
! cur = NULL;
return (cur);
}
/*
! * Construct a key management cookie extension.
*/
-
static uint8_t *
! sadb_make_kmc_ext(uint8_t *cur, uint8_t *end, uint32_t kmp, uint32_t kmc)
{
sadb_x_kmc_t *kmcext = (sadb_x_kmc_t *)cur;
! if (cur == NULL)
! return (NULL);
cur += sizeof (*kmcext);
if (cur > end)
return (NULL);
--- 1013,1213 ----
return (newbie);
}
/*
! * Takes two uint8_t (bounds on buffer in which to construct extension) and an
! * addr (address to write into extension) pointer, a uint16_t (type of address
! * in extension), and af, port, proto, and prefix values (further extension
! * content). Returns a byte-aligned pointer to the end of the extension, which
! * is of variable length depending on the address family.
*/
static uint8_t *
! sadb_make_addr_ext(const uint8_t *start, const uint8_t *end, uint16_t exttype,
sa_family_t af, uint32_t *addr, uint16_t port, uint8_t proto, int prefix)
{
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
! uint8_t *cur = (uint8_t *)start;
int addrext_len;
sadb_address_t *addrext = (sadb_address_t *)cur;
! ASSERT(cur != NULL && end != NULL);
cur += sizeof (*addrext);
+ sin = (struct sockaddr_in *)cur;
+ sin6 = (struct sockaddr_in6 *)cur;
+ cur += (af == AF_INET) ? sizeof (*sin) : sizeof (*sin6);
+
+ addrext_len = roundup(cur - start, sizeof (uint64_t));
+ cur = (uint8_t *)start + addrext_len;
+
if (cur > end)
return (NULL);
addrext->sadb_address_proto = proto;
addrext->sadb_address_prefixlen = prefix;
addrext->sadb_address_reserved = 0;
addrext->sadb_address_exttype = exttype;
+ addrext->sadb_address_len = SADB_8TO64(addrext_len);
switch (af) {
case AF_INET:
sin->sin_family = af;
bzero(sin->sin_zero, sizeof (sin->sin_zero));
sin->sin_port = port;
IPSA_COPY_ADDR(&sin->sin_addr, addr, af);
break;
case AF_INET6:
bzero(sin6, sizeof (*sin6));
sin6->sin6_family = af;
sin6->sin6_port = port;
IPSA_COPY_ADDR(&sin6->sin6_addr, addr, af);
break;
}
! return (cur);
! }
! /*
! * Takes ipsec_selector_t (address information used in forming addr
! * extensions) and ipsec_policy_t (contains pointer to selector key used in
! * tunnel mode) pointers, tunnel mode boolean, and creates address extensions
! * inside message contents bounds checked by byte-aligned start and end
! * pointers. Returns new value for cur pointer or NULL on failure.
! * XXX TODO: Original packet contents go here.
! */
! static uint8_t *
! sadb_sel_to_addrexts(const ipsec_selector_t *sel, const ipsec_policy_t *pp,
! const ipsec_action_t *ap, const uint8_t *start, const uint8_t *end,
! boolean_t tunnel_mode)
! {
! uint8_t proto, pfxlen, *cur = (uint8_t *)start;
! ipsec_selkey_t *ipsl;
! sa_family_t af;
! uint16_t lport, rport;
! uint32_t *saddrptr, *daddrptr;
+ if (tunnel_mode) {
+ /*
+ * Form inner address extensions based NOT on the inner
+ * selectors (i.e. the packet data), but on the policy's
+ * selector key (i.e. the policy's selector information).
+ *
+ * NOTE: The position of IPv4 and IPv6 addresses is the
+ * same in ipsec_selkey_t (unless the compiler does very
+ * strange things with unions, consult your local C language
+ * lawyer for details).
+ */
+ ASSERT(pp != NULL);
+
+ ipsl = &(pp->ipsp_sel->ipsl_key);
+ if (ipsl->ipsl_valid & IPSL_IPV4) {
+ af = AF_INET;
+ ASSERT(sel->ips_protocol == IPPROTO_ENCAP);
+ ASSERT(!(ipsl->ipsl_valid & IPSL_IPV6));
+ } else {
+ af = AF_INET6;
+ ASSERT(sel->ips_protocol == IPPROTO_IPV6);
+ ASSERT(ipsl->ipsl_valid & IPSL_IPV6);
+ }
+
+ if (ipsl->ipsl_valid & IPSL_LOCAL_ADDR) {
+ saddrptr = (uint32_t *)(&ipsl->ipsl_local);
+ pfxlen = ipsl->ipsl_local_pfxlen;
+ } else {
+ saddrptr = (uint32_t *)(&ipv6_all_zeros);
+ pfxlen = 0;
+ }
+ /* XXX What about ICMP type/code? */
+ lport = (ipsl->ipsl_valid & IPSL_LOCAL_PORT) ?
+ ipsl->ipsl_lport : 0;
+ proto = (ipsl->ipsl_valid & IPSL_PROTOCOL) ?
+ ipsl->ipsl_proto : 0;
+
+ cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_SRC,
+ af, saddrptr, lport, proto, pfxlen);
+ if (cur == NULL)
+ goto done;
+
+ if (ipsl->ipsl_valid & IPSL_REMOTE_ADDR) {
+ daddrptr = (uint32_t *)(&ipsl->ipsl_remote);
+ pfxlen = ipsl->ipsl_remote_pfxlen;
+ } else {
+ daddrptr = (uint32_t *)(&ipv6_all_zeros);
+ pfxlen = 0;
+ }
+ /* XXX What about ICMP type/code? */
+ rport = (ipsl->ipsl_valid & IPSL_REMOTE_PORT) ?
+ ipsl->ipsl_rport : 0;
+
+ cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_DST,
+ af, daddrptr, rport, proto, pfxlen);
+ if (cur == NULL)
+ goto done;
+
+ /*
+ * TODO - if we go to RFC 3408's dream of transport mode
+ * IP-in-IP _with_ inner-packet address selectors, we'll need
+ * to further distinguish tunnel mode here. For now, having
+ * inner addresses and/or ports is sufficient.
+ *
+ * Meanwhile, whack proto/ports to reflect IP-in-IP for the
+ * outer addresses.
+ */
+ proto = sel->ips_protocol; /* Either _ENCAP or _IPV6 */
+ lport = rport = 0;
+ } else if ((ap != NULL) && (!ap->ipa_want_unique)) {
+ /* Not in tunnel mode, action doesn't want pop from pkt */
+ proto = 0;
+ lport = 0;
+ rport = 0;
+ if (pp != NULL) {
+ ipsl = &(pp->ipsp_sel->ipsl_key);
+ if (ipsl->ipsl_valid & IPSL_PROTOCOL)
+ proto = ipsl->ipsl_proto;
+ if (ipsl->ipsl_valid & IPSL_REMOTE_PORT)
+ rport = ipsl->ipsl_rport;
+ if (ipsl->ipsl_valid & IPSL_LOCAL_PORT)
+ lport = ipsl->ipsl_lport;
+ }
+ } else {
+ /* Not in tunnel mode, action wants pop from pkt */
+ proto = sel->ips_protocol;
+ lport = sel->ips_local_port;
+ rport = sel->ips_remote_port;
+ }
+
+ af = sel->ips_isv4 ? AF_INET : AF_INET6;
+
+ /*
+ * NOTE: The position of IPv4 and IPv6 addresses is the same
+ * in ipsec_selector_t.
+ */
+ cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_SRC, af,
+ (uint32_t *)(&sel->ips_local_addr_v6), lport, proto, 0);
+ if (cur == NULL)
+ goto done;
+
+ cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_DST, af,
+ (uint32_t *)(&sel->ips_remote_addr_v6), rport, proto, 0);
+ done:
return (cur);
}
/*
! * Use byte aligned buffer defined by cur and end pointers to create a key
! * management extension using kmc and kmp uint32_t parameters.
*/
static uint8_t *
! sadb_make_kmc_ext(const uint8_t *start, const uint8_t *end,
! uint32_t kmp, uint32_t kmc)
{
+ uint8_t *cur = (uint8_t *)start;
sadb_x_kmc_t *kmcext = (sadb_x_kmc_t *)cur;
! ASSERT(cur != NULL && end != NULL);
cur += sizeof (*kmcext);
if (cur > end)
return (NULL);
*** 3196,3206 ****
*/
newbie->ipsa_kcfauthkey.ck_format = CRYPTO_KEY_RAW;
newbie->ipsa_kcfauthkey.ck_length = newbie->ipsa_authkeybits;
newbie->ipsa_kcfauthkey.ck_data = newbie->ipsa_authkey;
! mutex_enter(&ipss->ipsec_alg_lock);
alg = ipss->ipsec_alglists[IPSEC_ALG_AUTH]
[newbie->ipsa_auth_alg];
if (alg != NULL && ALG_VALID(alg)) {
newbie->ipsa_amech.cm_type = alg->alg_mech_type;
newbie->ipsa_amech.cm_param =
--- 3353,3363 ----
*/
newbie->ipsa_kcfauthkey.ck_format = CRYPTO_KEY_RAW;
newbie->ipsa_kcfauthkey.ck_length = newbie->ipsa_authkeybits;
newbie->ipsa_kcfauthkey.ck_data = newbie->ipsa_authkey;
! rw_enter(&ipss->ipsec_alg_lock, RW_READER);
alg = ipss->ipsec_alglists[IPSEC_ALG_AUTH]
[newbie->ipsa_auth_alg];
if (alg != NULL && ALG_VALID(alg)) {
newbie->ipsa_amech.cm_type = alg->alg_mech_type;
newbie->ipsa_amech.cm_param =
*** 3209,3219 ****
newbie->ipsa_mac_len = (size_t)alg->alg_datalen;
} else {
newbie->ipsa_amech.cm_type = CRYPTO_MECHANISM_INVALID;
}
error = ipsec_create_ctx_tmpl(newbie, IPSEC_ALG_AUTH);
! mutex_exit(&ipss->ipsec_alg_lock);
if (error != 0) {
mutex_exit(&newbie->ipsa_lock);
/*
* An error here indicates that alg is the wrong type
* (IE: not authentication) or its not in the alg tables
--- 3366,3376 ----
newbie->ipsa_mac_len = (size_t)alg->alg_datalen;
} else {
newbie->ipsa_amech.cm_type = CRYPTO_MECHANISM_INVALID;
}
error = ipsec_create_ctx_tmpl(newbie, IPSEC_ALG_AUTH);
! rw_exit(&ipss->ipsec_alg_lock);
if (error != 0) {
mutex_exit(&newbie->ipsa_lock);
/*
* An error here indicates that alg is the wrong type
* (IE: not authentication) or its not in the alg tables
*** 3226,3236 ****
goto error;
}
}
if (ekey != NULL) {
! mutex_enter(&ipss->ipsec_alg_lock);
async = async || (ipss->ipsec_algs_exec_mode[IPSEC_ALG_ENCR] ==
IPSEC_ALGS_EXEC_ASYNC);
alg = ipss->ipsec_alglists[IPSEC_ALG_ENCR]
[newbie->ipsa_encr_alg];
--- 3383,3393 ----
goto error;
}
}
if (ekey != NULL) {
! rw_enter(&ipss->ipsec_alg_lock, RW_READER);
async = async || (ipss->ipsec_algs_exec_mode[IPSEC_ALG_ENCR] ==
IPSEC_ALGS_EXEC_ASYNC);
alg = ipss->ipsec_alglists[IPSEC_ALG_ENCR]
[newbie->ipsa_encr_alg];
*** 3259,3269 ****
newbie->ipsa_emech.cm_param = NULL;
newbie->ipsa_emech.cm_param_len = 0;
} else {
newbie->ipsa_emech.cm_type = CRYPTO_MECHANISM_INVALID;
}
! mutex_exit(&ipss->ipsec_alg_lock);
/*
* The byte stream following the sadb_key_t is made up of:
* key bytes, [salt bytes], [IV initial value]
* All of these have variable length. The IV is typically
--- 3416,3426 ----
newbie->ipsa_emech.cm_param = NULL;
newbie->ipsa_emech.cm_param_len = 0;
} else {
newbie->ipsa_emech.cm_type = CRYPTO_MECHANISM_INVALID;
}
! rw_exit(&ipss->ipsec_alg_lock);
/*
* The byte stream following the sadb_key_t is made up of:
* key bytes, [salt bytes], [IV initial value]
* All of these have variable length. The IV is typically
*** 3371,3383 ****
*/
newbie->ipsa_kcfencrkey.ck_format = CRYPTO_KEY_RAW;
newbie->ipsa_kcfencrkey.ck_length = newbie->ipsa_encrkeybits;
newbie->ipsa_kcfencrkey.ck_data = newbie->ipsa_encrkey;
! mutex_enter(&ipss->ipsec_alg_lock);
error = ipsec_create_ctx_tmpl(newbie, IPSEC_ALG_ENCR);
! mutex_exit(&ipss->ipsec_alg_lock);
if (error != 0) {
mutex_exit(&newbie->ipsa_lock);
/* See above for error explanation. */
*diagnostic = SADB_X_DIAGNOSTIC_BAD_CTX;
goto error;
--- 3528,3540 ----
*/
newbie->ipsa_kcfencrkey.ck_format = CRYPTO_KEY_RAW;
newbie->ipsa_kcfencrkey.ck_length = newbie->ipsa_encrkeybits;
newbie->ipsa_kcfencrkey.ck_data = newbie->ipsa_encrkey;
! rw_enter(&ipss->ipsec_alg_lock, RW_READER);
error = ipsec_create_ctx_tmpl(newbie, IPSEC_ALG_ENCR);
! rw_exit(&ipss->ipsec_alg_lock);
if (error != 0) {
mutex_exit(&newbie->ipsa_lock);
/* See above for error explanation. */
*diagnostic = SADB_X_DIAGNOSTIC_BAD_CTX;
goto error;
*** 3797,3812 ****
mp = mp->b_cont;
end = mp->b_wptr + alloclen;
samsg = (sadb_msg_t *)mp->b_wptr;
mp->b_wptr += sizeof (*samsg);
! samsg->sadb_msg_version = PF_KEY_V2;
! samsg->sadb_msg_type = SADB_EXPIRE;
! samsg->sadb_msg_errno = 0;
samsg->sadb_msg_satype = assoc->ipsa_type;
samsg->sadb_msg_len = SADB_8TO64(alloclen);
- samsg->sadb_msg_reserved = 0;
samsg->sadb_msg_seq = 0;
samsg->sadb_msg_pid = 0;
saext = (sadb_sa_t *)mp->b_wptr;
mp->b_wptr += sizeof (*saext);
--- 3954,3966 ----
mp = mp->b_cont;
end = mp->b_wptr + alloclen;
samsg = (sadb_msg_t *)mp->b_wptr;
mp->b_wptr += sizeof (*samsg);
! INITIALIZE_SAMSG(samsg, SADB_EXPIRE);
samsg->sadb_msg_satype = assoc->ipsa_type;
samsg->sadb_msg_len = SADB_8TO64(alloclen);
samsg->sadb_msg_seq = 0;
samsg->sadb_msg_pid = 0;
saext = (sadb_sa_t *)mp->b_wptr;
mp->b_wptr += sizeof (*saext);
*** 4094,4104 ****
return (retval);
}
/*
* Called by a consumer protocol to do ther dirty work of reaping dead
! * Security Associations.
*
* NOTE: sadb_age_assoc() marks expired SA's as DEAD but only removed
* SA's that are already marked DEAD, so expired SA's are only reaped
* the second time sadb_ager() runs.
*/
--- 4248,4258 ----
return (retval);
}
/*
* Called by a consumer protocol to do ther dirty work of reaping dead
! * Security Associations and outstanding acquire records.
*
* NOTE: sadb_age_assoc() marks expired SA's as DEAD but only removed
* SA's that are already marked DEAD, so expired SA's are only reaped
* the second time sadb_ager() runs.
*/
*** 4808,4817 ****
--- 4962,4973 ----
{
ipsacq_t *walker;
sa_family_t fam;
uint32_t blank_address[4] = {0, 0, 0, 0};
+ ASSERT(MUTEX_HELD(&bucket->iacqf_lock));
+
if (isrc == NULL) {
ASSERT(idst == NULL);
isrc = idst = blank_address;
}
*** 4841,4908 ****
return (walker);
}
/*
! * For this mblk, insert a new acquire record. Assume bucket contains addrs
! * of all of the same length. Give up (and drop) if memory
! * cannot be allocated for a new one; otherwise, invoke callback to
! * send the acquire up..
*
! * In cases where we need both AH and ESP, add the SA to the ESP ACQUIRE
! * list. The ah_add_sa_finish() routines can look at the packet's attached
! * attributes and handle this case specially.
*/
void
sadb_acquire(mblk_t *datamp, ip_xmit_attr_t *ixa, boolean_t need_ah,
boolean_t need_esp)
{
! mblk_t *asyncmp;
sadbp_t *spp;
sadb_t *sp;
ipsacq_t *newbie;
iacqf_t *bucket;
- mblk_t *extended;
ipha_t *ipha = (ipha_t *)datamp->b_rptr;
ip6_t *ip6h = (ip6_t *)datamp->b_rptr;
! uint32_t *src, *dst, *isrc, *idst;
ipsec_policy_t *pp = ixa->ixa_ipsec_policy;
ipsec_action_t *ap = ixa->ixa_ipsec_action;
sa_family_t af;
! int hashoffset;
! uint32_t seq;
uint64_t unique_id = 0;
ipsec_selector_t sel;
boolean_t tunnel_mode = (ixa->ixa_flags & IXAF_IPSEC_TUNNEL) != 0;
ts_label_t *tsl = NULL;
netstack_t *ns = ixa->ixa_ipst->ips_netstack;
ipsec_stack_t *ipss = ns->netstack_ipsec;
sadb_sens_t *sens = NULL;
- int sens_len;
-
- ASSERT((pp != NULL) || (ap != NULL));
-
- ASSERT(need_ah != NULL || need_esp != NULL);
-
- /* Assign sadb pointers */
- if (need_esp) { /* ESP for AH+ESP */
ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
-
- spp = &espstack->esp_sadb;
- } else {
ipsecah_stack_t *ahstack = ns->netstack_ipsecah;
! spp = &ahstack->ah_sadb;
! }
sp = (ixa->ixa_flags & IXAF_IS_IPV4) ? &spp->s_v4 : &spp->s_v6;
if (is_system_labeled())
tsl = ixa->ixa_tsl;
if (ap == NULL)
ap = pp->ipsp_act;
-
ASSERT(ap != NULL);
if (ap->ipa_act.ipa_apply.ipp_use_unique || tunnel_mode)
unique_id = SA_FORM_UNIQUE_ID(ixa);
--- 4997,5068 ----
return (walker);
}
/*
! * Take a pointers to mblk_t (packet for which we need to acquire an SA) and
! * ip_xmit_attr_t (transmit attributes used to generate or retrieve acquire
! * record) and two booleans need_ah and need_esp, one but not both of which
! * must be true. Acquire records are stored in hash buckets, and we assume
! * bucket contains addrs of all of the same length. If this is a new acquire
! * record, we generate an acquire samsg to send to protocol keysock layer,
! * which assumes ownership from there. If we run into problems along the way,
! * we generate errors if possible and drop packets if need be. Before sending
! * to keysock, we simply unlock the acquire record and let the ager deal with
! * releasing locks and freeing resources.
*
! * This code is called by the IP stack when trying to send a packet for which
! * all necessary SAs can't be found to include in ip_xmit_attr_t. Be aware of
! * the following case: you need both ESP and AH and have SAs for neither. In
! * that case both need_esp and need_ah are true, but we go with need_esp, as
! * ESP will call us back for an AH acquire if it's successful and the AH SA
! * still missing. It can also be that the packet needs both, but an SA already
! * exists for one, in which case only the missing one will be flagged as
! * needed, although the ipsec_action_t has want flags for both.
*/
void
sadb_acquire(mblk_t *datamp, ip_xmit_attr_t *ixa, boolean_t need_ah,
boolean_t need_esp)
{
! mblk_t *asyncmp, *regular, *extended, *prop_m, *eprop_m;
sadbp_t *spp;
sadb_t *sp;
ipsacq_t *newbie;
iacqf_t *bucket;
ipha_t *ipha = (ipha_t *)datamp->b_rptr;
ip6_t *ip6h = (ip6_t *)datamp->b_rptr;
! uint32_t seq, *src, *dst, *isrc, *idst;
ipsec_policy_t *pp = ixa->ixa_ipsec_policy;
ipsec_action_t *ap = ixa->ixa_ipsec_action;
sa_family_t af;
! int hashoffset, sens_len;
uint64_t unique_id = 0;
+ uint_t propsize, epropsize, combs_limit;
+ uint8_t *start, *end;
+ sadb_msg_t *samsg;
+ sadb_prop_t *prop, *eprop;
ipsec_selector_t sel;
boolean_t tunnel_mode = (ixa->ixa_flags & IXAF_IPSEC_TUNNEL) != 0;
ts_label_t *tsl = NULL;
netstack_t *ns = ixa->ixa_ipst->ips_netstack;
ipsec_stack_t *ipss = ns->netstack_ipsec;
sadb_sens_t *sens = NULL;
ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
ipsecah_stack_t *ahstack = ns->netstack_ipsecah;
+ queue_t *q;
! ASSERT(need_ah || need_esp);
! ASSERT((ap != NULL) || (pp != NULL));
!
! spp = need_esp ? &espstack->esp_sadb : &ahstack->ah_sadb;
sp = (ixa->ixa_flags & IXAF_IS_IPV4) ? &spp->s_v4 : &spp->s_v6;
if (is_system_labeled())
tsl = ixa->ixa_tsl;
if (ap == NULL)
ap = pp->ipsp_act;
ASSERT(ap != NULL);
if (ap->ipa_act.ipa_apply.ipp_use_unique || tunnel_mode)
unique_id = SA_FORM_UNIQUE_ID(ixa);
*** 4911,4934 ****
*
* Immediately, make sure the ACQUIRE sequence number doesn't slip
* below the lowest point allowed in the kernel. (In other words,
* make sure the high bit on the sequence number is set.)
*/
-
seq = keysock_next_seq(ns) | IACQF_LOWEST_SEQ;
if (IPH_HDR_VERSION(ipha) == IP_VERSION) {
src = (uint32_t *)&ipha->ipha_src;
dst = (uint32_t *)&ipha->ipha_dst;
af = AF_INET;
hashoffset = OUTBOUND_HASH_V4(sp, ipha->ipha_dst);
ASSERT(ixa->ixa_flags & IXAF_IS_IPV4);
} else {
ASSERT(IPH_HDR_VERSION(ipha) == IPV6_VERSION);
src = (uint32_t *)&ip6h->ip6_src;
dst = (uint32_t *)&ip6h->ip6_dst;
af = AF_INET6;
hashoffset = OUTBOUND_HASH_V6(sp, ip6h->ip6_dst);
ASSERT(!(ixa->ixa_flags & IXAF_IS_IPV4));
}
if (tunnel_mode) {
--- 5071,5095 ----
*
* Immediately, make sure the ACQUIRE sequence number doesn't slip
* below the lowest point allowed in the kernel. (In other words,
* make sure the high bit on the sequence number is set.)
*/
seq = keysock_next_seq(ns) | IACQF_LOWEST_SEQ;
if (IPH_HDR_VERSION(ipha) == IP_VERSION) {
src = (uint32_t *)&ipha->ipha_src;
dst = (uint32_t *)&ipha->ipha_dst;
af = AF_INET;
+ ip6h = NULL;
hashoffset = OUTBOUND_HASH_V4(sp, ipha->ipha_dst);
ASSERT(ixa->ixa_flags & IXAF_IS_IPV4);
} else {
ASSERT(IPH_HDR_VERSION(ipha) == IPV6_VERSION);
src = (uint32_t *)&ip6h->ip6_src;
dst = (uint32_t *)&ip6h->ip6_dst;
af = AF_INET6;
+ ipha = NULL;
hashoffset = OUTBOUND_HASH_V6(sp, ip6h->ip6_dst);
ASSERT(!(ixa->ixa_flags & IXAF_IS_IPV4));
}
if (tunnel_mode) {
*** 4950,4971 ****
} else {
isrc = idst = NULL;
}
/*
! * Check buckets to see if there is an existing entry. If so,
! * grab it. sadb_checkacquire locks newbie if found.
*/
bucket = &(sp->sdb_acq[hashoffset]);
mutex_enter(&bucket->iacqf_lock);
newbie = sadb_checkacquire(bucket, ap, pp, src, dst, isrc, idst,
unique_id, tsl);
if (newbie == NULL) {
- /*
- * Otherwise, allocate a new one.
- */
newbie = kmem_zalloc(sizeof (*newbie), KM_NOSLEEP);
if (newbie == NULL) {
mutex_exit(&bucket->iacqf_lock);
ip_drop_packet(datamp, B_FALSE, NULL,
DROPPER(ipss, ipds_sadb_acquire_nomem),
--- 5111,5130 ----
} else {
isrc = idst = NULL;
}
/*
! * Check bucket for existing matching entry. If so, grab it. On match
! * sadb_checkacquire returns locked newbie.
*/
bucket = &(sp->sdb_acq[hashoffset]);
mutex_enter(&bucket->iacqf_lock);
newbie = sadb_checkacquire(bucket, ap, pp, src, dst, isrc, idst,
unique_id, tsl);
+ /* If not found, initialize a new one and insert into chain. */
if (newbie == NULL) {
newbie = kmem_zalloc(sizeof (*newbie), KM_NOSLEEP);
if (newbie == NULL) {
mutex_exit(&bucket->iacqf_lock);
ip_drop_packet(datamp, B_FALSE, NULL,
DROPPER(ipss, ipds_sadb_acquire_nomem),
*** 4998,5046 ****
* disperse blame for lock contention.
*
* we might be able to dispense with acquire record locks entirely..
* just use the bucket locks..
*/
-
mutex_exit(&bucket->iacqf_lock);
/*
* This assert looks silly for now, but we may need to enter newbie's
! * mutex during a search.
*/
ASSERT(MUTEX_HELD(&newbie->ipsacq_lock));
! /*
! * Make the ip_xmit_attr_t into something we can queue.
! * If no memory it frees datamp.
! */
asyncmp = ip_xmit_attr_to_mblk(ixa);
if (asyncmp != NULL)
linkb(asyncmp, datamp);
! /* Queue up packet. Use b_next. */
!
if (asyncmp == NULL) {
- /* Statistics for allocation failure */
if (ixa->ixa_flags & IXAF_IS_IPV4) {
BUMP_MIB(&ixa->ixa_ipst->ips_ip_mib,
ipIfStatsOutDiscards);
} else {
BUMP_MIB(&ixa->ixa_ipst->ips_ip6_mib,
ipIfStatsOutDiscards);
}
ip_drop_output("No memory for asyncmp", datamp, NULL);
freemsg(datamp);
! } else if (newbie->ipsacq_numpackets == 0) {
! /* First one. */
newbie->ipsacq_mp = asyncmp;
newbie->ipsacq_numpackets = 1;
newbie->ipsacq_expire = gethrestime_sec();
! /*
! * Extended ACQUIRE with both AH+ESP will use ESP's timeout
! * value.
! */
newbie->ipsacq_expire += *spp->s_acquire_timeout;
newbie->ipsacq_seq = seq;
newbie->ipsacq_addrfam = af;
newbie->ipsacq_srcport = ixa->ixa_ipsec_src_port;
--- 5157,5196 ----
* disperse blame for lock contention.
*
* we might be able to dispense with acquire record locks entirely..
* just use the bucket locks..
*/
mutex_exit(&bucket->iacqf_lock);
/*
* This assert looks silly for now, but we may need to enter newbie's
! * mutex during a search. Confirms we got locked newbie from
! * sadb_checkacquire.
*/
ASSERT(MUTEX_HELD(&newbie->ipsacq_lock));
! /* Make ip_xmit_attr_t into message we can queue, link packet data. */
asyncmp = ip_xmit_attr_to_mblk(ixa);
if (asyncmp != NULL)
linkb(asyncmp, datamp);
! /* Bump appropriate discard stat & free datamp if allocation failed. */
if (asyncmp == NULL) {
if (ixa->ixa_flags & IXAF_IS_IPV4) {
BUMP_MIB(&ixa->ixa_ipst->ips_ip_mib,
ipIfStatsOutDiscards);
} else {
BUMP_MIB(&ixa->ixa_ipst->ips_ip6_mib,
ipIfStatsOutDiscards);
}
ip_drop_output("No memory for asyncmp", datamp, NULL);
freemsg(datamp);
! } else if (newbie->ipsacq_numpackets == 0) { /* Pkt queue forms here. */
newbie->ipsacq_mp = asyncmp;
newbie->ipsacq_numpackets = 1;
newbie->ipsacq_expire = gethrestime_sec();
! /* Extended ACQUIRE with AH+ESP uses ESP's timeout */
newbie->ipsacq_expire += *spp->s_acquire_timeout;
newbie->ipsacq_seq = seq;
newbie->ipsacq_addrfam = af;
newbie->ipsacq_srcport = ixa->ixa_ipsec_src_port;
*** 5060,5092 ****
} else {
newbie->ipsacq_proto = ixa->ixa_ipsec_proto;
}
newbie->ipsacq_unique_id = unique_id;
! if (ixa->ixa_tsl != NULL) {
! label_hold(ixa->ixa_tsl);
! newbie->ipsacq_tsl = ixa->ixa_tsl;
}
! } else {
! /* Scan to the end of the list & insert. */
mblk_t *lastone = newbie->ipsacq_mp;
while (lastone->b_next != NULL)
lastone = lastone->b_next;
lastone->b_next = asyncmp;
if (newbie->ipsacq_numpackets++ == ipsacq_maxpackets) {
newbie->ipsacq_numpackets = ipsacq_maxpackets;
lastone = newbie->ipsacq_mp;
newbie->ipsacq_mp = lastone->b_next;
lastone->b_next = NULL;
- /* Freeing the async message */
lastone = ip_xmit_attr_free_mblk(lastone);
ip_drop_packet(lastone, B_FALSE, NULL,
DROPPER(ipss, ipds_sadb_acquire_toofull),
&ipss->ipsec_sadb_dropper);
! } else {
IP_ACQUIRE_STAT(ipss, qhiwater,
newbie->ipsacq_numpackets);
}
}
--- 5210,5241 ----
} else {
newbie->ipsacq_proto = ixa->ixa_ipsec_proto;
}
newbie->ipsacq_unique_id = unique_id;
! if (tsl != NULL) {
! label_hold(tsl);
! newbie->ipsacq_tsl = tsl;
}
! } else { /* Attempt to join packet queue as b_next. */
mblk_t *lastone = newbie->ipsacq_mp;
while (lastone->b_next != NULL)
lastone = lastone->b_next;
lastone->b_next = asyncmp;
+ /* Queue maxed: set counter to max, unchain, free & drop pkt */
if (newbie->ipsacq_numpackets++ == ipsacq_maxpackets) {
newbie->ipsacq_numpackets = ipsacq_maxpackets;
lastone = newbie->ipsacq_mp;
newbie->ipsacq_mp = lastone->b_next;
lastone->b_next = NULL;
lastone = ip_xmit_attr_free_mblk(lastone);
ip_drop_packet(lastone, B_FALSE, NULL,
DROPPER(ipss, ipds_sadb_acquire_toofull),
&ipss->ipsec_sadb_dropper);
! } else { /* Successfully queued */
IP_ACQUIRE_STAT(ipss, qhiwater,
newbie->ipsacq_numpackets);
}
}
*** 5098,5122 ****
newbie->ipsacq_srcaddr = src;
newbie->ipsacq_dstaddr = dst;
/*
! * If the acquire record has more than one queued packet, we've
! * already sent an ACQUIRE, and don't need to repeat ourself.
*/
! if (newbie->ipsacq_seq != seq || newbie->ipsacq_numpackets > 1) {
! /* I have an acquire outstanding already! */
! mutex_exit(&newbie->ipsacq_lock);
! return;
}
! if (!keysock_extended_reg(ns))
! goto punt_extended;
/*
! * Construct an extended ACQUIRE. There are logging
! * opportunities here in failure cases.
*/
bzero(&sel, sizeof (sel));
sel.ips_isv4 = (ixa->ixa_flags & IXAF_IS_IPV4) != 0;
if (tunnel_mode) {
sel.ips_protocol = (ixa->ixa_ipsec_inaf == AF_INET) ?
IPPROTO_ENCAP : IPPROTO_IPV6;
--- 5247,5308 ----
newbie->ipsacq_srcaddr = src;
newbie->ipsacq_dstaddr = dst;
/*
! * Sequence number mismatch or previously populated packet queue means
! * we retrieved an already-pending ACQUIRE record and needn't repeat
! * ourself. Unlock and return.
*/
! if (newbie->ipsacq_seq != seq || newbie->ipsacq_numpackets > 1)
! goto unlock_acqrec;
!
! /*
! * Even if we fail before sending to keysock, starting with a NULL
! * queue pointer, if gets this far, it counts as an acquire request.
! */
! if (need_esp) {
! ESP_BUMP_STAT(espstack, acquire_requests);
! q = espstack->esp_pfkey_q;
! } else {
! AH_BUMP_STAT(ahstack, acquire_requests);
! q = ahstack->ah_pfkey_q;
}
! if (q == NULL)
! goto unlock_acqrec;
!
! /* Initializes keysock M_CTL message for regular acquire. */
! regular = sadb_keysock_out(0);
! if (regular == NULL)
! goto unlock_acqrec;
!
/*
! * Check keysock stack to make sure we don't have extended register
! * pending. If not, have keysock initialize M_CTL msg for extended
! * acquire. If pending, set extended to NULL so we ignore it hereafter.
*/
+ if (keysock_extended_reg(ns)) {
+ extended = sadb_keysock_out(0);
+ if (extended == NULL)
+ goto bail_and_free_regular;
+ } else {
+ extended = NULL;
+ }
+
+ if (tsl != NULL) {
+ /*
+ * XXX MLS correct condition here?
+ * XXX MLS other credential attributes in acquire?
+ * XXX malloc failure? don't fall back to original?
+ */
+ sens = sadb_make_sens_ext(tsl, &sens_len);
+
+ if (sens == NULL)
+ goto bail_extended;
+ }
+ /* re-initialize selector using ixa and ipha */
bzero(&sel, sizeof (sel));
sel.ips_isv4 = (ixa->ixa_flags & IXAF_IS_IPV4) != 0;
if (tunnel_mode) {
sel.ips_protocol = (ixa->ixa_ipsec_inaf == AF_INET) ?
IPPROTO_ENCAP : IPPROTO_IPV6;
*** 5134,5182 ****
} else {
sel.ips_local_addr_v6 = ip6h->ip6_src;
sel.ips_remote_addr_v6 = ip6h->ip6_dst;
}
! extended = sadb_keysock_out(0);
! if (extended == NULL)
! goto punt_extended;
! if (ixa->ixa_tsl != NULL) {
/*
! * XXX MLS correct condition here?
! * XXX MLS other credential attributes in acquire?
! * XXX malloc failure? don't fall back to original?
*/
! sens = sadb_make_sens_ext(ixa->ixa_tsl, &sens_len);
! if (sens == NULL) {
! freeb(extended);
! goto punt_extended;
}
- }
! extended->b_cont = sadb_extended_acquire(&sel, pp, ap, tunnel_mode,
! seq, 0, sens, ns);
! if (sens != NULL)
! kmem_free(sens, sens_len);
! if (extended->b_cont == NULL) {
! freeb(extended);
! goto punt_extended;
}
! /*
! * Send an ACQUIRE message (and possible an extended ACQUIRE) based on
! * this new record. The send-acquire callback assumes that acqrec is
! * already locked.
! */
! (*spp->s_acqfn)(newbie, extended, ns);
return;
! punt_extended:
! (*spp->s_acqfn)(newbie, NULL, ns);
}
/*
* Unlink and free an acquire record.
*/
--- 5320,5419 ----
} else {
sel.ips_local_addr_v6 = ip6h->ip6_src;
sel.ips_remote_addr_v6 = ip6h->ip6_dst;
}
! /* Tack message containing sadb_msg_t onto keysock regular M_CTL */
! regular->b_cont = sadb_construct_acqmsg(newbie, &sel, ap, pp, ns, sens,
! need_esp, tunnel_mode, B_FALSE, B_FALSE); /* regular, no props */
! /* We have to do this, no matter the result of previous call */
! if (sens != NULL)
! kmem_free(sens, sens_len);
! if (regular->b_cont == NULL)
! goto bail_extended;
!
/*
! * If there's no extended pending, duplicate regular samsg, tacking it
! * on as the b_cont of the keysock-generated extended M_CTL.
*/
! if (extended != NULL) {
! extended->b_cont = dupb(regular->b_cont);
! if (extended->b_cont == NULL)
! goto bail_extended;
! }
! rw_enter(&ipss->ipsec_alg_lock, RW_READER);
! CALC_COMBS(combs_limit, ipss, need_esp);
! propsize = sizeof (sadb_prop_t) + (combs_limit * sizeof (sadb_comb_t));
!
! if ((prop_m = allocb(propsize, BPRI_HI)) == NULL)
! goto bail_and_unlock;
!
! if (extended != NULL) {
! epropsize = sizeof (sadb_prop_t)
! + (combs_limit * sizeof (sadb_x_ecomb_t));
! if ((eprop_m = allocb(epropsize, BPRI_HI)) == NULL)
! goto bail_and_unlock;
}
! prop = (sadb_prop_t *)prop_m->b_rptr;
! sadb_insert_prop(prop, ap, ns, combs_limit, need_esp);
! if (prop == NULL) {
! goto bail_and_unlock;
! /* 0 length prop is error, mark regular samsg a dud, & freeb prop_m */
! } else {
! samsg = (sadb_msg_t *)regular->b_cont->b_rptr;
! if (prop->sadb_prop_len == 0) {
! ERRNO_SAMSG(samsg, ENOENT);
! freeb(prop_m);
! }
! samsg->sadb_msg_len += prop->sadb_prop_len;
! prop_m->b_wptr += SADB_64TO8(prop->sadb_prop_len);
! regular->b_cont->b_cont = prop_m;
! }
! if (extended != NULL) {
! start = (uint8_t *)eprop_m->b_rptr;
! end = start + epropsize;
! eprop =
! (sadb_prop_t *)sadb_construct_eprop(ap, pp, ns, start, end);
! if (eprop == NULL)
! goto bail_and_unlock;
! /* If 0 ecombs, mark extended samsg a dud, and freeb eprop_m */
! else {
! samsg = (sadb_msg_t *)extended->b_cont->b_rptr;
!
! if (eprop->sadb_x_prop_numecombs == 0) {
! ERRNO_SAMSG(samsg, ENOENT);
! freeb(eprop_m);
}
+ samsg->sadb_msg_len += eprop->sadb_prop_len;
+ eprop_m->b_wptr += SADB_64TO8(eprop->sadb_prop_len);
+ extended->b_cont->b_cont = eprop_m;
+ }
+ }
! rw_exit(&ipss->ipsec_alg_lock);
! mutex_exit(&newbie->ipsacq_lock);
!
! if (extended != NULL)
! putnext(q, extended);
! putnext(q, regular);
return;
! /* We used a lot of b_cont mblk chaining, so we need to use freemsg. */
! bail_and_unlock:
! rw_exit(&ipss->ipsec_alg_lock);
! bail_extended:
! if (extended != NULL)
! freemsg(extended);
! bail_and_free_regular:
! freemsg(regular);
! unlock_acqrec:
! mutex_exit(&newbie->ipsacq_lock);
}
/*
* Unlink and free an acquire record.
*/
*** 5186,5195 ****
--- 5423,5433 ----
mblk_t *mp;
ipsec_stack_t *ipss = ns->netstack_ipsec;
ASSERT(MUTEX_HELD(acqrec->ipsacq_linklock));
+ /* XXX Should references be released before mutex is acquired? */
if (acqrec->ipsacq_policy != NULL) {
IPPOL_REFRELE(acqrec->ipsacq_policy);
}
if (acqrec->ipsacq_act != NULL) {
IPACT_REFRELE(acqrec->ipsacq_act);
*** 5260,5300 ****
/*
* Create an algorithm descriptor for an extended ACQUIRE. Filter crypto
* framework's view of reality vs. IPsec's. EF's wins, BTW.
*/
static uint8_t *
! sadb_new_algdesc(uint8_t *start, uint8_t *limit,
sadb_x_ecomb_t *ecomb, uint8_t satype, uint8_t algtype,
uint8_t alg, uint16_t minbits, uint16_t maxbits, ipsec_stack_t *ipss)
{
! uint8_t *cur = start;
ipsec_alginfo_t *algp;
sadb_x_algdesc_t *algdesc = (sadb_x_algdesc_t *)cur;
cur += sizeof (*algdesc);
! if (cur >= limit)
return (NULL);
ecomb->sadb_x_ecomb_numalgs++;
/*
* Normalize vs. crypto framework's limits. This way, you can specify
* a stronger policy, and when the framework loads a stronger version,
* you can just keep plowing w/o rewhacking your SPD.
*/
- mutex_enter(&ipss->ipsec_alg_lock);
algp = ipss->ipsec_alglists[(algtype == SADB_X_ALGTYPE_AUTH) ?
IPSEC_ALG_AUTH : IPSEC_ALG_ENCR][alg];
! if (algp == NULL) {
! mutex_exit(&ipss->ipsec_alg_lock);
return (NULL); /* Algorithm doesn't exist. Fail gracefully. */
- }
if (minbits < algp->alg_ef_minbits)
minbits = algp->alg_ef_minbits;
if (maxbits > algp->alg_ef_maxbits)
maxbits = algp->alg_ef_maxbits;
- mutex_exit(&ipss->ipsec_alg_lock);
algdesc->sadb_x_algdesc_reserved = SADB_8TO1(algp->alg_saltlen);
algdesc->sadb_x_algdesc_satype = satype;
algdesc->sadb_x_algdesc_algtype = algtype;
algdesc->sadb_x_algdesc_alg = alg;
--- 5498,5536 ----
/*
* Create an algorithm descriptor for an extended ACQUIRE. Filter crypto
* framework's view of reality vs. IPsec's. EF's wins, BTW.
*/
static uint8_t *
! sadb_new_algdesc(const uint8_t *start, const uint8_t *end,
sadb_x_ecomb_t *ecomb, uint8_t satype, uint8_t algtype,
uint8_t alg, uint16_t minbits, uint16_t maxbits, ipsec_stack_t *ipss)
{
! uint8_t *cur = (uint8_t *)start;
ipsec_alginfo_t *algp;
sadb_x_algdesc_t *algdesc = (sadb_x_algdesc_t *)cur;
+ ASSERT(RW_READ_HELD(&ipss->ipsec_alg_lock));
+
cur += sizeof (*algdesc);
! if (cur >= end)
return (NULL);
ecomb->sadb_x_ecomb_numalgs++;
/*
* Normalize vs. crypto framework's limits. This way, you can specify
* a stronger policy, and when the framework loads a stronger version,
* you can just keep plowing w/o rewhacking your SPD.
*/
algp = ipss->ipsec_alglists[(algtype == SADB_X_ALGTYPE_AUTH) ?
IPSEC_ALG_AUTH : IPSEC_ALG_ENCR][alg];
! if (algp == NULL)
return (NULL); /* Algorithm doesn't exist. Fail gracefully. */
if (minbits < algp->alg_ef_minbits)
minbits = algp->alg_ef_minbits;
if (maxbits > algp->alg_ef_maxbits)
maxbits = algp->alg_ef_maxbits;
algdesc->sadb_x_algdesc_reserved = SADB_8TO1(algp->alg_saltlen);
algdesc->sadb_x_algdesc_satype = satype;
algdesc->sadb_x_algdesc_algtype = algtype;
algdesc->sadb_x_algdesc_alg = alg;
*** 5303,5334 ****
return (cur);
}
/*
! * Convert the given ipsec_action_t into an ecomb starting at *ecomb
! * which must fit before *limit
! *
! * return NULL if we ran out of room or a pointer to the end of the ecomb.
*/
static uint8_t *
! sadb_action_to_ecomb(uint8_t *start, uint8_t *limit, ipsec_action_t *act,
! netstack_t *ns)
{
! uint8_t *cur = start;
sadb_x_ecomb_t *ecomb = (sadb_x_ecomb_t *)cur;
ipsec_prot_t *ipp;
ipsec_stack_t *ipss = ns->netstack_ipsec;
cur += sizeof (*ecomb);
! if (cur >= limit)
return (NULL);
! ASSERT(act->ipa_act.ipa_type == IPSEC_ACT_APPLY);
- ipp = &act->ipa_act.ipa_apply;
-
ecomb->sadb_x_ecomb_numalgs = 0;
ecomb->sadb_x_ecomb_reserved = 0;
ecomb->sadb_x_ecomb_reserved2 = 0;
/*
* No limits on allocations, since we really don't support that
--- 5539,5571 ----
return (cur);
}
/*
! * Use buffer defined by byte-aligned pointers start and end to convert
! * ipsec_action_t pointer act into an ecomb, using alg data hanging off of
! * netstack_t pointer ns. Return NULL rather than overrun buffer, otherwise
! * pointer to end of ecomb (which should be exact size of buffer).
*/
static uint8_t *
! sadb_action_to_ecomb(const uint8_t *start, const uint8_t *end,
! const ipsec_action_t *act, netstack_t *ns)
{
! uint8_t *cur = (uint8_t *)start;
sadb_x_ecomb_t *ecomb = (sadb_x_ecomb_t *)cur;
ipsec_prot_t *ipp;
ipsec_stack_t *ipss = ns->netstack_ipsec;
+ ASSERT(RW_READ_HELD(&ipss->ipsec_alg_lock));
+ ASSERT(act->ipa_act.ipa_type == IPSEC_ACT_APPLY);
+
cur += sizeof (*ecomb);
! if (cur >= end)
return (NULL);
! ipp = &((ipsec_action_t *)act)->ipa_act.ipa_apply;
ecomb->sadb_x_ecomb_numalgs = 0;
ecomb->sadb_x_ecomb_reserved = 0;
ecomb->sadb_x_ecomb_reserved2 = 0;
/*
* No limits on allocations, since we really don't support that
*** 5348,5377 ****
ecomb->sadb_x_ecomb_hard_addtime = 0;
ecomb->sadb_x_ecomb_soft_usetime = 0;
ecomb->sadb_x_ecomb_hard_usetime = 0;
if (ipp->ipp_use_ah) {
! cur = sadb_new_algdesc(cur, limit, ecomb,
SADB_SATYPE_AH, SADB_X_ALGTYPE_AUTH, ipp->ipp_auth_alg,
ipp->ipp_ah_minbits, ipp->ipp_ah_maxbits, ipss);
if (cur == NULL)
return (NULL);
ipsecah_fill_defs(ecomb, ns);
}
if (ipp->ipp_use_esp) {
if (ipp->ipp_use_espa) {
! cur = sadb_new_algdesc(cur, limit, ecomb,
SADB_SATYPE_ESP, SADB_X_ALGTYPE_AUTH,
ipp->ipp_esp_auth_alg,
ipp->ipp_espa_minbits,
ipp->ipp_espa_maxbits, ipss);
if (cur == NULL)
return (NULL);
}
! cur = sadb_new_algdesc(cur, limit, ecomb,
SADB_SATYPE_ESP, SADB_X_ALGTYPE_CRYPT,
ipp->ipp_encr_alg,
ipp->ipp_espe_minbits,
ipp->ipp_espe_maxbits, ipss);
if (cur == NULL)
--- 5585,5614 ----
ecomb->sadb_x_ecomb_hard_addtime = 0;
ecomb->sadb_x_ecomb_soft_usetime = 0;
ecomb->sadb_x_ecomb_hard_usetime = 0;
if (ipp->ipp_use_ah) {
! cur = sadb_new_algdesc(cur, end, ecomb,
SADB_SATYPE_AH, SADB_X_ALGTYPE_AUTH, ipp->ipp_auth_alg,
ipp->ipp_ah_minbits, ipp->ipp_ah_maxbits, ipss);
if (cur == NULL)
return (NULL);
ipsecah_fill_defs(ecomb, ns);
}
if (ipp->ipp_use_esp) {
if (ipp->ipp_use_espa) {
! cur = sadb_new_algdesc(cur, end, ecomb,
SADB_SATYPE_ESP, SADB_X_ALGTYPE_AUTH,
ipp->ipp_esp_auth_alg,
ipp->ipp_espa_minbits,
ipp->ipp_espa_maxbits, ipss);
if (cur == NULL)
return (NULL);
}
! cur = sadb_new_algdesc(cur, end, ecomb,
SADB_SATYPE_ESP, SADB_X_ALGTYPE_CRYPT,
ipp->ipp_encr_alg,
ipp->ipp_espe_minbits,
ipp->ipp_espe_maxbits, ipss);
if (cur == NULL)
*** 5477,5869 ****
}
/* End XXX label-library-leakage */
/*
! * Construct an extended ACQUIRE message based on a selector and the resulting
! * IPsec action.
! *
! * NOTE: This is used by both inverse ACQUIRE and actual ACQUIRE
! * generation. As a consequence, expect this function to evolve
! * rapidly.
*/
! static mblk_t *
! sadb_extended_acquire(ipsec_selector_t *sel, ipsec_policy_t *pol,
! ipsec_action_t *act, boolean_t tunnel_mode, uint32_t seq, uint32_t pid,
! sadb_sens_t *sens, netstack_t *ns)
{
! mblk_t *mp;
! sadb_msg_t *samsg;
! uint8_t *start, *cur, *end;
! uint32_t *saddrptr, *daddrptr;
! sa_family_t af;
! sadb_prop_t *eprop;
! ipsec_action_t *ap, *an;
! ipsec_selkey_t *ipsl;
! uint8_t proto, pfxlen;
! uint16_t lport, rport;
! uint32_t kmp, kmc;
! /*
! * Find the action we want sooner rather than later..
! */
! an = NULL;
! if (pol == NULL) {
! ap = act;
! } else {
! ap = pol->ipsp_act;
! if (ap != NULL)
! an = ap->ipa_next;
! }
! /*
! * Just take a swag for the allocation for now. We can always
! * alter it later.
! */
! #define SADB_EXTENDED_ACQUIRE_SIZE 4096
! mp = allocb(SADB_EXTENDED_ACQUIRE_SIZE, BPRI_HI);
! if (mp == NULL)
! return (NULL);
! start = mp->b_rptr;
! end = start + SADB_EXTENDED_ACQUIRE_SIZE;
! cur = start;
! samsg = (sadb_msg_t *)cur;
! cur += sizeof (*samsg);
- samsg->sadb_msg_version = PF_KEY_V2;
- samsg->sadb_msg_type = SADB_ACQUIRE;
- samsg->sadb_msg_errno = 0;
- samsg->sadb_msg_reserved = 0;
- samsg->sadb_msg_satype = 0;
- samsg->sadb_msg_seq = seq;
- samsg->sadb_msg_pid = pid;
-
- if (tunnel_mode) {
/*
! * Form inner address extensions based NOT on the inner
! * selectors (i.e. the packet data), but on the policy's
! * selector key (i.e. the policy's selector information).
! *
! * NOTE: The position of IPv4 and IPv6 addresses is the
! * same in ipsec_selkey_t (unless the compiler does very
! * strange things with unions, consult your local C language
! * lawyer for details).
*/
! ASSERT(pol != NULL);
!
! ipsl = &(pol->ipsp_sel->ipsl_key);
! if (ipsl->ipsl_valid & IPSL_IPV4) {
! af = AF_INET;
! ASSERT(sel->ips_protocol == IPPROTO_ENCAP);
! ASSERT(!(ipsl->ipsl_valid & IPSL_IPV6));
! } else {
! af = AF_INET6;
! ASSERT(sel->ips_protocol == IPPROTO_IPV6);
! ASSERT(ipsl->ipsl_valid & IPSL_IPV6);
}
! if (ipsl->ipsl_valid & IPSL_LOCAL_ADDR) {
! saddrptr = (uint32_t *)(&ipsl->ipsl_local);
! pfxlen = ipsl->ipsl_local_pfxlen;
! } else {
! saddrptr = (uint32_t *)(&ipv6_all_zeros);
! pfxlen = 0;
}
- /* XXX What about ICMP type/code? */
- lport = (ipsl->ipsl_valid & IPSL_LOCAL_PORT) ?
- ipsl->ipsl_lport : 0;
- proto = (ipsl->ipsl_valid & IPSL_PROTOCOL) ?
- ipsl->ipsl_proto : 0;
! cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_SRC,
! af, saddrptr, lport, proto, pfxlen);
! if (cur == NULL) {
! freeb(mp);
! return (NULL);
! }
! if (ipsl->ipsl_valid & IPSL_REMOTE_ADDR) {
! daddrptr = (uint32_t *)(&ipsl->ipsl_remote);
! pfxlen = ipsl->ipsl_remote_pfxlen;
} else {
! daddrptr = (uint32_t *)(&ipv6_all_zeros);
! pfxlen = 0;
}
- /* XXX What about ICMP type/code? */
- rport = (ipsl->ipsl_valid & IPSL_REMOTE_PORT) ?
- ipsl->ipsl_rport : 0;
! cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_DST,
! af, daddrptr, rport, proto, pfxlen);
! if (cur == NULL) {
! freeb(mp);
! return (NULL);
! }
! /*
! * TODO - if we go to 3408's dream of transport mode IP-in-IP
! * _with_ inner-packet address selectors, we'll need to further
! * distinguish tunnel mode here. For now, having inner
! * addresses and/or ports is sufficient.
! *
! * Meanwhile, whack proto/ports to reflect IP-in-IP for the
! * outer addresses.
! */
! proto = sel->ips_protocol; /* Either _ENCAP or _IPV6 */
! lport = rport = 0;
! } else if ((ap != NULL) && (!ap->ipa_want_unique)) {
! proto = 0;
! lport = 0;
! rport = 0;
! if (pol != NULL) {
! ipsl = &(pol->ipsp_sel->ipsl_key);
! if (ipsl->ipsl_valid & IPSL_PROTOCOL)
! proto = ipsl->ipsl_proto;
! if (ipsl->ipsl_valid & IPSL_REMOTE_PORT)
! rport = ipsl->ipsl_rport;
! if (ipsl->ipsl_valid & IPSL_LOCAL_PORT)
! lport = ipsl->ipsl_lport;
! }
} else {
! proto = sel->ips_protocol;
! lport = sel->ips_local_port;
! rport = sel->ips_remote_port;
}
- af = sel->ips_isv4 ? AF_INET : AF_INET6;
-
/*
! * NOTE: The position of IPv4 and IPv6 addresses is the same in
! * ipsec_selector_t.
*/
- cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_SRC, af,
- (uint32_t *)(&sel->ips_local_addr_v6), lport, proto, 0);
! if (cur == NULL) {
! freeb(mp);
! return (NULL);
}
! cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_DST, af,
! (uint32_t *)(&sel->ips_remote_addr_v6), rport, proto, 0);
!
! if (cur == NULL) {
! freeb(mp);
! return (NULL);
}
! if (sens != NULL) {
! uint8_t *sensext = cur;
! int senslen = SADB_64TO8(sens->sadb_sens_len);
! cur += senslen;
! if (cur > end) {
! freeb(mp);
! return (NULL);
! }
! bcopy(sens, sensext, senslen);
! }
! /*
! * This section will change a lot as policy evolves.
! * For now, it'll be relatively simple.
*/
! eprop = (sadb_prop_t *)cur;
cur += sizeof (*eprop);
! if (cur > end) {
! /* no space left */
! freeb(mp);
return (NULL);
- }
eprop->sadb_prop_exttype = SADB_X_EXT_EPROP;
eprop->sadb_x_prop_ereserved = 0;
eprop->sadb_x_prop_numecombs = 0;
eprop->sadb_prop_replay = 32; /* default */
- kmc = kmp = 0;
-
for (; ap != NULL; ap = an) {
- an = (pol != NULL) ? ap->ipa_next : NULL;
-
/*
! * Skip non-IPsec policies
*/
if (ap->ipa_act.ipa_type != IPSEC_ACT_APPLY)
continue;
- if (ap->ipa_act.ipa_apply.ipp_km_proto)
- kmp = ap->ipa_act.ipa_apply.ipp_km_proto;
- if (ap->ipa_act.ipa_apply.ipp_km_cookie)
- kmc = ap->ipa_act.ipa_apply.ipp_km_cookie;
if (ap->ipa_act.ipa_apply.ipp_replay_depth) {
eprop->sadb_prop_replay =
ap->ipa_act.ipa_apply.ipp_replay_depth;
}
cur = sadb_action_to_ecomb(cur, end, ap, ns);
! if (cur == NULL) { /* no space */
! freeb(mp);
return (NULL);
- }
eprop->sadb_x_prop_numecombs++;
}
- if (eprop->sadb_x_prop_numecombs == 0) {
/*
! * This will happen if we fail to find a policy
! * allowing for IPsec processing.
! * Construct an error message.
*/
! samsg->sadb_msg_len = SADB_8TO64(sizeof (*samsg));
! samsg->sadb_msg_errno = ENOENT;
! samsg->sadb_x_msg_diagnostic = 0;
! return (mp);
! }
! if ((kmp != 0) || (kmc != 0)) {
! cur = sadb_make_kmc_ext(cur, end, kmp, kmc);
! if (cur == NULL) {
! freeb(mp);
return (NULL);
}
- }
! eprop->sadb_prop_len = SADB_8TO64(cur - (uint8_t *)eprop);
! samsg->sadb_msg_len = SADB_8TO64(cur - start);
! mp->b_wptr = cur;
! return (mp);
}
/*
! * Generic setup of an RFC 2367 ACQUIRE message. Caller sets satype.
! *
! * NOTE: This function acquires alg_lock as a side-effect if-and-only-if we
! * succeed (i.e. return non-NULL). Caller MUST release it. This is to
! * maximize code consolidation while preventing algorithm changes from messing
! * with the callers finishing touches on the ACQUIRE itself.
*/
! mblk_t *
! sadb_setup_acquire(ipsacq_t *acqrec, uint8_t satype, ipsec_stack_t *ipss)
{
! uint_t allocsize;
! mblk_t *pfkeymp, *msgmp;
! sa_family_t af;
uint8_t *cur, *end;
sadb_msg_t *samsg;
! uint16_t sport_typecode;
! uint16_t dport_typecode;
! uint8_t check_proto;
! boolean_t tunnel_mode = (acqrec->ipsacq_inneraddrfam != 0);
! ASSERT(MUTEX_HELD(&acqrec->ipsacq_lock));
! pfkeymp = sadb_keysock_out(0);
! if (pfkeymp == NULL)
! return (NULL);
/*
! * First, allocate a basic ACQUIRE message
*/
! allocsize = sizeof (sadb_msg_t) + sizeof (sadb_address_t) +
! sizeof (sadb_address_t) + sizeof (sadb_prop_t);
! /* Make sure there's enough to cover both AF_INET and AF_INET6. */
! allocsize += 2 * sizeof (struct sockaddr_in6);
! mutex_enter(&ipss->ipsec_alg_lock);
! /* NOTE: The lock is now held through to this function's return. */
! allocsize += ipss->ipsec_nalgs[IPSEC_ALG_AUTH] *
! ipss->ipsec_nalgs[IPSEC_ALG_ENCR] * sizeof (sadb_comb_t);
! if (tunnel_mode) {
! /* Tunnel mode! */
! allocsize += 2 * sizeof (sadb_address_t);
! /* Enough to cover both AF_INET and AF_INET6. */
! allocsize += 2 * sizeof (struct sockaddr_in6);
! }
!
! msgmp = allocb(allocsize, BPRI_HI);
! if (msgmp == NULL) {
! freeb(pfkeymp);
! mutex_exit(&ipss->ipsec_alg_lock);
! return (NULL);
! }
!
! pfkeymp->b_cont = msgmp;
! cur = msgmp->b_rptr;
end = cur + allocsize;
samsg = (sadb_msg_t *)cur;
! cur += sizeof (sadb_msg_t);
! af = acqrec->ipsacq_addrfam;
! switch (af) {
! case AF_INET:
! check_proto = IPPROTO_ICMP;
! break;
! case AF_INET6:
! check_proto = IPPROTO_ICMPV6;
! break;
! default:
! /* This should never happen unless we have kernel bugs. */
! cmn_err(CE_WARN,
! "sadb_setup_acquire: corrupt ACQUIRE record.\n");
! ASSERT(0);
! mutex_exit(&ipss->ipsec_alg_lock);
! return (NULL);
}
! samsg->sadb_msg_version = PF_KEY_V2;
! samsg->sadb_msg_type = SADB_ACQUIRE;
! samsg->sadb_msg_satype = satype;
! samsg->sadb_msg_errno = 0;
! samsg->sadb_msg_pid = 0;
! samsg->sadb_msg_reserved = 0;
! samsg->sadb_msg_seq = acqrec->ipsacq_seq;
! ASSERT(MUTEX_HELD(&acqrec->ipsacq_lock));
! if ((acqrec->ipsacq_proto == check_proto) || tunnel_mode) {
! sport_typecode = dport_typecode = 0;
} else {
! sport_typecode = acqrec->ipsacq_srcport;
! dport_typecode = acqrec->ipsacq_dstport;
}
! cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_SRC, af,
! acqrec->ipsacq_srcaddr, sport_typecode, acqrec->ipsacq_proto, 0);
! cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_DST, af,
! acqrec->ipsacq_dstaddr, dport_typecode, acqrec->ipsacq_proto, 0);
! if (tunnel_mode) {
! sport_typecode = acqrec->ipsacq_srcport;
! dport_typecode = acqrec->ipsacq_dstport;
! cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_SRC,
! acqrec->ipsacq_inneraddrfam, acqrec->ipsacq_innersrc,
! sport_typecode, acqrec->ipsacq_inner_proto,
! acqrec->ipsacq_innersrcpfx);
! cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_DST,
! acqrec->ipsacq_inneraddrfam, acqrec->ipsacq_innerdst,
! dport_typecode, acqrec->ipsacq_inner_proto,
! acqrec->ipsacq_innerdstpfx);
}
! /* XXX Insert identity information here. */
! /* XXXMLS Insert sensitivity information here. */
! if (cur != NULL)
! samsg->sadb_msg_len = SADB_8TO64(cur - msgmp->b_rptr);
! else
! mutex_exit(&ipss->ipsec_alg_lock);
! return (pfkeymp);
}
/*
* Given an SADB_GETSPI message, find an appropriately ranged SA and
* allocate an SA. If there are message improprieties, return (ipsa_t *)-1.
--- 5714,6123 ----
}
/* End XXX label-library-leakage */
/*
! * Takes a pointer to sadb_prop_t (what we're initializing), ipsec_action_t
! * (first action in chain we need to walk of actions for each alg
! * combination), netstack_ns (contains pointers to alg properties and
! * per-protocol settings), a combs_limit integer (maximum applicable
! * combinations derived from per-protcol netstack_t alg array), and need_esp
! * boolean_t. We distinguish between two error cases: we exceed combs_limit,
! * which should only be a kernel bug (ipsec_alg_lock is our shepherd), or we
! * have an alg ID with a NULL netstack member or member with the valid bit
! * flipped, both of which indicate the needs to reset state, which we flag by
! * returning no combs. We return NULL if we exceed combs_limit and zero-length
! * prop if we run into an alg that can't be transferred into the prop.
*/
! static void
! sadb_insert_prop(sadb_prop_t *prop, const ipsec_action_t *ap, netstack_t *ns,
! uint_t combs_limit, boolean_t need_esp)
{
! sadb_comb_t *comb = (sadb_comb_t *)(prop + 1);
! ipsec_action_t *act = (ipsec_action_t *)ap;
! ipsec_prot_t *prot;
! ipsecah_stack_t *ahstack = ns->netstack_ipsecah;
! ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
! ipsec_stack_t *ipss = ns->netstack_ipsec;
! boolean_t need_ah = !need_esp;
! ASSERT(RW_READ_HELD(&ipss->ipsec_alg_lock));
! ASSERT((need_esp && ap->ipa_want_esp) || (need_ah && ap->ipa_want_ah));
! prop->sadb_prop_exttype = SADB_EXT_PROPOSAL;
! prop->sadb_prop_len = SADB_8TO64(sizeof (sadb_prop_t));
! *(uint32_t *)(&prop->sadb_prop_replay) = 0; /* Quick zero-out! */
! prop->sadb_prop_replay = need_esp ?
! espstack->ipsecesp_replay_size : ahstack->ipsecah_replay_size;
! /* Prioritize a proposal, preserving policy order. */
! for (; act != NULL; act = act->ipa_next) {
! ipsec_alginfo_t *aalg = NULL;
! ipsec_alginfo_t *ealg = NULL;
! if ((act->ipa_act.ipa_type != IPSEC_POLICY_APPLY) ||
! (need_esp && !act->ipa_act.ipa_apply.ipp_use_esp) ||
! (need_ah && !act->ipa_act.ipa_apply.ipp_use_ah))
! continue;
! if (--combs_limit == 0) {
! prop = NULL;
! return;
! }
! prot = &act->ipa_act.ipa_apply;
/*
! * Alg ID 0 is none/any, which is valid only for ESP without
! * message integrity (ipp_esp_auth_alg). NULL encryption ESP
! * uses a distinct alg, non-zero ID.
*/
! if ((need_esp && prot->ipp_esp_auth_alg != 0) || need_ah) {
! ASSERT(need_esp || (prot->ipp_auth_alg > 0));
! aalg = ipss->ipsec_alglists[IPSEC_ALG_AUTH][need_esp ?
! prot->ipp_esp_auth_alg : prot->ipp_auth_alg];
! if (aalg == NULL || !ALG_VALID(aalg))
! goto failure;
}
! if (need_esp) {
! ASSERT(prot->ipp_encr_alg > 0);
! ealg = ipss->ipsec_alglists[IPSEC_ALG_ENCR]
! [prot->ipp_encr_alg];
! if (ealg == NULL || !ALG_VALID(ealg))
! goto failure;
}
! comb->sadb_comb_flags = 0;
! comb->sadb_comb_reserved = 0;
! if (ealg != NULL) {
! comb->sadb_comb_encrypt = ealg->alg_id;
! comb->sadb_comb_encrypt_minbits =
! MAX(prot->ipp_espe_minbits, ealg->alg_ef_minbits);
! comb->sadb_comb_encrypt_maxbits =
! MIN(prot->ipp_espe_maxbits, ealg->alg_ef_maxbits);
} else {
! comb->sadb_comb_encrypt = 0;
! comb->sadb_comb_encrypt_minbits = 0;
! comb->sadb_comb_encrypt_maxbits = 0;
}
! if (aalg != NULL) {
! uint16_t minbits, maxbits;
! minbits = need_esp ?
! prot->ipp_espa_minbits : prot->ipp_ah_minbits;
! maxbits = need_esp ?
! prot->ipp_espa_maxbits : prot->ipp_ah_maxbits;
! comb->sadb_comb_auth = aalg->alg_id;
! comb->sadb_comb_auth_minbits =
! MAX(minbits, aalg->alg_ef_minbits);
! comb->sadb_comb_auth_maxbits =
! MIN(maxbits, aalg->alg_ef_maxbits);
} else {
! comb->sadb_comb_auth = 0;
! comb->sadb_comb_auth_minbits = 0;
! comb->sadb_comb_auth_maxbits = 0;
}
/*
! * The following may be based on algorithm properties, but in
! * the meantime, we just pick some good, sensible numbers.
! * Key mgmt. can (and perhaps should) be the place to finalize
! * such decisions.
*/
! /* 0 == unlimited == unsupported */
! comb->sadb_comb_soft_allocations = 0;
! comb->sadb_comb_hard_allocations = 0;
!
! /* These may want to come from policy rule. */
! if (need_esp) {
! comb->sadb_comb_soft_bytes =
! espstack->ipsecesp_default_soft_bytes;
! comb->sadb_comb_hard_bytes =
! espstack->ipsecesp_default_hard_bytes;
! comb->sadb_comb_soft_addtime =
! espstack->ipsecesp_default_soft_addtime;
! comb->sadb_comb_hard_addtime =
! espstack->ipsecesp_default_hard_addtime;
! comb->sadb_comb_soft_usetime =
! espstack->ipsecesp_default_soft_usetime;
! comb->sadb_comb_hard_usetime =
! espstack->ipsecesp_default_hard_usetime;
! } else {
! comb->sadb_comb_soft_bytes =
! ahstack->ipsecah_default_soft_bytes;
! comb->sadb_comb_hard_bytes =
! ahstack->ipsecah_default_hard_bytes;
! comb->sadb_comb_soft_addtime =
! ahstack->ipsecah_default_soft_addtime;
! comb->sadb_comb_hard_addtime =
! ahstack->ipsecah_default_hard_addtime;
! comb->sadb_comb_soft_usetime =
! ahstack->ipsecah_default_soft_usetime;
! comb->sadb_comb_hard_usetime =
! ahstack->ipsecah_default_hard_usetime;
}
! prop->sadb_prop_len += SADB_8TO64(sizeof (*comb));
! comb++;
}
! return;
! failure:
! prop->sadb_prop_len = 0;
! }
! /*
! * Construct extended properties using ipsec_action_t, ipsec_policy_t, and
! * netstack_t pointers. Byte-aligned pointers cur and end are used for bounds
! * checking here and in called code. We don't set length if numecombs is 0, so
! * callers must check this for error handling.
*/
! static uint8_t *
! sadb_construct_eprop(const ipsec_action_t *act, const ipsec_policy_t *pp,
! netstack_t *ns, const uint8_t *start, const uint8_t *end)
! {
! uint8_t *cur = (uint8_t *)start;
! sadb_prop_t *eprop = (sadb_prop_t *)cur;
! ipsec_action_t *an, *ap = (ipsec_action_t *)act;
! ipsec_stack_t *ipss = ns->netstack_ipsec;
!
! ASSERT(RW_READ_HELD(&ipss->ipsec_alg_lock));
!
cur += sizeof (*eprop);
! if (cur > end)
return (NULL);
eprop->sadb_prop_exttype = SADB_X_EXT_EPROP;
eprop->sadb_x_prop_ereserved = 0;
eprop->sadb_x_prop_numecombs = 0;
eprop->sadb_prop_replay = 32; /* default */
for (; ap != NULL; ap = an) {
/*
! * XXX Don't walk past first ap if there's no pp. Not clear on
! * the rationale for this, but it's what extended path did.
*/
+ an = (pp != NULL) ? ap->ipa_next : NULL;
+
if (ap->ipa_act.ipa_type != IPSEC_ACT_APPLY)
continue;
if (ap->ipa_act.ipa_apply.ipp_replay_depth) {
eprop->sadb_prop_replay =
ap->ipa_act.ipa_apply.ipp_replay_depth;
}
cur = sadb_action_to_ecomb(cur, end, ap, ns);
! if (cur == NULL)
return (NULL);
eprop->sadb_x_prop_numecombs++;
}
/*
! * This is an error. We return what we've got of eprops, caller needs
! * to check for condition and pass it further up (e.g. by error samsg).
*/
! if (eprop->sadb_x_prop_numecombs == 0)
! return (cur);
! eprop->sadb_prop_len = SADB_8TO64(cur - (uint8_t *)start);
!
! return (cur);
! bail:
return (NULL);
+ }
+
+ /*
+ * Convert ipsec_policy_t and ipsec_action_t pointers to kmc extension. Byte-
+ * aligned cur and end pointers used for bounds checking. sadb_x_kmcext_t
+ * handling encapsulated in sadb_make_kmc_ext. Returns new value for cur,
+ * NULL on failure.
+ * We encapsulate for recursion since we have to walk ipsec_action_t.
+ */
+ static uint8_t *
+ sadb_policy_to_kmcext(const ipsec_policy_t *pp, const ipsec_action_t *act,
+ const uint8_t *start, const uint8_t *end)
+ {
+ uint8_t *cur = (uint8_t *)start;
+ ipsec_action_t *an, *ap = (ipsec_action_t *)act;
+ uint32_t kmp = 0, kmc = 0;
+
+ for (; ap != NULL; ap = an) {
+ an = (pp != NULL) ? ap->ipa_next : NULL;
+
+ /*
+ * Skip non-IPsec policies
+ */
+ if (ap->ipa_act.ipa_type != IPSEC_ACT_APPLY)
+ continue;
+
+ if (ap->ipa_act.ipa_apply.ipp_km_proto)
+ kmp = ap->ipa_act.ipa_apply.ipp_km_proto;
+ if (ap->ipa_act.ipa_apply.ipp_km_cookie)
+ kmc = ap->ipa_act.ipa_apply.ipp_km_cookie;
}
! if ((kmp != 0) || (kmc != 0))
! cur = sadb_make_kmc_ext(cur, end, kmp, kmc);
! return (cur);
}
/*
! * Prepare the SADB_ACQUIRE message proper, which should be a b_cont to a
! * keysock registered M_CTL message. Takes a pointer to ipsacq_t (optional
! * acquire record for which we're sending message), ipsec_selector_t,
! * ipsec_action_t, ipsec_policy_t, netstack_t, and sense (required for called
! * to generate the message), and booleans for need_esp, tunnel_mode,
! * extended, and with_prop (all of these should be self-explanatory). Because
! * extended messages set satype to SADB_SATYPE_UNSPEC, extended-only callers
! * can fudge need_esp.
*/
! static mblk_t *
! sadb_construct_acqmsg(ipsacq_t *acqrec, ipsec_selector_t *sel,
! ipsec_action_t *ap, ipsec_policy_t *pp, netstack_t *ns, sadb_sens_t *sens,
! boolean_t need_esp, boolean_t tunnel_mode, boolean_t extended,
! boolean_t with_prop)
{
! uint_t combs_limit, allocsize;
uint8_t *cur, *end;
sadb_msg_t *samsg;
! sadb_prop_t *prop, *eprop;
! mblk_t *mp;
! int satype = extended ? SADB_SATYPE_UNSPEC
! : (need_esp ? SADB_SATYPE_ESP : SADB_SATYPE_AH);
! ipsec_stack_t *ipss = ns->netstack_ipsec;
! ASSERT((acqrec == NULL) || (MUTEX_HELD(&acqrec->ipsacq_lock)));
! ASSERT(ap != NULL);
! ASSERT((pp == NULL) || (pp->ipsp_refs != 0));
! ASSERT((ap == NULL) || (ap->ipa_refs != 0));
! /*
! * Set the limit used to size [e]prop [e]combs array to as many
! * algorithms as defined on the netstack (must hold ipsec_alg_lock
! * from here to when done reading off netstack for [e]prop
! * formation). need_esp may be fudged, so be generous to extended.
! */
! if (with_prop) {
! if (extended)
! need_esp = B_TRUE;
! rw_enter(&ipss->ipsec_alg_lock, RW_READER);
! CALC_COMBS(combs_limit, ipss, need_esp);
! }
/*
! * If this code is right, we may not need cur & end for bounds
! * checking, but we'll keep normal runtime checks until that statement
! * looks credible rather than merely plausible, at which point checks
! * can be moved to ASSERTs. sens is variably sized but already
! * set. kmc is fixed size. Pointers into message are byte-aligned, so
! * we're generally depending on all structures used in this
! * calculation to be so, too (in fact, all sadb_*_t types used here
! * are 64-bit aligned per PF_KEY requirements).
*/
! allocsize = sizeof (sadb_msg_t) + sizeof (sadb_prop_t);
! allocsize += ((tunnel_mode) ? 4 : 2) * (sizeof (sadb_address_t)
! + SADB_SOCKADDR_SIZE);
! if (sens != NULL)
! allocsize += SADB_64TO8(sens->sadb_sens_len);
! allocsize += sizeof (sadb_x_kmc_t);
! /* If we need props, size combs/combs array using combs_limit */
! if (with_prop)
! allocsize += combs_limit * (extended ?
! sizeof (sadb_x_ecomb_t) : sizeof (sadb_comb_t));
! ASSERT((allocsize & 0x7) == 0);
! mp = allocb(allocsize, BPRI_HI);
! if (mp == NULL)
! goto unlock_and_fail;
! cur = mp->b_rptr;
end = cur + allocsize;
+
samsg = (sadb_msg_t *)cur;
! INITIALIZE_SAMSG(samsg, SADB_ACQUIRE);
! samsg->sadb_msg_satype = satype;
! samsg->sadb_msg_pid = 0;
! samsg->sadb_msg_seq = (acqrec != NULL) ? acqrec->ipsacq_seq : 0;
! /* CALC_COMBS asserts on zero limit; broken config still possible */
! if (with_prop && (combs_limit == 0)) {
! ERRNO_SAMSG(samsg, ENOENT);
! goto unlock_and_bail;
}
! cur += sizeof (sadb_msg_t);
! cur = sadb_sel_to_addrexts(sel, pp, ap, cur, end, tunnel_mode);
! if (cur == NULL)
! goto unlock_and_fail;
! if (with_prop) {
! if (extended) {
! cur = sadb_construct_eprop(ap, pp, ns, cur, end);
! if (cur == NULL)
! goto unlock_and_fail;
!
! eprop = (sadb_prop_t *)cur;
! if (eprop->sadb_x_prop_numecombs == 0) {
! ERRNO_SAMSG(samsg, ENOENT);
! goto unlock_and_bail;
! }
} else {
! prop = (sadb_prop_t *)cur;
!
! sadb_insert_prop(prop, ap, ns, combs_limit, need_esp);
! if (prop == NULL) {
! goto unlock_and_fail;
! } else if (prop->sadb_prop_len == 0) {
! ERRNO_SAMSG(samsg, ENOENT);
! goto unlock_and_bail;
}
! cur += SADB_64TO8(prop->sadb_prop_len);
! }
! rw_exit(&ipss->ipsec_alg_lock);
! }
! if (sens != NULL) {
! uint8_t *sensext = cur;
! int senslen = SADB_64TO8(sens->sadb_sens_len);
!
! cur += senslen;
! if (cur > end)
! goto freeb_bail;
! bcopy(sens, sensext, senslen);
}
! cur = sadb_policy_to_kmcext(pp, ap, cur, end);
! if (cur == NULL)
! goto freeb_bail;
! samsg->sadb_msg_len = SADB_8TO64(cur - mp->b_rptr);
! mp->b_wptr = cur;
! return (mp);
! freeb_bail:
! /* This message isn't chained, so we can freeb. */
! freeb(mp);
! return (NULL);
! unlock_and_bail:
! if (with_prop)
! rw_exit(&ipss->ipsec_alg_lock);
! return (mp);
! unlock_and_fail:
! if (with_prop)
! rw_exit(&ipss->ipsec_alg_lock);
! return (NULL);
}
/*
* Given an SADB_GETSPI message, find an appropriately ranged SA and
* allocate an SA. If there are message improprieties, return (ipsa_t *)-1.
*** 6244,6282 ****
inet_ntop(af, addr, buf, sizeof (buf)));
}
/*
* Fills in a reference to the policy, if any, from the conn, in *ppp
*/
static void
ipsec_conn_pol(ipsec_selector_t *sel, conn_t *connp, ipsec_policy_t **ppp)
{
ipsec_policy_t *pp;
ipsec_latch_t *ipl = connp->conn_latch;
if ((ipl != NULL) && (connp->conn_ixa->ixa_ipsec_policy != NULL)) {
pp = connp->conn_ixa->ixa_ipsec_policy;
IPPOL_REFHOLD(pp);
! } else {
pp = ipsec_find_policy(IPSEC_TYPE_OUTBOUND, connp, sel,
connp->conn_netstack);
}
*ppp = pp;
}
/*
! * The following functions scan through active conn_t structures
! * and return a reference to the best-matching policy it can find.
! * Caller must release the reference.
*/
static void
ipsec_udp_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp, ip_stack_t *ipst)
{
connf_t *connfp;
conn_t *connp = NULL;
ipsec_selector_t portonly;
bzero((void *)&portonly, sizeof (portonly));
if (sel->ips_local_port == 0)
return;
--- 6498,6541 ----
inet_ntop(af, addr, buf, sizeof (buf)));
}
/*
* Fills in a reference to the policy, if any, from the conn, in *ppp
+ * If found, we hold a reference to the policy, caller must release.
*/
static void
ipsec_conn_pol(ipsec_selector_t *sel, conn_t *connp, ipsec_policy_t **ppp)
{
ipsec_policy_t *pp;
ipsec_latch_t *ipl = connp->conn_latch;
+ /* Use policy pointer already on conn_t if it's there. */
if ((ipl != NULL) && (connp->conn_ixa->ixa_ipsec_policy != NULL)) {
pp = connp->conn_ixa->ixa_ipsec_policy;
IPPOL_REFHOLD(pp);
! } else { /* otherwise query SPD */
! /* This holds a reference for us if successful) */
pp = ipsec_find_policy(IPSEC_TYPE_OUTBOUND, connp, sel,
connp->conn_netstack);
}
*ppp = pp;
}
/*
! * Takes ipsec_selector_t (for attributes to query), ipsec_policy_t (what we're
! * trying to find), and ip_stack_t (contains udp fanout we need to query). If we
! * find a matching connection, we return its policy settings.
*/
static void
ipsec_udp_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp, ip_stack_t *ipst)
{
connf_t *connfp;
conn_t *connp = NULL;
ipsec_selector_t portonly;
+ ASSERT(*ppp == NULL);
+
bzero((void *)&portonly, sizeof (portonly));
if (sel->ips_local_port == 0)
return;
*** 6322,6350 ****
ipsec_conn_pol(sel, connp, ppp);
CONN_DEC_REF(connp);
}
static conn_t *
! ipsec_find_listen_conn(uint16_t *pptr, ipsec_selector_t *sel, ip_stack_t *ipst)
{
connf_t *connfp;
conn_t *connp = NULL;
const in6_addr_t *v6addrmatch = &sel->ips_local_addr_v6;
! if (sel->ips_local_port == 0)
! return (NULL);
connfp = &ipst->ips_ipcl_bind_fanout[
IPCL_BIND_HASH(sel->ips_local_port, ipst)];
mutex_enter(&connfp->connf_lock);
if (sel->ips_isv4) {
connp = connfp->connf_head;
while (connp != NULL) {
if (IPCL_BIND_MATCH(connp, IPPROTO_TCP,
! sel->ips_local_addr_v4, pptr[1]))
break;
connp = connp->conn_next;
}
if (connp == NULL) {
--- 6581,6615 ----
ipsec_conn_pol(sel, connp, ppp);
CONN_DEC_REF(connp);
}
+ /*
+ * Takes ipsec_selector_t (connection attributes to form query) and ip_stack_t
+ * (contains bind fanout we need to query) pointers to look up existing TCP
+ * listener, returned via conn_t pointer. We return NULL on failure.
+ * We increment reference count on match, caller must decrement.
+ */
static conn_t *
! ipsec_find_listen_conn(ipsec_selector_t *sel, ip_stack_t *ipst)
{
connf_t *connfp;
conn_t *connp = NULL;
const in6_addr_t *v6addrmatch = &sel->ips_local_addr_v6;
! /* XXX Sure about the second part? */
! ASSERT(sel->ips_local_port != 0 && ipst != NULL);
connfp = &ipst->ips_ipcl_bind_fanout[
IPCL_BIND_HASH(sel->ips_local_port, ipst)];
mutex_enter(&connfp->connf_lock);
if (sel->ips_isv4) {
connp = connfp->connf_head;
while (connp != NULL) {
if (IPCL_BIND_MATCH(connp, IPPROTO_TCP,
! sel->ips_local_addr_v4, sel->ips_local_port))
break;
connp = connp->conn_next;
}
if (connp == NULL) {
*** 6355,6365 ****
if (connp == NULL) {
connp = connfp->connf_head;
while (connp != NULL) {
if (IPCL_BIND_MATCH_V6(connp, IPPROTO_TCP,
! *v6addrmatch, pptr[1]))
break;
connp = connp->conn_next;
}
if (connp == NULL) {
--- 6620,6630 ----
if (connp == NULL) {
connp = connfp->connf_head;
while (connp != NULL) {
if (IPCL_BIND_MATCH_V6(connp, IPPROTO_TCP,
! *v6addrmatch, sel->ips_local_port))
break;
connp = connp->conn_next;
}
if (connp == NULL) {
*** 6371,6403 ****
CONN_INC_REF(connp);
mutex_exit(&connfp->connf_lock);
return (connp);
}
static void
ipsec_tcp_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp, ip_stack_t *ipst)
{
connf_t *connfp;
conn_t *connp;
uint32_t ports;
uint16_t *pptr = (uint16_t *)&ports;
/*
* Find TCP state in the following order:
! * 1.) Connected conns.
* 2.) Listeners.
*
* Even though #2 will be the common case for inbound traffic, only
* following this order insures correctness.
*/
- if (sel->ips_local_port == 0)
- return;
/*
! * 0 should be fport, 1 should be lport. SRC is the local one here.
! * See ipsec_construct_inverse_acquire() for details.
*/
pptr[0] = sel->ips_remote_port;
pptr[1] = sel->ips_local_port;
connfp = &ipst->ips_ipcl_conn_fanout[
--- 6636,6674 ----
CONN_INC_REF(connp);
mutex_exit(&connfp->connf_lock);
return (connp);
}
+ /*
+ * Given ipsec_selector_t (contains attributes to query, ipsec_policy_t (what we
+ * need to find), and ip_stack_t pointer (contains connection state to query),
+ * find a matching TCP connection or listener and return its policy pointer.
+ */
static void
ipsec_tcp_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp, ip_stack_t *ipst)
{
connf_t *connfp;
conn_t *connp;
uint32_t ports;
uint16_t *pptr = (uint16_t *)&ports;
+ ASSERT(sel->ips_local_port != 0 && *ppp == NULL);
+
/*
* Find TCP state in the following order:
! * 1.) Connected conns. (walk ipst connection fanout)
* 2.) Listeners.
*
* Even though #2 will be the common case for inbound traffic, only
* following this order insures correctness.
*/
/*
! * pptr makes an array of port values, 0 for fport, 1 for lport. SRC is
! * the local one here. Connection lookup macros want this instead of
! * selector port members.
*/
pptr[0] = sel->ips_remote_port;
pptr[1] = sel->ips_local_port;
connfp = &ipst->ips_ipcl_conn_fanout[
*** 6427,6468 ****
CONN_INC_REF(connp);
mutex_exit(&connfp->connf_lock);
} else {
mutex_exit(&connfp->connf_lock);
! /* Try the listen hash. */
! if ((connp = ipsec_find_listen_conn(pptr, sel, ipst)) == NULL)
return;
}
ipsec_conn_pol(sel, connp, ppp);
CONN_DEC_REF(connp);
}
static void
! ipsec_sctp_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp,
! ip_stack_t *ipst)
{
conn_t *connp;
uint32_t ports;
uint16_t *pptr = (uint16_t *)&ports;
/*
* Find SCP state in the following order:
* 1.) Connected conns.
* 2.) Listeners.
*
* Even though #2 will be the common case for inbound traffic, only
* following this order insures correctness.
*/
- if (sel->ips_local_port == 0)
- return;
-
/*
! * 0 should be fport, 1 should be lport. SRC is the local one here.
! * See ipsec_construct_inverse_acquire() for details.
*/
pptr[0] = sel->ips_remote_port;
pptr[1] = sel->ips_local_port;
/*
--- 6698,6743 ----
CONN_INC_REF(connp);
mutex_exit(&connfp->connf_lock);
} else {
mutex_exit(&connfp->connf_lock);
! /* Try the listen hash. If found, comes with incremented ref. */
! if ((connp = ipsec_find_listen_conn(sel, ipst)) == NULL)
return;
}
ipsec_conn_pol(sel, connp, ppp);
CONN_DEC_REF(connp);
}
+ /*
+ * Given ipsec_selector_t (connection attributes to form query), ipsec_policy_t
+ * (populate with match), and ip_stack_t (connection state to query) pointers,
+ * call into sctp to find an existing connection and return its policy.
+ */
static void
! ipsec_sctp_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp, ip_stack_t *ipst)
{
conn_t *connp;
uint32_t ports;
uint16_t *pptr = (uint16_t *)&ports;
+ ASSERT(sel->ips_local_port != 0 && *ppp == NULL);
+
/*
* Find SCP state in the following order:
* 1.) Connected conns.
* 2.) Listeners.
*
* Even though #2 will be the common case for inbound traffic, only
* following this order insures correctness.
*/
/*
! * pptr makes an array of port values, 0 for fport, 1 for lport. SRC is
! * the local one here. Connection lookup macros want this instead of
! * selector port members.
*/
pptr[0] = sel->ips_remote_port;
pptr[1] = sel->ips_local_port;
/*
*** 6487,6500 ****
ipsec_conn_pol(sel, connp, ppp);
CONN_DEC_REF(connp);
}
/*
! * Fill in a query for the SPD (in "sel") using two PF_KEY address extensions.
! * Returns 0 or errno, and always sets *diagnostic to something appropriate
! * to PF_KEY.
! *
* NOTE: For right now, this function (and ipsec_selector_t for that matter),
* ignore prefix lengths in the address extension. Since we match on first-
* entered policies, this shouldn't matter. Also, since we normalize prefix-
* set addresses to mask out the lower bits, we should get a suitable search
* key for the SPD anyway. This is the function to change if the assumption
--- 6762,6774 ----
ipsec_conn_pol(sel, connp, ppp);
CONN_DEC_REF(connp);
}
/*
! * Takes ipsec_selector_t (what we're forming), two sadb_address_t (address
! * extentions needed to create selector), and diagnostic (what, if anything,
! * went wrong in PF_KEY terms) pointers, returns int (0 or errno).
* NOTE: For right now, this function (and ipsec_selector_t for that matter),
* ignore prefix lengths in the address extension. Since we match on first-
* entered policies, this shouldn't matter. Also, since we normalize prefix-
* set addresses to mask out the lower bits, we should get a suitable search
* key for the SPD anyway. This is the function to change if the assumption
*** 6546,6611 ****
}
return (0);
}
/*
! * We have encapsulation.
! * - Lookup tun_t by address and look for an associated
! * tunnel policy
! * - If there are inner selectors
! * - check ITPF_P_TUNNEL and ITPF_P_ACTIVE
! * - Look up tunnel policy based on selectors
! * - Else
! * - Sanity check the negotation
! * - If appropriate, fall through to global policy
*/
static int
ipsec_tun_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp,
sadb_address_t *innsrcext, sadb_address_t *inndstext, ipsec_tun_pol_t *itp,
int *diagnostic)
{
int err;
ipsec_policy_head_t *polhead;
*diagnostic = 0;
/* Check for inner selectors and act appropriately */
-
if (innsrcext != NULL) {
! /* Inner selectors present */
! ASSERT(inndstext != NULL);
if ((itp == NULL) ||
(itp->itp_flags & (ITPF_P_ACTIVE | ITPF_P_TUNNEL)) !=
(ITPF_P_ACTIVE | ITPF_P_TUNNEL)) {
- /*
- * If inner packet selectors, we must have negotiate
- * tunnel and active policy. If the tunnel has
- * transport-mode policy set on it, or has no policy,
- * fail.
- */
return (ENOENT);
} else {
/*
! * Reset "sel" to indicate inner selectors. Pass
! * inner PF_KEY address extensions for this to happen.
*/
if ((err = ipsec_get_inverse_acquire_sel(sel,
innsrcext, inndstext, diagnostic)) != 0)
return (err);
- /*
- * Now look for a tunnel policy based on those inner
- * selectors. (Common code is below.)
- */
}
! } else {
! /* No inner selectors present */
! if ((itp == NULL) || !(itp->itp_flags & ITPF_P_ACTIVE)) {
/*
! * Transport mode negotiation with no tunnel policy
! * configured - return to indicate a global policy
! * check is needed.
*/
return (0);
} else if (itp->itp_flags & ITPF_P_TUNNEL) {
/* Tunnel mode set with no inner selectors. */
return (ENOENT);
}
--- 6820,6877 ----
}
return (0);
}
/*
! * We're passed pointers to ipsec_selector (inner info needed to form query),
! * ipsec_policy_t (what we're trying to populate), a pair of sadb_address_t
! * (extentions needed to reset selector), ipsec_tun_pol_t (tunnel policy that
! * may already be populated from previous SPD query), and integer (error detail
! * in PF_KEY2 terms, always 0). Return 0 or errno.
! * Caller may have fudged inner selector, so we need to reset it via if we have
! * to reuse it.
*/
static int
ipsec_tun_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp,
sadb_address_t *innsrcext, sadb_address_t *inndstext, ipsec_tun_pol_t *itp,
int *diagnostic)
{
int err;
ipsec_policy_head_t *polhead;
+ ASSERT(*ppp == NULL);
+
*diagnostic = 0;
/* Check for inner selectors and act appropriately */
if (innsrcext != NULL) {
! ASSERT(inndstext != NULL); /* Need a pair */
! /*
! * If inner packet selectors, we must have negotiated tunnel and
! * active policy already. If the tunnel has transport-mode
! * policy set on it or no policy at all, fail.
! */
if ((itp == NULL) ||
(itp->itp_flags & (ITPF_P_ACTIVE | ITPF_P_TUNNEL)) !=
(ITPF_P_ACTIVE | ITPF_P_TUNNEL)) {
return (ENOENT);
} else {
/*
! * If we got a sane policy back from the SPD, reset the
! * possibly fudged selector for subsequent operations.
*/
if ((err = ipsec_get_inverse_acquire_sel(sel,
innsrcext, inndstext, diagnostic)) != 0)
return (err);
}
! } else { /* No inner selectors present */
!
/*
! * Transport mode negotiation with no tunnel policy configured
! * - return to indicate a global policy check is needed.
*/
+ if ((itp == NULL) || !(itp->itp_flags & ITPF_P_ACTIVE)) {
return (0);
} else if (itp->itp_flags & ITPF_P_TUNNEL) {
/* Tunnel mode set with no inner selectors. */
return (ENOENT);
}
*** 6633,6653 ****
return (0);
}
/*
! * For sctp conn_faddr is the primary address, hence this is of limited
! * use for sctp.
*/
static void
ipsec_oth_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp,
ip_stack_t *ipst)
{
boolean_t isv4 = sel->ips_isv4;
connf_t *connfp;
conn_t *connp;
if (isv4) {
connfp = &ipst->ips_ipcl_proto_fanout_v4[sel->ips_protocol];
} else {
connfp = &ipst->ips_ipcl_proto_fanout_v6[sel->ips_protocol];
}
--- 6899,6925 ----
return (0);
}
/*
! * Takes ipsec_selector_t (data to form query), ipsec_policy_t (what we need
! * to populate), and ip_stack_t (contains state data to query) pointers. This is
! * a generic protocol look-up function to find a relevant connection that can be
! * converted in a policy.
! * XXX For sctp conn_faddr is the primary address, hence this is of limited
! * use for sctp. Do we care, given sctp has its own lookup?
*/
static void
ipsec_oth_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp,
ip_stack_t *ipst)
{
boolean_t isv4 = sel->ips_isv4;
connf_t *connfp;
conn_t *connp;
+ ASSERT(*ppp == NULL);
+
if (isv4) {
connfp = &ipst->ips_ipcl_proto_fanout_v4[sel->ips_protocol];
} else {
connfp = &ipst->ips_ipcl_proto_fanout_v6[sel->ips_protocol];
}
*** 6682,6704 ****
ipsec_conn_pol(sel, connp, ppp);
CONN_DEC_REF(connp);
}
/*
! * Construct an inverse ACQUIRE reply based on:
*
- * 1.) Current global policy.
- * 2.) An conn_t match depending on what all was passed in the extv[].
- * 3.) A tunnel's policy head.
- * ...
- * N.) Other stuff TBD (e.g. identities)
- *
- * If there is an error, set sadb_msg_errno and sadb_x_msg_diagnostic
- * in this function so the caller can extract them where appropriately.
- *
- * The SRC address is the local one - just like an outbound ACQUIRE message.
- *
* XXX MLS: key management supplies a label which we just reflect back up
* again. clearly we need to involve the label in the rest of the checks.
*/
mblk_t *
ipsec_construct_inverse_acquire(sadb_msg_t *samsg, sadb_ext_t *extv[],
--- 6954,6973 ----
ipsec_conn_pol(sel, connp, ppp);
CONN_DEC_REF(connp);
}
/*
! * This code is called from keysock to handle inverse acquire messages. We
! * are passed a pointer to sadb_msg_t, a fixed-size array of sadb_ext_t, and a
! * netstack_t pointer and return a mblk_t pointer, in which we attempt to
! * construct a return acquire message. In case of errors, we return a NULL
! * pointer and populate samsg->sadb_msg_errno and samsg->sadb_msg_diagnostic,
! * which is handled as an error at the keysock layer. Otherwise keysock does a
! * passup with our message.
! * Caller performs basic sanity checks such as NULL external addresses and
! * only one of two inner addrs being NULL. Remaining checks happen here.
*
* XXX MLS: key management supplies a label which we just reflect back up
* again. clearly we need to involve the label in the rest of the checks.
*/
mblk_t *
ipsec_construct_inverse_acquire(sadb_msg_t *samsg, sadb_ext_t *extv[],
*** 6714,6727 ****
struct sockaddr_in6 *src, *dst;
struct sockaddr_in6 *isrc, *idst;
ipsec_tun_pol_t *itp = NULL;
ipsec_policy_t *pp = NULL;
ipsec_selector_t sel, isel;
! mblk_t *retmp = NULL;
ip_stack_t *ipst = ns->netstack_ip;
-
/* Normalize addresses */
if (sadb_addrcheck(NULL, (mblk_t *)samsg, (sadb_ext_t *)srcext, 0, ns)
== KS_IN_ADDR_UNKNOWN) {
err = EINVAL;
diagnostic = SADB_X_DIAGNOSTIC_BAD_SRC;
--- 6983,6998 ----
struct sockaddr_in6 *src, *dst;
struct sockaddr_in6 *isrc, *idst;
ipsec_tun_pol_t *itp = NULL;
ipsec_policy_t *pp = NULL;
ipsec_selector_t sel, isel;
! mblk_t *retmp;
ip_stack_t *ipst = ns->netstack_ip;
+ sadb_msg_t *retmsg;
+ ipsec_action_t *ap;
+ boolean_t tunnel_mode = B_FALSE;
/* Normalize addresses */
if (sadb_addrcheck(NULL, (mblk_t *)samsg, (sadb_ext_t *)srcext, 0, ns)
== KS_IN_ADDR_UNKNOWN) {
err = EINVAL;
diagnostic = SADB_X_DIAGNOSTIC_BAD_SRC;
*** 6740,6755 ****
diagnostic = SADB_X_DIAGNOSTIC_AF_MISMATCH;
goto bail;
}
/* Check for tunnel mode and act appropriately */
if (innsrcext != NULL) {
! if (inndstext == NULL) {
! err = EINVAL;
! diagnostic = SADB_X_DIAGNOSTIC_MISSING_INNER_DST;
! goto bail;
! }
if (sadb_addrcheck(NULL, (mblk_t *)samsg,
(sadb_ext_t *)innsrcext, 0, ns) == KS_IN_ADDR_UNKNOWN) {
err = EINVAL;
diagnostic = SADB_X_DIAGNOSTIC_MALFORMED_INNER_SRC;
goto bail;
--- 7011,7026 ----
diagnostic = SADB_X_DIAGNOSTIC_AF_MISMATCH;
goto bail;
}
/* Check for tunnel mode and act appropriately */
+ /*
+ * Note: keysock_inverse_acquire catches unbalanced extensions and
+ * makes them into keysock_error calls, so ASSERTs here to confirm.
+ */
if (innsrcext != NULL) {
! ASSERT(inndstext != NULL);
if (sadb_addrcheck(NULL, (mblk_t *)samsg,
(sadb_ext_t *)innsrcext, 0, ns) == KS_IN_ADDR_UNKNOWN) {
err = EINVAL;
diagnostic = SADB_X_DIAGNOSTIC_MALFORMED_INNER_SRC;
goto bail;
*** 6771,6793 ****
isrc->sin6_family != AF_INET6) {
err = EINVAL;
diagnostic = SADB_X_DIAGNOSTIC_BAD_INNER_SRC_AF;
goto bail;
}
! } else if (inndstext != NULL) {
! err = EINVAL;
! diagnostic = SADB_X_DIAGNOSTIC_MISSING_INNER_SRC;
! goto bail;
! }
! /* Get selectors first, based on outer addresses */
err = ipsec_get_inverse_acquire_sel(&sel, srcext, dstext, &diagnostic);
if (err != 0)
goto bail;
! /* Check for tunnel mode mismatches. */
! if (innsrcext != NULL &&
((isrc->sin6_family == AF_INET &&
sel.ips_protocol != IPPROTO_ENCAP && sel.ips_protocol != 0) ||
(isrc->sin6_family == AF_INET6 &&
sel.ips_protocol != IPPROTO_IPV6 && sel.ips_protocol != 0))) {
err = EPROTOTYPE;
--- 7042,7062 ----
isrc->sin6_family != AF_INET6) {
err = EINVAL;
diagnostic = SADB_X_DIAGNOSTIC_BAD_INNER_SRC_AF;
goto bail;
}
! tunnel_mode = B_TRUE;
! } else
! ASSERT(inndstext == NULL);
! /* Convert address extensions into outer selector */
err = ipsec_get_inverse_acquire_sel(&sel, srcext, dstext, &diagnostic);
if (err != 0)
goto bail;
! /* Sanity-check newfound outer selector for tunnel mode mismatches */
! if (tunnel_mode &&
((isrc->sin6_family == AF_INET &&
sel.ips_protocol != IPPROTO_ENCAP && sel.ips_protocol != 0) ||
(isrc->sin6_family == AF_INET6 &&
sel.ips_protocol != IPPROTO_IPV6 && sel.ips_protocol != 0))) {
err = EPROTOTYPE;
*** 6794,6806 ****
goto bail;
}
/*
* Okay, we have the addresses and other selector information.
! * Let's first find a conn...
*/
- pp = NULL;
switch (sel.ips_protocol) {
case IPPROTO_TCP:
ipsec_tcp_pol(&sel, &pp, ipst);
break;
case IPPROTO_UDP:
--- 7063,7077 ----
goto bail;
}
/*
* Okay, we have the addresses and other selector information.
! * If our selector is for a protocol on top of IP, we make protocol-
! * specific queries that work through useful state (e.g. connections or
! * listeners). If we get something back, a reference to it will already
! * be held, and we need to release that reference.
*/
switch (sel.ips_protocol) {
case IPPROTO_TCP:
ipsec_tcp_pol(&sel, &pp, ipst);
break;
case IPPROTO_UDP:
*** 6810,6821 ****
ipsec_sctp_pol(&sel, &pp, ipst);
break;
case IPPROTO_ENCAP:
case IPPROTO_IPV6:
/*
! * Assume sel.ips_remote_addr_* has the right address at
! * that exact position.
*/
itp = itp_get_byaddr((uint32_t *)(&sel.ips_local_addr_v6),
(uint32_t *)(&sel.ips_remote_addr_v6), src->sin6_family,
ipst);
--- 7081,7094 ----
ipsec_sctp_pol(&sel, &pp, ipst);
break;
case IPPROTO_ENCAP:
case IPPROTO_IPV6:
/*
! * These cases are IPv6 in IP or IP in IP. Revert to querying
! * SPD for tunnel policy, since there's no higher-level protocol
! * or stack state to assist. Assume sel.ips_remote_addr_* has
! * right address at exact position.
*/
itp = itp_get_byaddr((uint32_t *)(&sel.ips_local_addr_v6),
(uint32_t *)(&sel.ips_remote_addr_v6), src->sin6_family,
ipst);
*** 6824,6833 ****
--- 7097,7107 ----
* Transport-mode tunnel, make sure we fake out isel
* to contain something based on the outer protocol.
*/
bzero(&isel, sizeof (isel));
isel.ips_isv4 = (sel.ips_protocol == IPPROTO_ENCAP);
+ /* XXX does this make tunnel_mode true? */
} /* Else isel is initialized by ipsec_tun_pol(). */
err = ipsec_tun_pol(&isel, &pp, innsrcext, inndstext, itp,
&diagnostic);
/*
* NOTE: isel isn't used for now, but in RFC 430x IPsec, it
*** 6834,6851 ****
* may be.
*/
if (err != 0)
goto bail;
break;
! default:
ipsec_oth_pol(&sel, &pp, ipst);
break;
}
/*
! * If we didn't find a matching conn_t or other policy head, take a
! * look in the global policy.
*/
if (pp == NULL) {
pp = ipsec_find_policy(IPSEC_TYPE_OUTBOUND, NULL, &sel, ns);
if (pp == NULL) {
/* There's no global policy. */
--- 7108,7125 ----
* may be.
*/
if (err != 0)
goto bail;
break;
! default: /* Fall through to generic lookup */
ipsec_oth_pol(&sel, &pp, ipst);
break;
}
/*
! * If we didn't find a matching conn_t or other policy head (pp retains
! * initial NULL value), attempt to revert to the global policy.
*/
if (pp == NULL) {
pp = ipsec_find_policy(IPSEC_TYPE_OUTBOUND, NULL, &sel, ns);
if (pp == NULL) {
/* There's no global policy. */
*** 6858,6880 ****
/*
* Now that we have a policy entry/widget, construct an ACQUIRE
* message based on that, fix fields where appropriate,
* and return the message.
*/
! retmp = sadb_extended_acquire(&sel, pp, NULL,
! (itp != NULL && (itp->itp_flags & ITPF_P_TUNNEL)),
! samsg->sadb_msg_seq, samsg->sadb_msg_pid, sens, ns);
! if (pp != NULL) {
IPPOL_REFRELE(pp);
! }
ASSERT(err == 0 && diagnostic == 0);
- if (retmp == NULL)
err = ENOMEM;
bail:
if (itp != NULL) {
ITP_REFRELE(itp, ns);
}
samsg->sadb_msg_errno = (uint8_t)err;
samsg->sadb_x_msg_diagnostic = (uint16_t)diagnostic;
return (retmp);
}
--- 7132,7178 ----
/*
* Now that we have a policy entry/widget, construct an ACQUIRE
* message based on that, fix fields where appropriate,
* and return the message.
*/
! ap = pp->ipsp_act;
! ASSERT(ap != NULL);
!
! if (ap != NULL)
! IPACT_REFHOLD(ap);
!
! retmp = sadb_construct_acqmsg(NULL, &sel, ap, pp, ns, sens, 0,
! tunnel_mode, B_TRUE, B_TRUE);
! if (retmp == NULL)
! goto nomem_bail;
!
! retmsg = (sadb_msg_t *)retmp->b_rptr;
! retmsg->sadb_msg_seq = samsg->sadb_msg_seq;
! retmsg->sadb_msg_pid = samsg->sadb_msg_pid;
!
! if (pp != NULL)
IPPOL_REFRELE(pp);
! if (ap != NULL)
! IPACT_REFRELE(ap);
!
! return (retmp);
!
! nomem_bail:
! if (pp != NULL)
! IPPOL_REFRELE(pp);
! if (ap != NULL)
! IPACT_REFRELE(ap);
ASSERT(err == 0 && diagnostic == 0);
err = ENOMEM;
bail:
if (itp != NULL) {
ITP_REFRELE(itp, ns);
}
+ /*
+ * Write error info into original message, as we may not have resources
+ * for a proper reply.
+ */
samsg->sadb_msg_errno = (uint8_t)err;
samsg->sadb_x_msg_diagnostic = (uint16_t)diagnostic;
return (retmp);
}
*** 7188,7198 ****
crypto_key_t *key;
crypto_ctx_template_t *sa_tmpl;
int rv;
ipsec_stack_t *ipss = sa->ipsa_netstack->netstack_ipsec;
! ASSERT(MUTEX_HELD(&ipss->ipsec_alg_lock));
ASSERT(MUTEX_HELD(&sa->ipsa_lock));
/* get pointers to the algorithm info, context template, and key */
switch (alg_type) {
case IPSEC_ALG_AUTH:
--- 7486,7496 ----
crypto_key_t *key;
crypto_ctx_template_t *sa_tmpl;
int rv;
ipsec_stack_t *ipss = sa->ipsa_netstack->netstack_ipsec;
! ASSERT(RW_READ_HELD(&ipss->ipsec_alg_lock));
ASSERT(MUTEX_HELD(&sa->ipsa_lock));
/* get pointers to the algorithm info, context template, and key */
switch (alg_type) {
case IPSEC_ALG_AUTH: