X-Git-Url: https://git.saurik.com/apple/network_cmds.git/blobdiff_plain/7ba0088d6898d7fd2873f736f1f556673a8be855..a2c93a7642e62c7ca181f6c580d729f73b14bded:/racoon.tproj/isakmp.c diff --git a/racoon.tproj/isakmp.c b/racoon.tproj/isakmp.c index d2fa6e1..e188b3d 100644 --- a/racoon.tproj/isakmp.c +++ b/racoon.tproj/isakmp.c @@ -1,4 +1,4 @@ -/* $KAME: isakmp.c,v 1.171 2001/12/12 22:35:37 itojun Exp $ */ +/* $KAME: isakmp.c,v 1.176 2002/08/28 04:08:30 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -87,6 +87,9 @@ #include "isakmp_inf.h" #include "isakmp_newg.h" #include "strnames.h" +#ifndef HAVE_ARC4RANDOM +#include "arc4random.h" +#endif static int nostate1 __P((struct ph1handle *, vchar_t *)); static int nostate2 __P((struct ph2handle *, vchar_t *)); @@ -144,6 +147,8 @@ static int isakmp_ph2begin_i __P((struct ph1handle *, struct ph2handle *)); static int isakmp_ph2begin_r __P((struct ph1handle *, vchar_t *)); static int etypesw1 __P((int)); static int etypesw2 __P((int)); +static void isakmp_free_addrs __P((void)); + /* * isakmp packet handler @@ -173,11 +178,23 @@ isakmp_handler(so_isakmp) goto end; } - /* check isakmp header length */ - if (len < sizeof(isakmp)) { + /* check isakmp header length, as well as sanity of header length */ + if (len < sizeof(isakmp) || ntohl(isakmp.len) < sizeof(isakmp)) { plog(LLV_ERROR, LOCATION, (struct sockaddr *)&remote, "packet shorter than isakmp header size.\n"); /* dummy receive */ + if ((len = recvfrom(so_isakmp, (char *)&isakmp, sizeof(isakmp), + 0, (struct sockaddr *)&remote, &remote_len)) < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "failed to receive isakmp packet\n"); + } + goto end; + } + + /* reject if the size is toooo big */ + if (ntohl(isakmp.len) > 0xffff) { + plog(LLV_ERROR, LOCATION, NULL, + "the length of the isakmp header is too big.\n"); if ((len = recvfrom(so_isakmp, (char *)&isakmp, sizeof(isakmp), 0, (struct sockaddr *)&remote, &remote_len)) < 0) { plog(LLV_ERROR, LOCATION, NULL, @@ -186,6 +203,8 @@ isakmp_handler(so_isakmp) goto end; } + remote_len = sizeof(remote); + /* read real message */ if ((buf = vmalloc(ntohl(isakmp.len))) == NULL) { plog(LLV_ERROR, LOCATION, NULL, @@ -205,7 +224,8 @@ isakmp_handler(so_isakmp) if (errno == EINTR) continue; plog(LLV_ERROR, LOCATION, NULL, - "failed to receive isakmp packet\n"); + "failed to read isakmp packet from socket %d, len=%d\n", so_isakmp, buf->l); + error = -2; /* serious problem with socket */ goto end; } @@ -269,6 +289,182 @@ end: return(error); } +#ifdef IKE_NAT_T +/* + * isakmp packet handler for natt port (4500) + */ +int +isakmp_natt_handler(so_isakmp) + int so_isakmp; +{ + u_char temp_buffer[sizeof(struct isakmp) + 4]; + struct isakmp *isakmp = (struct isakmp*)(temp_buffer + 4); + struct sockaddr_storage remote; + struct sockaddr_storage local; + int remote_len = sizeof(remote); + int local_len = sizeof(local); + int len; + u_short port; + vchar_t *buf = NULL; + int error = -1; + + /* read message by MSG_PEEK */ + while ((len = recvfromto(so_isakmp, temp_buffer, sizeof(temp_buffer), + MSG_PEEK, (struct sockaddr *)&remote, &remote_len, + (struct sockaddr *)&local, &local_len)) < 0) { + if (errno == EINTR) + continue; + plog(LLV_ERROR, LOCATION, NULL, + "failed to receive isakmp packet\n"); + goto end; + } + + /* remove the four bytes of zeros on nat traversal port */ + if (*(u_long*)temp_buffer != 0L) + { + /* + * This is a UDP encapsulated IPSec packet, + * we should drop it. + * + * TBD: Need a way to read the packet. + * The kernel intercepts these packets on Mac OS X + * but not all kernels will handle this the same way. + */ + goto end; + } + + /* check isakmp header length */ + if (len < sizeof(temp_buffer)) { + plog(LLV_ERROR, LOCATION, (struct sockaddr *)&remote, + "packet shorter than isakmp header size.\n"); + /* dummy receive */ + if ((len = recvfrom(so_isakmp, (char *)temp_buffer, sizeof(temp_buffer), + 0, (struct sockaddr *)&remote, &remote_len)) < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "failed to receive isakmp packet\n"); + } + goto end; + } + + remote_len = sizeof(remote); + + /* read real message */ + if ((buf = vmalloc(ntohl(isakmp->len) + 4)) == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "failed to allocate reading buffer\n"); + /* dummy receive */ + if ((len = recvfrom(so_isakmp, (char *)temp_buffer, sizeof(temp_buffer), + 0, (struct sockaddr *)&remote, &remote_len)) < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "failed to receive isakmp packet\n"); + } + goto end; + } + + while ((len = recvfromto(so_isakmp, buf->v, buf->l, + 0, (struct sockaddr *)&remote, &remote_len, + (struct sockaddr *)&local, &local_len)) < 0) { + if (errno == EINTR) + continue; + plog(LLV_ERROR, LOCATION, NULL, + "failed to read isakmp packet from socket %d, len=%d\n", so_isakmp, buf->l); + error = -2; /* serious problem with socket */ + goto end; + } + + if (len != buf->l) { + plog(LLV_ERROR, LOCATION, (struct sockaddr *)&remote, + "received invalid length, header says %d, packet is %d bytes why ?\n", + len, buf->l); + goto end; + } + + /* + * Discard first 4 bytes, they're either: + * 0 - this is IKE traffic + * !0 - first four bytes are the SPI of a UDP encapsulated IPSec packet + * The seond type of packet should be interecepted by the kernel + * or dropped before we get to this point. + */ + { + vchar_t *newbuf = vmalloc(buf->l - 4); + if (newbuf == NULL) + { + plog(LLV_ERROR, LOCATION, (struct sockaddr *)&remote, + "couldn't allocate smaller buffer.\n"); + goto end; + } + memcpy(newbuf->v, buf->v + 4, newbuf->l); + vfree(buf); + buf = newbuf; + len = buf->l; + } + + plog(LLV_DEBUG, LOCATION, NULL, "===\n"); + plog(LLV_DEBUG, LOCATION, (struct sockaddr *)&local, + "%d bytes message received from %s\n", + len, saddr2str((struct sockaddr *)&remote)); + plogdump(LLV_DEBUG, buf->v, buf->l); + + /* avoid packets with malicious port/address */ + switch (remote.ss_family) { + case AF_INET: + port = ((struct sockaddr_in *)&remote)->sin_port; + break; +#ifdef INET6 + case AF_INET6: + port = ((struct sockaddr_in6 *)&remote)->sin6_port; + break; +#endif + default: + plog(LLV_ERROR, LOCATION, NULL, + "invalid family: %d\n", remote.ss_family); + goto end; + } + if (port == 0) { + plog(LLV_ERROR, LOCATION, (struct sockaddr *)&remote, + "src port == 0 (valid as UDP but not with IKE)\n"); + goto end; + } + + { + struct isakmp *isakmp = (struct isakmp*)buf->v; + plog(LLV_DEBUG, LOCATION, (struct sockaddr*)&remote, + "natt receiving packet %.8X%.8X:%.8X%.8X %u\n", + *(u_long*)isakmp->i_ck, *(u_long*)&isakmp->i_ck[4], + *(u_long*)isakmp->r_ck, *(u_long*)&isakmp->r_ck[4], + isakmp->msgid); + } + + /* XXX: check sender whether to be allowed or not to accept */ + + /* XXX: I don't know how to check isakmp half connection attack. */ + + /* simply reply if the packet was processed. */ + if (check_recvdpkt((struct sockaddr *)&remote, + (struct sockaddr *)&local, buf)) { + plog(LLV_NOTIFY, LOCATION, NULL, + "the packet is retransmitted by %s.\n", + saddr2str((struct sockaddr *)&remote)); + error = 0; + goto end; + } + + /* isakmp main routine */ + if (isakmp_main(buf, (struct sockaddr *)&remote, + (struct sockaddr *)&local) != 0) goto end; + + error = 0; + +end: + if (buf != NULL) + vfree(buf); + + return(error); +} +#endif + + /* * main processing to handle isakmp payload */ @@ -348,17 +544,40 @@ isakmp_main(msg, remote, local) /* must be same addresses in one stream of a phase at least. */ if (cmpsaddrstrict(iph1->remote, remote) != 0) { - char *saddr_db, *saddr_act; - - saddr_db = strdup(saddr2str(iph1->remote)); - saddr_act = strdup(saddr2str(remote)); - - plog(LLV_WARNING, LOCATION, remote, - "remote address mismatched. db=%s, act=%s\n", - saddr_db, saddr_act); - - racoon_free(saddr_db); - racoon_free(saddr_act); +#ifdef IKE_NAT_T + if (iph1->side == RESPONDER && + (iph1->natt_flags & NATT_TYPE_MASK) != 0 && + cmpsaddrwop(iph1->remote, remote) == 0) + { + /* + * If the initiator detects a NAT it may switch to a + * new port. Technically, the remote address may change + * as well, depending on the NAT. Handling that would + * require more changes. + * + * We should record the new remote port so we can + * send + */ + plog(LLV_WARNING, LOCATION, remote, + "remote port changed from %s\n", saddr2str(iph1->remote)); + memcpy(iph1->remote, remote, iph1->remote->sa_len); + memcpy(iph1->local, local, iph1->local->sa_len); + } + else +#endif + { + char *saddr_db, *saddr_act; + + saddr_db = strdup(saddr2str(iph1->remote)); + saddr_act = strdup(saddr2str(remote)); + + plog(LLV_WARNING, LOCATION, remote, + "remote address mismatched. db=%s, act=%s\n", + saddr_db, saddr_act); + + racoon_free(saddr_db); + racoon_free(saddr_act); + } } /* * don't check of exchange type here because other type will be @@ -712,10 +931,6 @@ quick_main(iph2, msg) return -1; } - /* when using commit bit, status will be reached here. */ - if (iph2->status == PHASE2ST_ADDSA) - return 0; - /* free resend buffer */ if (iph2->sendbuf == NULL) { plog(LLV_ERROR, LOCATION, NULL, @@ -728,6 +943,10 @@ quick_main(iph2, msg) /* turn off schedule */ if (iph2->scr) SCHED_KILL(iph2->scr); + + /* when using commit bit, status will be reached here. */ + if (iph2->status == PHASE2ST_ADDSA) + return 0; /* send */ plog(LLV_DEBUG, LOCATION, NULL, "===\n"); @@ -1208,8 +1427,6 @@ isakmp_init() initctdtree(); init_recvdpkt(); - srandom(time(0)); - if (isakmp_open() < 0) goto err; @@ -1254,22 +1471,98 @@ isakmp_pindex(index, msgid) return buf; } -/* open ISAKMP sockets. */ int -isakmp_open() +isakmp_setup_socket(struct sockaddr* in_addr) { + int sock = -1; const int yes = 1; - int ifnum; #ifdef INET6 int pktinfo; #endif + if ((sock = socket(in_addr->sa_family, SOCK_DGRAM, 0)) < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "socket (%s)\n", strerror(errno)); + return -1; + } + + /* receive my interface address on inbound packets. */ + switch (in_addr->sa_family) { + case AF_INET: + if (setsockopt(sock, IPPROTO_IP, IP_RECVDSTADDR, + (const void *)&yes, sizeof(yes)) < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "setsockopt (%s)\n", strerror(errno)); + close(sock); + return -1; + } + break; +#ifdef INET6 + case AF_INET6: +#ifdef ADVAPI +#ifdef IPV6_RECVPKTINFO + pktinfo = IPV6_RECVPKTINFO; +#else /* old adv. API */ + pktinfo = IPV6_PKTINFO; +#endif /* IPV6_RECVPKTINFO */ +#else + pktinfo = IPV6_RECVDSTADDR; +#endif + if (setsockopt(sock, IPPROTO_IPV6, pktinfo, + (const void *)&yes, sizeof(yes)) < 0) + { + plog(LLV_ERROR, LOCATION, NULL, + "setsockopt(%d): %s\n", + pktinfo, strerror(errno)); + close(sock); + return -1; + } + break; +#endif + } + +#ifdef IPV6_USE_MIN_MTU + if (in_addr->sa_family == AF_INET6 && + setsockopt(sock, IPPROTO_IPV6, IPV6_USE_MIN_MTU, + (void *)&yes, sizeof(yes)) < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "setsockopt (%s)\n", strerror(errno)); + close(sock); + return -1; + } +#endif + + if (setsockopt_bypass(sock, in_addr->sa_family) < 0) { + close(sock); + return -1; + } + + if (bind(sock, in_addr, in_addr->sa_len) < 0) { + plog(LLV_ERROR, LOCATION, in_addr, + "failed to bind (%s).\n", strerror(errno)); + close(sock); + return -1; + } + + return sock; +} + +/* open ISAKMP sockets. */ +int +isakmp_open() +{ + int ifnum; struct myaddrs *p; ifnum = 0; for (p = lcconf->myaddrs; p; p = p->next) { if (!p->addr) continue; - + + if (p->sock != -1) { + ifnum++; + continue; // socket already open + } + /* warn if wildcard address - should we forbid this? */ switch (p->addr->sa_family) { case AF_INET: @@ -1292,72 +1585,34 @@ isakmp_open() lcconf->default_af); goto err_and_next; } + + p->sock = isakmp_setup_socket(p->addr); + if (p->sock < 0) goto err_and_next; - if ((p->sock = socket(p->addr->sa_family, SOCK_DGRAM, 0)) < 0) { - plog(LLV_ERROR, LOCATION, NULL, - "socket (%s)\n", strerror(errno)); - goto err_and_next; - } - - /* receive my interface address on inbound packets. */ - switch (p->addr->sa_family) { - case AF_INET: - if (setsockopt(p->sock, IPPROTO_IP, IP_RECVDSTADDR, - (const void *)&yes, sizeof(yes)) < 0) { - plog(LLV_ERROR, LOCATION, NULL, - "setsockopt (%s)\n", strerror(errno)); - goto err_and_next; - } - break; -#ifdef INET6 - case AF_INET6: -#ifdef ADVAPI -#ifdef IPV6_RECVPKTINFO - pktinfo = IPV6_RECVPKTINFO; -#else /* old adv. API */ - pktinfo = IPV6_PKTINFO; -#endif /* IPV6_RECVPKTINFO */ -#else - pktinfo = IPV6_RECVDSTADDR; -#endif - if (setsockopt(p->sock, IPPROTO_IPV6, pktinfo, - (const void *)&yes, sizeof(yes)) < 0) - { - plog(LLV_ERROR, LOCATION, NULL, - "setsockopt(%d): %s\n", - pktinfo, strerror(errno)); - goto err_and_next; - } - break; -#endif - } + plog(LLV_DEBUG, LOCATION, NULL, + "%s used as isakmp port (fd=%d)\n", + saddr2str(p->addr), p->sock); -#ifdef IPV6_USE_MIN_MTU - if (p->addr->sa_family == AF_INET6 && - setsockopt(p->sock, IPPROTO_IPV6, IPV6_USE_MIN_MTU, - (void *)&yes, sizeof(yes)) < 0) { - plog(LLV_ERROR, LOCATION, NULL, - "setsockopt (%s)\n", strerror(errno)); - return -1; + ifnum++; + +#ifdef IKE_NAT_T + /* + * We have to listen on 4500 in addition to 500 with IPv4 + * to support NAT traversal. + */ + if (p->addr->sa_family == AF_INET) + { + struct sockaddr_in sin = *(struct sockaddr_in*)p->addr; + + sin.sin_port = htons(PORT_ISAKMP_NATT); + p->nattsock = isakmp_setup_socket((struct sockaddr*)&sin); + if (p->nattsock >= 0) + plog(LLV_DEBUG, LOCATION, NULL, + "%s used as nat-t isakmp port (fd=%d)\n", + saddr2str((struct sockaddr*)&sin), p->nattsock); } #endif - if (setsockopt_bypass(p->sock, p->addr->sa_family) < 0) - goto err_and_next; - - if (bind(p->sock, p->addr, p->addr->sa_len) < 0) { - plog(LLV_ERROR, LOCATION, p->addr, - "failed to bind (%s).\n", strerror(errno)); - close(p->sock); - goto err_and_next; - } - - ifnum++; - - plog(LLV_INFO, LOCATION, NULL, - "%s used as isakmp port (fd=%d)\n", - saddr2str(p->addr), p->sock); - continue; err_and_next: @@ -1379,20 +1634,80 @@ isakmp_open() void isakmp_close() +{ + isakmp_close_sockets(); + isakmp_free_addrs(); +} + +void +isakmp_close_sockets() +{ + struct myaddrs *p; + + for (p = lcconf->myaddrs; p; p = p->next) { + + if (!p->addr) + continue; + + if (p->sock >= 0) { + close(p->sock); + p->sock = -1; + } + +#ifdef IKE_NAT_T + if (p->nattsock >= 0) { + close(p->nattsock); + p->nattsock = -1; + } +#endif + + } + +} + +void +isakmp_free_addrs() { struct myaddrs *p, *next; for (p = lcconf->myaddrs; p; p = next) { next = p->next; - if (!p->addr) - continue; - close(p->sock); - racoon_free(p->addr); + if (p->addr) + racoon_free(p->addr); racoon_free(p); } lcconf->myaddrs = NULL; + +} + + +// close sockets for addresses that have gone away +void +isakmp_close_unused() +{ + struct myaddrs *p, *next, **prev; + + prev = &(lcconf->myaddrs); + for (p = lcconf->myaddrs; p; p = next) { + next = p->next; + if (p->addrcount == 0) { // not in use ? + + if (p->sock >= 0) + close(p->sock); + + #ifdef IKE_NAT_T + if (p->nattsock >= 0) + close(p->nattsock); + #endif + *prev = p->next; + if (p->addr) + racoon_free(p->addr); + racoon_free(p); + } else + prev = &(p->next); + } } int @@ -1402,11 +1717,32 @@ isakmp_send(iph1, sbuf) { int len = 0; int s; + vchar_t *newbuf = NULL; /* select the socket to be sent */ s = getsockmyaddr(iph1->local); if (s == -1) return -1; + +#ifdef IKE_NAT_T + /* prepend four bytes of zeros if source or destination port is PORT_ISAKMP_NATT */ + if (iph1->remote->sa_family == AF_INET && + (((struct sockaddr_in*)(iph1->remote))->sin_port == htons(PORT_ISAKMP_NATT)) || + ((struct sockaddr_in*)(iph1->local))->sin_port == htons(PORT_ISAKMP_NATT)) + { + + /* There's probably a better way to do this */ + newbuf = vmalloc(sbuf->l + 4); + if (newbuf == NULL) { + plog(LLV_ERROR, LOCATION, NULL, "sendfromto natt prepend failed\n"); + return -1; + } + + memset(newbuf->v, 0, 4); + memcpy(newbuf->v + 4, sbuf->v, sbuf->l); + sbuf = newbuf; + } +#endif len = sendfromto(s, sbuf->v, sbuf->l, iph1->local, iph1->remote, lcconf->count_persend); @@ -1414,6 +1750,8 @@ isakmp_send(iph1, sbuf) plog(LLV_ERROR, LOCATION, NULL, "sendfromto failed\n"); return -1; } + + if (newbuf) vfree(newbuf); return 0; } @@ -1478,6 +1816,15 @@ isakmp_ph2resend(iph2) return -1; } + if (iph2->ph1 == 0) { + plog(LLV_ERROR, LOCATION, NULL, + "internal error - attempt to re-send phase2 with no phase1 bound.\n"); + iph2->retry_counter = -1; + remph2(iph2); + delph2(iph2); + return -1; + } + if (isakmp_send(iph2->ph1, iph2->sendbuf) < 0) return -1; @@ -2035,7 +2382,7 @@ isakmp_newmsgid2(iph1) u_int32_t msgid2; do { - msgid2 = random(); + msgid2 = arc4random(); } while (getph2bymsgid(iph1, msgid2)); return msgid2; @@ -2179,7 +2526,7 @@ getname(ap) if (getnameinfo((struct sockaddr *)&addr, addr.sin_len, ntop_buf, sizeof(ntop_buf), NULL, 0, NI_NUMERICHOST | niflags)) - strncpy(ntop_buf, "?", sizeof(ntop_buf)); + strlcpy(ntop_buf, "?", sizeof(ntop_buf)); return ntop_buf; } @@ -2203,7 +2550,7 @@ getname6(ap) if (getnameinfo((struct sockaddr *)&addr, addr.sin6_len, ntop_buf, sizeof(ntop_buf), NULL, 0, NI_NUMERICHOST | niflags)) - strncpy(ntop_buf, "?", sizeof(ntop_buf)); + strlcpy(ntop_buf, "?", sizeof(ntop_buf)); return ntop_buf; } @@ -2252,8 +2599,8 @@ isakmp_printpacket(msg, from, my, decoded) if (getnameinfo(from, from->sa_len, hostbuf, sizeof(hostbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV | niflags)) { - strncpy(hostbuf, "?", sizeof(hostbuf)); - strncpy(portbuf, "?", sizeof(portbuf)); + strlcpy(hostbuf, "?", sizeof(hostbuf)); + strlcpy(portbuf, "?", sizeof(portbuf)); } printf("%s:%s", hostbuf, portbuf); } else @@ -2263,8 +2610,8 @@ isakmp_printpacket(msg, from, my, decoded) if (getnameinfo(my, my->sa_len, hostbuf, sizeof(hostbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV | niflags)) { - strncpy(hostbuf, "?", sizeof(hostbuf)); - strncpy(portbuf, "?", sizeof(portbuf)); + strlcpy(hostbuf, "?", sizeof(hostbuf)); + strlcpy(portbuf, "?", sizeof(portbuf)); } printf("%s:%s", hostbuf, portbuf); } else