Line data Source code
1 : /* $OpenBSD: ip_ipcomp.c,v 1.66 2018/09/13 12:29:43 mpi Exp $ */
2 :
3 : /*
4 : * Copyright (c) 2001 Jean-Jacques Bernard-Gundol (jj@wabbitt.org)
5 : *
6 : * Redistribution and use in source and binary forms, with or without
7 : * modification, are permitted provided that the following conditions
8 : * are met:
9 : *
10 : * 1. Redistributions of source code must retain the above copyright
11 : * notice, this list of conditions and the following disclaimer.
12 : * 2. Redistributions in binary form must reproduce the above copyright
13 : * notice, this list of conditions and the following disclaimer in the
14 : * documentation and/or other materials provided with the distribution.
15 : * 3. The name of the author may not be used to endorse or promote products
16 : * derived from this software without specific prior written permission.
17 : *
18 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 : * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 : */
29 :
30 : /* IP payload compression protocol (IPComp), see RFC 2393 */
31 :
32 : #include <sys/param.h>
33 : #include <sys/systm.h>
34 : #include <sys/mbuf.h>
35 : #include <sys/socket.h>
36 :
37 : #include <net/if.h>
38 : #include <net/if_var.h>
39 : #include <net/bpf.h>
40 :
41 : #include <netinet/in.h>
42 : #include <netinet/ip.h>
43 : #include <netinet/ip_var.h>
44 :
45 : #ifdef INET6
46 : #include <netinet/ip6.h>
47 : #endif /* INET6 */
48 :
49 : #include <netinet/ip_ipsp.h>
50 : #include <netinet/ip_ipcomp.h>
51 : #include <net/pfkeyv2.h>
52 : #include <net/if_enc.h>
53 :
54 : #include <crypto/cryptodev.h>
55 : #include <crypto/xform.h>
56 :
57 : #include "bpfilter.h"
58 :
59 : #ifdef ENCDEBUG
60 : #define DPRINTF(x) if (encdebug) printf x
61 : #else
62 : #define DPRINTF(x)
63 : #endif
64 :
65 : /*
66 : * ipcomp_attach() is called from the transformation code
67 : */
68 : int
69 0 : ipcomp_attach(void)
70 : {
71 0 : return 0;
72 : }
73 :
74 : /*
75 : * ipcomp_init() is called when an CPI is being set up.
76 : */
77 : int
78 0 : ipcomp_init(struct tdb *tdbp, struct xformsw *xsp, struct ipsecinit *ii)
79 : {
80 : struct comp_algo *tcomp = NULL;
81 0 : struct cryptoini cric;
82 :
83 0 : switch (ii->ii_compalg) {
84 : case SADB_X_CALG_DEFLATE:
85 : tcomp = &comp_algo_deflate;
86 0 : break;
87 : case SADB_X_CALG_LZS:
88 : tcomp = &comp_algo_lzs;
89 0 : break;
90 :
91 : default:
92 : DPRINTF(("%s: unsupported compression algorithm %d specified\n",
93 : __func__, ii->ii_compalg));
94 0 : return EINVAL;
95 : }
96 :
97 0 : tdbp->tdb_compalgxform = tcomp;
98 :
99 : DPRINTF(("%s: initialized TDB with ipcomp algorithm %s\n", __func__,
100 : tcomp->name));
101 :
102 0 : tdbp->tdb_xform = xsp;
103 :
104 : /* Initialize crypto session */
105 0 : memset(&cric, 0, sizeof(cric));
106 0 : cric.cri_alg = tdbp->tdb_compalgxform->type;
107 :
108 0 : return crypto_newsession(&tdbp->tdb_cryptoid, &cric, 0);
109 0 : }
110 :
111 : /*
112 : * ipcomp_zeroize() used when IPCA is deleted
113 : */
114 : int
115 0 : ipcomp_zeroize(struct tdb *tdbp)
116 : {
117 : int err;
118 :
119 0 : err = crypto_freesession(tdbp->tdb_cryptoid);
120 0 : tdbp->tdb_cryptoid = 0;
121 0 : return err;
122 : }
123 :
124 : /*
125 : * ipcomp_input() gets called to uncompress an input packet
126 : */
127 : int
128 0 : ipcomp_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff)
129 : {
130 0 : struct comp_algo *ipcompx = (struct comp_algo *) tdb->tdb_compalgxform;
131 : struct tdb_crypto *tc;
132 : int hlen;
133 :
134 : struct cryptodesc *crdc = NULL;
135 : struct cryptop *crp;
136 :
137 : hlen = IPCOMP_HLENGTH;
138 :
139 : /* Get crypto descriptors */
140 0 : crp = crypto_getreq(1);
141 0 : if (crp == NULL) {
142 0 : m_freem(m);
143 : DPRINTF(("%s: failed to acquire crypto descriptors\n", __func__));
144 0 : ipcompstat_inc(ipcomps_crypto);
145 0 : return ENOBUFS;
146 : }
147 : /* Get IPsec-specific opaque pointer */
148 0 : tc = malloc(sizeof(*tc), M_XDATA, M_NOWAIT | M_ZERO);
149 0 : if (tc == NULL) {
150 0 : m_freem(m);
151 0 : crypto_freereq(crp);
152 : DPRINTF(("%s: failed to allocate tdb_crypto\n", __func__));
153 0 : ipcompstat_inc(ipcomps_crypto);
154 0 : return ENOBUFS;
155 : }
156 0 : crdc = &crp->crp_desc[0];
157 :
158 0 : crdc->crd_skip = skip + hlen;
159 0 : crdc->crd_len = m->m_pkthdr.len - (skip + hlen);
160 0 : crdc->crd_inject = skip;
161 :
162 : /* Decompression operation */
163 0 : crdc->crd_alg = ipcompx->type;
164 :
165 : /* Crypto operation descriptor */
166 0 : crp->crp_ilen = m->m_pkthdr.len - (skip + hlen);
167 0 : crp->crp_flags = CRYPTO_F_IMBUF;
168 0 : crp->crp_buf = (caddr_t)m;
169 0 : crp->crp_callback = ipsec_input_cb;
170 0 : crp->crp_sid = tdb->tdb_cryptoid;
171 0 : crp->crp_opaque = (caddr_t)tc;
172 :
173 : /* These are passed as-is to the callback */
174 0 : tc->tc_skip = skip;
175 0 : tc->tc_protoff = protoff;
176 0 : tc->tc_spi = tdb->tdb_spi;
177 0 : tc->tc_proto = IPPROTO_IPCOMP;
178 0 : tc->tc_rdomain = tdb->tdb_rdomain;
179 0 : tc->tc_dst = tdb->tdb_dst;
180 :
181 0 : return crypto_dispatch(crp);
182 0 : }
183 :
184 : int
185 0 : ipcomp_input_cb(struct tdb *tdb, struct tdb_crypto *tc, struct mbuf *m, int clen)
186 : {
187 0 : int skip, protoff, roff, hlen = IPCOMP_HLENGTH;
188 0 : u_int8_t nproto;
189 : u_int64_t ibytes;
190 : struct mbuf *m1, *mo;
191 : struct ipcomp *ipcomp;
192 : caddr_t addr;
193 : #ifdef ENCDEBUG
194 : char buf[INET6_ADDRSTRLEN];
195 : #endif
196 :
197 0 : NET_ASSERT_LOCKED();
198 :
199 0 : skip = tc->tc_skip;
200 0 : protoff = tc->tc_protoff;
201 :
202 : /* update the counters */
203 0 : ibytes = m->m_pkthdr.len - (skip + hlen);
204 0 : tdb->tdb_cur_bytes += ibytes;
205 0 : tdb->tdb_ibytes += ibytes;
206 0 : ipcompstat_add(ipcomps_ibytes, ibytes);
207 :
208 : /* Hard expiration */
209 0 : if ((tdb->tdb_flags & TDBF_BYTES) &&
210 0 : (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes)) {
211 0 : pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD);
212 0 : tdb_delete(tdb);
213 0 : goto baddone;
214 : }
215 : /* Notify on soft expiration */
216 0 : if ((tdb->tdb_flags & TDBF_SOFT_BYTES) &&
217 0 : (tdb->tdb_cur_bytes >= tdb->tdb_soft_bytes)) {
218 0 : pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT);
219 0 : tdb->tdb_flags &= ~TDBF_SOFT_BYTES; /* Turn off checking */
220 0 : }
221 :
222 : /* In case it's not done already, adjust the size of the mbuf chain */
223 0 : m->m_pkthdr.len = clen + hlen + skip;
224 :
225 0 : if ((m->m_len < skip + hlen) && (m = m_pullup(m, skip + hlen)) == 0) {
226 0 : ipcompstat_inc(ipcomps_hdrops);
227 0 : goto baddone;
228 : }
229 :
230 : /* Find the beginning of the IPCOMP header */
231 0 : m1 = m_getptr(m, skip, &roff);
232 0 : if (m1 == NULL) {
233 : DPRINTF(("%s: bad mbuf chain, IPCA %s/%08x\n", __func__,
234 : ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
235 : ntohl(tdb->tdb_spi)));
236 0 : ipcompstat_inc(ipcomps_hdrops);
237 0 : goto baddone;
238 : }
239 : /* Keep the next protocol field */
240 0 : addr = (caddr_t) mtod(m, struct ip *) + skip;
241 0 : ipcomp = (struct ipcomp *) addr;
242 0 : nproto = ipcomp->ipcomp_nh;
243 :
244 : /* Remove the IPCOMP header from the mbuf */
245 0 : if (roff == 0) {
246 : /* The IPCOMP header is at the beginning of m1 */
247 0 : m_adj(m1, hlen);
248 : /*
249 : * If m1 is the first mbuf, it has set M_PKTHDR and m_adj()
250 : * has already adjusted the packet header length for us.
251 : */
252 0 : if (m1 != m)
253 0 : m->m_pkthdr.len -= hlen;
254 0 : } else if (roff + hlen >= m1->m_len) {
255 : int adjlen;
256 :
257 0 : if (roff + hlen > m1->m_len) {
258 0 : adjlen = roff + hlen - m1->m_len;
259 :
260 : /* Adjust the next mbuf by the remainder */
261 0 : m_adj(m1->m_next, adjlen);
262 :
263 : /*
264 : * The second mbuf is guaranteed not to have a
265 : * pkthdr...
266 : */
267 0 : m->m_pkthdr.len -= adjlen;
268 0 : }
269 : /* Now, let's unlink the mbuf chain for a second... */
270 0 : mo = m1->m_next;
271 0 : m1->m_next = NULL;
272 :
273 : /* ...and trim the end of the first part of the chain...sick */
274 0 : adjlen = m1->m_len - roff;
275 0 : m_adj(m1, -adjlen);
276 : /*
277 : * If m1 is the first mbuf, it has set M_PKTHDR and m_adj()
278 : * has already adjusted the packet header length for us.
279 : */
280 0 : if (m1 != m)
281 0 : m->m_pkthdr.len -= adjlen;
282 :
283 : /* Finally, let's relink */
284 0 : m1->m_next = mo;
285 0 : } else {
286 0 : memmove(mtod(m1, u_char *) + roff,
287 : mtod(m1, u_char *) + roff + hlen,
288 : m1->m_len - (roff + hlen));
289 0 : m1->m_len -= hlen;
290 0 : m->m_pkthdr.len -= hlen;
291 : }
292 :
293 : /* Release the crypto descriptors */
294 0 : free(tc, M_XDATA, 0);
295 :
296 : /* Restore the Next Protocol field */
297 0 : m_copyback(m, protoff, sizeof(u_int8_t), &nproto, M_NOWAIT);
298 :
299 : /* Back to generic IPsec input processing */
300 0 : return ipsec_common_input_cb(m, tdb, skip, protoff);
301 :
302 : baddone:
303 0 : m_freem(m);
304 0 : free(tc, M_XDATA, 0);
305 0 : return -1;
306 0 : }
307 :
308 : /*
309 : * IPComp output routine, called by ipsp_process_packet()
310 : */
311 : int
312 0 : ipcomp_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip,
313 : int protoff)
314 : {
315 0 : struct comp_algo *ipcompx = (struct comp_algo *) tdb->tdb_compalgxform;
316 : int error, hlen;
317 : struct cryptodesc *crdc = NULL;
318 : struct cryptop *crp = NULL;
319 : struct tdb_crypto *tc;
320 : struct mbuf *mi;
321 : #ifdef ENCDEBUG
322 : char buf[INET6_ADDRSTRLEN];
323 : #endif
324 : #if NBPFILTER > 0
325 : struct ifnet *encif;
326 :
327 0 : if ((encif = enc_getif(0, tdb->tdb_tap)) != NULL) {
328 0 : encif->if_opackets++;
329 0 : encif->if_obytes += m->m_pkthdr.len;
330 :
331 0 : if (encif->if_bpf) {
332 0 : struct enchdr hdr;
333 :
334 0 : memset(&hdr, 0, sizeof(hdr));
335 :
336 0 : hdr.af = tdb->tdb_dst.sa.sa_family;
337 0 : hdr.spi = tdb->tdb_spi;
338 :
339 0 : bpf_mtap_hdr(encif->if_bpf, (char *)&hdr,
340 : ENC_HDRLEN, m, BPF_DIRECTION_OUT, NULL);
341 0 : }
342 : }
343 : #endif
344 : hlen = IPCOMP_HLENGTH;
345 :
346 0 : ipcompstat_inc(ipcomps_output);
347 :
348 0 : switch (tdb->tdb_dst.sa.sa_family) {
349 : case AF_INET:
350 : /* Check for IPv4 maximum packet size violations */
351 : /*
352 : * Since compression is going to reduce the size, no need to
353 : * worry
354 : */
355 0 : if (m->m_pkthdr.len + hlen > IP_MAXPACKET) {
356 : DPRINTF(("%s: packet in IPCA %s/%08x got too big\n",
357 : __func__, ipsp_address(&tdb->tdb_dst, buf,
358 : sizeof(buf)), ntohl(tdb->tdb_spi)));
359 0 : ipcompstat_inc(ipcomps_toobig);
360 : error = EMSGSIZE;
361 0 : goto drop;
362 : }
363 : break;
364 :
365 : #ifdef INET6
366 : case AF_INET6:
367 : /* Check for IPv6 maximum packet size violations */
368 0 : if (m->m_pkthdr.len + hlen > IPV6_MAXPACKET) {
369 : DPRINTF(("%s: packet in IPCA %s/%08x got too big\n",
370 : __func__, ipsp_address(&tdb->tdb_dst, buf,
371 : sizeof(buf)), ntohl(tdb->tdb_spi)));
372 0 : ipcompstat_inc(ipcomps_toobig);
373 : error = EMSGSIZE;
374 0 : goto drop;
375 : }
376 : break;
377 : #endif /* INET6 */
378 :
379 : default:
380 : DPRINTF(("%s: unknown/unsupported protocol family %d, "
381 : "IPCA %s/%08x\n", __func__, tdb->tdb_dst.sa.sa_family,
382 : ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
383 : ntohl(tdb->tdb_spi)));
384 0 : ipcompstat_inc(ipcomps_nopf);
385 : error = EPFNOSUPPORT;
386 0 : goto drop;
387 : }
388 :
389 : /* Update the counters */
390 :
391 0 : tdb->tdb_cur_bytes += m->m_pkthdr.len - skip;
392 0 : ipcompstat_add(ipcomps_obytes, m->m_pkthdr.len - skip);
393 :
394 : /* Hard byte expiration */
395 0 : if ((tdb->tdb_flags & TDBF_BYTES) &&
396 0 : (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes)) {
397 0 : pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD);
398 0 : tdb_delete(tdb);
399 : error = EINVAL;
400 0 : goto drop;
401 : }
402 : /* Soft byte expiration */
403 0 : if ((tdb->tdb_flags & TDBF_SOFT_BYTES) &&
404 0 : (tdb->tdb_cur_bytes >= tdb->tdb_soft_bytes)) {
405 0 : pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT);
406 0 : tdb->tdb_flags &= ~TDBF_SOFT_BYTES; /* Turn off checking */
407 0 : }
408 : /*
409 : * Loop through mbuf chain; if we find a readonly mbuf,
410 : * copy the packet.
411 : */
412 : mi = m;
413 0 : while (mi != NULL && !M_READONLY(mi))
414 0 : mi = mi->m_next;
415 :
416 0 : if (mi != NULL) {
417 0 : struct mbuf *n = m_dup_pkt(m, 0, M_DONTWAIT);
418 :
419 0 : if (n == NULL) {
420 : DPRINTF(("%s: bad mbuf chain, IPCA %s/%08x\n", __func__,
421 : ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
422 : ntohl(tdb->tdb_spi)));
423 0 : ipcompstat_inc(ipcomps_hdrops);
424 : error = ENOBUFS;
425 0 : goto drop;
426 : }
427 :
428 0 : m_freem(m);
429 : m = n;
430 0 : }
431 : /* Ok now, we can pass to the crypto processing */
432 :
433 : /* Get crypto descriptors */
434 0 : crp = crypto_getreq(1);
435 0 : if (crp == NULL) {
436 : DPRINTF(("%s: failed to acquire crypto descriptors\n", __func__));
437 0 : ipcompstat_inc(ipcomps_crypto);
438 : error = ENOBUFS;
439 0 : goto drop;
440 : }
441 0 : crdc = &crp->crp_desc[0];
442 :
443 : /* Compression descriptor */
444 0 : crdc->crd_skip = skip;
445 0 : crdc->crd_len = m->m_pkthdr.len - skip;
446 0 : crdc->crd_flags = CRD_F_COMP;
447 0 : crdc->crd_inject = skip;
448 :
449 : /* Compression operation */
450 0 : crdc->crd_alg = ipcompx->type;
451 :
452 : /* IPsec-specific opaque crypto info */
453 0 : tc = malloc(sizeof(*tc), M_XDATA, M_NOWAIT | M_ZERO);
454 0 : if (tc == NULL) {
455 : DPRINTF(("%s: failed to allocate tdb_crypto\n", __func__));
456 0 : ipcompstat_inc(ipcomps_crypto);
457 : error = ENOBUFS;
458 0 : goto drop;
459 : }
460 :
461 0 : tc->tc_spi = tdb->tdb_spi;
462 0 : tc->tc_proto = tdb->tdb_sproto;
463 0 : tc->tc_skip = skip;
464 0 : tc->tc_rdomain = tdb->tdb_rdomain;
465 0 : tc->tc_dst = tdb->tdb_dst;
466 :
467 : /* Crypto operation descriptor */
468 0 : crp->crp_ilen = m->m_pkthdr.len; /* Total input length */
469 0 : crp->crp_flags = CRYPTO_F_IMBUF;
470 0 : crp->crp_buf = (caddr_t)m;
471 0 : crp->crp_callback = ipsec_output_cb;
472 0 : crp->crp_opaque = (caddr_t)tc;
473 0 : crp->crp_sid = tdb->tdb_cryptoid;
474 :
475 0 : return crypto_dispatch(crp);
476 :
477 : drop:
478 0 : m_freem(m);
479 0 : crypto_freereq(crp);
480 0 : return error;
481 0 : }
482 :
483 : /*
484 : * IPComp output callback.
485 : */
486 : int
487 0 : ipcomp_output_cb(struct tdb *tdb, struct tdb_crypto *tc, struct mbuf *m,
488 : int ilen, int olen)
489 : {
490 : struct mbuf *mo;
491 0 : int skip, rlen, roff;
492 : u_int16_t cpi;
493 : struct ip *ip;
494 : #ifdef INET6
495 : struct ip6_hdr *ip6;
496 : #endif
497 : struct ipcomp *ipcomp;
498 : #ifdef ENCDEBUG
499 : char buf[INET6_ADDRSTRLEN];
500 : #endif
501 :
502 0 : skip = tc->tc_skip;
503 0 : rlen = ilen - skip;
504 :
505 : /* Check sizes. */
506 0 : if (rlen <= olen + IPCOMP_HLENGTH) {
507 : /* Compression was useless, we have lost time. */
508 0 : ipcompstat_inc(ipcomps_minlen); /* misnomer, but like to count */
509 0 : goto skiphdr;
510 : }
511 :
512 : /* Inject IPCOMP header */
513 0 : mo = m_makespace(m, skip, IPCOMP_HLENGTH, &roff);
514 0 : if (mo == NULL) {
515 : DPRINTF(("%s: failed to inject IPCOMP header for "
516 : "IPCA %s/%08x\n", __func__, ipsp_address(&tdb->tdb_dst, buf,
517 : sizeof(buf)), ntohl(tdb->tdb_spi)));
518 0 : ipcompstat_inc(ipcomps_wrap);
519 0 : goto baddone;
520 : }
521 :
522 : /* Initialize the IPCOMP header */
523 0 : ipcomp = (struct ipcomp *)(mtod(mo, caddr_t) + roff);
524 0 : memset(ipcomp, 0, sizeof(struct ipcomp));
525 0 : cpi = (u_int16_t) ntohl(tdb->tdb_spi);
526 0 : ipcomp->ipcomp_cpi = htons(cpi);
527 :
528 : /* m_pullup before ? */
529 0 : switch (tdb->tdb_dst.sa.sa_family) {
530 : case AF_INET:
531 0 : ip = mtod(m, struct ip *);
532 0 : ipcomp->ipcomp_nh = ip->ip_p;
533 0 : ip->ip_p = IPPROTO_IPCOMP;
534 0 : break;
535 : #ifdef INET6
536 : case AF_INET6:
537 0 : ip6 = mtod(m, struct ip6_hdr *);
538 0 : ipcomp->ipcomp_nh = ip6->ip6_nxt;
539 0 : ip6->ip6_nxt = IPPROTO_IPCOMP;
540 0 : break;
541 : #endif
542 : default:
543 : DPRINTF(("%s: unsupported protocol family %d, IPCA %s/%08x\n",
544 : __func__, tdb->tdb_dst.sa.sa_family,
545 : ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
546 : ntohl(tdb->tdb_spi)));
547 0 : ipcompstat_inc(ipcomps_nopf);
548 0 : goto baddone;
549 : }
550 :
551 : skiphdr:
552 : /* Release the crypto descriptor. */
553 0 : free(tc, M_XDATA, 0);
554 :
555 0 : if (ipsp_process_done(m, tdb)) {
556 0 : ipcompstat_inc(ipcomps_outfail);
557 0 : return -1;
558 : }
559 0 : return 0;
560 :
561 : baddone:
562 0 : m_freem(m);
563 0 : free(tc, M_XDATA, 0);
564 0 : return -1;
565 0 : }
|