17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5bffb04cfSmarkfen * Common Development and Distribution License (the "License"). 6bffb04cfSmarkfen * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22*930af642SDan McDonald * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #include <sys/types.h> 277c478bd9Sstevel@tonic-gate #include <sys/stream.h> 285d3b8cb7SBill Sommerfeld #include <sys/strsubr.h> 297c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 308810c16bSdanmcd #include <sys/ddi.h> 317c478bd9Sstevel@tonic-gate #include <sys/strlog.h> 327c478bd9Sstevel@tonic-gate 337c478bd9Sstevel@tonic-gate #include <inet/common.h> 347c478bd9Sstevel@tonic-gate #include <inet/mib2.h> 357c478bd9Sstevel@tonic-gate #include <inet/ip.h> 367c478bd9Sstevel@tonic-gate #include <inet/ip6.h> 377c478bd9Sstevel@tonic-gate 387c478bd9Sstevel@tonic-gate #include <net/pfkeyv2.h> 397c478bd9Sstevel@tonic-gate #include <inet/sadb.h> 407c478bd9Sstevel@tonic-gate #include <inet/ipsec_impl.h> 41f4b3ec61Sdh155122 #include <inet/ipdrop.h> 427c478bd9Sstevel@tonic-gate #include <inet/ipsecesp.h> 437c478bd9Sstevel@tonic-gate #include <inet/ipsecah.h> 447c478bd9Sstevel@tonic-gate #include <sys/kstat.h> 457c478bd9Sstevel@tonic-gate 467c478bd9Sstevel@tonic-gate /* 477c478bd9Sstevel@tonic-gate * Returns B_TRUE if the identities in the SA match the identities 487c478bd9Sstevel@tonic-gate * in the "latch" structure. 497c478bd9Sstevel@tonic-gate */ 507c478bd9Sstevel@tonic-gate 517c478bd9Sstevel@tonic-gate static boolean_t 527c478bd9Sstevel@tonic-gate ipsec_match_outbound_ids(ipsec_latch_t *ipl, ipsa_t *sa) 537c478bd9Sstevel@tonic-gate { 547c478bd9Sstevel@tonic-gate ASSERT(ipl->ipl_ids_latched == B_TRUE); 557c478bd9Sstevel@tonic-gate return ipsid_equal(ipl->ipl_local_cid, sa->ipsa_src_cid) && 567c478bd9Sstevel@tonic-gate ipsid_equal(ipl->ipl_remote_cid, sa->ipsa_dst_cid); 577c478bd9Sstevel@tonic-gate } 587c478bd9Sstevel@tonic-gate 59bd670b35SErik Nordmark /* l1 is packet label; l2 is SA label */ 605d3b8cb7SBill Sommerfeld boolean_t 61bd670b35SErik Nordmark ipsec_label_match(ts_label_t *l1, ts_label_t *l2) 625d3b8cb7SBill Sommerfeld { 635d3b8cb7SBill Sommerfeld if (!is_system_labeled()) 645d3b8cb7SBill Sommerfeld return (B_TRUE); 655d3b8cb7SBill Sommerfeld 665d3b8cb7SBill Sommerfeld /* 67bd670b35SErik Nordmark * Check for NULL label. Unlabeled SA (l2) always matches; 685d3b8cb7SBill Sommerfeld * unlabeled user with labeled SA always fails 695d3b8cb7SBill Sommerfeld */ 70bd670b35SErik Nordmark if (l2 == NULL) 715d3b8cb7SBill Sommerfeld return (B_TRUE); 725d3b8cb7SBill Sommerfeld 735d3b8cb7SBill Sommerfeld if (l1 == NULL) 745d3b8cb7SBill Sommerfeld return (B_FALSE); 755d3b8cb7SBill Sommerfeld 765d3b8cb7SBill Sommerfeld /* Simple IPsec MLS policy: labels must be equal */ 775d3b8cb7SBill Sommerfeld /* In future will need bit in policy saying whether this is the case */ 785d3b8cb7SBill Sommerfeld 795d3b8cb7SBill Sommerfeld /* 805d3b8cb7SBill Sommerfeld * label_equal() checks DOI and label contents. We should be 815d3b8cb7SBill Sommerfeld * good to go with this check. 825d3b8cb7SBill Sommerfeld */ 835d3b8cb7SBill Sommerfeld return (label_equal(l1, l2)); 845d3b8cb7SBill Sommerfeld } 855d3b8cb7SBill Sommerfeld 865d3b8cb7SBill Sommerfeld 877c478bd9Sstevel@tonic-gate /* 887c478bd9Sstevel@tonic-gate * Look up a security association based on the unique ID generated by IP and 898810c16bSdanmcd * transport or tunnel information, such as ports and upper-layer protocol, 908810c16bSdanmcd * and the inner and outer address(es). Used for uniqueness testing and 918810c16bSdanmcd * outbound packets. The outer source address may be ignored. 927c478bd9Sstevel@tonic-gate * 937c478bd9Sstevel@tonic-gate * I expect an SA hash bucket, and that its per-bucket mutex is held. 947c478bd9Sstevel@tonic-gate * The SA ptr I return will have its reference count incremented by one. 957c478bd9Sstevel@tonic-gate */ 967c478bd9Sstevel@tonic-gate ipsa_t * 97bd670b35SErik Nordmark ipsec_getassocbyconn(isaf_t *bucket, ip_xmit_attr_t *ixa, uint32_t *src, 98bd670b35SErik Nordmark uint32_t *dst, sa_family_t af, uint8_t protocol, ts_label_t *tsl) 997c478bd9Sstevel@tonic-gate { 1007c478bd9Sstevel@tonic-gate ipsa_t *retval, *candidate; 1017c478bd9Sstevel@tonic-gate ipsec_action_t *candact; 1027c478bd9Sstevel@tonic-gate boolean_t need_unique; 103bd670b35SErik Nordmark boolean_t tunnel_mode = (ixa->ixa_flags & IXAF_IPSEC_TUNNEL); 1047c478bd9Sstevel@tonic-gate uint64_t unique_id; 1057c478bd9Sstevel@tonic-gate uint32_t old_flags, excludeflags; 106bd670b35SErik Nordmark ipsec_policy_t *pp = ixa->ixa_ipsec_policy; 107bd670b35SErik Nordmark ipsec_action_t *actlist = ixa->ixa_ipsec_action; 1087c478bd9Sstevel@tonic-gate ipsec_action_t *act; 109bd670b35SErik Nordmark ipsec_latch_t *ipl = ixa->ixa_ipsec_latch; 1107c478bd9Sstevel@tonic-gate ipsa_ref_t *ipr = NULL; 111bd670b35SErik Nordmark sa_family_t inaf = ixa->ixa_ipsec_inaf; 112bd670b35SErik Nordmark uint32_t *insrc = ixa->ixa_ipsec_insrc; 113bd670b35SErik Nordmark uint32_t *indst = ixa->ixa_ipsec_indst; 114bd670b35SErik Nordmark uint8_t insrcpfx = ixa->ixa_ipsec_insrcpfx; 115bd670b35SErik Nordmark uint8_t indstpfx = ixa->ixa_ipsec_indstpfx; 1167c478bd9Sstevel@tonic-gate 1177c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&bucket->isaf_lock)); 1187c478bd9Sstevel@tonic-gate 1197c478bd9Sstevel@tonic-gate /* 120bd670b35SErik Nordmark * Caller must set ip_xmit_attr_t structure such that we know 1218810c16bSdanmcd * whether this is tunnel mode or transport mode based on 122bd670b35SErik Nordmark * IXAF_IPSEC_TUNNEL. If this flag is set, we assume that 1238810c16bSdanmcd * there are valid inner src and destination addresses to compare. 1248810c16bSdanmcd */ 1258810c16bSdanmcd 1268810c16bSdanmcd /* 1277c478bd9Sstevel@tonic-gate * Fast path: do we have a latch structure, is it for this bucket, 1287c478bd9Sstevel@tonic-gate * and does the generation number match? If so, refhold and return. 1297c478bd9Sstevel@tonic-gate */ 1307c478bd9Sstevel@tonic-gate 1317c478bd9Sstevel@tonic-gate if (ipl != NULL) { 1327c478bd9Sstevel@tonic-gate ASSERT((protocol == IPPROTO_AH) || (protocol == IPPROTO_ESP)); 133bd670b35SErik Nordmark ipr = &ixa->ixa_ipsec_ref[protocol - IPPROTO_ESP]; 1347c478bd9Sstevel@tonic-gate 1357c478bd9Sstevel@tonic-gate retval = ipr->ipsr_sa; 1367c478bd9Sstevel@tonic-gate 1377c478bd9Sstevel@tonic-gate /* 1387c478bd9Sstevel@tonic-gate * NOTE: The isaf_gen check (incremented upon 1397c478bd9Sstevel@tonic-gate * sadb_unlinkassoc()) protects against retval being a freed 1407c478bd9Sstevel@tonic-gate * SA. (We're exploiting short-circuit evaluation.) 1417c478bd9Sstevel@tonic-gate */ 1427c478bd9Sstevel@tonic-gate if ((bucket == ipr->ipsr_bucket) && 1437c478bd9Sstevel@tonic-gate (bucket->isaf_gen == ipr->ipsr_gen) && 1447c478bd9Sstevel@tonic-gate (retval->ipsa_state != IPSA_STATE_DEAD) && 1457c478bd9Sstevel@tonic-gate !(retval->ipsa_flags & IPSA_F_CINVALID)) { 1467c478bd9Sstevel@tonic-gate IPSA_REFHOLD(retval); 1477c478bd9Sstevel@tonic-gate return (retval); 1487c478bd9Sstevel@tonic-gate } 1497c478bd9Sstevel@tonic-gate } 1507c478bd9Sstevel@tonic-gate 1517c478bd9Sstevel@tonic-gate ASSERT((pp != NULL) || (actlist != NULL)); 1527c478bd9Sstevel@tonic-gate if (actlist == NULL) 1537c478bd9Sstevel@tonic-gate actlist = pp->ipsp_act; 1547c478bd9Sstevel@tonic-gate ASSERT(actlist != NULL); 1557c478bd9Sstevel@tonic-gate 1567c478bd9Sstevel@tonic-gate need_unique = actlist->ipa_want_unique; 157bd670b35SErik Nordmark unique_id = SA_FORM_UNIQUE_ID(ixa); 1587c478bd9Sstevel@tonic-gate 1597c478bd9Sstevel@tonic-gate /* 1607c478bd9Sstevel@tonic-gate * Precompute mask for SA flags comparison: If we need a 1617c478bd9Sstevel@tonic-gate * unique SA and an SA has already been used, or if the SA has 1627c478bd9Sstevel@tonic-gate * a unique value which doesn't match, we aren't interested in 1637c478bd9Sstevel@tonic-gate * the SA.. 1647c478bd9Sstevel@tonic-gate */ 1657c478bd9Sstevel@tonic-gate 1667c478bd9Sstevel@tonic-gate excludeflags = IPSA_F_UNIQUE; 1677c478bd9Sstevel@tonic-gate if (need_unique) 1687c478bd9Sstevel@tonic-gate excludeflags |= IPSA_F_USED; 1697c478bd9Sstevel@tonic-gate 1707c478bd9Sstevel@tonic-gate /* 1717c478bd9Sstevel@tonic-gate * Walk the hash bucket, matching on: 1727c478bd9Sstevel@tonic-gate * 1737c478bd9Sstevel@tonic-gate * - unique_id 1747c478bd9Sstevel@tonic-gate * - destination 1757c478bd9Sstevel@tonic-gate * - source 1767c478bd9Sstevel@tonic-gate * - algorithms 1778810c16bSdanmcd * - inner dst 1788810c16bSdanmcd * - inner src 1797c478bd9Sstevel@tonic-gate * - <MORE TBD> 1807c478bd9Sstevel@tonic-gate * 1817c478bd9Sstevel@tonic-gate * Make sure that wildcard sources are inserted at the end of the hash 1827c478bd9Sstevel@tonic-gate * bucket. 1837c478bd9Sstevel@tonic-gate * 1847c478bd9Sstevel@tonic-gate * DEFINITIONS: A _shared_ SA is one with unique_id == 0 and USED. 1857c478bd9Sstevel@tonic-gate * An _unused_ SA is one with unique_id == 0 and not USED. 1867c478bd9Sstevel@tonic-gate * A _unique_ SA is one with unique_id != 0 and USED. 1877c478bd9Sstevel@tonic-gate * An SA with unique_id != 0 and not USED never happens. 1887c478bd9Sstevel@tonic-gate */ 1897c478bd9Sstevel@tonic-gate 1907c478bd9Sstevel@tonic-gate candidate = NULL; 1917c478bd9Sstevel@tonic-gate 1927c478bd9Sstevel@tonic-gate for (retval = bucket->isaf_ipsa; retval != NULL; 1937c478bd9Sstevel@tonic-gate retval = retval->ipsa_next) { 1947c478bd9Sstevel@tonic-gate ASSERT((candidate == NULL) || 1957c478bd9Sstevel@tonic-gate MUTEX_HELD(&candidate->ipsa_lock)); 1967c478bd9Sstevel@tonic-gate 1977c478bd9Sstevel@tonic-gate /* 1987c478bd9Sstevel@tonic-gate * Q: Should I lock this SA? 1997c478bd9Sstevel@tonic-gate * A: For now, yes. I change and use too many fields in here 2007c478bd9Sstevel@tonic-gate * (e.g. unique_id) that I may be racing with other threads. 2017c478bd9Sstevel@tonic-gate * Also, the refcnt needs to be bumped up. 2027c478bd9Sstevel@tonic-gate */ 2037c478bd9Sstevel@tonic-gate 2047c478bd9Sstevel@tonic-gate mutex_enter(&retval->ipsa_lock); 2057c478bd9Sstevel@tonic-gate 2067c478bd9Sstevel@tonic-gate /* My apologies for the use of goto instead of continue. */ 2078810c16bSdanmcd 2088810c16bSdanmcd /* Outer destination address */ 2097c478bd9Sstevel@tonic-gate if (!IPSA_ARE_ADDR_EQUAL(dst, retval->ipsa_dstaddr, af)) 2107c478bd9Sstevel@tonic-gate goto next_ipsa; /* Destination mismatch. */ 2118810c16bSdanmcd 2128810c16bSdanmcd /* Outer source address */ 2137c478bd9Sstevel@tonic-gate if (!IPSA_ARE_ADDR_EQUAL(src, retval->ipsa_srcaddr, af) && 2147c478bd9Sstevel@tonic-gate !IPSA_IS_ADDR_UNSPEC(retval->ipsa_srcaddr, af)) 2157c478bd9Sstevel@tonic-gate goto next_ipsa; /* Specific source and not matched. */ 2167c478bd9Sstevel@tonic-gate 2178810c16bSdanmcd if (tunnel_mode) { 2188810c16bSdanmcd /* Check tunnel mode */ 2198810c16bSdanmcd if (!(retval->ipsa_flags & IPSA_F_TUNNEL)) 2208810c16bSdanmcd goto next_ipsa; /* Not tunnel mode SA */ 2218810c16bSdanmcd 2228810c16bSdanmcd /* Inner destination address */ 2238810c16bSdanmcd if (!IPSA_IS_ADDR_UNSPEC(retval->ipsa_innerdst, inaf)) { 2248810c16bSdanmcd if (!ip_addr_match((uint8_t *)indst, 2258810c16bSdanmcd min(indstpfx, retval->ipsa_innerdstpfx), 2268810c16bSdanmcd (in6_addr_t *)retval->ipsa_innerdst)) 2278810c16bSdanmcd goto next_ipsa; /* not matched. */ 2288810c16bSdanmcd } 2298810c16bSdanmcd 2308810c16bSdanmcd /* Inner source address */ 2318810c16bSdanmcd if (!IPSA_IS_ADDR_UNSPEC(retval->ipsa_innersrc, inaf)) { 2328810c16bSdanmcd if (!ip_addr_match((uint8_t *)insrc, 2338810c16bSdanmcd min(insrcpfx, retval->ipsa_innersrcpfx), 2348810c16bSdanmcd (in6_addr_t *)retval->ipsa_innersrc)) 2358810c16bSdanmcd goto next_ipsa; /* not matched. */ 2368810c16bSdanmcd } 2378810c16bSdanmcd } else { 2388810c16bSdanmcd /* Check transport mode */ 2398810c16bSdanmcd if (retval->ipsa_flags & IPSA_F_TUNNEL) 2408810c16bSdanmcd goto next_ipsa; /* Not transport mode SA */ 2418810c16bSdanmcd 2428810c16bSdanmcd /* 2438810c16bSdanmcd * TODO - If we ever do RFC 3884's dream of transport- 2448810c16bSdanmcd * mode SAs with inner IP address selectors, we need 2458810c16bSdanmcd * to put some code here. 2468810c16bSdanmcd */ 2478810c16bSdanmcd } 2488810c16bSdanmcd 2497c478bd9Sstevel@tonic-gate /* 2507c478bd9Sstevel@tonic-gate * XXX should be able to use cached/latched action 2517c478bd9Sstevel@tonic-gate * to dodge this loop 2527c478bd9Sstevel@tonic-gate */ 2537c478bd9Sstevel@tonic-gate for (act = actlist; act != NULL; act = act->ipa_next) { 2547c478bd9Sstevel@tonic-gate ipsec_act_t *ap = &act->ipa_act; 2557c478bd9Sstevel@tonic-gate if (ap->ipa_type != IPSEC_POLICY_APPLY) 2567c478bd9Sstevel@tonic-gate continue; 2577c478bd9Sstevel@tonic-gate 2587c478bd9Sstevel@tonic-gate /* 2597c478bd9Sstevel@tonic-gate * XXX ugly. should be better way to do this test 2607c478bd9Sstevel@tonic-gate */ 2617c478bd9Sstevel@tonic-gate if (protocol == IPPROTO_AH) { 2627c478bd9Sstevel@tonic-gate if (!(ap->ipa_apply.ipp_use_ah)) 2637c478bd9Sstevel@tonic-gate continue; 2647c478bd9Sstevel@tonic-gate if (ap->ipa_apply.ipp_auth_alg != 2657c478bd9Sstevel@tonic-gate retval->ipsa_auth_alg) 2667c478bd9Sstevel@tonic-gate continue; 2677c478bd9Sstevel@tonic-gate if (ap->ipa_apply.ipp_ah_minbits > 2687c478bd9Sstevel@tonic-gate retval->ipsa_authkeybits) 2697c478bd9Sstevel@tonic-gate continue; 2707c478bd9Sstevel@tonic-gate } else { 2717c478bd9Sstevel@tonic-gate if (!(ap->ipa_apply.ipp_use_esp)) 2727c478bd9Sstevel@tonic-gate continue; 2737c478bd9Sstevel@tonic-gate 2747c478bd9Sstevel@tonic-gate if ((ap->ipa_apply.ipp_encr_alg != 2757c478bd9Sstevel@tonic-gate retval->ipsa_encr_alg)) 2767c478bd9Sstevel@tonic-gate continue; 2777c478bd9Sstevel@tonic-gate 2787c478bd9Sstevel@tonic-gate if (ap->ipa_apply.ipp_espe_minbits > 2797c478bd9Sstevel@tonic-gate retval->ipsa_encrkeybits) 2807c478bd9Sstevel@tonic-gate continue; 2817c478bd9Sstevel@tonic-gate 2827c478bd9Sstevel@tonic-gate if (ap->ipa_apply.ipp_esp_auth_alg != 0) { 2837c478bd9Sstevel@tonic-gate if (ap->ipa_apply.ipp_esp_auth_alg != 2847c478bd9Sstevel@tonic-gate retval->ipsa_auth_alg) 2857c478bd9Sstevel@tonic-gate continue; 2867c478bd9Sstevel@tonic-gate if (ap->ipa_apply.ipp_espa_minbits > 2877c478bd9Sstevel@tonic-gate retval->ipsa_authkeybits) 2887c478bd9Sstevel@tonic-gate continue; 2897c478bd9Sstevel@tonic-gate } 2907c478bd9Sstevel@tonic-gate } 2917c478bd9Sstevel@tonic-gate 2927c478bd9Sstevel@tonic-gate /* 2937c478bd9Sstevel@tonic-gate * Check key mgmt proto, cookie 2947c478bd9Sstevel@tonic-gate */ 2957c478bd9Sstevel@tonic-gate if ((ap->ipa_apply.ipp_km_proto != 0) && 2967c478bd9Sstevel@tonic-gate (retval->ipsa_kmp != 0) && 2977c478bd9Sstevel@tonic-gate (ap->ipa_apply.ipp_km_proto != retval->ipsa_kmp)) 2987c478bd9Sstevel@tonic-gate continue; 2997c478bd9Sstevel@tonic-gate 3007c478bd9Sstevel@tonic-gate if ((ap->ipa_apply.ipp_km_cookie != 0) && 3017c478bd9Sstevel@tonic-gate (retval->ipsa_kmc != 0) && 3027c478bd9Sstevel@tonic-gate (ap->ipa_apply.ipp_km_cookie != retval->ipsa_kmc)) 3037c478bd9Sstevel@tonic-gate continue; 3047c478bd9Sstevel@tonic-gate 3057c478bd9Sstevel@tonic-gate break; 3067c478bd9Sstevel@tonic-gate } 3077c478bd9Sstevel@tonic-gate if (act == NULL) 3087c478bd9Sstevel@tonic-gate goto next_ipsa; /* nothing matched */ 3097c478bd9Sstevel@tonic-gate 3107c478bd9Sstevel@tonic-gate /* 3117c478bd9Sstevel@tonic-gate * Do identities match? 3127c478bd9Sstevel@tonic-gate */ 3137c478bd9Sstevel@tonic-gate if (ipl && ipl->ipl_ids_latched && 3147c478bd9Sstevel@tonic-gate !ipsec_match_outbound_ids(ipl, retval)) 3157c478bd9Sstevel@tonic-gate goto next_ipsa; 3167c478bd9Sstevel@tonic-gate 3177c478bd9Sstevel@tonic-gate /* 3185d3b8cb7SBill Sommerfeld * Do labels match? 3195d3b8cb7SBill Sommerfeld */ 320bd670b35SErik Nordmark if (!ipsec_label_match(tsl, retval->ipsa_tsl)) 3215d3b8cb7SBill Sommerfeld goto next_ipsa; 3225d3b8cb7SBill Sommerfeld 3235d3b8cb7SBill Sommerfeld /* 3247c478bd9Sstevel@tonic-gate * At this point, we know that we have at least a match on: 3257c478bd9Sstevel@tonic-gate * 3267c478bd9Sstevel@tonic-gate * - dest 3277c478bd9Sstevel@tonic-gate * - source (if source is specified, i.e. non-zeroes) 3288810c16bSdanmcd * - inner dest (if specified) 3298810c16bSdanmcd * - inner source (if specified) 3307c478bd9Sstevel@tonic-gate * - auth alg (if auth alg is specified, i.e. non-zero) 3317c478bd9Sstevel@tonic-gate * - encrypt. alg (if encrypt. alg is specified, i.e. non-zero) 3327c478bd9Sstevel@tonic-gate * and we know that the SA keylengths are appropriate. 3337c478bd9Sstevel@tonic-gate * 3347c478bd9Sstevel@tonic-gate * (Keep in mind known-src SAs are hit before zero-src SAs, 3357c478bd9Sstevel@tonic-gate * thanks to sadb_insertassoc().) 3367c478bd9Sstevel@tonic-gate * If we need a unique asssociation, optimally we have 3377c478bd9Sstevel@tonic-gate * ipsa_unique_id == unique_id, otherwise NOT USED 3387c478bd9Sstevel@tonic-gate * is held in reserve (stored in candidate). 3397c478bd9Sstevel@tonic-gate * 3407c478bd9Sstevel@tonic-gate * For those stored in candidate, take best-match (i.e. given 3417c478bd9Sstevel@tonic-gate * a choice, candidate should have non-zero ipsa_src). 3427c478bd9Sstevel@tonic-gate */ 3437c478bd9Sstevel@tonic-gate 3447c478bd9Sstevel@tonic-gate /* 3457c478bd9Sstevel@tonic-gate * If SA has a unique value which matches, we're all set... 3467c478bd9Sstevel@tonic-gate * "key management knows best" 3477c478bd9Sstevel@tonic-gate */ 3487c478bd9Sstevel@tonic-gate if ((retval->ipsa_flags & IPSA_F_UNIQUE) && 3497c478bd9Sstevel@tonic-gate ((unique_id & retval->ipsa_unique_mask) == 3507c478bd9Sstevel@tonic-gate retval->ipsa_unique_id)) 3517c478bd9Sstevel@tonic-gate break; 3527c478bd9Sstevel@tonic-gate 3537c478bd9Sstevel@tonic-gate /* 3547c478bd9Sstevel@tonic-gate * If we need a unique SA and this SA has already been used, 3557c478bd9Sstevel@tonic-gate * or if the SA has a unique value which doesn't match, 3567c478bd9Sstevel@tonic-gate * this isn't for us. 3577c478bd9Sstevel@tonic-gate */ 3587c478bd9Sstevel@tonic-gate 3597c478bd9Sstevel@tonic-gate if (retval->ipsa_flags & excludeflags) 3607c478bd9Sstevel@tonic-gate goto next_ipsa; 3617c478bd9Sstevel@tonic-gate 3627c478bd9Sstevel@tonic-gate 3637c478bd9Sstevel@tonic-gate /* 3647c478bd9Sstevel@tonic-gate * I found a candidate.. 3657c478bd9Sstevel@tonic-gate */ 3667c478bd9Sstevel@tonic-gate if (candidate == NULL) { 3677c478bd9Sstevel@tonic-gate /* 3687c478bd9Sstevel@tonic-gate * and didn't already have one.. 3697c478bd9Sstevel@tonic-gate */ 3707c478bd9Sstevel@tonic-gate candidate = retval; 3717c478bd9Sstevel@tonic-gate candact = act; 3727c478bd9Sstevel@tonic-gate continue; 3737c478bd9Sstevel@tonic-gate } else { 3747c478bd9Sstevel@tonic-gate /* 3757c478bd9Sstevel@tonic-gate * If candidate's source address is zero and 3767c478bd9Sstevel@tonic-gate * the current match (i.e. retval) address is 3777c478bd9Sstevel@tonic-gate * not zero, we have a better candidate.. 3787c478bd9Sstevel@tonic-gate */ 3797c478bd9Sstevel@tonic-gate if (IPSA_IS_ADDR_UNSPEC(candidate->ipsa_srcaddr, af) && 3807c478bd9Sstevel@tonic-gate !IPSA_IS_ADDR_UNSPEC(retval->ipsa_srcaddr, af)) { 3817c478bd9Sstevel@tonic-gate mutex_exit(&candidate->ipsa_lock); 3827c478bd9Sstevel@tonic-gate candidate = retval; 3837c478bd9Sstevel@tonic-gate candact = act; 3847c478bd9Sstevel@tonic-gate continue; 3857c478bd9Sstevel@tonic-gate } 3867c478bd9Sstevel@tonic-gate } 3877c478bd9Sstevel@tonic-gate next_ipsa: 3887c478bd9Sstevel@tonic-gate mutex_exit(&retval->ipsa_lock); 3897c478bd9Sstevel@tonic-gate } 3907c478bd9Sstevel@tonic-gate ASSERT((retval == NULL) || MUTEX_HELD(&retval->ipsa_lock)); 3917c478bd9Sstevel@tonic-gate ASSERT((candidate == NULL) || MUTEX_HELD(&candidate->ipsa_lock)); 3927c478bd9Sstevel@tonic-gate ASSERT((retval == NULL) || (act != NULL)); 3937c478bd9Sstevel@tonic-gate ASSERT((candidate == NULL) || (candact != NULL)); 3947c478bd9Sstevel@tonic-gate 3957c478bd9Sstevel@tonic-gate /* Let caller react to a lookup failure when it gets NULL. */ 3967c478bd9Sstevel@tonic-gate if (retval == NULL && candidate == NULL) 3977c478bd9Sstevel@tonic-gate return (NULL); 3987c478bd9Sstevel@tonic-gate 3997c478bd9Sstevel@tonic-gate if (retval == NULL) { 4007c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&candidate->ipsa_lock)); 4017c478bd9Sstevel@tonic-gate retval = candidate; 4027c478bd9Sstevel@tonic-gate act = candact; 4037c478bd9Sstevel@tonic-gate } else if (candidate != NULL) { 4047c478bd9Sstevel@tonic-gate mutex_exit(&candidate->ipsa_lock); 4057c478bd9Sstevel@tonic-gate } 4067c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&retval->ipsa_lock)); 4077c478bd9Sstevel@tonic-gate ASSERT(act != NULL); 4087c478bd9Sstevel@tonic-gate 4097c478bd9Sstevel@tonic-gate /* 4107c478bd9Sstevel@tonic-gate * Even though I hold the mutex, since the reference counter is an 4117c478bd9Sstevel@tonic-gate * atomic operation, I really have to use the IPSA_REFHOLD macro. 4127c478bd9Sstevel@tonic-gate */ 4137c478bd9Sstevel@tonic-gate IPSA_REFHOLD(retval); 4147c478bd9Sstevel@tonic-gate 4157c478bd9Sstevel@tonic-gate /* 4167c478bd9Sstevel@tonic-gate * This association is no longer unused. 4177c478bd9Sstevel@tonic-gate */ 4187c478bd9Sstevel@tonic-gate old_flags = retval->ipsa_flags; 4197c478bd9Sstevel@tonic-gate retval->ipsa_flags |= IPSA_F_USED; 4207c478bd9Sstevel@tonic-gate 4217c478bd9Sstevel@tonic-gate /* 4227c478bd9Sstevel@tonic-gate * Cache a reference to this SA for the fast path. 4237c478bd9Sstevel@tonic-gate */ 4247c478bd9Sstevel@tonic-gate if (ipr != NULL) { 4257c478bd9Sstevel@tonic-gate ipr->ipsr_bucket = bucket; 4267c478bd9Sstevel@tonic-gate ipr->ipsr_gen = bucket->isaf_gen; 4277c478bd9Sstevel@tonic-gate ipr->ipsr_sa = retval; 4287c478bd9Sstevel@tonic-gate /* I'm now caching, so the cache-invalid flag goes away! */ 4297c478bd9Sstevel@tonic-gate retval->ipsa_flags &= ~IPSA_F_CINVALID; 4307c478bd9Sstevel@tonic-gate } 4317c478bd9Sstevel@tonic-gate /* 4327c478bd9Sstevel@tonic-gate * Latch various things while we're here.. 4337c478bd9Sstevel@tonic-gate */ 4347c478bd9Sstevel@tonic-gate if (ipl != NULL) { 4357c478bd9Sstevel@tonic-gate if (!ipl->ipl_ids_latched) { 4367c478bd9Sstevel@tonic-gate ipsec_latch_ids(ipl, 4377c478bd9Sstevel@tonic-gate retval->ipsa_src_cid, retval->ipsa_dst_cid); 4387c478bd9Sstevel@tonic-gate } 439bd670b35SErik Nordmark if (ixa->ixa_ipsec_action == NULL) { 4407c478bd9Sstevel@tonic-gate IPACT_REFHOLD(act); 441bd670b35SErik Nordmark ixa->ixa_ipsec_action = act; 4427c478bd9Sstevel@tonic-gate } 4437c478bd9Sstevel@tonic-gate } 4447c478bd9Sstevel@tonic-gate 4457c478bd9Sstevel@tonic-gate /* 4467c478bd9Sstevel@tonic-gate * Set the uniqueness only first time. 4477c478bd9Sstevel@tonic-gate */ 4487c478bd9Sstevel@tonic-gate if (need_unique && !(old_flags & IPSA_F_USED)) { 4497c478bd9Sstevel@tonic-gate if (retval->ipsa_unique_id == 0) { 4507c478bd9Sstevel@tonic-gate ASSERT((retval->ipsa_flags & IPSA_F_UNIQUE) == 0); 4517c478bd9Sstevel@tonic-gate /* 4527c478bd9Sstevel@tonic-gate * From now on, only this src, dst[ports, addr], 4537c478bd9Sstevel@tonic-gate * proto, should use it. 4547c478bd9Sstevel@tonic-gate */ 4557c478bd9Sstevel@tonic-gate retval->ipsa_flags |= IPSA_F_UNIQUE; 4567c478bd9Sstevel@tonic-gate retval->ipsa_unique_id = unique_id; 4577c478bd9Sstevel@tonic-gate retval->ipsa_unique_mask = SA_UNIQUE_MASK( 458bd670b35SErik Nordmark ixa->ixa_ipsec_src_port, ixa->ixa_ipsec_dst_port, 4598810c16bSdanmcd protocol, 0); 4607c478bd9Sstevel@tonic-gate } 4617c478bd9Sstevel@tonic-gate 4627c478bd9Sstevel@tonic-gate /* 4637c478bd9Sstevel@tonic-gate * Set the source address and adjust the hash 4647c478bd9Sstevel@tonic-gate * buckets only if src_addr is zero. 4657c478bd9Sstevel@tonic-gate */ 4667c478bd9Sstevel@tonic-gate if (IPSA_IS_ADDR_UNSPEC(retval->ipsa_srcaddr, af)) { 4677c478bd9Sstevel@tonic-gate /* 4687c478bd9Sstevel@tonic-gate * sadb_unlinkassoc() will decrement the refcnt. Bump 4697c478bd9Sstevel@tonic-gate * up when we have the lock so that we don't have to 4707c478bd9Sstevel@tonic-gate * acquire locks when we come back from 4717c478bd9Sstevel@tonic-gate * sadb_insertassoc(). 4727c478bd9Sstevel@tonic-gate * 4737c478bd9Sstevel@tonic-gate * We don't need to bump the bucket's gen since 4747c478bd9Sstevel@tonic-gate * we aren't moving to a new bucket. 4757c478bd9Sstevel@tonic-gate */ 4767c478bd9Sstevel@tonic-gate IPSA_REFHOLD(retval); 4777c478bd9Sstevel@tonic-gate IPSA_COPY_ADDR(retval->ipsa_srcaddr, src, af); 4787c478bd9Sstevel@tonic-gate mutex_exit(&retval->ipsa_lock); 4797c478bd9Sstevel@tonic-gate sadb_unlinkassoc(retval); 4807c478bd9Sstevel@tonic-gate /* 4817c478bd9Sstevel@tonic-gate * Since the bucket lock is held, we know 4827c478bd9Sstevel@tonic-gate * sadb_insertassoc() will succeed. 4837c478bd9Sstevel@tonic-gate */ 4847c478bd9Sstevel@tonic-gate #ifdef DEBUG 4857c478bd9Sstevel@tonic-gate if (sadb_insertassoc(retval, bucket) != 0) { 4867c478bd9Sstevel@tonic-gate cmn_err(CE_PANIC, 4877c478bd9Sstevel@tonic-gate "sadb_insertassoc() failed in " 4887c478bd9Sstevel@tonic-gate "ipsec_getassocbyconn().\n"); 4897c478bd9Sstevel@tonic-gate } 4907c478bd9Sstevel@tonic-gate #else /* non-DEBUG */ 4917c478bd9Sstevel@tonic-gate (void) sadb_insertassoc(retval, bucket); 4927c478bd9Sstevel@tonic-gate #endif /* DEBUG */ 4937c478bd9Sstevel@tonic-gate return (retval); 4947c478bd9Sstevel@tonic-gate } 4957c478bd9Sstevel@tonic-gate } 4967c478bd9Sstevel@tonic-gate mutex_exit(&retval->ipsa_lock); 4977c478bd9Sstevel@tonic-gate 4987c478bd9Sstevel@tonic-gate return (retval); 4997c478bd9Sstevel@tonic-gate } 5007c478bd9Sstevel@tonic-gate 5017c478bd9Sstevel@tonic-gate /* 5027c478bd9Sstevel@tonic-gate * Look up a security association based on the security parameters index (SPI) 5037c478bd9Sstevel@tonic-gate * and address(es). This is used for inbound packets and general SA lookups 5047c478bd9Sstevel@tonic-gate * (even in outbound SA tables). The source address may be ignored. Return 5057c478bd9Sstevel@tonic-gate * NULL if no association is available. If an SA is found, return it, with 5067c478bd9Sstevel@tonic-gate * its refcnt incremented. The caller must REFRELE after using the SA. 5077c478bd9Sstevel@tonic-gate * The hash bucket must be locked down before calling. 5087c478bd9Sstevel@tonic-gate */ 5097c478bd9Sstevel@tonic-gate ipsa_t * 5107c478bd9Sstevel@tonic-gate ipsec_getassocbyspi(isaf_t *bucket, uint32_t spi, uint32_t *src, uint32_t *dst, 5117c478bd9Sstevel@tonic-gate sa_family_t af) 5127c478bd9Sstevel@tonic-gate { 5137c478bd9Sstevel@tonic-gate ipsa_t *retval; 5147c478bd9Sstevel@tonic-gate 5157c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&bucket->isaf_lock)); 5167c478bd9Sstevel@tonic-gate 5177c478bd9Sstevel@tonic-gate /* 5187c478bd9Sstevel@tonic-gate * Walk the hash bucket, matching exactly on SPI, then destination, 5197c478bd9Sstevel@tonic-gate * then source. 5207c478bd9Sstevel@tonic-gate * 5217c478bd9Sstevel@tonic-gate * Per-SA locking doesn't need to happen, because I'm only matching 5227c478bd9Sstevel@tonic-gate * on addresses. Addresses are only changed during insertion/deletion 5237c478bd9Sstevel@tonic-gate * from the hash bucket. Since the hash bucket lock is held, we don't 5247c478bd9Sstevel@tonic-gate * need to worry about addresses changing. 5257c478bd9Sstevel@tonic-gate */ 5267c478bd9Sstevel@tonic-gate 5277c478bd9Sstevel@tonic-gate for (retval = bucket->isaf_ipsa; retval != NULL; 5287c478bd9Sstevel@tonic-gate retval = retval->ipsa_next) { 5297c478bd9Sstevel@tonic-gate if (retval->ipsa_spi != spi) 5307c478bd9Sstevel@tonic-gate continue; 5317c478bd9Sstevel@tonic-gate if (!IPSA_ARE_ADDR_EQUAL(dst, retval->ipsa_dstaddr, af)) 5327c478bd9Sstevel@tonic-gate continue; 5337c478bd9Sstevel@tonic-gate 5347c478bd9Sstevel@tonic-gate /* 5357c478bd9Sstevel@tonic-gate * Assume that wildcard source addresses are inserted at the 5367c478bd9Sstevel@tonic-gate * end of the hash bucket. (See sadb_insertassoc().) 5377c478bd9Sstevel@tonic-gate * The following check for source addresses is a weak form 5387c478bd9Sstevel@tonic-gate * of access control/source identity verification. If an 5397c478bd9Sstevel@tonic-gate * SA has a source address, I only match an all-zeroes 5407c478bd9Sstevel@tonic-gate * source address, or that particular one. If the SA has 5417c478bd9Sstevel@tonic-gate * an all-zeroes source, then I match regardless. 5427c478bd9Sstevel@tonic-gate * 5437c478bd9Sstevel@tonic-gate * There is a weakness here in that a packet with all-zeroes 5447c478bd9Sstevel@tonic-gate * for an address will match regardless of the source address 5457c478bd9Sstevel@tonic-gate * stored in the packet. 54607b56925Ssommerfe * 54707b56925Ssommerfe * Note that port-level packet selectors, if present, 54807b56925Ssommerfe * are checked in ipsec_check_ipsecin_unique(). 5497c478bd9Sstevel@tonic-gate */ 5507c478bd9Sstevel@tonic-gate if (IPSA_ARE_ADDR_EQUAL(src, retval->ipsa_srcaddr, af) || 5517c478bd9Sstevel@tonic-gate IPSA_IS_ADDR_UNSPEC(retval->ipsa_srcaddr, af) || 5527c478bd9Sstevel@tonic-gate IPSA_IS_ADDR_UNSPEC(src, af)) 5537c478bd9Sstevel@tonic-gate break; 5547c478bd9Sstevel@tonic-gate } 5557c478bd9Sstevel@tonic-gate 5567c478bd9Sstevel@tonic-gate if (retval != NULL) { 5577c478bd9Sstevel@tonic-gate /* 5587c478bd9Sstevel@tonic-gate * Just refhold the return value. The caller will then 5597c478bd9Sstevel@tonic-gate * make the appropriate calls to set the USED flag. 5607c478bd9Sstevel@tonic-gate */ 5617c478bd9Sstevel@tonic-gate IPSA_REFHOLD(retval); 5627c478bd9Sstevel@tonic-gate } 5637c478bd9Sstevel@tonic-gate 5647c478bd9Sstevel@tonic-gate return (retval); 5657c478bd9Sstevel@tonic-gate } 5667c478bd9Sstevel@tonic-gate 5677c478bd9Sstevel@tonic-gate boolean_t 568bd670b35SErik Nordmark ipsec_outbound_sa(mblk_t *data_mp, ip_xmit_attr_t *ixa, uint_t proto) 5697c478bd9Sstevel@tonic-gate { 5707c478bd9Sstevel@tonic-gate ipaddr_t dst; 5717c478bd9Sstevel@tonic-gate uint32_t *dst_ptr, *src_ptr; 5727c478bd9Sstevel@tonic-gate isaf_t *bucket; 5737c478bd9Sstevel@tonic-gate ipsa_t *assoc; 574bd670b35SErik Nordmark ip_pkt_t ipp; 5757c478bd9Sstevel@tonic-gate in6_addr_t dst6; 5767c478bd9Sstevel@tonic-gate ipsa_t **sa; 5777c478bd9Sstevel@tonic-gate sadbp_t *sadbp; 578fb87b5d2Ssommerfe sadb_t *sp; 5797c478bd9Sstevel@tonic-gate sa_family_t af; 580bd670b35SErik Nordmark ip_stack_t *ipst = ixa->ixa_ipst; 581bd670b35SErik Nordmark netstack_t *ns = ipst->ips_netstack; 5827c478bd9Sstevel@tonic-gate 583bd670b35SErik Nordmark ASSERT(ixa->ixa_flags & IXAF_IPSEC_SECURE); 5847c478bd9Sstevel@tonic-gate 5857c478bd9Sstevel@tonic-gate if (proto == IPPROTO_ESP) { 586f4b3ec61Sdh155122 ipsecesp_stack_t *espstack; 587f4b3ec61Sdh155122 588f4b3ec61Sdh155122 espstack = ns->netstack_ipsecesp; 589bd670b35SErik Nordmark sa = &ixa->ixa_ipsec_esp_sa; 590f4b3ec61Sdh155122 sadbp = &espstack->esp_sadb; 5917c478bd9Sstevel@tonic-gate } else { 592f4b3ec61Sdh155122 ipsecah_stack_t *ahstack; 593f4b3ec61Sdh155122 5947c478bd9Sstevel@tonic-gate ASSERT(proto == IPPROTO_AH); 595f4b3ec61Sdh155122 ahstack = ns->netstack_ipsecah; 596bd670b35SErik Nordmark sa = &ixa->ixa_ipsec_ah_sa; 597f4b3ec61Sdh155122 sadbp = &ahstack->ah_sadb; 5987c478bd9Sstevel@tonic-gate } 5997c478bd9Sstevel@tonic-gate 6007c478bd9Sstevel@tonic-gate ASSERT(*sa == NULL); 6017c478bd9Sstevel@tonic-gate 602bd670b35SErik Nordmark if (ixa->ixa_flags & IXAF_IS_IPV4) { 6037c478bd9Sstevel@tonic-gate ipha_t *ipha = (ipha_t *)data_mp->b_rptr; 6047c478bd9Sstevel@tonic-gate 6057c478bd9Sstevel@tonic-gate ASSERT(IPH_HDR_VERSION(ipha) == IPV4_VERSION); 6067c478bd9Sstevel@tonic-gate dst = ip_get_dst(ipha); 607fb87b5d2Ssommerfe sp = &sadbp->s_v4; 6087c478bd9Sstevel@tonic-gate af = AF_INET; 6097c478bd9Sstevel@tonic-gate 6107c478bd9Sstevel@tonic-gate /* 6117c478bd9Sstevel@tonic-gate * NOTE:Getting the outbound association is considerably 6127c478bd9Sstevel@tonic-gate * painful. ipsec_getassocbyconn() will require more 6137c478bd9Sstevel@tonic-gate * parameters as policy implementations mature. 6147c478bd9Sstevel@tonic-gate */ 615fb87b5d2Ssommerfe bucket = OUTBOUND_BUCKET_V4(sp, dst); 6167c478bd9Sstevel@tonic-gate src_ptr = (uint32_t *)&ipha->ipha_src; 6177c478bd9Sstevel@tonic-gate dst_ptr = (uint32_t *)&dst; 6187c478bd9Sstevel@tonic-gate } else { 6197c478bd9Sstevel@tonic-gate ip6_t *ip6h = (ip6_t *)data_mp->b_rptr; 6207c478bd9Sstevel@tonic-gate 6217c478bd9Sstevel@tonic-gate ASSERT(IPH_HDR_VERSION(ip6h) == IPV6_VERSION); 6223062294fSDan McDonald dst6 = ip_get_dst_v6(ip6h, data_mp, NULL); 6237c478bd9Sstevel@tonic-gate af = AF_INET6; 6247c478bd9Sstevel@tonic-gate 6257c478bd9Sstevel@tonic-gate bzero(&ipp, sizeof (ipp)); 626fb87b5d2Ssommerfe sp = &sadbp->s_v6; 6277c478bd9Sstevel@tonic-gate 6287c478bd9Sstevel@tonic-gate /* Same NOTE: applies here! */ 629fb87b5d2Ssommerfe bucket = OUTBOUND_BUCKET_V6(sp, dst6); 6307c478bd9Sstevel@tonic-gate src_ptr = (uint32_t *)&ip6h->ip6_src; 6317c478bd9Sstevel@tonic-gate dst_ptr = (uint32_t *)&dst6; 6327c478bd9Sstevel@tonic-gate } 6337c478bd9Sstevel@tonic-gate 6347c478bd9Sstevel@tonic-gate mutex_enter(&bucket->isaf_lock); 635bd670b35SErik Nordmark assoc = ipsec_getassocbyconn(bucket, ixa, src_ptr, dst_ptr, af, 636bd670b35SErik Nordmark proto, ixa->ixa_tsl); 6377c478bd9Sstevel@tonic-gate mutex_exit(&bucket->isaf_lock); 6387c478bd9Sstevel@tonic-gate 6397c478bd9Sstevel@tonic-gate if (assoc == NULL) 6407c478bd9Sstevel@tonic-gate return (B_FALSE); 6417c478bd9Sstevel@tonic-gate 6427c478bd9Sstevel@tonic-gate if (assoc->ipsa_state == IPSA_STATE_DEAD) { 6437c478bd9Sstevel@tonic-gate IPSA_REFRELE(assoc); 6447c478bd9Sstevel@tonic-gate return (B_FALSE); 6457c478bd9Sstevel@tonic-gate } 6467c478bd9Sstevel@tonic-gate 6477c478bd9Sstevel@tonic-gate ASSERT(assoc->ipsa_state != IPSA_STATE_LARVAL); 6487c478bd9Sstevel@tonic-gate 6497c478bd9Sstevel@tonic-gate *sa = assoc; 6507c478bd9Sstevel@tonic-gate return (B_TRUE); 6517c478bd9Sstevel@tonic-gate } 6527c478bd9Sstevel@tonic-gate 6537c478bd9Sstevel@tonic-gate /* 6547c478bd9Sstevel@tonic-gate * Inbound IPsec SA selection. 655bd670b35SErik Nordmark * Can return a pulled up mblk. 656bd670b35SErik Nordmark * When it returns non-NULL ahp is updated 6577c478bd9Sstevel@tonic-gate */ 658bd670b35SErik Nordmark mblk_t * 659bd670b35SErik Nordmark ipsec_inbound_ah_sa(mblk_t *mp, ip_recv_attr_t *ira, ah_t **ahp) 6607c478bd9Sstevel@tonic-gate { 6617c478bd9Sstevel@tonic-gate ipha_t *ipha; 6627c478bd9Sstevel@tonic-gate ipsa_t *assoc; 6637c478bd9Sstevel@tonic-gate ah_t *ah; 6647c478bd9Sstevel@tonic-gate isaf_t *hptr; 6657c478bd9Sstevel@tonic-gate boolean_t isv6; 6667c478bd9Sstevel@tonic-gate ip6_t *ip6h; 6677c478bd9Sstevel@tonic-gate int ah_offset; 6687c478bd9Sstevel@tonic-gate uint32_t *src_ptr, *dst_ptr; 6697c478bd9Sstevel@tonic-gate int pullup_len; 670fb87b5d2Ssommerfe sadb_t *sp; 6717c478bd9Sstevel@tonic-gate sa_family_t af; 672bd670b35SErik Nordmark netstack_t *ns = ira->ira_ill->ill_ipst->ips_netstack; 673f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 674f4b3ec61Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 6757c478bd9Sstevel@tonic-gate 676f4b3ec61Sdh155122 IP_AH_BUMP_STAT(ipss, in_requests); 6777c478bd9Sstevel@tonic-gate 678bd670b35SErik Nordmark isv6 = !(ira->ira_flags & IRAF_IS_IPV4); 6797c478bd9Sstevel@tonic-gate if (isv6) { 6807c478bd9Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 6817c478bd9Sstevel@tonic-gate ah_offset = ipsec_ah_get_hdr_size_v6(mp, B_TRUE); 6827c478bd9Sstevel@tonic-gate } else { 6837c478bd9Sstevel@tonic-gate ipha = (ipha_t *)mp->b_rptr; 6847c478bd9Sstevel@tonic-gate ASSERT(ipha->ipha_protocol == IPPROTO_AH); 6857c478bd9Sstevel@tonic-gate ah_offset = ipha->ipha_version_and_hdr_length - 6867c478bd9Sstevel@tonic-gate (uint8_t)((IP_VERSION << 4)); 6877c478bd9Sstevel@tonic-gate ah_offset <<= 2; 6887c478bd9Sstevel@tonic-gate } 6897c478bd9Sstevel@tonic-gate 6907c478bd9Sstevel@tonic-gate /* 6917c478bd9Sstevel@tonic-gate * We assume that the IP header is pulled up until 6927c478bd9Sstevel@tonic-gate * the options. We need to see whether we have the 6937c478bd9Sstevel@tonic-gate * AH header in the same mblk or not. 6947c478bd9Sstevel@tonic-gate */ 6957c478bd9Sstevel@tonic-gate pullup_len = ah_offset + sizeof (ah_t); 6967c478bd9Sstevel@tonic-gate if (mp->b_rptr + pullup_len > mp->b_wptr) { 6977c478bd9Sstevel@tonic-gate if (!pullupmsg(mp, pullup_len)) { 698f4b3ec61Sdh155122 ipsec_rl_strlog(ns, ip_mod_info.mi_idnum, 0, 0, 699bffb04cfSmarkfen SL_WARN | SL_ERROR, 7007c478bd9Sstevel@tonic-gate "ipsec_inbound_ah_sa: Small AH header\n"); 701f4b3ec61Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 702bd670b35SErik Nordmark ip_drop_packet(mp, B_TRUE, ira->ira_ill, 703f4b3ec61Sdh155122 DROPPER(ipss, ipds_ah_bad_length), 704f4b3ec61Sdh155122 &ipss->ipsec_dropper); 7057c478bd9Sstevel@tonic-gate return (NULL); 7067c478bd9Sstevel@tonic-gate } 7077c478bd9Sstevel@tonic-gate if (isv6) 7087c478bd9Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 7097c478bd9Sstevel@tonic-gate else 7107c478bd9Sstevel@tonic-gate ipha = (ipha_t *)mp->b_rptr; 7117c478bd9Sstevel@tonic-gate } 7127c478bd9Sstevel@tonic-gate 7137c478bd9Sstevel@tonic-gate ah = (ah_t *)(mp->b_rptr + ah_offset); 7147c478bd9Sstevel@tonic-gate 7157c478bd9Sstevel@tonic-gate if (isv6) { 7167c478bd9Sstevel@tonic-gate src_ptr = (uint32_t *)&ip6h->ip6_src; 7177c478bd9Sstevel@tonic-gate dst_ptr = (uint32_t *)&ip6h->ip6_dst; 718f4b3ec61Sdh155122 sp = &ahstack->ah_sadb.s_v6; 7197c478bd9Sstevel@tonic-gate af = AF_INET6; 7207c478bd9Sstevel@tonic-gate } else { 7217c478bd9Sstevel@tonic-gate src_ptr = (uint32_t *)&ipha->ipha_src; 7227c478bd9Sstevel@tonic-gate dst_ptr = (uint32_t *)&ipha->ipha_dst; 723f4b3ec61Sdh155122 sp = &ahstack->ah_sadb.s_v4; 7247c478bd9Sstevel@tonic-gate af = AF_INET; 7257c478bd9Sstevel@tonic-gate } 7267c478bd9Sstevel@tonic-gate 727fb87b5d2Ssommerfe hptr = INBOUND_BUCKET(sp, ah->ah_spi); 7287c478bd9Sstevel@tonic-gate mutex_enter(&hptr->isaf_lock); 7297c478bd9Sstevel@tonic-gate assoc = ipsec_getassocbyspi(hptr, ah->ah_spi, src_ptr, dst_ptr, af); 7307c478bd9Sstevel@tonic-gate mutex_exit(&hptr->isaf_lock); 7317c478bd9Sstevel@tonic-gate 7329c2c14abSThejaswini Singarajipura if (assoc == NULL || assoc->ipsa_state == IPSA_STATE_DEAD || 7339c2c14abSThejaswini Singarajipura assoc->ipsa_state == IPSA_STATE_ACTIVE_ELSEWHERE) { 734f4b3ec61Sdh155122 IP_AH_BUMP_STAT(ipss, lookup_failure); 735f4b3ec61Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 736bd670b35SErik Nordmark ipsecah_in_assocfailure(mp, 0, 7377c478bd9Sstevel@tonic-gate SL_ERROR | SL_CONSOLE | SL_WARN, 7387c478bd9Sstevel@tonic-gate "ipsec_inbound_ah_sa: No association found for " 7397c478bd9Sstevel@tonic-gate "spi 0x%x, dst addr %s\n", 740bd670b35SErik Nordmark ah->ah_spi, dst_ptr, af, ira); 7417c478bd9Sstevel@tonic-gate if (assoc != NULL) { 7427c478bd9Sstevel@tonic-gate IPSA_REFRELE(assoc); 7437c478bd9Sstevel@tonic-gate } 7447c478bd9Sstevel@tonic-gate return (NULL); 7457c478bd9Sstevel@tonic-gate } 7467c478bd9Sstevel@tonic-gate 747*930af642SDan McDonald if (assoc->ipsa_state == IPSA_STATE_LARVAL) { 7487c478bd9Sstevel@tonic-gate /* Not fully baked; swap the packet under a rock until then */ 749*930af642SDan McDonald 750*930af642SDan McDonald mp = sadb_set_lpkt(assoc, mp, ira); 751*930af642SDan McDonald if (mp == NULL) { 7527c478bd9Sstevel@tonic-gate IPSA_REFRELE(assoc); 7537c478bd9Sstevel@tonic-gate return (NULL); 7547c478bd9Sstevel@tonic-gate } 755*930af642SDan McDonald /* Looks like the SA is no longer LARVAL. */ 756*930af642SDan McDonald } 7577c478bd9Sstevel@tonic-gate 758bd670b35SErik Nordmark /* Are the IPsec fields initialized at all? */ 759bd670b35SErik Nordmark if (!(ira->ira_flags & IRAF_IPSEC_SECURE)) { 760bd670b35SErik Nordmark ira->ira_ipsec_action = NULL; 761bd670b35SErik Nordmark ira->ira_ipsec_ah_sa = NULL; 762bd670b35SErik Nordmark ira->ira_ipsec_esp_sa = NULL; 763bd670b35SErik Nordmark } 764bd670b35SErik Nordmark 7657c478bd9Sstevel@tonic-gate /* 7667c478bd9Sstevel@tonic-gate * Save a reference to the association so that it can 7677c478bd9Sstevel@tonic-gate * be retrieved after execution. We free any AH SA reference 7687c478bd9Sstevel@tonic-gate * already there (innermost SA "wins". The reference to 7697c478bd9Sstevel@tonic-gate * the SA will also be used later when doing the policy checks. 7707c478bd9Sstevel@tonic-gate */ 771bd670b35SErik Nordmark if (ira->ira_ipsec_ah_sa != NULL) { 772bd670b35SErik Nordmark IPSA_REFRELE(ira->ira_ipsec_ah_sa); 7737c478bd9Sstevel@tonic-gate } 774bd670b35SErik Nordmark ira->ira_flags |= IRAF_IPSEC_SECURE; 775bd670b35SErik Nordmark ira->ira_ipsec_ah_sa = assoc; 7767c478bd9Sstevel@tonic-gate 777bd670b35SErik Nordmark *ahp = ah; 778bd670b35SErik Nordmark return (mp); 7797c478bd9Sstevel@tonic-gate } 7807c478bd9Sstevel@tonic-gate 781bd670b35SErik Nordmark /* 782bd670b35SErik Nordmark * Can return a pulled up mblk. 783bd670b35SErik Nordmark * When it returns non-NULL esphp is updated 784bd670b35SErik Nordmark */ 785bd670b35SErik Nordmark mblk_t * 786bd670b35SErik Nordmark ipsec_inbound_esp_sa(mblk_t *data_mp, ip_recv_attr_t *ira, esph_t **esphp) 7877c478bd9Sstevel@tonic-gate { 788bd670b35SErik Nordmark mblk_t *placeholder; 7897c478bd9Sstevel@tonic-gate uint32_t *src_ptr, *dst_ptr; 7907c478bd9Sstevel@tonic-gate ipha_t *ipha; 7917c478bd9Sstevel@tonic-gate ip6_t *ip6h; 7927c478bd9Sstevel@tonic-gate esph_t *esph; 7937c478bd9Sstevel@tonic-gate ipsa_t *ipsa; 7947c478bd9Sstevel@tonic-gate isaf_t *bucket; 7957c478bd9Sstevel@tonic-gate uint_t preamble; 7967c478bd9Sstevel@tonic-gate sa_family_t af; 7977c478bd9Sstevel@tonic-gate boolean_t isv6; 798fb87b5d2Ssommerfe sadb_t *sp; 799bd670b35SErik Nordmark netstack_t *ns = ira->ira_ill->ill_ipst->ips_netstack; 800f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 801f4b3ec61Sdh155122 ipsecesp_stack_t *espstack = ns->netstack_ipsecesp; 8027c478bd9Sstevel@tonic-gate 803f4b3ec61Sdh155122 IP_ESP_BUMP_STAT(ipss, in_requests); 8047c478bd9Sstevel@tonic-gate 805bd670b35SErik Nordmark isv6 = !(ira->ira_flags & IRAF_IS_IPV4); 8067c478bd9Sstevel@tonic-gate if (isv6) { 8077c478bd9Sstevel@tonic-gate ip6h = (ip6_t *)data_mp->b_rptr; 8087c478bd9Sstevel@tonic-gate } else { 8097c478bd9Sstevel@tonic-gate ipha = (ipha_t *)data_mp->b_rptr; 8107c478bd9Sstevel@tonic-gate } 8117c478bd9Sstevel@tonic-gate 8127c478bd9Sstevel@tonic-gate /* 8137c478bd9Sstevel@tonic-gate * Put all data into one mblk if it's not there already. 8147c478bd9Sstevel@tonic-gate * XXX This is probably bad long-term. Figure out better ways of doing 8157c478bd9Sstevel@tonic-gate * this. Much of the inbound path depends on all of the data being 8167c478bd9Sstevel@tonic-gate * in one mblk. 8177c478bd9Sstevel@tonic-gate * 8187c478bd9Sstevel@tonic-gate * XXX Jumbogram issues will have to be dealt with here. 8197c478bd9Sstevel@tonic-gate * If the plen is 0, we'll have to scan for a HBH header with the 8207c478bd9Sstevel@tonic-gate * actual packet length. 8217c478bd9Sstevel@tonic-gate */ 8227c478bd9Sstevel@tonic-gate if (data_mp->b_datap->db_ref > 1 || 823bd670b35SErik Nordmark (data_mp->b_wptr - data_mp->b_rptr) < ira->ira_pktlen) { 8247c478bd9Sstevel@tonic-gate placeholder = msgpullup(data_mp, -1); 8257c478bd9Sstevel@tonic-gate if (placeholder == NULL) { 826f4b3ec61Sdh155122 IP_ESP_BUMP_STAT(ipss, in_discards); 827bd670b35SErik Nordmark ip_drop_packet(data_mp, B_TRUE, ira->ira_ill, 828f4b3ec61Sdh155122 DROPPER(ipss, ipds_esp_nomem), 829f4b3ec61Sdh155122 &ipss->ipsec_dropper); 8307c478bd9Sstevel@tonic-gate return (NULL); 8317c478bd9Sstevel@tonic-gate } else { 8327c478bd9Sstevel@tonic-gate /* Reset packet with new pulled up mblk. */ 8337c478bd9Sstevel@tonic-gate freemsg(data_mp); 8347c478bd9Sstevel@tonic-gate data_mp = placeholder; 8357c478bd9Sstevel@tonic-gate } 8367c478bd9Sstevel@tonic-gate } 8377c478bd9Sstevel@tonic-gate 8387c478bd9Sstevel@tonic-gate /* 8397c478bd9Sstevel@tonic-gate * Find the ESP header, point the address pointers at the appropriate 8407c478bd9Sstevel@tonic-gate * IPv4/IPv6 places. 8417c478bd9Sstevel@tonic-gate */ 8427c478bd9Sstevel@tonic-gate if (isv6) { 8437c478bd9Sstevel@tonic-gate ip6h = (ip6_t *)data_mp->b_rptr; 8447c478bd9Sstevel@tonic-gate src_ptr = (uint32_t *)&ip6h->ip6_src; 8457c478bd9Sstevel@tonic-gate dst_ptr = (uint32_t *)&ip6h->ip6_dst; 8467c478bd9Sstevel@tonic-gate if (ip6h->ip6_nxt != IPPROTO_ESP) { 8477c478bd9Sstevel@tonic-gate /* There are options that need to be processed. */ 8487c478bd9Sstevel@tonic-gate preamble = ip_hdr_length_v6(data_mp, ip6h); 8497c478bd9Sstevel@tonic-gate } else { 8507c478bd9Sstevel@tonic-gate preamble = sizeof (ip6_t); 8517c478bd9Sstevel@tonic-gate } 8527c478bd9Sstevel@tonic-gate 853f4b3ec61Sdh155122 sp = &espstack->esp_sadb.s_v6; 8547c478bd9Sstevel@tonic-gate af = AF_INET6; 8557c478bd9Sstevel@tonic-gate } else { 8567c478bd9Sstevel@tonic-gate ipha = (ipha_t *)data_mp->b_rptr; 8577c478bd9Sstevel@tonic-gate src_ptr = (uint32_t *)&ipha->ipha_src; 8587c478bd9Sstevel@tonic-gate dst_ptr = (uint32_t *)&ipha->ipha_dst; 8597c478bd9Sstevel@tonic-gate preamble = IPH_HDR_LENGTH(ipha); 8607c478bd9Sstevel@tonic-gate 861f4b3ec61Sdh155122 sp = &espstack->esp_sadb.s_v4; 8627c478bd9Sstevel@tonic-gate af = AF_INET; 8637c478bd9Sstevel@tonic-gate } 8647c478bd9Sstevel@tonic-gate 8657c478bd9Sstevel@tonic-gate esph = (esph_t *)(data_mp->b_rptr + preamble); 8667c478bd9Sstevel@tonic-gate 8677c478bd9Sstevel@tonic-gate /* Since hash is common on inbound (SPI value), hash here. */ 868fb87b5d2Ssommerfe bucket = INBOUND_BUCKET(sp, esph->esph_spi); 8697c478bd9Sstevel@tonic-gate mutex_enter(&bucket->isaf_lock); 8707c478bd9Sstevel@tonic-gate ipsa = ipsec_getassocbyspi(bucket, esph->esph_spi, src_ptr, dst_ptr, 8717c478bd9Sstevel@tonic-gate af); 8727c478bd9Sstevel@tonic-gate mutex_exit(&bucket->isaf_lock); 8737c478bd9Sstevel@tonic-gate 8749c2c14abSThejaswini Singarajipura if (ipsa == NULL || ipsa->ipsa_state == IPSA_STATE_DEAD || 8759c2c14abSThejaswini Singarajipura ipsa->ipsa_state == IPSA_STATE_ACTIVE_ELSEWHERE) { 8767c478bd9Sstevel@tonic-gate /* This is a loggable error! AUDIT ME! */ 877f4b3ec61Sdh155122 IP_ESP_BUMP_STAT(ipss, lookup_failure); 878f4b3ec61Sdh155122 IP_ESP_BUMP_STAT(ipss, in_discards); 879bd670b35SErik Nordmark ipsecesp_in_assocfailure(data_mp, 0, 8807c478bd9Sstevel@tonic-gate SL_ERROR | SL_CONSOLE | SL_WARN, 8817c478bd9Sstevel@tonic-gate "ipsec_inbound_esp_sa: No association found for " 8827c478bd9Sstevel@tonic-gate "spi 0x%x, dst addr %s\n", 883bd670b35SErik Nordmark esph->esph_spi, dst_ptr, af, ira); 8847c478bd9Sstevel@tonic-gate if (ipsa != NULL) { 8857c478bd9Sstevel@tonic-gate IPSA_REFRELE(ipsa); 8867c478bd9Sstevel@tonic-gate } 8877c478bd9Sstevel@tonic-gate return (NULL); 8887c478bd9Sstevel@tonic-gate } 8897c478bd9Sstevel@tonic-gate 890*930af642SDan McDonald if (ipsa->ipsa_state == IPSA_STATE_LARVAL) { 8917c478bd9Sstevel@tonic-gate /* Not fully baked; swap the packet under a rock until then */ 892*930af642SDan McDonald 893*930af642SDan McDonald data_mp = sadb_set_lpkt(ipsa, data_mp, ira); 894*930af642SDan McDonald if (data_mp == NULL) { 8957c478bd9Sstevel@tonic-gate IPSA_REFRELE(ipsa); 8967c478bd9Sstevel@tonic-gate return (NULL); 8977c478bd9Sstevel@tonic-gate } 898*930af642SDan McDonald /* Looks like the SA is no longer LARVAL. */ 899*930af642SDan McDonald } 9007c478bd9Sstevel@tonic-gate 901bd670b35SErik Nordmark /* Are the IPsec fields initialized at all? */ 902bd670b35SErik Nordmark if (!(ira->ira_flags & IRAF_IPSEC_SECURE)) { 903bd670b35SErik Nordmark ira->ira_ipsec_action = NULL; 904bd670b35SErik Nordmark ira->ira_ipsec_ah_sa = NULL; 905bd670b35SErik Nordmark ira->ira_ipsec_esp_sa = NULL; 906bd670b35SErik Nordmark } 907bd670b35SErik Nordmark 9087c478bd9Sstevel@tonic-gate /* 9097c478bd9Sstevel@tonic-gate * Save a reference to the association so that it can 9107c478bd9Sstevel@tonic-gate * be retrieved after execution. We free any AH SA reference 9117c478bd9Sstevel@tonic-gate * already there (innermost SA "wins". The reference to 9127c478bd9Sstevel@tonic-gate * the SA will also be used later when doing the policy checks. 9137c478bd9Sstevel@tonic-gate */ 914bd670b35SErik Nordmark if (ira->ira_ipsec_esp_sa != NULL) { 915bd670b35SErik Nordmark IPSA_REFRELE(ira->ira_ipsec_esp_sa); 9167c478bd9Sstevel@tonic-gate } 917bd670b35SErik Nordmark ira->ira_flags |= IRAF_IPSEC_SECURE; 918bd670b35SErik Nordmark ira->ira_ipsec_esp_sa = ipsa; 9197c478bd9Sstevel@tonic-gate 920bd670b35SErik Nordmark *esphp = esph; 921bd670b35SErik Nordmark return (data_mp); 9227c478bd9Sstevel@tonic-gate } 923