Home | History | Annotate | Line # | Download | only in netinet
      1 /*	$NetBSD: ip_ipsec_pxy.c,v 1.3 2012/07/22 14:27:51 darrenr Exp $	*/
      2 
      3 /*
      4  * Copyright (C) 2012 by Darren Reed.
      5  *
      6  * See the IPFILTER.LICENCE file for details on licencing.
      7  *
      8  * Simple ISAKMP transparent proxy for in-kernel use.  For use with the NAT
      9  * code.
     10  *
     11  * Id: ip_ipsec_pxy.c,v 1.1.1.2 2012/07/22 13:45:19 darrenr Exp
     12  *
     13  */
     14 
     15 #include <sys/cdefs.h>
     16 __KERNEL_RCSID(1, "$NetBSD: ip_ipsec_pxy.c,v 1.3 2012/07/22 14:27:51 darrenr Exp $");
     17 
     18 #define	IPF_IPSEC_PROXY
     19 
     20 
     21 /*
     22  * IPSec proxy
     23  */
     24 typedef struct ipf_ipsec_softc_s {
     25 	frentry_t	ipsec_fr;
     26 	int		ipsec_proxy_init;
     27 	int		ipsec_proxy_ttl;
     28 	ipftq_t		*ipsec_nat_tqe;
     29 	ipftq_t		*ipsec_state_tqe;
     30 	char		ipsec_buffer[1500];
     31 } ipf_ipsec_softc_t;
     32 
     33 
     34 void *ipf_p_ipsec_soft_create(ipf_main_softc_t *);
     35 void ipf_p_ipsec_soft_destroy(ipf_main_softc_t *, void *);
     36 int ipf_p_ipsec_soft_init(ipf_main_softc_t *, void *);
     37 void ipf_p_ipsec_soft_fini(ipf_main_softc_t *, void *);
     38 int ipf_p_ipsec_init(void);
     39 void ipf_p_ipsec_fini(void);
     40 int ipf_p_ipsec_new(void *, fr_info_t *, ap_session_t *, nat_t *);
     41 void ipf_p_ipsec_del(ipf_main_softc_t *, ap_session_t *);
     42 int ipf_p_ipsec_inout(void *, fr_info_t *, ap_session_t *, nat_t *);
     43 int ipf_p_ipsec_match(fr_info_t *, ap_session_t *, nat_t *);
     44 
     45 
     46 /*
     47  * IPSec application proxy initialization.
     48  */
     49 void *
     50 ipf_p_ipsec_soft_create(ipf_main_softc_t *softc)
     51 {
     52 	ipf_ipsec_softc_t *softi;
     53 
     54 	KMALLOC(softi, ipf_ipsec_softc_t *);
     55 	if (softi == NULL)
     56 		return NULL;
     57 
     58 	bzero((char *)softi, sizeof(*softi));
     59 	softi->ipsec_fr.fr_ref = 1;
     60 	softi->ipsec_fr.fr_flags = FR_OUTQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
     61 	MUTEX_INIT(&softi->ipsec_fr.fr_lock, "IPsec proxy rule lock");
     62 	softi->ipsec_proxy_init = 1;
     63 	softi->ipsec_proxy_ttl = 60;
     64 
     65 	return softi;
     66 }
     67 
     68 
     69 int
     70 ipf_p_ipsec_soft_init(ipf_main_softc_t *softc, void *arg)
     71 {
     72 	ipf_ipsec_softc_t *softi = arg;
     73 
     74 	softi->ipsec_nat_tqe = ipf_state_add_tq(softc, softi->ipsec_proxy_ttl);
     75 	if (softi->ipsec_nat_tqe == NULL)
     76 		return -1;
     77 	softi->ipsec_state_tqe = ipf_nat_add_tq(softc, softi->ipsec_proxy_ttl);
     78 	if (softi->ipsec_state_tqe == NULL) {
     79 		if (ipf_deletetimeoutqueue(softi->ipsec_nat_tqe) == 0)
     80 			ipf_freetimeoutqueue(softc, softi->ipsec_nat_tqe);
     81 		softi->ipsec_nat_tqe = NULL;
     82 		return -1;
     83 	}
     84 
     85 	softi->ipsec_nat_tqe->ifq_flags |= IFQF_PROXY;
     86 	softi->ipsec_state_tqe->ifq_flags |= IFQF_PROXY;
     87 	softi->ipsec_fr.fr_age[0] = softi->ipsec_proxy_ttl;
     88 	softi->ipsec_fr.fr_age[1] = softi->ipsec_proxy_ttl;
     89 	return 0;
     90 }
     91 
     92 
     93 void
     94 ipf_p_ipsec_soft_fini(ipf_main_softc_t *softc, void *arg)
     95 {
     96 	ipf_ipsec_softc_t *softi = arg;
     97 
     98 	if (arg == NULL)
     99 		return;
    100 
    101 	if (softi->ipsec_nat_tqe != NULL) {
    102 		if (ipf_deletetimeoutqueue(softi->ipsec_nat_tqe) == 0)
    103 			ipf_freetimeoutqueue(softc, softi->ipsec_nat_tqe);
    104 	}
    105 	softi->ipsec_nat_tqe = NULL;
    106 	if (softi->ipsec_state_tqe != NULL) {
    107 		if (ipf_deletetimeoutqueue(softi->ipsec_state_tqe) == 0)
    108 			ipf_freetimeoutqueue(softc, softi->ipsec_state_tqe);
    109 	}
    110 	softi->ipsec_state_tqe = NULL;
    111 }
    112 
    113 
    114 void
    115 ipf_p_ipsec_soft_destroy(ipf_main_softc_t *softc, void *arg)
    116 {
    117 	ipf_ipsec_softc_t *softi = arg;
    118 
    119 	if (softi->ipsec_proxy_init == 1) {
    120 		MUTEX_DESTROY(&softi->ipsec_fr.fr_lock);
    121 		softi->ipsec_proxy_init = 0;
    122 	}
    123 
    124 	KFREE(softi);
    125 }
    126 
    127 
    128 /*
    129  * Setup for a new IPSEC proxy.
    130  */
    131 int
    132 ipf_p_ipsec_new(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat)
    133 {
    134 	ipf_ipsec_softc_t *softi = arg;
    135 	ipf_main_softc_t *softc = fin->fin_main_soft;
    136 #ifdef USE_MUTEXES
    137 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
    138 #endif
    139 	int p, off, dlen, ttl;
    140 	ipsec_pxy_t *ipsec;
    141 	ipnat_t *ipn, *np;
    142 	fr_info_t fi;
    143 	char *ptr;
    144 	int size;
    145 	ip_t *ip;
    146 	mb_t *m;
    147 
    148 	if (fin->fin_v != 4)
    149 		return -1;
    150 
    151 	off = fin->fin_plen - fin->fin_dlen + fin->fin_ipoff;
    152 	bzero(softi->ipsec_buffer, sizeof(softi->ipsec_buffer));
    153 	ip = fin->fin_ip;
    154 	m = fin->fin_m;
    155 
    156 	dlen = M_LEN(m) - off;
    157 	if (dlen < 16)
    158 		return -1;
    159 	COPYDATA(m, off, MIN(sizeof(softi->ipsec_buffer), dlen),
    160 		 softi->ipsec_buffer);
    161 
    162 	if (ipf_nat_outlookup(fin, 0, IPPROTO_ESP, nat->nat_nsrcip,
    163 			  ip->ip_dst) != NULL)
    164 		return -1;
    165 
    166 	np = nat->nat_ptr;
    167 	size = np->in_size;
    168 	KMALLOC(ipsec, ipsec_pxy_t *);
    169 	if (ipsec == NULL)
    170 		return -1;
    171 
    172 	KMALLOCS(ipn, ipnat_t *, size);
    173 	if (ipn == NULL) {
    174 		KFREE(ipsec);
    175 		return -1;
    176 	}
    177 
    178 	aps->aps_data = ipsec;
    179 	aps->aps_psiz = sizeof(*ipsec);
    180 	bzero((char *)ipsec, sizeof(*ipsec));
    181 	bzero((char *)ipn, size);
    182 	ipsec->ipsc_rule = ipn;
    183 
    184 	/*
    185 	 * Create NAT rule against which the tunnel/transport mapping is
    186 	 * created.  This is required because the current NAT rule does not
    187 	 * describe ESP but UDP instead.
    188 	 */
    189 	ipn->in_size = size;
    190 	ttl = IPF_TTLVAL(softi->ipsec_nat_tqe->ifq_ttl);
    191 	ipn->in_tqehead[0] = ipf_nat_add_tq(softc, ttl);
    192 	ipn->in_tqehead[1] = ipf_nat_add_tq(softc, ttl);
    193 	ipn->in_ifps[0] = fin->fin_ifp;
    194 	ipn->in_apr = NULL;
    195 	ipn->in_use = 1;
    196 	ipn->in_hits = 1;
    197 	ipn->in_snip = ntohl(nat->nat_nsrcaddr);
    198 	ipn->in_ippip = 1;
    199 	ipn->in_osrcip = nat->nat_osrcip;
    200 	ipn->in_osrcmsk = 0xffffffff;
    201 	ipn->in_nsrcip = nat->nat_nsrcip;
    202 	ipn->in_nsrcmsk = 0xffffffff;
    203 	ipn->in_odstip = nat->nat_odstip;
    204 	ipn->in_odstmsk = 0xffffffff;
    205 	ipn->in_ndstip = nat->nat_ndstip;
    206 	ipn->in_ndstmsk = 0xffffffff;
    207 	ipn->in_redir = NAT_MAP;
    208 	ipn->in_pr[0] = IPPROTO_ESP;
    209 	ipn->in_pr[1] = IPPROTO_ESP;
    210 	ipn->in_flags = (np->in_flags | IPN_PROXYRULE);
    211 	MUTEX_INIT(&ipn->in_lock, "IPSec proxy NAT rule");
    212 
    213 	ipn->in_namelen = np->in_namelen;
    214 	bcopy(np->in_names, ipn->in_ifnames, ipn->in_namelen);
    215 	ipn->in_ifnames[0] = np->in_ifnames[0];
    216 	ipn->in_ifnames[1] = np->in_ifnames[1];
    217 
    218 	bcopy((char *)fin, (char *)&fi, sizeof(fi));
    219 	fi.fin_fi.fi_p = IPPROTO_ESP;
    220 	fi.fin_fr = &softi->ipsec_fr;
    221 	fi.fin_data[0] = 0;
    222 	fi.fin_data[1] = 0;
    223 	p = ip->ip_p;
    224 	ip->ip_p = IPPROTO_ESP;
    225 	fi.fin_flx &= ~(FI_TCPUDP|FI_STATE|FI_FRAG);
    226 	fi.fin_flx |= FI_IGNORE;
    227 
    228 	ptr = softi->ipsec_buffer;
    229 	bcopy(ptr, (char *)ipsec->ipsc_icookie, sizeof(ipsec_cookie_t));
    230 	ptr += sizeof(ipsec_cookie_t);
    231 	bcopy(ptr, (char *)ipsec->ipsc_rcookie, sizeof(ipsec_cookie_t));
    232 	/*
    233 	 * The responder cookie should only be non-zero if the initiator
    234 	 * cookie is non-zero.  Therefore, it is safe to assume(!) that the
    235 	 * cookies are both set after copying if the responder is non-zero.
    236 	 */
    237 	if ((ipsec->ipsc_rcookie[0]|ipsec->ipsc_rcookie[1]) != 0)
    238 		ipsec->ipsc_rckset = 1;
    239 
    240 	MUTEX_ENTER(&softn->ipf_nat_new);
    241 	ipsec->ipsc_nat = ipf_nat_add(&fi, ipn, &ipsec->ipsc_nat,
    242 				      NAT_SLAVE|SI_WILDP, NAT_OUTBOUND);
    243 	MUTEX_EXIT(&softn->ipf_nat_new);
    244 	if (ipsec->ipsc_nat != NULL) {
    245 		(void) ipf_nat_proto(&fi, ipsec->ipsc_nat, 0);
    246 		MUTEX_ENTER(&ipsec->ipsc_nat->nat_lock);
    247 		ipf_nat_update(&fi, ipsec->ipsc_nat);
    248 		MUTEX_EXIT(&ipsec->ipsc_nat->nat_lock);
    249 
    250 		fi.fin_data[0] = 0;
    251 		fi.fin_data[1] = 0;
    252 		(void) ipf_state_add(softc, &fi, &ipsec->ipsc_state, SI_WILDP);
    253 	}
    254 	ip->ip_p = p & 0xff;
    255 	return 0;
    256 }
    257 
    258 
    259 /*
    260  * For outgoing IKE packets.  refresh timeouts for NAT & state entries, if
    261  * we can.  If they have disappeared, recreate them.
    262  */
    263 int
    264 ipf_p_ipsec_inout(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat)
    265 {
    266 	ipf_ipsec_softc_t *softi = arg;
    267 	ipf_main_softc_t *softc = fin->fin_main_soft;
    268 	ipsec_pxy_t *ipsec;
    269 	fr_info_t fi;
    270 	ip_t *ip;
    271 	int p;
    272 
    273 	if ((fin->fin_out == 1) && (nat->nat_dir == NAT_INBOUND))
    274 		return 0;
    275 
    276 	if ((fin->fin_out == 0) && (nat->nat_dir == NAT_OUTBOUND))
    277 		return 0;
    278 
    279 	ipsec = aps->aps_data;
    280 
    281 	if (ipsec != NULL) {
    282 		ip = fin->fin_ip;
    283 		p = ip->ip_p;
    284 
    285 		if ((ipsec->ipsc_nat == NULL) || (ipsec->ipsc_state == NULL)) {
    286 			bcopy((char *)fin, (char *)&fi, sizeof(fi));
    287 			fi.fin_fi.fi_p = IPPROTO_ESP;
    288 			fi.fin_fr = &softi->ipsec_fr;
    289 			fi.fin_data[0] = 0;
    290 			fi.fin_data[1] = 0;
    291 			ip->ip_p = IPPROTO_ESP;
    292 			fi.fin_flx &= ~(FI_TCPUDP|FI_STATE|FI_FRAG);
    293 			fi.fin_flx |= FI_IGNORE;
    294 		}
    295 
    296 		/*
    297 		 * Update NAT timeout/create NAT if missing.
    298 		 */
    299 		if (ipsec->ipsc_nat != NULL)
    300 			ipf_queueback(softc->ipf_ticks,
    301 				      &ipsec->ipsc_nat->nat_tqe);
    302 		else {
    303 #ifdef USE_MUTEXES
    304 			ipf_nat_softc_t *softn = softc->ipf_nat_soft;
    305 #endif
    306 
    307 			MUTEX_ENTER(&softn->ipf_nat_new);
    308 			ipsec->ipsc_nat = ipf_nat_add(&fi, ipsec->ipsc_rule,
    309 						      &ipsec->ipsc_nat,
    310 						      NAT_SLAVE|SI_WILDP,
    311 						      nat->nat_dir);
    312 			MUTEX_EXIT(&softn->ipf_nat_new);
    313 			if (ipsec->ipsc_nat != NULL) {
    314 				(void) ipf_nat_proto(&fi, ipsec->ipsc_nat, 0);
    315 				MUTEX_ENTER(&ipsec->ipsc_nat->nat_lock);
    316 				ipf_nat_update(&fi, ipsec->ipsc_nat);
    317 				MUTEX_EXIT(&ipsec->ipsc_nat->nat_lock);
    318 			}
    319 		}
    320 
    321 		/*
    322 		 * Update state timeout/create state if missing.
    323 		 */
    324 		READ_ENTER(&softc->ipf_state);
    325 		if (ipsec->ipsc_state != NULL) {
    326 			ipf_queueback(softc->ipf_ticks,
    327 				      &ipsec->ipsc_state->is_sti);
    328 			ipsec->ipsc_state->is_die = nat->nat_age;
    329 			RWLOCK_EXIT(&softc->ipf_state);
    330 		} else {
    331 			RWLOCK_EXIT(&softc->ipf_state);
    332 			fi.fin_data[0] = 0;
    333 			fi.fin_data[1] = 0;
    334 			(void) ipf_state_add(softc, &fi, &ipsec->ipsc_state,
    335 					     SI_WILDP);
    336 		}
    337 		ip->ip_p = p;
    338 	}
    339 	return 0;
    340 }
    341 
    342 
    343 /*
    344  * This extends the NAT matching to be based on the cookies associated with
    345  * a session and found at the front of IKE packets.  The cookies are always
    346  * in the same order (not reversed depending on packet flow direction as with
    347  * UDP/TCP port numbers).
    348  */
    349 int
    350 ipf_p_ipsec_match(fr_info_t *fin, ap_session_t *aps, nat_t *nat)
    351 {
    352 	ipsec_pxy_t *ipsec;
    353 	u_32_t cookies[4];
    354 	mb_t *m;
    355 	int off;
    356 
    357 	nat = nat;	/* LINT */
    358 
    359 	if ((fin->fin_dlen < sizeof(cookies)) || (fin->fin_flx & FI_FRAG))
    360 		return -1;
    361 
    362 	off = fin->fin_plen - fin->fin_dlen + fin->fin_ipoff;
    363 	ipsec = aps->aps_data;
    364 	m = fin->fin_m;
    365 	COPYDATA(m, off, sizeof(cookies), (char *)cookies);
    366 
    367 	if ((cookies[0] != ipsec->ipsc_icookie[0]) ||
    368 	    (cookies[1] != ipsec->ipsc_icookie[1]))
    369 		return -1;
    370 
    371 	if (ipsec->ipsc_rckset == 0) {
    372 		if ((cookies[2]|cookies[3]) == 0) {
    373 			return 0;
    374 		}
    375 		ipsec->ipsc_rckset = 1;
    376 		ipsec->ipsc_rcookie[0] = cookies[2];
    377 		ipsec->ipsc_rcookie[1] = cookies[3];
    378 		return 0;
    379 	}
    380 
    381 	if ((cookies[2] != ipsec->ipsc_rcookie[0]) ||
    382 	    (cookies[3] != ipsec->ipsc_rcookie[1]))
    383 		return -1;
    384 	return 0;
    385 }
    386 
    387 
    388 /*
    389  * clean up after ourselves.
    390  */
    391 void
    392 ipf_p_ipsec_del(ipf_main_softc_t *softc, ap_session_t *aps)
    393 {
    394 	ipsec_pxy_t *ipsec;
    395 
    396 	ipsec = aps->aps_data;
    397 
    398 	if (ipsec != NULL) {
    399 		/*
    400 		 * Don't bother changing any of the NAT structure details,
    401 		 * *_del() is on a callback from aps_free(), from nat_delete()
    402 		 */
    403 
    404 		READ_ENTER(&softc->ipf_state);
    405 		if (ipsec->ipsc_state != NULL) {
    406 			ipsec->ipsc_state->is_die = softc->ipf_ticks + 1;
    407 			ipsec->ipsc_state->is_me = NULL;
    408 			ipf_queuefront(&ipsec->ipsc_state->is_sti);
    409 		}
    410 		RWLOCK_EXIT(&softc->ipf_state);
    411 
    412 		ipsec->ipsc_state = NULL;
    413 		ipsec->ipsc_nat = NULL;
    414 		ipsec->ipsc_rule->in_flags |= IPN_DELETE;
    415 		ipf_nat_rule_deref(softc, &ipsec->ipsc_rule);
    416 	}
    417 }
    418