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