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