1 /* $NetBSD: ah_output.c,v 1.24 2003/09/07 15:59:36 itojun Exp $ */
2 /* $KAME: ah_output.c,v 1.31 2001/07/26 06:53:15 jinmei Exp $ */
3
4 /*
5 * Copyright (C) 1995, 1996, 1997, and 1998 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 * RFC1826/2402 authentication header.
35 */
36
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: ah_output.c,v 1.24 2003/09/07 15:59:36 itojun Exp $");
39
40 #include "opt_inet.h"
41
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/malloc.h>
45 #include <sys/mbuf.h>
46 #include <sys/domain.h>
47 #include <sys/protosw.h>
48 #include <sys/socket.h>
49 #include <sys/socketvar.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
58 #include <netinet/in.h>
59
60 #include <netinet/in_systm.h>
61 #include <netinet/ip.h>
62 #include <netinet/in_var.h>
63
64 #ifdef INET6
65 #include <netinet/ip6.h>
66 #include <netinet6/ip6_var.h>
67 #include <netinet/icmp6.h>
68 #endif
69
70 #include <netinet6/ipsec.h>
71 #include <netinet6/ah.h>
72 #include <netkey/key.h>
73 #include <netkey/keydb.h>
74
75 #include <net/net_osdep.h>
76
77 #ifdef INET
78 static struct in_addr *ah4_finaldst __P((struct mbuf *));
79 #endif
80
81 /*
82 * compute AH header size.
83 * transport mode only. for tunnel mode, we should implement
84 * virtual interface, and control MTU/MSS by the interface MTU.
85 */
86 size_t
87 ah_hdrsiz(isr)
88 struct ipsecrequest *isr;
89 {
90 const struct ah_algorithm *algo;
91 size_t hdrsiz;
92
93 /* sanity check */
94 if (isr == NULL)
95 panic("ah_hdrsiz: NULL was passed.");
96
97 if (isr->saidx.proto != IPPROTO_AH)
98 panic("unsupported mode passed to ah_hdrsiz");
99
100 if (isr->sav == NULL)
101 goto estimate;
102 if (isr->sav->state != SADB_SASTATE_MATURE
103 && isr->sav->state != SADB_SASTATE_DYING)
104 goto estimate;
105
106 /* we need transport mode AH. */
107 algo = ah_algorithm_lookup(isr->sav->alg_auth);
108 if (!algo)
109 goto estimate;
110
111 /*
112 * XXX
113 * right now we don't calcurate the padding size. simply
114 * treat the padding size as constant, for simplicity.
115 *
116 * XXX variable size padding support
117 */
118 hdrsiz = (((*algo->sumsiz)(isr->sav) + 3) & ~(4 - 1));
119 if (isr->sav->flags & SADB_X_EXT_OLD)
120 hdrsiz += sizeof(struct ah);
121 else
122 hdrsiz += sizeof(struct newah);
123
124 return hdrsiz;
125
126 estimate:
127 /* ASSUMING:
128 * sizeof(struct newah) > sizeof(struct ah).
129 * AH_MAXSUMSIZE is multiple of 4.
130 */
131 return sizeof(struct newah) + AH_MAXSUMSIZE;
132 }
133
134 #ifdef INET
135 /*
136 * Modify the packet so that it includes the authentication data.
137 * The mbuf passed must start with IPv4 header.
138 *
139 * assumes that the first mbuf contains IPv4 header + option only.
140 * the function does not modify m.
141 */
142 int
143 ah4_output(m, isr)
144 struct mbuf *m;
145 struct ipsecrequest *isr;
146 {
147 struct secasvar *sav = isr->sav;
148 const struct ah_algorithm *algo;
149 u_int32_t spi;
150 u_char *ahdrpos;
151 u_int8_t *ahsumpos = NULL;
152 size_t hlen = 0; /* IP header+option in bytes */
153 size_t plen = 0; /* AH payload size in bytes */
154 size_t ahlen = 0; /* plen + sizeof(ah) */
155 struct ip *ip;
156 struct in_addr dst;
157 struct in_addr *finaldst;
158 int error;
159
160 /* sanity checks */
161 if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) {
162 struct ip *ip;
163
164 ip = mtod(m, struct ip *);
165 ipseclog((LOG_DEBUG, "ah4_output: internal error: "
166 "sav->replay is null: %x->%x, SPI=%u\n",
167 (u_int32_t)ntohl(ip->ip_src.s_addr),
168 (u_int32_t)ntohl(ip->ip_dst.s_addr),
169 (u_int32_t)ntohl(sav->spi)));
170 ipsecstat.out_inval++;
171 m_freem(m);
172 return EINVAL;
173 }
174
175 algo = ah_algorithm_lookup(sav->alg_auth);
176 if (!algo) {
177 ipseclog((LOG_ERR, "ah4_output: unsupported algorithm: "
178 "SPI=%u\n", (u_int32_t)ntohl(sav->spi)));
179 ipsecstat.out_inval++;
180 m_freem(m);
181 return EINVAL;
182 }
183 spi = sav->spi;
184
185 /*
186 * determine the size to grow.
187 */
188 if (sav->flags & SADB_X_EXT_OLD) {
189 /* RFC 1826 */
190 plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /* XXX pad to 8byte? */
191 ahlen = plen + sizeof(struct ah);
192 } else {
193 /* RFC 2402 */
194 plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /* XXX pad to 8byte? */
195 ahlen = plen + sizeof(struct newah);
196 }
197
198 /*
199 * grow the mbuf to accomodate AH.
200 */
201 ip = mtod(m, struct ip *);
202 hlen = ip->ip_hl << 2;
203
204 if (m->m_len != hlen)
205 panic("ah4_output: assumption failed (first mbuf length)");
206 if (M_LEADINGSPACE(m->m_next) < ahlen) {
207 struct mbuf *n;
208 MGET(n, M_DONTWAIT, MT_DATA);
209 if (!n) {
210 ipseclog((LOG_DEBUG, "ENOBUFS in ah4_output %d\n",
211 __LINE__));
212 m_freem(m);
213 return ENOBUFS;
214 }
215 n->m_len = ahlen;
216 n->m_next = m->m_next;
217 m->m_next = n;
218 m->m_pkthdr.len += ahlen;
219 ahdrpos = mtod(n, u_char *);
220 } else {
221 m->m_next->m_len += ahlen;
222 m->m_next->m_data -= ahlen;
223 m->m_pkthdr.len += ahlen;
224 ahdrpos = mtod(m->m_next, u_char *);
225 }
226
227 ip = mtod(m, struct ip *); /* just to be sure */
228
229 /*
230 * initialize AH.
231 */
232 if (sav->flags & SADB_X_EXT_OLD) {
233 struct ah *ahdr;
234
235 ahdr = (struct ah *)ahdrpos;
236 ahsumpos = (u_char *)(ahdr + 1);
237 ahdr->ah_len = plen >> 2;
238 ahdr->ah_nxt = ip->ip_p;
239 ahdr->ah_reserve = htons(0);
240 ahdr->ah_spi = spi;
241 bzero(ahdr + 1, plen);
242 } else {
243 struct newah *ahdr;
244
245 ahdr = (struct newah *)ahdrpos;
246 ahsumpos = (u_char *)(ahdr + 1);
247 ahdr->ah_len = (plen >> 2) + 1; /* plus one for seq# */
248 ahdr->ah_nxt = ip->ip_p;
249 ahdr->ah_reserve = htons(0);
250 ahdr->ah_spi = spi;
251 if (sav->replay->count == ~0) {
252 if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
253 /* XXX Is it noisy ? */
254 ipseclog((LOG_WARNING,
255 "replay counter overflowed. %s\n",
256 ipsec_logsastr(sav)));
257 ipsecstat.out_inval++;
258 m_freem(m);
259 return EINVAL;
260 }
261 }
262 sav->replay->count++;
263 /*
264 * XXX sequence number must not be cycled, if the SA is
265 * installed by IKE daemon.
266 */
267 ahdr->ah_seq = htonl(sav->replay->count & 0xffffffff);
268 bzero(ahdr + 1, plen);
269 }
270
271 /*
272 * modify IPv4 header.
273 */
274 ip->ip_p = IPPROTO_AH;
275 if (ahlen < (IP_MAXPACKET - ntohs(ip->ip_len)))
276 ip->ip_len = htons(ntohs(ip->ip_len) + ahlen);
277 else {
278 ipseclog((LOG_ERR, "IPv4 AH output: size exceeds limit\n"));
279 ipsecstat.out_inval++;
280 m_freem(m);
281 return EMSGSIZE;
282 }
283
284 /*
285 * If there is source routing option, update destination field in
286 * the IPv4 header to the final destination.
287 * Note that we do not need to update source routing option itself
288 * (as done in IPv4 AH processing -- see ip6_output()), since
289 * source routing option is not part of the ICV computation.
290 */
291 finaldst = ah4_finaldst(m);
292 if (finaldst) {
293 dst.s_addr = ip->ip_dst.s_addr;
294 ip->ip_dst.s_addr = finaldst->s_addr;
295 }
296
297 /*
298 * calcurate the checksum, based on security association
299 * and the algorithm specified.
300 */
301 error = ah4_calccksum(m, ahsumpos, plen, algo, sav);
302 if (error) {
303 ipseclog((LOG_ERR,
304 "error after ah4_calccksum, called from ah4_output"));
305 m_freem(m);
306 m = NULL;
307 ipsecstat.out_inval++;
308 return error;
309 }
310
311 if (finaldst) {
312 ip = mtod(m, struct ip *); /* just to make sure */
313 ip->ip_dst.s_addr = dst.s_addr;
314 }
315 ipsecstat.out_success++;
316 ipsecstat.out_ahhist[sav->alg_auth]++;
317 key_sa_recordxfer(sav, m);
318
319 return 0;
320 }
321 #endif
322
323 /* Calculate AH length */
324 int
325 ah_hdrlen(sav)
326 struct secasvar *sav;
327 {
328 const struct ah_algorithm *algo;
329 int plen, ahlen;
330
331 algo = ah_algorithm_lookup(sav->alg_auth);
332 if (!algo)
333 return 0;
334 if (sav->flags & SADB_X_EXT_OLD) {
335 /* RFC 1826 */
336 plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /* XXX pad to 8byte? */
337 ahlen = plen + sizeof(struct ah);
338 } else {
339 /* RFC 2402 */
340 plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /* XXX pad to 8byte? */
341 ahlen = plen + sizeof(struct newah);
342 }
343
344 return (ahlen);
345 }
346
347 #ifdef INET6
348 /*
349 * Fill in the Authentication Header and calculate checksum.
350 */
351 int
352 ah6_output(m, nexthdrp, md, isr)
353 struct mbuf *m;
354 u_char *nexthdrp;
355 struct mbuf *md;
356 struct ipsecrequest *isr;
357 {
358 struct mbuf *mprev;
359 struct mbuf *mah;
360 struct secasvar *sav = isr->sav;
361 const struct ah_algorithm *algo;
362 u_int32_t spi;
363 u_int8_t *ahsumpos = NULL;
364 size_t plen; /* AH payload size in bytes */
365 int error = 0;
366 int ahlen;
367 struct ip6_hdr *ip6;
368
369 if (m->m_len < sizeof(struct ip6_hdr)) {
370 ipseclog((LOG_DEBUG, "ah6_output: first mbuf too short\n"));
371 m_freem(m);
372 return EINVAL;
373 }
374
375 ahlen = ah_hdrlen(sav);
376 if (ahlen == 0)
377 return 0;
378
379 for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next)
380 ;
381 if (!mprev || mprev->m_next != md) {
382 ipseclog((LOG_DEBUG, "ah6_output: md is not in chain\n"));
383 m_freem(m);
384 return EINVAL;
385 }
386
387 MGET(mah, M_DONTWAIT, MT_DATA);
388 if (!mah) {
389 m_freem(m);
390 return ENOBUFS;
391 }
392 if (ahlen > MLEN) {
393 MCLGET(mah, M_DONTWAIT);
394 if ((mah->m_flags & M_EXT) == 0) {
395 m_free(mah);
396 m_freem(m);
397 return ENOBUFS;
398 }
399 }
400 mah->m_len = ahlen;
401 mah->m_next = md;
402 mprev->m_next = mah;
403 m->m_pkthdr.len += ahlen;
404
405 /* fix plen */
406 if (m->m_pkthdr.len - sizeof(struct ip6_hdr) > IPV6_MAXPACKET) {
407 ipseclog((LOG_ERR,
408 "ah6_output: AH with IPv6 jumbogram is not supported\n"));
409 m_freem(m);
410 return EINVAL;
411 }
412 ip6 = mtod(m, struct ip6_hdr *);
413 ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
414
415 if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) {
416 ipseclog((LOG_DEBUG, "ah6_output: internal error: "
417 "sav->replay is null: SPI=%u\n",
418 (u_int32_t)ntohl(sav->spi)));
419 ipsec6stat.out_inval++;
420 m_freem(m);
421 return EINVAL;
422 }
423
424 algo = ah_algorithm_lookup(sav->alg_auth);
425 if (!algo) {
426 ipseclog((LOG_ERR, "ah6_output: unsupported algorithm: "
427 "SPI=%u\n", (u_int32_t)ntohl(sav->spi)));
428 ipsec6stat.out_inval++;
429 m_freem(m);
430 return EINVAL;
431 }
432 spi = sav->spi;
433
434 /*
435 * initialize AH.
436 */
437 if (sav->flags & SADB_X_EXT_OLD) {
438 struct ah *ahdr = mtod(mah, struct ah *);
439
440 plen = mah->m_len - sizeof(struct ah);
441 ahsumpos = (u_char *)(ahdr + 1);
442 ahdr->ah_nxt = *nexthdrp;
443 *nexthdrp = IPPROTO_AH;
444 ahdr->ah_len = plen >> 2;
445 ahdr->ah_reserve = htons(0);
446 ahdr->ah_spi = spi;
447 bzero(ahdr + 1, plen);
448 } else {
449 struct newah *ahdr = mtod(mah, struct newah *);
450
451 plen = mah->m_len - sizeof(struct newah);
452 ahsumpos = (u_char *)(ahdr + 1);
453 ahdr->ah_nxt = *nexthdrp;
454 *nexthdrp = IPPROTO_AH;
455 ahdr->ah_len = (plen >> 2) + 1; /* plus one for seq# */
456 ahdr->ah_reserve = htons(0);
457 ahdr->ah_spi = spi;
458 if (sav->replay->count == ~0) {
459 if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
460 /* XXX Is it noisy ? */
461 ipseclog((LOG_WARNING,
462 "replay counter overflowed. %s\n",
463 ipsec_logsastr(sav)));
464 ipsec6stat.out_inval++;
465 m_freem(m);
466 return EINVAL;
467 }
468 }
469 sav->replay->count++;
470 /*
471 * XXX sequence number must not be cycled, if the SA is
472 * installed by IKE daemon.
473 */
474 ahdr->ah_seq = htonl(sav->replay->count);
475 bzero(ahdr + 1, plen);
476 }
477
478 /*
479 * calcurate the checksum, based on security association
480 * and the algorithm specified.
481 */
482 error = ah6_calccksum(m, ahsumpos, plen, algo, sav);
483 if (error) {
484 ipsec6stat.out_inval++;
485 m_freem(m);
486 } else {
487 ipsec6stat.out_success++;
488 key_sa_recordxfer(sav, m);
489 }
490 ipsec6stat.out_ahhist[sav->alg_auth]++;
491
492 return (error);
493 }
494 #endif
495
496 #ifdef INET
497 /*
498 * Find the final destination if there is loose/strict source routing option.
499 * Returns NULL if there's no source routing options.
500 * Returns NULL on errors too.
501 * Note that this function will return a pointer INTO the given parameter,
502 * struct mbuf *m.
503 * The mbuf must be pulled up toward, at least, ip option part.
504 */
505 static struct in_addr *
506 ah4_finaldst(m)
507 struct mbuf *m;
508 {
509 struct ip *ip;
510 int optlen;
511 u_char *q;
512 int i;
513 int hlen;
514
515 if (!m)
516 panic("ah4_finaldst: m == NULL");
517 ip = mtod(m, struct ip *);
518 hlen = (ip->ip_hl << 2);
519
520 if (m->m_len < hlen) {
521 ipseclog((LOG_DEBUG,
522 "ah4_finaldst: parameter mbuf wrong (not pulled up)\n"));
523 return NULL;
524 }
525
526 if (hlen == sizeof(struct ip))
527 return NULL;
528
529 optlen = hlen - sizeof(struct ip);
530 if (optlen < 0) {
531 ipseclog((LOG_DEBUG, "ah4_finaldst: wrong optlen %d\n",
532 optlen));
533 return NULL;
534 }
535
536 q = (u_char *)(ip + 1);
537 i = 0;
538 while (i < optlen) {
539 if (i + IPOPT_OPTVAL >= optlen)
540 return NULL;
541 if (q[i + IPOPT_OPTVAL] == IPOPT_EOL ||
542 q[i + IPOPT_OPTVAL] == IPOPT_NOP ||
543 i + IPOPT_OLEN < optlen)
544 ;
545 else
546 return NULL;
547
548 switch (q[i + IPOPT_OPTVAL]) {
549 case IPOPT_EOL:
550 i = optlen; /* bye */
551 break;
552 case IPOPT_NOP:
553 i++;
554 break;
555 case IPOPT_LSRR:
556 case IPOPT_SSRR:
557 if (q[i + IPOPT_OLEN] < 2 + sizeof(struct in_addr) ||
558 optlen - i < q[i + IPOPT_OLEN]) {
559 ipseclog((LOG_ERR,
560 "ip_finaldst: invalid IP option "
561 "(code=%02x len=%02x)\n",
562 q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN]));
563 return NULL;
564 }
565 i += q[i + IPOPT_OLEN] - sizeof(struct in_addr);
566 return (struct in_addr *)(q + i);
567 default:
568 if (q[i + IPOPT_OLEN] < 2 ||
569 optlen - i < q[i + IPOPT_OLEN]) {
570 ipseclog((LOG_ERR,
571 "ip_finaldst: invalid IP option "
572 "(code=%02x len=%02x)\n",
573 q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN]));
574 return NULL;
575 }
576 i += q[i + IPOPT_OLEN];
577 break;
578 }
579 }
580 return NULL;
581 }
582 #endif
Cache object: 79d5f1f1c81fd95041f0b0f182a4498f
|