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: