10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 51659Smarkfen * Common Development and Distribution License (the "License"). 61659Smarkfen * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 221659Smarkfen * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate #include <sys/types.h> 290Sstevel@tonic-gate #include <sys/stream.h> 300Sstevel@tonic-gate #include <sys/stropts.h> 310Sstevel@tonic-gate #include <sys/errno.h> 320Sstevel@tonic-gate #include <sys/strlog.h> 330Sstevel@tonic-gate #include <sys/tihdr.h> 340Sstevel@tonic-gate #include <sys/socket.h> 350Sstevel@tonic-gate #include <sys/ddi.h> 360Sstevel@tonic-gate #include <sys/sunddi.h> 370Sstevel@tonic-gate #include <sys/kmem.h> 380Sstevel@tonic-gate #include <sys/cmn_err.h> 390Sstevel@tonic-gate #include <sys/vtrace.h> 400Sstevel@tonic-gate #include <sys/debug.h> 410Sstevel@tonic-gate #include <sys/atomic.h> 420Sstevel@tonic-gate #include <sys/strsun.h> 430Sstevel@tonic-gate #include <sys/random.h> 440Sstevel@tonic-gate #include <netinet/in.h> 450Sstevel@tonic-gate #include <net/if.h> 460Sstevel@tonic-gate #include <netinet/ip6.h> 470Sstevel@tonic-gate #include <netinet/icmp6.h> 480Sstevel@tonic-gate #include <net/pfkeyv2.h> 490Sstevel@tonic-gate 500Sstevel@tonic-gate #include <inet/common.h> 510Sstevel@tonic-gate #include <inet/mi.h> 520Sstevel@tonic-gate #include <inet/ip.h> 530Sstevel@tonic-gate #include <inet/ip6.h> 540Sstevel@tonic-gate #include <inet/nd.h> 550Sstevel@tonic-gate #include <inet/ipsec_info.h> 560Sstevel@tonic-gate #include <inet/ipsec_impl.h> 570Sstevel@tonic-gate #include <inet/sadb.h> 580Sstevel@tonic-gate #include <inet/ipsecah.h> 590Sstevel@tonic-gate #include <inet/ipsec_impl.h> 600Sstevel@tonic-gate #include <inet/ipdrop.h> 610Sstevel@tonic-gate #include <sys/taskq.h> 620Sstevel@tonic-gate #include <sys/policy.h> 630Sstevel@tonic-gate #include <sys/iphada.h> 640Sstevel@tonic-gate #include <sys/strsun.h> 650Sstevel@tonic-gate 660Sstevel@tonic-gate #include <sys/crypto/common.h> 670Sstevel@tonic-gate #include <sys/crypto/api.h> 680Sstevel@tonic-gate #include <sys/kstat.h> 69*1676Sjpk #include <sys/strsubr.h> 700Sstevel@tonic-gate 710Sstevel@tonic-gate /* Packet dropper for AH drops. */ 720Sstevel@tonic-gate static ipdropper_t ah_dropper; 730Sstevel@tonic-gate 740Sstevel@tonic-gate static kmutex_t ipsecah_param_lock; /* Protect ipsecah_param_arr[] below. */ 750Sstevel@tonic-gate /* 760Sstevel@tonic-gate * Table of ND variables supported by ipsecah. These are loaded into 770Sstevel@tonic-gate * ipsecah_g_nd in ipsecah_init_nd. 780Sstevel@tonic-gate * All of these are alterable, within the min/max values given, at run time. 790Sstevel@tonic-gate */ 800Sstevel@tonic-gate static ipsecahparam_t ipsecah_param_arr[] = { 810Sstevel@tonic-gate /* min max value name */ 820Sstevel@tonic-gate { 0, 3, 0, "ipsecah_debug"}, 830Sstevel@tonic-gate { 125, 32000, SADB_AGE_INTERVAL_DEFAULT, "ipsecah_age_interval"}, 840Sstevel@tonic-gate { 1, 10, 1, "ipsecah_reap_delay"}, 850Sstevel@tonic-gate { 1, SADB_MAX_REPLAY, 64, "ipsecah_replay_size"}, 860Sstevel@tonic-gate { 1, 300, 15, "ipsecah_acquire_timeout"}, 870Sstevel@tonic-gate { 1, 1800, 90, "ipsecah_larval_timeout"}, 880Sstevel@tonic-gate /* Default lifetime values for ACQUIRE messages. */ 890Sstevel@tonic-gate { 0, 0xffffffffU, 0, "ipsecah_default_soft_bytes"}, 900Sstevel@tonic-gate { 0, 0xffffffffU, 0, "ipsecah_default_hard_bytes"}, 910Sstevel@tonic-gate { 0, 0xffffffffU, 24000, "ipsecah_default_soft_addtime"}, 920Sstevel@tonic-gate { 0, 0xffffffffU, 28800, "ipsecah_default_hard_addtime"}, 930Sstevel@tonic-gate { 0, 0xffffffffU, 0, "ipsecah_default_soft_usetime"}, 940Sstevel@tonic-gate { 0, 0xffffffffU, 0, "ipsecah_default_hard_usetime"}, 950Sstevel@tonic-gate { 0, 1, 0, "ipsecah_log_unknown_spi"}, 960Sstevel@tonic-gate }; 970Sstevel@tonic-gate #define ipsecah_debug ipsecah_param_arr[0].ipsecah_param_value 980Sstevel@tonic-gate #define ipsecah_age_interval ipsecah_param_arr[1].ipsecah_param_value 990Sstevel@tonic-gate #define ipsecah_age_int_max ipsecah_param_arr[1].ipsecah_param_max 1000Sstevel@tonic-gate #define ipsecah_reap_delay ipsecah_param_arr[2].ipsecah_param_value 1010Sstevel@tonic-gate #define ipsecah_replay_size ipsecah_param_arr[3].ipsecah_param_value 1020Sstevel@tonic-gate #define ipsecah_acquire_timeout ipsecah_param_arr[4].ipsecah_param_value 1030Sstevel@tonic-gate #define ipsecah_larval_timeout ipsecah_param_arr[5].ipsecah_param_value 1040Sstevel@tonic-gate #define ipsecah_default_soft_bytes ipsecah_param_arr[6].ipsecah_param_value 1050Sstevel@tonic-gate #define ipsecah_default_hard_bytes ipsecah_param_arr[7].ipsecah_param_value 1060Sstevel@tonic-gate #define ipsecah_default_soft_addtime ipsecah_param_arr[8].ipsecah_param_value 1070Sstevel@tonic-gate #define ipsecah_default_hard_addtime ipsecah_param_arr[9].ipsecah_param_value 1080Sstevel@tonic-gate #define ipsecah_default_soft_usetime ipsecah_param_arr[10].ipsecah_param_value 1090Sstevel@tonic-gate #define ipsecah_default_hard_usetime ipsecah_param_arr[11].ipsecah_param_value 1100Sstevel@tonic-gate #define ipsecah_log_unknown_spi ipsecah_param_arr[12].ipsecah_param_value 1110Sstevel@tonic-gate 1120Sstevel@tonic-gate #define ah0dbg(a) printf a 1130Sstevel@tonic-gate /* NOTE: != 0 instead of > 0 so lint doesn't complain. */ 1140Sstevel@tonic-gate #define ah1dbg(a) if (ipsecah_debug != 0) printf a 1150Sstevel@tonic-gate #define ah2dbg(a) if (ipsecah_debug > 1) printf a 1160Sstevel@tonic-gate #define ah3dbg(a) if (ipsecah_debug > 2) printf a 1170Sstevel@tonic-gate 1180Sstevel@tonic-gate static IDP ipsecah_g_nd; 1190Sstevel@tonic-gate 1200Sstevel@tonic-gate /* 1210Sstevel@tonic-gate * XXX This is broken. Padding should be determined dynamically 1220Sstevel@tonic-gate * depending on the ICV size and IP version number so that the 1230Sstevel@tonic-gate * total AH header size is a multiple of 32 bits or 64 bits 1240Sstevel@tonic-gate * for V4 and V6 respectively. For 96bit ICVs we have no problems. 1250Sstevel@tonic-gate * Anything different from that, we need to fix our code. 1260Sstevel@tonic-gate */ 1270Sstevel@tonic-gate #define IPV4_PADDING_ALIGN 0x04 /* Multiple of 32 bits */ 1280Sstevel@tonic-gate #define IPV6_PADDING_ALIGN 0x04 /* Multiple of 32 bits */ 1290Sstevel@tonic-gate 1300Sstevel@tonic-gate /* 1310Sstevel@tonic-gate * Helper macro. Avoids a call to msgdsize if there is only one 1320Sstevel@tonic-gate * mblk in the chain. 1330Sstevel@tonic-gate */ 1340Sstevel@tonic-gate #define AH_MSGSIZE(mp) ((mp)->b_cont != NULL ? msgdsize(mp) : MBLKL(mp)) 1350Sstevel@tonic-gate 1360Sstevel@tonic-gate static ipsec_status_t ah_auth_out_done(mblk_t *); 1370Sstevel@tonic-gate static ipsec_status_t ah_auth_in_done(mblk_t *); 1380Sstevel@tonic-gate static mblk_t *ah_process_ip_options_v4(mblk_t *, ipsa_t *, int *, uint_t, 1390Sstevel@tonic-gate boolean_t); 1400Sstevel@tonic-gate static mblk_t *ah_process_ip_options_v6(mblk_t *, ipsa_t *, int *, uint_t, 1410Sstevel@tonic-gate boolean_t); 1420Sstevel@tonic-gate static void ah_getspi(mblk_t *, keysock_in_t *); 1430Sstevel@tonic-gate static ipsec_status_t ah_inbound_accelerated(mblk_t *, boolean_t, ipsa_t *, 1440Sstevel@tonic-gate uint32_t); 1450Sstevel@tonic-gate static ipsec_status_t ah_outbound_accelerated_v4(mblk_t *, ipsa_t *); 1460Sstevel@tonic-gate static ipsec_status_t ah_outbound_accelerated_v6(mblk_t *, ipsa_t *); 1470Sstevel@tonic-gate static ipsec_status_t ah_outbound(mblk_t *); 1480Sstevel@tonic-gate 1490Sstevel@tonic-gate static int ipsecah_open(queue_t *, dev_t *, int, int, cred_t *); 1500Sstevel@tonic-gate static int ipsecah_close(queue_t *); 1510Sstevel@tonic-gate static void ipsecah_rput(queue_t *, mblk_t *); 1520Sstevel@tonic-gate static void ipsecah_wput(queue_t *, mblk_t *); 1530Sstevel@tonic-gate static void ah_send_acquire(ipsacq_t *, mblk_t *); 1540Sstevel@tonic-gate static boolean_t ah_register_out(uint32_t, uint32_t, uint_t); 1550Sstevel@tonic-gate 1560Sstevel@tonic-gate static struct module_info info = { 1570Sstevel@tonic-gate 5136, "ipsecah", 0, INFPSZ, 65536, 1024 1580Sstevel@tonic-gate }; 1590Sstevel@tonic-gate 1600Sstevel@tonic-gate static struct qinit rinit = { 1610Sstevel@tonic-gate (pfi_t)ipsecah_rput, NULL, ipsecah_open, ipsecah_close, NULL, &info, 1620Sstevel@tonic-gate NULL 1630Sstevel@tonic-gate }; 1640Sstevel@tonic-gate 1650Sstevel@tonic-gate static struct qinit winit = { 1660Sstevel@tonic-gate (pfi_t)ipsecah_wput, NULL, ipsecah_open, ipsecah_close, NULL, &info, 1670Sstevel@tonic-gate NULL 1680Sstevel@tonic-gate }; 1690Sstevel@tonic-gate 1700Sstevel@tonic-gate struct streamtab ipsecahinfo = { 1710Sstevel@tonic-gate &rinit, &winit, NULL, NULL 1720Sstevel@tonic-gate }; 1730Sstevel@tonic-gate 1740Sstevel@tonic-gate /* 1750Sstevel@tonic-gate * Keysock instance of AH. "There can be only one." :) 1760Sstevel@tonic-gate * Use casptr() on this because I don't set it until KEYSOCK_HELLO comes down. 1770Sstevel@tonic-gate * Paired up with the ah_pfkey_q is the ah_event, which will age SAs. 1780Sstevel@tonic-gate */ 1790Sstevel@tonic-gate static queue_t *ah_pfkey_q; 1800Sstevel@tonic-gate static timeout_id_t ah_event; 1810Sstevel@tonic-gate static taskq_t *ah_taskq; 1820Sstevel@tonic-gate 1830Sstevel@tonic-gate static mblk_t *ah_ip_unbind; 1840Sstevel@tonic-gate 1850Sstevel@tonic-gate /* 1860Sstevel@tonic-gate * Stats. This may eventually become a full-blown SNMP MIB once that spec 1870Sstevel@tonic-gate * stabilizes. 1880Sstevel@tonic-gate */ 1890Sstevel@tonic-gate typedef struct 1900Sstevel@tonic-gate { 1910Sstevel@tonic-gate kstat_named_t ah_stat_num_aalgs; 1920Sstevel@tonic-gate kstat_named_t ah_stat_good_auth; 1930Sstevel@tonic-gate kstat_named_t ah_stat_bad_auth; 1940Sstevel@tonic-gate kstat_named_t ah_stat_replay_failures; 1950Sstevel@tonic-gate kstat_named_t ah_stat_replay_early_failures; 1960Sstevel@tonic-gate kstat_named_t ah_stat_keysock_in; 1970Sstevel@tonic-gate kstat_named_t ah_stat_out_requests; 1980Sstevel@tonic-gate kstat_named_t ah_stat_acquire_requests; 1990Sstevel@tonic-gate kstat_named_t ah_stat_bytes_expired; 2000Sstevel@tonic-gate kstat_named_t ah_stat_out_discards; 2010Sstevel@tonic-gate kstat_named_t ah_stat_in_accelerated; 2020Sstevel@tonic-gate kstat_named_t ah_stat_out_accelerated; 2030Sstevel@tonic-gate kstat_named_t ah_stat_noaccel; 2040Sstevel@tonic-gate kstat_named_t ah_stat_crypto_sync; 2050Sstevel@tonic-gate kstat_named_t ah_stat_crypto_async; 2060Sstevel@tonic-gate kstat_named_t ah_stat_crypto_failures; 2070Sstevel@tonic-gate } ah_kstats_t; 2080Sstevel@tonic-gate 2090Sstevel@tonic-gate #define AH_BUMP_STAT(x) (ah_kstats->ah_stat_ ## x).value.ui64++ 2100Sstevel@tonic-gate #define AH_DEBUMP_STAT(x) (ah_kstats->ah_stat_ ## x).value.ui64-- 2110Sstevel@tonic-gate 212564Ssommerfe uint32_t ah_hash_size = IPSEC_DEFAULT_HASH_SIZE; 2130Sstevel@tonic-gate static kstat_t *ah_ksp; 2140Sstevel@tonic-gate static ah_kstats_t *ah_kstats; 2150Sstevel@tonic-gate 2160Sstevel@tonic-gate static int ah_kstat_update(kstat_t *, int); 2170Sstevel@tonic-gate 2181659Smarkfen uint64_t ipsacq_maxpackets = IPSACQ_MAXPACKETS; 2191659Smarkfen 2200Sstevel@tonic-gate static boolean_t 2210Sstevel@tonic-gate ah_kstat_init(void) 2220Sstevel@tonic-gate { 2230Sstevel@tonic-gate 2240Sstevel@tonic-gate ah_ksp = kstat_create("ipsecah", 0, "ah_stat", "net", 2250Sstevel@tonic-gate KSTAT_TYPE_NAMED, sizeof (*ah_kstats) / sizeof (kstat_named_t), 2260Sstevel@tonic-gate KSTAT_FLAG_PERSISTENT); 2270Sstevel@tonic-gate 2280Sstevel@tonic-gate if (ah_ksp == NULL) 2290Sstevel@tonic-gate return (B_FALSE); 2300Sstevel@tonic-gate 2310Sstevel@tonic-gate ah_kstats = ah_ksp->ks_data; 2320Sstevel@tonic-gate 2330Sstevel@tonic-gate ah_ksp->ks_update = ah_kstat_update; 2340Sstevel@tonic-gate 2350Sstevel@tonic-gate #define K64 KSTAT_DATA_UINT64 2360Sstevel@tonic-gate #define KI(x) kstat_named_init(&(ah_kstats->ah_stat_##x), #x, K64) 2370Sstevel@tonic-gate 2380Sstevel@tonic-gate KI(num_aalgs); 2390Sstevel@tonic-gate KI(good_auth); 2400Sstevel@tonic-gate KI(bad_auth); 2410Sstevel@tonic-gate KI(replay_failures); 2420Sstevel@tonic-gate KI(replay_early_failures); 2430Sstevel@tonic-gate KI(keysock_in); 2440Sstevel@tonic-gate KI(out_requests); 2450Sstevel@tonic-gate KI(acquire_requests); 2460Sstevel@tonic-gate KI(bytes_expired); 2470Sstevel@tonic-gate KI(out_discards); 2480Sstevel@tonic-gate KI(in_accelerated); 2490Sstevel@tonic-gate KI(out_accelerated); 2500Sstevel@tonic-gate KI(noaccel); 2510Sstevel@tonic-gate KI(crypto_sync); 2520Sstevel@tonic-gate KI(crypto_async); 2530Sstevel@tonic-gate KI(crypto_failures); 2540Sstevel@tonic-gate 2550Sstevel@tonic-gate #undef KI 2560Sstevel@tonic-gate #undef K64 2570Sstevel@tonic-gate 2580Sstevel@tonic-gate kstat_install(ah_ksp); 2591659Smarkfen IP_ACQUIRE_STAT(maxpackets, ipsacq_maxpackets); 2600Sstevel@tonic-gate return (B_TRUE); 2610Sstevel@tonic-gate } 2620Sstevel@tonic-gate 2630Sstevel@tonic-gate static int 2640Sstevel@tonic-gate ah_kstat_update(kstat_t *kp, int rw) 2650Sstevel@tonic-gate { 2660Sstevel@tonic-gate ah_kstats_t *ekp; 2670Sstevel@tonic-gate 2680Sstevel@tonic-gate if ((kp == NULL) || (kp->ks_data == NULL)) 2690Sstevel@tonic-gate return (EIO); 2700Sstevel@tonic-gate 2710Sstevel@tonic-gate if (rw == KSTAT_WRITE) 2720Sstevel@tonic-gate return (EACCES); 2730Sstevel@tonic-gate 2740Sstevel@tonic-gate ASSERT(kp == ah_ksp); 2750Sstevel@tonic-gate ekp = (ah_kstats_t *)kp->ks_data; 2760Sstevel@tonic-gate ASSERT(ekp == ah_kstats); 2770Sstevel@tonic-gate 2780Sstevel@tonic-gate mutex_enter(&alg_lock); 2790Sstevel@tonic-gate ekp->ah_stat_num_aalgs.value.ui64 = ipsec_nalgs[IPSEC_ALG_AUTH]; 2800Sstevel@tonic-gate mutex_exit(&alg_lock); 2810Sstevel@tonic-gate 2820Sstevel@tonic-gate return (0); 2830Sstevel@tonic-gate } 2840Sstevel@tonic-gate 2850Sstevel@tonic-gate /* 2860Sstevel@tonic-gate * Don't have to lock ipsec_age_interval, as only one thread will access it at 2870Sstevel@tonic-gate * a time, because I control the one function that does a qtimeout() on 2880Sstevel@tonic-gate * ah_pfkey_q. 2890Sstevel@tonic-gate */ 2900Sstevel@tonic-gate /* ARGSUSED */ 2910Sstevel@tonic-gate static void 2920Sstevel@tonic-gate ah_ager(void *ignoreme) 2930Sstevel@tonic-gate { 2940Sstevel@tonic-gate hrtime_t begin = gethrtime(); 2950Sstevel@tonic-gate 2960Sstevel@tonic-gate sadb_ager(&ah_sadb.s_v4, ah_pfkey_q, ah_sadb.s_ip_q, 2970Sstevel@tonic-gate ipsecah_reap_delay); 2980Sstevel@tonic-gate sadb_ager(&ah_sadb.s_v6, ah_pfkey_q, ah_sadb.s_ip_q, 2990Sstevel@tonic-gate ipsecah_reap_delay); 3000Sstevel@tonic-gate 3010Sstevel@tonic-gate ah_event = sadb_retimeout(begin, ah_pfkey_q, ah_ager, 3020Sstevel@tonic-gate &ipsecah_age_interval, ipsecah_age_int_max, info.mi_idnum); 3030Sstevel@tonic-gate } 3040Sstevel@tonic-gate 3050Sstevel@tonic-gate /* 3060Sstevel@tonic-gate * Get an AH NDD parameter. 3070Sstevel@tonic-gate */ 3080Sstevel@tonic-gate /* ARGSUSED */ 3090Sstevel@tonic-gate static int 3100Sstevel@tonic-gate ipsecah_param_get(q, mp, cp, cr) 3110Sstevel@tonic-gate queue_t *q; 3120Sstevel@tonic-gate mblk_t *mp; 3130Sstevel@tonic-gate caddr_t cp; 3140Sstevel@tonic-gate cred_t *cr; 3150Sstevel@tonic-gate { 3160Sstevel@tonic-gate ipsecahparam_t *ipsecahpa = (ipsecahparam_t *)cp; 3170Sstevel@tonic-gate uint_t value; 3180Sstevel@tonic-gate 3190Sstevel@tonic-gate mutex_enter(&ipsecah_param_lock); 3200Sstevel@tonic-gate value = ipsecahpa->ipsecah_param_value; 3210Sstevel@tonic-gate mutex_exit(&ipsecah_param_lock); 3220Sstevel@tonic-gate 3230Sstevel@tonic-gate (void) mi_mpprintf(mp, "%u", value); 3240Sstevel@tonic-gate return (0); 3250Sstevel@tonic-gate } 3260Sstevel@tonic-gate 3270Sstevel@tonic-gate /* 3280Sstevel@tonic-gate * This routine sets an NDD variable in a ipsecahparam_t structure. 3290Sstevel@tonic-gate */ 3300Sstevel@tonic-gate /* ARGSUSED */ 3310Sstevel@tonic-gate static int 3320Sstevel@tonic-gate ipsecah_param_set(q, mp, value, cp, cr) 3330Sstevel@tonic-gate queue_t *q; 3340Sstevel@tonic-gate mblk_t *mp; 3350Sstevel@tonic-gate char *value; 3360Sstevel@tonic-gate caddr_t cp; 3370Sstevel@tonic-gate cred_t *cr; 3380Sstevel@tonic-gate { 3390Sstevel@tonic-gate ulong_t new_value; 3400Sstevel@tonic-gate ipsecahparam_t *ipsecahpa = (ipsecahparam_t *)cp; 3410Sstevel@tonic-gate 3420Sstevel@tonic-gate /* 3430Sstevel@tonic-gate * Fail the request if the new value does not lie within the 3440Sstevel@tonic-gate * required bounds. 3450Sstevel@tonic-gate */ 3460Sstevel@tonic-gate if (ddi_strtoul(value, NULL, 10, &new_value) != 0 || 3470Sstevel@tonic-gate new_value < ipsecahpa->ipsecah_param_min || 3480Sstevel@tonic-gate new_value > ipsecahpa->ipsecah_param_max) { 3490Sstevel@tonic-gate return (EINVAL); 3500Sstevel@tonic-gate } 3510Sstevel@tonic-gate 3520Sstevel@tonic-gate /* Set the new value */ 3530Sstevel@tonic-gate mutex_enter(&ipsecah_param_lock); 3540Sstevel@tonic-gate ipsecahpa->ipsecah_param_value = new_value; 3550Sstevel@tonic-gate mutex_exit(&ipsecah_param_lock); 3560Sstevel@tonic-gate return (0); 3570Sstevel@tonic-gate } 3580Sstevel@tonic-gate 3590Sstevel@tonic-gate /* 3600Sstevel@tonic-gate * Using lifetime NDD variables, fill in an extended combination's 3610Sstevel@tonic-gate * lifetime information. 3620Sstevel@tonic-gate */ 3630Sstevel@tonic-gate void 3640Sstevel@tonic-gate ipsecah_fill_defs(sadb_x_ecomb_t *ecomb) 3650Sstevel@tonic-gate { 3660Sstevel@tonic-gate ecomb->sadb_x_ecomb_soft_bytes = ipsecah_default_soft_bytes; 3670Sstevel@tonic-gate ecomb->sadb_x_ecomb_hard_bytes = ipsecah_default_hard_bytes; 3680Sstevel@tonic-gate ecomb->sadb_x_ecomb_soft_addtime = ipsecah_default_soft_addtime; 3690Sstevel@tonic-gate ecomb->sadb_x_ecomb_hard_addtime = ipsecah_default_hard_addtime; 3700Sstevel@tonic-gate ecomb->sadb_x_ecomb_soft_usetime = ipsecah_default_soft_usetime; 3710Sstevel@tonic-gate ecomb->sadb_x_ecomb_hard_usetime = ipsecah_default_hard_usetime; 3720Sstevel@tonic-gate } 3730Sstevel@tonic-gate 3740Sstevel@tonic-gate /* 3750Sstevel@tonic-gate * Initialize things for AH at module load time. 3760Sstevel@tonic-gate */ 3770Sstevel@tonic-gate boolean_t 3780Sstevel@tonic-gate ipsecah_ddi_init(void) 3790Sstevel@tonic-gate { 3800Sstevel@tonic-gate int count; 3810Sstevel@tonic-gate ipsecahparam_t *ahp = ipsecah_param_arr; 3820Sstevel@tonic-gate 3830Sstevel@tonic-gate for (count = A_CNT(ipsecah_param_arr); count-- > 0; ahp++) { 3840Sstevel@tonic-gate if (ahp->ipsecah_param_name != NULL && 3850Sstevel@tonic-gate ahp->ipsecah_param_name[0]) { 3860Sstevel@tonic-gate if (!nd_load(&ipsecah_g_nd, ahp->ipsecah_param_name, 3870Sstevel@tonic-gate ipsecah_param_get, ipsecah_param_set, 3880Sstevel@tonic-gate (caddr_t)ahp)) { 3890Sstevel@tonic-gate nd_free(&ipsecah_g_nd); 3900Sstevel@tonic-gate return (B_FALSE); 3910Sstevel@tonic-gate } 3920Sstevel@tonic-gate } 3930Sstevel@tonic-gate } 3940Sstevel@tonic-gate 3950Sstevel@tonic-gate if (!ah_kstat_init()) { 3960Sstevel@tonic-gate nd_free(&ipsecah_g_nd); 3970Sstevel@tonic-gate return (B_FALSE); 3980Sstevel@tonic-gate } 3990Sstevel@tonic-gate 4000Sstevel@tonic-gate ah_taskq = taskq_create("ah_taskq", 1, minclsyspri, 4010Sstevel@tonic-gate IPSEC_TASKQ_MIN, IPSEC_TASKQ_MAX, 0); 4020Sstevel@tonic-gate 4030Sstevel@tonic-gate ah_sadb.s_acquire_timeout = &ipsecah_acquire_timeout; 4040Sstevel@tonic-gate ah_sadb.s_acqfn = ah_send_acquire; 405564Ssommerfe 406564Ssommerfe sadbp_init("AH", &ah_sadb, SADB_SATYPE_AH, ah_hash_size); 4070Sstevel@tonic-gate 4080Sstevel@tonic-gate mutex_init(&ipsecah_param_lock, NULL, MUTEX_DEFAULT, 0); 4090Sstevel@tonic-gate 4100Sstevel@tonic-gate ip_drop_register(&ah_dropper, "IPsec AH"); 4110Sstevel@tonic-gate 4120Sstevel@tonic-gate return (B_TRUE); 4130Sstevel@tonic-gate } 4140Sstevel@tonic-gate 4150Sstevel@tonic-gate /* 4160Sstevel@tonic-gate * Destroy things for AH at module unload time. 4170Sstevel@tonic-gate */ 4180Sstevel@tonic-gate void 4190Sstevel@tonic-gate ipsecah_ddi_destroy(void) 4200Sstevel@tonic-gate { 4210Sstevel@tonic-gate ah1dbg(("In ddi_destroy.\n")); 4220Sstevel@tonic-gate 4230Sstevel@tonic-gate sadbp_destroy(&ah_sadb); 4240Sstevel@tonic-gate ip_drop_unregister(&ah_dropper); 4250Sstevel@tonic-gate taskq_destroy(ah_taskq); 4260Sstevel@tonic-gate mutex_destroy(&ipsecah_param_lock); 4270Sstevel@tonic-gate nd_free(&ipsecah_g_nd); 4280Sstevel@tonic-gate 4290Sstevel@tonic-gate kstat_delete(ah_ksp); 4300Sstevel@tonic-gate } 4310Sstevel@tonic-gate 4320Sstevel@tonic-gate /* 4330Sstevel@tonic-gate * AH module open routine. The module should be opened by keysock. 4340Sstevel@tonic-gate */ 4350Sstevel@tonic-gate /* ARGSUSED */ 4360Sstevel@tonic-gate static int 4370Sstevel@tonic-gate ipsecah_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp) 4380Sstevel@tonic-gate { 4390Sstevel@tonic-gate if (secpolicy_net_config(credp, B_FALSE) != 0) { 4400Sstevel@tonic-gate ah1dbg(("Non-privileged user trying to open ipsecah.\n")); 4410Sstevel@tonic-gate return (EPERM); 4420Sstevel@tonic-gate } 4430Sstevel@tonic-gate 4440Sstevel@tonic-gate if (q->q_ptr != NULL) 4450Sstevel@tonic-gate return (0); /* Re-open of an already open instance. */ 4460Sstevel@tonic-gate 4470Sstevel@tonic-gate if (sflag != MODOPEN) 4480Sstevel@tonic-gate return (EINVAL); 4490Sstevel@tonic-gate 4500Sstevel@tonic-gate /* 4510Sstevel@tonic-gate * ASSUMPTIONS (because I'm MT_OCEXCL): 4520Sstevel@tonic-gate * 4530Sstevel@tonic-gate * * I'm being pushed on top of IP for all my opens (incl. #1). 4540Sstevel@tonic-gate * * Only ipsecah_open() can write into ah_sadb.s_ip_q. 4550Sstevel@tonic-gate * * Because of this, I can check lazily for ah_sadb.s_ip_q. 4560Sstevel@tonic-gate * 4570Sstevel@tonic-gate * If these assumptions are wrong, I'm in BIG trouble... 4580Sstevel@tonic-gate */ 4590Sstevel@tonic-gate 4600Sstevel@tonic-gate q->q_ptr = q; /* just so I know I'm open */ 4610Sstevel@tonic-gate 4620Sstevel@tonic-gate if (ah_sadb.s_ip_q == NULL) { 4630Sstevel@tonic-gate struct T_unbind_req *tur; 4640Sstevel@tonic-gate 4650Sstevel@tonic-gate ah_sadb.s_ip_q = WR(q); 4660Sstevel@tonic-gate /* Allocate an unbind... */ 4670Sstevel@tonic-gate ah_ip_unbind = allocb(sizeof (struct T_unbind_req), BPRI_HI); 4680Sstevel@tonic-gate 4690Sstevel@tonic-gate /* 4700Sstevel@tonic-gate * Send down T_BIND_REQ to bind IPPROTO_AH. 4710Sstevel@tonic-gate * Handle the ACK here in AH. 4720Sstevel@tonic-gate */ 4730Sstevel@tonic-gate qprocson(q); 4740Sstevel@tonic-gate if (ah_ip_unbind == NULL || 4750Sstevel@tonic-gate !sadb_t_bind_req(ah_sadb.s_ip_q, IPPROTO_AH)) { 4760Sstevel@tonic-gate if (ah_ip_unbind != NULL) { 4770Sstevel@tonic-gate freeb(ah_ip_unbind); 4780Sstevel@tonic-gate ah_ip_unbind = NULL; 4790Sstevel@tonic-gate } 4800Sstevel@tonic-gate q->q_ptr = NULL; 4810Sstevel@tonic-gate qprocsoff(q); 4820Sstevel@tonic-gate return (ENOMEM); 4830Sstevel@tonic-gate } 4840Sstevel@tonic-gate 4850Sstevel@tonic-gate ah_ip_unbind->b_datap->db_type = M_PROTO; 4860Sstevel@tonic-gate tur = (struct T_unbind_req *)ah_ip_unbind->b_rptr; 4870Sstevel@tonic-gate tur->PRIM_type = T_UNBIND_REQ; 4880Sstevel@tonic-gate } else { 4890Sstevel@tonic-gate qprocson(q); 4900Sstevel@tonic-gate } 4910Sstevel@tonic-gate 4920Sstevel@tonic-gate /* 4930Sstevel@tonic-gate * For now, there's not much I can do. I'll be getting a message 4940Sstevel@tonic-gate * passed down to me from keysock (in my wput), and a T_BIND_ACK 4950Sstevel@tonic-gate * up from IP (in my rput). 4960Sstevel@tonic-gate */ 4970Sstevel@tonic-gate 4980Sstevel@tonic-gate return (0); 4990Sstevel@tonic-gate } 5000Sstevel@tonic-gate 5010Sstevel@tonic-gate /* 5020Sstevel@tonic-gate * AH module close routine. 5030Sstevel@tonic-gate */ 5040Sstevel@tonic-gate static int 5050Sstevel@tonic-gate ipsecah_close(queue_t *q) 5060Sstevel@tonic-gate { 5070Sstevel@tonic-gate /* 5080Sstevel@tonic-gate * If ah_sadb.s_ip_q is attached to this instance, send a 5090Sstevel@tonic-gate * T_UNBIND_REQ to IP for the instance before doing 5100Sstevel@tonic-gate * a qprocsoff(). 5110Sstevel@tonic-gate */ 5120Sstevel@tonic-gate if (WR(q) == ah_sadb.s_ip_q && ah_ip_unbind != NULL) { 5130Sstevel@tonic-gate putnext(WR(q), ah_ip_unbind); 5140Sstevel@tonic-gate ah_ip_unbind = NULL; 5150Sstevel@tonic-gate } 5160Sstevel@tonic-gate 5170Sstevel@tonic-gate /* 5180Sstevel@tonic-gate * Clean up q_ptr, if needed. 5190Sstevel@tonic-gate */ 5200Sstevel@tonic-gate qprocsoff(q); 5210Sstevel@tonic-gate 5220Sstevel@tonic-gate /* Keysock queue check is safe, because of OCEXCL perimeter. */ 5230Sstevel@tonic-gate 5240Sstevel@tonic-gate if (q == ah_pfkey_q) { 5250Sstevel@tonic-gate ah0dbg(("ipsecah_close: Ummm... keysock is closing AH.\n")); 5260Sstevel@tonic-gate ah_pfkey_q = NULL; 5270Sstevel@tonic-gate /* Detach qtimeouts. */ 5280Sstevel@tonic-gate (void) quntimeout(q, ah_event); 5290Sstevel@tonic-gate } 5300Sstevel@tonic-gate 5310Sstevel@tonic-gate if (WR(q) == ah_sadb.s_ip_q) { 5320Sstevel@tonic-gate /* 5330Sstevel@tonic-gate * If the ah_sadb.s_ip_q is attached to this instance, find 5340Sstevel@tonic-gate * another. The OCEXCL outer perimeter helps us here. 5350Sstevel@tonic-gate */ 5360Sstevel@tonic-gate 5370Sstevel@tonic-gate ah_sadb.s_ip_q = NULL; 5380Sstevel@tonic-gate 5390Sstevel@tonic-gate /* 5400Sstevel@tonic-gate * Find a replacement queue for ah_sadb.s_ip_q. 5410Sstevel@tonic-gate */ 5420Sstevel@tonic-gate if (ah_pfkey_q != NULL && ah_pfkey_q != RD(q)) { 5430Sstevel@tonic-gate /* 5440Sstevel@tonic-gate * See if we can use the pfkey_q. 5450Sstevel@tonic-gate */ 5460Sstevel@tonic-gate ah_sadb.s_ip_q = WR(ah_pfkey_q); 5470Sstevel@tonic-gate } 5480Sstevel@tonic-gate 5490Sstevel@tonic-gate if (ah_sadb.s_ip_q == NULL || 5500Sstevel@tonic-gate !sadb_t_bind_req(ah_sadb.s_ip_q, IPPROTO_AH)) { 5510Sstevel@tonic-gate ah1dbg(("ipsecah: Can't reassign ah_sadb.s_ip_q.\n")); 5520Sstevel@tonic-gate ah_sadb.s_ip_q = NULL; 5530Sstevel@tonic-gate } else { 5540Sstevel@tonic-gate ah_ip_unbind = allocb(sizeof (struct T_unbind_req), 5550Sstevel@tonic-gate BPRI_HI); 5560Sstevel@tonic-gate 5570Sstevel@tonic-gate if (ah_ip_unbind != NULL) { 5580Sstevel@tonic-gate struct T_unbind_req *tur; 5590Sstevel@tonic-gate 5600Sstevel@tonic-gate ah_ip_unbind->b_datap->db_type = M_PROTO; 5610Sstevel@tonic-gate tur = (struct T_unbind_req *) 5620Sstevel@tonic-gate ah_ip_unbind->b_rptr; 5630Sstevel@tonic-gate tur->PRIM_type = T_UNBIND_REQ; 5640Sstevel@tonic-gate } 5650Sstevel@tonic-gate /* If it's NULL, I can't do much here. */ 5660Sstevel@tonic-gate } 5670Sstevel@tonic-gate } 5680Sstevel@tonic-gate 5690Sstevel@tonic-gate return (0); 5700Sstevel@tonic-gate } 5710Sstevel@tonic-gate 5720Sstevel@tonic-gate /* 5730Sstevel@tonic-gate * AH module read put routine. 5740Sstevel@tonic-gate */ 5750Sstevel@tonic-gate /* ARGSUSED */ 5760Sstevel@tonic-gate static void 5770Sstevel@tonic-gate ipsecah_rput(queue_t *q, mblk_t *mp) 5780Sstevel@tonic-gate { 5790Sstevel@tonic-gate keysock_in_t *ksi; 5800Sstevel@tonic-gate int *addrtype; 5810Sstevel@tonic-gate ire_t *ire; 5820Sstevel@tonic-gate mblk_t *ire_mp, *last_mp; 5830Sstevel@tonic-gate 5840Sstevel@tonic-gate switch (mp->b_datap->db_type) { 5850Sstevel@tonic-gate case M_CTL: 5860Sstevel@tonic-gate /* 5870Sstevel@tonic-gate * IPsec request of some variety from IP. IPSEC_{IN,OUT} 5880Sstevel@tonic-gate * are the common cases, but even ICMP error messages from IP 5890Sstevel@tonic-gate * may rise up here. 5900Sstevel@tonic-gate * 5910Sstevel@tonic-gate * Ummmm, actually, this can also be the reflected KEYSOCK_IN 5920Sstevel@tonic-gate * message, with an IRE_DB_TYPE hung off at the end. 5930Sstevel@tonic-gate */ 5940Sstevel@tonic-gate switch (((ipsec_info_t *)(mp->b_rptr))->ipsec_info_type) { 5950Sstevel@tonic-gate case KEYSOCK_IN: 5960Sstevel@tonic-gate last_mp = mp; 5970Sstevel@tonic-gate while (last_mp->b_cont != NULL && 5980Sstevel@tonic-gate last_mp->b_cont->b_datap->db_type != IRE_DB_TYPE) 5990Sstevel@tonic-gate last_mp = last_mp->b_cont; 6000Sstevel@tonic-gate 6010Sstevel@tonic-gate if (last_mp->b_cont == NULL) { 6020Sstevel@tonic-gate freemsg(mp); 6030Sstevel@tonic-gate break; /* Out of switch. */ 6040Sstevel@tonic-gate } 6050Sstevel@tonic-gate 6060Sstevel@tonic-gate ire_mp = last_mp->b_cont; 6070Sstevel@tonic-gate last_mp->b_cont = NULL; 6080Sstevel@tonic-gate 6090Sstevel@tonic-gate ksi = (keysock_in_t *)mp->b_rptr; 6100Sstevel@tonic-gate 6110Sstevel@tonic-gate if (ksi->ks_in_srctype == KS_IN_ADDR_UNKNOWN) 6120Sstevel@tonic-gate addrtype = &ksi->ks_in_srctype; 6130Sstevel@tonic-gate else if (ksi->ks_in_dsttype == KS_IN_ADDR_UNKNOWN) 6140Sstevel@tonic-gate addrtype = &ksi->ks_in_dsttype; 6150Sstevel@tonic-gate else if (ksi->ks_in_proxytype == KS_IN_ADDR_UNKNOWN) 6160Sstevel@tonic-gate addrtype = &ksi->ks_in_proxytype; 6170Sstevel@tonic-gate 6180Sstevel@tonic-gate ire = (ire_t *)ire_mp->b_rptr; 6190Sstevel@tonic-gate 6200Sstevel@tonic-gate *addrtype = sadb_addrset(ire); 6210Sstevel@tonic-gate 6220Sstevel@tonic-gate freemsg(ire_mp); 6230Sstevel@tonic-gate if (ah_pfkey_q != NULL) { 6240Sstevel@tonic-gate /* 6250Sstevel@tonic-gate * Decrement counter to make up for 6260Sstevel@tonic-gate * auto-increment in ipsecah_wput(). 6270Sstevel@tonic-gate * I'm running all MT-hot through here, so 6280Sstevel@tonic-gate * don't worry about perimeters and lateral 6290Sstevel@tonic-gate * puts. 6300Sstevel@tonic-gate */ 6310Sstevel@tonic-gate AH_DEBUMP_STAT(keysock_in); 6320Sstevel@tonic-gate ipsecah_wput(WR(ah_pfkey_q), mp); 6330Sstevel@tonic-gate } else { 6340Sstevel@tonic-gate freemsg(mp); 6350Sstevel@tonic-gate } 6360Sstevel@tonic-gate break; 6370Sstevel@tonic-gate default: 6380Sstevel@tonic-gate freemsg(mp); 6390Sstevel@tonic-gate break; 6400Sstevel@tonic-gate } 6410Sstevel@tonic-gate break; 6420Sstevel@tonic-gate case M_PROTO: 6430Sstevel@tonic-gate case M_PCPROTO: 6440Sstevel@tonic-gate /* TPI message of some sort. */ 6450Sstevel@tonic-gate switch (*((t_scalar_t *)mp->b_rptr)) { 6460Sstevel@tonic-gate case T_BIND_ACK: 6470Sstevel@tonic-gate /* We expect this. */ 6480Sstevel@tonic-gate ah3dbg(("Thank you IP from AH for T_BIND_ACK\n")); 6490Sstevel@tonic-gate break; 6500Sstevel@tonic-gate case T_ERROR_ACK: 6510Sstevel@tonic-gate cmn_err(CE_WARN, 6520Sstevel@tonic-gate "ipsecah: AH received T_ERROR_ACK from IP."); 6530Sstevel@tonic-gate break; 6540Sstevel@tonic-gate case T_OK_ACK: 6550Sstevel@tonic-gate /* Probably from a (rarely sent) T_UNBIND_REQ. */ 6560Sstevel@tonic-gate break; 6570Sstevel@tonic-gate default: 6580Sstevel@tonic-gate ah1dbg(("Unknown M_{,PC}PROTO message.\n")); 6590Sstevel@tonic-gate } 6600Sstevel@tonic-gate freemsg(mp); 6610Sstevel@tonic-gate break; 6620Sstevel@tonic-gate default: 6630Sstevel@tonic-gate /* For now, passthru message. */ 6640Sstevel@tonic-gate ah2dbg(("AH got unknown mblk type %d.\n", 6650Sstevel@tonic-gate mp->b_datap->db_type)); 6660Sstevel@tonic-gate putnext(q, mp); 6670Sstevel@tonic-gate } 6680Sstevel@tonic-gate } 6690Sstevel@tonic-gate 6700Sstevel@tonic-gate /* 6710Sstevel@tonic-gate * Construct an SADB_REGISTER message with the current algorithms. 6720Sstevel@tonic-gate */ 6730Sstevel@tonic-gate static boolean_t 6740Sstevel@tonic-gate ah_register_out(uint32_t sequence, uint32_t pid, uint_t serial) 6750Sstevel@tonic-gate { 6760Sstevel@tonic-gate mblk_t *mp; 6770Sstevel@tonic-gate boolean_t rc = B_TRUE; 6780Sstevel@tonic-gate sadb_msg_t *samsg; 6790Sstevel@tonic-gate sadb_supported_t *sasupp; 6800Sstevel@tonic-gate sadb_alg_t *saalg; 6810Sstevel@tonic-gate uint_t allocsize = sizeof (*samsg); 6820Sstevel@tonic-gate uint_t i, numalgs_snap; 6830Sstevel@tonic-gate ipsec_alginfo_t **authalgs; 6840Sstevel@tonic-gate uint_t num_aalgs; 6850Sstevel@tonic-gate 6860Sstevel@tonic-gate /* Allocate the KEYSOCK_OUT. */ 6870Sstevel@tonic-gate mp = sadb_keysock_out(serial); 6880Sstevel@tonic-gate if (mp == NULL) { 6890Sstevel@tonic-gate ah0dbg(("ah_register_out: couldn't allocate mblk.\n")); 6900Sstevel@tonic-gate return (B_FALSE); 6910Sstevel@tonic-gate } 6920Sstevel@tonic-gate 6930Sstevel@tonic-gate /* 6940Sstevel@tonic-gate * Allocate the PF_KEY message that follows KEYSOCK_OUT. 6950Sstevel@tonic-gate * The alg reader lock needs to be held while allocating 6960Sstevel@tonic-gate * the variable part (i.e. the algorithms) of the message. 6970Sstevel@tonic-gate */ 6980Sstevel@tonic-gate 6990Sstevel@tonic-gate mutex_enter(&alg_lock); 7000Sstevel@tonic-gate 7010Sstevel@tonic-gate /* 7020Sstevel@tonic-gate * Return only valid algorithms, so the number of algorithms 7030Sstevel@tonic-gate * to send up may be less than the number of algorithm entries 7040Sstevel@tonic-gate * in the table. 7050Sstevel@tonic-gate */ 7060Sstevel@tonic-gate authalgs = ipsec_alglists[IPSEC_ALG_AUTH]; 7070Sstevel@tonic-gate for (num_aalgs = 0, i = 0; i < IPSEC_MAX_ALGS; i++) 7080Sstevel@tonic-gate if (authalgs[i] != NULL && ALG_VALID(authalgs[i])) 7090Sstevel@tonic-gate num_aalgs++; 7100Sstevel@tonic-gate 7110Sstevel@tonic-gate /* 7120Sstevel@tonic-gate * Fill SADB_REGISTER message's algorithm descriptors. Hold 7130Sstevel@tonic-gate * down the lock while filling it. 7140Sstevel@tonic-gate */ 7150Sstevel@tonic-gate if (num_aalgs != 0) { 7160Sstevel@tonic-gate allocsize += (num_aalgs * sizeof (*saalg)); 7170Sstevel@tonic-gate allocsize += sizeof (*sasupp); 7180Sstevel@tonic-gate } 7190Sstevel@tonic-gate mp->b_cont = allocb(allocsize, BPRI_HI); 7200Sstevel@tonic-gate if (mp->b_cont == NULL) { 7210Sstevel@tonic-gate mutex_exit(&alg_lock); 7220Sstevel@tonic-gate freemsg(mp); 7230Sstevel@tonic-gate return (B_FALSE); 7240Sstevel@tonic-gate } 7250Sstevel@tonic-gate 7260Sstevel@tonic-gate mp->b_cont->b_wptr += allocsize; 7270Sstevel@tonic-gate if (num_aalgs != 0) { 7280Sstevel@tonic-gate 7290Sstevel@tonic-gate saalg = (sadb_alg_t *)(mp->b_cont->b_rptr + sizeof (*samsg) + 7300Sstevel@tonic-gate sizeof (*sasupp)); 7310Sstevel@tonic-gate ASSERT(((ulong_t)saalg & 0x7) == 0); 7320Sstevel@tonic-gate 7330Sstevel@tonic-gate numalgs_snap = 0; 7340Sstevel@tonic-gate for (i = 0; 7350Sstevel@tonic-gate ((i < IPSEC_MAX_ALGS) && (numalgs_snap < num_aalgs)); i++) { 7360Sstevel@tonic-gate if (authalgs[i] == NULL || !ALG_VALID(authalgs[i])) 7370Sstevel@tonic-gate continue; 7380Sstevel@tonic-gate 7390Sstevel@tonic-gate saalg->sadb_alg_id = authalgs[i]->alg_id; 7400Sstevel@tonic-gate saalg->sadb_alg_ivlen = 0; 7410Sstevel@tonic-gate saalg->sadb_alg_minbits = authalgs[i]->alg_ef_minbits; 7420Sstevel@tonic-gate saalg->sadb_alg_maxbits = authalgs[i]->alg_ef_maxbits; 7430Sstevel@tonic-gate saalg->sadb_x_alg_increment = 7440Sstevel@tonic-gate authalgs[i]->alg_increment; 7450Sstevel@tonic-gate saalg->sadb_x_alg_defincr = authalgs[i]->alg_ef_default; 7460Sstevel@tonic-gate numalgs_snap++; 7470Sstevel@tonic-gate saalg++; 7480Sstevel@tonic-gate } 7490Sstevel@tonic-gate ASSERT(numalgs_snap == num_aalgs); 7500Sstevel@tonic-gate #ifdef DEBUG 7510Sstevel@tonic-gate /* 7520Sstevel@tonic-gate * Reality check to make sure I snagged all of the 7530Sstevel@tonic-gate * algorithms. 7540Sstevel@tonic-gate */ 7550Sstevel@tonic-gate for (; i < IPSEC_MAX_ALGS; i++) 7560Sstevel@tonic-gate if (authalgs[i] != NULL && ALG_VALID(authalgs[i])) 7570Sstevel@tonic-gate cmn_err(CE_PANIC, 7580Sstevel@tonic-gate "ah_register_out()! Missed #%d.\n", i); 7590Sstevel@tonic-gate #endif /* DEBUG */ 7600Sstevel@tonic-gate } 7610Sstevel@tonic-gate 7620Sstevel@tonic-gate mutex_exit(&alg_lock); 7630Sstevel@tonic-gate 7640Sstevel@tonic-gate /* Now fill the restof the SADB_REGISTER message. */ 7650Sstevel@tonic-gate 7660Sstevel@tonic-gate samsg = (sadb_msg_t *)mp->b_cont->b_rptr; 7670Sstevel@tonic-gate samsg->sadb_msg_version = PF_KEY_V2; 7680Sstevel@tonic-gate samsg->sadb_msg_type = SADB_REGISTER; 7690Sstevel@tonic-gate samsg->sadb_msg_errno = 0; 7700Sstevel@tonic-gate samsg->sadb_msg_satype = SADB_SATYPE_AH; 7710Sstevel@tonic-gate samsg->sadb_msg_len = SADB_8TO64(allocsize); 7720Sstevel@tonic-gate samsg->sadb_msg_reserved = 0; 7730Sstevel@tonic-gate /* 7740Sstevel@tonic-gate * Assume caller has sufficient sequence/pid number info. If it's one 7750Sstevel@tonic-gate * from me over a new alg., I could give two hoots about sequence. 7760Sstevel@tonic-gate */ 7770Sstevel@tonic-gate samsg->sadb_msg_seq = sequence; 7780Sstevel@tonic-gate samsg->sadb_msg_pid = pid; 7790Sstevel@tonic-gate 7800Sstevel@tonic-gate if (allocsize > sizeof (*samsg)) { 7810Sstevel@tonic-gate sasupp = (sadb_supported_t *)(samsg + 1); 7820Sstevel@tonic-gate sasupp->sadb_supported_len = 7830Sstevel@tonic-gate SADB_8TO64(allocsize - sizeof (sadb_msg_t)); 7840Sstevel@tonic-gate sasupp->sadb_supported_exttype = SADB_EXT_SUPPORTED_AUTH; 7850Sstevel@tonic-gate sasupp->sadb_supported_reserved = 0; 7860Sstevel@tonic-gate } 7870Sstevel@tonic-gate 7880Sstevel@tonic-gate if (ah_pfkey_q != NULL) 7890Sstevel@tonic-gate putnext(ah_pfkey_q, mp); 7900Sstevel@tonic-gate else { 7910Sstevel@tonic-gate rc = B_FALSE; 7920Sstevel@tonic-gate freemsg(mp); 7930Sstevel@tonic-gate } 7940Sstevel@tonic-gate 7950Sstevel@tonic-gate return (rc); 7960Sstevel@tonic-gate } 7970Sstevel@tonic-gate 7980Sstevel@tonic-gate /* 7990Sstevel@tonic-gate * Invoked when the algorithm table changes. Causes SADB_REGISTER 8000Sstevel@tonic-gate * messages continaining the current list of algorithms to be 8010Sstevel@tonic-gate * sent up to the AH listeners. 8020Sstevel@tonic-gate */ 8030Sstevel@tonic-gate void 8040Sstevel@tonic-gate ipsecah_algs_changed(void) 8050Sstevel@tonic-gate { 8060Sstevel@tonic-gate /* 8070Sstevel@tonic-gate * Time to send a PF_KEY SADB_REGISTER message to AH listeners 8080Sstevel@tonic-gate * everywhere. (The function itself checks for NULL ah_pfkey_q.) 8090Sstevel@tonic-gate */ 8100Sstevel@tonic-gate (void) ah_register_out(0, 0, 0); 8110Sstevel@tonic-gate } 8120Sstevel@tonic-gate 8130Sstevel@tonic-gate /* 8140Sstevel@tonic-gate * Stub function that taskq_dispatch() invokes to take the mblk (in arg) 8150Sstevel@tonic-gate * and put() it into AH and STREAMS again. 8160Sstevel@tonic-gate */ 8170Sstevel@tonic-gate static void 8180Sstevel@tonic-gate inbound_task(void *arg) 8190Sstevel@tonic-gate { 8200Sstevel@tonic-gate ah_t *ah; 8210Sstevel@tonic-gate mblk_t *mp = (mblk_t *)arg; 8220Sstevel@tonic-gate ipsec_in_t *ii = (ipsec_in_t *)mp->b_rptr; 8230Sstevel@tonic-gate int ipsec_rc; 8240Sstevel@tonic-gate 8250Sstevel@tonic-gate ah2dbg(("in AH inbound_task")); 8260Sstevel@tonic-gate 8270Sstevel@tonic-gate ah = ipsec_inbound_ah_sa(mp); 8280Sstevel@tonic-gate if (ah == NULL) 8290Sstevel@tonic-gate return; 8300Sstevel@tonic-gate ASSERT(ii->ipsec_in_ah_sa != NULL); 8310Sstevel@tonic-gate ipsec_rc = ii->ipsec_in_ah_sa->ipsa_input_func(mp, ah); 8320Sstevel@tonic-gate if (ipsec_rc != IPSEC_STATUS_SUCCESS) 8330Sstevel@tonic-gate return; 8340Sstevel@tonic-gate ip_fanout_proto_again(mp, NULL, NULL, NULL); 8350Sstevel@tonic-gate } 8360Sstevel@tonic-gate 8370Sstevel@tonic-gate 8380Sstevel@tonic-gate /* 8390Sstevel@tonic-gate * Now that weak-key passed, actually ADD the security association, and 8400Sstevel@tonic-gate * send back a reply ADD message. 8410Sstevel@tonic-gate */ 8420Sstevel@tonic-gate static int 8430Sstevel@tonic-gate ah_add_sa_finish(mblk_t *mp, sadb_msg_t *samsg, keysock_in_t *ksi) 8440Sstevel@tonic-gate { 845691Ssommerfe isaf_t *primary, *secondary, *inbound, *outbound; 8460Sstevel@tonic-gate sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA]; 8470Sstevel@tonic-gate sadb_address_t *dstext = 8480Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST]; 8490Sstevel@tonic-gate struct sockaddr_in *dst; 8500Sstevel@tonic-gate struct sockaddr_in6 *dst6; 8510Sstevel@tonic-gate boolean_t is_ipv4, clone = B_FALSE, is_inbound = B_FALSE; 8520Sstevel@tonic-gate uint32_t *dstaddr; 8530Sstevel@tonic-gate ipsa_t *larval; 8540Sstevel@tonic-gate ipsacq_t *acqrec; 8550Sstevel@tonic-gate iacqf_t *acq_bucket; 8560Sstevel@tonic-gate mblk_t *acq_msgs = NULL; 8570Sstevel@tonic-gate mblk_t *lpkt; 8580Sstevel@tonic-gate int rc; 8590Sstevel@tonic-gate sadb_t *sp; 8600Sstevel@tonic-gate int outhash; 8610Sstevel@tonic-gate 8620Sstevel@tonic-gate /* 8630Sstevel@tonic-gate * Locate the appropriate table(s). 8640Sstevel@tonic-gate */ 8650Sstevel@tonic-gate 8660Sstevel@tonic-gate dst = (struct sockaddr_in *)(dstext + 1); 8670Sstevel@tonic-gate dst6 = (struct sockaddr_in6 *)dst; 8680Sstevel@tonic-gate is_ipv4 = (dst->sin_family == AF_INET); 8690Sstevel@tonic-gate if (is_ipv4) { 8700Sstevel@tonic-gate sp = &ah_sadb.s_v4; 8710Sstevel@tonic-gate dstaddr = (uint32_t *)(&dst->sin_addr); 872564Ssommerfe outhash = OUTBOUND_HASH_V4(sp, *(ipaddr_t *)dstaddr); 8730Sstevel@tonic-gate } else { 8740Sstevel@tonic-gate ASSERT(dst->sin_family == AF_INET6); 8750Sstevel@tonic-gate sp = &ah_sadb.s_v6; 8760Sstevel@tonic-gate dstaddr = (uint32_t *)(&dst6->sin6_addr); 877564Ssommerfe outhash = OUTBOUND_HASH_V6(sp, *(in6_addr_t *)dstaddr); 8780Sstevel@tonic-gate } 8790Sstevel@tonic-gate 880564Ssommerfe inbound = INBOUND_BUCKET(sp, assoc->sadb_sa_spi); 881691Ssommerfe outbound = &sp->sdb_of[outhash]; 8820Sstevel@tonic-gate 8830Sstevel@tonic-gate switch (ksi->ks_in_dsttype) { 8840Sstevel@tonic-gate case KS_IN_ADDR_MBCAST: 8850Sstevel@tonic-gate clone = B_TRUE; /* All mcast SAs can be bidirectional */ 8860Sstevel@tonic-gate /* FALLTHRU */ 8870Sstevel@tonic-gate case KS_IN_ADDR_ME: 8880Sstevel@tonic-gate primary = inbound; 889691Ssommerfe secondary = outbound; 8900Sstevel@tonic-gate /* 8910Sstevel@tonic-gate * If the source address is either one of mine, or unspecified 8920Sstevel@tonic-gate * (which is best summed up by saying "not 'not mine'"), 8930Sstevel@tonic-gate * then the association is potentially bi-directional, 8940Sstevel@tonic-gate * in that it can be used for inbound traffic and outbound 8950Sstevel@tonic-gate * traffic. The best example of such and SA is a multicast 8960Sstevel@tonic-gate * SA (which allows me to receive the outbound traffic). 8970Sstevel@tonic-gate */ 8980Sstevel@tonic-gate if (ksi->ks_in_srctype != KS_IN_ADDR_NOTME) 8990Sstevel@tonic-gate clone = B_TRUE; 9000Sstevel@tonic-gate is_inbound = B_TRUE; 9010Sstevel@tonic-gate break; 9020Sstevel@tonic-gate case KS_IN_ADDR_NOTME: 903691Ssommerfe primary = outbound; 904564Ssommerfe secondary = inbound; 9050Sstevel@tonic-gate /* 9060Sstevel@tonic-gate * If the source address literally not mine (either 9070Sstevel@tonic-gate * unspecified or not mine), then this SA may have an 9080Sstevel@tonic-gate * address that WILL be mine after some configuration. 9090Sstevel@tonic-gate * We pay the price for this by making it a bi-directional 9100Sstevel@tonic-gate * SA. 9110Sstevel@tonic-gate */ 9120Sstevel@tonic-gate if (ksi->ks_in_srctype != KS_IN_ADDR_ME) 9130Sstevel@tonic-gate clone = B_TRUE; 9140Sstevel@tonic-gate break; 9150Sstevel@tonic-gate default: 9160Sstevel@tonic-gate samsg->sadb_x_msg_diagnostic = SADB_X_DIAGNOSTIC_BAD_DST; 9170Sstevel@tonic-gate return (EINVAL); 9180Sstevel@tonic-gate } 9190Sstevel@tonic-gate 9200Sstevel@tonic-gate /* 9210Sstevel@tonic-gate * Find a ACQUIRE list entry if possible. If we've added an SA that 9220Sstevel@tonic-gate * suits the needs of an ACQUIRE list entry, we can eliminate the 9230Sstevel@tonic-gate * ACQUIRE list entry and transmit the enqueued packets. Use the 9240Sstevel@tonic-gate * high-bit of the sequence number to queue it. Key off destination 9250Sstevel@tonic-gate * addr, and change acqrec's state. 9260Sstevel@tonic-gate */ 9270Sstevel@tonic-gate 9280Sstevel@tonic-gate if (samsg->sadb_msg_seq & IACQF_LOWEST_SEQ) { 9290Sstevel@tonic-gate acq_bucket = &sp->sdb_acq[outhash]; 9300Sstevel@tonic-gate mutex_enter(&acq_bucket->iacqf_lock); 9310Sstevel@tonic-gate for (acqrec = acq_bucket->iacqf_ipsacq; acqrec != NULL; 9320Sstevel@tonic-gate acqrec = acqrec->ipsacq_next) { 9330Sstevel@tonic-gate mutex_enter(&acqrec->ipsacq_lock); 9340Sstevel@tonic-gate /* 9350Sstevel@tonic-gate * Q: I only check sequence. Should I check dst? 9360Sstevel@tonic-gate * A: Yes, check dest because those are the packets 9370Sstevel@tonic-gate * that are queued up. 9380Sstevel@tonic-gate */ 9390Sstevel@tonic-gate if (acqrec->ipsacq_seq == samsg->sadb_msg_seq && 9400Sstevel@tonic-gate IPSA_ARE_ADDR_EQUAL(dstaddr, 9410Sstevel@tonic-gate acqrec->ipsacq_dstaddr, acqrec->ipsacq_addrfam)) 9420Sstevel@tonic-gate break; 9430Sstevel@tonic-gate mutex_exit(&acqrec->ipsacq_lock); 9440Sstevel@tonic-gate } 9450Sstevel@tonic-gate if (acqrec != NULL) { 9460Sstevel@tonic-gate /* 9470Sstevel@tonic-gate * AHA! I found an ACQUIRE record for this SA. 9480Sstevel@tonic-gate * Grab the msg list, and free the acquire record. 9490Sstevel@tonic-gate * I already am holding the lock for this record, 9500Sstevel@tonic-gate * so all I have to do is free it. 9510Sstevel@tonic-gate */ 9520Sstevel@tonic-gate acq_msgs = acqrec->ipsacq_mp; 9530Sstevel@tonic-gate acqrec->ipsacq_mp = NULL; 9540Sstevel@tonic-gate mutex_exit(&acqrec->ipsacq_lock); 9550Sstevel@tonic-gate sadb_destroy_acquire(acqrec); 9560Sstevel@tonic-gate } 9570Sstevel@tonic-gate mutex_exit(&acq_bucket->iacqf_lock); 9580Sstevel@tonic-gate } 9590Sstevel@tonic-gate 9600Sstevel@tonic-gate /* 9610Sstevel@tonic-gate * Find PF_KEY message, and see if I'm an update. If so, find entry 9620Sstevel@tonic-gate * in larval list (if there). 9630Sstevel@tonic-gate */ 9640Sstevel@tonic-gate 9650Sstevel@tonic-gate larval = NULL; 9660Sstevel@tonic-gate 9670Sstevel@tonic-gate if (samsg->sadb_msg_type == SADB_UPDATE) { 9680Sstevel@tonic-gate mutex_enter(&inbound->isaf_lock); 9690Sstevel@tonic-gate larval = ipsec_getassocbyspi(inbound, assoc->sadb_sa_spi, 9700Sstevel@tonic-gate ALL_ZEROES_PTR, dstaddr, dst->sin_family); 9710Sstevel@tonic-gate mutex_exit(&inbound->isaf_lock); 9720Sstevel@tonic-gate 9730Sstevel@tonic-gate if ((larval == NULL) || 9740Sstevel@tonic-gate (larval->ipsa_state != IPSA_STATE_LARVAL)) { 9750Sstevel@tonic-gate ah0dbg(("Larval update, but larval disappeared.\n")); 9760Sstevel@tonic-gate return (ESRCH); 9770Sstevel@tonic-gate } /* Else sadb_common_add unlinks it for me! */ 9780Sstevel@tonic-gate } 9790Sstevel@tonic-gate 9800Sstevel@tonic-gate lpkt = NULL; 9810Sstevel@tonic-gate if (larval != NULL) 9820Sstevel@tonic-gate lpkt = sadb_clear_lpkt(larval); 9830Sstevel@tonic-gate 9840Sstevel@tonic-gate rc = sadb_common_add(ah_sadb.s_ip_q, ah_pfkey_q, mp, samsg, ksi, 9850Sstevel@tonic-gate primary, secondary, larval, clone, is_inbound); 9860Sstevel@tonic-gate 9870Sstevel@tonic-gate /* 9880Sstevel@tonic-gate * How much more stack will I create with all of these 9890Sstevel@tonic-gate * ah_inbound_* and ah_outbound_*() calls? 9900Sstevel@tonic-gate */ 9910Sstevel@tonic-gate 9920Sstevel@tonic-gate 9930Sstevel@tonic-gate if (rc == 0 && lpkt != NULL) 9940Sstevel@tonic-gate rc = !taskq_dispatch(ah_taskq, inbound_task, 9950Sstevel@tonic-gate (void *) lpkt, TQ_NOSLEEP); 9960Sstevel@tonic-gate 9970Sstevel@tonic-gate if (rc != 0) { 9980Sstevel@tonic-gate ip_drop_packet(lpkt, B_TRUE, NULL, NULL, 9990Sstevel@tonic-gate &ipdrops_sadb_inlarval_timeout, &ah_dropper); 10000Sstevel@tonic-gate } 10010Sstevel@tonic-gate 10020Sstevel@tonic-gate while (acq_msgs != NULL) { 10030Sstevel@tonic-gate mblk_t *mp = acq_msgs; 10040Sstevel@tonic-gate 10050Sstevel@tonic-gate acq_msgs = acq_msgs->b_next; 10060Sstevel@tonic-gate mp->b_next = NULL; 10070Sstevel@tonic-gate if (rc == 0) { 10080Sstevel@tonic-gate ipsec_out_t *io = (ipsec_out_t *)mp->b_rptr; 10090Sstevel@tonic-gate 10100Sstevel@tonic-gate ASSERT(ah_sadb.s_ip_q != NULL); 10110Sstevel@tonic-gate if (ipsec_outbound_sa(mp, IPPROTO_AH)) { 10120Sstevel@tonic-gate io->ipsec_out_ah_done = B_TRUE; 10130Sstevel@tonic-gate if (ah_outbound(mp) == IPSEC_STATUS_SUCCESS) { 10140Sstevel@tonic-gate ipha_t *ipha = (ipha_t *) 10150Sstevel@tonic-gate mp->b_cont->b_rptr; 10160Sstevel@tonic-gate if (is_ipv4) { 10170Sstevel@tonic-gate ip_wput_ipsec_out(NULL, mp, 10180Sstevel@tonic-gate ipha, NULL, NULL); 10190Sstevel@tonic-gate } else { 10200Sstevel@tonic-gate ip6_t *ip6h = (ip6_t *)ipha; 10210Sstevel@tonic-gate ip_wput_ipsec_out_v6(NULL, 10220Sstevel@tonic-gate mp, ip6h, NULL, NULL); 10230Sstevel@tonic-gate } 10240Sstevel@tonic-gate } 10250Sstevel@tonic-gate continue; 10260Sstevel@tonic-gate } 10270Sstevel@tonic-gate } 10280Sstevel@tonic-gate AH_BUMP_STAT(out_discards); 10290Sstevel@tonic-gate ip_drop_packet(mp, B_FALSE, NULL, NULL, 10300Sstevel@tonic-gate &ipdrops_sadb_acquire_timeout, &ah_dropper); 10310Sstevel@tonic-gate } 10320Sstevel@tonic-gate 10330Sstevel@tonic-gate return (rc); 10340Sstevel@tonic-gate } 10350Sstevel@tonic-gate 10360Sstevel@tonic-gate /* 10370Sstevel@tonic-gate * Add new AH security association. This may become a generic AH/ESP 10380Sstevel@tonic-gate * routine eventually. 10390Sstevel@tonic-gate */ 10400Sstevel@tonic-gate static int 10410Sstevel@tonic-gate ah_add_sa(mblk_t *mp, keysock_in_t *ksi, int *diagnostic) 10420Sstevel@tonic-gate { 10430Sstevel@tonic-gate sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA]; 10440Sstevel@tonic-gate sadb_address_t *srcext = 10450Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_SRC]; 10460Sstevel@tonic-gate sadb_address_t *dstext = 10470Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST]; 10480Sstevel@tonic-gate sadb_key_t *key = (sadb_key_t *)ksi->ks_in_extv[SADB_EXT_KEY_AUTH]; 10490Sstevel@tonic-gate struct sockaddr_in *src, *dst; 10500Sstevel@tonic-gate /* We don't need sockaddr_in6 for now. */ 10510Sstevel@tonic-gate sadb_lifetime_t *soft = 10520Sstevel@tonic-gate (sadb_lifetime_t *)ksi->ks_in_extv[SADB_EXT_LIFETIME_SOFT]; 10530Sstevel@tonic-gate sadb_lifetime_t *hard = 10540Sstevel@tonic-gate (sadb_lifetime_t *)ksi->ks_in_extv[SADB_EXT_LIFETIME_HARD]; 10550Sstevel@tonic-gate ipsec_alginfo_t *aalg; 10560Sstevel@tonic-gate 10570Sstevel@tonic-gate /* I need certain extensions present for an ADD message. */ 10580Sstevel@tonic-gate if (srcext == NULL) { 10590Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_SRC; 10600Sstevel@tonic-gate return (EINVAL); 10610Sstevel@tonic-gate } 10620Sstevel@tonic-gate if (dstext == NULL) { 10630Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_DST; 10640Sstevel@tonic-gate return (EINVAL); 10650Sstevel@tonic-gate } 10660Sstevel@tonic-gate if (assoc == NULL) { 10670Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_SA; 10680Sstevel@tonic-gate return (EINVAL); 10690Sstevel@tonic-gate } 10700Sstevel@tonic-gate if (key == NULL) { 10710Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_AKEY; 10720Sstevel@tonic-gate return (EINVAL); 10730Sstevel@tonic-gate } 10740Sstevel@tonic-gate 10750Sstevel@tonic-gate src = (struct sockaddr_in *)(srcext + 1); 10760Sstevel@tonic-gate dst = (struct sockaddr_in *)(dstext + 1); 10770Sstevel@tonic-gate 10780Sstevel@tonic-gate /* Sundry ADD-specific reality checks. */ 10790Sstevel@tonic-gate /* XXX STATS : Logging/stats here? */ 10800Sstevel@tonic-gate 10810Sstevel@tonic-gate if (assoc->sadb_sa_state != SADB_SASTATE_MATURE) { 10820Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_BAD_SASTATE; 10830Sstevel@tonic-gate return (EINVAL); 10840Sstevel@tonic-gate } 10850Sstevel@tonic-gate if (assoc->sadb_sa_encrypt != SADB_EALG_NONE) { 10860Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_ENCR_NOTSUPP; 10870Sstevel@tonic-gate return (EINVAL); 10880Sstevel@tonic-gate } 10890Sstevel@tonic-gate if (assoc->sadb_sa_flags & ~(SADB_SAFLAGS_NOREPLAY)) { 10900Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_BAD_SAFLAGS; 10910Sstevel@tonic-gate return (EINVAL); 10920Sstevel@tonic-gate } 10930Sstevel@tonic-gate 10940Sstevel@tonic-gate if ((*diagnostic = sadb_hardsoftchk(hard, soft)) != 0) 10950Sstevel@tonic-gate return (EINVAL); 10960Sstevel@tonic-gate 10970Sstevel@tonic-gate if (src->sin_family != dst->sin_family) { 10980Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_AF_MISMATCH; 10990Sstevel@tonic-gate return (EINVAL); 11000Sstevel@tonic-gate } 11010Sstevel@tonic-gate 11020Sstevel@tonic-gate /* Stuff I don't support, for now. XXX Diagnostic? */ 11030Sstevel@tonic-gate if (ksi->ks_in_extv[SADB_EXT_LIFETIME_CURRENT] != NULL || 11040Sstevel@tonic-gate ksi->ks_in_extv[SADB_EXT_SENSITIVITY] != NULL) 11050Sstevel@tonic-gate return (EOPNOTSUPP); 11060Sstevel@tonic-gate 11070Sstevel@tonic-gate /* 11080Sstevel@tonic-gate * XXX Policy : I'm not checking identities or sensitivity 11090Sstevel@tonic-gate * labels at this time, but if I did, I'd do them here, before I sent 11100Sstevel@tonic-gate * the weak key check up to the algorithm. 11110Sstevel@tonic-gate */ 11120Sstevel@tonic-gate 11130Sstevel@tonic-gate /* verify that there is a mapping for the specified algorithm */ 11140Sstevel@tonic-gate mutex_enter(&alg_lock); 11150Sstevel@tonic-gate aalg = ipsec_alglists[IPSEC_ALG_AUTH][assoc->sadb_sa_auth]; 11160Sstevel@tonic-gate if (aalg == NULL || !ALG_VALID(aalg)) { 11170Sstevel@tonic-gate mutex_exit(&alg_lock); 11180Sstevel@tonic-gate ah1dbg(("Couldn't find auth alg #%d.\n", assoc->sadb_sa_auth)); 11190Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_BAD_AALG; 11200Sstevel@tonic-gate return (EINVAL); 11210Sstevel@tonic-gate } 11220Sstevel@tonic-gate ASSERT(aalg->alg_mech_type != CRYPTO_MECHANISM_INVALID); 11230Sstevel@tonic-gate 11240Sstevel@tonic-gate /* sanity check key sizes */ 11250Sstevel@tonic-gate if (!ipsec_valid_key_size(key->sadb_key_bits, aalg)) { 11260Sstevel@tonic-gate mutex_exit(&alg_lock); 11270Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_BAD_AKEYBITS; 11280Sstevel@tonic-gate return (EINVAL); 11290Sstevel@tonic-gate } 11300Sstevel@tonic-gate 11310Sstevel@tonic-gate /* check key and fix parity if needed */ 11320Sstevel@tonic-gate if (ipsec_check_key(aalg->alg_mech_type, key, B_TRUE, 11330Sstevel@tonic-gate diagnostic) != 0) { 11340Sstevel@tonic-gate mutex_exit(&alg_lock); 11350Sstevel@tonic-gate return (EINVAL); 11360Sstevel@tonic-gate } 11370Sstevel@tonic-gate 11380Sstevel@tonic-gate mutex_exit(&alg_lock); 11390Sstevel@tonic-gate 11400Sstevel@tonic-gate return (ah_add_sa_finish(mp, (sadb_msg_t *)mp->b_cont->b_rptr, ksi)); 11410Sstevel@tonic-gate } 11420Sstevel@tonic-gate 11430Sstevel@tonic-gate /* 11440Sstevel@tonic-gate * Update a security association. Updates come in two varieties. The first 11450Sstevel@tonic-gate * is an update of lifetimes on a non-larval SA. The second is an update of 11460Sstevel@tonic-gate * a larval SA, which ends up looking a lot more like an add. 11470Sstevel@tonic-gate */ 11480Sstevel@tonic-gate static int 11490Sstevel@tonic-gate ah_update_sa(mblk_t *mp, keysock_in_t *ksi, int *diagnostic) 11500Sstevel@tonic-gate { 11510Sstevel@tonic-gate sadb_address_t *dstext = 11520Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST]; 11530Sstevel@tonic-gate struct sockaddr_in *sin; 11540Sstevel@tonic-gate 11550Sstevel@tonic-gate if (dstext == NULL) { 11560Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_DST; 11570Sstevel@tonic-gate return (EINVAL); 11580Sstevel@tonic-gate } 11590Sstevel@tonic-gate sin = (struct sockaddr_in *)(dstext + 1); 11600Sstevel@tonic-gate return (sadb_update_sa(mp, ksi, 11610Sstevel@tonic-gate (sin->sin_family == AF_INET6) ? &ah_sadb.s_v6 : &ah_sadb.s_v4, 11620Sstevel@tonic-gate diagnostic, ah_pfkey_q, ah_add_sa)); 11630Sstevel@tonic-gate } 11640Sstevel@tonic-gate 11650Sstevel@tonic-gate /* 11660Sstevel@tonic-gate * Delete a security association. This is REALLY likely to be code common to 11670Sstevel@tonic-gate * both AH and ESP. Find the association, then unlink it. 11680Sstevel@tonic-gate */ 11690Sstevel@tonic-gate static int 11700Sstevel@tonic-gate ah_del_sa(mblk_t *mp, keysock_in_t *ksi, int *diagnostic) 11710Sstevel@tonic-gate { 11720Sstevel@tonic-gate sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA]; 11730Sstevel@tonic-gate sadb_address_t *dstext = 11740Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST]; 11750Sstevel@tonic-gate sadb_address_t *srcext = 11760Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_SRC]; 11770Sstevel@tonic-gate struct sockaddr_in *sin; 11780Sstevel@tonic-gate 11790Sstevel@tonic-gate if (assoc == NULL) { 11800Sstevel@tonic-gate if (dstext != NULL) 11810Sstevel@tonic-gate sin = (struct sockaddr_in *)(dstext + 1); 11820Sstevel@tonic-gate else if (srcext != NULL) 11830Sstevel@tonic-gate sin = (struct sockaddr_in *)(srcext + 1); 11840Sstevel@tonic-gate else { 11850Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_SA; 11860Sstevel@tonic-gate return (EINVAL); 11870Sstevel@tonic-gate } 11880Sstevel@tonic-gate return sadb_purge_sa(mp, ksi, 11890Sstevel@tonic-gate (sin->sin_family == AF_INET6) ? &ah_sadb.s_v6 : 11900Sstevel@tonic-gate &ah_sadb.s_v4, 11910Sstevel@tonic-gate diagnostic, ah_pfkey_q, ah_sadb.s_ip_q); 11920Sstevel@tonic-gate } 11930Sstevel@tonic-gate 11940Sstevel@tonic-gate return (sadb_del_sa(mp, ksi, &ah_sadb, diagnostic, ah_pfkey_q)); 11950Sstevel@tonic-gate } 11960Sstevel@tonic-gate 11970Sstevel@tonic-gate /* 11980Sstevel@tonic-gate * Convert the entire contents of all of AH's SA tables into PF_KEY SADB_DUMP 11990Sstevel@tonic-gate * messages. 12000Sstevel@tonic-gate */ 12010Sstevel@tonic-gate static void 12020Sstevel@tonic-gate ah_dump(mblk_t *mp, keysock_in_t *ksi) 12030Sstevel@tonic-gate { 12040Sstevel@tonic-gate int error; 12050Sstevel@tonic-gate sadb_msg_t *samsg; 12060Sstevel@tonic-gate 12070Sstevel@tonic-gate /* 12080Sstevel@tonic-gate * Dump each fanout, bailing if error is non-zero. 12090Sstevel@tonic-gate */ 12100Sstevel@tonic-gate 12110Sstevel@tonic-gate error = sadb_dump(ah_pfkey_q, mp, ksi->ks_in_serial, &ah_sadb.s_v4); 12120Sstevel@tonic-gate if (error != 0) 12130Sstevel@tonic-gate goto bail; 12140Sstevel@tonic-gate 12150Sstevel@tonic-gate error = sadb_dump(ah_pfkey_q, mp, ksi->ks_in_serial, &ah_sadb.s_v6); 12160Sstevel@tonic-gate bail: 12170Sstevel@tonic-gate ASSERT(mp->b_cont != NULL); 12180Sstevel@tonic-gate samsg = (sadb_msg_t *)mp->b_cont->b_rptr; 12190Sstevel@tonic-gate samsg->sadb_msg_errno = (uint8_t)error; 12200Sstevel@tonic-gate sadb_pfkey_echo(ah_pfkey_q, mp, (sadb_msg_t *)mp->b_cont->b_rptr, ksi, 12210Sstevel@tonic-gate NULL); 12220Sstevel@tonic-gate } 12230Sstevel@tonic-gate 12240Sstevel@tonic-gate /* 12250Sstevel@tonic-gate * AH parsing of PF_KEY messages. Keysock did most of the really silly 12260Sstevel@tonic-gate * error cases. What I receive is a fully-formed, syntactically legal 12270Sstevel@tonic-gate * PF_KEY message. I then need to check semantics... 12280Sstevel@tonic-gate * 12290Sstevel@tonic-gate * This code may become common to AH and ESP. Stay tuned. 12300Sstevel@tonic-gate * 12310Sstevel@tonic-gate * I also make the assumption that db_ref's are cool. If this assumption 12320Sstevel@tonic-gate * is wrong, this means that someone other than keysock or me has been 12330Sstevel@tonic-gate * mucking with PF_KEY messages. 12340Sstevel@tonic-gate */ 12350Sstevel@tonic-gate static void 12360Sstevel@tonic-gate ah_parse_pfkey(mblk_t *mp) 12370Sstevel@tonic-gate { 12380Sstevel@tonic-gate mblk_t *msg = mp->b_cont; 12390Sstevel@tonic-gate sadb_msg_t *samsg; 12400Sstevel@tonic-gate keysock_in_t *ksi; 12410Sstevel@tonic-gate int error; 12420Sstevel@tonic-gate int diagnostic = SADB_X_DIAGNOSTIC_NONE; 12430Sstevel@tonic-gate 12440Sstevel@tonic-gate ASSERT(msg != NULL); 12450Sstevel@tonic-gate samsg = (sadb_msg_t *)msg->b_rptr; 12460Sstevel@tonic-gate ksi = (keysock_in_t *)mp->b_rptr; 12470Sstevel@tonic-gate 12480Sstevel@tonic-gate /* 12490Sstevel@tonic-gate * If applicable, convert unspecified AF_INET6 to unspecified 12500Sstevel@tonic-gate * AF_INET. 12510Sstevel@tonic-gate */ 12520Sstevel@tonic-gate sadb_srcaddrfix(ksi); 12530Sstevel@tonic-gate 12540Sstevel@tonic-gate switch (samsg->sadb_msg_type) { 12550Sstevel@tonic-gate case SADB_ADD: 12560Sstevel@tonic-gate error = ah_add_sa(mp, ksi, &diagnostic); 12570Sstevel@tonic-gate if (error != 0) { 12580Sstevel@tonic-gate sadb_pfkey_error(ah_pfkey_q, mp, error, diagnostic, 12590Sstevel@tonic-gate ksi->ks_in_serial); 12600Sstevel@tonic-gate } 12610Sstevel@tonic-gate /* else ah_add_sa() took care of things. */ 12620Sstevel@tonic-gate break; 12630Sstevel@tonic-gate case SADB_DELETE: 12640Sstevel@tonic-gate error = ah_del_sa(mp, ksi, &diagnostic); 12650Sstevel@tonic-gate if (error != 0) { 12660Sstevel@tonic-gate sadb_pfkey_error(ah_pfkey_q, mp, error, diagnostic, 12670Sstevel@tonic-gate ksi->ks_in_serial); 12680Sstevel@tonic-gate } 12690Sstevel@tonic-gate /* Else ah_del_sa() took care of things. */ 12700Sstevel@tonic-gate break; 12710Sstevel@tonic-gate case SADB_GET: 12720Sstevel@tonic-gate error = sadb_get_sa(mp, ksi, &ah_sadb, &diagnostic, ah_pfkey_q); 12730Sstevel@tonic-gate if (error != 0) { 12740Sstevel@tonic-gate sadb_pfkey_error(ah_pfkey_q, mp, error, diagnostic, 12750Sstevel@tonic-gate ksi->ks_in_serial); 12760Sstevel@tonic-gate } 12770Sstevel@tonic-gate /* Else sadb_get_sa() took care of things. */ 12780Sstevel@tonic-gate break; 12790Sstevel@tonic-gate case SADB_FLUSH: 12800Sstevel@tonic-gate sadbp_flush(&ah_sadb); 12810Sstevel@tonic-gate sadb_pfkey_echo(ah_pfkey_q, mp, samsg, ksi, NULL); 12820Sstevel@tonic-gate break; 12830Sstevel@tonic-gate case SADB_REGISTER: 12840Sstevel@tonic-gate /* 12850Sstevel@tonic-gate * Hmmm, let's do it! Check for extensions (there should 12860Sstevel@tonic-gate * be none), extract the fields, call ah_register_out(), 12870Sstevel@tonic-gate * then either free or report an error. 12880Sstevel@tonic-gate * 12890Sstevel@tonic-gate * Keysock takes care of the PF_KEY bookkeeping for this. 12900Sstevel@tonic-gate */ 12910Sstevel@tonic-gate if (ah_register_out(samsg->sadb_msg_seq, samsg->sadb_msg_pid, 12920Sstevel@tonic-gate ksi->ks_in_serial)) { 12930Sstevel@tonic-gate freemsg(mp); 12940Sstevel@tonic-gate } else { 12950Sstevel@tonic-gate /* 12960Sstevel@tonic-gate * Only way this path hits is if there is a memory 12970Sstevel@tonic-gate * failure. It will not return B_FALSE because of 12980Sstevel@tonic-gate * lack of ah_pfkey_q if I am in wput(). 12990Sstevel@tonic-gate */ 13000Sstevel@tonic-gate sadb_pfkey_error(ah_pfkey_q, mp, ENOMEM, diagnostic, 13010Sstevel@tonic-gate ksi->ks_in_serial); 13020Sstevel@tonic-gate } 13030Sstevel@tonic-gate break; 13040Sstevel@tonic-gate case SADB_UPDATE: 13050Sstevel@tonic-gate /* 13060Sstevel@tonic-gate * Find a larval, if not there, find a full one and get 13070Sstevel@tonic-gate * strict. 13080Sstevel@tonic-gate */ 13090Sstevel@tonic-gate error = ah_update_sa(mp, ksi, &diagnostic); 13100Sstevel@tonic-gate if (error != 0) { 13110Sstevel@tonic-gate sadb_pfkey_error(ah_pfkey_q, mp, error, diagnostic, 13120Sstevel@tonic-gate ksi->ks_in_serial); 13130Sstevel@tonic-gate } 13140Sstevel@tonic-gate /* else ah_update_sa() took care of things. */ 13150Sstevel@tonic-gate break; 13160Sstevel@tonic-gate case SADB_GETSPI: 13170Sstevel@tonic-gate /* 13180Sstevel@tonic-gate * Reserve a new larval entry. 13190Sstevel@tonic-gate */ 13200Sstevel@tonic-gate ah_getspi(mp, ksi); 13210Sstevel@tonic-gate break; 13220Sstevel@tonic-gate case SADB_ACQUIRE: 13230Sstevel@tonic-gate /* 13240Sstevel@tonic-gate * Find larval and/or ACQUIRE record and kill it (them), I'm 13250Sstevel@tonic-gate * most likely an error. Inbound ACQUIRE messages should only 13260Sstevel@tonic-gate * have the base header. 13270Sstevel@tonic-gate */ 13280Sstevel@tonic-gate sadb_in_acquire(samsg, &ah_sadb, ah_pfkey_q); 13290Sstevel@tonic-gate freemsg(mp); 13300Sstevel@tonic-gate break; 13310Sstevel@tonic-gate case SADB_DUMP: 13320Sstevel@tonic-gate /* 13330Sstevel@tonic-gate * Dump all entries. 13340Sstevel@tonic-gate */ 13350Sstevel@tonic-gate ah_dump(mp, ksi); 13360Sstevel@tonic-gate /* ah_dump will take care of the return message, etc. */ 13370Sstevel@tonic-gate break; 13380Sstevel@tonic-gate case SADB_EXPIRE: 13390Sstevel@tonic-gate /* Should never reach me. */ 13400Sstevel@tonic-gate sadb_pfkey_error(ah_pfkey_q, mp, EOPNOTSUPP, diagnostic, 13410Sstevel@tonic-gate ksi->ks_in_serial); 13420Sstevel@tonic-gate break; 13430Sstevel@tonic-gate default: 13440Sstevel@tonic-gate sadb_pfkey_error(ah_pfkey_q, mp, EINVAL, 13450Sstevel@tonic-gate SADB_X_DIAGNOSTIC_UNKNOWN_MSG, ksi->ks_in_serial); 13460Sstevel@tonic-gate break; 13470Sstevel@tonic-gate } 13480Sstevel@tonic-gate } 13490Sstevel@tonic-gate 13500Sstevel@tonic-gate /* 13510Sstevel@tonic-gate * Handle case where PF_KEY says it can't find a keysock for one of my 13520Sstevel@tonic-gate * ACQUIRE messages. 13530Sstevel@tonic-gate */ 13540Sstevel@tonic-gate static void 13550Sstevel@tonic-gate ah_keysock_no_socket(mblk_t *mp) 13560Sstevel@tonic-gate { 13570Sstevel@tonic-gate sadb_msg_t *samsg; 13580Sstevel@tonic-gate keysock_out_err_t *kse = (keysock_out_err_t *)mp->b_rptr; 13590Sstevel@tonic-gate 13600Sstevel@tonic-gate if (mp->b_cont == NULL) { 13610Sstevel@tonic-gate freemsg(mp); 13620Sstevel@tonic-gate return; 13630Sstevel@tonic-gate } 13640Sstevel@tonic-gate samsg = (sadb_msg_t *)mp->b_cont->b_rptr; 13650Sstevel@tonic-gate 13660Sstevel@tonic-gate /* 13670Sstevel@tonic-gate * If keysock can't find any registered, delete the acquire record 13680Sstevel@tonic-gate * immediately, and handle errors. 13690Sstevel@tonic-gate */ 13700Sstevel@tonic-gate if (samsg->sadb_msg_type == SADB_ACQUIRE) { 13710Sstevel@tonic-gate samsg->sadb_msg_errno = kse->ks_err_errno; 13720Sstevel@tonic-gate samsg->sadb_msg_len = SADB_8TO64(sizeof (*samsg)); 13730Sstevel@tonic-gate /* 13740Sstevel@tonic-gate * Use the write-side of the ah_pfkey_q, in case there is 13750Sstevel@tonic-gate * no ah_sadb.s_ip_q. 13760Sstevel@tonic-gate */ 13770Sstevel@tonic-gate sadb_in_acquire(samsg, &ah_sadb, WR(ah_pfkey_q)); 13780Sstevel@tonic-gate } 13790Sstevel@tonic-gate 13800Sstevel@tonic-gate freemsg(mp); 13810Sstevel@tonic-gate } 13820Sstevel@tonic-gate 13830Sstevel@tonic-gate /* 13840Sstevel@tonic-gate * First-cut reality check for an inbound PF_KEY message. 13850Sstevel@tonic-gate */ 13860Sstevel@tonic-gate static boolean_t 13870Sstevel@tonic-gate ah_pfkey_reality_failures(mblk_t *mp, keysock_in_t *ksi) 13880Sstevel@tonic-gate { 13890Sstevel@tonic-gate int diagnostic; 13900Sstevel@tonic-gate 13910Sstevel@tonic-gate if (mp->b_cont == NULL) { 13920Sstevel@tonic-gate freemsg(mp); 13930Sstevel@tonic-gate return (B_TRUE); 13940Sstevel@tonic-gate } 13950Sstevel@tonic-gate 13960Sstevel@tonic-gate if (ksi->ks_in_extv[SADB_EXT_KEY_ENCRYPT] != NULL) { 13970Sstevel@tonic-gate diagnostic = SADB_X_DIAGNOSTIC_EKEY_PRESENT; 13980Sstevel@tonic-gate goto badmsg; 13990Sstevel@tonic-gate } 14000Sstevel@tonic-gate if (ksi->ks_in_extv[SADB_EXT_PROPOSAL] != NULL) { 14010Sstevel@tonic-gate diagnostic = SADB_X_DIAGNOSTIC_PROP_PRESENT; 14020Sstevel@tonic-gate goto badmsg; 14030Sstevel@tonic-gate } 14040Sstevel@tonic-gate if (ksi->ks_in_extv[SADB_EXT_SUPPORTED_AUTH] != NULL || 14050Sstevel@tonic-gate ksi->ks_in_extv[SADB_EXT_SUPPORTED_ENCRYPT] != NULL) { 14060Sstevel@tonic-gate diagnostic = SADB_X_DIAGNOSTIC_SUPP_PRESENT; 14070Sstevel@tonic-gate goto badmsg; 14080Sstevel@tonic-gate } 14090Sstevel@tonic-gate if (ksi->ks_in_srctype == KS_IN_ADDR_MBCAST) { 14100Sstevel@tonic-gate diagnostic = SADB_X_DIAGNOSTIC_BAD_SRC; 14110Sstevel@tonic-gate goto badmsg; 14120Sstevel@tonic-gate } 14130Sstevel@tonic-gate if (ksi->ks_in_dsttype == KS_IN_ADDR_UNSPEC) { 14140Sstevel@tonic-gate diagnostic = SADB_X_DIAGNOSTIC_BAD_DST; 14150Sstevel@tonic-gate goto badmsg; 14160Sstevel@tonic-gate } 14170Sstevel@tonic-gate 14180Sstevel@tonic-gate return (B_FALSE); /* False ==> no failures */ 14190Sstevel@tonic-gate 14200Sstevel@tonic-gate badmsg: 14210Sstevel@tonic-gate sadb_pfkey_error(ah_pfkey_q, mp, EINVAL, diagnostic, ksi->ks_in_serial); 14220Sstevel@tonic-gate return (B_TRUE); /* True ==> failures */ 14230Sstevel@tonic-gate } 14240Sstevel@tonic-gate 14250Sstevel@tonic-gate /* 14260Sstevel@tonic-gate * AH module write put routine. 14270Sstevel@tonic-gate */ 14280Sstevel@tonic-gate static void 14290Sstevel@tonic-gate ipsecah_wput(queue_t *q, mblk_t *mp) 14300Sstevel@tonic-gate { 14310Sstevel@tonic-gate ipsec_info_t *ii; 14320Sstevel@tonic-gate keysock_in_t *ksi; 14330Sstevel@tonic-gate int rc; 14340Sstevel@tonic-gate struct iocblk *iocp; 14350Sstevel@tonic-gate 14360Sstevel@tonic-gate ah3dbg(("In ah_wput().\n")); 14370Sstevel@tonic-gate 14380Sstevel@tonic-gate /* NOTE: Each case must take care of freeing or passing mp. */ 14390Sstevel@tonic-gate switch (mp->b_datap->db_type) { 14400Sstevel@tonic-gate case M_CTL: 14410Sstevel@tonic-gate if ((mp->b_wptr - mp->b_rptr) < sizeof (ipsec_info_t)) { 14420Sstevel@tonic-gate /* Not big enough message. */ 14430Sstevel@tonic-gate freemsg(mp); 14440Sstevel@tonic-gate break; 14450Sstevel@tonic-gate } 14460Sstevel@tonic-gate ii = (ipsec_info_t *)mp->b_rptr; 14470Sstevel@tonic-gate 14480Sstevel@tonic-gate switch (ii->ipsec_info_type) { 14490Sstevel@tonic-gate case KEYSOCK_OUT_ERR: 14500Sstevel@tonic-gate ah1dbg(("Got KEYSOCK_OUT_ERR message.\n")); 14510Sstevel@tonic-gate ah_keysock_no_socket(mp); 14520Sstevel@tonic-gate break; 14530Sstevel@tonic-gate case KEYSOCK_IN: 14540Sstevel@tonic-gate AH_BUMP_STAT(keysock_in); 14550Sstevel@tonic-gate ah3dbg(("Got KEYSOCK_IN message.\n")); 14560Sstevel@tonic-gate ksi = (keysock_in_t *)ii; 14570Sstevel@tonic-gate /* 14580Sstevel@tonic-gate * Some common reality checks. 14590Sstevel@tonic-gate */ 14600Sstevel@tonic-gate 14610Sstevel@tonic-gate if (ah_pfkey_reality_failures(mp, ksi)) 14620Sstevel@tonic-gate return; 14630Sstevel@tonic-gate 14640Sstevel@tonic-gate /* 14650Sstevel@tonic-gate * Use 'q' instead of ah_sadb.s_ip_q, since 14660Sstevel@tonic-gate * it's the write side already, and it'll go 14670Sstevel@tonic-gate * down to IP. Use ah_pfkey_q because we 14680Sstevel@tonic-gate * wouldn't get here if that weren't set, and 14690Sstevel@tonic-gate * the RD(q) has been done already. 14700Sstevel@tonic-gate */ 14710Sstevel@tonic-gate if (ksi->ks_in_srctype == KS_IN_ADDR_UNKNOWN) { 14720Sstevel@tonic-gate rc = sadb_addrcheck(q, ah_pfkey_q, mp, 14730Sstevel@tonic-gate ksi->ks_in_extv[SADB_EXT_ADDRESS_SRC], 14740Sstevel@tonic-gate ksi->ks_in_serial); 14750Sstevel@tonic-gate if (rc == KS_IN_ADDR_UNKNOWN) 14760Sstevel@tonic-gate return; 14770Sstevel@tonic-gate else 14780Sstevel@tonic-gate ksi->ks_in_srctype = rc; 14790Sstevel@tonic-gate } 14800Sstevel@tonic-gate if (ksi->ks_in_dsttype == KS_IN_ADDR_UNKNOWN) { 14810Sstevel@tonic-gate rc = sadb_addrcheck(q, ah_pfkey_q, mp, 14820Sstevel@tonic-gate ksi->ks_in_extv[SADB_EXT_ADDRESS_DST], 14830Sstevel@tonic-gate ksi->ks_in_serial); 14840Sstevel@tonic-gate if (rc == KS_IN_ADDR_UNKNOWN) 14850Sstevel@tonic-gate return; 14860Sstevel@tonic-gate else 14870Sstevel@tonic-gate ksi->ks_in_dsttype = rc; 14880Sstevel@tonic-gate } 14890Sstevel@tonic-gate /* 14900Sstevel@tonic-gate * XXX Proxy may be a different address family. 14910Sstevel@tonic-gate */ 14920Sstevel@tonic-gate if (ksi->ks_in_proxytype == KS_IN_ADDR_UNKNOWN) { 14930Sstevel@tonic-gate rc = sadb_addrcheck(q, ah_pfkey_q, mp, 14940Sstevel@tonic-gate ksi->ks_in_extv[SADB_EXT_ADDRESS_PROXY], 14950Sstevel@tonic-gate ksi->ks_in_serial); 14960Sstevel@tonic-gate if (rc == KS_IN_ADDR_UNKNOWN) 14970Sstevel@tonic-gate return; 14980Sstevel@tonic-gate else 14990Sstevel@tonic-gate ksi->ks_in_proxytype = rc; 15000Sstevel@tonic-gate } 15010Sstevel@tonic-gate ah_parse_pfkey(mp); 15020Sstevel@tonic-gate break; 15030Sstevel@tonic-gate case KEYSOCK_HELLO: 15040Sstevel@tonic-gate sadb_keysock_hello(&ah_pfkey_q, q, mp, 15050Sstevel@tonic-gate ah_ager, &ah_event, SADB_SATYPE_AH); 15060Sstevel@tonic-gate break; 15070Sstevel@tonic-gate default: 15080Sstevel@tonic-gate ah1dbg(("Got M_CTL from above of 0x%x.\n", 15090Sstevel@tonic-gate ii->ipsec_info_type)); 15100Sstevel@tonic-gate freemsg(mp); 15110Sstevel@tonic-gate break; 15120Sstevel@tonic-gate } 15130Sstevel@tonic-gate break; 15140Sstevel@tonic-gate case M_IOCTL: 15150Sstevel@tonic-gate iocp = (struct iocblk *)mp->b_rptr; 15160Sstevel@tonic-gate switch (iocp->ioc_cmd) { 15170Sstevel@tonic-gate case ND_SET: 15180Sstevel@tonic-gate case ND_GET: 15190Sstevel@tonic-gate if (nd_getset(q, ipsecah_g_nd, mp)) { 15200Sstevel@tonic-gate qreply(q, mp); 15210Sstevel@tonic-gate return; 15220Sstevel@tonic-gate } else { 15230Sstevel@tonic-gate iocp->ioc_error = ENOENT; 15240Sstevel@tonic-gate } 15250Sstevel@tonic-gate /* FALLTHRU */ 15260Sstevel@tonic-gate default: 15270Sstevel@tonic-gate /* We really don't support any other ioctls, do we? */ 15280Sstevel@tonic-gate 15290Sstevel@tonic-gate /* Return EINVAL */ 15300Sstevel@tonic-gate if (iocp->ioc_error != ENOENT) 15310Sstevel@tonic-gate iocp->ioc_error = EINVAL; 15320Sstevel@tonic-gate iocp->ioc_count = 0; 15330Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 15340Sstevel@tonic-gate qreply(q, mp); 15350Sstevel@tonic-gate return; 15360Sstevel@tonic-gate } 15370Sstevel@tonic-gate default: 15380Sstevel@tonic-gate ah3dbg(("Got default message, type %d, passing to IP.\n", 15390Sstevel@tonic-gate mp->b_datap->db_type)); 15400Sstevel@tonic-gate putnext(q, mp); 15410Sstevel@tonic-gate } 15420Sstevel@tonic-gate } 15430Sstevel@tonic-gate 15440Sstevel@tonic-gate /* 15450Sstevel@tonic-gate * Updating use times can be tricky business if the ipsa_haspeer flag is 15460Sstevel@tonic-gate * set. This function is called once in an SA's lifetime. 15470Sstevel@tonic-gate * 15480Sstevel@tonic-gate * Caller has to REFRELE "assoc" which is passed in. This function has 15490Sstevel@tonic-gate * to REFRELE any peer SA that is obtained. 15500Sstevel@tonic-gate */ 15510Sstevel@tonic-gate static void 15520Sstevel@tonic-gate ah_set_usetime(ipsa_t *assoc, boolean_t inbound) 15530Sstevel@tonic-gate { 15540Sstevel@tonic-gate ipsa_t *inassoc, *outassoc; 15550Sstevel@tonic-gate isaf_t *bucket; 15560Sstevel@tonic-gate sadb_t *sp; 15570Sstevel@tonic-gate int outhash; 15580Sstevel@tonic-gate boolean_t isv6; 15590Sstevel@tonic-gate 15600Sstevel@tonic-gate /* No peer? No problem! */ 15610Sstevel@tonic-gate if (!assoc->ipsa_haspeer) { 15620Sstevel@tonic-gate sadb_set_usetime(assoc); 15630Sstevel@tonic-gate return; 15640Sstevel@tonic-gate } 15650Sstevel@tonic-gate 15660Sstevel@tonic-gate /* 15670Sstevel@tonic-gate * Otherwise, we want to grab both the original assoc and its peer. 15680Sstevel@tonic-gate * There might be a race for this, but if it's a real race, the times 15690Sstevel@tonic-gate * will be out-of-synch by at most a second, and since our time 15700Sstevel@tonic-gate * granularity is a second, this won't be a problem. 15710Sstevel@tonic-gate * 15720Sstevel@tonic-gate * If we need tight synchronization on the peer SA, then we need to 15730Sstevel@tonic-gate * reconsider. 15740Sstevel@tonic-gate */ 15750Sstevel@tonic-gate 15760Sstevel@tonic-gate /* Use address family to select IPv6/IPv4 */ 15770Sstevel@tonic-gate isv6 = (assoc->ipsa_addrfam == AF_INET6); 15780Sstevel@tonic-gate if (isv6) { 15790Sstevel@tonic-gate sp = &ah_sadb.s_v6; 15800Sstevel@tonic-gate } else { 15810Sstevel@tonic-gate sp = &ah_sadb.s_v4; 15820Sstevel@tonic-gate ASSERT(assoc->ipsa_addrfam == AF_INET); 15830Sstevel@tonic-gate } 15840Sstevel@tonic-gate if (inbound) { 15850Sstevel@tonic-gate inassoc = assoc; 15860Sstevel@tonic-gate if (isv6) 1587564Ssommerfe outhash = OUTBOUND_HASH_V6(sp, *((in6_addr_t *) 15880Sstevel@tonic-gate &inassoc->ipsa_dstaddr)); 15890Sstevel@tonic-gate else 1590564Ssommerfe outhash = OUTBOUND_HASH_V4(sp, *((ipaddr_t *) 15910Sstevel@tonic-gate &inassoc->ipsa_dstaddr)); 15920Sstevel@tonic-gate bucket = &sp->sdb_of[outhash]; 15930Sstevel@tonic-gate 15940Sstevel@tonic-gate mutex_enter(&bucket->isaf_lock); 15950Sstevel@tonic-gate outassoc = ipsec_getassocbyspi(bucket, inassoc->ipsa_spi, 15960Sstevel@tonic-gate inassoc->ipsa_srcaddr, inassoc->ipsa_dstaddr, 15970Sstevel@tonic-gate inassoc->ipsa_addrfam); 15980Sstevel@tonic-gate mutex_exit(&bucket->isaf_lock); 15990Sstevel@tonic-gate if (outassoc == NULL) { 16000Sstevel@tonic-gate /* Q: Do we wish to set haspeer == B_FALSE? */ 16010Sstevel@tonic-gate ah0dbg(("ah_set_usetime: " 16020Sstevel@tonic-gate "can't find peer for inbound.\n")); 16030Sstevel@tonic-gate sadb_set_usetime(inassoc); 16040Sstevel@tonic-gate return; 16050Sstevel@tonic-gate } 16060Sstevel@tonic-gate } else { 16070Sstevel@tonic-gate outassoc = assoc; 1608564Ssommerfe bucket = INBOUND_BUCKET(sp, outassoc->ipsa_spi); 16090Sstevel@tonic-gate mutex_enter(&bucket->isaf_lock); 16100Sstevel@tonic-gate inassoc = ipsec_getassocbyspi(bucket, outassoc->ipsa_spi, 16110Sstevel@tonic-gate outassoc->ipsa_srcaddr, outassoc->ipsa_dstaddr, 16120Sstevel@tonic-gate outassoc->ipsa_addrfam); 16130Sstevel@tonic-gate mutex_exit(&bucket->isaf_lock); 16140Sstevel@tonic-gate if (inassoc == NULL) { 16150Sstevel@tonic-gate /* Q: Do we wish to set haspeer == B_FALSE? */ 16160Sstevel@tonic-gate ah0dbg(("ah_set_usetime: " 16170Sstevel@tonic-gate "can't find peer for outbound.\n")); 16180Sstevel@tonic-gate sadb_set_usetime(outassoc); 16190Sstevel@tonic-gate return; 16200Sstevel@tonic-gate } 16210Sstevel@tonic-gate } 16220Sstevel@tonic-gate 16230Sstevel@tonic-gate /* Update usetime on both. */ 16240Sstevel@tonic-gate sadb_set_usetime(inassoc); 16250Sstevel@tonic-gate sadb_set_usetime(outassoc); 16260Sstevel@tonic-gate 16270Sstevel@tonic-gate /* 16280Sstevel@tonic-gate * REFRELE any peer SA. 16290Sstevel@tonic-gate * 16300Sstevel@tonic-gate * Because of the multi-line macro nature of IPSA_REFRELE, keep 16310Sstevel@tonic-gate * them in { }. 16320Sstevel@tonic-gate */ 16330Sstevel@tonic-gate if (inbound) { 16340Sstevel@tonic-gate IPSA_REFRELE(outassoc); 16350Sstevel@tonic-gate } else { 16360Sstevel@tonic-gate IPSA_REFRELE(inassoc); 16370Sstevel@tonic-gate } 16380Sstevel@tonic-gate } 16390Sstevel@tonic-gate 16400Sstevel@tonic-gate /* 16410Sstevel@tonic-gate * Add a number of bytes to what the SA has protected so far. Return 16420Sstevel@tonic-gate * B_TRUE if the SA can still protect that many bytes. 16430Sstevel@tonic-gate * 16440Sstevel@tonic-gate * Caller must REFRELE the passed-in assoc. This function must REFRELE 16450Sstevel@tonic-gate * any obtained peer SA. 16460Sstevel@tonic-gate */ 16470Sstevel@tonic-gate static boolean_t 16480Sstevel@tonic-gate ah_age_bytes(ipsa_t *assoc, uint64_t bytes, boolean_t inbound) 16490Sstevel@tonic-gate { 16500Sstevel@tonic-gate ipsa_t *inassoc, *outassoc; 16510Sstevel@tonic-gate isaf_t *bucket; 16520Sstevel@tonic-gate boolean_t inrc, outrc, isv6; 16530Sstevel@tonic-gate sadb_t *sp; 16540Sstevel@tonic-gate int outhash; 16550Sstevel@tonic-gate 16560Sstevel@tonic-gate /* No peer? No problem! */ 16570Sstevel@tonic-gate if (!assoc->ipsa_haspeer) { 16580Sstevel@tonic-gate return (sadb_age_bytes(ah_pfkey_q, assoc, bytes, 16590Sstevel@tonic-gate B_TRUE)); 16600Sstevel@tonic-gate } 16610Sstevel@tonic-gate 16620Sstevel@tonic-gate /* 16630Sstevel@tonic-gate * Otherwise, we want to grab both the original assoc and its peer. 16640Sstevel@tonic-gate * There might be a race for this, but if it's a real race, two 16650Sstevel@tonic-gate * expire messages may occur. We limit this by only sending the 16660Sstevel@tonic-gate * expire message on one of the peers, we'll pick the inbound 16670Sstevel@tonic-gate * arbitrarily. 16680Sstevel@tonic-gate * 16690Sstevel@tonic-gate * If we need tight synchronization on the peer SA, then we need to 16700Sstevel@tonic-gate * reconsider. 16710Sstevel@tonic-gate */ 16720Sstevel@tonic-gate 16730Sstevel@tonic-gate /* Pick v4/v6 bucket based on addrfam. */ 16740Sstevel@tonic-gate isv6 = (assoc->ipsa_addrfam == AF_INET6); 16750Sstevel@tonic-gate if (isv6) { 16760Sstevel@tonic-gate sp = &ah_sadb.s_v6; 16770Sstevel@tonic-gate } else { 16780Sstevel@tonic-gate sp = &ah_sadb.s_v4; 16790Sstevel@tonic-gate ASSERT(assoc->ipsa_addrfam == AF_INET); 16800Sstevel@tonic-gate } 16810Sstevel@tonic-gate if (inbound) { 16820Sstevel@tonic-gate inassoc = assoc; 16830Sstevel@tonic-gate if (isv6) 1684564Ssommerfe outhash = OUTBOUND_HASH_V6(sp, *((in6_addr_t *) 16850Sstevel@tonic-gate &inassoc->ipsa_dstaddr)); 16860Sstevel@tonic-gate else 1687564Ssommerfe outhash = OUTBOUND_HASH_V4(sp, *((ipaddr_t *) 16880Sstevel@tonic-gate &inassoc->ipsa_dstaddr)); 16890Sstevel@tonic-gate bucket = &sp->sdb_of[outhash]; 16900Sstevel@tonic-gate mutex_enter(&bucket->isaf_lock); 16910Sstevel@tonic-gate outassoc = ipsec_getassocbyspi(bucket, inassoc->ipsa_spi, 16920Sstevel@tonic-gate inassoc->ipsa_srcaddr, inassoc->ipsa_dstaddr, 16930Sstevel@tonic-gate inassoc->ipsa_addrfam); 16940Sstevel@tonic-gate mutex_exit(&bucket->isaf_lock); 16950Sstevel@tonic-gate if (outassoc == NULL) { 16960Sstevel@tonic-gate /* Q: Do we wish to set haspeer == B_FALSE? */ 16970Sstevel@tonic-gate ah0dbg(("ah_age_bytes: " 16980Sstevel@tonic-gate "can't find peer for inbound.\n")); 16990Sstevel@tonic-gate return (sadb_age_bytes(ah_pfkey_q, inassoc, 17000Sstevel@tonic-gate bytes, B_TRUE)); 17010Sstevel@tonic-gate } 17020Sstevel@tonic-gate } else { 17030Sstevel@tonic-gate outassoc = assoc; 1704564Ssommerfe bucket = INBOUND_BUCKET(sp, outassoc->ipsa_spi); 17050Sstevel@tonic-gate mutex_enter(&bucket->isaf_lock); 17060Sstevel@tonic-gate inassoc = ipsec_getassocbyspi(bucket, outassoc->ipsa_spi, 17070Sstevel@tonic-gate outassoc->ipsa_srcaddr, outassoc->ipsa_dstaddr, 17080Sstevel@tonic-gate outassoc->ipsa_addrfam); 17090Sstevel@tonic-gate mutex_exit(&bucket->isaf_lock); 17100Sstevel@tonic-gate if (inassoc == NULL) { 17110Sstevel@tonic-gate /* Q: Do we wish to set haspeer == B_FALSE? */ 17120Sstevel@tonic-gate ah0dbg(("ah_age_bytes: " 17130Sstevel@tonic-gate "can't find peer for outbound.\n")); 17140Sstevel@tonic-gate return (sadb_age_bytes(ah_pfkey_q, outassoc, 17150Sstevel@tonic-gate bytes, B_TRUE)); 17160Sstevel@tonic-gate } 17170Sstevel@tonic-gate } 17180Sstevel@tonic-gate 17190Sstevel@tonic-gate inrc = sadb_age_bytes(ah_pfkey_q, inassoc, bytes, B_TRUE); 17200Sstevel@tonic-gate outrc = sadb_age_bytes(ah_pfkey_q, outassoc, bytes, B_FALSE); 17210Sstevel@tonic-gate 17220Sstevel@tonic-gate /* 17230Sstevel@tonic-gate * REFRELE any peer SA. 17240Sstevel@tonic-gate * 17250Sstevel@tonic-gate * Because of the multi-line macro nature of IPSA_REFRELE, keep 17260Sstevel@tonic-gate * them in { }. 17270Sstevel@tonic-gate */ 17280Sstevel@tonic-gate if (inbound) { 17290Sstevel@tonic-gate IPSA_REFRELE(outassoc); 17300Sstevel@tonic-gate } else { 17310Sstevel@tonic-gate IPSA_REFRELE(inassoc); 17320Sstevel@tonic-gate } 17330Sstevel@tonic-gate 17340Sstevel@tonic-gate return (inrc && outrc); 17350Sstevel@tonic-gate } 17360Sstevel@tonic-gate 17370Sstevel@tonic-gate /* 17380Sstevel@tonic-gate * Perform the really difficult work of inserting the proposed situation. 17390Sstevel@tonic-gate * Called while holding the algorithm lock. 17400Sstevel@tonic-gate */ 17410Sstevel@tonic-gate static void 17420Sstevel@tonic-gate ah_insert_prop(sadb_prop_t *prop, ipsacq_t *acqrec, uint_t combs) 17430Sstevel@tonic-gate { 17440Sstevel@tonic-gate sadb_comb_t *comb = (sadb_comb_t *)(prop + 1); 17450Sstevel@tonic-gate ipsec_out_t *io; 17460Sstevel@tonic-gate ipsec_action_t *ap; 17470Sstevel@tonic-gate ipsec_prot_t *prot; 17480Sstevel@tonic-gate io = (ipsec_out_t *)acqrec->ipsacq_mp->b_rptr; 17490Sstevel@tonic-gate 17500Sstevel@tonic-gate ASSERT(MUTEX_HELD(&alg_lock)); 17510Sstevel@tonic-gate ASSERT(io->ipsec_out_type == IPSEC_OUT); 17520Sstevel@tonic-gate 17530Sstevel@tonic-gate prop->sadb_prop_exttype = SADB_EXT_PROPOSAL; 17540Sstevel@tonic-gate prop->sadb_prop_len = SADB_8TO64(sizeof (sadb_prop_t)); 17550Sstevel@tonic-gate *(uint32_t *)(&prop->sadb_prop_replay) = 0; /* Quick zero-out! */ 17560Sstevel@tonic-gate 17570Sstevel@tonic-gate prop->sadb_prop_replay = ipsecah_replay_size; 17580Sstevel@tonic-gate 17590Sstevel@tonic-gate /* 17600Sstevel@tonic-gate * Based upon algorithm properties, and what-not, prioritize a 17610Sstevel@tonic-gate * proposal, based on the ordering of the ah algorithms in the 17620Sstevel@tonic-gate * alternatives presented in the policy rule passed down 17630Sstevel@tonic-gate * through the ipsec_out_t and attached to the acquire record. 17640Sstevel@tonic-gate */ 17650Sstevel@tonic-gate 17660Sstevel@tonic-gate for (ap = acqrec->ipsacq_act; ap != NULL; 17670Sstevel@tonic-gate ap = ap->ipa_next) { 17680Sstevel@tonic-gate ipsec_alginfo_t *aalg; 17690Sstevel@tonic-gate 17700Sstevel@tonic-gate if ((ap->ipa_act.ipa_type != IPSEC_POLICY_APPLY) || 17710Sstevel@tonic-gate (!ap->ipa_act.ipa_apply.ipp_use_ah)) 17720Sstevel@tonic-gate continue; 17730Sstevel@tonic-gate 17740Sstevel@tonic-gate prot = &ap->ipa_act.ipa_apply; 17750Sstevel@tonic-gate 17760Sstevel@tonic-gate ASSERT(prot->ipp_auth_alg > 0); 17770Sstevel@tonic-gate 17780Sstevel@tonic-gate aalg = ipsec_alglists[IPSEC_ALG_AUTH][prot->ipp_auth_alg]; 17790Sstevel@tonic-gate if (aalg == NULL || !ALG_VALID(aalg)) 17800Sstevel@tonic-gate continue; 17810Sstevel@tonic-gate 17820Sstevel@tonic-gate /* XXX check aalg for duplicates??.. */ 17830Sstevel@tonic-gate 17840Sstevel@tonic-gate comb->sadb_comb_flags = 0; 17850Sstevel@tonic-gate comb->sadb_comb_reserved = 0; 17860Sstevel@tonic-gate comb->sadb_comb_encrypt = 0; 17870Sstevel@tonic-gate comb->sadb_comb_encrypt_minbits = 0; 17880Sstevel@tonic-gate comb->sadb_comb_encrypt_maxbits = 0; 17890Sstevel@tonic-gate 17900Sstevel@tonic-gate comb->sadb_comb_auth = aalg->alg_id; 17910Sstevel@tonic-gate comb->sadb_comb_auth_minbits = prot->ipp_ah_minbits; 17920Sstevel@tonic-gate comb->sadb_comb_auth_maxbits = prot->ipp_ah_maxbits; 17930Sstevel@tonic-gate 17940Sstevel@tonic-gate /* 17950Sstevel@tonic-gate * The following may be based on algorithm 17960Sstevel@tonic-gate * properties, but in the meantime, we just pick 17970Sstevel@tonic-gate * some good, sensible numbers. Key mgmt. can 17980Sstevel@tonic-gate * (and perhaps should) be the place to finalize 17990Sstevel@tonic-gate * such decisions. 18000Sstevel@tonic-gate */ 18010Sstevel@tonic-gate 18020Sstevel@tonic-gate /* 18030Sstevel@tonic-gate * No limits on allocations, since we really don't 18040Sstevel@tonic-gate * support that concept currently. 18050Sstevel@tonic-gate */ 18060Sstevel@tonic-gate comb->sadb_comb_soft_allocations = 0; 18070Sstevel@tonic-gate comb->sadb_comb_hard_allocations = 0; 18080Sstevel@tonic-gate 18090Sstevel@tonic-gate /* 18100Sstevel@tonic-gate * These may want to come from policy rule.. 18110Sstevel@tonic-gate */ 18120Sstevel@tonic-gate comb->sadb_comb_soft_bytes = ipsecah_default_soft_bytes; 18130Sstevel@tonic-gate comb->sadb_comb_hard_bytes = ipsecah_default_hard_bytes; 18140Sstevel@tonic-gate comb->sadb_comb_soft_addtime = ipsecah_default_soft_addtime; 18150Sstevel@tonic-gate comb->sadb_comb_hard_addtime = ipsecah_default_hard_addtime; 18160Sstevel@tonic-gate comb->sadb_comb_soft_usetime = ipsecah_default_soft_usetime; 18170Sstevel@tonic-gate comb->sadb_comb_hard_usetime = ipsecah_default_hard_usetime; 18180Sstevel@tonic-gate 18190Sstevel@tonic-gate prop->sadb_prop_len += SADB_8TO64(sizeof (*comb)); 18200Sstevel@tonic-gate if (--combs == 0) 18210Sstevel@tonic-gate return; /* out of space.. */ 18220Sstevel@tonic-gate comb++; 18230Sstevel@tonic-gate } 18240Sstevel@tonic-gate } 18250Sstevel@tonic-gate 18260Sstevel@tonic-gate /* 18270Sstevel@tonic-gate * Prepare and actually send the SADB_ACQUIRE message to PF_KEY. 18280Sstevel@tonic-gate */ 18290Sstevel@tonic-gate static void 18300Sstevel@tonic-gate ah_send_acquire(ipsacq_t *acqrec, mblk_t *extended) 18310Sstevel@tonic-gate { 18320Sstevel@tonic-gate mblk_t *pfkeymp, *msgmp; 18330Sstevel@tonic-gate uint_t allocsize, combs; 18340Sstevel@tonic-gate sadb_msg_t *samsg; 18350Sstevel@tonic-gate sadb_prop_t *prop; 18360Sstevel@tonic-gate uint8_t *cur, *end; 18370Sstevel@tonic-gate 18380Sstevel@tonic-gate AH_BUMP_STAT(acquire_requests); 18390Sstevel@tonic-gate 18400Sstevel@tonic-gate ASSERT(MUTEX_HELD(&acqrec->ipsacq_lock)); 18410Sstevel@tonic-gate 18420Sstevel@tonic-gate pfkeymp = sadb_keysock_out(0); 18430Sstevel@tonic-gate if (pfkeymp == NULL) { 18440Sstevel@tonic-gate ah1dbg(("ah_send_acquire: 1st allocb() failed.\n")); 18450Sstevel@tonic-gate /* Just bail. */ 18460Sstevel@tonic-gate goto done; 18470Sstevel@tonic-gate } 18480Sstevel@tonic-gate 18490Sstevel@tonic-gate /* 18500Sstevel@tonic-gate * First, allocate a basic ACQUIRE message. Beyond that, 18510Sstevel@tonic-gate * you need to extract certificate info from 18520Sstevel@tonic-gate */ 18530Sstevel@tonic-gate allocsize = sizeof (sadb_msg_t) + sizeof (sadb_address_t) + 18540Sstevel@tonic-gate sizeof (sadb_address_t) + sizeof (sadb_prop_t); 18550Sstevel@tonic-gate 18560Sstevel@tonic-gate switch (acqrec->ipsacq_addrfam) { 18570Sstevel@tonic-gate case AF_INET: 18580Sstevel@tonic-gate allocsize += 2 * sizeof (struct sockaddr_in); 18590Sstevel@tonic-gate break; 18600Sstevel@tonic-gate case AF_INET6: 18610Sstevel@tonic-gate allocsize += 2 * sizeof (struct sockaddr_in6); 18620Sstevel@tonic-gate break; 18630Sstevel@tonic-gate } 18640Sstevel@tonic-gate 18650Sstevel@tonic-gate mutex_enter(&alg_lock); 18660Sstevel@tonic-gate 18670Sstevel@tonic-gate combs = ipsec_nalgs[IPSEC_ALG_AUTH]; 18680Sstevel@tonic-gate 18690Sstevel@tonic-gate allocsize += combs * sizeof (sadb_comb_t); 18700Sstevel@tonic-gate 18710Sstevel@tonic-gate /* 18720Sstevel@tonic-gate * XXX If there are: 18730Sstevel@tonic-gate * certificate IDs 18740Sstevel@tonic-gate * proxy address 18750Sstevel@tonic-gate * <Others> 18760Sstevel@tonic-gate * add additional allocation size. 18770Sstevel@tonic-gate */ 18780Sstevel@tonic-gate 18790Sstevel@tonic-gate msgmp = allocb(allocsize, BPRI_HI); 18800Sstevel@tonic-gate if (msgmp == NULL) { 18810Sstevel@tonic-gate ah0dbg(("ah_send_acquire: 2nd allocb() failed.\n")); 18820Sstevel@tonic-gate /* Just bail. */ 18830Sstevel@tonic-gate freemsg(pfkeymp); 18840Sstevel@tonic-gate pfkeymp = NULL; 18850Sstevel@tonic-gate goto done; 18860Sstevel@tonic-gate } 18870Sstevel@tonic-gate 18880Sstevel@tonic-gate cur = msgmp->b_rptr; 18890Sstevel@tonic-gate end = cur + allocsize; 18900Sstevel@tonic-gate samsg = (sadb_msg_t *)cur; 18910Sstevel@tonic-gate pfkeymp->b_cont = msgmp; 18920Sstevel@tonic-gate 18930Sstevel@tonic-gate /* Set up ACQUIRE. */ 18940Sstevel@tonic-gate cur = sadb_setup_acquire(cur, end, acqrec); 18950Sstevel@tonic-gate if (cur == NULL) { 18960Sstevel@tonic-gate ah0dbg(("sadb_setup_acquire failed.\n")); 18970Sstevel@tonic-gate /* Just bail. */ 18980Sstevel@tonic-gate freemsg(pfkeymp); 18990Sstevel@tonic-gate pfkeymp = NULL; 19000Sstevel@tonic-gate goto done; 19010Sstevel@tonic-gate } 19020Sstevel@tonic-gate samsg->sadb_msg_satype = SADB_SATYPE_AH; 19030Sstevel@tonic-gate 19040Sstevel@tonic-gate /* XXX Insert proxy address information here. */ 19050Sstevel@tonic-gate 19060Sstevel@tonic-gate /* XXX Insert identity information here. */ 19070Sstevel@tonic-gate 19080Sstevel@tonic-gate /* XXXMLS Insert sensitivity information here. */ 19090Sstevel@tonic-gate 19100Sstevel@tonic-gate /* Insert proposal here. */ 19110Sstevel@tonic-gate 19120Sstevel@tonic-gate prop = (sadb_prop_t *)(((uint64_t *)samsg) + samsg->sadb_msg_len); 19130Sstevel@tonic-gate ah_insert_prop(prop, acqrec, combs); 19140Sstevel@tonic-gate samsg->sadb_msg_len += prop->sadb_prop_len; 19150Sstevel@tonic-gate msgmp->b_wptr += SADB_64TO8(samsg->sadb_msg_len); 19160Sstevel@tonic-gate 19170Sstevel@tonic-gate done: 19180Sstevel@tonic-gate mutex_exit(&alg_lock); 19190Sstevel@tonic-gate 19200Sstevel@tonic-gate /* 19210Sstevel@tonic-gate * Must mutex_exit() before sending PF_KEY message up, in 19220Sstevel@tonic-gate * order to avoid recursive mutex_enter() if there are no registered 19230Sstevel@tonic-gate * listeners. 19240Sstevel@tonic-gate * 19250Sstevel@tonic-gate * Once I've sent the message, I'm cool anyway. 19260Sstevel@tonic-gate */ 19270Sstevel@tonic-gate mutex_exit(&acqrec->ipsacq_lock); 19280Sstevel@tonic-gate if (ah_pfkey_q != NULL && pfkeymp != NULL) { 19290Sstevel@tonic-gate if (extended != NULL) { 19300Sstevel@tonic-gate putnext(ah_pfkey_q, extended); 19310Sstevel@tonic-gate } 19320Sstevel@tonic-gate putnext(ah_pfkey_q, pfkeymp); 19330Sstevel@tonic-gate return; 19340Sstevel@tonic-gate } 19350Sstevel@tonic-gate /* NOTE: freemsg() works for extended == NULL. */ 19360Sstevel@tonic-gate freemsg(extended); 19370Sstevel@tonic-gate freemsg(pfkeymp); 19380Sstevel@tonic-gate } 19390Sstevel@tonic-gate 19400Sstevel@tonic-gate /* 19410Sstevel@tonic-gate * Handle the SADB_GETSPI message. Create a larval SA. 19420Sstevel@tonic-gate */ 19430Sstevel@tonic-gate static void 19440Sstevel@tonic-gate ah_getspi(mblk_t *mp, keysock_in_t *ksi) 19450Sstevel@tonic-gate { 19460Sstevel@tonic-gate ipsa_t *newbie, *target; 19470Sstevel@tonic-gate isaf_t *outbound, *inbound; 19480Sstevel@tonic-gate int rc, diagnostic; 19490Sstevel@tonic-gate sadb_sa_t *assoc; 19500Sstevel@tonic-gate keysock_out_t *kso; 19510Sstevel@tonic-gate uint32_t newspi; 19520Sstevel@tonic-gate 19530Sstevel@tonic-gate /* 19540Sstevel@tonic-gate * Randomly generate a proposed SPI value. 19550Sstevel@tonic-gate */ 19560Sstevel@tonic-gate (void) random_get_pseudo_bytes((uint8_t *)&newspi, sizeof (uint32_t)); 19570Sstevel@tonic-gate newbie = sadb_getspi(ksi, newspi, &diagnostic); 19580Sstevel@tonic-gate 19590Sstevel@tonic-gate if (newbie == NULL) { 19600Sstevel@tonic-gate sadb_pfkey_error(ah_pfkey_q, mp, ENOMEM, diagnostic, 19610Sstevel@tonic-gate ksi->ks_in_serial); 19620Sstevel@tonic-gate return; 19630Sstevel@tonic-gate } else if (newbie == (ipsa_t *)-1) { 19640Sstevel@tonic-gate sadb_pfkey_error(ah_pfkey_q, mp, EINVAL, diagnostic, 19650Sstevel@tonic-gate ksi->ks_in_serial); 19660Sstevel@tonic-gate return; 19670Sstevel@tonic-gate } 19680Sstevel@tonic-gate 19690Sstevel@tonic-gate /* 19700Sstevel@tonic-gate * XXX - We may randomly collide. We really should recover from this. 19710Sstevel@tonic-gate * Unfortunately, that could require spending way-too-much-time 19720Sstevel@tonic-gate * in here. For now, let the user retry. 19730Sstevel@tonic-gate */ 19740Sstevel@tonic-gate 19750Sstevel@tonic-gate if (newbie->ipsa_addrfam == AF_INET6) { 1976564Ssommerfe outbound = OUTBOUND_BUCKET_V6(&ah_sadb.s_v6, 1977564Ssommerfe *(uint32_t *)(newbie->ipsa_dstaddr)); 1978564Ssommerfe inbound = INBOUND_BUCKET(&ah_sadb.s_v6, newbie->ipsa_spi); 19790Sstevel@tonic-gate } else { 1980564Ssommerfe outbound = OUTBOUND_BUCKET_V4(&ah_sadb.s_v4, 1981564Ssommerfe *(uint32_t *)(newbie->ipsa_dstaddr)); 1982564Ssommerfe inbound = INBOUND_BUCKET(&ah_sadb.s_v4, newbie->ipsa_spi); 19830Sstevel@tonic-gate } 19840Sstevel@tonic-gate 19850Sstevel@tonic-gate mutex_enter(&outbound->isaf_lock); 19860Sstevel@tonic-gate mutex_enter(&inbound->isaf_lock); 19870Sstevel@tonic-gate 19880Sstevel@tonic-gate /* 19890Sstevel@tonic-gate * Check for collisions (i.e. did sadb_getspi() return with something 19900Sstevel@tonic-gate * that already exists?). 19910Sstevel@tonic-gate * 19920Sstevel@tonic-gate * Try outbound first. Even though SADB_GETSPI is traditionally 19930Sstevel@tonic-gate * for inbound SAs, you never know what a user might do. 19940Sstevel@tonic-gate */ 19950Sstevel@tonic-gate target = ipsec_getassocbyspi(outbound, newbie->ipsa_spi, 19960Sstevel@tonic-gate newbie->ipsa_srcaddr, newbie->ipsa_dstaddr, newbie->ipsa_addrfam); 19970Sstevel@tonic-gate if (target == NULL) { 19980Sstevel@tonic-gate target = ipsec_getassocbyspi(inbound, newbie->ipsa_spi, 19990Sstevel@tonic-gate newbie->ipsa_srcaddr, newbie->ipsa_dstaddr, 20000Sstevel@tonic-gate newbie->ipsa_addrfam); 20010Sstevel@tonic-gate } 20020Sstevel@tonic-gate 20030Sstevel@tonic-gate /* 20040Sstevel@tonic-gate * I don't have collisions elsewhere! 20050Sstevel@tonic-gate * (Nor will I because I'm still holding inbound/outbound locks.) 20060Sstevel@tonic-gate */ 20070Sstevel@tonic-gate 20080Sstevel@tonic-gate if (target != NULL) { 20090Sstevel@tonic-gate rc = EEXIST; 20100Sstevel@tonic-gate IPSA_REFRELE(target); 20110Sstevel@tonic-gate } else { 20120Sstevel@tonic-gate /* 20130Sstevel@tonic-gate * sadb_insertassoc() also checks for collisions, so 20140Sstevel@tonic-gate * if there's a colliding larval entry, rc will be set 20150Sstevel@tonic-gate * to EEXIST. 20160Sstevel@tonic-gate */ 20170Sstevel@tonic-gate rc = sadb_insertassoc(newbie, inbound); 20180Sstevel@tonic-gate (void) drv_getparm(TIME, &newbie->ipsa_hardexpiretime); 20190Sstevel@tonic-gate newbie->ipsa_hardexpiretime += ipsecah_larval_timeout; 20200Sstevel@tonic-gate } 20210Sstevel@tonic-gate 20220Sstevel@tonic-gate /* 20230Sstevel@tonic-gate * Can exit outbound mutex. Hold inbound until we're done with 20240Sstevel@tonic-gate * newbie. 20250Sstevel@tonic-gate */ 20260Sstevel@tonic-gate mutex_exit(&outbound->isaf_lock); 20270Sstevel@tonic-gate 20280Sstevel@tonic-gate if (rc != 0) { 20290Sstevel@tonic-gate mutex_exit(&inbound->isaf_lock); 20300Sstevel@tonic-gate IPSA_REFRELE(newbie); 20310Sstevel@tonic-gate sadb_pfkey_error(ah_pfkey_q, mp, rc, SADB_X_DIAGNOSTIC_NONE, 20320Sstevel@tonic-gate ksi->ks_in_serial); 20330Sstevel@tonic-gate return; 20340Sstevel@tonic-gate } 20350Sstevel@tonic-gate 20360Sstevel@tonic-gate /* Can write here because I'm still holding the bucket lock. */ 20370Sstevel@tonic-gate newbie->ipsa_type = SADB_SATYPE_AH; 20380Sstevel@tonic-gate 20390Sstevel@tonic-gate /* 20400Sstevel@tonic-gate * Construct successful return message. We have one thing going 20410Sstevel@tonic-gate * for us in PF_KEY v2. That's the fact that 20420Sstevel@tonic-gate * sizeof (sadb_spirange_t) == sizeof (sadb_sa_t) 20430Sstevel@tonic-gate */ 20440Sstevel@tonic-gate assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SPIRANGE]; 20450Sstevel@tonic-gate assoc->sadb_sa_exttype = SADB_EXT_SA; 20460Sstevel@tonic-gate assoc->sadb_sa_spi = newbie->ipsa_spi; 20470Sstevel@tonic-gate *((uint64_t *)(&assoc->sadb_sa_replay)) = 0; 20480Sstevel@tonic-gate mutex_exit(&inbound->isaf_lock); 20490Sstevel@tonic-gate 20500Sstevel@tonic-gate /* Convert KEYSOCK_IN to KEYSOCK_OUT. */ 20510Sstevel@tonic-gate kso = (keysock_out_t *)ksi; 20520Sstevel@tonic-gate kso->ks_out_len = sizeof (*kso); 20530Sstevel@tonic-gate kso->ks_out_serial = ksi->ks_in_serial; 20540Sstevel@tonic-gate kso->ks_out_type = KEYSOCK_OUT; 20550Sstevel@tonic-gate 20560Sstevel@tonic-gate /* 20570Sstevel@tonic-gate * Can safely putnext() to ah_pfkey_q, because this is a turnaround 20580Sstevel@tonic-gate * from the ah_pfkey_q. 20590Sstevel@tonic-gate */ 20600Sstevel@tonic-gate putnext(ah_pfkey_q, mp); 20610Sstevel@tonic-gate } 20620Sstevel@tonic-gate 20630Sstevel@tonic-gate /* 20640Sstevel@tonic-gate * IPv6 sends up the ICMP errors for validation and the removal of the AH 20650Sstevel@tonic-gate * header. 20660Sstevel@tonic-gate */ 20670Sstevel@tonic-gate static ipsec_status_t 20680Sstevel@tonic-gate ah_icmp_error_v6(mblk_t *ipsec_mp) 20690Sstevel@tonic-gate { 20700Sstevel@tonic-gate mblk_t *mp; 20710Sstevel@tonic-gate ip6_t *ip6h, *oip6h; 20720Sstevel@tonic-gate uint16_t hdr_length, ah_length; 20730Sstevel@tonic-gate uint8_t *nexthdrp; 20740Sstevel@tonic-gate ah_t *ah; 20750Sstevel@tonic-gate icmp6_t *icmp6; 20760Sstevel@tonic-gate isaf_t *isaf; 20770Sstevel@tonic-gate ipsa_t *assoc; 20780Sstevel@tonic-gate uint8_t *post_ah_ptr; 20790Sstevel@tonic-gate 20800Sstevel@tonic-gate mp = ipsec_mp->b_cont; 20810Sstevel@tonic-gate ASSERT(mp->b_datap->db_type == M_CTL); 20820Sstevel@tonic-gate 20830Sstevel@tonic-gate /* 20840Sstevel@tonic-gate * Change the type to M_DATA till we finish pullups. 20850Sstevel@tonic-gate */ 20860Sstevel@tonic-gate mp->b_datap->db_type = M_DATA; 20870Sstevel@tonic-gate 20880Sstevel@tonic-gate /* 20890Sstevel@tonic-gate * Eat the cost of a pullupmsg() for now. It makes the rest of this 20900Sstevel@tonic-gate * code far less convoluted. 20910Sstevel@tonic-gate */ 20920Sstevel@tonic-gate if (!pullupmsg(mp, -1) || 20930Sstevel@tonic-gate !ip_hdr_length_nexthdr_v6(mp, (ip6_t *)mp->b_rptr, &hdr_length, 20940Sstevel@tonic-gate &nexthdrp) || 20950Sstevel@tonic-gate mp->b_rptr + hdr_length + sizeof (icmp6_t) + sizeof (ip6_t) + 20960Sstevel@tonic-gate sizeof (ah_t) > mp->b_wptr) { 20970Sstevel@tonic-gate IP_AH_BUMP_STAT(in_discards); 20980Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, &ipdrops_ah_nomem, 20990Sstevel@tonic-gate &ah_dropper); 21000Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 21010Sstevel@tonic-gate } 21020Sstevel@tonic-gate 21030Sstevel@tonic-gate oip6h = (ip6_t *)mp->b_rptr; 21040Sstevel@tonic-gate icmp6 = (icmp6_t *)((uint8_t *)oip6h + hdr_length); 21050Sstevel@tonic-gate ip6h = (ip6_t *)(icmp6 + 1); 21060Sstevel@tonic-gate if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_length, &nexthdrp)) { 21070Sstevel@tonic-gate IP_AH_BUMP_STAT(in_discards); 21080Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 21090Sstevel@tonic-gate &ipdrops_ah_bad_v6_hdrs, &ah_dropper); 21100Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 21110Sstevel@tonic-gate } 21120Sstevel@tonic-gate ah = (ah_t *)((uint8_t *)ip6h + hdr_length); 21130Sstevel@tonic-gate 2114564Ssommerfe isaf = OUTBOUND_BUCKET_V6(&ah_sadb.s_v6, ip6h->ip6_dst); 21150Sstevel@tonic-gate mutex_enter(&isaf->isaf_lock); 21160Sstevel@tonic-gate assoc = ipsec_getassocbyspi(isaf, ah->ah_spi, 21170Sstevel@tonic-gate (uint32_t *)&ip6h->ip6_src, (uint32_t *)&ip6h->ip6_dst, AF_INET6); 21180Sstevel@tonic-gate mutex_exit(&isaf->isaf_lock); 21190Sstevel@tonic-gate 21200Sstevel@tonic-gate if (assoc == NULL) { 21210Sstevel@tonic-gate IP_AH_BUMP_STAT(lookup_failure); 21220Sstevel@tonic-gate IP_AH_BUMP_STAT(in_discards); 21230Sstevel@tonic-gate if (ipsecah_log_unknown_spi) { 21240Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 21250Sstevel@tonic-gate SL_CONSOLE | SL_WARN | SL_ERROR, 21260Sstevel@tonic-gate "Bad ICMP message - No association for the " 21270Sstevel@tonic-gate "attached AH header whose spi is 0x%x, " 21280Sstevel@tonic-gate "sender is 0x%x\n", 21290Sstevel@tonic-gate ah->ah_spi, &oip6h->ip6_src, AF_INET6); 21300Sstevel@tonic-gate } 21310Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, &ipdrops_ah_no_sa, 21320Sstevel@tonic-gate &ah_dropper); 21330Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 21340Sstevel@tonic-gate } 21350Sstevel@tonic-gate 21360Sstevel@tonic-gate IPSA_REFRELE(assoc); 21370Sstevel@tonic-gate 21380Sstevel@tonic-gate /* 21390Sstevel@tonic-gate * There seems to be a valid association. If there is enough of AH 21400Sstevel@tonic-gate * header remove it, otherwise bail. One could check whether it has 21410Sstevel@tonic-gate * complete AH header plus 8 bytes but it does not make sense if an 21420Sstevel@tonic-gate * icmp error is returned for ICMP messages e.g ICMP time exceeded, 21430Sstevel@tonic-gate * that are being sent up. Let the caller figure out. 21440Sstevel@tonic-gate * 21450Sstevel@tonic-gate * NOTE: ah_length is the number of 32 bit words minus 2. 21460Sstevel@tonic-gate */ 21470Sstevel@tonic-gate ah_length = (ah->ah_length << 2) + 8; 21480Sstevel@tonic-gate post_ah_ptr = (uint8_t *)ah + ah_length; 21490Sstevel@tonic-gate 21500Sstevel@tonic-gate if (post_ah_ptr > mp->b_wptr) { 21510Sstevel@tonic-gate IP_AH_BUMP_STAT(in_discards); 21520Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 21530Sstevel@tonic-gate &ipdrops_ah_bad_length, &ah_dropper); 21540Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 21550Sstevel@tonic-gate } 21560Sstevel@tonic-gate 21570Sstevel@tonic-gate ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - ah_length); 21580Sstevel@tonic-gate *nexthdrp = ah->ah_nexthdr; 21590Sstevel@tonic-gate ovbcopy(post_ah_ptr, ah, 21600Sstevel@tonic-gate (size_t)((uintptr_t)mp->b_wptr - (uintptr_t)post_ah_ptr)); 21610Sstevel@tonic-gate mp->b_wptr -= ah_length; 21620Sstevel@tonic-gate /* Rewhack to be an ICMP error. */ 21630Sstevel@tonic-gate mp->b_datap->db_type = M_CTL; 21640Sstevel@tonic-gate 21650Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 21660Sstevel@tonic-gate } 21670Sstevel@tonic-gate 21680Sstevel@tonic-gate /* 21690Sstevel@tonic-gate * IP sends up the ICMP errors for validation and the removal of 21700Sstevel@tonic-gate * the AH header. 21710Sstevel@tonic-gate */ 21720Sstevel@tonic-gate static ipsec_status_t 21730Sstevel@tonic-gate ah_icmp_error_v4(mblk_t *ipsec_mp) 21740Sstevel@tonic-gate { 21750Sstevel@tonic-gate mblk_t *mp; 21760Sstevel@tonic-gate mblk_t *mp1; 21770Sstevel@tonic-gate icmph_t *icmph; 21780Sstevel@tonic-gate int iph_hdr_length; 21790Sstevel@tonic-gate int hdr_length; 21800Sstevel@tonic-gate isaf_t *hptr; 21810Sstevel@tonic-gate ipsa_t *assoc; 21820Sstevel@tonic-gate int ah_length; 21830Sstevel@tonic-gate ipha_t *ipha; 21840Sstevel@tonic-gate ipha_t *oipha; 21850Sstevel@tonic-gate ah_t *ah; 21860Sstevel@tonic-gate uint32_t length; 21870Sstevel@tonic-gate int alloc_size; 21880Sstevel@tonic-gate uint8_t nexthdr; 21890Sstevel@tonic-gate 21900Sstevel@tonic-gate mp = ipsec_mp->b_cont; 21910Sstevel@tonic-gate ASSERT(mp->b_datap->db_type == M_CTL); 21920Sstevel@tonic-gate 21930Sstevel@tonic-gate /* 21940Sstevel@tonic-gate * Change the type to M_DATA till we finish pullups. 21950Sstevel@tonic-gate */ 21960Sstevel@tonic-gate mp->b_datap->db_type = M_DATA; 21970Sstevel@tonic-gate 21980Sstevel@tonic-gate oipha = ipha = (ipha_t *)mp->b_rptr; 21990Sstevel@tonic-gate iph_hdr_length = IPH_HDR_LENGTH(ipha); 22000Sstevel@tonic-gate icmph = (icmph_t *)&mp->b_rptr[iph_hdr_length]; 22010Sstevel@tonic-gate 22020Sstevel@tonic-gate ipha = (ipha_t *)&icmph[1]; 22030Sstevel@tonic-gate hdr_length = IPH_HDR_LENGTH(ipha); 22040Sstevel@tonic-gate 22050Sstevel@tonic-gate /* 22060Sstevel@tonic-gate * See if we have enough to locate the SPI 22070Sstevel@tonic-gate */ 22080Sstevel@tonic-gate if ((uchar_t *)ipha + hdr_length + 8 > mp->b_wptr) { 22090Sstevel@tonic-gate if (!pullupmsg(mp, (uchar_t *)ipha + hdr_length + 8 - 22100Sstevel@tonic-gate mp->b_rptr)) { 22110Sstevel@tonic-gate ipsec_rl_strlog(info.mi_idnum, 0, 0, 22120Sstevel@tonic-gate SL_WARN | SL_ERROR, 22130Sstevel@tonic-gate "ICMP error: Small AH header\n"); 22140Sstevel@tonic-gate IP_AH_BUMP_STAT(in_discards); 22150Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 22160Sstevel@tonic-gate &ipdrops_ah_bad_length, &ah_dropper); 22170Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 22180Sstevel@tonic-gate } 22190Sstevel@tonic-gate icmph = (icmph_t *)&mp->b_rptr[iph_hdr_length]; 22200Sstevel@tonic-gate ipha = (ipha_t *)&icmph[1]; 22210Sstevel@tonic-gate } 22220Sstevel@tonic-gate 22230Sstevel@tonic-gate ah = (ah_t *)((uint8_t *)ipha + hdr_length); 22240Sstevel@tonic-gate nexthdr = ah->ah_nexthdr; 22250Sstevel@tonic-gate 2226564Ssommerfe hptr = OUTBOUND_BUCKET_V4(&ah_sadb.s_v4, ipha->ipha_dst); 22270Sstevel@tonic-gate mutex_enter(&hptr->isaf_lock); 22280Sstevel@tonic-gate assoc = ipsec_getassocbyspi(hptr, ah->ah_spi, 22290Sstevel@tonic-gate (uint32_t *)&ipha->ipha_src, (uint32_t *)&ipha->ipha_dst, AF_INET); 22300Sstevel@tonic-gate mutex_exit(&hptr->isaf_lock); 22310Sstevel@tonic-gate 22320Sstevel@tonic-gate if (assoc == NULL) { 22330Sstevel@tonic-gate IP_AH_BUMP_STAT(lookup_failure); 22340Sstevel@tonic-gate IP_AH_BUMP_STAT(in_discards); 22350Sstevel@tonic-gate if (ipsecah_log_unknown_spi) { 22360Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 22370Sstevel@tonic-gate SL_CONSOLE | SL_WARN | SL_ERROR, 22380Sstevel@tonic-gate "Bad ICMP message - No association for the " 22390Sstevel@tonic-gate "attached AH header whose spi is 0x%x, " 22400Sstevel@tonic-gate "sender is 0x%x\n", 22410Sstevel@tonic-gate ah->ah_spi, &oipha->ipha_src, AF_INET); 22420Sstevel@tonic-gate } 22430Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, &ipdrops_ah_no_sa, 22440Sstevel@tonic-gate &ah_dropper); 22450Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 22460Sstevel@tonic-gate } 22470Sstevel@tonic-gate 22480Sstevel@tonic-gate IPSA_REFRELE(assoc); 22490Sstevel@tonic-gate /* 22500Sstevel@tonic-gate * There seems to be a valid association. If there 22510Sstevel@tonic-gate * is enough of AH header remove it, otherwise remove 22520Sstevel@tonic-gate * as much as possible and send it back. One could check 22530Sstevel@tonic-gate * whether it has complete AH header plus 8 bytes but it 22540Sstevel@tonic-gate * does not make sense if an icmp error is returned for 22550Sstevel@tonic-gate * ICMP messages e.g ICMP time exceeded, that are being 22560Sstevel@tonic-gate * sent up. Let the caller figure out. 22570Sstevel@tonic-gate * 22580Sstevel@tonic-gate * NOTE: ah_length is the number of 32 bit words minus 2. 22590Sstevel@tonic-gate */ 22600Sstevel@tonic-gate ah_length = (ah->ah_length << 2) + 8; 22610Sstevel@tonic-gate 22620Sstevel@tonic-gate if ((uchar_t *)ipha + hdr_length + ah_length > mp->b_wptr) { 22630Sstevel@tonic-gate if (mp->b_cont == NULL) { 22640Sstevel@tonic-gate /* 22650Sstevel@tonic-gate * There is nothing to pullup. Just remove as 22660Sstevel@tonic-gate * much as possible. This is a common case for 22670Sstevel@tonic-gate * IPV4. 22680Sstevel@tonic-gate */ 22690Sstevel@tonic-gate ah_length = (mp->b_wptr - ((uchar_t *)ipha + 22700Sstevel@tonic-gate hdr_length)); 22710Sstevel@tonic-gate goto done; 22720Sstevel@tonic-gate } 22730Sstevel@tonic-gate /* Pullup the full ah header */ 22740Sstevel@tonic-gate if (!pullupmsg(mp, (uchar_t *)ah + ah_length - mp->b_rptr)) { 22750Sstevel@tonic-gate /* 22760Sstevel@tonic-gate * pullupmsg could have failed if there was not 22770Sstevel@tonic-gate * enough to pullup or memory allocation failed. 22780Sstevel@tonic-gate * We tried hard, give up now. 22790Sstevel@tonic-gate */ 22800Sstevel@tonic-gate IP_AH_BUMP_STAT(in_discards); 22810Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 22820Sstevel@tonic-gate &ipdrops_ah_nomem, &ah_dropper); 22830Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 22840Sstevel@tonic-gate } 22850Sstevel@tonic-gate icmph = (icmph_t *)&mp->b_rptr[iph_hdr_length]; 22860Sstevel@tonic-gate ipha = (ipha_t *)&icmph[1]; 22870Sstevel@tonic-gate } 22880Sstevel@tonic-gate done: 22890Sstevel@tonic-gate /* 22900Sstevel@tonic-gate * Remove the AH header and change the protocol. 22910Sstevel@tonic-gate * Don't update the spi fields in the ipsec_in 22920Sstevel@tonic-gate * message as we are called just to validate the 22930Sstevel@tonic-gate * message attached to the ICMP message. 22940Sstevel@tonic-gate * 22950Sstevel@tonic-gate * If we never pulled up since all of the message 22960Sstevel@tonic-gate * is in one single mblk, we can't remove the AH header 22970Sstevel@tonic-gate * by just setting the b_wptr to the beginning of the 22980Sstevel@tonic-gate * AH header. We need to allocate a mblk that can hold 22990Sstevel@tonic-gate * up until the inner IP header and copy them. 23000Sstevel@tonic-gate */ 23010Sstevel@tonic-gate alloc_size = iph_hdr_length + sizeof (icmph_t) + hdr_length; 23020Sstevel@tonic-gate 23030Sstevel@tonic-gate if ((mp1 = allocb(alloc_size, BPRI_LO)) == NULL) { 23040Sstevel@tonic-gate IP_AH_BUMP_STAT(in_discards); 23050Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, &ipdrops_ah_nomem, 23060Sstevel@tonic-gate &ah_dropper); 23070Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 23080Sstevel@tonic-gate } 23090Sstevel@tonic-gate /* ICMP errors are M_CTL messages */ 23100Sstevel@tonic-gate mp1->b_datap->db_type = M_CTL; 23110Sstevel@tonic-gate ipsec_mp->b_cont = mp1; 23120Sstevel@tonic-gate bcopy(mp->b_rptr, mp1->b_rptr, alloc_size); 23130Sstevel@tonic-gate mp1->b_wptr += alloc_size; 23140Sstevel@tonic-gate 23150Sstevel@tonic-gate /* 23160Sstevel@tonic-gate * Skip whatever we have copied and as much of AH header 23170Sstevel@tonic-gate * possible. If we still have something left in the original 23180Sstevel@tonic-gate * message, tag on. 23190Sstevel@tonic-gate */ 23200Sstevel@tonic-gate mp->b_rptr = (uchar_t *)ipha + hdr_length + ah_length; 23210Sstevel@tonic-gate 23220Sstevel@tonic-gate if (mp->b_rptr != mp->b_wptr) { 23230Sstevel@tonic-gate mp1->b_cont = mp; 23240Sstevel@tonic-gate } else { 23250Sstevel@tonic-gate if (mp->b_cont != NULL) 23260Sstevel@tonic-gate mp1->b_cont = mp->b_cont; 23270Sstevel@tonic-gate freeb(mp); 23280Sstevel@tonic-gate } 23290Sstevel@tonic-gate 23300Sstevel@tonic-gate ipha = (ipha_t *)(mp1->b_rptr + iph_hdr_length + sizeof (icmph_t)); 23310Sstevel@tonic-gate ipha->ipha_protocol = nexthdr; 23320Sstevel@tonic-gate length = ntohs(ipha->ipha_length); 23330Sstevel@tonic-gate length -= ah_length; 23340Sstevel@tonic-gate ipha->ipha_length = htons((uint16_t)length); 23350Sstevel@tonic-gate ipha->ipha_hdr_checksum = 0; 23360Sstevel@tonic-gate ipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(ipha); 23370Sstevel@tonic-gate 23380Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 23390Sstevel@tonic-gate } 23400Sstevel@tonic-gate 23410Sstevel@tonic-gate /* 23420Sstevel@tonic-gate * IP calls this to validate the ICMP errors that 23430Sstevel@tonic-gate * we got from the network. 23440Sstevel@tonic-gate */ 23450Sstevel@tonic-gate ipsec_status_t 23460Sstevel@tonic-gate ipsecah_icmp_error(mblk_t *mp) 23470Sstevel@tonic-gate { 23480Sstevel@tonic-gate ipsec_in_t *ii = (ipsec_in_t *)mp->b_rptr; 23490Sstevel@tonic-gate 23500Sstevel@tonic-gate if (ii->ipsec_in_v4) 23510Sstevel@tonic-gate return (ah_icmp_error_v4(mp)); 23520Sstevel@tonic-gate else 23530Sstevel@tonic-gate return (ah_icmp_error_v6(mp)); 23540Sstevel@tonic-gate } 23550Sstevel@tonic-gate 23560Sstevel@tonic-gate static int 23570Sstevel@tonic-gate ah_fix_tlv_options_v6(uint8_t *oi_opt, uint8_t *pi_opt, uint_t ehdrlen, 23580Sstevel@tonic-gate uint8_t hdr_type, boolean_t copy_always) 23590Sstevel@tonic-gate { 23600Sstevel@tonic-gate uint8_t opt_type; 23610Sstevel@tonic-gate uint_t optlen; 23620Sstevel@tonic-gate 23630Sstevel@tonic-gate ASSERT(hdr_type == IPPROTO_DSTOPTS || hdr_type == IPPROTO_HOPOPTS); 23640Sstevel@tonic-gate 23650Sstevel@tonic-gate /* 23660Sstevel@tonic-gate * Copy the next header and hdr ext. len of the HOP-by-HOP 23670Sstevel@tonic-gate * and Destination option. 23680Sstevel@tonic-gate */ 23690Sstevel@tonic-gate *pi_opt++ = *oi_opt++; 23700Sstevel@tonic-gate *pi_opt++ = *oi_opt++; 23710Sstevel@tonic-gate ehdrlen -= 2; 23720Sstevel@tonic-gate 23730Sstevel@tonic-gate /* 23740Sstevel@tonic-gate * Now handle all the TLV encoded options. 23750Sstevel@tonic-gate */ 23760Sstevel@tonic-gate while (ehdrlen != 0) { 23770Sstevel@tonic-gate opt_type = *oi_opt; 23780Sstevel@tonic-gate 23790Sstevel@tonic-gate if (opt_type == IP6OPT_PAD1) { 23800Sstevel@tonic-gate optlen = 1; 23810Sstevel@tonic-gate } else { 23820Sstevel@tonic-gate if (ehdrlen < 2) 23830Sstevel@tonic-gate goto bad_opt; 23840Sstevel@tonic-gate optlen = 2 + oi_opt[1]; 23850Sstevel@tonic-gate if (optlen > ehdrlen) 23860Sstevel@tonic-gate goto bad_opt; 23870Sstevel@tonic-gate } 23880Sstevel@tonic-gate if (copy_always || !(opt_type & IP6OPT_MUTABLE)) { 23890Sstevel@tonic-gate bcopy(oi_opt, pi_opt, optlen); 23900Sstevel@tonic-gate } else { 23910Sstevel@tonic-gate if (optlen == 1) { 23920Sstevel@tonic-gate *pi_opt = 0; 23930Sstevel@tonic-gate } else { 23940Sstevel@tonic-gate /* 23950Sstevel@tonic-gate * Copy the type and data length fields. 23960Sstevel@tonic-gate * Zero the option data by skipping 23970Sstevel@tonic-gate * option type and option data len 23980Sstevel@tonic-gate * fields. 23990Sstevel@tonic-gate */ 24000Sstevel@tonic-gate *pi_opt = *oi_opt; 24010Sstevel@tonic-gate *(pi_opt + 1) = *(oi_opt + 1); 24020Sstevel@tonic-gate bzero(pi_opt + 2, optlen - 2); 24030Sstevel@tonic-gate } 24040Sstevel@tonic-gate } 24050Sstevel@tonic-gate ehdrlen -= optlen; 24060Sstevel@tonic-gate oi_opt += optlen; 24070Sstevel@tonic-gate pi_opt += optlen; 24080Sstevel@tonic-gate } 24090Sstevel@tonic-gate return (0); 24100Sstevel@tonic-gate bad_opt: 24110Sstevel@tonic-gate return (-1); 24120Sstevel@tonic-gate } 24130Sstevel@tonic-gate 24140Sstevel@tonic-gate /* 24150Sstevel@tonic-gate * Construct a pseudo header for AH, processing all the options. 24160Sstevel@tonic-gate * 24170Sstevel@tonic-gate * oip6h is the IPv6 header of the incoming or outgoing packet. 24180Sstevel@tonic-gate * ip6h is the pointer to the pseudo headers IPV6 header. All 24190Sstevel@tonic-gate * the space needed for the options have been allocated including 24200Sstevel@tonic-gate * the AH header. 24210Sstevel@tonic-gate * 24220Sstevel@tonic-gate * If copy_always is set, all the options that appear before AH are copied 24230Sstevel@tonic-gate * blindly without checking for IP6OPT_MUTABLE. This is used by 24240Sstevel@tonic-gate * ah_auth_out_done(). Please refer to that function for details. 24250Sstevel@tonic-gate * 24260Sstevel@tonic-gate * NOTE : 24270Sstevel@tonic-gate * 24280Sstevel@tonic-gate * * AH header is never copied in this function even if copy_always 24290Sstevel@tonic-gate * is set. It just returns the ah_offset - offset of the AH header 24300Sstevel@tonic-gate * and the caller needs to do the copying. This is done so that we 24310Sstevel@tonic-gate * don't have pass extra arguments e.g. SA etc. and also, 24320Sstevel@tonic-gate * it is not needed when ah_auth_out_done is calling this function. 24330Sstevel@tonic-gate */ 24340Sstevel@tonic-gate static uint_t 24350Sstevel@tonic-gate ah_fix_phdr_v6(ip6_t *ip6h, ip6_t *oip6h, boolean_t outbound, 24360Sstevel@tonic-gate boolean_t copy_always) 24370Sstevel@tonic-gate { 24380Sstevel@tonic-gate uint8_t *oi_opt; 24390Sstevel@tonic-gate uint8_t *pi_opt; 24400Sstevel@tonic-gate uint8_t nexthdr; 24410Sstevel@tonic-gate uint8_t *prev_nexthdr; 24420Sstevel@tonic-gate ip6_hbh_t *hbhhdr; 24430Sstevel@tonic-gate ip6_dest_t *dsthdr = NULL; 24440Sstevel@tonic-gate ip6_rthdr0_t *rthdr; 24450Sstevel@tonic-gate int ehdrlen; 24460Sstevel@tonic-gate ah_t *ah; 24470Sstevel@tonic-gate int ret; 24480Sstevel@tonic-gate 24490Sstevel@tonic-gate /* 24500Sstevel@tonic-gate * In the outbound case for source route, ULP has already moved 24510Sstevel@tonic-gate * the first hop, which is now in ip6_dst. We need to re-arrange 24520Sstevel@tonic-gate * the header to make it look like how it would appear in the 24530Sstevel@tonic-gate * receiver i.e 24540Sstevel@tonic-gate * 24550Sstevel@tonic-gate * Because of ip_massage_options_v6 the header looks like 24560Sstevel@tonic-gate * this : 24570Sstevel@tonic-gate * 24580Sstevel@tonic-gate * ip6_src = S, ip6_dst = I1. followed by I2,I3,D. 24590Sstevel@tonic-gate * 24600Sstevel@tonic-gate * When it reaches the receiver, it would look like 24610Sstevel@tonic-gate * 24620Sstevel@tonic-gate * ip6_src = S, ip6_dst = D. followed by I1,I2,I3. 24630Sstevel@tonic-gate * 24640Sstevel@tonic-gate * NOTE : We assume that there are no problems with the options 24650Sstevel@tonic-gate * as IP should have already checked this. 24660Sstevel@tonic-gate */ 24670Sstevel@tonic-gate 24680Sstevel@tonic-gate oi_opt = (uchar_t *)&oip6h[1]; 24690Sstevel@tonic-gate pi_opt = (uchar_t *)&ip6h[1]; 24700Sstevel@tonic-gate 24710Sstevel@tonic-gate /* 24720Sstevel@tonic-gate * We set the prev_nexthdr properly in the pseudo header. 24730Sstevel@tonic-gate * After we finish authentication and come back from the 24740Sstevel@tonic-gate * algorithm module, pseudo header will become the real 24750Sstevel@tonic-gate * IP header. 24760Sstevel@tonic-gate */ 24770Sstevel@tonic-gate prev_nexthdr = (uint8_t *)&ip6h->ip6_nxt; 24780Sstevel@tonic-gate nexthdr = oip6h->ip6_nxt; 24790Sstevel@tonic-gate /* Assume IP has already stripped it */ 24800Sstevel@tonic-gate ASSERT(nexthdr != IPPROTO_FRAGMENT && nexthdr != IPPROTO_RAW); 24810Sstevel@tonic-gate ah = NULL; 24820Sstevel@tonic-gate dsthdr = NULL; 24830Sstevel@tonic-gate for (;;) { 24840Sstevel@tonic-gate switch (nexthdr) { 24850Sstevel@tonic-gate case IPPROTO_HOPOPTS: 24860Sstevel@tonic-gate hbhhdr = (ip6_hbh_t *)oi_opt; 24870Sstevel@tonic-gate nexthdr = hbhhdr->ip6h_nxt; 24880Sstevel@tonic-gate ehdrlen = 8 * (hbhhdr->ip6h_len + 1); 24890Sstevel@tonic-gate ret = ah_fix_tlv_options_v6(oi_opt, pi_opt, ehdrlen, 24900Sstevel@tonic-gate IPPROTO_HOPOPTS, copy_always); 24910Sstevel@tonic-gate /* 24920Sstevel@tonic-gate * Return a zero offset indicating error if there 24930Sstevel@tonic-gate * was error. 24940Sstevel@tonic-gate */ 24950Sstevel@tonic-gate if (ret == -1) 24960Sstevel@tonic-gate return (0); 24970Sstevel@tonic-gate hbhhdr = (ip6_hbh_t *)pi_opt; 24980Sstevel@tonic-gate prev_nexthdr = (uint8_t *)&hbhhdr->ip6h_nxt; 24990Sstevel@tonic-gate break; 25000Sstevel@tonic-gate case IPPROTO_ROUTING: 25010Sstevel@tonic-gate rthdr = (ip6_rthdr0_t *)oi_opt; 25020Sstevel@tonic-gate nexthdr = rthdr->ip6r0_nxt; 25030Sstevel@tonic-gate ehdrlen = 8 * (rthdr->ip6r0_len + 1); 25040Sstevel@tonic-gate if (!copy_always && outbound) { 25050Sstevel@tonic-gate int i, left; 25060Sstevel@tonic-gate ip6_rthdr0_t *prthdr; 25070Sstevel@tonic-gate in6_addr_t *ap, *pap; 25080Sstevel@tonic-gate 25090Sstevel@tonic-gate left = rthdr->ip6r0_segleft; 25100Sstevel@tonic-gate prthdr = (ip6_rthdr0_t *)pi_opt; 25110Sstevel@tonic-gate pap = (in6_addr_t *)(prthdr + 1); 25120Sstevel@tonic-gate ap = (in6_addr_t *)(rthdr + 1); 25130Sstevel@tonic-gate /* 25140Sstevel@tonic-gate * First eight bytes except seg_left 25150Sstevel@tonic-gate * does not change en route. 25160Sstevel@tonic-gate */ 25170Sstevel@tonic-gate bcopy(oi_opt, pi_opt, 8); 25180Sstevel@tonic-gate prthdr->ip6r0_segleft = 0; 25190Sstevel@tonic-gate /* 25200Sstevel@tonic-gate * First address has been moved to 25210Sstevel@tonic-gate * the destination address of the 25220Sstevel@tonic-gate * ip header by ip_massage_options_v6. 25230Sstevel@tonic-gate * And the real destination address is 25240Sstevel@tonic-gate * in the last address part of the 25250Sstevel@tonic-gate * option. 25260Sstevel@tonic-gate */ 25270Sstevel@tonic-gate *pap = oip6h->ip6_dst; 25280Sstevel@tonic-gate for (i = 1; i < left - 1; i++) 25290Sstevel@tonic-gate pap[i] = ap[i - 1]; 25300Sstevel@tonic-gate ip6h->ip6_dst = *(ap + left - 1); 25310Sstevel@tonic-gate } else { 25320Sstevel@tonic-gate bcopy(oi_opt, pi_opt, ehdrlen); 25330Sstevel@tonic-gate } 25340Sstevel@tonic-gate rthdr = (ip6_rthdr0_t *)pi_opt; 25350Sstevel@tonic-gate prev_nexthdr = (uint8_t *)&rthdr->ip6r0_nxt; 25360Sstevel@tonic-gate break; 25370Sstevel@tonic-gate case IPPROTO_DSTOPTS: 25380Sstevel@tonic-gate /* 25390Sstevel@tonic-gate * Destination options are tricky. If there is 25400Sstevel@tonic-gate * a terminal (e.g. non-IPv6-extension) header 25410Sstevel@tonic-gate * following the destination options, don't 25420Sstevel@tonic-gate * reset prev_nexthdr or advance the AH insertion 25430Sstevel@tonic-gate * point and just treat this as a terminal header. 25440Sstevel@tonic-gate * 25450Sstevel@tonic-gate * If this is an inbound packet, just deal with 25460Sstevel@tonic-gate * it as is. 25470Sstevel@tonic-gate */ 25480Sstevel@tonic-gate dsthdr = (ip6_dest_t *)oi_opt; 25490Sstevel@tonic-gate /* 25500Sstevel@tonic-gate * XXX I hope common-subexpression elimination 25510Sstevel@tonic-gate * saves us the double-evaluate. 25520Sstevel@tonic-gate */ 25530Sstevel@tonic-gate if (outbound && dsthdr->ip6d_nxt != IPPROTO_ROUTING && 25540Sstevel@tonic-gate dsthdr->ip6d_nxt != IPPROTO_HOPOPTS) 25550Sstevel@tonic-gate goto terminal_hdr; 25560Sstevel@tonic-gate nexthdr = dsthdr->ip6d_nxt; 25570Sstevel@tonic-gate ehdrlen = 8 * (dsthdr->ip6d_len + 1); 25580Sstevel@tonic-gate ret = ah_fix_tlv_options_v6(oi_opt, pi_opt, ehdrlen, 25590Sstevel@tonic-gate IPPROTO_DSTOPTS, copy_always); 25600Sstevel@tonic-gate /* 25610Sstevel@tonic-gate * Return a zero offset indicating error if there 25620Sstevel@tonic-gate * was error. 25630Sstevel@tonic-gate */ 25640Sstevel@tonic-gate if (ret == -1) 25650Sstevel@tonic-gate return (0); 25660Sstevel@tonic-gate break; 25670Sstevel@tonic-gate case IPPROTO_AH: 25680Sstevel@tonic-gate /* 25690Sstevel@tonic-gate * Be conservative in what you send. We shouldn't 25700Sstevel@tonic-gate * see two same-scoped AH's in one packet. 25710Sstevel@tonic-gate * (Inner-IP-scoped AH will be hit by terminal 25720Sstevel@tonic-gate * header of IP or IPv6.) 25730Sstevel@tonic-gate */ 25740Sstevel@tonic-gate ASSERT(!outbound); 25750Sstevel@tonic-gate return ((uint_t)(pi_opt - (uint8_t *)ip6h)); 25760Sstevel@tonic-gate default: 25770Sstevel@tonic-gate ASSERT(outbound); 25780Sstevel@tonic-gate terminal_hdr: 25790Sstevel@tonic-gate *prev_nexthdr = IPPROTO_AH; 25800Sstevel@tonic-gate ah = (ah_t *)pi_opt; 25810Sstevel@tonic-gate ah->ah_nexthdr = nexthdr; 25820Sstevel@tonic-gate return ((uint_t)(pi_opt - (uint8_t *)ip6h)); 25830Sstevel@tonic-gate } 25840Sstevel@tonic-gate pi_opt += ehdrlen; 25850Sstevel@tonic-gate oi_opt += ehdrlen; 25860Sstevel@tonic-gate } 25870Sstevel@tonic-gate /* NOTREACHED */ 25880Sstevel@tonic-gate } 25890Sstevel@tonic-gate 25900Sstevel@tonic-gate static boolean_t 25910Sstevel@tonic-gate ah_finish_up(ah_t *phdr_ah, ah_t *inbound_ah, ipsa_t *assoc, 25920Sstevel@tonic-gate int ah_data_sz, int ah_align_sz) 25930Sstevel@tonic-gate { 25940Sstevel@tonic-gate int i; 25950Sstevel@tonic-gate 25960Sstevel@tonic-gate /* 25970Sstevel@tonic-gate * Padding : 25980Sstevel@tonic-gate * 25990Sstevel@tonic-gate * 1) Authentication data may have to be padded 26000Sstevel@tonic-gate * before ICV calculation if ICV is not a multiple 26010Sstevel@tonic-gate * of 64 bits. This padding is arbitrary and transmitted 26020Sstevel@tonic-gate * with the packet at the end of the authentication data. 26030Sstevel@tonic-gate * Payload length should include the padding bytes. 26040Sstevel@tonic-gate * 26050Sstevel@tonic-gate * 2) Explicit padding of the whole datagram may be 26060Sstevel@tonic-gate * required by the algorithm which need not be 26070Sstevel@tonic-gate * transmitted. It is assumed that this will be taken 26080Sstevel@tonic-gate * care by the algorithm module. 26090Sstevel@tonic-gate */ 26100Sstevel@tonic-gate bzero(phdr_ah + 1, ah_data_sz); /* Zero out ICV for pseudo-hdr. */ 26110Sstevel@tonic-gate 26120Sstevel@tonic-gate if (inbound_ah == NULL) { 26130Sstevel@tonic-gate /* Outbound AH datagram. */ 26140Sstevel@tonic-gate 26150Sstevel@tonic-gate phdr_ah->ah_length = (ah_align_sz >> 2) + 1; 26160Sstevel@tonic-gate phdr_ah->ah_reserved = 0; 26170Sstevel@tonic-gate phdr_ah->ah_spi = assoc->ipsa_spi; 26180Sstevel@tonic-gate 26190Sstevel@tonic-gate phdr_ah->ah_replay = 26200Sstevel@tonic-gate htonl(atomic_add_32_nv(&assoc->ipsa_replay, 1)); 26210Sstevel@tonic-gate if (phdr_ah->ah_replay == 0 && assoc->ipsa_replay_wsize != 0) { 26220Sstevel@tonic-gate /* 26230Sstevel@tonic-gate * XXX We have replay counter wrapping. We probably 26240Sstevel@tonic-gate * want to nuke this SA (and its peer). 26250Sstevel@tonic-gate */ 26260Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 26270Sstevel@tonic-gate SL_ERROR | SL_CONSOLE | SL_WARN, 26280Sstevel@tonic-gate "Outbound AH SA (0x%x), dst %s has wrapped " 26290Sstevel@tonic-gate "sequence.\n", phdr_ah->ah_spi, 26300Sstevel@tonic-gate assoc->ipsa_dstaddr, assoc->ipsa_addrfam); 26310Sstevel@tonic-gate 26320Sstevel@tonic-gate sadb_replay_delete(assoc); 26330Sstevel@tonic-gate /* Caller will free phdr_mp and return NULL. */ 26340Sstevel@tonic-gate return (B_FALSE); 26350Sstevel@tonic-gate } 26360Sstevel@tonic-gate 26370Sstevel@tonic-gate if (ah_data_sz != ah_align_sz) { 26380Sstevel@tonic-gate uchar_t *pad = ((uchar_t *)phdr_ah + sizeof (ah_t) + 26390Sstevel@tonic-gate ah_data_sz); 26400Sstevel@tonic-gate 26410Sstevel@tonic-gate for (i = 0; i < (ah_align_sz - ah_data_sz); i++) { 26420Sstevel@tonic-gate pad[i] = (uchar_t)i; /* Fill the padding */ 26430Sstevel@tonic-gate } 26440Sstevel@tonic-gate } 26450Sstevel@tonic-gate } else { 26460Sstevel@tonic-gate /* Inbound AH datagram. */ 26470Sstevel@tonic-gate phdr_ah->ah_nexthdr = inbound_ah->ah_nexthdr; 26480Sstevel@tonic-gate phdr_ah->ah_length = inbound_ah->ah_length; 26490Sstevel@tonic-gate phdr_ah->ah_reserved = 0; 26500Sstevel@tonic-gate ASSERT(inbound_ah->ah_spi == assoc->ipsa_spi); 26510Sstevel@tonic-gate phdr_ah->ah_spi = inbound_ah->ah_spi; 26520Sstevel@tonic-gate phdr_ah->ah_replay = inbound_ah->ah_replay; 26530Sstevel@tonic-gate 26540Sstevel@tonic-gate if (ah_data_sz != ah_align_sz) { 26550Sstevel@tonic-gate uchar_t *opad = ((uchar_t *)inbound_ah + sizeof (ah_t) + 26560Sstevel@tonic-gate ah_data_sz); 26570Sstevel@tonic-gate uchar_t *pad = ((uchar_t *)phdr_ah + sizeof (ah_t) + 26580Sstevel@tonic-gate ah_data_sz); 26590Sstevel@tonic-gate 26600Sstevel@tonic-gate for (i = 0; i < (ah_align_sz - ah_data_sz); i++) { 26610Sstevel@tonic-gate pad[i] = opad[i]; /* Copy the padding */ 26620Sstevel@tonic-gate } 26630Sstevel@tonic-gate } 26640Sstevel@tonic-gate } 26650Sstevel@tonic-gate 26660Sstevel@tonic-gate return (B_TRUE); 26670Sstevel@tonic-gate } 26680Sstevel@tonic-gate 26690Sstevel@tonic-gate /* 26700Sstevel@tonic-gate * Called upon failing the inbound ICV check. The message passed as 26710Sstevel@tonic-gate * argument is freed. 26720Sstevel@tonic-gate */ 26730Sstevel@tonic-gate static void 26740Sstevel@tonic-gate ah_log_bad_auth(mblk_t *ipsec_in) 26750Sstevel@tonic-gate { 26760Sstevel@tonic-gate mblk_t *mp = ipsec_in->b_cont->b_cont; 26770Sstevel@tonic-gate ipsec_in_t *ii = (ipsec_in_t *)ipsec_in->b_rptr; 26780Sstevel@tonic-gate boolean_t isv4 = ii->ipsec_in_v4; 26790Sstevel@tonic-gate ipsa_t *assoc = ii->ipsec_in_ah_sa; 26800Sstevel@tonic-gate int af; 26810Sstevel@tonic-gate void *addr; 26820Sstevel@tonic-gate 26830Sstevel@tonic-gate mp->b_rptr -= ii->ipsec_in_skip_len; 26840Sstevel@tonic-gate 26850Sstevel@tonic-gate if (isv4) { 26860Sstevel@tonic-gate ipha_t *ipha = (ipha_t *)mp->b_rptr; 26870Sstevel@tonic-gate addr = &ipha->ipha_dst; 26880Sstevel@tonic-gate af = AF_INET; 26890Sstevel@tonic-gate } else { 26900Sstevel@tonic-gate ip6_t *ip6h = (ip6_t *)mp->b_rptr; 26910Sstevel@tonic-gate addr = &ip6h->ip6_dst; 26920Sstevel@tonic-gate af = AF_INET6; 26930Sstevel@tonic-gate } 26940Sstevel@tonic-gate 26950Sstevel@tonic-gate /* 26960Sstevel@tonic-gate * Log the event. Don't print to the console, block 26970Sstevel@tonic-gate * potential denial-of-service attack. 26980Sstevel@tonic-gate */ 26990Sstevel@tonic-gate AH_BUMP_STAT(bad_auth); 27000Sstevel@tonic-gate 27010Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, SL_ERROR | SL_WARN, 27020Sstevel@tonic-gate "AH Authentication failed spi %x, dst_addr %s", 27030Sstevel@tonic-gate assoc->ipsa_spi, addr, af); 27040Sstevel@tonic-gate 27050Sstevel@tonic-gate IP_AH_BUMP_STAT(in_discards); 27060Sstevel@tonic-gate ip_drop_packet(ipsec_in, B_TRUE, NULL, NULL, &ipdrops_ah_bad_auth, 27070Sstevel@tonic-gate &ah_dropper); 27080Sstevel@tonic-gate } 27090Sstevel@tonic-gate 27100Sstevel@tonic-gate /* 27110Sstevel@tonic-gate * Kernel crypto framework callback invoked after completion of async 27120Sstevel@tonic-gate * crypto requests. 27130Sstevel@tonic-gate */ 27140Sstevel@tonic-gate static void 27150Sstevel@tonic-gate ah_kcf_callback(void *arg, int status) 27160Sstevel@tonic-gate { 27170Sstevel@tonic-gate mblk_t *ipsec_mp = (mblk_t *)arg; 27180Sstevel@tonic-gate ipsec_in_t *ii = (ipsec_in_t *)ipsec_mp->b_rptr; 27190Sstevel@tonic-gate boolean_t is_inbound = (ii->ipsec_in_type == IPSEC_IN); 27200Sstevel@tonic-gate 27210Sstevel@tonic-gate ASSERT(ipsec_mp->b_cont != NULL); 27220Sstevel@tonic-gate 27230Sstevel@tonic-gate if (status == CRYPTO_SUCCESS) { 27240Sstevel@tonic-gate if (is_inbound) { 27250Sstevel@tonic-gate if (ah_auth_in_done(ipsec_mp) != IPSEC_STATUS_SUCCESS) 27260Sstevel@tonic-gate return; 27270Sstevel@tonic-gate /* finish IPsec processing */ 27280Sstevel@tonic-gate ip_fanout_proto_again(ipsec_mp, NULL, NULL, NULL); 27290Sstevel@tonic-gate } else { 27300Sstevel@tonic-gate ipha_t *ipha; 27310Sstevel@tonic-gate 27320Sstevel@tonic-gate if (ah_auth_out_done(ipsec_mp) != IPSEC_STATUS_SUCCESS) 27330Sstevel@tonic-gate return; 27340Sstevel@tonic-gate 27350Sstevel@tonic-gate /* finish IPsec processing */ 27360Sstevel@tonic-gate ipha = (ipha_t *)ipsec_mp->b_cont->b_rptr; 27370Sstevel@tonic-gate if (IPH_HDR_VERSION(ipha) == IP_VERSION) { 27380Sstevel@tonic-gate ip_wput_ipsec_out(NULL, ipsec_mp, ipha, NULL, 27390Sstevel@tonic-gate NULL); 27400Sstevel@tonic-gate } else { 27410Sstevel@tonic-gate ip6_t *ip6h = (ip6_t *)ipha; 27420Sstevel@tonic-gate ip_wput_ipsec_out_v6(NULL, ipsec_mp, ip6h, 27430Sstevel@tonic-gate NULL, NULL); 27440Sstevel@tonic-gate } 27450Sstevel@tonic-gate } 27460Sstevel@tonic-gate 27470Sstevel@tonic-gate } else if (status == CRYPTO_INVALID_MAC) { 27480Sstevel@tonic-gate ah_log_bad_auth(ipsec_mp); 27490Sstevel@tonic-gate 27500Sstevel@tonic-gate } else { 27510Sstevel@tonic-gate ah1dbg(("ah_kcf_callback: crypto failed with 0x%x\n", status)); 27520Sstevel@tonic-gate AH_BUMP_STAT(crypto_failures); 27530Sstevel@tonic-gate if (is_inbound) 27540Sstevel@tonic-gate IP_AH_BUMP_STAT(in_discards); 27550Sstevel@tonic-gate else 27560Sstevel@tonic-gate AH_BUMP_STAT(out_discards); 27570Sstevel@tonic-gate ip_drop_packet(ipsec_mp, is_inbound, NULL, NULL, 27580Sstevel@tonic-gate &ipdrops_ah_crypto_failed, &ah_dropper); 27590Sstevel@tonic-gate } 27600Sstevel@tonic-gate } 27610Sstevel@tonic-gate 27620Sstevel@tonic-gate /* 27630Sstevel@tonic-gate * Invoked on kernel crypto failure during inbound and outbound processing. 27640Sstevel@tonic-gate */ 27650Sstevel@tonic-gate static void 27660Sstevel@tonic-gate ah_crypto_failed(mblk_t *mp, boolean_t is_inbound, int kef_rc) 27670Sstevel@tonic-gate { 27680Sstevel@tonic-gate ah1dbg(("crypto failed for %s AH with 0x%x\n", 27690Sstevel@tonic-gate is_inbound ? "inbound" : "outbound", kef_rc)); 27700Sstevel@tonic-gate ip_drop_packet(mp, is_inbound, NULL, NULL, &ipdrops_ah_crypto_failed, 27710Sstevel@tonic-gate &ah_dropper); 27720Sstevel@tonic-gate AH_BUMP_STAT(crypto_failures); 27730Sstevel@tonic-gate if (is_inbound) 27740Sstevel@tonic-gate IP_AH_BUMP_STAT(in_discards); 27750Sstevel@tonic-gate else 27760Sstevel@tonic-gate AH_BUMP_STAT(out_discards); 27770Sstevel@tonic-gate } 27780Sstevel@tonic-gate 27790Sstevel@tonic-gate /* 27800Sstevel@tonic-gate * Helper macros for the ah_submit_req_{inbound,outbound}() functions. 27810Sstevel@tonic-gate */ 27820Sstevel@tonic-gate 27830Sstevel@tonic-gate #define AH_INIT_CALLREQ(_cr) { \ 27840Sstevel@tonic-gate (_cr)->cr_flag = CRYPTO_SKIP_REQID|CRYPTO_RESTRICTED; \ 27850Sstevel@tonic-gate if (ipsec_algs_exec_mode[IPSEC_ALG_AUTH] == IPSEC_ALGS_EXEC_ASYNC) \ 27860Sstevel@tonic-gate (_cr)->cr_flag |= CRYPTO_ALWAYS_QUEUE; \ 27870Sstevel@tonic-gate (_cr)->cr_callback_arg = ipsec_mp; \ 27880Sstevel@tonic-gate (_cr)->cr_callback_func = ah_kcf_callback; \ 27890Sstevel@tonic-gate } 27900Sstevel@tonic-gate 27910Sstevel@tonic-gate #define AH_INIT_CRYPTO_DATA(data, msglen, mblk) { \ 27920Sstevel@tonic-gate (data)->cd_format = CRYPTO_DATA_MBLK; \ 27930Sstevel@tonic-gate (data)->cd_mp = mblk; \ 27940Sstevel@tonic-gate (data)->cd_offset = 0; \ 27950Sstevel@tonic-gate (data)->cd_length = msglen; \ 27960Sstevel@tonic-gate } 27970Sstevel@tonic-gate 27980Sstevel@tonic-gate #define AH_INIT_CRYPTO_MAC(mac, icvlen, icvbuf) { \ 27990Sstevel@tonic-gate (mac)->cd_format = CRYPTO_DATA_RAW; \ 28000Sstevel@tonic-gate (mac)->cd_offset = 0; \ 28010Sstevel@tonic-gate (mac)->cd_length = icvlen; \ 28020Sstevel@tonic-gate (mac)->cd_raw.iov_base = icvbuf; \ 28030Sstevel@tonic-gate (mac)->cd_raw.iov_len = icvlen; \ 28040Sstevel@tonic-gate } 28050Sstevel@tonic-gate 28060Sstevel@tonic-gate /* 28070Sstevel@tonic-gate * Submit an inbound packet for processing by the crypto framework. 28080Sstevel@tonic-gate */ 28090Sstevel@tonic-gate static ipsec_status_t 28100Sstevel@tonic-gate ah_submit_req_inbound(mblk_t *ipsec_mp, size_t skip_len, uint32_t ah_offset, 28110Sstevel@tonic-gate ipsa_t *assoc) 28120Sstevel@tonic-gate { 28130Sstevel@tonic-gate int kef_rc; 28140Sstevel@tonic-gate mblk_t *phdr_mp; 28150Sstevel@tonic-gate crypto_call_req_t call_req; 28160Sstevel@tonic-gate ipsec_in_t *ii = (ipsec_in_t *)ipsec_mp->b_rptr; 28170Sstevel@tonic-gate uint_t icv_len = assoc->ipsa_mac_len; 28180Sstevel@tonic-gate crypto_ctx_template_t ctx_tmpl; 28190Sstevel@tonic-gate 28200Sstevel@tonic-gate phdr_mp = ipsec_mp->b_cont; 28210Sstevel@tonic-gate ASSERT(phdr_mp != NULL); 28220Sstevel@tonic-gate ASSERT(ii->ipsec_in_type == IPSEC_IN); 28230Sstevel@tonic-gate 28240Sstevel@tonic-gate /* init arguments for the crypto framework */ 28250Sstevel@tonic-gate AH_INIT_CRYPTO_DATA(&ii->ipsec_in_crypto_data, AH_MSGSIZE(phdr_mp), 28260Sstevel@tonic-gate phdr_mp); 28270Sstevel@tonic-gate 28280Sstevel@tonic-gate AH_INIT_CRYPTO_MAC(&ii->ipsec_in_crypto_mac, icv_len, 28290Sstevel@tonic-gate (char *)phdr_mp->b_cont->b_rptr - skip_len + ah_offset + 28300Sstevel@tonic-gate sizeof (ah_t)); 28310Sstevel@tonic-gate 28320Sstevel@tonic-gate AH_INIT_CALLREQ(&call_req); 28330Sstevel@tonic-gate 28340Sstevel@tonic-gate ii->ipsec_in_skip_len = skip_len; 28350Sstevel@tonic-gate 28360Sstevel@tonic-gate IPSEC_CTX_TMPL(assoc, ipsa_authtmpl, IPSEC_ALG_AUTH, ctx_tmpl); 28370Sstevel@tonic-gate 28380Sstevel@tonic-gate /* call KEF to do the MAC operation */ 28390Sstevel@tonic-gate kef_rc = crypto_mac_verify(&assoc->ipsa_amech, 28400Sstevel@tonic-gate &ii->ipsec_in_crypto_data, &assoc->ipsa_kcfauthkey, ctx_tmpl, 28410Sstevel@tonic-gate &ii->ipsec_in_crypto_mac, &call_req); 28420Sstevel@tonic-gate 28430Sstevel@tonic-gate switch (kef_rc) { 28440Sstevel@tonic-gate case CRYPTO_SUCCESS: 28450Sstevel@tonic-gate AH_BUMP_STAT(crypto_sync); 28460Sstevel@tonic-gate return (ah_auth_in_done(ipsec_mp)); 28470Sstevel@tonic-gate case CRYPTO_QUEUED: 28480Sstevel@tonic-gate /* ah_callback() will be invoked on completion */ 28490Sstevel@tonic-gate AH_BUMP_STAT(crypto_async); 28500Sstevel@tonic-gate return (IPSEC_STATUS_PENDING); 28510Sstevel@tonic-gate case CRYPTO_INVALID_MAC: 28520Sstevel@tonic-gate AH_BUMP_STAT(crypto_sync); 28530Sstevel@tonic-gate ah_log_bad_auth(ipsec_mp); 28540Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 28550Sstevel@tonic-gate } 28560Sstevel@tonic-gate 28570Sstevel@tonic-gate ah_crypto_failed(ipsec_mp, B_TRUE, kef_rc); 28580Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 28590Sstevel@tonic-gate } 28600Sstevel@tonic-gate 28610Sstevel@tonic-gate /* 28620Sstevel@tonic-gate * Submit an outbound packet for processing by the crypto framework. 28630Sstevel@tonic-gate */ 28640Sstevel@tonic-gate static ipsec_status_t 28650Sstevel@tonic-gate ah_submit_req_outbound(mblk_t *ipsec_mp, size_t skip_len, ipsa_t *assoc) 28660Sstevel@tonic-gate { 28670Sstevel@tonic-gate int kef_rc; 28680Sstevel@tonic-gate mblk_t *phdr_mp; 28690Sstevel@tonic-gate crypto_call_req_t call_req; 28700Sstevel@tonic-gate ipsec_out_t *io = (ipsec_out_t *)ipsec_mp->b_rptr; 28710Sstevel@tonic-gate uint_t icv_len = assoc->ipsa_mac_len; 28720Sstevel@tonic-gate 28730Sstevel@tonic-gate phdr_mp = ipsec_mp->b_cont; 28740Sstevel@tonic-gate ASSERT(phdr_mp != NULL); 28750Sstevel@tonic-gate ASSERT(io->ipsec_out_type == IPSEC_OUT); 28760Sstevel@tonic-gate 28770Sstevel@tonic-gate /* init arguments for the crypto framework */ 28780Sstevel@tonic-gate AH_INIT_CRYPTO_DATA(&io->ipsec_out_crypto_data, AH_MSGSIZE(phdr_mp), 28790Sstevel@tonic-gate phdr_mp); 28800Sstevel@tonic-gate 28810Sstevel@tonic-gate AH_INIT_CRYPTO_MAC(&io->ipsec_out_crypto_mac, icv_len, 28820Sstevel@tonic-gate (char *)phdr_mp->b_wptr); 28830Sstevel@tonic-gate 28840Sstevel@tonic-gate AH_INIT_CALLREQ(&call_req); 28850Sstevel@tonic-gate 28860Sstevel@tonic-gate io->ipsec_out_skip_len = skip_len; 28870Sstevel@tonic-gate 28880Sstevel@tonic-gate ASSERT(io->ipsec_out_ah_sa != NULL); 28890Sstevel@tonic-gate 28900Sstevel@tonic-gate /* call KEF to do the MAC operation */ 28910Sstevel@tonic-gate kef_rc = crypto_mac(&assoc->ipsa_amech, &io->ipsec_out_crypto_data, 28920Sstevel@tonic-gate &assoc->ipsa_kcfauthkey, assoc->ipsa_authtmpl, 28930Sstevel@tonic-gate &io->ipsec_out_crypto_mac, &call_req); 28940Sstevel@tonic-gate 28950Sstevel@tonic-gate switch (kef_rc) { 28960Sstevel@tonic-gate case CRYPTO_SUCCESS: 28970Sstevel@tonic-gate AH_BUMP_STAT(crypto_sync); 28980Sstevel@tonic-gate return (ah_auth_out_done(ipsec_mp)); 28990Sstevel@tonic-gate case CRYPTO_QUEUED: 29000Sstevel@tonic-gate /* ah_callback() will be invoked on completion */ 29010Sstevel@tonic-gate AH_BUMP_STAT(crypto_async); 29020Sstevel@tonic-gate return (IPSEC_STATUS_PENDING); 29030Sstevel@tonic-gate } 29040Sstevel@tonic-gate 29050Sstevel@tonic-gate ah_crypto_failed(ipsec_mp, B_FALSE, kef_rc); 29060Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 29070Sstevel@tonic-gate } 29080Sstevel@tonic-gate 29090Sstevel@tonic-gate /* 29100Sstevel@tonic-gate * This function constructs a pseudo header by looking at the IP header 29110Sstevel@tonic-gate * and options if any. This is called for both outbound and inbound, 29120Sstevel@tonic-gate * before computing the ICV. 29130Sstevel@tonic-gate */ 29140Sstevel@tonic-gate static mblk_t * 29150Sstevel@tonic-gate ah_process_ip_options_v6(mblk_t *mp, ipsa_t *assoc, int *length_to_skip, 29160Sstevel@tonic-gate uint_t ah_data_sz, boolean_t outbound) 29170Sstevel@tonic-gate { 29180Sstevel@tonic-gate ip6_t *ip6h; 29190Sstevel@tonic-gate ip6_t *oip6h; 29200Sstevel@tonic-gate mblk_t *phdr_mp; 29210Sstevel@tonic-gate int option_length; 29220Sstevel@tonic-gate uint_t ah_align_sz; 29230Sstevel@tonic-gate uint_t ah_offset; 29240Sstevel@tonic-gate int hdr_size; 29250Sstevel@tonic-gate 29260Sstevel@tonic-gate /* 29270Sstevel@tonic-gate * Allocate space for the authentication data also. It is 29280Sstevel@tonic-gate * useful both during the ICV calculation where we need to 29290Sstevel@tonic-gate * feed in zeroes and while sending the datagram back to IP 29300Sstevel@tonic-gate * where we will be using the same space. 29310Sstevel@tonic-gate * 29320Sstevel@tonic-gate * We need to allocate space for padding bytes if it is not 29330Sstevel@tonic-gate * a multiple of IPV6_PADDING_ALIGN. 29340Sstevel@tonic-gate * 29350Sstevel@tonic-gate * In addition, we allocate space for the ICV computed by 29360Sstevel@tonic-gate * the kernel crypto framework, saving us a separate kmem 29370Sstevel@tonic-gate * allocation down the road. 29380Sstevel@tonic-gate */ 29390Sstevel@tonic-gate 29400Sstevel@tonic-gate ah_align_sz = P2ALIGN(ah_data_sz + IPV6_PADDING_ALIGN - 1, 29410Sstevel@tonic-gate IPV6_PADDING_ALIGN); 29420Sstevel@tonic-gate 29430Sstevel@tonic-gate ASSERT(ah_align_sz >= ah_data_sz); 29440Sstevel@tonic-gate 29450Sstevel@tonic-gate hdr_size = ipsec_ah_get_hdr_size_v6(mp, B_FALSE); 29460Sstevel@tonic-gate option_length = hdr_size - IPV6_HDR_LEN; 29470Sstevel@tonic-gate 29480Sstevel@tonic-gate /* This was not included in ipsec_ah_get_hdr_size_v6() */ 29490Sstevel@tonic-gate hdr_size += (sizeof (ah_t) + ah_align_sz); 29500Sstevel@tonic-gate 29510Sstevel@tonic-gate if (!outbound && (MBLKL(mp) < hdr_size)) { 29520Sstevel@tonic-gate /* 29530Sstevel@tonic-gate * We have post-AH header options in a separate mblk, 29540Sstevel@tonic-gate * a pullup is required. 29550Sstevel@tonic-gate */ 29560Sstevel@tonic-gate if (!pullupmsg(mp, hdr_size)) 29570Sstevel@tonic-gate return (NULL); 29580Sstevel@tonic-gate } 29590Sstevel@tonic-gate 29600Sstevel@tonic-gate if ((phdr_mp = allocb(hdr_size + ah_data_sz, BPRI_HI)) == NULL) { 29610Sstevel@tonic-gate return (NULL); 29620Sstevel@tonic-gate } 29630Sstevel@tonic-gate 29640Sstevel@tonic-gate oip6h = (ip6_t *)mp->b_rptr; 29650Sstevel@tonic-gate 29660Sstevel@tonic-gate /* 29670Sstevel@tonic-gate * Form the basic IP header first. Zero out the header 29680Sstevel@tonic-gate * so that the mutable fields are zeroed out. 29690Sstevel@tonic-gate */ 29700Sstevel@tonic-gate ip6h = (ip6_t *)phdr_mp->b_rptr; 29710Sstevel@tonic-gate bzero(ip6h, sizeof (ip6_t)); 29720Sstevel@tonic-gate ip6h->ip6_vcf = IPV6_DEFAULT_VERS_AND_FLOW; 29730Sstevel@tonic-gate 29740Sstevel@tonic-gate if (outbound) { 29750Sstevel@tonic-gate /* 29760Sstevel@tonic-gate * Include the size of AH and authentication data. 29770Sstevel@tonic-gate * This is how our recipient would compute the 29780Sstevel@tonic-gate * authentication data. Look at what we do in the 29790Sstevel@tonic-gate * inbound case below. 29800Sstevel@tonic-gate */ 29810Sstevel@tonic-gate ip6h->ip6_plen = htons(ntohs(oip6h->ip6_plen) + 29820Sstevel@tonic-gate sizeof (ah_t) + ah_align_sz); 29830Sstevel@tonic-gate } else { 29840Sstevel@tonic-gate ip6h->ip6_plen = oip6h->ip6_plen; 29850Sstevel@tonic-gate } 29860Sstevel@tonic-gate 29870Sstevel@tonic-gate ip6h->ip6_src = oip6h->ip6_src; 29880Sstevel@tonic-gate ip6h->ip6_dst = oip6h->ip6_dst; 29890Sstevel@tonic-gate 29900Sstevel@tonic-gate *length_to_skip = IPV6_HDR_LEN; 29910Sstevel@tonic-gate if (option_length == 0) { 29920Sstevel@tonic-gate /* Form the AH header */ 29930Sstevel@tonic-gate ip6h->ip6_nxt = IPPROTO_AH; 29940Sstevel@tonic-gate ((ah_t *)(ip6h + 1))->ah_nexthdr = oip6h->ip6_nxt; 29950Sstevel@tonic-gate ah_offset = *length_to_skip; 29960Sstevel@tonic-gate } else { 29970Sstevel@tonic-gate ip6h->ip6_nxt = oip6h->ip6_nxt; 29980Sstevel@tonic-gate /* option_length does not include the AH header's size */ 29990Sstevel@tonic-gate *length_to_skip += option_length; 30000Sstevel@tonic-gate 30010Sstevel@tonic-gate ah_offset = ah_fix_phdr_v6(ip6h, oip6h, outbound, B_FALSE); 30020Sstevel@tonic-gate if (ah_offset == 0) { 30030Sstevel@tonic-gate ip_drop_packet(phdr_mp, !outbound, NULL, NULL, 30040Sstevel@tonic-gate &ipdrops_ah_bad_v6_hdrs, &ah_dropper); 30050Sstevel@tonic-gate return (NULL); 30060Sstevel@tonic-gate } 30070Sstevel@tonic-gate } 30080Sstevel@tonic-gate 30090Sstevel@tonic-gate if (!ah_finish_up(((ah_t *)((uint8_t *)ip6h + ah_offset)), 30100Sstevel@tonic-gate (outbound ? NULL : ((ah_t *)((uint8_t *)oip6h + ah_offset))), 30110Sstevel@tonic-gate assoc, ah_data_sz, ah_align_sz)) { 30120Sstevel@tonic-gate freeb(phdr_mp); 30130Sstevel@tonic-gate /* 30140Sstevel@tonic-gate * Returning NULL will tell the caller to 30150Sstevel@tonic-gate * IPSA_REFELE(), free the memory, etc. 30160Sstevel@tonic-gate */ 30170Sstevel@tonic-gate return (NULL); 30180Sstevel@tonic-gate } 30190Sstevel@tonic-gate 30200Sstevel@tonic-gate phdr_mp->b_wptr = ((uint8_t *)ip6h + ah_offset + sizeof (ah_t) + 30210Sstevel@tonic-gate ah_align_sz); 30220Sstevel@tonic-gate if (!outbound) 30230Sstevel@tonic-gate *length_to_skip += sizeof (ah_t) + ah_align_sz; 30240Sstevel@tonic-gate return (phdr_mp); 30250Sstevel@tonic-gate } 30260Sstevel@tonic-gate 30270Sstevel@tonic-gate /* 30280Sstevel@tonic-gate * This function constructs a pseudo header by looking at the IP header 30290Sstevel@tonic-gate * and options if any. This is called for both outbound and inbound, 30300Sstevel@tonic-gate * before computing the ICV. 30310Sstevel@tonic-gate */ 30320Sstevel@tonic-gate static mblk_t * 30330Sstevel@tonic-gate ah_process_ip_options_v4(mblk_t *mp, ipsa_t *assoc, int *length_to_skip, 30340Sstevel@tonic-gate uint_t ah_data_sz, boolean_t outbound) 30350Sstevel@tonic-gate { 30360Sstevel@tonic-gate ipoptp_t opts; 30370Sstevel@tonic-gate uint32_t option_length; 30380Sstevel@tonic-gate ipha_t *ipha; 30390Sstevel@tonic-gate ipha_t *oipha; 30400Sstevel@tonic-gate mblk_t *phdr_mp; 30410Sstevel@tonic-gate int size; 30420Sstevel@tonic-gate uchar_t *optptr; 30430Sstevel@tonic-gate uint8_t optval; 30440Sstevel@tonic-gate uint8_t optlen; 30450Sstevel@tonic-gate ipaddr_t dst; 30460Sstevel@tonic-gate uint32_t v_hlen_tos_len; 30470Sstevel@tonic-gate int ip_hdr_length; 30480Sstevel@tonic-gate uint_t ah_align_sz; 30490Sstevel@tonic-gate uint32_t off; 30500Sstevel@tonic-gate 30510Sstevel@tonic-gate #ifdef _BIG_ENDIAN 30520Sstevel@tonic-gate #define V_HLEN (v_hlen_tos_len >> 24) 30530Sstevel@tonic-gate #else 30540Sstevel@tonic-gate #define V_HLEN (v_hlen_tos_len & 0xFF) 30550Sstevel@tonic-gate #endif 30560Sstevel@tonic-gate 30570Sstevel@tonic-gate oipha = (ipha_t *)mp->b_rptr; 30580Sstevel@tonic-gate v_hlen_tos_len = ((uint32_t *)oipha)[0]; 30590Sstevel@tonic-gate 30600Sstevel@tonic-gate /* 30610Sstevel@tonic-gate * Allocate space for the authentication data also. It is 30620Sstevel@tonic-gate * useful both during the ICV calculation where we need to 30630Sstevel@tonic-gate * feed in zeroes and while sending the datagram back to IP 30640Sstevel@tonic-gate * where we will be using the same space. 30650Sstevel@tonic-gate * 30660Sstevel@tonic-gate * We need to allocate space for padding bytes if it is not 30670Sstevel@tonic-gate * a multiple of IPV4_PADDING_ALIGN. 30680Sstevel@tonic-gate * 30690Sstevel@tonic-gate * In addition, we allocate space for the ICV computed by 30700Sstevel@tonic-gate * the kernel crypto framework, saving us a separate kmem 30710Sstevel@tonic-gate * allocation down the road. 30720Sstevel@tonic-gate */ 30730Sstevel@tonic-gate 30740Sstevel@tonic-gate ah_align_sz = P2ALIGN(ah_data_sz + IPV4_PADDING_ALIGN - 1, 30750Sstevel@tonic-gate IPV4_PADDING_ALIGN); 30760Sstevel@tonic-gate 30770Sstevel@tonic-gate ASSERT(ah_align_sz >= ah_data_sz); 30780Sstevel@tonic-gate 30790Sstevel@tonic-gate size = IP_SIMPLE_HDR_LENGTH + sizeof (ah_t) + ah_align_sz + 30800Sstevel@tonic-gate ah_data_sz; 30810Sstevel@tonic-gate 30820Sstevel@tonic-gate if (V_HLEN != IP_SIMPLE_HDR_VERSION) { 30830Sstevel@tonic-gate option_length = oipha->ipha_version_and_hdr_length - 30840Sstevel@tonic-gate (uint8_t)((IP_VERSION << 4) + 30850Sstevel@tonic-gate IP_SIMPLE_HDR_LENGTH_IN_WORDS); 30860Sstevel@tonic-gate option_length <<= 2; 30870Sstevel@tonic-gate size += option_length; 30880Sstevel@tonic-gate } 30890Sstevel@tonic-gate 30900Sstevel@tonic-gate if ((phdr_mp = allocb(size, BPRI_HI)) == NULL) { 30910Sstevel@tonic-gate return (NULL); 30920Sstevel@tonic-gate } 30930Sstevel@tonic-gate 30940Sstevel@tonic-gate /* 30950Sstevel@tonic-gate * Form the basic IP header first. 30960Sstevel@tonic-gate */ 30970Sstevel@tonic-gate ipha = (ipha_t *)phdr_mp->b_rptr; 30980Sstevel@tonic-gate ipha->ipha_version_and_hdr_length = oipha->ipha_version_and_hdr_length; 30990Sstevel@tonic-gate ipha->ipha_type_of_service = 0; 31000Sstevel@tonic-gate 31010Sstevel@tonic-gate if (outbound) { 31020Sstevel@tonic-gate /* 31030Sstevel@tonic-gate * Include the size of AH and authentication data. 31040Sstevel@tonic-gate * This is how our recipient would compute the 31050Sstevel@tonic-gate * authentication data. Look at what we do in the 31060Sstevel@tonic-gate * inbound case below. 31070Sstevel@tonic-gate */ 31080Sstevel@tonic-gate ipha->ipha_length = ntohs(htons(oipha->ipha_length) + 31090Sstevel@tonic-gate sizeof (ah_t) + ah_align_sz); 31100Sstevel@tonic-gate } else { 31110Sstevel@tonic-gate ipha->ipha_length = oipha->ipha_length; 31120Sstevel@tonic-gate } 31130Sstevel@tonic-gate 31140Sstevel@tonic-gate ipha->ipha_ident = oipha->ipha_ident; 31150Sstevel@tonic-gate ipha->ipha_fragment_offset_and_flags = 0; 31160Sstevel@tonic-gate ipha->ipha_ttl = 0; 31170Sstevel@tonic-gate ipha->ipha_protocol = IPPROTO_AH; 31180Sstevel@tonic-gate ipha->ipha_hdr_checksum = 0; 31190Sstevel@tonic-gate ipha->ipha_src = oipha->ipha_src; 31200Sstevel@tonic-gate ipha->ipha_dst = dst = oipha->ipha_dst; 31210Sstevel@tonic-gate 31220Sstevel@tonic-gate /* 31230Sstevel@tonic-gate * If there is no option to process return now. 31240Sstevel@tonic-gate */ 31250Sstevel@tonic-gate ip_hdr_length = IP_SIMPLE_HDR_LENGTH; 31260Sstevel@tonic-gate 31270Sstevel@tonic-gate if (V_HLEN == IP_SIMPLE_HDR_VERSION) { 31280Sstevel@tonic-gate /* Form the AH header */ 31290Sstevel@tonic-gate goto ah_hdr; 31300Sstevel@tonic-gate } 31310Sstevel@tonic-gate 31320Sstevel@tonic-gate ip_hdr_length += option_length; 31330Sstevel@tonic-gate 31340Sstevel@tonic-gate /* 31350Sstevel@tonic-gate * We have options. In the outbound case for source route, 31360Sstevel@tonic-gate * ULP has already moved the first hop, which is now in 31370Sstevel@tonic-gate * ipha_dst. We need the final destination for the calculation 31380Sstevel@tonic-gate * of authentication data. And also make sure that mutable 31390Sstevel@tonic-gate * and experimental fields are zeroed out in the IP options. 31400Sstevel@tonic-gate */ 31410Sstevel@tonic-gate 31420Sstevel@tonic-gate bcopy(&oipha[1], &ipha[1], option_length); 31430Sstevel@tonic-gate 31440Sstevel@tonic-gate for (optval = ipoptp_first(&opts, ipha); 31450Sstevel@tonic-gate optval != IPOPT_EOL; 31460Sstevel@tonic-gate optval = ipoptp_next(&opts)) { 31470Sstevel@tonic-gate optptr = opts.ipoptp_cur; 31480Sstevel@tonic-gate optlen = opts.ipoptp_len; 31490Sstevel@tonic-gate switch (optval) { 31500Sstevel@tonic-gate case IPOPT_EXTSEC: 31510Sstevel@tonic-gate case IPOPT_COMSEC: 31520Sstevel@tonic-gate case IPOPT_RA: 31530Sstevel@tonic-gate case IPOPT_SDMDD: 31540Sstevel@tonic-gate case IPOPT_SECURITY: 31550Sstevel@tonic-gate /* 31560Sstevel@tonic-gate * These options are Immutable, leave them as-is. 31570Sstevel@tonic-gate * Note that IPOPT_NOP is also Immutable, but it 31580Sstevel@tonic-gate * was skipped by ipoptp_next() and thus remains 31590Sstevel@tonic-gate * intact in the header. 31600Sstevel@tonic-gate */ 31610Sstevel@tonic-gate break; 31620Sstevel@tonic-gate case IPOPT_SSRR: 31630Sstevel@tonic-gate case IPOPT_LSRR: 31640Sstevel@tonic-gate if ((opts.ipoptp_flags & IPOPTP_ERROR) != 0) 31650Sstevel@tonic-gate goto bad_ipv4opt; 31660Sstevel@tonic-gate /* 31670Sstevel@tonic-gate * These two are mutable and will be zeroed, but 31680Sstevel@tonic-gate * first get the final destination. 31690Sstevel@tonic-gate */ 31700Sstevel@tonic-gate off = optptr[IPOPT_OFFSET]; 31710Sstevel@tonic-gate /* 31720Sstevel@tonic-gate * If one of the conditions is true, it means 31730Sstevel@tonic-gate * end of options and dst already has the right 31740Sstevel@tonic-gate * value. So, just fall through. 31750Sstevel@tonic-gate */ 31760Sstevel@tonic-gate if (!(optlen < IP_ADDR_LEN || off > optlen - 3)) { 31770Sstevel@tonic-gate off = optlen - IP_ADDR_LEN; 31780Sstevel@tonic-gate bcopy(&optptr[off], &dst, IP_ADDR_LEN); 31790Sstevel@tonic-gate } 31800Sstevel@tonic-gate /* FALLTHRU */ 31810Sstevel@tonic-gate case IPOPT_RR: 31820Sstevel@tonic-gate case IPOPT_TS: 31830Sstevel@tonic-gate case IPOPT_SATID: 31840Sstevel@tonic-gate default: 31850Sstevel@tonic-gate /* 31860Sstevel@tonic-gate * optlen should include from the beginning of an 31870Sstevel@tonic-gate * option. 31880Sstevel@tonic-gate * NOTE : Stream Identifier Option (SID): RFC 791 31890Sstevel@tonic-gate * shows the bit pattern of optlen as 2 and documents 31900Sstevel@tonic-gate * the length as 4. We assume it to be 2 here. 31910Sstevel@tonic-gate */ 31920Sstevel@tonic-gate bzero(optptr, optlen); 31930Sstevel@tonic-gate break; 31940Sstevel@tonic-gate } 31950Sstevel@tonic-gate } 31960Sstevel@tonic-gate 31970Sstevel@tonic-gate if ((opts.ipoptp_flags & IPOPTP_ERROR) != 0) { 31980Sstevel@tonic-gate bad_ipv4opt: 31990Sstevel@tonic-gate ah1dbg(("AH : bad IPv4 option")); 32000Sstevel@tonic-gate freeb(phdr_mp); 32010Sstevel@tonic-gate return (NULL); 32020Sstevel@tonic-gate } 32030Sstevel@tonic-gate 32040Sstevel@tonic-gate /* 32050Sstevel@tonic-gate * Don't change ipha_dst for an inbound datagram as it points 32060Sstevel@tonic-gate * to the right value. Only for the outbound with LSRR/SSRR, 32070Sstevel@tonic-gate * because of ip_massage_options called by the ULP, ipha_dst 32080Sstevel@tonic-gate * points to the first hop and we need to use the final 32090Sstevel@tonic-gate * destination for computing the ICV. 32100Sstevel@tonic-gate */ 32110Sstevel@tonic-gate 32120Sstevel@tonic-gate if (outbound) 32130Sstevel@tonic-gate ipha->ipha_dst = dst; 32140Sstevel@tonic-gate ah_hdr: 32150Sstevel@tonic-gate ((ah_t *)((uint8_t *)ipha + ip_hdr_length))->ah_nexthdr = 32160Sstevel@tonic-gate oipha->ipha_protocol; 32170Sstevel@tonic-gate if (!ah_finish_up(((ah_t *)((uint8_t *)ipha + ip_hdr_length)), 32180Sstevel@tonic-gate (outbound ? NULL : ((ah_t *)((uint8_t *)oipha + ip_hdr_length))), 32190Sstevel@tonic-gate assoc, ah_data_sz, ah_align_sz)) { 32200Sstevel@tonic-gate freeb(phdr_mp); 32210Sstevel@tonic-gate /* 32220Sstevel@tonic-gate * Returning NULL will tell the caller to IPSA_REFELE(), free 32230Sstevel@tonic-gate * the memory, etc. 32240Sstevel@tonic-gate */ 32250Sstevel@tonic-gate return (NULL); 32260Sstevel@tonic-gate } 32270Sstevel@tonic-gate 32280Sstevel@tonic-gate phdr_mp->b_wptr = ((uchar_t *)ipha + ip_hdr_length + 32290Sstevel@tonic-gate sizeof (ah_t) + ah_align_sz); 32300Sstevel@tonic-gate 32310Sstevel@tonic-gate ASSERT(phdr_mp->b_wptr <= phdr_mp->b_datap->db_lim); 32320Sstevel@tonic-gate if (outbound) 32330Sstevel@tonic-gate *length_to_skip = ip_hdr_length; 32340Sstevel@tonic-gate else 32350Sstevel@tonic-gate *length_to_skip = ip_hdr_length + sizeof (ah_t) + ah_align_sz; 32360Sstevel@tonic-gate return (phdr_mp); 32370Sstevel@tonic-gate } 32380Sstevel@tonic-gate 32390Sstevel@tonic-gate /* 32400Sstevel@tonic-gate * Authenticate an outbound datagram. This function is called 32410Sstevel@tonic-gate * whenever IP sends an outbound datagram that needs authentication. 32420Sstevel@tonic-gate */ 32430Sstevel@tonic-gate static ipsec_status_t 32440Sstevel@tonic-gate ah_outbound(mblk_t *ipsec_out) 32450Sstevel@tonic-gate { 32460Sstevel@tonic-gate mblk_t *mp; 32470Sstevel@tonic-gate mblk_t *phdr_mp; 32480Sstevel@tonic-gate ipsec_out_t *oi; 32490Sstevel@tonic-gate ipsa_t *assoc; 32500Sstevel@tonic-gate int length_to_skip; 32510Sstevel@tonic-gate uint_t ah_align_sz; 32520Sstevel@tonic-gate uint_t age_bytes; 32530Sstevel@tonic-gate 32540Sstevel@tonic-gate /* 32550Sstevel@tonic-gate * Construct the chain of mblks 32560Sstevel@tonic-gate * 32570Sstevel@tonic-gate * IPSEC_OUT->PSEUDO_HDR->DATA 32580Sstevel@tonic-gate * 32590Sstevel@tonic-gate * one by one. 32600Sstevel@tonic-gate */ 32610Sstevel@tonic-gate 32620Sstevel@tonic-gate AH_BUMP_STAT(out_requests); 32630Sstevel@tonic-gate 32640Sstevel@tonic-gate ASSERT(ipsec_out->b_datap->db_type == M_CTL); 32650Sstevel@tonic-gate 32660Sstevel@tonic-gate ASSERT(MBLKL(ipsec_out) >= sizeof (ipsec_info_t)); 32670Sstevel@tonic-gate 32680Sstevel@tonic-gate mp = ipsec_out->b_cont; 32690Sstevel@tonic-gate oi = (ipsec_out_t *)ipsec_out->b_rptr; 32700Sstevel@tonic-gate 32710Sstevel@tonic-gate ASSERT(mp->b_datap->db_type == M_DATA); 32720Sstevel@tonic-gate 32730Sstevel@tonic-gate assoc = oi->ipsec_out_ah_sa; 32740Sstevel@tonic-gate ASSERT(assoc != NULL); 32750Sstevel@tonic-gate if (assoc->ipsa_usetime == 0) 32760Sstevel@tonic-gate ah_set_usetime(assoc, B_FALSE); 32770Sstevel@tonic-gate 32780Sstevel@tonic-gate /* 32790Sstevel@tonic-gate * Age SA according to number of bytes that will be sent after 32800Sstevel@tonic-gate * adding the AH header, ICV, and padding to the packet. 32810Sstevel@tonic-gate */ 32820Sstevel@tonic-gate 32830Sstevel@tonic-gate if (oi->ipsec_out_v4) { 32840Sstevel@tonic-gate ipha_t *ipha = (ipha_t *)mp->b_rptr; 32850Sstevel@tonic-gate ah_align_sz = P2ALIGN(assoc->ipsa_mac_len + 32860Sstevel@tonic-gate IPV4_PADDING_ALIGN - 1, IPV4_PADDING_ALIGN); 32870Sstevel@tonic-gate age_bytes = ntohs(ipha->ipha_length) + sizeof (ah_t) + 32880Sstevel@tonic-gate ah_align_sz; 32890Sstevel@tonic-gate } else { 32900Sstevel@tonic-gate ip6_t *ip6h = (ip6_t *)mp->b_rptr; 32910Sstevel@tonic-gate ah_align_sz = P2ALIGN(assoc->ipsa_mac_len + 32920Sstevel@tonic-gate IPV6_PADDING_ALIGN - 1, IPV6_PADDING_ALIGN); 32930Sstevel@tonic-gate age_bytes = sizeof (ip6_t) + ntohs(ip6h->ip6_plen) + 32940Sstevel@tonic-gate sizeof (ah_t) + ah_align_sz; 32950Sstevel@tonic-gate } 32960Sstevel@tonic-gate 32970Sstevel@tonic-gate if (!ah_age_bytes(assoc, age_bytes, B_FALSE)) { 32980Sstevel@tonic-gate /* rig things as if ipsec_getassocbyconn() failed */ 32990Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, SL_ERROR | SL_WARN, 33000Sstevel@tonic-gate "AH association 0x%x, dst %s had bytes expire.\n", 33010Sstevel@tonic-gate ntohl(assoc->ipsa_spi), assoc->ipsa_dstaddr, AF_INET); 33020Sstevel@tonic-gate freemsg(ipsec_out); 33030Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 33040Sstevel@tonic-gate } 33050Sstevel@tonic-gate 33060Sstevel@tonic-gate if (oi->ipsec_out_is_capab_ill) { 33070Sstevel@tonic-gate ah3dbg(("ah_outbound: pkt can be accelerated\n")); 33080Sstevel@tonic-gate if (oi->ipsec_out_v4) 33090Sstevel@tonic-gate return (ah_outbound_accelerated_v4(ipsec_out, assoc)); 33100Sstevel@tonic-gate else 33110Sstevel@tonic-gate return (ah_outbound_accelerated_v6(ipsec_out, assoc)); 33120Sstevel@tonic-gate } 33130Sstevel@tonic-gate AH_BUMP_STAT(noaccel); 33140Sstevel@tonic-gate 33150Sstevel@tonic-gate /* 33160Sstevel@tonic-gate * Insert pseudo header: 33170Sstevel@tonic-gate * IPSEC_INFO -> [IP, ULP] => IPSEC_INFO -> [IP, AH, ICV] -> ULP 33180Sstevel@tonic-gate */ 33190Sstevel@tonic-gate 33200Sstevel@tonic-gate if (oi->ipsec_out_v4) { 33210Sstevel@tonic-gate phdr_mp = ah_process_ip_options_v4(mp, assoc, &length_to_skip, 33220Sstevel@tonic-gate assoc->ipsa_mac_len, B_TRUE); 33230Sstevel@tonic-gate } else { 33240Sstevel@tonic-gate phdr_mp = ah_process_ip_options_v6(mp, assoc, &length_to_skip, 33250Sstevel@tonic-gate assoc->ipsa_mac_len, B_TRUE); 33260Sstevel@tonic-gate } 33270Sstevel@tonic-gate 33280Sstevel@tonic-gate if (phdr_mp == NULL) { 33290Sstevel@tonic-gate AH_BUMP_STAT(out_discards); 33300Sstevel@tonic-gate ip_drop_packet(ipsec_out, B_FALSE, NULL, NULL, 33310Sstevel@tonic-gate &ipdrops_ah_bad_v4_opts, &ah_dropper); 33320Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 33330Sstevel@tonic-gate } 33340Sstevel@tonic-gate 33350Sstevel@tonic-gate ipsec_out->b_cont = phdr_mp; 33360Sstevel@tonic-gate phdr_mp->b_cont = mp; 33370Sstevel@tonic-gate mp->b_rptr += length_to_skip; 33380Sstevel@tonic-gate 33390Sstevel@tonic-gate /* 33400Sstevel@tonic-gate * At this point ipsec_out points to the IPSEC_OUT, new_mp 33410Sstevel@tonic-gate * points to an mblk containing the pseudo header (IP header, 33420Sstevel@tonic-gate * AH header, and ICV with mutable fields zero'ed out). 33430Sstevel@tonic-gate * mp points to the mblk containing the ULP data. The original 33440Sstevel@tonic-gate * IP header is kept before the ULP data in mp. 33450Sstevel@tonic-gate */ 33460Sstevel@tonic-gate 33470Sstevel@tonic-gate /* submit MAC request to KCF */ 33480Sstevel@tonic-gate return (ah_submit_req_outbound(ipsec_out, length_to_skip, assoc)); 33490Sstevel@tonic-gate } 33500Sstevel@tonic-gate 33510Sstevel@tonic-gate static ipsec_status_t 33520Sstevel@tonic-gate ah_inbound(mblk_t *ipsec_in_mp, void *arg) 33530Sstevel@tonic-gate { 33540Sstevel@tonic-gate mblk_t *data_mp = ipsec_in_mp->b_cont; 33550Sstevel@tonic-gate ipsec_in_t *ii = (ipsec_in_t *)ipsec_in_mp->b_rptr; 33560Sstevel@tonic-gate ah_t *ah = (ah_t *)arg; 33570Sstevel@tonic-gate ipsa_t *assoc = ii->ipsec_in_ah_sa; 33580Sstevel@tonic-gate int length_to_skip; 33590Sstevel@tonic-gate int ah_length; 33600Sstevel@tonic-gate mblk_t *phdr_mp; 33610Sstevel@tonic-gate uint32_t ah_offset; 33620Sstevel@tonic-gate 33630Sstevel@tonic-gate ASSERT(assoc != NULL); 33640Sstevel@tonic-gate if (assoc->ipsa_usetime == 0) 33650Sstevel@tonic-gate ah_set_usetime(assoc, B_TRUE); 33660Sstevel@tonic-gate 33670Sstevel@tonic-gate /* 33680Sstevel@tonic-gate * We may wish to check replay in-range-only here as an optimization. 33690Sstevel@tonic-gate * Include the reality check of ipsa->ipsa_replay > 33700Sstevel@tonic-gate * ipsa->ipsa_replay_wsize for times when it's the first N packets, 33710Sstevel@tonic-gate * where N == ipsa->ipsa_replay_wsize. 33720Sstevel@tonic-gate * 33730Sstevel@tonic-gate * Another check that may come here later is the "collision" check. 33740Sstevel@tonic-gate * If legitimate packets flow quickly enough, this won't be a problem, 33750Sstevel@tonic-gate * but collisions may cause authentication algorithm crunching to 33760Sstevel@tonic-gate * take place when it doesn't need to. 33770Sstevel@tonic-gate */ 33780Sstevel@tonic-gate if (!sadb_replay_peek(assoc, ah->ah_replay)) { 33790Sstevel@tonic-gate AH_BUMP_STAT(replay_early_failures); 33800Sstevel@tonic-gate IP_AH_BUMP_STAT(in_discards); 33810Sstevel@tonic-gate ip_drop_packet(ipsec_in_mp, B_TRUE, NULL, NULL, 33820Sstevel@tonic-gate &ipdrops_ah_early_replay, &ah_dropper); 33830Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 33840Sstevel@tonic-gate } 33850Sstevel@tonic-gate 33860Sstevel@tonic-gate /* 33870Sstevel@tonic-gate * The offset of the AH header can be computed from its pointer 33880Sstevel@tonic-gate * within the data mblk, which was pulled up until the AH header 33890Sstevel@tonic-gate * by ipsec_inbound_ah_sa() during SA selection. 33900Sstevel@tonic-gate */ 33910Sstevel@tonic-gate ah_offset = (uchar_t *)ah - data_mp->b_rptr; 33920Sstevel@tonic-gate 33930Sstevel@tonic-gate /* 33940Sstevel@tonic-gate * Has this packet already been processed by a hardware 33950Sstevel@tonic-gate * IPsec accelerator? 33960Sstevel@tonic-gate */ 33970Sstevel@tonic-gate if (ii->ipsec_in_accelerated) { 33980Sstevel@tonic-gate ah3dbg(("ah_inbound_v6: pkt processed by ill=%d isv6=%d\n", 33990Sstevel@tonic-gate ii->ipsec_in_ill_index, !ii->ipsec_in_v4)); 34000Sstevel@tonic-gate return (ah_inbound_accelerated(ipsec_in_mp, ii->ipsec_in_v4, 34010Sstevel@tonic-gate assoc, ah_offset)); 34020Sstevel@tonic-gate } 34030Sstevel@tonic-gate AH_BUMP_STAT(noaccel); 34040Sstevel@tonic-gate 34050Sstevel@tonic-gate /* 34060Sstevel@tonic-gate * We need to pullup until the ICV before we call 34070Sstevel@tonic-gate * ah_process_ip_options_v6. 34080Sstevel@tonic-gate */ 34090Sstevel@tonic-gate ah_length = (ah->ah_length << 2) + 8; 34100Sstevel@tonic-gate 34110Sstevel@tonic-gate /* 34120Sstevel@tonic-gate * NOTE : If we want to use any field of IP/AH header, you need 34130Sstevel@tonic-gate * to re-assign following the pullup. 34140Sstevel@tonic-gate */ 34150Sstevel@tonic-gate if (((uchar_t *)ah + ah_length) > data_mp->b_wptr) { 34160Sstevel@tonic-gate if (!pullupmsg(data_mp, (uchar_t *)ah + ah_length - 34170Sstevel@tonic-gate data_mp->b_rptr)) { 34180Sstevel@tonic-gate (void) ipsec_rl_strlog(info.mi_idnum, 0, 0, 34190Sstevel@tonic-gate SL_WARN | SL_ERROR, 34200Sstevel@tonic-gate "ah_inbound: Small AH header\n"); 34210Sstevel@tonic-gate IP_AH_BUMP_STAT(in_discards); 34220Sstevel@tonic-gate ip_drop_packet(ipsec_in_mp, B_TRUE, NULL, NULL, 34230Sstevel@tonic-gate &ipdrops_ah_nomem, &ah_dropper); 34240Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 34250Sstevel@tonic-gate } 34260Sstevel@tonic-gate } 34270Sstevel@tonic-gate 34280Sstevel@tonic-gate /* 34290Sstevel@tonic-gate * Insert pseudo header: 34300Sstevel@tonic-gate * IPSEC_INFO -> [IP, ULP] => IPSEC_INFO -> [IP, AH, ICV] -> ULP 34310Sstevel@tonic-gate */ 34320Sstevel@tonic-gate if (ii->ipsec_in_v4) { 34330Sstevel@tonic-gate phdr_mp = ah_process_ip_options_v4(data_mp, assoc, 34340Sstevel@tonic-gate &length_to_skip, assoc->ipsa_mac_len, B_FALSE); 34350Sstevel@tonic-gate } else { 34360Sstevel@tonic-gate phdr_mp = ah_process_ip_options_v6(data_mp, assoc, 34370Sstevel@tonic-gate &length_to_skip, assoc->ipsa_mac_len, B_FALSE); 34380Sstevel@tonic-gate } 34390Sstevel@tonic-gate 34400Sstevel@tonic-gate if (phdr_mp == NULL) { 34410Sstevel@tonic-gate IP_AH_BUMP_STAT(in_discards); 34420Sstevel@tonic-gate ip_drop_packet(ipsec_in_mp, B_TRUE, NULL, NULL, 34430Sstevel@tonic-gate ii->ipsec_in_v4 ? &ipdrops_ah_bad_v4_opts : 34440Sstevel@tonic-gate &ipdrops_ah_bad_v6_hdrs, &ah_dropper); 34450Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 34460Sstevel@tonic-gate } 34470Sstevel@tonic-gate 34480Sstevel@tonic-gate ipsec_in_mp->b_cont = phdr_mp; 34490Sstevel@tonic-gate phdr_mp->b_cont = data_mp; 34500Sstevel@tonic-gate data_mp->b_rptr += length_to_skip; 34510Sstevel@tonic-gate 34520Sstevel@tonic-gate /* submit request to KCF */ 34530Sstevel@tonic-gate return (ah_submit_req_inbound(ipsec_in_mp, length_to_skip, ah_offset, 34540Sstevel@tonic-gate assoc)); 34550Sstevel@tonic-gate } 34560Sstevel@tonic-gate 34570Sstevel@tonic-gate /* 34580Sstevel@tonic-gate * ah_inbound_accelerated: 34590Sstevel@tonic-gate * Called from ah_inbound() to process IPsec packets that have been 34600Sstevel@tonic-gate * accelerated by hardware. 34610Sstevel@tonic-gate * 34620Sstevel@tonic-gate * Basically does what ah_auth_in_done() with some changes since 34630Sstevel@tonic-gate * no pseudo-headers are involved, i.e. the passed message is a 34640Sstevel@tonic-gate * IPSEC_INFO->DATA. 34650Sstevel@tonic-gate * 34660Sstevel@tonic-gate * It is assumed that only packets that have been successfully 34670Sstevel@tonic-gate * processed by the adapter come here. 34680Sstevel@tonic-gate * 34690Sstevel@tonic-gate * 1. get algorithm structure corresponding to association 34700Sstevel@tonic-gate * 2. calculate pointers to authentication header and ICV 34710Sstevel@tonic-gate * 3. compare ICV in AH header with ICV in data attributes 34720Sstevel@tonic-gate * 3.1 if different: 34730Sstevel@tonic-gate * 3.1.1 generate error 34740Sstevel@tonic-gate * 3.1.2 discard message 34750Sstevel@tonic-gate * 3.2 if ICV matches: 34760Sstevel@tonic-gate * 3.2.1 check replay 34770Sstevel@tonic-gate * 3.2.2 remove AH header 34780Sstevel@tonic-gate * 3.2.3 age SA byte 34790Sstevel@tonic-gate * 3.2.4 send to IP 34800Sstevel@tonic-gate */ 34810Sstevel@tonic-gate ipsec_status_t 34820Sstevel@tonic-gate ah_inbound_accelerated(mblk_t *ipsec_in, boolean_t isv4, ipsa_t *assoc, 34830Sstevel@tonic-gate uint32_t ah_offset) 34840Sstevel@tonic-gate { 34850Sstevel@tonic-gate mblk_t *mp; 34860Sstevel@tonic-gate ipha_t *ipha; 34870Sstevel@tonic-gate ah_t *ah; 34880Sstevel@tonic-gate ipsec_in_t *ii; 34890Sstevel@tonic-gate uint32_t icv_len; 34900Sstevel@tonic-gate uint32_t align_len; 34910Sstevel@tonic-gate uint32_t age_bytes; 34920Sstevel@tonic-gate ip6_t *ip6h; 34930Sstevel@tonic-gate uint8_t *in_icv; 34940Sstevel@tonic-gate mblk_t *hada_mp; 34950Sstevel@tonic-gate uint32_t next_hdr; 34960Sstevel@tonic-gate da_ipsec_t *hada; 34970Sstevel@tonic-gate kstat_named_t *counter; 34980Sstevel@tonic-gate 34990Sstevel@tonic-gate AH_BUMP_STAT(in_accelerated); 35000Sstevel@tonic-gate 35010Sstevel@tonic-gate ii = (ipsec_in_t *)ipsec_in->b_rptr; 35020Sstevel@tonic-gate mp = ipsec_in->b_cont; 35030Sstevel@tonic-gate hada_mp = ii->ipsec_in_da; 35040Sstevel@tonic-gate ASSERT(hada_mp != NULL); 35050Sstevel@tonic-gate hada = (da_ipsec_t *)hada_mp->b_rptr; 35060Sstevel@tonic-gate 35070Sstevel@tonic-gate /* 35080Sstevel@tonic-gate * We only support one level of decapsulation in hardware, so 35090Sstevel@tonic-gate * nuke the pointer. 35100Sstevel@tonic-gate */ 35110Sstevel@tonic-gate ii->ipsec_in_da = NULL; 35120Sstevel@tonic-gate ii->ipsec_in_accelerated = B_FALSE; 35130Sstevel@tonic-gate 35140Sstevel@tonic-gate /* 35150Sstevel@tonic-gate * Extract ICV length from attributes M_CTL and sanity check 35160Sstevel@tonic-gate * its value. We allow the mblk to be smaller than da_ipsec_t 35170Sstevel@tonic-gate * for a small ICV, as long as the entire ICV fits within the mblk. 35180Sstevel@tonic-gate * Also ensures that the ICV length computed by Provider 35190Sstevel@tonic-gate * corresponds to the ICV length of the algorithm specified by the SA. 35200Sstevel@tonic-gate */ 35210Sstevel@tonic-gate icv_len = hada->da_icv_len; 35220Sstevel@tonic-gate if ((icv_len != assoc->ipsa_mac_len) || 35230Sstevel@tonic-gate (icv_len > DA_ICV_MAX_LEN) || (MBLKL(hada_mp) < 35240Sstevel@tonic-gate (sizeof (da_ipsec_t) - DA_ICV_MAX_LEN + icv_len))) { 35250Sstevel@tonic-gate ah0dbg(("ah_inbound_accelerated: " 35260Sstevel@tonic-gate "ICV len (%u) incorrect or mblk too small (%u)\n", 35270Sstevel@tonic-gate icv_len, (uint32_t)(MBLKL(hada_mp)))); 35280Sstevel@tonic-gate counter = &ipdrops_ah_bad_length; 35290Sstevel@tonic-gate goto ah_in_discard; 35300Sstevel@tonic-gate } 35310Sstevel@tonic-gate ASSERT(icv_len != 0); 35320Sstevel@tonic-gate 35330Sstevel@tonic-gate /* compute the padded AH ICV len */ 35340Sstevel@tonic-gate if (isv4) { 35350Sstevel@tonic-gate ipha = (ipha_t *)mp->b_rptr; 35360Sstevel@tonic-gate align_len = (icv_len + IPV4_PADDING_ALIGN - 1) & 35370Sstevel@tonic-gate -IPV4_PADDING_ALIGN; 35380Sstevel@tonic-gate } else { 35390Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 35400Sstevel@tonic-gate align_len = (icv_len + IPV6_PADDING_ALIGN - 1) & 35410Sstevel@tonic-gate -IPV6_PADDING_ALIGN; 35420Sstevel@tonic-gate } 35430Sstevel@tonic-gate 35440Sstevel@tonic-gate ah = (ah_t *)(mp->b_rptr + ah_offset); 35450Sstevel@tonic-gate in_icv = (uint8_t *)ah + sizeof (ah_t); 35460Sstevel@tonic-gate 35470Sstevel@tonic-gate /* compare ICV in AH header vs ICV computed by adapter */ 35480Sstevel@tonic-gate if (bcmp(hada->da_icv, in_icv, icv_len)) { 35490Sstevel@tonic-gate int af; 35500Sstevel@tonic-gate void *addr; 35510Sstevel@tonic-gate 35520Sstevel@tonic-gate if (isv4) { 35530Sstevel@tonic-gate addr = &ipha->ipha_dst; 35540Sstevel@tonic-gate af = AF_INET; 35550Sstevel@tonic-gate } else { 35560Sstevel@tonic-gate addr = &ip6h->ip6_dst; 35570Sstevel@tonic-gate af = AF_INET6; 35580Sstevel@tonic-gate } 35590Sstevel@tonic-gate 35600Sstevel@tonic-gate /* 35610Sstevel@tonic-gate * Log the event. Don't print to the console, block 35620Sstevel@tonic-gate * potential denial-of-service attack. 35630Sstevel@tonic-gate */ 35640Sstevel@tonic-gate AH_BUMP_STAT(bad_auth); 35650Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, SL_ERROR | SL_WARN, 35660Sstevel@tonic-gate "AH Authentication failed spi %x, dst_addr %s", 35670Sstevel@tonic-gate assoc->ipsa_spi, addr, af); 35680Sstevel@tonic-gate counter = &ipdrops_ah_bad_auth; 35690Sstevel@tonic-gate goto ah_in_discard; 35700Sstevel@tonic-gate } 35710Sstevel@tonic-gate 35720Sstevel@tonic-gate ah3dbg(("AH succeeded, checking replay\n")); 35730Sstevel@tonic-gate AH_BUMP_STAT(good_auth); 35740Sstevel@tonic-gate 35750Sstevel@tonic-gate if (!sadb_replay_check(assoc, ah->ah_replay)) { 35760Sstevel@tonic-gate int af; 35770Sstevel@tonic-gate void *addr; 35780Sstevel@tonic-gate 35790Sstevel@tonic-gate if (isv4) { 35800Sstevel@tonic-gate addr = &ipha->ipha_dst; 35810Sstevel@tonic-gate af = AF_INET; 35820Sstevel@tonic-gate } else { 35830Sstevel@tonic-gate addr = &ip6h->ip6_dst; 35840Sstevel@tonic-gate af = AF_INET6; 35850Sstevel@tonic-gate } 35860Sstevel@tonic-gate 35870Sstevel@tonic-gate /* 35880Sstevel@tonic-gate * Log the event. As of now we print out an event. 35890Sstevel@tonic-gate * Do not print the replay failure number, or else 35900Sstevel@tonic-gate * syslog cannot collate the error messages. Printing 35910Sstevel@tonic-gate * the replay number that failed (or printing to the 35920Sstevel@tonic-gate * console) opens a denial-of-service attack. 35930Sstevel@tonic-gate */ 35940Sstevel@tonic-gate AH_BUMP_STAT(replay_failures); 35950Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 35960Sstevel@tonic-gate SL_ERROR | SL_WARN, 35970Sstevel@tonic-gate "Replay failed for AH spi %x, dst_addr %s", 35980Sstevel@tonic-gate assoc->ipsa_spi, addr, af); 35990Sstevel@tonic-gate counter = &ipdrops_ah_replay; 36000Sstevel@tonic-gate goto ah_in_discard; 36010Sstevel@tonic-gate } 36020Sstevel@tonic-gate 36030Sstevel@tonic-gate /* 36040Sstevel@tonic-gate * Remove AH header. We do this by copying everything before 36050Sstevel@tonic-gate * the AH header onto the AH header+ICV. 36060Sstevel@tonic-gate */ 36070Sstevel@tonic-gate /* overwrite AH with what was preceeding it (IP header) */ 36080Sstevel@tonic-gate next_hdr = ah->ah_nexthdr; 36090Sstevel@tonic-gate ovbcopy(mp->b_rptr, mp->b_rptr + sizeof (ah_t) + align_len, 36100Sstevel@tonic-gate ah_offset); 36110Sstevel@tonic-gate mp->b_rptr += sizeof (ah_t) + align_len; 36120Sstevel@tonic-gate if (isv4) { 36130Sstevel@tonic-gate /* adjust IP header next protocol */ 36140Sstevel@tonic-gate ipha = (ipha_t *)mp->b_rptr; 36150Sstevel@tonic-gate ipha->ipha_protocol = next_hdr; 36160Sstevel@tonic-gate 36170Sstevel@tonic-gate age_bytes = ipha->ipha_length; 36180Sstevel@tonic-gate 36190Sstevel@tonic-gate /* adjust length in IP header */ 36200Sstevel@tonic-gate ipha->ipha_length -= (sizeof (ah_t) + align_len); 36210Sstevel@tonic-gate 36220Sstevel@tonic-gate /* recalculate checksum */ 36230Sstevel@tonic-gate ipha->ipha_hdr_checksum = 0; 36240Sstevel@tonic-gate ipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(ipha); 36250Sstevel@tonic-gate } else { 36260Sstevel@tonic-gate /* adjust IP header next protocol */ 36270Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 36280Sstevel@tonic-gate ip6h->ip6_nxt = next_hdr; 36290Sstevel@tonic-gate 36300Sstevel@tonic-gate age_bytes = sizeof (ip6_t) + ntohs(ip6h->ip6_plen) + 36310Sstevel@tonic-gate sizeof (ah_t); 36320Sstevel@tonic-gate 36330Sstevel@tonic-gate /* adjust length in IP header */ 36340Sstevel@tonic-gate ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - 36350Sstevel@tonic-gate (sizeof (ah_t) + align_len)); 36360Sstevel@tonic-gate } 36370Sstevel@tonic-gate 36380Sstevel@tonic-gate /* age SA */ 36390Sstevel@tonic-gate if (!ah_age_bytes(assoc, age_bytes, B_TRUE)) { 36400Sstevel@tonic-gate /* The ipsa has hit hard expiration, LOG and AUDIT. */ 36410Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 36420Sstevel@tonic-gate SL_ERROR | SL_WARN, 36430Sstevel@tonic-gate "AH Association 0x%x, dst %s had bytes expire.\n", 36440Sstevel@tonic-gate assoc->ipsa_spi, assoc->ipsa_dstaddr, 36450Sstevel@tonic-gate AF_INET); 36460Sstevel@tonic-gate AH_BUMP_STAT(bytes_expired); 36470Sstevel@tonic-gate counter = &ipdrops_ah_bytes_expire; 36480Sstevel@tonic-gate goto ah_in_discard; 36490Sstevel@tonic-gate } 36500Sstevel@tonic-gate 36510Sstevel@tonic-gate freeb(hada_mp); 36520Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 36530Sstevel@tonic-gate 36540Sstevel@tonic-gate ah_in_discard: 36550Sstevel@tonic-gate IP_AH_BUMP_STAT(in_discards); 36560Sstevel@tonic-gate freeb(hada_mp); 36570Sstevel@tonic-gate ip_drop_packet(ipsec_in, B_TRUE, NULL, NULL, counter, &ah_dropper); 36580Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 36590Sstevel@tonic-gate } 36600Sstevel@tonic-gate 36610Sstevel@tonic-gate /* 36620Sstevel@tonic-gate * ah_outbound_accelerated_v4: 36630Sstevel@tonic-gate * Called from ah_outbound_v4() and once it is determined that the 36640Sstevel@tonic-gate * packet is elligible for hardware acceleration. 36650Sstevel@tonic-gate * 36660Sstevel@tonic-gate * We proceed as follows: 36670Sstevel@tonic-gate * 1. allocate and initialize attributes mblk 36680Sstevel@tonic-gate * 2. mark IPSEC_OUT to indicate that pkt is accelerated 36690Sstevel@tonic-gate * 3. insert AH header 36700Sstevel@tonic-gate */ 36710Sstevel@tonic-gate static ipsec_status_t 36720Sstevel@tonic-gate ah_outbound_accelerated_v4(mblk_t *ipsec_mp, ipsa_t *assoc) 36730Sstevel@tonic-gate { 36740Sstevel@tonic-gate mblk_t *mp, *new_mp; 36750Sstevel@tonic-gate ipsec_out_t *oi; 36760Sstevel@tonic-gate uint_t ah_data_sz; /* ICV length, algorithm dependent */ 36770Sstevel@tonic-gate uint_t ah_align_sz; /* ICV length + padding */ 36780Sstevel@tonic-gate uint32_t v_hlen_tos_len; /* from original IP header */ 36790Sstevel@tonic-gate ipha_t *oipha; /* original IP header */ 36800Sstevel@tonic-gate ipha_t *nipha; /* new IP header */ 36810Sstevel@tonic-gate uint_t option_length = 0; 36820Sstevel@tonic-gate uint_t new_hdr_len; /* new header length */ 36830Sstevel@tonic-gate uint_t iphdr_length; 36840Sstevel@tonic-gate ah_t *ah_hdr; /* ptr to AH header */ 36850Sstevel@tonic-gate 36860Sstevel@tonic-gate AH_BUMP_STAT(out_accelerated); 36870Sstevel@tonic-gate 36880Sstevel@tonic-gate oi = (ipsec_out_t *)ipsec_mp->b_rptr; 36890Sstevel@tonic-gate mp = ipsec_mp->b_cont; 36900Sstevel@tonic-gate 36910Sstevel@tonic-gate oipha = (ipha_t *)mp->b_rptr; 36920Sstevel@tonic-gate v_hlen_tos_len = ((uint32_t *)oipha)[0]; 36930Sstevel@tonic-gate 36940Sstevel@tonic-gate /* mark packet as being accelerated in IPSEC_OUT */ 36950Sstevel@tonic-gate ASSERT(oi->ipsec_out_accelerated == B_FALSE); 36960Sstevel@tonic-gate oi->ipsec_out_accelerated = B_TRUE; 36970Sstevel@tonic-gate 36980Sstevel@tonic-gate /* calculate authentication data length, i.e. ICV + padding */ 36990Sstevel@tonic-gate ah_data_sz = assoc->ipsa_mac_len; 37000Sstevel@tonic-gate ah_align_sz = (ah_data_sz + IPV4_PADDING_ALIGN - 1) & 37010Sstevel@tonic-gate -IPV4_PADDING_ALIGN; 37020Sstevel@tonic-gate 37030Sstevel@tonic-gate /* 37040Sstevel@tonic-gate * Insert pseudo header: 37050Sstevel@tonic-gate * IPSEC_INFO -> [IP, ULP] => IPSEC_INFO -> [IP, AH, ICV] -> ULP 37060Sstevel@tonic-gate */ 37070Sstevel@tonic-gate 37080Sstevel@tonic-gate /* IP + AH + authentication + padding data length */ 37090Sstevel@tonic-gate new_hdr_len = IP_SIMPLE_HDR_LENGTH + sizeof (ah_t) + ah_align_sz; 37100Sstevel@tonic-gate if (V_HLEN != IP_SIMPLE_HDR_VERSION) { 37110Sstevel@tonic-gate option_length = oipha->ipha_version_and_hdr_length - 37120Sstevel@tonic-gate (uint8_t)((IP_VERSION << 4) + 37130Sstevel@tonic-gate IP_SIMPLE_HDR_LENGTH_IN_WORDS); 37140Sstevel@tonic-gate option_length <<= 2; 37150Sstevel@tonic-gate new_hdr_len += option_length; 37160Sstevel@tonic-gate } 37170Sstevel@tonic-gate 37180Sstevel@tonic-gate /* allocate pseudo-header mblk */ 37190Sstevel@tonic-gate if ((new_mp = allocb(new_hdr_len, BPRI_HI)) == NULL) { 37200Sstevel@tonic-gate /* IPsec kstats: bump bean counter here */ 37210Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_FALSE, NULL, NULL, 37220Sstevel@tonic-gate &ipdrops_ah_nomem, &ah_dropper); 37230Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 37240Sstevel@tonic-gate } 37250Sstevel@tonic-gate 37260Sstevel@tonic-gate new_mp->b_cont = mp; 37270Sstevel@tonic-gate ipsec_mp->b_cont = new_mp; 37280Sstevel@tonic-gate new_mp->b_wptr += new_hdr_len; 37290Sstevel@tonic-gate 37300Sstevel@tonic-gate /* copy original IP header to new header */ 37310Sstevel@tonic-gate bcopy(mp->b_rptr, new_mp->b_rptr, IP_SIMPLE_HDR_LENGTH + 37320Sstevel@tonic-gate option_length); 37330Sstevel@tonic-gate 37340Sstevel@tonic-gate /* update IP header */ 37350Sstevel@tonic-gate nipha = (ipha_t *)new_mp->b_rptr; 37360Sstevel@tonic-gate nipha->ipha_protocol = IPPROTO_AH; 37370Sstevel@tonic-gate iphdr_length = ntohs(nipha->ipha_length); 37380Sstevel@tonic-gate iphdr_length += sizeof (ah_t) + ah_align_sz; 37390Sstevel@tonic-gate nipha->ipha_length = htons(iphdr_length); 37400Sstevel@tonic-gate nipha->ipha_hdr_checksum = 0; 37410Sstevel@tonic-gate nipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(nipha); 37420Sstevel@tonic-gate 37430Sstevel@tonic-gate /* skip original IP header in mp */ 37440Sstevel@tonic-gate mp->b_rptr += IP_SIMPLE_HDR_LENGTH + option_length; 37450Sstevel@tonic-gate 37460Sstevel@tonic-gate /* initialize AH header */ 37470Sstevel@tonic-gate ah_hdr = (ah_t *)(new_mp->b_rptr + IP_SIMPLE_HDR_LENGTH + 37480Sstevel@tonic-gate option_length); 37490Sstevel@tonic-gate ah_hdr->ah_nexthdr = oipha->ipha_protocol; 37500Sstevel@tonic-gate if (!ah_finish_up(ah_hdr, NULL, assoc, ah_data_sz, ah_align_sz)) { 37510Sstevel@tonic-gate /* Only way this fails is if outbound replay counter wraps. */ 37520Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_FALSE, NULL, NULL, 37530Sstevel@tonic-gate &ipdrops_ah_replay, &ah_dropper); 37540Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 37550Sstevel@tonic-gate } 37560Sstevel@tonic-gate 37570Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 37580Sstevel@tonic-gate } 37590Sstevel@tonic-gate 37600Sstevel@tonic-gate /* 37610Sstevel@tonic-gate * ah_outbound_accelerated_v6: 37620Sstevel@tonic-gate * 37630Sstevel@tonic-gate * Called from ah_outbound_v6() once it is determined that the packet 37640Sstevel@tonic-gate * is eligible for hardware acceleration. 37650Sstevel@tonic-gate * 37660Sstevel@tonic-gate * We proceed as follows: 37670Sstevel@tonic-gate * 1. allocate and initialize attributes mblk 37680Sstevel@tonic-gate * 2. mark IPSEC_OUT to indicate that pkt is accelerated 37690Sstevel@tonic-gate * 3. insert AH header 37700Sstevel@tonic-gate */ 37710Sstevel@tonic-gate static ipsec_status_t 37720Sstevel@tonic-gate ah_outbound_accelerated_v6(mblk_t *ipsec_mp, ipsa_t *assoc) 37730Sstevel@tonic-gate { 37740Sstevel@tonic-gate mblk_t *mp, *phdr_mp; 37750Sstevel@tonic-gate ipsec_out_t *oi; 37760Sstevel@tonic-gate uint_t ah_data_sz; /* ICV length, algorithm dependent */ 37770Sstevel@tonic-gate uint_t ah_align_sz; /* ICV length + padding */ 37780Sstevel@tonic-gate ip6_t *oip6h; /* original IP header */ 37790Sstevel@tonic-gate ip6_t *ip6h; /* new IP header */ 37800Sstevel@tonic-gate uint_t option_length = 0; 37810Sstevel@tonic-gate uint_t hdr_size; 37820Sstevel@tonic-gate uint_t ah_offset; 37830Sstevel@tonic-gate ah_t *ah_hdr; /* ptr to AH header */ 37840Sstevel@tonic-gate 37850Sstevel@tonic-gate AH_BUMP_STAT(out_accelerated); 37860Sstevel@tonic-gate 37870Sstevel@tonic-gate oi = (ipsec_out_t *)ipsec_mp->b_rptr; 37880Sstevel@tonic-gate mp = ipsec_mp->b_cont; 37890Sstevel@tonic-gate 37900Sstevel@tonic-gate oip6h = (ip6_t *)mp->b_rptr; 37910Sstevel@tonic-gate 37920Sstevel@tonic-gate /* mark packet as being accelerated in IPSEC_OUT */ 37930Sstevel@tonic-gate ASSERT(oi->ipsec_out_accelerated == B_FALSE); 37940Sstevel@tonic-gate oi->ipsec_out_accelerated = B_TRUE; 37950Sstevel@tonic-gate 37960Sstevel@tonic-gate /* calculate authentication data length, i.e. ICV + padding */ 37970Sstevel@tonic-gate ah_data_sz = assoc->ipsa_mac_len; 37980Sstevel@tonic-gate ah_align_sz = (ah_data_sz + IPV4_PADDING_ALIGN - 1) & 37990Sstevel@tonic-gate -IPV4_PADDING_ALIGN; 38000Sstevel@tonic-gate 38010Sstevel@tonic-gate ASSERT(ah_align_sz >= ah_data_sz); 38020Sstevel@tonic-gate 38030Sstevel@tonic-gate hdr_size = ipsec_ah_get_hdr_size_v6(mp, B_FALSE); 38040Sstevel@tonic-gate option_length = hdr_size - IPV6_HDR_LEN; 38050Sstevel@tonic-gate 38060Sstevel@tonic-gate /* This was not included in ipsec_ah_get_hdr_size_v6() */ 38070Sstevel@tonic-gate hdr_size += (sizeof (ah_t) + ah_align_sz); 38080Sstevel@tonic-gate 38090Sstevel@tonic-gate if ((phdr_mp = allocb(hdr_size, BPRI_HI)) == NULL) { 38100Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_FALSE, NULL, NULL, &ipdrops_ah_nomem, 38110Sstevel@tonic-gate &ah_dropper); 38120Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 38130Sstevel@tonic-gate } 38140Sstevel@tonic-gate phdr_mp->b_wptr += hdr_size; 38150Sstevel@tonic-gate 38160Sstevel@tonic-gate /* 38170Sstevel@tonic-gate * Form the basic IP header first. We always assign every bit 38180Sstevel@tonic-gate * of the v6 basic header, so a separate bzero is unneeded. 38190Sstevel@tonic-gate */ 38200Sstevel@tonic-gate ip6h = (ip6_t *)phdr_mp->b_rptr; 38210Sstevel@tonic-gate ip6h->ip6_vcf = oip6h->ip6_vcf; 38220Sstevel@tonic-gate ip6h->ip6_hlim = oip6h->ip6_hlim; 38230Sstevel@tonic-gate ip6h->ip6_src = oip6h->ip6_src; 38240Sstevel@tonic-gate ip6h->ip6_dst = oip6h->ip6_dst; 38250Sstevel@tonic-gate /* 38260Sstevel@tonic-gate * Include the size of AH and authentication data. 38270Sstevel@tonic-gate * This is how our recipient would compute the 38280Sstevel@tonic-gate * authentication data. Look at what we do in the 38290Sstevel@tonic-gate * inbound case below. 38300Sstevel@tonic-gate */ 38310Sstevel@tonic-gate ip6h->ip6_plen = htons(ntohs(oip6h->ip6_plen) + sizeof (ah_t) + 38320Sstevel@tonic-gate ah_align_sz); 38330Sstevel@tonic-gate 38340Sstevel@tonic-gate /* 38350Sstevel@tonic-gate * Insert pseudo header: 38360Sstevel@tonic-gate * IPSEC_INFO -> [IP6, LLH, ULP] => 38370Sstevel@tonic-gate * IPSEC_INFO -> [IP, LLH, AH, ICV] -> ULP 38380Sstevel@tonic-gate */ 38390Sstevel@tonic-gate 38400Sstevel@tonic-gate if (option_length == 0) { 38410Sstevel@tonic-gate /* Form the AH header */ 38420Sstevel@tonic-gate ip6h->ip6_nxt = IPPROTO_AH; 38430Sstevel@tonic-gate ((ah_t *)(ip6h + 1))->ah_nexthdr = oip6h->ip6_nxt; 38440Sstevel@tonic-gate ah_offset = IPV6_HDR_LEN; 38450Sstevel@tonic-gate } else { 38460Sstevel@tonic-gate ip6h->ip6_nxt = oip6h->ip6_nxt; 38470Sstevel@tonic-gate /* option_length does not include the AH header's size */ 38480Sstevel@tonic-gate ah_offset = ah_fix_phdr_v6(ip6h, oip6h, B_TRUE, B_FALSE); 38490Sstevel@tonic-gate if (ah_offset == 0) { 38500Sstevel@tonic-gate freemsg(phdr_mp); 38510Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_FALSE, NULL, NULL, 38520Sstevel@tonic-gate &ipdrops_ah_bad_v6_hdrs, &ah_dropper); 38530Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 38540Sstevel@tonic-gate } 38550Sstevel@tonic-gate } 38560Sstevel@tonic-gate 38570Sstevel@tonic-gate phdr_mp->b_cont = mp; 38580Sstevel@tonic-gate ipsec_mp->b_cont = phdr_mp; 38590Sstevel@tonic-gate 38600Sstevel@tonic-gate /* skip original IP header in mp */ 38610Sstevel@tonic-gate mp->b_rptr += IPV6_HDR_LEN + option_length; 38620Sstevel@tonic-gate 38630Sstevel@tonic-gate /* initialize AH header */ 38640Sstevel@tonic-gate ah_hdr = (ah_t *)(phdr_mp->b_rptr + IPV6_HDR_LEN + option_length); 38650Sstevel@tonic-gate ah_hdr->ah_nexthdr = oip6h->ip6_nxt; 38660Sstevel@tonic-gate 38670Sstevel@tonic-gate if (!ah_finish_up(((ah_t *)((uint8_t *)ip6h + ah_offset)), NULL, 38680Sstevel@tonic-gate assoc, ah_data_sz, ah_align_sz)) { 38690Sstevel@tonic-gate /* Only way this fails is if outbound replay counter wraps. */ 38700Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_FALSE, NULL, NULL, 38710Sstevel@tonic-gate &ipdrops_ah_replay, &ah_dropper); 38720Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 38730Sstevel@tonic-gate } 38740Sstevel@tonic-gate 38750Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 38760Sstevel@tonic-gate } 38770Sstevel@tonic-gate 38780Sstevel@tonic-gate /* 38790Sstevel@tonic-gate * Invoked after processing of an inbound packet by the 38800Sstevel@tonic-gate * kernel crypto framework. Called by ah_submit_req() for a sync request, 38810Sstevel@tonic-gate * or by the kcf callback for an async request. 38820Sstevel@tonic-gate * Returns IPSEC_STATUS_SUCCESS on success, IPSEC_STATUS_FAILED on failure. 38830Sstevel@tonic-gate * On failure, the mblk chain ipsec_in is freed by this function. 38840Sstevel@tonic-gate */ 38850Sstevel@tonic-gate static ipsec_status_t 38860Sstevel@tonic-gate ah_auth_in_done(mblk_t *ipsec_in) 38870Sstevel@tonic-gate { 38880Sstevel@tonic-gate mblk_t *phdr_mp; 38890Sstevel@tonic-gate ipha_t *ipha; 38900Sstevel@tonic-gate uint_t ah_offset = 0; 38910Sstevel@tonic-gate mblk_t *mp; 38920Sstevel@tonic-gate int align_len; 38930Sstevel@tonic-gate ah_t *ah; 38940Sstevel@tonic-gate ipha_t *nipha; 38950Sstevel@tonic-gate uint32_t length; 38960Sstevel@tonic-gate ipsec_in_t *ii; 38970Sstevel@tonic-gate boolean_t isv4; 38980Sstevel@tonic-gate ip6_t *ip6h; 38990Sstevel@tonic-gate ip6_t *nip6h; 39000Sstevel@tonic-gate uint_t icv_len; 39010Sstevel@tonic-gate ipsa_t *assoc; 39020Sstevel@tonic-gate kstat_named_t *counter; 39030Sstevel@tonic-gate 39040Sstevel@tonic-gate ii = (ipsec_in_t *)ipsec_in->b_rptr; 39050Sstevel@tonic-gate isv4 = ii->ipsec_in_v4; 39060Sstevel@tonic-gate assoc = ii->ipsec_in_ah_sa; 39070Sstevel@tonic-gate icv_len = (uint_t)ii->ipsec_in_crypto_mac.cd_raw.iov_len; 39080Sstevel@tonic-gate 39090Sstevel@tonic-gate phdr_mp = ipsec_in->b_cont; 39100Sstevel@tonic-gate if (phdr_mp == NULL) { 39110Sstevel@tonic-gate ip_drop_packet(ipsec_in, B_TRUE, NULL, NULL, &ipdrops_ah_nomem, 39120Sstevel@tonic-gate &ah_dropper); 39130Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 39140Sstevel@tonic-gate } 39150Sstevel@tonic-gate 39160Sstevel@tonic-gate mp = phdr_mp->b_cont; 39170Sstevel@tonic-gate if (mp == NULL) { 39180Sstevel@tonic-gate ip_drop_packet(ipsec_in, B_TRUE, NULL, NULL, &ipdrops_ah_nomem, 39190Sstevel@tonic-gate &ah_dropper); 39200Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 39210Sstevel@tonic-gate } 39220Sstevel@tonic-gate mp->b_rptr -= ii->ipsec_in_skip_len; 39230Sstevel@tonic-gate 39240Sstevel@tonic-gate if (isv4) { 39250Sstevel@tonic-gate ipha = (ipha_t *)mp->b_rptr; 39260Sstevel@tonic-gate ah_offset = ipha->ipha_version_and_hdr_length - 39270Sstevel@tonic-gate (uint8_t)((IP_VERSION << 4)); 39280Sstevel@tonic-gate ah_offset <<= 2; 39290Sstevel@tonic-gate align_len = P2ALIGN(icv_len + IPV4_PADDING_ALIGN - 1, 39300Sstevel@tonic-gate IPV4_PADDING_ALIGN); 39310Sstevel@tonic-gate } else { 39320Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 39330Sstevel@tonic-gate ah_offset = ipsec_ah_get_hdr_size_v6(mp, B_TRUE); 39340Sstevel@tonic-gate ASSERT((mp->b_wptr - mp->b_rptr) >= ah_offset); 39350Sstevel@tonic-gate align_len = P2ALIGN(icv_len + IPV6_PADDING_ALIGN - 1, 39360Sstevel@tonic-gate IPV6_PADDING_ALIGN); 39370Sstevel@tonic-gate } 39380Sstevel@tonic-gate 39390Sstevel@tonic-gate ah = (ah_t *)(mp->b_rptr + ah_offset); 39400Sstevel@tonic-gate 39410Sstevel@tonic-gate /* 39420Sstevel@tonic-gate * We get here only when authentication passed. 39430Sstevel@tonic-gate */ 39440Sstevel@tonic-gate 39450Sstevel@tonic-gate ah3dbg(("AH succeeded, checking replay\n")); 39460Sstevel@tonic-gate AH_BUMP_STAT(good_auth); 39470Sstevel@tonic-gate 39480Sstevel@tonic-gate if (!sadb_replay_check(assoc, ah->ah_replay)) { 39490Sstevel@tonic-gate int af; 39500Sstevel@tonic-gate void *addr; 39510Sstevel@tonic-gate 39520Sstevel@tonic-gate if (isv4) { 39530Sstevel@tonic-gate addr = &ipha->ipha_dst; 39540Sstevel@tonic-gate af = AF_INET; 39550Sstevel@tonic-gate } else { 39560Sstevel@tonic-gate addr = &ip6h->ip6_dst; 39570Sstevel@tonic-gate af = AF_INET6; 39580Sstevel@tonic-gate } 39590Sstevel@tonic-gate 39600Sstevel@tonic-gate /* 39610Sstevel@tonic-gate * Log the event. As of now we print out an event. 39620Sstevel@tonic-gate * Do not print the replay failure number, or else 39630Sstevel@tonic-gate * syslog cannot collate the error messages. Printing 39640Sstevel@tonic-gate * the replay number that failed (or printing to the 39650Sstevel@tonic-gate * console) opens a denial-of-service attack. 39660Sstevel@tonic-gate */ 39670Sstevel@tonic-gate AH_BUMP_STAT(replay_failures); 39680Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 39690Sstevel@tonic-gate SL_ERROR | SL_WARN, 39700Sstevel@tonic-gate "Replay failed for AH spi %x, dst_addr %s", 39710Sstevel@tonic-gate assoc->ipsa_spi, addr, af); 39720Sstevel@tonic-gate counter = &ipdrops_ah_replay; 39730Sstevel@tonic-gate goto ah_in_discard; 39740Sstevel@tonic-gate } 39750Sstevel@tonic-gate 39760Sstevel@tonic-gate /* 39770Sstevel@tonic-gate * We need to remove the AH header from the original 39780Sstevel@tonic-gate * datagram. Easy way to do this is to use phdr_mp 39790Sstevel@tonic-gate * to hold the IP header and the orginal mp to hold 39800Sstevel@tonic-gate * the rest of it. So, we copy the IP header on to 39810Sstevel@tonic-gate * phdr_mp, and set the b_rptr in mp past AH header. 39820Sstevel@tonic-gate */ 39830Sstevel@tonic-gate if (isv4) { 39840Sstevel@tonic-gate bcopy(mp->b_rptr, phdr_mp->b_rptr, ah_offset); 39850Sstevel@tonic-gate phdr_mp->b_wptr = phdr_mp->b_rptr + ah_offset; 39860Sstevel@tonic-gate nipha = (ipha_t *)phdr_mp->b_rptr; 39870Sstevel@tonic-gate /* 39880Sstevel@tonic-gate * Assign the right protocol, adjust the length as we 39890Sstevel@tonic-gate * are removing the AH header and adjust the checksum to 39900Sstevel@tonic-gate * account for the protocol and length. 39910Sstevel@tonic-gate */ 39920Sstevel@tonic-gate nipha->ipha_protocol = ah->ah_nexthdr; 39930Sstevel@tonic-gate length = ntohs(nipha->ipha_length); 39940Sstevel@tonic-gate if (!ah_age_bytes(assoc, length, B_TRUE)) { 39950Sstevel@tonic-gate /* The ipsa has hit hard expiration, LOG and AUDIT. */ 39960Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 39970Sstevel@tonic-gate SL_ERROR | SL_WARN, 39980Sstevel@tonic-gate "AH Association 0x%x, dst %s had bytes expire.\n", 39990Sstevel@tonic-gate assoc->ipsa_spi, assoc->ipsa_dstaddr, 40000Sstevel@tonic-gate AF_INET); 40010Sstevel@tonic-gate AH_BUMP_STAT(bytes_expired); 40020Sstevel@tonic-gate counter = &ipdrops_ah_bytes_expire; 40030Sstevel@tonic-gate goto ah_in_discard; 40040Sstevel@tonic-gate } 40050Sstevel@tonic-gate length -= (sizeof (ah_t) + align_len); 40060Sstevel@tonic-gate 40070Sstevel@tonic-gate nipha->ipha_length = htons((uint16_t)length); 40080Sstevel@tonic-gate nipha->ipha_hdr_checksum = 0; 40090Sstevel@tonic-gate nipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(nipha); 40100Sstevel@tonic-gate /* 40110Sstevel@tonic-gate * Skip IP,AH and the authentication data in the 40120Sstevel@tonic-gate * original datagram. 40130Sstevel@tonic-gate */ 40140Sstevel@tonic-gate mp->b_rptr += (ah_offset + sizeof (ah_t) + align_len); 40150Sstevel@tonic-gate } else { 40160Sstevel@tonic-gate uchar_t *whereptr; 40170Sstevel@tonic-gate int hdrlen; 40180Sstevel@tonic-gate uint8_t *nexthdr; 40190Sstevel@tonic-gate ip6_hbh_t *hbhhdr; 40200Sstevel@tonic-gate ip6_dest_t *dsthdr; 40210Sstevel@tonic-gate ip6_rthdr0_t *rthdr; 40220Sstevel@tonic-gate 40230Sstevel@tonic-gate nip6h = (ip6_t *)phdr_mp->b_rptr; 40240Sstevel@tonic-gate 40250Sstevel@tonic-gate /* 40260Sstevel@tonic-gate * Make phdr_mp hold until the AH header and make 40270Sstevel@tonic-gate * mp hold everything past AH header. 40280Sstevel@tonic-gate */ 40290Sstevel@tonic-gate length = ntohs(nip6h->ip6_plen); 40300Sstevel@tonic-gate if (!ah_age_bytes(assoc, length + sizeof (ip6_t), B_TRUE)) { 40310Sstevel@tonic-gate /* The ipsa has hit hard expiration, LOG and AUDIT. */ 40320Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 40330Sstevel@tonic-gate SL_ERROR | SL_WARN, 40340Sstevel@tonic-gate "AH Association 0x%x, dst %s had bytes " 40350Sstevel@tonic-gate "expire.\n", assoc->ipsa_spi, &ip6h->ip6_dst, 40360Sstevel@tonic-gate AF_INET6); 40370Sstevel@tonic-gate AH_BUMP_STAT(bytes_expired); 40380Sstevel@tonic-gate counter = &ipdrops_ah_bytes_expire; 40390Sstevel@tonic-gate goto ah_in_discard; 40400Sstevel@tonic-gate } 40410Sstevel@tonic-gate bcopy(ip6h, nip6h, ah_offset); 40420Sstevel@tonic-gate phdr_mp->b_wptr = phdr_mp->b_rptr + ah_offset; 40430Sstevel@tonic-gate mp->b_rptr += (ah_offset + sizeof (ah_t) + align_len); 40440Sstevel@tonic-gate 40450Sstevel@tonic-gate /* 40460Sstevel@tonic-gate * Update the next header field of the header preceding 40470Sstevel@tonic-gate * AH with the next header field of AH. Start with the 40480Sstevel@tonic-gate * IPv6 header and proceed with the extension headers 40490Sstevel@tonic-gate * until we find what we're looking for. 40500Sstevel@tonic-gate */ 40510Sstevel@tonic-gate nexthdr = &nip6h->ip6_nxt; 40520Sstevel@tonic-gate whereptr = (uchar_t *)nip6h; 40530Sstevel@tonic-gate hdrlen = sizeof (ip6_t); 40540Sstevel@tonic-gate 40550Sstevel@tonic-gate while (*nexthdr != IPPROTO_AH) { 40560Sstevel@tonic-gate whereptr += hdrlen; 40570Sstevel@tonic-gate /* Assume IP has already stripped it */ 40580Sstevel@tonic-gate ASSERT(*nexthdr != IPPROTO_FRAGMENT && 40590Sstevel@tonic-gate *nexthdr != IPPROTO_RAW); 40600Sstevel@tonic-gate switch (*nexthdr) { 40610Sstevel@tonic-gate case IPPROTO_HOPOPTS: 40620Sstevel@tonic-gate hbhhdr = (ip6_hbh_t *)whereptr; 40630Sstevel@tonic-gate nexthdr = &hbhhdr->ip6h_nxt; 40640Sstevel@tonic-gate hdrlen = 8 * (hbhhdr->ip6h_len + 1); 40650Sstevel@tonic-gate break; 40660Sstevel@tonic-gate case IPPROTO_DSTOPTS: 40670Sstevel@tonic-gate dsthdr = (ip6_dest_t *)whereptr; 40680Sstevel@tonic-gate nexthdr = &dsthdr->ip6d_nxt; 40690Sstevel@tonic-gate hdrlen = 8 * (dsthdr->ip6d_len + 1); 40700Sstevel@tonic-gate break; 40710Sstevel@tonic-gate case IPPROTO_ROUTING: 40720Sstevel@tonic-gate rthdr = (ip6_rthdr0_t *)whereptr; 40730Sstevel@tonic-gate nexthdr = &rthdr->ip6r0_nxt; 40740Sstevel@tonic-gate hdrlen = 8 * (rthdr->ip6r0_len + 1); 40750Sstevel@tonic-gate break; 40760Sstevel@tonic-gate } 40770Sstevel@tonic-gate } 40780Sstevel@tonic-gate *nexthdr = ah->ah_nexthdr; 40790Sstevel@tonic-gate 40800Sstevel@tonic-gate length -= (sizeof (ah_t) + align_len); 40810Sstevel@tonic-gate nip6h->ip6_plen = htons((uint16_t)length); 40820Sstevel@tonic-gate } 40830Sstevel@tonic-gate 4084*1676Sjpk if (is_system_labeled()) { 4085*1676Sjpk /* 4086*1676Sjpk * inherit the label by setting it in the new ip header 4087*1676Sjpk */ 4088*1676Sjpk mblk_setcred(phdr_mp, DB_CRED(mp)); 4089*1676Sjpk } 40900Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 40910Sstevel@tonic-gate 40920Sstevel@tonic-gate ah_in_discard: 40930Sstevel@tonic-gate IP_AH_BUMP_STAT(in_discards); 40940Sstevel@tonic-gate ip_drop_packet(ipsec_in, B_TRUE, NULL, NULL, counter, &ah_dropper); 40950Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 40960Sstevel@tonic-gate } 40970Sstevel@tonic-gate 40980Sstevel@tonic-gate /* 40990Sstevel@tonic-gate * Invoked after processing of an outbound packet by the 41000Sstevel@tonic-gate * kernel crypto framework, either by ah_submit_req() for a request 41010Sstevel@tonic-gate * executed syncrhonously, or by the KEF callback for a request 41020Sstevel@tonic-gate * executed asynchronously. 41030Sstevel@tonic-gate */ 41040Sstevel@tonic-gate static ipsec_status_t 41050Sstevel@tonic-gate ah_auth_out_done(mblk_t *ipsec_out) 41060Sstevel@tonic-gate { 41070Sstevel@tonic-gate mblk_t *phdr_mp; 41080Sstevel@tonic-gate mblk_t *mp; 41090Sstevel@tonic-gate int align_len; 41100Sstevel@tonic-gate uint32_t hdrs_length; 41110Sstevel@tonic-gate uchar_t *ptr; 41120Sstevel@tonic-gate uint32_t length; 41130Sstevel@tonic-gate boolean_t isv4; 41140Sstevel@tonic-gate ipsec_out_t *io; 41150Sstevel@tonic-gate size_t icv_len; 41160Sstevel@tonic-gate 41170Sstevel@tonic-gate io = (ipsec_out_t *)ipsec_out->b_rptr; 41180Sstevel@tonic-gate isv4 = io->ipsec_out_v4; 41190Sstevel@tonic-gate icv_len = io->ipsec_out_crypto_mac.cd_raw.iov_len; 41200Sstevel@tonic-gate 41210Sstevel@tonic-gate phdr_mp = ipsec_out->b_cont; 41220Sstevel@tonic-gate if (phdr_mp == NULL) { 41230Sstevel@tonic-gate ip_drop_packet(ipsec_out, B_FALSE, NULL, NULL, 41240Sstevel@tonic-gate &ipdrops_ah_nomem, &ah_dropper); 41250Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 41260Sstevel@tonic-gate } 41270Sstevel@tonic-gate 41280Sstevel@tonic-gate mp = phdr_mp->b_cont; 41290Sstevel@tonic-gate if (mp == NULL) { 41300Sstevel@tonic-gate ip_drop_packet(ipsec_out, B_FALSE, NULL, NULL, 41310Sstevel@tonic-gate &ipdrops_ah_nomem, &ah_dropper); 41320Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 41330Sstevel@tonic-gate } 41340Sstevel@tonic-gate mp->b_rptr -= io->ipsec_out_skip_len; 41350Sstevel@tonic-gate 41360Sstevel@tonic-gate if (isv4) { 41370Sstevel@tonic-gate ipha_t *ipha; 41380Sstevel@tonic-gate ipha_t *nipha; 41390Sstevel@tonic-gate 41400Sstevel@tonic-gate ipha = (ipha_t *)mp->b_rptr; 41410Sstevel@tonic-gate hdrs_length = ipha->ipha_version_and_hdr_length - 41420Sstevel@tonic-gate (uint8_t)((IP_VERSION << 4)); 41430Sstevel@tonic-gate hdrs_length <<= 2; 41440Sstevel@tonic-gate align_len = P2ALIGN(icv_len + IPV4_PADDING_ALIGN - 1, 41450Sstevel@tonic-gate IPV4_PADDING_ALIGN); 41460Sstevel@tonic-gate /* 41470Sstevel@tonic-gate * phdr_mp must have the right amount of space for the 41480Sstevel@tonic-gate * combined IP and AH header. Copy the IP header and 41490Sstevel@tonic-gate * the ack_data onto AH. Note that the AH header was 41500Sstevel@tonic-gate * already formed before the ICV calculation and hence 41510Sstevel@tonic-gate * you don't have to copy it here. 41520Sstevel@tonic-gate */ 41530Sstevel@tonic-gate bcopy(mp->b_rptr, phdr_mp->b_rptr, hdrs_length); 41540Sstevel@tonic-gate 41550Sstevel@tonic-gate ptr = phdr_mp->b_rptr + hdrs_length + sizeof (ah_t); 41560Sstevel@tonic-gate bcopy(phdr_mp->b_wptr, ptr, icv_len); 41570Sstevel@tonic-gate 41580Sstevel@tonic-gate /* 41590Sstevel@tonic-gate * Compute the new header checksum as we are assigning 41600Sstevel@tonic-gate * IPPROTO_AH and adjusting the length here. 41610Sstevel@tonic-gate */ 41620Sstevel@tonic-gate nipha = (ipha_t *)phdr_mp->b_rptr; 41630Sstevel@tonic-gate 41640Sstevel@tonic-gate nipha->ipha_protocol = IPPROTO_AH; 41650Sstevel@tonic-gate length = ntohs(nipha->ipha_length); 41660Sstevel@tonic-gate length += (sizeof (ah_t) + align_len); 41670Sstevel@tonic-gate nipha->ipha_length = htons((uint16_t)length); 41680Sstevel@tonic-gate nipha->ipha_hdr_checksum = 0; 41690Sstevel@tonic-gate nipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(nipha); 41700Sstevel@tonic-gate } else { 41710Sstevel@tonic-gate ip6_t *ip6h; 41720Sstevel@tonic-gate ip6_t *nip6h; 41730Sstevel@tonic-gate uint_t ah_offset; 41740Sstevel@tonic-gate 41750Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 41760Sstevel@tonic-gate nip6h = (ip6_t *)phdr_mp->b_rptr; 41770Sstevel@tonic-gate align_len = P2ALIGN(icv_len + IPV6_PADDING_ALIGN - 1, 41780Sstevel@tonic-gate IPV6_PADDING_ALIGN); 41790Sstevel@tonic-gate /* 41800Sstevel@tonic-gate * phdr_mp must have the right amount of space for the 41810Sstevel@tonic-gate * combined IP and AH header. Copy the IP header with 41820Sstevel@tonic-gate * options into the pseudo header. When we constructed 41830Sstevel@tonic-gate * a pseudo header, we did not copy some of the mutable 41840Sstevel@tonic-gate * fields. We do it now by calling ah_fix_phdr_v6() 41850Sstevel@tonic-gate * with the last argument B_TRUE. It returns the 41860Sstevel@tonic-gate * ah_offset into the pseudo header. 41870Sstevel@tonic-gate */ 41880Sstevel@tonic-gate 41890Sstevel@tonic-gate bcopy(ip6h, nip6h, IPV6_HDR_LEN); 41900Sstevel@tonic-gate ah_offset = ah_fix_phdr_v6(nip6h, ip6h, B_TRUE, B_TRUE); 41910Sstevel@tonic-gate ASSERT(ah_offset != 0); 41920Sstevel@tonic-gate /* 41930Sstevel@tonic-gate * phdr_mp can hold exactly the whole IP header with options 41940Sstevel@tonic-gate * plus the AH header also. Thus subtracting the AH header's 41950Sstevel@tonic-gate * size should give exactly how much of the original header 41960Sstevel@tonic-gate * should be skipped. 41970Sstevel@tonic-gate */ 41980Sstevel@tonic-gate hdrs_length = (phdr_mp->b_wptr - phdr_mp->b_rptr) - 41990Sstevel@tonic-gate sizeof (ah_t) - icv_len; 42000Sstevel@tonic-gate bcopy(phdr_mp->b_wptr, ((uint8_t *)nip6h + ah_offset + 42010Sstevel@tonic-gate sizeof (ah_t)), icv_len); 42020Sstevel@tonic-gate length = ntohs(nip6h->ip6_plen); 42030Sstevel@tonic-gate length += (sizeof (ah_t) + align_len); 42040Sstevel@tonic-gate nip6h->ip6_plen = htons((uint16_t)length); 42050Sstevel@tonic-gate } 42060Sstevel@tonic-gate 4207*1676Sjpk if (is_system_labeled()) { 4208*1676Sjpk /* 4209*1676Sjpk * inherit the label by setting it in the new ip header 4210*1676Sjpk */ 4211*1676Sjpk mblk_setcred(phdr_mp, DB_CRED(mp)); 4212*1676Sjpk } 4213*1676Sjpk 42140Sstevel@tonic-gate /* Skip the original IP header */ 42150Sstevel@tonic-gate mp->b_rptr += hdrs_length; 42160Sstevel@tonic-gate if (mp->b_rptr == mp->b_wptr) { 42170Sstevel@tonic-gate phdr_mp->b_cont = mp->b_cont; 42180Sstevel@tonic-gate freeb(mp); 42190Sstevel@tonic-gate } 42200Sstevel@tonic-gate 42210Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 42220Sstevel@tonic-gate } 42230Sstevel@tonic-gate 42240Sstevel@tonic-gate /* 42250Sstevel@tonic-gate * Wrapper to allow IP to trigger an AH association failure message 42260Sstevel@tonic-gate * during SA inbound selection. 42270Sstevel@tonic-gate */ 42280Sstevel@tonic-gate void 42290Sstevel@tonic-gate ipsecah_in_assocfailure(mblk_t *mp, char level, ushort_t sl, char *fmt, 42300Sstevel@tonic-gate uint32_t spi, void *addr, int af) 42310Sstevel@tonic-gate { 42320Sstevel@tonic-gate if (ipsecah_log_unknown_spi) { 42330Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, level, sl, fmt, spi, 42340Sstevel@tonic-gate addr, af); 42350Sstevel@tonic-gate } 42360Sstevel@tonic-gate 42370Sstevel@tonic-gate ip_drop_packet(mp, B_TRUE, NULL, NULL, &ipdrops_ah_no_sa, 42380Sstevel@tonic-gate &ah_dropper); 42390Sstevel@tonic-gate } 42400Sstevel@tonic-gate 42410Sstevel@tonic-gate /* 42420Sstevel@tonic-gate * Initialize the AH input and output processing functions. 42430Sstevel@tonic-gate */ 42440Sstevel@tonic-gate void 42450Sstevel@tonic-gate ipsecah_init_funcs(ipsa_t *sa) 42460Sstevel@tonic-gate { 42470Sstevel@tonic-gate if (sa->ipsa_output_func == NULL) 42480Sstevel@tonic-gate sa->ipsa_output_func = ah_outbound; 42490Sstevel@tonic-gate if (sa->ipsa_input_func == NULL) 42500Sstevel@tonic-gate sa->ipsa_input_func = ah_inbound; 42510Sstevel@tonic-gate } 4252