xref: /openbsd-src/sys/netinet/ip_ipcomp.c (revision 24bb5fcea3ed904bc467217bdaadb5dfc618d5bf)
1 /* $OpenBSD: ip_ipcomp.c,v 1.71 2021/07/08 21:07:19 bluhm 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(fmt, args...)						\
61 	do {								\
62 		if (encdebug)						\
63 			printf("%s: " fmt "\n", __func__, ## args);	\
64 	} while (0)
65 #else
66 #define DPRINTF(fmt, args...)						\
67 	do { } while (0)
68 #endif
69 
70 /*
71  * ipcomp_attach() is called from the transformation code
72  */
73 int
74 ipcomp_attach(void)
75 {
76 	return 0;
77 }
78 
79 /*
80  * ipcomp_init() is called when an CPI is being set up.
81  */
82 int
83 ipcomp_init(struct tdb *tdbp, const struct xformsw *xsp, struct ipsecinit *ii)
84 {
85 	const struct comp_algo *tcomp = NULL;
86 	struct cryptoini cric;
87 	int error;
88 
89 	switch (ii->ii_compalg) {
90 	case SADB_X_CALG_DEFLATE:
91 		tcomp = &comp_algo_deflate;
92 		break;
93 	case SADB_X_CALG_LZS:
94 		tcomp = &comp_algo_lzs;
95 		break;
96 
97 	default:
98 		DPRINTF("unsupported compression algorithm %d specified",
99 		    ii->ii_compalg);
100 		return EINVAL;
101 	}
102 
103 	tdbp->tdb_compalgxform = tcomp;
104 
105 	DPRINTF("initialized TDB with ipcomp algorithm %s", tcomp->name);
106 
107 	tdbp->tdb_xform = xsp;
108 
109 	/* Initialize crypto session */
110 	memset(&cric, 0, sizeof(cric));
111 	cric.cri_alg = tdbp->tdb_compalgxform->type;
112 
113 	KERNEL_LOCK();
114 	error = crypto_newsession(&tdbp->tdb_cryptoid, &cric, 0);
115 	KERNEL_UNLOCK();
116 	return error;
117 }
118 
119 /*
120  * ipcomp_zeroize() used when IPCA is deleted
121  */
122 int
123 ipcomp_zeroize(struct tdb *tdbp)
124 {
125 	int error;
126 
127 	KERNEL_LOCK();
128 	error = crypto_freesession(tdbp->tdb_cryptoid);
129 	KERNEL_UNLOCK();
130 	tdbp->tdb_cryptoid = 0;
131 	return error;
132 }
133 
134 /*
135  * ipcomp_input() gets called to uncompress an input packet
136  */
137 int
138 ipcomp_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff)
139 {
140 	const struct comp_algo *ipcompx = tdb->tdb_compalgxform;
141 	struct tdb_crypto *tc;
142 	int hlen, error;
143 
144 	struct cryptodesc *crdc = NULL;
145 	struct cryptop *crp;
146 
147 	hlen = IPCOMP_HLENGTH;
148 
149 	/* Get crypto descriptors */
150 	crp = crypto_getreq(1);
151 	if (crp == NULL) {
152 		m_freem(m);
153 		DPRINTF("failed to acquire crypto descriptors");
154 		ipcompstat_inc(ipcomps_crypto);
155 		return ENOBUFS;
156 	}
157 	/* Get IPsec-specific opaque pointer */
158 	tc = malloc(sizeof(*tc), M_XDATA, M_NOWAIT | M_ZERO);
159 	if (tc == NULL) {
160 		m_freem(m);
161 		crypto_freereq(crp);
162 		DPRINTF("failed to allocate tdb_crypto");
163 		ipcompstat_inc(ipcomps_crypto);
164 		return ENOBUFS;
165 	}
166 	crdc = &crp->crp_desc[0];
167 
168 	crdc->crd_skip = skip + hlen;
169 	crdc->crd_len = m->m_pkthdr.len - (skip + hlen);
170 	crdc->crd_inject = skip;
171 
172 	/* Decompression operation */
173 	crdc->crd_alg = ipcompx->type;
174 
175 	/* Crypto operation descriptor */
176 	crp->crp_ilen = m->m_pkthdr.len - (skip + hlen);
177 	crp->crp_flags = CRYPTO_F_IMBUF;
178 	crp->crp_buf = (caddr_t)m;
179 	crp->crp_callback = ipsec_input_cb;
180 	crp->crp_sid = tdb->tdb_cryptoid;
181 	crp->crp_opaque = (caddr_t)tc;
182 
183 	/* These are passed as-is to the callback */
184 	tc->tc_skip = skip;
185 	tc->tc_protoff = protoff;
186 	tc->tc_spi = tdb->tdb_spi;
187 	tc->tc_proto = IPPROTO_IPCOMP;
188 	tc->tc_rdomain = tdb->tdb_rdomain;
189 	tc->tc_dst = tdb->tdb_dst;
190 
191 	KERNEL_LOCK();
192 	error = crypto_dispatch(crp);
193 	KERNEL_UNLOCK();
194 	return error;
195 }
196 
197 int
198 ipcomp_input_cb(struct tdb *tdb, struct tdb_crypto *tc, struct mbuf *m, int clen)
199 {
200 	int skip, protoff, roff, hlen = IPCOMP_HLENGTH;
201 	u_int8_t nproto;
202 	u_int64_t ibytes;
203 	struct mbuf *m1, *mo;
204 	struct ipcomp  *ipcomp;
205 	caddr_t addr;
206 #ifdef ENCDEBUG
207 	char buf[INET6_ADDRSTRLEN];
208 #endif
209 
210 	NET_ASSERT_LOCKED();
211 
212 	skip = tc->tc_skip;
213 	protoff = tc->tc_protoff;
214 
215 	/* update the counters */
216 	ibytes = m->m_pkthdr.len - (skip + hlen);
217 	tdb->tdb_cur_bytes += ibytes;
218 	tdb->tdb_ibytes += ibytes;
219 	ipcompstat_add(ipcomps_ibytes, ibytes);
220 
221 	/* Hard expiration */
222 	if ((tdb->tdb_flags & TDBF_BYTES) &&
223 	    (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes)) {
224 		pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD);
225 		tdb_delete(tdb);
226 		goto baddone;
227 	}
228 	/* Notify on soft expiration */
229 	if ((tdb->tdb_flags & TDBF_SOFT_BYTES) &&
230 	    (tdb->tdb_cur_bytes >= tdb->tdb_soft_bytes)) {
231 		pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT);
232 		tdb->tdb_flags &= ~TDBF_SOFT_BYTES;	/* Turn off checking */
233 	}
234 
235 	/* In case it's not done already, adjust the size of the mbuf chain */
236 	m->m_pkthdr.len = clen + hlen + skip;
237 
238 	if ((m->m_len < skip + hlen) && (m = m_pullup(m, skip + hlen)) == 0) {
239 		ipcompstat_inc(ipcomps_hdrops);
240 		goto baddone;
241 	}
242 
243 	/* Find the beginning of the IPCOMP header */
244 	m1 = m_getptr(m, skip, &roff);
245 	if (m1 == NULL) {
246 		DPRINTF("bad mbuf chain, IPCA %s/%08x",
247 		    ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
248 		    ntohl(tdb->tdb_spi));
249 		ipcompstat_inc(ipcomps_hdrops);
250 		goto baddone;
251 	}
252 	/* Keep the next protocol field */
253 	addr = (caddr_t) mtod(m, struct ip *) + skip;
254 	ipcomp = (struct ipcomp *) addr;
255 	nproto = ipcomp->ipcomp_nh;
256 
257 	/* Remove the IPCOMP header from the mbuf */
258 	if (roff == 0) {
259 		/* The IPCOMP header is at the beginning of m1 */
260 		m_adj(m1, hlen);
261 		/*
262 		 * If m1 is the first mbuf, it has set M_PKTHDR and m_adj()
263 		 * has already adjusted the packet header length for us.
264 		 */
265 		if (m1 != m)
266 			m->m_pkthdr.len -= hlen;
267 	} else if (roff + hlen >= m1->m_len) {
268 		int adjlen;
269 
270 		if (roff + hlen > m1->m_len) {
271 			adjlen = roff + hlen - m1->m_len;
272 
273 			/* Adjust the next mbuf by the remainder */
274 			m_adj(m1->m_next, adjlen);
275 
276 			/*
277 			 * The second mbuf is guaranteed not to have a
278 			 * pkthdr...
279 			 */
280 			m->m_pkthdr.len -= adjlen;
281 		}
282 		/* Now, let's unlink the mbuf chain for a second... */
283 		mo = m1->m_next;
284 		m1->m_next = NULL;
285 
286 		/* ...and trim the end of the first part of the chain...sick */
287 		adjlen = m1->m_len - roff;
288 		m_adj(m1, -adjlen);
289 		/*
290 		 * If m1 is the first mbuf, it has set M_PKTHDR and m_adj()
291 		 * has already adjusted the packet header length for us.
292 		 */
293 		if (m1 != m)
294 			m->m_pkthdr.len -= adjlen;
295 
296 		/* Finally, let's relink */
297 		m1->m_next = mo;
298 	} else {
299 		memmove(mtod(m1, u_char *) + roff,
300 		    mtod(m1, u_char *) + roff + hlen,
301 		    m1->m_len - (roff + hlen));
302 		m1->m_len -= hlen;
303 		m->m_pkthdr.len -= hlen;
304 	}
305 
306 	/* Release the crypto descriptors */
307 	free(tc, M_XDATA, 0);
308 
309 	/* Restore the Next Protocol field */
310 	m_copyback(m, protoff, sizeof(u_int8_t), &nproto, M_NOWAIT);
311 
312 	/* Back to generic IPsec input processing */
313 	return ipsec_common_input_cb(m, tdb, skip, protoff);
314 
315  baddone:
316 	m_freem(m);
317 	free(tc, M_XDATA, 0);
318 	return -1;
319 }
320 
321 /*
322  * IPComp output routine, called by ipsp_process_packet()
323  */
324 int
325 ipcomp_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip,
326     int protoff)
327 {
328 	const struct comp_algo *ipcompx = tdb->tdb_compalgxform;
329 	int error, hlen;
330 	struct cryptodesc *crdc = NULL;
331 	struct cryptop *crp = NULL;
332 	struct tdb_crypto *tc;
333 	struct mbuf    *mi;
334 #ifdef ENCDEBUG
335 	char buf[INET6_ADDRSTRLEN];
336 #endif
337 #if NBPFILTER > 0
338 	struct ifnet *encif;
339 
340 	if ((encif = enc_getif(0, tdb->tdb_tap)) != NULL) {
341 		encif->if_opackets++;
342 		encif->if_obytes += m->m_pkthdr.len;
343 
344 		if (encif->if_bpf) {
345 			struct enchdr hdr;
346 
347 			memset(&hdr, 0, sizeof(hdr));
348 
349 			hdr.af = tdb->tdb_dst.sa.sa_family;
350 			hdr.spi = tdb->tdb_spi;
351 
352 			bpf_mtap_hdr(encif->if_bpf, (char *)&hdr,
353 			    ENC_HDRLEN, m, BPF_DIRECTION_OUT);
354 		}
355 	}
356 #endif
357 	hlen = IPCOMP_HLENGTH;
358 
359 	ipcompstat_inc(ipcomps_output);
360 
361 	switch (tdb->tdb_dst.sa.sa_family) {
362 	case AF_INET:
363 		/* Check for IPv4 maximum packet size violations */
364 		/*
365 		 * Since compression is going to reduce the size, no need to
366 		 * worry
367 		 */
368 		if (m->m_pkthdr.len + hlen > IP_MAXPACKET) {
369 			DPRINTF("packet in IPCA %s/%08x got too big",
370 			    ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
371 			    ntohl(tdb->tdb_spi));
372 			ipcompstat_inc(ipcomps_toobig);
373 			error = EMSGSIZE;
374 			goto drop;
375 		}
376 		break;
377 
378 #ifdef INET6
379 	case AF_INET6:
380 		/* Check for IPv6 maximum packet size violations */
381 		if (m->m_pkthdr.len + hlen > IPV6_MAXPACKET) {
382 			DPRINTF("packet in IPCA %s/%08x got too big",
383 			    ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
384 			    ntohl(tdb->tdb_spi));
385 			ipcompstat_inc(ipcomps_toobig);
386 			error = EMSGSIZE;
387 			goto drop;
388 		}
389 		break;
390 #endif /* INET6 */
391 
392 	default:
393 		DPRINTF("unknown/unsupported protocol family %d, IPCA %s/%08x",
394 		    tdb->tdb_dst.sa.sa_family,
395 		    ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
396 		    ntohl(tdb->tdb_spi));
397 		ipcompstat_inc(ipcomps_nopf);
398 		error = EPFNOSUPPORT;
399 		goto drop;
400 	}
401 
402 	/* Update the counters */
403 
404 	tdb->tdb_cur_bytes += m->m_pkthdr.len - skip;
405 	ipcompstat_add(ipcomps_obytes, m->m_pkthdr.len - skip);
406 
407 	/* Hard byte expiration */
408 	if ((tdb->tdb_flags & TDBF_BYTES) &&
409 	    (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes)) {
410 		pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD);
411 		tdb_delete(tdb);
412 		error = EINVAL;
413 		goto drop;
414 	}
415 	/* Soft byte expiration */
416 	if ((tdb->tdb_flags & TDBF_SOFT_BYTES) &&
417 	    (tdb->tdb_cur_bytes >= tdb->tdb_soft_bytes)) {
418 		pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT);
419 		tdb->tdb_flags &= ~TDBF_SOFT_BYTES;	/* Turn off checking */
420 	}
421 	/*
422 	 * Loop through mbuf chain; if we find a readonly mbuf,
423 	 * copy the packet.
424 	 */
425 	mi = m;
426 	while (mi != NULL && !M_READONLY(mi))
427 		mi = mi->m_next;
428 
429 	if (mi != NULL) {
430 		struct mbuf *n = m_dup_pkt(m, 0, M_DONTWAIT);
431 
432 		if (n == NULL) {
433 			DPRINTF("bad mbuf chain, IPCA %s/%08x",
434 			    ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
435 			    ntohl(tdb->tdb_spi));
436 			ipcompstat_inc(ipcomps_hdrops);
437 			error = ENOBUFS;
438 			goto drop;
439 		}
440 
441 		m_freem(m);
442 		m = n;
443 	}
444 	/* Ok now, we can pass to the crypto processing */
445 
446 	/* Get crypto descriptors */
447 	crp = crypto_getreq(1);
448 	if (crp == NULL) {
449 		DPRINTF("failed to acquire crypto descriptors");
450 		ipcompstat_inc(ipcomps_crypto);
451 		error = ENOBUFS;
452 		goto drop;
453 	}
454 	crdc = &crp->crp_desc[0];
455 
456 	/* Compression descriptor */
457 	crdc->crd_skip = skip;
458 	crdc->crd_len = m->m_pkthdr.len - skip;
459 	crdc->crd_flags = CRD_F_COMP;
460 	crdc->crd_inject = skip;
461 
462 	/* Compression operation */
463 	crdc->crd_alg = ipcompx->type;
464 
465 	/* IPsec-specific opaque crypto info */
466 	tc = malloc(sizeof(*tc), M_XDATA, M_NOWAIT | M_ZERO);
467 	if (tc == NULL) {
468 		DPRINTF("failed to allocate tdb_crypto");
469 		ipcompstat_inc(ipcomps_crypto);
470 		error = ENOBUFS;
471 		goto drop;
472 	}
473 
474 	tc->tc_spi = tdb->tdb_spi;
475 	tc->tc_proto = tdb->tdb_sproto;
476 	tc->tc_skip = skip;
477 	tc->tc_rdomain = tdb->tdb_rdomain;
478 	tc->tc_dst = tdb->tdb_dst;
479 
480 	/* Crypto operation descriptor */
481 	crp->crp_ilen = m->m_pkthdr.len;	/* Total input length */
482 	crp->crp_flags = CRYPTO_F_IMBUF;
483 	crp->crp_buf = (caddr_t)m;
484 	crp->crp_callback = ipsec_output_cb;
485 	crp->crp_opaque = (caddr_t)tc;
486 	crp->crp_sid = tdb->tdb_cryptoid;
487 
488 	KERNEL_LOCK();
489 	error = crypto_dispatch(crp);
490 	KERNEL_UNLOCK();
491 	return error;
492 
493  drop:
494 	m_freem(m);
495 	crypto_freereq(crp);
496 	return error;
497 }
498 
499 /*
500  * IPComp output callback.
501  */
502 int
503 ipcomp_output_cb(struct tdb *tdb, struct tdb_crypto *tc, struct mbuf *m,
504     int ilen, int olen)
505 {
506 	struct mbuf *mo;
507 	int skip, rlen, roff;
508 	u_int16_t cpi;
509 	struct ip *ip;
510 #ifdef INET6
511 	struct ip6_hdr *ip6;
512 #endif
513 	struct ipcomp  *ipcomp;
514 #ifdef ENCDEBUG
515 	char buf[INET6_ADDRSTRLEN];
516 #endif
517 
518 	skip = tc->tc_skip;
519 	rlen = ilen - skip;
520 
521 	/* Check sizes. */
522 	if (rlen <= olen + IPCOMP_HLENGTH) {
523 		/* Compression was useless, we have lost time. */
524 		ipcompstat_inc(ipcomps_minlen); /* misnomer, but like to count */
525 		goto skiphdr;
526 	}
527 
528 	/* Inject IPCOMP header */
529 	mo = m_makespace(m, skip, IPCOMP_HLENGTH, &roff);
530 	if (mo == NULL) {
531 		DPRINTF("ailed to inject IPCOMP header for IPCA %s/%08x",
532 		    ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
533 		    ntohl(tdb->tdb_spi));
534 		ipcompstat_inc(ipcomps_wrap);
535 		goto baddone;
536 	}
537 
538 	/* Initialize the IPCOMP header */
539 	ipcomp = (struct ipcomp *)(mtod(mo, caddr_t) + roff);
540 	memset(ipcomp, 0, sizeof(struct ipcomp));
541 	cpi = (u_int16_t) ntohl(tdb->tdb_spi);
542 	ipcomp->ipcomp_cpi = htons(cpi);
543 
544 	/* m_pullup before ? */
545 	switch (tdb->tdb_dst.sa.sa_family) {
546 	case AF_INET:
547 		ip = mtod(m, struct ip *);
548 		ipcomp->ipcomp_nh = ip->ip_p;
549 		ip->ip_p = IPPROTO_IPCOMP;
550 		break;
551 #ifdef INET6
552 	case AF_INET6:
553 		ip6 = mtod(m, struct ip6_hdr *);
554 		ipcomp->ipcomp_nh = ip6->ip6_nxt;
555 		ip6->ip6_nxt = IPPROTO_IPCOMP;
556 		break;
557 #endif
558 	default:
559 		DPRINTF("unsupported protocol family %d, IPCA %s/%08x",
560 		    tdb->tdb_dst.sa.sa_family,
561 		    ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
562 		    ntohl(tdb->tdb_spi));
563 		ipcompstat_inc(ipcomps_nopf);
564 		goto baddone;
565 	}
566 
567  skiphdr:
568 	/* Release the crypto descriptor. */
569 	free(tc, M_XDATA, 0);
570 
571 	if (ipsp_process_done(m, tdb)) {
572 		ipcompstat_inc(ipcomps_outfail);
573 		return -1;
574 	}
575 	return 0;
576 
577  baddone:
578 	m_freem(m);
579 	free(tc, M_XDATA, 0);
580 	return -1;
581 }
582