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