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