README for Princeton Patch 7 February 15 1999 irwin@princeton.edu This file accompanies Princeton Patch 7 to CMU-dhcpd 3.3.7; it describes how to apply the patch, and what has changed. After you have applied the patch, you no longer need this file, since the information in it also appears in the RELEASE_NOTES file which results from applying the patch. This patch (as well as the complete pre-patched current distribution) is available via: http://www.princeton.edu/~irwin/dhcpd.html ========================================================================= UPDATING FROM PATCHLEVEL 6 The patch is against dhcpd version 3.3.7 as distributed by CMU, with Princeton patches 1, 2, 3, 4, 5, and 6 already applied. Assuming you have already applied the previous patches, you can apply this patch as follows: 1. Make a record of any local customizations you have made to the product, such as changes you have made to 'Makefile' (or possibly 'Makefile.in'). If you did customize any 'Makefile.in' files, put the original version back to ensure the patch will apply cleanly. 2. Change to the directory containing the dhcpd source, and feed the patch file to the 'patch' program. (The 'patch' program is not supplied here; it is included with many operating systems, and is freely available from many software libraries.) E.g. using GNU patch: 'patch -p0 < CMU-dhcpd-3.3.7-PU-patch7'. 3. Please check for and remove any of the following obsolete files you find ('patch' may have removed them for you): ./syslog.h ./man/bootpef.8 ./man/bootptest.8 ./sample/dhcpd.conf.sample ./sample/dhcpd.conf.sample2 ./Announce.old ./README.old ./Changes ./Installation ./README.PRINCETON ./README.PRINCETON1 ./README.PRINCETON2 ./README.PRINCETON3 ./README.PRINCETON4 ./README.PRINCETON5 ./README.PRINCETON6 4. Run 'make distclean' to remove old object files, Makefiles and configuration info. 5. If you had previously customized any 'Makefile.in' files (as opposed to customizing any 'Makefile' files) , now's the time to re-apply your customizations if necessary. (You *did* make a record of them, right?) Most of the definitions that used to be in Makefile.in have moved to defaults.h, so you may not need to make any changes to any 'Makefile.in' files at this time. Before re-apply any of your old customizations, first check to see if you can now accomplish what you need with a commandline option instead of a compile-time definition; most defaults and features that previously required a compile-time definition can now be overridden using commandline options. New commandline options are covered in the WHAT'S CHANGED section below (and appear in the dhcpd.8 man page, of course). If you do change any 'Makefile.in' files, save the originals first, so you'll have clean copies available when it comes time to apply the next patch. 6. Run './configure' to figure out your system's configuration and generate new 'Makefile' files from the 'Makefile.in' files. 7. If you had previously customized any 'Makefile' files (as opposed to any 'Makefile.in' files), now's the time to re-apply your customizations. if necessary. (You *did* make a record of them, right?) Most of the definitions that used to be in 'Makefile' files have moved to defaults.h, so you may not need to make any changes to 'Makefile' files at this time. Before re-apply any of your old customizations, first check to see if you can now accomplish what you need with a commandline option instead of a compile-time definition; most defaults and features that previously required a compile-time definition can now be overridden using commandline options. New commandline options are covered in the WHAT'S CHANGED section below (and appear in the dhcpd.8 man page, of course). 8. Examine the 'defaults.h' file and customize it if necessary. This file now contains many of the definitions that previously lived in Makefile.in. Avoid changing items that can also be set using a commandline option. If you do change the 'defaults.h file, save the original first, so you'll have a clean copy available when it comes time to apply the next patch. 9. Run 'make' to compile the program. If you receive any compile-time errors, review the INSTALL document for tips. 10. Kill any running dhcpd, then install the new dhcpd executable. (It often lives in /etc/dhcpd or /usr/local/etc/dhcpd, but you may install it elsewhere.) You'll probably want to back up your old dhcpd executable before installing the new one. 11. Update any startup script you may use to take advantage of new commandline options added in this patch. 12. Start the new dhcpd executable. 13. Optionally install the new dhcpd.8, bootptab.5, and dhcpd.conf.5 man pages manually. --------------------------------------------------------------------- BUILDING FROM SCRATCH You can get the current version of the source from http://www.princeton.edu/~irwin/dhcpd.html Then see the INSTALL document for instructions. --------------------------------------------------------------------- PLATFORMS This code is currently in production at Princeton, but has NOT been tested extensively in all environments. In particular, I'm presently compiling and running on Solaris 2.5.1 and 2.6 SPARC systems. --------------------------------------------------------------------- WHAT'S CHANGED Changes since PU patch 6: --- I've received permission from Carnegie Mellon University to distribute their source, so you can now download the pre-patched source. If you prefer, you can still download just the patches, of course, and apply them yourself. --- A wider variety of precompiled versions of dhcpd for Solaris on SPARC and i86pc architectures has been made available by mark@sunsite.unc.edu (mark@zang.com). Visit: http://sunsite.unc.edu/pub/packages/solaris/sparc ftp://sunsite.unc.edu/pub/packages/solaris/sparc (Look for a filename that starts with 'dhcp' and check to see what version number (patchlevel) it includes.) --- Previously each Princeton patch had its own README file, containing its release notes, any patch instructions, legal notice, etc. I've removed these files (./README.PRINCETON*). The current and previous release notes are now in the new RELEASE_NOTES file, in reverse chronological order. The legal notices are now in the new COPYING file. Removed obsolete documentation: ./Changes ./Announce.old ./README.old ./TODO ./man/bootpef.8 Replaced obsolete ./README with a new version. The "Installation" file has been renamed "INSTALL" and tidied up. --- Removed "-I/usr/include" from CDEFS in the top-level Makefile, finally. It doesn't belong there. This addresses the long-standing ugliness that caused the familiar compile-time error: "__builtin_va_alist undeclared" in report.c. This also addresses the "ioctl(SIOCGIFCONF): not supported" runtime error. If you now get "__builtin_va_alist undeclared", fix your installation of gcc. Chances are the fixed include files that gcc needs were not installed. For more details, search the Solaris FAQ for 'builtin_va_alist': http://www.fwi.uva.nl/pub/solaris/solaris2.html --- The fake 'syslog.h' file (which provided some definitions in case the system didn't provide syslog.h) has been removed. It has been replaced with 'my_syslog.h' which simply includes the real syslog.h if 'configure' found it, else it provides those definitions we need. All other files that did #include or #include "syslog.h" have been changed to #include "my_syslog.h". The -I$(top_srcdir) has been removed from CFLAGS in the top-level Makefile. Its purpose was to cause the fake syslog.h to be found if the system version was missing. This is no longer necessary. Note that you should no longer need to use the old workaround of putting "-idirafter ." in CDEFS. --- By default the server did not write a pidfile (although you could override this by defining the PID_FILE compile-time option to an absolute pathname). Now PID_FILE is defined by default at compile-time to /etc/dhcpd.pid. You can override the absolute pathname with the new command-line -P option. It is a fatal error if the server cannot write its pidfile. --- If you do not specify a current working directory using the -c command-line option, the current working directory will default to /. (Previously, the server simply left its current working directory unchanged.) This change to the default was made so that by default, the daemon does not keep open a directory that you may wish to unmount later. If you haven't been specifying a -c option, you may now need to, if your dhcp server host is also the home of client bootfiles. This is because the current working directory plays a role in interpreting client bootfile names. If a client bootfile name is specified as a relative pathname, and the server needs to check the existance or size of the bootfile, the bootfile pathname is taken relative to the server's current working directory. (Existance checking for bootfiles is turned on by default; it is controlled by the compile-time definition CHECK_FILE_ACCESS in the Makefile.) So if your dhcp server host is also the home of client bootfiles, and in the past you cd'd to the bootfile directory before starting the dhcp server, then now you will need to specify the bootfile directory using the -c option when starting the server. It is now a fatal error if the server cannot change to the specified (or default) current working directory. --- When a DHCP client specified a Client Identifier option which does not correspond to its htype/chaddr, we log a message indicating the mismatch. (As before, we will still honor the Client Identifier specified by the client.) --- Added -I option, to cause the server to ignore any Client Identifier specified by DHCP clients. This is based on an earlier suggestion by Mel Lew . When you specify this option, the server proceeds as if the client did not specify any Client Identifier option; the server constructs a cliid by concatenating the htype and haddr fields. Note that since the Client Identifier option is entirely ignored (it is not parsed), no messages indicating problems with its value are logged. This is useful if you have clients who specify garbage Client Identifier values. Note that specifying this option produces behavior that violates the DHCP specification. --- An error message produced when we try to assign a dynamic IP address which is a member of multiple pools (due to erroneous overlapping pools definitions in the dhcpd.conf file) did not print the names of the pools involved. (This message was also triggered by another bug described below, in which we sometimes neglected to remove a dynamic address from a pool's free list.) --- Although we ignore DHCPINFORM messages, we were leaking memory each time we received one. --- haddrtoa() did not function correctly if called with hlen=0. (E.g. when a client tried to tell us its hardware address of Cliid had a length of 0.) Instead of returning a ptr to a null string, it returned a pointer to the last value it returned, and overwrote one byte of memory residing immediately below its (static) storage. This memory corruption eventually led to random errors and crashes. --- We failed to update our notion of the lease expiration time when we received a DHCPREQUEST from a currently-bound client in the INIT-REBOOT state. We sent the client a good DHCPACK with the correct (extended) lease expiration time, and even logged the correct (extended) lease expiration time (if you had debugging on high enough). But the lease expiration time stored in our bindings data was not updated. As a result, if that time arrived (before any other transaction occurred that would cause the binding to be updated or deleted), the server expired the lease. The client would continue to use the lease, of course. For static IP addresses you might not notice the problem, because if the client later sends a DHCPREQUEST to RENEW the lease, we'll create a new lease if the address is for a static IP address and "Renew Unbound Statics" is enabled. For dynamic IP addresses it is more noticable, since we may try to assign the free (from our point of view) dynamic IP address to someone else, and possibly notice it still in-use when we PING it. (Problem reported by Juan Courcoul ) --- We failed to update the free list for a pool of dynamic IP addresses when we decided to grant one of these addresses to a client sending us a DHCPREQUEST in INIT-REBOOT state, if our bindings data indicated that client was not presently bound. As a result, the dynamic IP address was granted to the client AND remained in the free list. When trying to assign a dynamic address from this pool to another client, we could discover this entry, then complain that it was both in the free list AND in the current bindings. We'd log the discrepancy: Tried to assign an ip which already had a binding a.b.c.d ...will not try to assign this address ...The bound IP address is listed as being in pool ...but was found on the free list for pool ...This suggests the conf file has 'network' statements with overlapping IP ranges. (Since we did notice the discrepancy, we correctly avoided re-assigning this address.) (Problem reported by Juan Courcoul ) --- When we detect a mismatch between a the Client ID option specified by a DHCP client and the chaddr field, the message we log could fail to include the chaddr value; e.g.: Client ID option (00 00 00 00 00 00 00 ) doesn't correspond to chaddr () Now we should report the chaddr value more often. (There are still times we aren't reporting the chaddr.) --- When dhcp_opts() logs an error message, it now appends the IPsrc value from the offending packet. This info was already being logged back when the packet was received, if your debuglevel is high enough, but it's more convenient to have it right on the error message as well. --- Y2K: I am not aware of any Y2K problems with the code. Brandon Hume of Dalhousie University has reviewed the code; his findings are at: http://noc.dal.ca/CommServ/reports/dhcpd_y2k.html --- When parsing the bootp header fields 'file' and 'sname' (bp->bp_file and bp->bp_sname), we now set the final byte of these fields to NUL, to guard against buffer overflow attacks. According to RFC, these fields sent by the client are supposed to either be null, or a null-terminated string; it should therefore be safe to overwrite the final byte with NUL. --- report() allocated a static 128-byte buffer for constructing messages; this has been increased to 256 to accomodate larger messages report() may be called with. vsprintf() call replaced with vsnprintf() (where available) to guard against buffer overflow. --- When we scanned interfaces at startup, if we detected an interface that was not UP and skipped it, we could log a message like: "interface qe1, skipping (not IPv4)". Now the message we log is simply "interface qe1, skipping". (In init_interfaces(), the test for address family has moved to after the test for appropriate interface flags.) --- If the same IP address was erroneously specified in multiple bootptab entries, readtab() would go ahead and create inconsistent in-memory bootptab hashtables. This could produce all sorts of runtime problems. For example, if you had two clients assigned the same static address, if one client issues a DHCPDISCOVER at the same time the other client already has a lease on the address you could see: dhcp_discover(): cliid () assigned a static IP (192.168.84.11) in bootptab, cliid not found in boundhshtbl, but this IP *FOUND* in ipboundhshtbl! the unexpected entry found in ipboundhshtbl had the cliid 01 08 C0 6F 89 22 A3 freeing unexpected binding found in ipboundhshtbl We never did support specifying the same IP address, hardware address, client identifier, or entry name in multiple bootptab entries. The behavior when you did so was not defined. Now readtab() is more careful when it reads the bootptab file. It checks for duplicate entry names, IP addresses, hardware address, and client IDs before attempting to insert any of the parsed data into the appropriate hash tables. When a duplicate is found, an error is logged at LOG_ERR, and the entire entry containing the duplicate value is skipped. Some other error messages produced by readtab() were clarified or moved from LOG_NOTICE to LOG_ERR. --- When running at high debug levels, the following spurious message was produced when we received a PAD option (option 0) in a client's Parameter Request List option: "ignoring unknown or unexpected option (0) in client's Parameter Request List option" We no longer produce this message. --- Added new -B commandline option, to allow you to specify a different bindings dump file, overridding the default /tmp/bind.dump. The name of the bindings dump file is now included in the log message at the conclusion of a successful bindings dump. --- You can now run the server with an effective uid and gid other than root. However, it must still be started as root, and retains its real uid and gid. Here is the new information from the manual page: RUNNING AS AN UNPRIVILEGED USER The server must be started as root. It needs to be able to perform several privileged operations at startup time. How- ever, after it completes these operations, it only needs root privileges occasionally. (It needs root privileges whenever it needs to insert entries into the host's IP ARP table to send a packet to a host on a directly-attached net- work. It needs root privileges whenever it needs to open a raw datagram socket to use to ping a dynamic IP address to verify it is not presently in-use before assigning it to a client.) To limit any accidental damage the server could do it it contains bugs, you can specify that the server should change its effective uid and/or effective gid to something other than root. After performing some startup operations, it will make the change. To use this feature, specify the username (or uid) under which the server should run with the -U option; specify the groupname (or gid) under which the server should run with -G option. Note that this is only supported if the host sup- ports seteuid(). While running, the server may need to manipulate the host's IP ARP table from time to time. If it has changed its effective uid to something other than root's, it will tem- porarily change its effective uid back to root's to manipu- late the IP ARP table. Note that while this feature makes it less convenient to exploit the server's root privileges if there are any bugs present in the server, it does not provide complete protec- tion, since the server still retains a real uid of root. To make best use of this feature, you should probably create a new user and group (assign (assigning a unique uid and gid); for the server to use; e.g. user dhcp and group dhcp. The server needs to be able to read the bootptab and dhcpd.conf files, and needs to be able to read and write in the bindings directory. Set the owners/groups and/or permis- sions of the files and directories above to provide the necessary access to the user/group you selected for the dhcp server. Keep in mind that you don't want to allow arbitrary users to modify the bootptab and dhcpd.conf files, or write in the bindings directory. The server can write bootpd.dump and bind.dump files upon receipt of the appropriate signals. Make sure the user/group you selected for the dhcp server can write these files if you intend to send those signals to the server, and that the user that sends these signals has the necessary privilege to send those signals. --- Whenver the server had to write a file, it wasn't as careful as it should have been about how it created the file for writing. This could allow an unprivileged user the opportunity to cause the server to remove or overwrite any file on the system. Now we're more careful about how we open files for writing. (The server creates a pidfile at startup, and upon receipt of USR1 or USR2 signals, creates a bootptab dump file or a bindings dump file.) --- If we received a signal to dump the current bootptab or bindings database, and were unable to open the appropriate dump file for writing, we'd log the error and exit. Now we log the error and continue. Not being able to write a dump file isn't a fatal error. --- Added a -A option to allow you to disable the "Check File Access" feature from the commandline. (Previously you could only disable it by undefined the CHECK_FILE_ACCESS compile-time definition.) --- Makefile.in has been cleaned up. Definitions specific to the program have been moved to the new defaults.h file, to make maintenance and customization easier. The definitions in defaults.h (and those remaining in Makefile.in) have been cleaned up to make it clearer what you might want to edit and what you should probably just leave alone. Since almost all the program's features you can manipulate via compile-time definitions can now *also* be manipulated via commandline options, most changes you may have made in the past no longer are necessary. The major exceptions are definitions for LOG_FACILITY and DEBUG (in defaults.h) and OPT (in Makefile.in). The 'clean' target will also remove core files. The 'distclean' target will also 'make clean'. --- In the sample/ directory, obsolete sample file dhcpd.conf.sample" was removed, and "dhcpd.conf.sample2" was renamed to "dhcpd.conf". Obsolete man page "bootptab.5" and "bootptest.8" were deleted. --- Added a sample SysV-style startup script to the sample/ directory. --- Added new 'de' (deny client) tag support in bootptab. Here is the description from the bootptab man page: The de tag is strictly a boolean tag; it does not take the usual equals-sign and value. It may only be specified in entries which contain a hardware address or client identif- ier. Its presence indicates special handling for packets received from this client's hardware address or client iden- tifier; the intent is to deny BOOTP and DHCP service from this server to the client, without interfering with any ser- vice the client may be able to receive from another BOOTP or DHCP server. BOOTPREQUEST and DHCPDISCOVER packets from the client are ignored. DHCPREQUEST packets from clients that are in SELECTING state are ignored (as usual) if the packet's Server Identifier Option does not correspond to this server. For DHCPREQUEST packets from clients that are in SELECTING state with a Server Identifier Option that specifies this server: if no offer or binding is present for this client identifier we send a DHCPNAK (as usual), but if an offer or binding is present for this client identifier we send a DHCPNAK and discard the binding/offer. For DHCPRE- QUEST packets from clients that are in INIT-REBOOT, if there is not presently a binding for this client identifier, the packet is ignored; if there is presently a binding for this client identifier, the binding is discarded and we send a DHCPNAK. DHCPREQUEST packets from clients that are in RENEWING or REBINDING (or confused) state, if there is not presently a binding for this client identifier, the packet is ignored; if there is presently a binding for this client identifier, the binding is discarded and we send a DHCPNAK. You might find this tag useful in the following scenario: you serve dynamic IP addreses on a network to any client that asks; the 'network' statement for the pool of dynamic addresses does NOT contain a "registered" or "roaming" accessflag. But then you have a small collection of client machines to which you do NOT want to serve dynamic addresses (e.g. interlopers). Add them to your bootptab, specifying just their hardware addresses and the new 'de' tag. ---