diff options
author | 2000-07-21 00:33:53 +0000 | |
---|---|---|
committer | 2000-07-21 00:33:53 +0000 | |
commit | 6f2840a99cbca5ec1471c2615c6023e2ab572dea (patch) | |
tree | 1c1c8d47424ea97e0658bb089893a03819edb547 | |
parent | CAST-128 keys are 40-128 bits long (not 40-160) (diff) | |
download | wireguard-openbsd-6f2840a99cbca5ec1471c2615c6023e2ab572dea.tar.xz wireguard-openbsd-6f2840a99cbca5ec1471c2615c6023e2ab572dea.zip |
Deal with a bunch of dhcp issues, ok fries@:
- some minor cleanup (syscall return codes, dead code, use strlcpy,
etc)
- sanity check dhcp option values recieved by dhclient
so that things that should look like a hostname look like a
hostname, and things that should look like an ip address look
like an ip address, if they don't ignore the lease
offer because it's bogus.
- Make the dhcp server attempt to ping an address when it recieves
a RELEASE from it. If the address answers a ping, ignore the
release offer. This helps make spoofing releases to liberate
addresses more difficult.
-rw-r--r-- | usr.sbin/dhcp/common/dispatch.c | 214 | ||||
-rw-r--r-- | usr.sbin/dhcp/common/memory.c | 4 | ||||
-rw-r--r-- | usr.sbin/dhcp/dhclient/dhclient.c | 232 | ||||
-rw-r--r-- | usr.sbin/dhcp/includes/dhcpd.h | 4 | ||||
-rw-r--r-- | usr.sbin/dhcp/server/dhcp.c | 55 | ||||
-rw-r--r-- | usr.sbin/dhcp/server/dhcpd.c | 37 |
6 files changed, 372 insertions, 174 deletions
diff --git a/usr.sbin/dhcp/common/dispatch.c b/usr.sbin/dhcp/common/dispatch.c index fda88e57620..0d66a3cde74 100644 --- a/usr.sbin/dhcp/common/dispatch.c +++ b/usr.sbin/dhcp/common/dispatch.c @@ -42,11 +42,12 @@ #ifndef lint static char copyright[] = -"$Id: dispatch.c,v 1.2 2000/02/09 11:55:47 niklas Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; +"$Id: dispatch.c,v 1.3 2000/07/21 00:33:53 beck Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" #include <sys/ioctl.h> +#include <net/if_media.h> /* Most boxes has less than 16 interfaces, so this might be a good guess. */ #define INITIAL_IFREQ_COUNT 16 @@ -162,7 +163,7 @@ void discover_interfaces (state) /* See if this is the sort of interface we want to deal with. */ - strcpy (ifr.ifr_name, ifp -> ifr_name); + strlcpy (ifr.ifr_name, ifp -> ifr_name, sizeof(ifr.ifr_name)); if (ioctl (sock, SIOCGIFFLAGS, &ifr) < 0) error ("Can't get interface flags for %s: %m", ifr.ifr_name); @@ -191,9 +192,11 @@ void discover_interfaces (state) if (!tmp) error ("Insufficient memory to %s %s", "record interface", ifp -> ifr_name); - strcpy (tmp -> name, ifp -> ifr_name); + strlcpy (tmp -> name, ifp -> ifr_name, + sizeof(tmp->name)); tmp -> next = interfaces; tmp -> flags = ir; + tmp -> noifmedia = tmp -> dead = tmp->errors = 0; interfaces = tmp; } @@ -402,7 +405,7 @@ void discover_interfaces (state) close (sock); #ifdef USE_FALLBACK - strcpy (fallback_interface.name, "fallback"); + strlcpy (fallback_interface.name, "fallback", sizeof(fallback_interface.name)); fallback_interface.shared_network = &fallback_network; fallback_network.name = "fallback-net"; if_register_fallback (&fallback_interface); @@ -427,102 +430,6 @@ void reinitialize_interfaces () interfaces_invalidated = 1; } -#ifdef USE_POLL -/* Wait for packets to come in using poll(). Anyway, when a packet - comes in, call receive_packet to receive the packet and possibly - strip hardware addressing information from it, and then call - do_packet to try to do something with it. - - As you can see by comparing this with the code that uses select(), - below, this is gratuitously complex. Quelle surprise, eh? This is - SysV we're talking about, after all, and even in the 90's, it - wouldn't do for SysV to make networking *easy*, would it? Rant, - rant... */ - -void dispatch () -{ - struct protocol *l; - int nfds = 0; - struct pollfd *fds; - int count; - int i; - int to_msec; - - nfds = 0; - for (l = protocols; l; l = l -> next) { - ++nfds; - } - fds = (struct pollfd *)malloc ((nfds) * sizeof (struct pollfd)); - if (!fds) - error ("Can't allocate poll structures."); - - do { - /* Call any expired timeouts, and then if there's - still a timeout registered, time out the select - call then. */ - another: - if (timeouts) { - struct timeout *t; - if (timeouts -> when <= cur_time) { - t = timeouts; - timeouts = timeouts -> next; - (*(t -> func)) (t -> what); - t -> next = free_timeouts; - free_timeouts = t; - goto another; - } - /* Figure timeout in milliseconds, and check for - potential overflow. We assume that integers - are 32 bits, which is harmless if they're 64 - bits - we'll just get extra timeouts in that - case. Lease times would have to be quite - long in order for a 32-bit integer to overflow, - anyway. */ - to_msec = timeouts -> when - cur_time; - if (to_msec > 2147483) - to_msec = 2147483; - to_msec *= 1000; - } else - to_msec = -1; - - /* Set up the descriptors to be polled. */ - i = 0; - for (l = protocols; l; l = l -> next) { - fds [i].fd = l -> fd; - fds [i].events = POLLIN; - fds [i].revents = 0; - ++i; - } - - /* Wait for a packet or a timeout... XXX */ - count = poll (fds, nfds, to_msec); - - /* Get the current time... */ - GET_TIME (&cur_time); - - /* Not likely to be transitory... */ - if (count < 0) { - if (errno == EAGAIN || errno == EINTR) - continue; - else - error ("poll: %m"); - } - - i = 0; - for (l = protocols; l; l = l -> next) { - if ((fds [i].revents & POLLIN)) { - fds [i].revents = 0; - if (l -> handler) - (*(l -> handler)) (l); - if (interfaces_invalidated) - break; - } - ++i; - } - interfaces_invalidated = 0; - } while (1); -} -#else /* Wait for packets to come in using select(). When one does, call receive_packet to receive the packet and possibly strip hardware addressing information from it, and then call do_packet to try to @@ -563,12 +470,20 @@ void dispatch () /* Set up the read mask. */ FD_ZERO (&r); + max = -1; + for (l = protocols; l; l = l -> next) { - FD_SET (l -> fd, &r); - if (l -> fd > max) - max = l -> fd; + struct interface_info *ip = l -> local; + if (ip && !ip->dead) { + FD_SET (l -> fd, &r); + if (l -> fd > max) + max = l -> fd; + } } + if (max == -1) + error("No interfaces to select on - exiting."); + /* Wait for a packet or a timeout... XXX */ count = select (max + 1, &r, &w, &x, tvp); @@ -576,13 +491,15 @@ void dispatch () GET_TIME (&cur_time); /* Not likely to be transitory... */ - if (count < 0) + if (count == -1) error ("select: %m"); for (l = protocols; l; l = l -> next) { + struct interface_info *ip; if (!FD_ISSET (l -> fd, &r)) continue; - if (l -> handler) + ip = l->local; + if (ip && !ip-> dead && l -> handler) (*(l -> handler)) (l); if (interfaces_invalidated) break; @@ -590,7 +507,7 @@ void dispatch () interfaces_invalidated = 0; } while (1); } -#endif /* USE_POLL */ + static void got_one (l) struct protocol *l; @@ -598,15 +515,27 @@ static void got_one (l) struct sockaddr_in from; struct hardware hfrom; struct iaddr ifrom; - int result; + static int death = 0; + size_t result; static unsigned char packbuf [4095]; /* Packet input buffer. Must be as large as largest possible MTU. */ struct interface_info *ip = l -> local; + if ((result = receive_packet (ip, packbuf, sizeof packbuf, - &from, &hfrom)) < 0) { - warn ("receive_packet failed on %s: %m", ip -> name); + &from, &hfrom)) == -1) { + warn ("receive_packet failed on %s: %s", ip -> name, + strerror(errno)); + ip->errors++; + if ((! interface_status(ip)) + || (ip->noifmedia && ip->errors > 20)) { + /* our interface has gone away. */ + warn("Interface %s appears to no longer be valid", + ip->name); + ip->dead = 1; + interfaces_invalidated = 1; + } return; } if (result == 0) @@ -771,3 +700,70 @@ void remove_protocol (proto) } } } + +int +interface_status(struct interface_info *ifinfo) +{ + char * ifname = ifinfo->name; + int ifsock = ifinfo->rfdesc; + struct ifreq ifr; + struct ifmediareq ifmr; + + + + /* get interface flags */ + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) { + syslog(LOG_ERR, "ioctl(SIOCGIFFLAGS) on %s: %m", + ifname); + goto inactive; + } + /* + * if one of UP and RUNNING flags is dropped, + * the interface is not active. + */ + if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { + goto inactive; + } + + /* Next, check carrier on the interface, if possible */ + if (ifinfo->noifmedia) + goto active; + memset(&ifmr, 0, sizeof(ifmr)); + strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name)); + + if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) { + if (errno != EINVAL) { + syslog(LOG_DEBUG, "ioctl(SIOCGIFMEDIA) on %s: %m", + ifname); + ifinfo->noifmedia = 1; + goto active; + } + /* + * EINVAL (or ENOTTY) simply means that the interface + * does not support the SIOCGIFMEDIA ioctl. We regard it alive. + */ + ifinfo->noifmedia = 1; + goto active; + } + + if (ifmr.ifm_status & IFM_AVALID) { + switch(ifmr.ifm_active & IFM_NMASK) { + case IFM_ETHER: + if (ifmr.ifm_status & IFM_ACTIVE) + goto active; + else + goto inactive; + break; + default: + goto inactive; + } + } + + inactive: + return(0); + + active: + return(1); +} diff --git a/usr.sbin/dhcp/common/memory.c b/usr.sbin/dhcp/common/memory.c index bdc90a97c02..b8674016f0a 100644 --- a/usr.sbin/dhcp/common/memory.c +++ b/usr.sbin/dhcp/common/memory.c @@ -42,7 +42,7 @@ #ifndef lint static char copyright[] = -"$Id: memory.c,v 1.2 2000/06/23 01:44:15 beck Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; +"$Id: memory.c,v 1.3 2000/07/21 00:33:53 beck Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" @@ -628,6 +628,8 @@ void release_lease (lease) lt = *lease; lt.ends = cur_time; supersede_lease (lease, <, 1); + note ("Released lease for IP address %s", + piaddr (lease -> ip_addr)); } /* Abandon the specified lease for the specified time. sets it's diff --git a/usr.sbin/dhcp/dhclient/dhclient.c b/usr.sbin/dhcp/dhclient/dhclient.c index f0796d8e3d0..2d40a89b208 100644 --- a/usr.sbin/dhcp/dhclient/dhclient.c +++ b/usr.sbin/dhcp/dhclient/dhclient.c @@ -56,11 +56,25 @@ #ifndef lint static char copyright[] = -"$Id: dhclient.c,v 1.8 2000/06/25 08:39:59 dugsong Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; +"$Id: dhclient.c,v 1.9 2000/07/21 00:33:53 beck Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" + +#define PERIOD 0x2e +#define hyphenchar(c) ((c) == 0x2d) +#define bslashchar(c) ((c) == 0x5c) +#define periodchar(c) ((c) == PERIOD) +#define asterchar(c) ((c) == 0x2a) +#define alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) \ + || ((c) >= 0x61 && (c) <= 0x7a)) +#define digitchar(c) ((c) >= 0x30 && (c) <= 0x39) + +#define borderchar(c) (alphachar(c) || digitchar(c)) +#define middlechar(c) (borderchar(c) || hyphenchar(c)) +#define domainchar(c) ((c) > 0x20 && (c) < 0x7f) + TIME cur_time; TIME default_lease_time = 43200; /* 12 hours... */ TIME max_lease_time = 86400; /* 24 hours... */ @@ -95,6 +109,10 @@ int onetry; static void usage PROTO ((void)); +static int check_option (struct client_lease *l, int option); + +static int ipv4addrs(char * buf); + int main (argc, argv, envp) int argc; char **argv, **envp; @@ -103,16 +121,7 @@ int main (argc, argv, envp) struct servent *ent; struct interface_info *ip; -#ifdef SYSLOG_4_2 - openlog ("dhclient", LOG_NDELAY); - log_priority = LOG_DAEMON; -#else openlog ("dhclient", LOG_NDELAY, LOG_DAEMON); -#endif - -#if !(defined (DEBUG) || defined (SYSLOG_4_2) || defined (__CYGWIN32__)) - setlogmask (LOG_UPTO (LOG_INFO)); -#endif for (i = 1; i < argc; i++) { if (!strcmp (argv [i], "-p")) { @@ -149,9 +158,6 @@ int main (argc, argv, envp) local_port = htons (68); else local_port = ent -> s_port; -#ifndef __CYGWIN32__ - endservent (); -#endif } remote_port = htons (ntohs (local_port) - 1); /* XXX */ @@ -793,6 +799,12 @@ struct client_lease *packet_to_lease (packet) lease -> options [i].data [lease -> options [i].len] = 0; } + if (!check_option(lease,i)) { + /* ignore a bogus lease offer */ + warn ("Invalid lease option - ignoring offer"); + free_client_lease (lease); + return (NULL); + } } } @@ -804,42 +816,35 @@ struct client_lease *packet_to_lease (packet) if ((!packet -> options [DHO_DHCP_OPTION_OVERLOAD].len || !(packet -> options [DHO_DHCP_OPTION_OVERLOAD].data [0] & 2)) && packet -> raw -> sname [0]) { - int len; /* Don't count on the NUL terminator. */ - for (len = 0; len < 64; len++) - if (!packet -> raw -> sname [len]) - break; - lease -> server_name = malloc (len + 1); - if (!lease -> server_name) { - warn ("dhcpoffer: no memory for filename.\n"); + lease->server_name = malloc(DHCP_SNAME_LEN + 1); + if (!lease -> server_name ) { + warn ("dhcpoffer: no memory for filename."); free_client_lease (lease); return (struct client_lease *)0; - } else { - memcpy (lease -> server_name, - packet -> raw -> sname, len); - lease -> server_name [len] = 0; } + memcpy(lease->server_name, packet->raw->sname, DHCP_SNAME_LEN); + lease->server_name[DHCP_SNAME_LEN]='\0'; + if (! res_hnok (lease->server_name) ) { + warn ("Bogus server name %s", lease->server_name ); + free_client_lease (lease); + return (struct client_lease *)0; + } } /* Ditto for the filename. */ if ((!packet -> options [DHO_DHCP_OPTION_OVERLOAD].len || !(packet -> options [DHO_DHCP_OPTION_OVERLOAD].data [0] & 1)) && packet -> raw -> file [0]) { - int len; - /* Don't count on the NUL terminator. */ - for (len = 0; len < 64; len++) - if (!packet -> raw -> file [len]) - break; - lease -> filename = malloc (len + 1); + /* Don't count on the NUL terminator. */ + lease->filename = malloc(DHCP_FILE_LEN + 1); if (!lease -> filename) { - warn ("dhcpoffer: no memory for filename.\n"); + warn ("dhcpoffer: no memory for filename."); free_client_lease (lease); return (struct client_lease *)0; - } else { - memcpy (lease -> filename, - packet -> raw -> file, len); - lease -> filename [len] = 0; } + memcpy(lease->filename, packet->raw->file, DHCP_FILE_LEN); + lease->filename[DHCP_FILE_LEN]='\0'; } return lease; } @@ -984,9 +989,8 @@ void send_discover (ipp) ip -> client -> packet_length, inaddr_any, &sockaddr_broadcast, (struct hardware *)0); - if (result < 0) - warn ("send_packet: %m"); - + if (result < 0) + warn ("send_discover/send_packet: %m"); add_timeout (cur_time + ip -> client -> interval, send_discover, ip); } @@ -1232,8 +1236,8 @@ void send_request (ipp) from, &destination, (struct hardware *)0); - if (result < 0) - warn ("send_packet: %m"); + if (result < 0) + warn ("send_request/send_packet: %m"); add_timeout (cur_time + ip -> client -> interval, send_request, ip); @@ -1256,8 +1260,8 @@ void send_decline (ipp) ip -> client -> packet_length, inaddr_any, &sockaddr_broadcast, (struct hardware *)0); - if (result < 0) - warn ("send_packet: %m"); + if (result < 0) + warn ("send_decline/send_packet: %m"); } void send_release (ipp) @@ -1277,8 +1281,8 @@ void send_release (ipp) ip -> client -> packet_length, inaddr_any, &sockaddr_broadcast, (struct hardware *)0); - if (result < 0) - warn ("send_packet: %m"); + if (result < 0) + warn ("send_release/send_packet: %m"); } void make_discover (ip, lease) @@ -2088,3 +2092,143 @@ void write_client_pid_file () fclose (pf); } } + +int check_option (struct client_lease *l, int option) { + char *opbuf; + + /* we use this, since this is what gets passed to dhclient-script */ + + opbuf = pretty_print_option (option, l->options[option].data, + l->options[option].len, 0, 0); + switch(option) { + case DHO_SUBNET_MASK : + case DHO_TIME_SERVERS : + case DHO_NAME_SERVERS : + case DHO_ROUTERS : + case DHO_DOMAIN_NAME_SERVERS : + case DHO_LOG_SERVERS : + case DHO_COOKIE_SERVERS : + case DHO_LPR_SERVERS : + case DHO_IMPRESS_SERVERS : + case DHO_RESOURCE_LOCATION_SERVERS : + case DHO_SWAP_SERVER : + case DHO_BROADCAST_ADDRESS : + case DHO_NIS_SERVERS : + case DHO_NTP_SERVERS : + case DHO_NETBIOS_NAME_SERVERS : + case DHO_NETBIOS_DD_SERVER : + case DHO_FONT_SERVERS : + /* These should be a list of one or more IP addresses, separated + * by spaces. If they aren't, this lease is not valid. + */ + if (!ipv4addrs(opbuf)) { + warn("Invalid IP address in option: %s", opbuf); + return(0); + } + return(1) ; + case DHO_HOST_NAME : + case DHO_DOMAIN_NAME : + case DHO_NIS_DOMAIN : + case DHO_DHCP_SERVER_IDENTIFIER : + /* This has to be a valid internet domain name */ + if (!res_hnok(opbuf)) { + warn("Bogus name option: %s", opbuf); + return(0); + } + return(1); + case DHO_PAD : + case DHO_TIME_OFFSET : + case DHO_BOOT_SIZE : + case DHO_MERIT_DUMP : + case DHO_ROOT_PATH : + case DHO_EXTENSIONS_PATH : + case DHO_IP_FORWARDING : + case DHO_NON_LOCAL_SOURCE_ROUTING : + case DHO_POLICY_FILTER : + case DHO_MAX_DGRAM_REASSEMBLY : + case DHO_DEFAULT_IP_TTL : + case DHO_PATH_MTU_AGING_TIMEOUT : + case DHO_PATH_MTU_PLATEAU_TABLE : + case DHO_INTERFACE_MTU : + case DHO_ALL_SUBNETS_LOCAL : + case DHO_PERFORM_MASK_DISCOVERY : + case DHO_MASK_SUPPLIER : + case DHO_ROUTER_DISCOVERY : + case DHO_ROUTER_SOLICITATION_ADDRESS : + case DHO_STATIC_ROUTES : + case DHO_TRAILER_ENCAPSULATION : + case DHO_ARP_CACHE_TIMEOUT : + case DHO_IEEE802_3_ENCAPSULATION : + case DHO_DEFAULT_TCP_TTL : + case DHO_TCP_KEEPALIVE_INTERVAL : + case DHO_TCP_KEEPALIVE_GARBAGE : + case DHO_VENDOR_ENCAPSULATED_OPTIONS : + case DHO_NETBIOS_NODE_TYPE : + case DHO_NETBIOS_SCOPE : + case DHO_X_DISPLAY_MANAGER : + case DHO_DHCP_REQUESTED_ADDRESS : + case DHO_DHCP_LEASE_TIME : + case DHO_DHCP_OPTION_OVERLOAD : + case DHO_DHCP_MESSAGE_TYPE : + case DHO_DHCP_PARAMETER_REQUEST_LIST : + case DHO_DHCP_MESSAGE : + case DHO_DHCP_MAX_MESSAGE_SIZE : + case DHO_DHCP_RENEWAL_TIME : + case DHO_DHCP_REBINDING_TIME : + case DHO_DHCP_CLASS_IDENTIFIER : + case DHO_DHCP_CLIENT_IDENTIFIER : + case DHO_DHCP_USER_CLASS_ID : + case DHO_END : + /* do nothing */ + return(1); + default: + warn("unknown dhcp option value 0x%x", option); + return(0); + } +} + +int +res_hnok(dn) + const char *dn; +{ + int pch = PERIOD, ch = *dn++; + + while (ch != '\0') { + int nch = *dn++; + + if (periodchar(ch)) { + ; + } else if (periodchar(pch)) { + if (!borderchar(ch)) + return (0); + } else if (periodchar(nch) || nch == '\0') { + if (!borderchar(ch)) + return (0); + } else { + if (!middlechar(ch)) + return (0); + } + pch = ch, ch = nch; + } + return (1); +} + +/* Does buf consist only of dotted decimal ipv4 addrs? + * return how many if so, + * otherwise, return 0 + */ +int ipv4addrs(char * buf) { + struct in_addr jnk; + int count = 0; + + while (inet_aton(buf, &jnk) == 1){ + count++; + while (periodchar(*buf) || digitchar(*buf)) + buf++; + if (*buf == '\0') + return(count); + while (*buf == ' ') + buf++; + } + return(0); +} diff --git a/usr.sbin/dhcp/includes/dhcpd.h b/usr.sbin/dhcp/includes/dhcpd.h index e9ea614e594..9ec584d9aea 100644 --- a/usr.sbin/dhcp/includes/dhcpd.h +++ b/usr.sbin/dhcp/includes/dhcpd.h @@ -144,6 +144,7 @@ struct lease { # define ABANDONED_LEASE 16 struct lease_state *state; + u_int8_t releasing; }; struct lease_state { @@ -363,6 +364,9 @@ struct interface_info { /* Only used by DHCP client code. */ struct client_state *client; + int noifmedia; + int errors; + int dead; }; struct hardware_link { diff --git a/usr.sbin/dhcp/server/dhcp.c b/usr.sbin/dhcp/server/dhcp.c index 6cd75f2a654..5acd78cbd1a 100644 --- a/usr.sbin/dhcp/server/dhcp.c +++ b/usr.sbin/dhcp/server/dhcp.c @@ -42,7 +42,7 @@ #ifndef lint static char copyright[] = -"$Id: dhcp.c,v 1.1 1998/08/18 03:43:34 deraadt Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; +"$Id: dhcp.c,v 1.2 2000/07/21 00:33:54 beck Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" @@ -342,19 +342,50 @@ void dhcprelease (packet) } - note ("DHCPRELEASE of %s from %s via %s (%sfound)", - inet_ntoa (packet -> raw -> ciaddr), - print_hw_addr (packet -> raw -> htype, - packet -> raw -> hlen, - packet -> raw -> chaddr), - packet -> raw -> giaddr.s_addr - ? inet_ntoa (packet -> raw -> giaddr) - : packet -> interface -> name, - lease ? "" : "not "); - /* If we found a lease, release it. */ if (lease) { - release_lease (lease); + /* first, we ping this lease to see if it's still + * there. if it is, we don't release it. + * this avoids the problem of spoofed releases + * being used to liberate addresses from the + * server. + */ + if (! lease->releasing) { + note ("DHCPRELEASE of %s from %s via %s (found)", + inet_ntoa (packet -> raw -> ciaddr), + print_hw_addr (packet -> raw -> htype, + packet -> raw -> hlen, + packet -> raw -> chaddr), + packet -> raw -> giaddr.s_addr + ? inet_ntoa (packet -> raw -> giaddr) + : packet -> interface -> name, + lease ? "" : "not "); + + lease->releasing = 1; + add_timeout (cur_time + 1, lease_ping_timeout, lease); + icmp_echorequest (&(lease -> ip_addr)); + ++outstanding_pings; + } + else { + note ("DHCPRELEASE of %s from %s via %s ignored (release already pending)", + inet_ntoa (packet -> raw -> ciaddr), + print_hw_addr (packet -> raw -> htype, + packet -> raw -> hlen, + packet -> raw -> chaddr), + packet -> raw -> giaddr.s_addr + ? inet_ntoa (packet -> raw -> giaddr) + : packet -> interface -> name); + } + } + else { + note ("DHCPRELEASE of %s from %s via %s for nonexistent lease", + inet_ntoa (packet -> raw -> ciaddr), + print_hw_addr (packet -> raw -> htype, + packet -> raw -> hlen, + packet -> raw -> chaddr), + packet -> raw -> giaddr.s_addr + ? inet_ntoa (packet -> raw -> giaddr) + : packet -> interface -> name); } } diff --git a/usr.sbin/dhcp/server/dhcpd.c b/usr.sbin/dhcp/server/dhcpd.c index a4dfe5052cc..5c2dcc6b903 100644 --- a/usr.sbin/dhcp/server/dhcpd.c +++ b/usr.sbin/dhcp/server/dhcpd.c @@ -42,7 +42,7 @@ #ifndef lint static char ocopyright[] = -"$Id: dhcpd.c,v 1.1 1998/08/18 03:43:34 deraadt Exp $ Copyright 1995, 1996 The Internet Software Consortium."; +"$Id: dhcpd.c,v 1.2 2000/07/21 00:33:55 beck Exp $ Copyright 1995, 1996 The Internet Software Consortium."; #endif static char copyright[] = @@ -318,18 +318,33 @@ void lease_pinged (from, packet, length) return; } - if (!lp -> state) { + if (!lp -> state && ! lp -> releasing) { warn ("ICMP Echo Reply for %s arrived late or is spurious.\n", piaddr (from)); return; } /* At this point it looks like we pinged a lease and got a - response, which shouldn't have happened. */ - free_lease_state (lp -> state, "lease_pinged"); - lp -> state = (struct lease_state *)0; + * response, which shouldn't have happened. + * if it did it's either one of two two cases: + * 1 - we pinged this lease before offering it and + * something answered, so we abandon it. + * 2 - we pinged this lease before releaseing it + * and something answered, so we don't release it. + */ + if (lp -> releasing) { + warn ("IP address %s answers a ping after sending a release", + piaddr (lp -> ip_addr)); + warn ("Possible release spoof - Not releasing address %s", + piaddr (lp -> ip_addr)); + lp -> releasing = 0; + } + else { + free_lease_state (lp -> state, "lease_pinged"); + lp -> state = (struct lease_state *)0; - abandon_lease (lp, "pinged before offer"); + abandon_lease (lp, "pinged before offer"); + } cancel_timeout (lease_ping_timeout, lp); --outstanding_pings; } @@ -338,7 +353,13 @@ void lease_ping_timeout (vlp) void *vlp; { struct lease *lp = vlp; - + --outstanding_pings; - dhcp_reply (lp); + if (lp->releasing) { + lp->releasing = 0; + release_lease(lp); + } + else { + dhcp_reply (lp); + } } |