1 /* $NetBSD: ipcomp_input.c,v 1.36.10.1 2011/04/03 06:08:35 jdc Exp $ */
2 /* $KAME: ipcomp_input.c,v 1.29 2001/09/04 08:43:19 itojun Exp $ */
3
4 /*
5 * Copyright (C) 1999 WIDE Project.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the project nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 /*
34 * RFC2393 IP payload compression protocol (IPComp).
35 */
36
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: ipcomp_input.c,v 1.36.10.1 2011/04/03 06:08:35 jdc Exp $");
39
40 #include "opt_inet.h"
41 #include "opt_ipsec.h"
42
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/malloc.h>
46 #include <sys/mbuf.h>
47 #include <sys/domain.h>
48 #include <sys/protosw.h>
49 #include <sys/socket.h>
50 #include <sys/errno.h>
51 #include <sys/time.h>
52 #include <sys/kernel.h>
53 #include <sys/syslog.h>
54
55 #include <net/if.h>
56 #include <net/route.h>
57 #include <net/netisr.h>
58 #include <net/zlib.h>
59 #include <sys/cpu.h>
60
61 #include <netinet/in.h>
62 #include <netinet/in_systm.h>
63 #include <netinet/in_var.h>
64 #include <netinet/in_proto.h>
65 #include <netinet/ip.h>
66 #include <netinet/ip_var.h>
67 #include <netinet/ip_ecn.h>
68
69 #ifdef INET6
70 #include <netinet/ip6.h>
71 #include <netinet6/ip6_var.h>
72 #endif
73 #include <netinet6/ipcomp.h>
74
75 #include <netinet6/ipsec.h>
76 #include <netinet6/ipsec_private.h>
77 #include <netkey/key.h>
78 #include <netkey/keydb.h>
79
80 #include <machine/stdarg.h>
81
82 #include <net/net_osdep.h>
83
84 /*#define IPLEN_FLIPPED*/
85
86 #ifdef INET
87 void
88 ipcomp4_init(void)
89 {
90
91 ipsec4_init();
92 }
93
94 void
95 #if __STDC__
96 ipcomp4_input(struct mbuf *m, ...)
97 #else
98 ipcomp4_input(m, va_alist)
99 struct mbuf *m;
100 va_dcl
101 #endif
102 {
103 struct mbuf *md;
104 struct ip *ip;
105 struct ipcomp *ipcomp;
106 const struct ipcomp_algorithm *algo;
107 u_int16_t cpi; /* host order */
108 u_int16_t nxt;
109 size_t hlen;
110 int error;
111 size_t newlen, olen;
112 struct secasvar *sav = NULL;
113 int off, proto;
114 va_list ap;
115 u_int16_t sport = 0;
116 u_int16_t dport = 0;
117 #ifdef IPSEC_NAT_T
118 struct m_tag *tag = NULL;
119 #endif
120
121 va_start(ap, m);
122 off = va_arg(ap, int);
123 proto = va_arg(ap, int);
124 va_end(ap);
125
126 if (m->m_pkthdr.len < off + sizeof(struct ipcomp)) {
127 ipseclog((LOG_DEBUG, "IPv4 IPComp input: assumption failed "
128 "(packet too short)\n"));
129 IPSEC_STATINC(IPSEC_STAT_IN_INVAL);
130 goto fail;
131 }
132 #ifdef IPSEC_NAT_T
133 /* find the source port for NAT-T */
134 if ((tag = m_tag_find(m, PACKET_TAG_IPSEC_NAT_T_PORTS, NULL)) != NULL) {
135 sport = ((u_int16_t *)(tag + 1))[0];
136 dport = ((u_int16_t *)(tag + 1))[1];
137 }
138 #endif
139
140 md = m_pulldown(m, off, sizeof(*ipcomp), NULL);
141 if (!md) {
142 m = NULL; /* already freed */
143 ipseclog((LOG_DEBUG, "IPv4 IPComp input: assumption failed "
144 "(pulldown failure)\n"));
145 IPSEC_STATINC(IPSEC_STAT_IN_INVAL);
146 goto fail;
147 }
148 ipcomp = mtod(md, struct ipcomp *);
149 ip = mtod(m, struct ip *);
150 nxt = ipcomp->comp_nxt;
151 if (nxt == IPPROTO_IPCOMP || nxt == IPPROTO_AH || nxt == IPPROTO_ESP) {
152 /* nested ipcomp - possible attack, not likely useful */
153 ipseclog((LOG_DEBUG, "IPv4 IPComp input: nested ipcomp "
154 "(bailing)\n"));
155 IPSEC_STATINC(IPSEC_STAT_IN_INVAL);
156 goto fail;
157 }
158 hlen = ip->ip_hl << 2;
159
160 cpi = ntohs(ipcomp->comp_cpi);
161
162 if (cpi >= IPCOMP_CPI_NEGOTIATE_MIN) {
163 sav = key_allocsa(AF_INET, (void *)&ip->ip_src,
164 (void *)&ip->ip_dst, IPPROTO_IPCOMP, htonl(cpi),
165 sport, dport);
166 if (sav != NULL &&
167 (sav->state == SADB_SASTATE_MATURE ||
168 sav->state == SADB_SASTATE_DYING)) {
169 cpi = sav->alg_enc; /* XXX */
170 /* other parameters to look at? */
171 }
172 }
173 algo = ipcomp_algorithm_lookup(cpi);
174 if (!algo) {
175 ipseclog((LOG_WARNING, "IPv4 IPComp input: unknown cpi %u\n",
176 cpi));
177 IPSEC_STATINC(IPSEC_STAT_IN_NOSA);
178 goto fail;
179 }
180
181 /* chop ipcomp header */
182 ipcomp = NULL;
183 md->m_data += sizeof(struct ipcomp);
184 md->m_len -= sizeof(struct ipcomp);
185 m->m_pkthdr.len -= sizeof(struct ipcomp);
186 #ifdef IPLEN_FLIPPED
187 ip->ip_len -= sizeof(struct ipcomp);
188 #else
189 ip->ip_len = htons(ntohs(ip->ip_len) - sizeof(struct ipcomp));
190 #endif
191
192 olen = m->m_pkthdr.len;
193 newlen = m->m_pkthdr.len - off;
194 error = (*algo->decompress)(m, m->m_next, &newlen);
195 if (error != 0) {
196 if (error == EINVAL)
197 IPSEC_STATINC(IPSEC_STAT_IN_INVAL);
198 else if (error == ENOBUFS)
199 IPSEC_STATINC(IPSEC_STAT_IN_NOMEM);
200 m = NULL;
201 goto fail;
202 }
203 IPSEC_STATINC(IPSEC_STAT_IN_COMPHIST + cpi);
204
205 /*
206 * returning decompressed packet onto icmp is meaningless.
207 * mark it decrypted to prevent icmp from attaching original packet.
208 */
209 m->m_flags |= M_DECRYPTED;
210
211 m->m_pkthdr.len = off + newlen;
212 ip = mtod(m, struct ip *);
213 {
214 size_t len;
215 #ifdef IPLEN_FLIPPED
216 len = ip->ip_len;
217 #else
218 len = ntohs(ip->ip_len);
219 #endif
220 /*
221 * be careful about underflow. also, do not assign exact value
222 * as ip_len is manipulated differently on *BSDs.
223 */
224 len += m->m_pkthdr.len;
225 len -= olen;
226 if (len & ~0xffff) {
227 /* packet too big after decompress */
228 IPSEC_STATINC(IPSEC_STAT_IN_INVAL);
229 goto fail;
230 }
231 #ifdef IPLEN_FLIPPED
232 ip->ip_len = len & 0xffff;
233 #else
234 ip->ip_len = htons(len & 0xffff);
235 #endif
236 ip->ip_p = nxt;
237 }
238
239 if (sav) {
240 key_sa_recordxfer(sav, m);
241 if (ipsec_addhist(m, IPPROTO_IPCOMP, (u_int32_t)cpi) != 0) {
242 IPSEC_STATINC(IPSEC_STAT_IN_NOMEM);
243 goto fail;
244 }
245 key_freesav(sav);
246 sav = NULL;
247 }
248
249 if (nxt != IPPROTO_DONE) {
250 if ((inetsw[ip_protox[nxt]].pr_flags & PR_LASTHDR) != 0 &&
251 ipsec4_in_reject(m, NULL)) {
252 IPSEC_STATINC(IPSEC_STAT_IN_POLVIO);
253 goto fail;
254 }
255 (*inetsw[ip_protox[nxt]].pr_input)(m, off, nxt);
256 } else
257 m_freem(m);
258 m = NULL;
259
260 IPSEC_STATINC(IPSEC_STAT_IN_SUCCESS);
261 return;
262
263 fail:
264 if (sav)
265 key_freesav(sav);
266 if (m)
267 m_freem(m);
268 return;
269 }
270 #endif /* INET */
271
272 #ifdef INET6
273 void
274 ipcomp6_init(void)
275 {
276
277 ipsec6_init();
278 }
279
280 int
281 ipcomp6_input(struct mbuf **mp, int *offp, int proto)
282 {
283 struct mbuf *m, *md;
284 int off;
285 struct ip6_hdr *ip6;
286 struct ipcomp *ipcomp;
287 const struct ipcomp_algorithm *algo;
288 u_int16_t cpi; /* host order */
289 u_int16_t nxt;
290 int error;
291 size_t newlen;
292 struct secasvar *sav = NULL;
293 u_int8_t *prvnxtp;
294
295 m = *mp;
296 off = *offp;
297
298 md = m_pulldown(m, off, sizeof(*ipcomp), NULL);
299 if (!md) {
300 m = NULL; /* already freed */
301 ipseclog((LOG_DEBUG, "IPv6 IPComp input: assumption failed "
302 "(pulldown failure)\n"));
303 IPSEC6_STATINC(IPSEC_STAT_IN_INVAL);
304 goto fail;
305 }
306 ipcomp = mtod(md, struct ipcomp *);
307 ip6 = mtod(m, struct ip6_hdr *);
308 nxt = ipcomp->comp_nxt;
309
310 cpi = ntohs(ipcomp->comp_cpi);
311
312 if (cpi >= IPCOMP_CPI_NEGOTIATE_MIN) {
313 sav = key_allocsa(AF_INET6, (void *)&ip6->ip6_src,
314 (void *)&ip6->ip6_dst, IPPROTO_IPCOMP,
315 htonl(cpi), 0, 0);
316 if (sav != NULL &&
317 (sav->state == SADB_SASTATE_MATURE ||
318 sav->state == SADB_SASTATE_DYING)) {
319 cpi = sav->alg_enc; /* XXX */
320 /* other parameters to look at? */
321 }
322 }
323 algo = ipcomp_algorithm_lookup(cpi);
324 if (!algo) {
325 ipseclog((LOG_WARNING, "IPv6 IPComp input: unknown cpi %u; "
326 "dropping the packet for simplicity\n", cpi));
327 IPSEC6_STATINC(IPSEC_STAT_IN_NOSA);
328 goto fail;
329 }
330
331 /* chop ipcomp header */
332 ipcomp = NULL;
333 md->m_data += sizeof(struct ipcomp);
334 md->m_len -= sizeof(struct ipcomp);
335 m->m_pkthdr.len -= sizeof(struct ipcomp);
336
337 newlen = m->m_pkthdr.len - off;
338 error = (*algo->decompress)(m, md, &newlen);
339 if (error != 0) {
340 if (error == EINVAL)
341 IPSEC6_STATINC(IPSEC_STAT_IN_INVAL);
342 else if (error == ENOBUFS)
343 IPSEC6_STATINC(IPSEC_STAT_IN_NOMEM);
344 m = NULL;
345 goto fail;
346 }
347 IPSEC6_STATINC(IPSEC_STAT_IN_COMPHIST + cpi);
348 m->m_pkthdr.len = off + newlen;
349
350 /*
351 * returning decompressed packet onto icmp is meaningless.
352 * mark it decrypted to prevent icmp from attaching original packet.
353 */
354 m->m_flags |= M_DECRYPTED;
355
356 /* update next header field */
357 prvnxtp = ip6_get_prevhdr(m, off);
358 *prvnxtp = nxt;
359
360 /*
361 * no need to adjust payload length, as all the IPv6 protocols
362 * look at m->m_pkthdr.len
363 */
364
365 if (sav) {
366 key_sa_recordxfer(sav, m);
367 if (ipsec_addhist(m, IPPROTO_IPCOMP, (u_int32_t)cpi) != 0) {
368 IPSEC6_STATINC(IPSEC_STAT_IN_NOMEM);
369 goto fail;
370 }
371 key_freesav(sav);
372 sav = NULL;
373 }
374 *offp = off;
375 *mp = m;
376 IPSEC6_STATINC(IPSEC_STAT_IN_SUCCESS);
377 return nxt;
378
379 fail:
380 if (m)
381 m_freem(m);
382 if (sav)
383 key_freesav(sav);
384 return IPPROTO_DONE;
385 }
386 #endif /* INET6 */
Cache object: 995e1903a96b113335112a71a0869324
|