xref: /illumos-gate/usr/src/uts/common/inet/ipf/netinet/ip_ipsec_pxy.c (revision 60a3f738d56f92ae8b80e4b62a2331c6e1f2311f)
1 /*
2  * Copyright (C) 2001-2003 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: ip_ipsec_pxy.c,v 2.20.2.7 2005/07/15 21:56:50 darrenr Exp $
10  *
11  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
12  * Use is subject to license terms.
13  */
14 
15 #pragma ident	"%Z%%M%	%I%	%E% SMI"
16 
17 #define	IPF_IPSEC_PROXY
18 
19 
20 int ippr_ipsec_init __P((void));
21 void ippr_ipsec_fini __P((void));
22 int ippr_ipsec_new __P((fr_info_t *, ap_session_t *, nat_t *));
23 void ippr_ipsec_del __P((ap_session_t *));
24 int ippr_ipsec_inout __P((fr_info_t *, ap_session_t *, nat_t *));
25 int ippr_ipsec_match __P((fr_info_t *, ap_session_t *, nat_t *));
26 
27 static	frentry_t	ipsecfr;
28 static	ipftq_t		*ipsecnattqe;
29 static	ipftq_t		*ipsecstatetqe;
30 static	char	ipsec_buffer[1500];
31 
32 int	ipsec_proxy_init = 0;
33 int	ipsec_proxy_ttl = 60;
34 
35 /*
36  * IPSec application proxy initialization.
37  */
38 int ippr_ipsec_init()
39 {
40 	bzero((char *)&ipsecfr, sizeof(ipsecfr));
41 	ipsecfr.fr_ref = 1;
42 	ipsecfr.fr_flags = FR_OUTQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
43 	MUTEX_INIT(&ipsecfr.fr_lock, "IPsec proxy rule lock");
44 	ipsec_proxy_init = 1;
45 
46 	ipsecnattqe = fr_addtimeoutqueue(&nat_utqe, ipsec_proxy_ttl);
47 	if (ipsecnattqe == NULL)
48 		return -1;
49 	ipsecstatetqe = fr_addtimeoutqueue(&ips_utqe, ipsec_proxy_ttl);
50 	if (ipsecstatetqe == NULL) {
51 		if (fr_deletetimeoutqueue(ipsecnattqe) == 0)
52 			fr_freetimeoutqueue(ipsecnattqe);
53 		ipsecnattqe = NULL;
54 		return -1;
55 	}
56 
57 	ipsecnattqe->ifq_flags |= IFQF_PROXY;
58 	ipsecstatetqe->ifq_flags |= IFQF_PROXY;
59 
60 	ipsecfr.fr_age[0] = ipsec_proxy_ttl;
61 	ipsecfr.fr_age[1] = ipsec_proxy_ttl;
62 	return 0;
63 }
64 
65 
66 void ippr_ipsec_fini()
67 {
68 	if (ipsecnattqe != NULL) {
69 		if (fr_deletetimeoutqueue(ipsecnattqe) == 0)
70 			fr_freetimeoutqueue(ipsecnattqe);
71 	}
72 	ipsecnattqe = NULL;
73 	if (ipsecstatetqe != NULL) {
74 		if (fr_deletetimeoutqueue(ipsecstatetqe) == 0)
75 			fr_freetimeoutqueue(ipsecstatetqe);
76 	}
77 	ipsecstatetqe = NULL;
78 
79 	if (ipsec_proxy_init == 1) {
80 		MUTEX_DESTROY(&ipsecfr.fr_lock);
81 		ipsec_proxy_init = 0;
82 	}
83 }
84 
85 
86 /*
87  * Setup for a new IPSEC proxy.
88  */
89 int ippr_ipsec_new(fin, aps, nat)
90 fr_info_t *fin;
91 ap_session_t *aps;
92 nat_t *nat;
93 {
94 	ipsec_pxy_t *ipsec;
95 	fr_info_t fi;
96 	ipnat_t *ipn;
97 	char *ptr;
98 	int p, off, dlen, ttl;
99 	mb_t *m;
100 	ip_t *ip;
101 
102 	off = fin->fin_plen - fin->fin_dlen + fin->fin_ipoff;
103 	bzero(ipsec_buffer, sizeof(ipsec_buffer));
104 	ip = fin->fin_ip;
105 	m = fin->fin_m;
106 
107 	dlen = M_LEN(m) - off;
108 	if (dlen < 16)
109 		return -1;
110 	COPYDATA(m, off, MIN(sizeof(ipsec_buffer), dlen), ipsec_buffer);
111 
112 	if (nat_outlookup(fin, 0, IPPROTO_ESP, nat->nat_inip,
113 			  ip->ip_dst) != NULL)
114 		return -1;
115 
116 	aps->aps_psiz = sizeof(*ipsec);
117 	KMALLOCS(aps->aps_data, ipsec_pxy_t *, sizeof(*ipsec));
118 	if (aps->aps_data == NULL)
119 		return -1;
120 
121 	ipsec = aps->aps_data;
122 	bzero((char *)ipsec, sizeof(*ipsec));
123 
124 	/*
125 	 * Create NAT rule against which the tunnel/transport mapping is
126 	 * created.  This is required because the current NAT rule does not
127 	 * describe ESP but UDP instead.
128 	 */
129 	ipn = &ipsec->ipsc_rule;
130 	ttl = IPF_TTLVAL(ipsecnattqe->ifq_ttl);
131 	ipn->in_tqehead[0] = fr_addtimeoutqueue(&nat_utqe, ttl);
132 	ipn->in_tqehead[1] = fr_addtimeoutqueue(&nat_utqe, ttl);
133 	ipn->in_ifps[0] = fin->fin_ifp;
134 	ipn->in_apr = NULL;
135 	ipn->in_use = 1;
136 	ipn->in_hits = 1;
137 	ipn->in_nip = ntohl(nat->nat_outip.s_addr);
138 	ipn->in_ippip = 1;
139 	ipn->in_inip = nat->nat_inip.s_addr;
140 	ipn->in_inmsk = 0xffffffff;
141 	ipn->in_outip = fin->fin_saddr;
142 	ipn->in_outmsk = nat->nat_outip.s_addr;
143 	ipn->in_srcip = fin->fin_saddr;
144 	ipn->in_srcmsk = 0xffffffff;
145 	ipn->in_redir = NAT_MAP;
146 	bcopy(nat->nat_ptr->in_ifnames[0], ipn->in_ifnames[0],
147 	      sizeof(ipn->in_ifnames[0]));
148 	ipn->in_p = IPPROTO_ESP;
149 
150 	bcopy((char *)fin, (char *)&fi, sizeof(fi));
151 	fi.fin_state = NULL;
152 	fi.fin_nat = NULL;
153 	fi.fin_fi.fi_p = IPPROTO_ESP;
154 	fi.fin_fr = &ipsecfr;
155 	fi.fin_data[0] = 0;
156 	fi.fin_data[1] = 0;
157 	p = ip->ip_p;
158 	ip->ip_p = IPPROTO_ESP;
159 	fi.fin_flx &= ~(FI_TCPUDP|FI_STATE|FI_FRAG);
160 	fi.fin_flx |= FI_IGNORE;
161 
162 	ptr = ipsec_buffer;
163 	bcopy(ptr, (char *)ipsec->ipsc_icookie, sizeof(ipsec_cookie_t));
164 	ptr += sizeof(ipsec_cookie_t);
165 	bcopy(ptr, (char *)ipsec->ipsc_rcookie, sizeof(ipsec_cookie_t));
166 	/*
167 	 * The responder cookie should only be non-zero if the initiator
168 	 * cookie is non-zero.  Therefore, it is safe to assume(!) that the
169 	 * cookies are both set after copying if the responder is non-zero.
170 	 */
171 	if ((ipsec->ipsc_rcookie[0]|ipsec->ipsc_rcookie[1]) != 0)
172 		ipsec->ipsc_rckset = 1;
173 
174 	ipsec->ipsc_nat = nat_new(&fi, ipn, &ipsec->ipsc_nat,
175 				  NAT_SLAVE|SI_WILDP, NAT_OUTBOUND);
176 	if (ipsec->ipsc_nat != NULL) {
177 		(void) nat_proto(&fi, ipsec->ipsc_nat, 0);
178 		nat_update(&fi, ipsec->ipsc_nat, ipn);
179 
180 		fi.fin_data[0] = 0;
181 		fi.fin_data[1] = 0;
182 		ipsec->ipsc_state = fr_addstate(&fi, &ipsec->ipsc_state,
183 						SI_WILDP);
184 		if (fi.fin_state != NULL)
185 			fr_statederef(&fi, (ipstate_t **)&fi.fin_state);
186 	}
187 	ip->ip_p = p & 0xff;
188 	return 0;
189 }
190 
191 
192 /*
193  * For outgoing IKE packets.  refresh timeouts for NAT & state entries, if
194  * we can.  If they have disappeared, recreate them.
195  */
196 int ippr_ipsec_inout(fin, aps, nat)
197 fr_info_t *fin;
198 ap_session_t *aps;
199 nat_t *nat;
200 {
201 	ipsec_pxy_t *ipsec;
202 	fr_info_t fi;
203 	ip_t *ip;
204 	int p;
205 
206 	if ((fin->fin_out == 1) && (nat->nat_dir == NAT_INBOUND))
207 		return 0;
208 
209 	if ((fin->fin_out == 0) && (nat->nat_dir == NAT_OUTBOUND))
210 		return 0;
211 
212 	ipsec = aps->aps_data;
213 
214 	if (ipsec != NULL) {
215 		ip = fin->fin_ip;
216 		p = ip->ip_p;
217 
218 		if ((ipsec->ipsc_nat == NULL) || (ipsec->ipsc_state == NULL)) {
219 			bcopy((char *)fin, (char *)&fi, sizeof(fi));
220 			fi.fin_state = NULL;
221 			fi.fin_nat = NULL;
222 			fi.fin_fi.fi_p = IPPROTO_ESP;
223 			fi.fin_fr = &ipsecfr;
224 			fi.fin_data[0] = 0;
225 			fi.fin_data[1] = 0;
226 			ip->ip_p = IPPROTO_ESP;
227 			fi.fin_flx &= ~(FI_TCPUDP|FI_STATE|FI_FRAG);
228 			fi.fin_flx |= FI_IGNORE;
229 		}
230 
231 		/*
232 		 * Update NAT timeout/create NAT if missing.
233 		 */
234 		if (ipsec->ipsc_nat != NULL)
235 			fr_queueback(&ipsec->ipsc_nat->nat_tqe);
236 		else {
237 			ipsec->ipsc_nat = nat_new(&fi, &ipsec->ipsc_rule,
238 						  &ipsec->ipsc_nat,
239 						  NAT_SLAVE|SI_WILDP,
240 						  nat->nat_dir);
241 			if (ipsec->ipsc_nat != NULL) {
242 				(void) nat_proto(&fi, ipsec->ipsc_nat, 0);
243 				nat_update(&fi, ipsec->ipsc_nat,
244 					   &ipsec->ipsc_rule);
245 			}
246 		}
247 
248 		/*
249 		 * Update state timeout/create state if missing.
250 		 */
251 		READ_ENTER(&ipf_state);
252 		if (ipsec->ipsc_state != NULL) {
253 			fr_queueback(&ipsec->ipsc_state->is_sti);
254 			ipsec->ipsc_state->is_die = nat->nat_age;
255 			RWLOCK_EXIT(&ipf_state);
256 		} else {
257 			RWLOCK_EXIT(&ipf_state);
258 			fi.fin_data[0] = 0;
259 			fi.fin_data[1] = 0;
260 			ipsec->ipsc_state = fr_addstate(&fi,
261 							&ipsec->ipsc_state,
262 							SI_WILDP);
263 			if (fi.fin_state != NULL)
264 				fr_statederef(&fi, (ipstate_t **)&fi.fin_state);
265 		}
266 		ip->ip_p = p;
267 	}
268 	return 0;
269 }
270 
271 
272 /*
273  * This extends the NAT matching to be based on the cookies associated with
274  * a session and found at the front of IKE packets.  The cookies are always
275  * in the same order (not reversed depending on packet flow direction as with
276  * UDP/TCP port numbers).
277  */
278 int ippr_ipsec_match(fin, aps, nat)
279 fr_info_t *fin;
280 ap_session_t *aps;
281 nat_t *nat;
282 {
283 	ipsec_pxy_t *ipsec;
284 	u_32_t cookies[4];
285 	mb_t *m;
286 	int off;
287 
288 	nat = nat;	/* LINT */
289 
290 	if ((fin->fin_dlen < sizeof(cookies)) || (fin->fin_flx & FI_FRAG))
291 		return -1;
292 
293 	off = fin->fin_plen - fin->fin_dlen + fin->fin_ipoff;
294 	ipsec = aps->aps_data;
295 	m = fin->fin_m;
296 	COPYDATA(m, off, sizeof(cookies), (char *)cookies);
297 
298 	if ((cookies[0] != ipsec->ipsc_icookie[0]) ||
299 	    (cookies[1] != ipsec->ipsc_icookie[1]))
300 		return -1;
301 
302 	if (ipsec->ipsc_rckset == 0) {
303 		if ((cookies[2]|cookies[3]) == 0) {
304 			return 0;
305 		}
306 		ipsec->ipsc_rckset = 1;
307 		ipsec->ipsc_rcookie[0] = cookies[2];
308 		ipsec->ipsc_rcookie[1] = cookies[3];
309 		return 0;
310 	}
311 
312 	if ((cookies[2] != ipsec->ipsc_rcookie[0]) ||
313 	    (cookies[3] != ipsec->ipsc_rcookie[1]))
314 		return -1;
315 	return 0;
316 }
317 
318 
319 /*
320  * clean up after ourselves.
321  */
322 void ippr_ipsec_del(aps)
323 ap_session_t *aps;
324 {
325 	ipsec_pxy_t *ipsec;
326 
327 	ipsec = aps->aps_data;
328 
329 	if (ipsec != NULL) {
330 		/*
331 		 * Don't bother changing any of the NAT structure details,
332 		 * *_del() is on a callback from aps_free(), from nat_delete()
333 		 */
334 
335 		READ_ENTER(&ipf_state);
336 		if (ipsec->ipsc_state != NULL) {
337 			ipsec->ipsc_state->is_die = fr_ticks + 1;
338 			ipsec->ipsc_state->is_me = NULL;
339 			fr_queuefront(&ipsec->ipsc_state->is_sti);
340 		}
341 		RWLOCK_EXIT(&ipf_state);
342 
343 		ipsec->ipsc_state = NULL;
344 		ipsec->ipsc_nat = NULL;
345 	}
346 }
347