Synopsis: traceroute can do packet floods. NetBSD versions: 1.3.3 and before, NetBSD-current until 19990217 Thanks to: Curt Sampson Reported in NetBSD Security Advisory: SA1999-004 This patch fixes the traceroute flooding problem described in the NetBSD-SA1999-004 security advisory. For it to apply, make sure you have NetBSD 1.3.3 sources unpacked in /usr/src, then do: % cd /usr/src/usr.sbin/traceroute % patch <19990217-traceroute % make % su root # make install --- traceroute.c.orig-1.3.3 Wed Feb 17 02:29:50 1999 +++ traceroute.c-SA004 Wed Feb 17 02:49:28 1999 @@ -1,4 +1,4 @@ -/* $NetBSD: traceroute.c,v 1.19.2.2 1997/11/04 22:34:23 mellon Exp $ */ +/* $NetBSD: traceroute.c,v 1.27 1999/02/16 20:47:24 cjs Exp $ */ /* * Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997 @@ -29,7 +29,7 @@ #else __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997\n\ The Regents of the University of California. All rights reserved.\n"); -__RCSID("$NetBSD: traceroute.c,v 1.19.2.2 1997/11/04 22:34:23 mellon Exp $"); +__RCSID("$NetBSD: traceroute.c,v 1.27 1999/02/16 20:47:24 cjs Exp $"); #endif #endif @@ -225,6 +225,7 @@ #include #include +#include #ifdef HAVE_MALLOC_H #include #endif @@ -334,6 +335,7 @@ void tvsub(struct timeval *, struct timeval *); __dead void usage(void); int wait_for_reply(int, struct sockaddr_in *, struct timeval *); +int find_local_ip(struct sockaddr_in *, struct sockaddr_in *); int main(int argc, char **argv) @@ -694,7 +696,7 @@ * Otherwise, use the first interface found. * Warn if there are more than one. */ - setsin(from, al->addr); + setsin(from, al->addr && !find_local_ip(from, to)); if (n > 1 && device == NULL) { Fprintf(stderr, "%s: Warning: Multiple interfaces found; using %s @ %s\n", @@ -871,6 +873,7 @@ struct timezone tz; register int cc = 0; int fromlen = sizeof(*fromp); + int retval; FD_ZERO(&fds); FD_SET(sock, &fds); @@ -880,9 +883,16 @@ (void)gettimeofday(&now, &tz); tvsub(&wait, &now); - if (select(sock + 1, &fds, NULL, NULL, &wait) > 0) - cc = recvfrom(s, (char *)packet, sizeof(packet), 0, - (struct sockaddr *)fromp, &fromlen); + retval = select(sock + 1, &fds, NULL, NULL, &wait); + if (retval < 0) { + /* If we continue, we probably just flood the remote host. */ + Fprintf(stderr, "%s: select: %s\n", prog, strerror(errno)); + exit(1); + } + if (retval > 0) { + cc = recvfrom(s, (char *)packet, sizeof(packet), 0, + (struct sockaddr *)fromp, &fromlen); + } return(cc); } @@ -1363,4 +1373,39 @@ [-w waittime]\n\thost [packetlen]\n", prog); exit(1); +} + +int +find_local_ip(struct sockaddr_in *from, struct sockaddr_in *to) +{ + int sock; + struct sockaddr_in help; + int help_len; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) return (0); + + help.sin_family = AF_INET; + /* + * At this point the port number doesn't matter + * since it only has to be greater than zero. + */ + help.sin_port = 42; + help.sin_addr.s_addr = to->sin_addr.s_addr; + if (connect(sock, (struct sockaddr *)&help, sizeof(help)) < 0) { + (void)close(sock); + return (0); + } + + help_len = sizeof(help); + if (getsockname(sock, (struct sockaddr *)&help, &help_len) < 0 || + help_len != sizeof(help) || + help.sin_addr.s_addr == INADDR_ANY) { + (void)close(sock); + return (0); + } + + (void)close(sock); + setsin(from, help.sin_addr.s_addr); + return (1); }