1 /* $NetBSD: ah_output.c,v 1.28.2.1 2007/09/25 00:45:48 xtraeme 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.28.2.1 2007/09/25 00:45:48 xtraeme 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 dst.s_addr = 0; /* XXX: GCC */
160
161 /* sanity checks */
162 if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) {
163 ip = mtod(m, struct ip *);
164 ipseclog((LOG_DEBUG, "ah4_output: internal error: "
165 "sav->replay is null: %x->%x, SPI=%u\n",
166 (u_int32_t)ntohl(ip->ip_src.s_addr),
167 (u_int32_t)ntohl(ip->ip_dst.s_addr),
168 (u_int32_t)ntohl(sav->spi)));
169 ipsecstat.out_inval++;
170 error = EINVAL;
171 goto fail;
172 }
173
174 algo = ah_algorithm_lookup(sav->alg_auth);
175 if (!algo) {
176 ipseclog((LOG_ERR, "ah4_output: unsupported algorithm: "
177 "SPI=%u\n", (u_int32_t)ntohl(sav->spi)));
178 ipsecstat.out_inval++;
179 error = EINVAL;
180 goto fail;
181 }
182 spi = sav->spi;
183
184 /*
185 * determine the size to grow.
186 */
187 if (sav->flags & SADB_X_EXT_OLD) {
188 /* RFC 1826 */
189 plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /* XXX pad to 8byte? */
190 ahlen = plen + sizeof(struct ah);
191 } else {
192 /* RFC 2402 */
193 plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /* XXX pad to 8byte? */
194 ahlen = plen + sizeof(struct newah);
195 }
196
197 /*
198 * grow the mbuf to accommodate AH.
199 */
200 ip = mtod(m, struct ip *);
201 hlen = ip->ip_hl << 2;
202
203 if (m->m_len != hlen)
204 panic("ah4_output: assumption failed (first mbuf length)");
205 if (M_LEADINGSPACE(m->m_next) < ahlen) {
206 struct mbuf *n;
207 MGET(n, M_DONTWAIT, MT_DATA);
208 if (!n) {
209 ipseclog((LOG_DEBUG, "ENOBUFS in ah4_output %d\n",
210 __LINE__));
211 error = ENOBUFS;
212 goto fail;
213 }
214 n->m_len = ahlen;
215 n->m_next = m->m_next;
216 m->m_next = n;
217 m->m_pkthdr.len += ahlen;
218 ahdrpos = mtod(n, u_char *);
219 } else {
220 m->m_next->m_len += ahlen;
221 m->m_next->m_data -= ahlen;
222 m->m_pkthdr.len += ahlen;
223 ahdrpos = mtod(m->m_next, u_char *);
224 }
225
226 ip = mtod(m, struct ip *); /* just to be sure */
227
228 /*
229 * initialize AH.
230 */
231 if (sav->flags & SADB_X_EXT_OLD) {
232 struct ah *ahdr;
233
234 ahdr = (struct ah *)ahdrpos;
235 ahsumpos = (u_char *)(ahdr + 1);
236 ahdr->ah_len = plen >> 2;
237 ahdr->ah_nxt = ip->ip_p;
238 ahdr->ah_reserve = htons(0);
239 ahdr->ah_spi = spi;
240 bzero(ahdr + 1, plen);
241 } else {
242 struct newah *ahdr;
243
244 ahdr = (struct newah *)ahdrpos;
245 ahsumpos = (u_char *)(ahdr + 1);
246 ahdr->ah_len = (plen >> 2) + 1; /* plus one for seq# */
247 ahdr->ah_nxt = ip->ip_p;
248 ahdr->ah_reserve = htons(0);
249 ahdr->ah_spi = spi;
250 if (sav->replay->count == ~0) {
251 if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
252 /* XXX Is it noisy ? */
253 ipseclog((LOG_WARNING,
254 "replay counter overflowed. %s\n",
255 ipsec_logsastr(sav)));
256 ipsecstat.out_inval++;
257 error = EINVAL;
258 goto fail;
259 }
260 }
261 sav->replay->count++;
262 /*
263 * XXX sequence number must not be cycled, if the SA is
264 * installed by IKE daemon.
265 */
266 ahdr->ah_seq = htonl(sav->replay->count & 0xffffffff);
267 bzero(ahdr + 1, plen);
268 }
269
270 /*
271 * modify IPv4 header.
272 */
273 ip->ip_p = IPPROTO_AH;
274 if (ahlen < (IP_MAXPACKET - ntohs(ip->ip_len)))
275 ip->ip_len = htons(ntohs(ip->ip_len) + ahlen);
276 else {
277 ipseclog((LOG_ERR, "IPv4 AH output: size exceeds limit\n"));
278 ipsecstat.out_inval++;
279 error = EMSGSIZE;
280 goto fail;
281 }
282
283 /*
284 * If there is source routing option, update destination field in
285 * the IPv4 header to the final destination.
286 * Note that we do not need to update source routing option itself
287 * (as done in IPv4 AH processing -- see ip6_output()), since
288 * source routing option is not part of the ICV computation.
289 */
290 finaldst = ah4_finaldst(m);
291 if (finaldst) {
292 dst.s_addr = ip->ip_dst.s_addr;
293 ip->ip_dst.s_addr = finaldst->s_addr;
294 }
295
296 /*
297 * calcurate the checksum, based on security association
298 * and the algorithm specified.
299 */
300 error = ah4_calccksum(m, ahsumpos, plen, algo, sav);
301 if (error) {
302 ipseclog((LOG_ERR,
303 "error after ah4_calccksum, called from ah4_output"));
304 ipsecstat.out_inval++;
305 goto fail;
306 }
307
308 if (finaldst) {
309 ip = mtod(m, struct ip *); /* just to make sure */
310 ip->ip_dst.s_addr = dst.s_addr;
311 }
312 ipsecstat.out_success++;
313 ipsecstat.out_ahhist[sav->alg_auth]++;
314 key_sa_recordxfer(sav, m);
315
316 return 0;
317
318 fail:
319 m_freem(m);
320 return error;
321 }
322 #endif
323
324 /* Calculate AH length */
325 int
326 ah_hdrlen(sav)
327 struct secasvar *sav;
328 {
329 const struct ah_algorithm *algo;
330 int plen, ahlen;
331
332 algo = ah_algorithm_lookup(sav->alg_auth);
333 if (!algo)
334 return 0;
335 if (sav->flags & SADB_X_EXT_OLD) {
336 /* RFC 1826 */
337 plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /* XXX pad to 8byte? */
338 ahlen = plen + sizeof(struct ah);
339 } else {
340 /* RFC 2402 */
341 plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /* XXX pad to 8byte? */
342 ahlen = plen + sizeof(struct newah);
343 }
344
345 return (ahlen);
346 }
347
348 #ifdef INET6
349 /*
350 * Fill in the Authentication Header and calculate checksum.
351 */
352 int
353 ah6_output(m, nexthdrp, md, isr)
354 struct mbuf *m;
355 u_char *nexthdrp;
356 struct mbuf *md;
357 struct ipsecrequest *isr;
358 {
359 struct mbuf *mprev;
360 struct mbuf *mah;
361 struct secasvar *sav = isr->sav;
362 const struct ah_algorithm *algo;
363 u_int32_t spi;
364 u_int8_t *ahsumpos = NULL;
365 size_t plen; /* AH payload size in bytes */
366 int error = 0;
367 int ahlen;
368 struct ip6_hdr *ip6;
369
370 if (m->m_len < sizeof(struct ip6_hdr)) {
371 ipseclog((LOG_DEBUG, "ah6_output: first mbuf too short\n"));
372 error = EINVAL;
373 goto fail;
374 }
375
376 ahlen = ah_hdrlen(sav);
377 if (ahlen == 0)
378 return 0;
379
380 for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next)
381 ;
382 if (!mprev || mprev->m_next != md) {
383 ipseclog((LOG_DEBUG, "ah6_output: md is not in chain\n"));
384 error = EINVAL;
385 goto fail;
386 }
387
388 MGET(mah, M_DONTWAIT, MT_DATA);
389 if (!mah) {
390 error = ENOBUFS;
391 goto fail;
392 }
393 if (ahlen > MLEN) {
394 MCLGET(mah, M_DONTWAIT);
395 if ((mah->m_flags & M_EXT) == 0) {
396 m_free(mah);
397 error = ENOBUFS;
398 goto fail;
399 }
400 }
401 mah->m_len = ahlen;
402 mah->m_next = md;
403 mprev->m_next = mah;
404 m->m_pkthdr.len += ahlen;
405
406 /* fix plen */
407 if (m->m_pkthdr.len - sizeof(struct ip6_hdr) > IPV6_MAXPACKET) {
408 ipseclog((LOG_ERR,
409 "ah6_output: AH with IPv6 jumbogram is not supported\n"));
410 error = EINVAL;
411 goto fail;
412 }
413 ip6 = mtod(m, struct ip6_hdr *);
414 ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
415
416 if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) {
417 ipseclog((LOG_DEBUG, "ah6_output: internal error: "
418 "sav->replay is null: SPI=%u\n",
419 (u_int32_t)ntohl(sav->spi)));
420 ipsec6stat.out_inval++;
421 error = EINVAL;
422 goto fail;
423 }
424
425 algo = ah_algorithm_lookup(sav->alg_auth);
426 if (!algo) {
427 ipseclog((LOG_ERR, "ah6_output: unsupported algorithm: "
428 "SPI=%u\n", (u_int32_t)ntohl(sav->spi)));
429 ipsec6stat.out_inval++;
430 error = EINVAL;
431 goto fail;
432 }
433 spi = sav->spi;
434
435 /*
436 * initialize AH.
437 */
438 if (sav->flags & SADB_X_EXT_OLD) {
439 struct ah *ahdr = mtod(mah, struct ah *);
440
441 plen = mah->m_len - sizeof(struct ah);
442 ahsumpos = (u_char *)(ahdr + 1);
443 ahdr->ah_nxt = *nexthdrp;
444 *nexthdrp = IPPROTO_AH;
445 ahdr->ah_len = plen >> 2;
446 ahdr->ah_reserve = htons(0);
447 ahdr->ah_spi = spi;
448 bzero(ahdr + 1, plen);
449 } else {
450 struct newah *ahdr = mtod(mah, struct newah *);
451
452 plen = mah->m_len - sizeof(struct newah);
453 ahsumpos = (u_char *)(ahdr + 1);
454 ahdr->ah_nxt = *nexthdrp;
455 *nexthdrp = IPPROTO_AH;
456 ahdr->ah_len = (plen >> 2) + 1; /* plus one for seq# */
457 ahdr->ah_reserve = htons(0);
458 ahdr->ah_spi = spi;
459 if (sav->replay->count == ~0) {
460 if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
461 /* XXX Is it noisy ? */
462 ipseclog((LOG_WARNING,
463 "replay counter overflowed. %s\n",
464 ipsec_logsastr(sav)));
465 ipsec6stat.out_inval++;
466 error = EINVAL;
467 goto fail;
468 }
469 }
470 sav->replay->count++;
471 /*
472 * XXX sequence number must not be cycled, if the SA is
473 * installed by IKE daemon.
474 */
475 ahdr->ah_seq = htonl(sav->replay->count);
476 bzero(ahdr + 1, plen);
477 }
478
479 /*
480 * calcurate the checksum, based on security association
481 * and the algorithm specified.
482 */
483 error = ah6_calccksum(m, ahsumpos, plen, algo, sav);
484 if (error) {
485 ipsec6stat.out_inval++;
486 goto fail;
487 } else {
488 ipsec6stat.out_success++;
489 key_sa_recordxfer(sav, m);
490 }
491 ipsec6stat.out_ahhist[sav->alg_auth]++;
492
493 return 0;
494
495 fail:
496 m_freem(m);
497 return error;
498
499 }
500 #endif
501
502 #ifdef INET
503 /*
504 * Find the final destination if there is loose/strict source routing option.
505 * Returns NULL if there's no source routing options.
506 * Returns NULL on errors too.
507 * Note that this function will return a pointer INTO the given parameter,
508 * struct mbuf *m.
509 * The mbuf must be pulled up toward, at least, ip option part.
510 */
511 static struct in_addr *
512 ah4_finaldst(m)
513 struct mbuf *m;
514 {
515 struct ip *ip;
516 int optlen;
517 u_char *q;
518 int i;
519 int hlen;
520
521 if (!m)
522 panic("ah4_finaldst: m == NULL");
523 ip = mtod(m, struct ip *);
524 hlen = (ip->ip_hl << 2);
525
526 if (m->m_len < hlen) {
527 ipseclog((LOG_DEBUG,
528 "ah4_finaldst: parameter mbuf wrong (not pulled up)\n"));
529 return NULL;
530 }
531
532 if (hlen == sizeof(struct ip))
533 return NULL;
534
535 optlen = hlen - sizeof(struct ip);
536 if (optlen < 0) {
537 ipseclog((LOG_DEBUG, "ah4_finaldst: wrong optlen %d\n",
538 optlen));
539 return NULL;
540 }
541
542 q = (u_char *)(ip + 1);
543 i = 0;
544 while (i < optlen) {
545 if (i + IPOPT_OPTVAL >= optlen)
546 return NULL;
547 if (q[i + IPOPT_OPTVAL] == IPOPT_EOL ||
548 q[i + IPOPT_OPTVAL] == IPOPT_NOP ||
549 i + IPOPT_OLEN < optlen)
550 ;
551 else
552 return NULL;
553
554 switch (q[i + IPOPT_OPTVAL]) {
555 case IPOPT_EOL:
556 i = optlen; /* bye */
557 break;
558 case IPOPT_NOP:
559 i++;
560 break;
561 case IPOPT_LSRR:
562 case IPOPT_SSRR:
563 if (q[i + IPOPT_OLEN] < 2 + sizeof(struct in_addr) ||
564 optlen - i < q[i + IPOPT_OLEN]) {
565 ipseclog((LOG_ERR,
566 "ip_finaldst: invalid IP option "
567 "(code=%02x len=%02x)\n",
568 q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN]));
569 return NULL;
570 }
571 i += q[i + IPOPT_OLEN] - sizeof(struct in_addr);
572 return (struct in_addr *)(q + i);
573 default:
574 if (q[i + IPOPT_OLEN] < 2 ||
575 optlen - i < q[i + IPOPT_OLEN]) {
576 ipseclog((LOG_ERR,
577 "ip_finaldst: invalid IP option "
578 "(code=%02x len=%02x)\n",
579 q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN]));
580 return NULL;
581 }
582 i += q[i + IPOPT_OLEN];
583 break;
584 }
585 }
586 return NULL;
587 }
588 #endif
Cache object: 2aa2329c03c17663b9f254f326685012
|