]>
Commit | Line | Data |
---|---|---|
1c79356b A |
1 | /* |
2 | * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the project nor the names of its contributors | |
14 | * may be used to endorse or promote products derived from this software | |
15 | * without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND | |
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE | |
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
27 | * SUCH DAMAGE. | |
28 | */ | |
29 | ||
30 | /* | |
31 | * RFC1826/2402 authentication header. | |
32 | */ | |
33 | ||
34 | #define _IP_VHL | |
35 | #if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__) | |
36 | #include "opt_inet.h" | |
37 | #endif | |
38 | ||
39 | #include <sys/param.h> | |
40 | #include <sys/systm.h> | |
41 | #include <sys/malloc.h> | |
42 | #include <sys/mbuf.h> | |
43 | #include <sys/domain.h> | |
44 | #include <sys/protosw.h> | |
45 | #include <sys/socket.h> | |
46 | #include <sys/errno.h> | |
47 | #include <sys/time.h> | |
48 | #include <sys/kernel.h> | |
49 | #include <sys/syslog.h> | |
50 | ||
51 | #include <net/if.h> | |
52 | #include <net/route.h> | |
53 | #include <net/netisr.h> | |
54 | #include <kern/cpu_number.h> | |
55 | ||
56 | #include <netinet/in.h> | |
57 | #include <netinet/in_systm.h> | |
58 | #include <netinet/in_var.h> | |
59 | #include <netinet/ip.h> | |
60 | #include <netinet/ip_var.h> | |
61 | #include <netinet/ip_ecn.h> | |
62 | ||
63 | #if INET6 | |
64 | #include <netinet/ip6.h> | |
65 | #include <netinet6/ip6_var.h> | |
66 | #include <netinet/icmp6.h> | |
67 | #endif | |
68 | ||
69 | #include <netinet6/ipsec.h> | |
70 | #include <netinet6/ah.h> | |
71 | #include <netkey/key.h> | |
72 | #include <netkey/keydb.h> | |
73 | #include <netkey/key_debug.h> | |
74 | ||
75 | ||
76 | #include <net/net_osdep.h> | |
77 | ||
78 | #define IPLEN_FLIPPED | |
79 | ||
80 | #if INET | |
81 | extern struct protosw inetsw[]; | |
82 | #if defined(__bsdi__) || defined(__NetBSD__) | |
83 | extern u_char ip_protox[]; | |
84 | #endif | |
85 | ||
86 | void | |
87 | ah4_input(struct mbuf *m, int off) | |
88 | { | |
89 | struct ip *ip; | |
90 | struct ah *ah; | |
91 | u_int32_t spi; | |
92 | struct ah_algorithm *algo; | |
93 | size_t siz; | |
94 | size_t siz1; | |
95 | u_char *cksum; | |
96 | struct secasvar *sav = NULL; | |
97 | u_int16_t nxt; | |
98 | size_t hlen; | |
99 | int s; | |
100 | ||
101 | #ifndef PULLDOWN_TEST | |
102 | if (m->m_len < off + sizeof(struct newah)) { | |
103 | m = m_pullup(m, off + sizeof(struct newah)); | |
104 | if (!m) { | |
105 | ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup;" | |
106 | "dropping the packet for simplicity\n")); | |
107 | ipsecstat.in_inval++; | |
108 | goto fail; | |
109 | } | |
110 | } | |
111 | ||
112 | ip = mtod(m, struct ip *); | |
113 | ah = (struct ah *)(((caddr_t)ip) + off); | |
114 | #else | |
115 | ip = mtod(m, struct ip *); | |
116 | IP6_EXTHDR_GET(ah, struct ah *, m, off, sizeof(struct newah)); | |
117 | if (ah == NULL) { | |
118 | ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup;" | |
119 | "dropping the packet for simplicity\n")); | |
120 | ipsecstat.in_inval++; | |
121 | goto fail; | |
122 | } | |
123 | #endif | |
124 | nxt = ah->ah_nxt; | |
125 | #ifdef _IP_VHL | |
126 | hlen = IP_VHL_HL(ip->ip_vhl) << 2; | |
127 | #else | |
128 | hlen = ip->ip_hl << 2; | |
129 | #endif | |
130 | ||
131 | /* find the sassoc. */ | |
132 | spi = ah->ah_spi; | |
133 | ||
134 | if ((sav = key_allocsa(AF_INET, | |
135 | (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst, | |
136 | IPPROTO_AH, spi)) == 0) { | |
137 | ipseclog((LOG_WARNING, | |
138 | "IPv4 AH input: no key association found for spi %u\n", | |
139 | (u_int32_t)ntohl(spi))); | |
140 | ipsecstat.in_nosa++; | |
141 | goto fail; | |
142 | } | |
143 | KEYDEBUG(KEYDEBUG_IPSEC_STAMP, | |
144 | printf("DP ah4_input called to allocate SA:%p\n", sav)); | |
145 | if (sav->state != SADB_SASTATE_MATURE | |
146 | && sav->state != SADB_SASTATE_DYING) { | |
147 | ipseclog((LOG_DEBUG, | |
148 | "IPv4 AH input: non-mature/dying SA found for spi %u\n", | |
149 | (u_int32_t)ntohl(spi))); | |
150 | ipsecstat.in_badspi++; | |
151 | goto fail; | |
152 | } | |
153 | if (sav->alg_auth == SADB_AALG_NONE) { | |
154 | ipseclog((LOG_DEBUG, "IPv4 AH input: " | |
155 | "unspecified authentication algorithm for spi %u\n", | |
156 | (u_int32_t)ntohl(spi))); | |
157 | ipsecstat.in_badspi++; | |
158 | goto fail; | |
159 | } | |
160 | ||
161 | algo = &ah_algorithms[sav->alg_auth]; | |
162 | ||
163 | siz = (*algo->sumsiz)(sav); | |
164 | siz1 = ((siz + 3) & ~(4 - 1)); | |
165 | ||
166 | /* | |
167 | * sanity checks for header, 1. | |
168 | */ | |
169 | { | |
170 | int sizoff; | |
171 | ||
172 | sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4; | |
173 | ||
174 | if ((ah->ah_len << 2) - sizoff != siz1) { | |
175 | ipseclog((LOG_NOTICE, "sum length mismatch in IPv4 AH input " | |
176 | "(%d should be %u): %s\n", | |
177 | (ah->ah_len << 2) - sizoff, (unsigned int)siz1, | |
178 | ipsec4_logpacketstr(ip, spi))); | |
179 | ipsecstat.in_inval++; | |
180 | goto fail; | |
181 | } | |
182 | ||
183 | #ifndef PULLDOWN_TEST | |
184 | if (m->m_len < off + sizeof(struct ah) + sizoff + siz1) { | |
185 | m = m_pullup(m, off + sizeof(struct ah) + sizoff + siz1); | |
186 | if (!m) { | |
187 | ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n")); | |
188 | ipsecstat.in_inval++; | |
189 | goto fail; | |
190 | } | |
191 | ||
192 | ip = mtod(m, struct ip *); | |
193 | ah = (struct ah *)(((caddr_t)ip) + off); | |
194 | } | |
195 | #else | |
196 | IP6_EXTHDR_GET(ah, struct ah *, m, off, | |
197 | sizeof(struct ah) + sizoff + siz1); | |
198 | if (ah == NULL) { | |
199 | ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n")); | |
200 | ipsecstat.in_inval++; | |
201 | goto fail; | |
202 | } | |
203 | #endif | |
204 | } | |
205 | ||
206 | /* | |
207 | * check for sequence number. | |
208 | */ | |
209 | if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) { | |
210 | if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav)) | |
211 | ; /*okey*/ | |
212 | else { | |
213 | ipsecstat.in_ahreplay++; | |
214 | ipseclog((LOG_WARNING, | |
215 | "replay packet in IPv4 AH input: %s %s\n", | |
216 | ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); | |
217 | goto fail; | |
218 | } | |
219 | } | |
220 | ||
221 | /* | |
222 | * alright, it seems sane. now we are going to check the | |
223 | * cryptographic checksum. | |
224 | */ | |
225 | cksum = _MALLOC(siz1, M_TEMP, M_NOWAIT); | |
226 | if (!cksum) { | |
227 | ipseclog((LOG_DEBUG, "IPv4 AH input: " | |
228 | "couldn't alloc temporary region for cksum\n")); | |
229 | ipsecstat.in_inval++; | |
230 | goto fail; | |
231 | } | |
232 | ||
233 | { | |
234 | #if 1 | |
235 | /* | |
236 | * some of IP header fields are flipped to the host endian. | |
237 | * convert them back to network endian. VERY stupid. | |
238 | */ | |
239 | #ifndef __NetBSD__ | |
240 | ip->ip_len = htons(ip->ip_len + hlen); | |
241 | ip->ip_id = htons(ip->ip_id); | |
242 | #else | |
243 | ip->ip_len = htons(ip->ip_len); | |
244 | #endif | |
245 | ip->ip_off = htons(ip->ip_off); | |
246 | #endif | |
247 | if (ah4_calccksum(m, (caddr_t)cksum, algo, sav)) { | |
248 | _FREE(cksum, M_TEMP); | |
249 | ipsecstat.in_inval++; | |
250 | goto fail; | |
251 | } | |
252 | ipsecstat.in_ahhist[sav->alg_auth]++; | |
253 | #if 1 | |
254 | /* | |
255 | * flip them back. | |
256 | */ | |
257 | #ifndef __NetBSD__ | |
258 | ip->ip_len = ntohs(ip->ip_len) - hlen; | |
259 | ip->ip_id = ntohs(ip->ip_id); | |
260 | #else | |
261 | ip->ip_len = ntohs(ip->ip_len); | |
262 | #endif | |
263 | ip->ip_off = ntohs(ip->ip_off); | |
264 | #endif | |
265 | } | |
266 | ||
267 | { | |
268 | caddr_t sumpos = NULL; | |
269 | ||
270 | if (sav->flags & SADB_X_EXT_OLD) { | |
271 | /* RFC 1826 */ | |
272 | sumpos = (caddr_t)(ah + 1); | |
273 | } else { | |
274 | /* RFC 2402 */ | |
275 | sumpos = (caddr_t)(((struct newah *)ah) + 1); | |
276 | } | |
277 | ||
278 | if (bcmp(sumpos, cksum, siz) != 0) { | |
279 | ipseclog((LOG_WARNING, | |
280 | "checksum mismatch in IPv4 AH input: %s %s\n", | |
281 | ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); | |
282 | _FREE(cksum, M_TEMP); | |
283 | ipsecstat.in_ahauthfail++; | |
284 | goto fail; | |
285 | } | |
286 | } | |
287 | ||
288 | _FREE(cksum, M_TEMP); | |
289 | ||
290 | m->m_flags |= M_AUTHIPHDR; | |
291 | m->m_flags |= M_AUTHIPDGM; | |
292 | ||
293 | #if 0 | |
294 | /* | |
295 | * looks okey, but we need more sanity check. | |
296 | * XXX should elaborate. | |
297 | */ | |
298 | if (ah->ah_nxt == IPPROTO_IPIP || ah->ah_nxt == IPPROTO_IP) { | |
299 | struct ip *nip; | |
300 | size_t sizoff; | |
301 | ||
302 | sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4; | |
303 | ||
304 | if (m->m_len < off + sizeof(struct ah) + sizoff + siz1 + hlen) { | |
305 | m = m_pullup(m, off + sizeof(struct ah) | |
306 | + sizoff + siz1 + hlen); | |
307 | if (!m) { | |
308 | ipseclog((LOG_DEBUG, | |
309 | "IPv4 AH input: can't pullup\n")); | |
310 | ipsecstat.in_inval++; | |
311 | goto fail; | |
312 | } | |
313 | } | |
314 | ||
315 | nip = (struct ip *)((u_char *)(ah + 1) + sizoff + siz1); | |
316 | if (nip->ip_src.s_addr != ip->ip_src.s_addr | |
317 | || nip->ip_dst.s_addr != ip->ip_dst.s_addr) { | |
318 | m->m_flags &= ~M_AUTHIPHDR; | |
319 | m->m_flags &= ~M_AUTHIPDGM; | |
320 | } | |
321 | } | |
322 | #if INET6 | |
323 | else if (ah->ah_nxt == IPPROTO_IPV6) { | |
324 | m->m_flags &= ~M_AUTHIPHDR; | |
325 | m->m_flags &= ~M_AUTHIPDGM; | |
326 | } | |
327 | #endif /*INET6*/ | |
328 | #endif /*0*/ | |
329 | ||
330 | if (m->m_flags & M_AUTHIPHDR | |
331 | && m->m_flags & M_AUTHIPDGM) { | |
332 | #if 0 | |
333 | ipseclog((LOG_DEBUG, | |
334 | "IPv4 AH input: authentication succeess\n")); | |
335 | #endif | |
336 | ipsecstat.in_ahauthsucc++; | |
337 | } else { | |
338 | ipseclog((LOG_WARNING, | |
339 | "authentication failed in IPv4 AH input: %s %s\n", | |
340 | ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); | |
341 | ipsecstat.in_ahauthfail++; | |
342 | goto fail; | |
343 | } | |
344 | ||
345 | /* | |
346 | * update sequence number. | |
347 | */ | |
348 | if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) { | |
349 | if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav)) { | |
350 | ipsecstat.in_ahreplay++; | |
351 | goto fail; | |
352 | } | |
353 | } | |
354 | ||
355 | /* was it transmitted over the IPsec tunnel SA? */ | |
356 | if (ipsec4_tunnel_validate(ip, nxt, sav) && nxt == IPPROTO_IPV4) { | |
357 | /* | |
358 | * strip off all the headers that precedes AH. | |
359 | * IP xx AH IP' payload -> IP' payload | |
360 | * | |
361 | * XXX more sanity checks | |
362 | * XXX relationship with gif? | |
363 | */ | |
364 | size_t stripsiz = 0; | |
365 | u_int8_t tos; | |
366 | ||
367 | tos = ip->ip_tos; | |
368 | if (sav->flags & SADB_X_EXT_OLD) { | |
369 | /* RFC 1826 */ | |
370 | stripsiz = sizeof(struct ah) + siz1; | |
371 | } else { | |
372 | /* RFC 2402 */ | |
373 | stripsiz = sizeof(struct newah) + siz1; | |
374 | } | |
375 | m_adj(m, off + stripsiz); | |
376 | if (m->m_len < sizeof(*ip)) { | |
377 | m = m_pullup(m, sizeof(*ip)); | |
378 | if (!m) { | |
379 | ipsecstat.in_inval++; | |
380 | goto fail; | |
381 | } | |
382 | } | |
383 | ip = mtod(m, struct ip *); | |
384 | /* ECN consideration. */ | |
385 | ip_ecn_egress(ip4_ipsec_ecn, &tos, &ip->ip_tos); | |
386 | if (!key_checktunnelsanity(sav, AF_INET, | |
387 | (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst)) { | |
388 | ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch " | |
389 | "in IPv4 AH input: %s %s\n", | |
390 | ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); | |
391 | ipsecstat.in_inval++; | |
392 | goto fail; | |
393 | } | |
394 | ||
395 | #if 0 /* XXX should we call ipfw rather than ipsec_in_reject? */ | |
396 | /* drop it if it does not match the default policy */ | |
397 | if (ipsec4_in_reject(m, NULL)) { | |
398 | ipsecstat.in_polvio++; | |
399 | goto fail; | |
400 | } | |
401 | #endif | |
402 | ||
403 | #if 1 | |
404 | /* | |
405 | * Should the inner packet be considered authentic? | |
406 | * My current answer is: NO. | |
407 | * | |
408 | * host1 -- gw1 === gw2 -- host2 | |
409 | * In this case, gw2 can trust the authenticity of the | |
410 | * outer packet, but NOT inner. Packet may be altered | |
411 | * between host1 and gw1. | |
412 | * | |
413 | * host1 -- gw1 === host2 | |
414 | * This case falls into the same scenario as above. | |
415 | * | |
416 | * host1 === host2 | |
417 | * This case is the only case when we may be able to leave | |
418 | * M_AUTHIPHDR and M_AUTHIPDGM set. | |
419 | * However, if host1 is wrongly configured, and allows | |
420 | * attacker to inject some packet with src=host1 and | |
421 | * dst=host2, you are in risk. | |
422 | */ | |
423 | m->m_flags &= ~M_AUTHIPHDR; | |
424 | m->m_flags &= ~M_AUTHIPDGM; | |
425 | #endif | |
426 | ||
427 | key_sa_recordxfer(sav, m); | |
428 | ||
429 | s = splimp(); | |
430 | if (IF_QFULL(&ipintrq)) { | |
431 | ipsecstat.in_inval++; | |
432 | goto fail; | |
433 | } | |
434 | IF_ENQUEUE(&ipintrq, m); | |
435 | m = NULL; | |
436 | schednetisr(NETISR_IP); /*can be skipped but to make sure*/ | |
437 | splx(s); | |
438 | nxt = IPPROTO_DONE; | |
439 | } else { | |
440 | /* | |
441 | * strip off AH. | |
442 | * We do deep-copy since KAME requires that | |
443 | * the packet is placed in a single external mbuf. | |
444 | */ | |
445 | size_t stripsiz = 0; | |
446 | ||
447 | if (sav->flags & SADB_X_EXT_OLD) { | |
448 | /* RFC 1826 */ | |
449 | stripsiz = sizeof(struct ah) + siz1; | |
450 | } else { | |
451 | /* RFC 2402 */ | |
452 | stripsiz = sizeof(struct newah) + siz1; | |
453 | } | |
454 | ||
455 | ip = mtod(m, struct ip *); | |
456 | #ifndef PULLDOWN_TEST | |
457 | ovbcopy((caddr_t)ip, (caddr_t)(((u_char *)ip) + stripsiz), off); | |
458 | m->m_data += stripsiz; | |
459 | m->m_len -= stripsiz; | |
460 | m->m_pkthdr.len -= stripsiz; | |
461 | #else | |
462 | /* | |
463 | * even in m_pulldown case, we need to strip off AH so that | |
464 | * we can compute checksum for multiple AH correctly. | |
465 | */ | |
466 | if (m->m_len >= stripsiz + off) { | |
467 | ovbcopy((caddr_t)ip, ((caddr_t)ip) + stripsiz, off); | |
468 | m->m_data += stripsiz; | |
469 | m->m_len -= stripsiz; | |
470 | m->m_pkthdr.len -= stripsiz; | |
471 | } else { | |
472 | /* | |
473 | * this comes with no copy if the boundary is on | |
474 | * cluster | |
475 | */ | |
476 | struct mbuf *n; | |
477 | ||
478 | n = m_split(m, off, M_DONTWAIT); | |
479 | if (n == NULL) { | |
480 | /* m is retained by m_split */ | |
481 | goto fail; | |
482 | } | |
483 | m_adj(n, stripsiz); | |
484 | m_cat(m, n); | |
485 | /* m_cat does not update m_pkthdr.len */ | |
486 | m->m_pkthdr.len += n->m_pkthdr.len; | |
487 | } | |
488 | #endif | |
489 | ||
490 | if (m->m_len < sizeof(*ip)) { | |
491 | m = m_pullup(m, sizeof(*ip)); | |
492 | if (m == NULL) { | |
493 | ipsecstat.in_inval++; | |
494 | goto fail; | |
495 | } | |
496 | } | |
497 | ip = mtod(m, struct ip *); | |
498 | #ifdef IPLEN_FLIPPED | |
499 | ip->ip_len = ip->ip_len - stripsiz; | |
500 | #else | |
501 | ip->ip_len = htons(ntohs(ip->ip_len) - stripsiz); | |
502 | #endif | |
503 | ip->ip_p = nxt; | |
504 | /* forget about IP hdr checksum, the check has already been passed */ | |
505 | ||
506 | key_sa_recordxfer(sav, m); | |
507 | ||
508 | if (nxt != IPPROTO_DONE) | |
509 | (*ip_protox[nxt]->pr_input)(m, off); | |
510 | else | |
511 | m_freem(m); | |
512 | m = NULL; | |
513 | } | |
514 | ||
515 | if (sav) { | |
516 | KEYDEBUG(KEYDEBUG_IPSEC_STAMP, | |
517 | printf("DP ah4_input call free SA:%p\n", sav)); | |
518 | key_freesav(sav); | |
519 | } | |
520 | ipsecstat.in_success++; | |
521 | return; | |
522 | ||
523 | fail: | |
524 | if (sav) { | |
525 | KEYDEBUG(KEYDEBUG_IPSEC_STAMP, | |
526 | printf("DP ah4_input call free SA:%p\n", sav)); | |
527 | key_freesav(sav); | |
528 | } | |
529 | if (m) | |
530 | m_freem(m); | |
531 | return; | |
532 | } | |
533 | #endif /* INET */ | |
534 | ||
535 | #if INET6 | |
536 | int | |
537 | ah6_input(mp, offp, proto) | |
538 | struct mbuf **mp; | |
539 | int *offp, proto; | |
540 | { | |
541 | struct mbuf *m = *mp; | |
542 | int off = *offp; | |
543 | struct ip6_hdr *ip6; | |
544 | struct ah *ah; | |
545 | u_int32_t spi; | |
546 | struct ah_algorithm *algo; | |
547 | size_t siz; | |
548 | size_t siz1; | |
549 | u_char *cksum; | |
550 | struct secasvar *sav = NULL; | |
551 | u_int16_t nxt; | |
552 | int s; | |
553 | ||
554 | #ifndef PULLDOWN_TEST | |
555 | IP6_EXTHDR_CHECK(m, off, sizeof(struct ah), IPPROTO_DONE); | |
556 | ah = (struct ah *)(mtod(m, caddr_t) + off); | |
557 | #else | |
558 | IP6_EXTHDR_GET(ah, struct ah *, m, off, sizeof(struct newah)); | |
559 | if (ah == NULL) { | |
560 | ipseclog((LOG_DEBUG, "IPv6 AH input: can't pullup\n")); | |
561 | ipsecstat.in_inval++; | |
562 | return IPPROTO_DONE; | |
563 | } | |
564 | #endif | |
565 | ip6 = mtod(m, struct ip6_hdr *); | |
566 | nxt = ah->ah_nxt; | |
567 | ||
568 | /* find the sassoc. */ | |
569 | spi = ah->ah_spi; | |
570 | ||
571 | if (ntohs(ip6->ip6_plen) == 0) { | |
572 | ipseclog((LOG_ERR, "IPv6 AH input: " | |
573 | "AH with IPv6 jumbogram is not supported.\n")); | |
574 | ipsec6stat.in_inval++; | |
575 | goto fail; | |
576 | } | |
577 | ||
578 | if ((sav = key_allocsa(AF_INET6, | |
579 | (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst, | |
580 | IPPROTO_AH, spi)) == 0) { | |
581 | ipseclog((LOG_WARNING, | |
582 | "IPv6 AH input: no key association found for spi %u\n", | |
583 | (u_int32_t)ntohl(spi))); | |
584 | ipsec6stat.in_nosa++; | |
585 | goto fail; | |
586 | } | |
587 | KEYDEBUG(KEYDEBUG_IPSEC_STAMP, | |
588 | printf("DP ah6_input called to allocate SA:%p\n", sav)); | |
589 | if (sav->state != SADB_SASTATE_MATURE | |
590 | && sav->state != SADB_SASTATE_DYING) { | |
591 | ipseclog((LOG_DEBUG, | |
592 | "IPv6 AH input: non-mature/dying SA found for spi %u; ", | |
593 | (u_int32_t)ntohl(spi))); | |
594 | ipsec6stat.in_badspi++; | |
595 | goto fail; | |
596 | } | |
597 | if (sav->alg_auth == SADB_AALG_NONE) { | |
598 | ipseclog((LOG_DEBUG, "IPv6 AH input: " | |
599 | "unspecified authentication algorithm for spi %u\n", | |
600 | (u_int32_t)ntohl(spi))); | |
601 | ipsec6stat.in_badspi++; | |
602 | goto fail; | |
603 | } | |
604 | ||
605 | algo = &ah_algorithms[sav->alg_auth]; | |
606 | ||
607 | siz = (*algo->sumsiz)(sav); | |
608 | siz1 = ((siz + 3) & ~(4 - 1)); | |
609 | ||
610 | /* | |
611 | * sanity checks for header, 1. | |
612 | */ | |
613 | { | |
614 | int sizoff; | |
615 | ||
616 | sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4; | |
617 | ||
618 | if ((ah->ah_len << 2) - sizoff != siz1) { | |
619 | ipseclog((LOG_NOTICE, "sum length mismatch in IPv6 AH input " | |
620 | "(%d should be %u): %s\n", | |
621 | (ah->ah_len << 2) - sizoff, (unsigned int)siz1, | |
622 | ipsec6_logpacketstr(ip6, spi))); | |
623 | ipsec6stat.in_inval++; | |
624 | goto fail; | |
625 | } | |
626 | #ifndef PULLDOWN_TEST | |
627 | IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1, IPPROTO_DONE); | |
628 | #else | |
629 | IP6_EXTHDR_GET(ah, struct ah *, m, off, | |
630 | sizeof(struct ah) + sizoff + siz1); | |
631 | if (ah == NULL) { | |
632 | ipseclog((LOG_NOTICE, "couldn't pullup gather IPv6 AH checksum part")); | |
633 | ipsecstat.in_inval++; | |
634 | m = NULL; | |
635 | goto fail; | |
636 | } | |
637 | #endif | |
638 | } | |
639 | ||
640 | /* | |
641 | * check for sequence number. | |
642 | */ | |
643 | if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) { | |
644 | if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav)) | |
645 | ; /*okey*/ | |
646 | else { | |
647 | ipsec6stat.in_ahreplay++; | |
648 | ipseclog((LOG_WARNING, | |
649 | "replay packet in IPv6 AH input: %s %s\n", | |
650 | ipsec6_logpacketstr(ip6, spi), | |
651 | ipsec_logsastr(sav))); | |
652 | goto fail; | |
653 | } | |
654 | } | |
655 | ||
656 | /* | |
657 | * alright, it seems sane. now we are going to check the | |
658 | * cryptographic checksum. | |
659 | */ | |
660 | cksum = _MALLOC(siz1, M_TEMP, M_NOWAIT); | |
661 | if (!cksum) { | |
662 | ipseclog((LOG_DEBUG, "IPv6 AH input: " | |
663 | "couldn't alloc temporary region for cksum\n")); | |
664 | ipsec6stat.in_inval++; | |
665 | goto fail; | |
666 | } | |
667 | ||
668 | if (ah6_calccksum(m, (caddr_t)cksum, algo, sav)) { | |
669 | _FREE(cksum, M_TEMP); | |
670 | ipsec6stat.in_inval++; | |
671 | goto fail; | |
672 | } | |
673 | ipsec6stat.in_ahhist[sav->alg_auth]++; | |
674 | ||
675 | { | |
676 | caddr_t sumpos = NULL; | |
677 | ||
678 | if (sav->flags & SADB_X_EXT_OLD) { | |
679 | /* RFC 1826 */ | |
680 | sumpos = (caddr_t)(ah + 1); | |
681 | } else { | |
682 | /* RFC 2402 */ | |
683 | sumpos = (caddr_t)(((struct newah *)ah) + 1); | |
684 | } | |
685 | ||
686 | if (bcmp(sumpos, cksum, siz) != 0) { | |
687 | ipseclog((LOG_WARNING, | |
688 | "checksum mismatch in IPv6 AH input: %s %s\n", | |
689 | ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav))); | |
690 | _FREE(cksum, M_TEMP); | |
691 | ipsec6stat.in_ahauthfail++; | |
692 | goto fail; | |
693 | } | |
694 | } | |
695 | ||
696 | _FREE(cksum, M_TEMP); | |
697 | ||
698 | m->m_flags |= M_AUTHIPHDR; | |
699 | m->m_flags |= M_AUTHIPDGM; | |
700 | ||
701 | #if 0 | |
702 | /* | |
703 | * looks okey, but we need more sanity check. | |
704 | * XXX should elaborate. | |
705 | */ | |
706 | if (ah->ah_nxt == IPPROTO_IPV6) { | |
707 | struct ip6_hdr *nip6; | |
708 | size_t sizoff; | |
709 | ||
710 | sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4; | |
711 | ||
712 | IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1 | |
713 | + sizeof(struct ip6_hdr), IPPROTO_DONE); | |
714 | ||
715 | nip6 = (struct ip6_hdr *)((u_char *)(ah + 1) + sizoff + siz1); | |
716 | if (!IN6_ARE_ADDR_EQUAL(&nip6->ip6_src, &ip6->ip6_src) | |
717 | || !IN6_ARE_ADDR_EQUAL(&nip6->ip6_dst, &ip6->ip6_dst)) { | |
718 | m->m_flags &= ~M_AUTHIPHDR; | |
719 | m->m_flags &= ~M_AUTHIPDGM; | |
720 | } | |
721 | } else if (ah->ah_nxt == IPPROTO_IPIP) { | |
722 | m->m_flags &= ~M_AUTHIPHDR; | |
723 | m->m_flags &= ~M_AUTHIPDGM; | |
724 | } else if (ah->ah_nxt == IPPROTO_IP) { | |
725 | m->m_flags &= ~M_AUTHIPHDR; | |
726 | m->m_flags &= ~M_AUTHIPDGM; | |
727 | } | |
728 | #endif | |
729 | ||
730 | if (m->m_flags & M_AUTHIPHDR | |
731 | && m->m_flags & M_AUTHIPDGM) { | |
732 | #if 0 | |
733 | ipseclog((LOG_DEBUG, | |
734 | "IPv6 AH input: authentication succeess\n")); | |
735 | #endif | |
736 | ipsec6stat.in_ahauthsucc++; | |
737 | } else { | |
738 | ipseclog((LOG_WARNING, | |
739 | "authentication failed in IPv6 AH input: %s %s\n", | |
740 | ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav))); | |
741 | ipsec6stat.in_ahauthfail++; | |
742 | goto fail; | |
743 | } | |
744 | ||
745 | /* | |
746 | * update sequence number. | |
747 | */ | |
748 | if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) { | |
749 | if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav)) { | |
750 | ipsec6stat.in_ahreplay++; | |
751 | goto fail; | |
752 | } | |
753 | } | |
754 | ||
755 | /* was it transmitted over the IPsec tunnel SA? */ | |
756 | if (ipsec6_tunnel_validate(ip6, nxt, sav) && nxt == IPPROTO_IPV6) { | |
757 | /* | |
758 | * strip off all the headers that precedes AH. | |
759 | * IP6 xx AH IP6' payload -> IP6' payload | |
760 | * | |
761 | * XXX more sanity checks | |
762 | * XXX relationship with gif? | |
763 | */ | |
764 | size_t stripsiz = 0; | |
765 | u_int32_t flowinfo; /*net endian*/ | |
766 | ||
767 | flowinfo = ip6->ip6_flow; | |
768 | if (sav->flags & SADB_X_EXT_OLD) { | |
769 | /* RFC 1826 */ | |
770 | stripsiz = sizeof(struct ah) + siz1; | |
771 | } else { | |
772 | /* RFC 2402 */ | |
773 | stripsiz = sizeof(struct newah) + siz1; | |
774 | } | |
775 | m_adj(m, off + stripsiz); | |
776 | if (m->m_len < sizeof(*ip6)) { | |
777 | /* | |
778 | * m_pullup is prohibited in KAME IPv6 input processing | |
779 | * but there's no other way! | |
780 | */ | |
781 | m = m_pullup(m, sizeof(*ip6)); | |
782 | if (!m) { | |
783 | ipsec6stat.in_inval++; | |
784 | goto fail; | |
785 | } | |
786 | } | |
787 | ip6 = mtod(m, struct ip6_hdr *); | |
788 | /* ECN consideration. */ | |
789 | ip6_ecn_egress(ip6_ipsec_ecn, &flowinfo, &ip6->ip6_flow); | |
790 | if (!key_checktunnelsanity(sav, AF_INET6, | |
791 | (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst)) { | |
792 | ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch " | |
793 | "in IPv6 AH input: %s %s\n", | |
794 | ipsec6_logpacketstr(ip6, spi), | |
795 | ipsec_logsastr(sav))); | |
796 | ipsec6stat.in_inval++; | |
797 | goto fail; | |
798 | } | |
799 | ||
800 | #if 0 /* XXX should we call ipfw rather than ipsec_in_reject? */ | |
801 | /* drop it if it does not match the default policy */ | |
802 | if (ipsec6_in_reject(m, NULL)) { | |
803 | ipsec6stat.in_polvio++; | |
804 | goto fail; | |
805 | } | |
806 | #endif | |
807 | ||
808 | #if 1 | |
809 | /* | |
810 | * should the inner packet be considered authentic? | |
811 | * see comment in ah4_input(). | |
812 | */ | |
813 | m->m_flags &= ~M_AUTHIPHDR; | |
814 | m->m_flags &= ~M_AUTHIPDGM; | |
815 | #endif | |
816 | ||
817 | key_sa_recordxfer(sav, m); | |
818 | ||
819 | s = splimp(); | |
820 | if (IF_QFULL(&ip6intrq)) { | |
821 | ipsec6stat.in_inval++; | |
822 | goto fail; | |
823 | } | |
824 | IF_ENQUEUE(&ip6intrq, m); | |
825 | m = NULL; | |
826 | schednetisr(NETISR_IPV6); /*can be skipped but to make sure*/ | |
827 | splx(s); | |
828 | nxt = IPPROTO_DONE; | |
829 | } else { | |
830 | /* | |
831 | * strip off AH. | |
832 | * We do deep-copy since KAME requires that | |
833 | * the packet is placed in a single mbuf. | |
834 | */ | |
835 | size_t stripsiz = 0; | |
836 | char *prvnxtp; | |
837 | ||
838 | /* | |
839 | * Copy the value of the next header field of AH to the | |
840 | * next header field of the previous header. | |
841 | * This is necessary because AH will be stripped off below. | |
842 | */ | |
843 | prvnxtp = ip6_get_prevhdr(m, off); /* XXX */ | |
844 | *prvnxtp = nxt; | |
845 | ||
846 | if (sav->flags & SADB_X_EXT_OLD) { | |
847 | /* RFC 1826 */ | |
848 | stripsiz = sizeof(struct ah) + siz1; | |
849 | } else { | |
850 | /* RFC 2402 */ | |
851 | stripsiz = sizeof(struct newah) + siz1; | |
852 | } | |
853 | ||
854 | ip6 = mtod(m, struct ip6_hdr *); | |
855 | #ifndef PULLDOWN_TEST | |
856 | ovbcopy((caddr_t)ip6, ((caddr_t)ip6) + stripsiz, off); | |
857 | m->m_data += stripsiz; | |
858 | m->m_len -= stripsiz; | |
859 | m->m_pkthdr.len -= stripsiz; | |
860 | #else | |
861 | /* | |
862 | * even in m_pulldown case, we need to strip off AH so that | |
863 | * we can compute checksum for multiple AH correctly. | |
864 | */ | |
865 | if (m->m_len >= stripsiz + off) { | |
866 | ovbcopy((caddr_t)ip6, ((caddr_t)ip6) + stripsiz, off); | |
867 | m->m_data += stripsiz; | |
868 | m->m_len -= stripsiz; | |
869 | m->m_pkthdr.len -= stripsiz; | |
870 | } else { | |
871 | /* | |
872 | * this comes with no copy if the boundary is on | |
873 | * cluster | |
874 | */ | |
875 | struct mbuf *n; | |
876 | ||
877 | n = m_split(m, off, M_DONTWAIT); | |
878 | if (n == NULL) { | |
879 | /* m is retained by m_split */ | |
880 | goto fail; | |
881 | } | |
882 | m_adj(n, stripsiz); | |
883 | m_cat(m, n); | |
884 | /* m_cat does not update m_pkthdr.len */ | |
885 | m->m_pkthdr.len += n->m_pkthdr.len; | |
886 | } | |
887 | #endif | |
888 | ip6 = mtod(m, struct ip6_hdr *); | |
889 | /* XXX jumbogram */ | |
890 | ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - stripsiz); | |
891 | ||
892 | key_sa_recordxfer(sav, m); | |
893 | } | |
894 | ||
895 | *offp = off; | |
896 | *mp = m; | |
897 | ||
898 | if (sav) { | |
899 | KEYDEBUG(KEYDEBUG_IPSEC_STAMP, | |
900 | printf("DP ah6_input call free SA:%p\n", sav)); | |
901 | key_freesav(sav); | |
902 | } | |
903 | ipsec6stat.in_success++; | |
904 | return nxt; | |
905 | ||
906 | fail: | |
907 | if (sav) { | |
908 | KEYDEBUG(KEYDEBUG_IPSEC_STAMP, | |
909 | printf("DP ah6_input call free SA:%p\n", sav)); | |
910 | key_freesav(sav); | |
911 | } | |
912 | if (m) | |
913 | m_freem(m); | |
914 | return IPPROTO_DONE; | |
915 | } | |
916 | #endif /* INET6 */ |