diff options
author | Mark Murray <markm@FreeBSD.org> | 1999-03-14 17:13:19 +0000 |
---|---|---|
committer | Mark Murray <markm@FreeBSD.org> | 1999-03-14 17:13:19 +0000 |
commit | 2aef693010b252e8cff0ce46a6ebf15b74c82219 (patch) | |
tree | 5c4b2dfe1ca36eeb731956db3380eef1053a2d03 | |
download | src-test2-2aef693010b252e8cff0ce46a6ebf15b74c82219.tar.gz src-test2-2aef693010b252e8cff0ce46a6ebf15b74c82219.zip |
Notes
56 files changed, 9731 insertions, 0 deletions
diff --git a/contrib/tcp_wrappers/BLURB b/contrib/tcp_wrappers/BLURB new file mode 100644 index 000000000000..69178c1ae253 --- /dev/null +++ b/contrib/tcp_wrappers/BLURB @@ -0,0 +1,36 @@ +@(#) BLURB 1.28 97/03/21 19:27:18 + +With this package you can monitor and filter incoming requests for the +SYSTAT, FINGER, FTP, TELNET, RLOGIN, RSH, EXEC, TFTP, TALK, and other +network services. + +The package provides tiny daemon wrapper programs that can be installed +without any changes to existing software or to existing configuration +files. The wrappers report the name of the client host and of the +requested service; the wrappers do not exchange information with the +client or server applications, and impose no overhead on the actual +conversation between the client and server applications. + +This patch upgrades the tcp wrappers version 7.5 source code to +version 7.6. The source-routing protection in version 7.5 was not +as strong as it could be. And all this effort was not needed with +modern UNIX systems that can already stop source-routed traffic in +the kernel. Examples are 4.4BSD derivatives, Solaris 2.x, and Linux. + +This release does not introduce new features. Do not bother applying +this patch when you built your version 7.x tcp wrapper without +enabling the KILL_IP_OPTIONS compiler switch; when you can disable +IP source routing options in the kernel; when you run a UNIX version +that pre-dates 4.4BSD, such as SunOS 4. Such systems are unable to +receive source-routed connections and are therefore not vulnerable +to IP spoofing attacks with source-routed TCP connections. + +A complete change log is given in the CHANGES document. As always, +problem reports and suggestions for improvement are welcome. + + Wietse Venema (wietse@wzv.win.tue.nl), + Department of Mathematics and Computing Science, + Eindhoven University of Technology, + The Netherlands. + + Currently visiting IBM T.J. Watson Research, Hawthorne NY, USA. diff --git a/contrib/tcp_wrappers/Banners.Makefile b/contrib/tcp_wrappers/Banners.Makefile new file mode 100644 index 000000000000..915e3dd96774 --- /dev/null +++ b/contrib/tcp_wrappers/Banners.Makefile @@ -0,0 +1,70 @@ +# @(#) Banners.Makefile 1.3 97/02/12 02:13:18 +# +# Install this file as the Makefile in your directory with banner files. +# It will convert a prototype banner text to a form that is suitable for +# the ftp, telnet, rlogin, and other services. +# +# You'll have to comment out the IN definition below if your daemon +# names don't start with `in.'. +# +# The prototype text should live in the banners directory, as a file with +# the name "prototype". In the prototype text you can use %<character> +# sequences as described in the hosts_access.5 manual page (`nroff -man' +# format). The sequences will be expanded while the banner message is +# sent to the client. For example: +# +# Hello %u@%h, what brings you here? +# +# Expands to: Hello username@hostname, what brings you here? Note: the +# use of %u forces a client username lookup. +# +# In order to use banners, build the tcp wrapper with -DPROCESS_OPTIONS +# and use hosts.allow rules like this: +# +# daemons ... : clients ... : banners /some/directory ... +# +# Of course, nothing prevents you from using multiple banner directories. +# For example, one banner directory for clients that are granted service, +# one banner directory for rejected clients, and one banner directory for +# clients with a hostname problem. +# +SHELL = /bin/sh +IN = in. +BANNERS = $(IN)telnetd $(IN)ftpd $(IN)rlogind # $(IN)fingerd $(IN)rshd + +all: $(BANNERS) + +$(IN)telnetd: prototype + cp prototype $@ + chmod 644 $@ + +$(IN)ftpd: prototype + sed 's/^/220-/' prototype > $@ + chmod 644 $@ + +$(IN)rlogind: prototype nul + ( ./nul ; cat prototype ) > $@ + chmod 644 $@ + +# Other services: banners may interfere with normal operation +# so they should probably be used only when refusing service. +# In particular, banners don't work with standard rsh daemons. +# You would have to use an rshd that has built-in tcp wrapper +# support, for example the rshd that is part of the logdaemon +# utilities. + +$(IN)fingerd: prototype + cp prototype $@ + chmod 644 $@ + +$(IN)rshd: prototype nul + ( ./nul ; cat prototype ) > $@ + chmod 644 $@ + +# In case no /dev/zero available, let's hope they have at least +# a C compiler of some sort. + +nul: + echo 'main() { write(1,"",1); return(0); }' >nul.c + $(CC) $(CFLAGS) -s -o nul nul.c + rm -f nul.c diff --git a/contrib/tcp_wrappers/CHANGES b/contrib/tcp_wrappers/CHANGES new file mode 100644 index 000000000000..e68ee750e015 --- /dev/null +++ b/contrib/tcp_wrappers/CHANGES @@ -0,0 +1,451 @@ +Request: after building the programs, please run the `tcpdchk' wrapper +configuration checker. See the `tcpdchk.8' manual page (`nroff -man' +format) for instructions. `tcpdchk' automatically identifies the most +common configuration problems, and will save you and me a lot of time. + +Changes per release 7.6 (Mar 1997) +================================== + +- Improved the anti source-routing protection. The code in version +7.5 was not as strong as it could be, because I tried to be compatible +with Linux. That was a mistake. Sorry for the inconvenience. + +- The program no longer terminates case of a source-routed connection, +making the IP-spoofing code more usable for long-running daemons. + +- When syslogging DNS hostname problems, always stop after a limited +number of characters. + +Changes per release 7.5 (Feb 1997) +================================== + +- Optionally refuse source-routed TCP connections requests altogether. +Credits to Niels Provos of Universitaet Hamburg. File: fix_options.c. + +- Support for IRIX 6 (Lael Tucker). + +- Support for Amdahl UTS 2.1.5 (Richard E. Richmond). + +- Support for SINIX 5.42 (Klaus Nielsen). + +- SCO 5 now has vsyslog() (Bill Golden). + +- Hints and tips for dealing with IRIX inetd (Niko Makila, Aaron +M Lee). + +- Support for BSD/OS (Paul Borman). + +- Support for Tandem (Emad Qawas). + +- Support for ISC (Frederick B. Cohen). + +- Workaround for UNICOS - it would choke on a setjmp() expression +(Bruce Kelly). File: hosts_access.c, tcpdchk.c. + +- Increased the level of buffer overflow paranoia when printing +unwanted IP options. File: fix_options.c. + +Changes per release 7.4 (Mar 1996) +================================== + +- IRIX 5.3 (and possibly, earlier releases, too) library routines call +the non-reentrant strtok() routine. The result is that hosts may slip +through allow/deny filters. Workaround is to not rely on the vendor's +strtok() routine (#ifdef LIBC_CALLS_STRTOK). Credits to Th. Eifert +(Aachen University) for spotting this one. This fix supersedes the +earlier workaround for a similar problem in FreeBSD 2.0. + +Changes per release 7.3 (Feb 1996) +================================== + +- More tests added to tcpdchk and tcpdmatch: make sure that the +REAL_DAEMON_DIR actually is a directory and not a regular file; +detect if tcpd recursively calls itself. + +- Edwin Kremer found an amusing fencepost error in the xgets() +routine: lines longer than BUFLEN characters would be garbled. + +- The access control routines now refuse to execute "dangerous" actions +such as `twist' when they are called from within a resident process. +This prevents you from shooting yourself into the foot with critical +systems programs such as, e.g., portmap or rpcbind. + +- Support for Unicos 8.x (Bruce Kelly). The program now closes the +syslog client socket before running the real daemon: Cray UNICOS +refuses to checkpoint processes with open network ports. + +- Support for MachTen UNIX (Albert M.C Tam). + +- Support for Interactive UNIX R3.2 V4.0 (Bobby D. Wright). + +- Support for SCO 3.2v5.0.0 OpenServer 5 (bob@odt.handy.com) + +- Support for Unixware 1.x and Unixware 2.x. The old Unixware Makefile +rule was broken. Sorry about that. + +- Some FreeBSD 2.0 libc routines call strtok() and severely mess up the +allow/deny rule processing. This is very bad. Workaround: call our own +strtok() clone (#ifdef USE_STRSEP). + +- The programs now log a warning when they detect that a non-existent +banner directory is specified. + +- The hosts_access.3 manual page used obsolete names for the RQ_* +constants. + +Changes per release 7.2 (Jan 1995) +================================== + +- Added a note to the README and manpages on using the IDENT service to +detect sequence number spoofing and other host impersonation attacks. + +- Portability: ConvexOS puts RPC version numbers before the daemon path +name (Jukka Ukkonen). + +- Portability: the AIX compiler disliked the strchr() declaration +in socket.c. I should have removed it when I included <string.h>. + +- Backwards compatibility: some people relied on the old leading dot or +trailing dot magic in daemon process names. + +- Backwards compatibility: hostname lookup remains enabled when +-DPARANOID is turned off. In order to disable hostname lookups you +must turn off -DALWAYS_HOSTNAME. + +- Eliminated false complaints from the tcpdmatch/tcpdchk configuration +checking programs about process names not in inetd.conf or about KNOWN +username patterns. + +Changes per release 7.1 (Jan 1995) +================================== + +- Portability: HP-UX permits you to break inetd.conf entries with +backslash-newline. + +- Portability: EP/IX has no putenv() and some inetd.conf entries are +spread out over two lines. + +- Portability: SCO with NIS support has no *netgrent() routines. + +Changes per release 7.0 (Jan 1995) +================================== + +- Added a last-minute workaround for a Solaris 2.4 gethostbyname() +foulup with multi-homed hosts in DNS through NIS mode. + +- Added a last-minute defense against TLI weirdness: address lookups +apparently succeed but the result netbuf is empty (ticlts transport). + +- Dropped several new solutions that were in need of a problem. Beta +testers may recognize what new features were kicked out during the last +weeks before release 7.0 came out. Such is life. + +- Got rid of out the environment replacement routines, at least for +most architectures. One should not have to replace working system +software when all that is needed is a 4.4BSD setenv() emulator. + +- By popular request I have added an option to send banner messages to +clients. There is a Banners.Makefile that gives some aid for sites that +are going to use this feature. John C. Wingenbach did some pioneering +work here. I used to think that banners are frivolous. Now that I had +a personal need for them I know that banners can be useful. + +- At last: an extensible functional interface to the pattern matching +engine. request_init() and request_set() accept a variable-length +name-value argument list. The result can be passed to hosts_access(). + +- When PARANOID mode is disabled (compile time), the wrapper does no +hostname lookup or hostname double checks unless required by %letter +expansions, or by access control rules that match host names. This is +useful for sites that don't care about internet hostnames anyway. +Inspired by the authors of the firewalls and internet security book. + +- When PARANOID mode is disabled (compile time), hosts with a name/name +or name/address conflict can be matched with the PARANOID host wildcard +pattern, so that you can take some intelligent action instead of just +dropping clients. Like showing a banner that explains the problem. + +- New percent escapes: %A expands to the server address; %H expands to +the corresponding hostname (or address if no name is available); %n and +%N expand to the client and server hostname (or "unknown"); %s expands +to everything we know about the server endpoint (the opposite of the %c +sequence for client information). + +- Symmetry: server and client host information is now treated on equal +footing, so that we can reuse a lot of code. + +- Lazy evaluation of host names, host addresses, usernames, and so on, +to avoid doing unnecessary work. + +- Dropping #ifdefs for some archaic systems made the code simpler. + +- Dropping the FAIL pattern made the pattern matcher much simpler. Run +the "tcpdchk" program to scan your access control files for any uses of +this obscure language feature. + +- Moving host-specific pattern matching from string_match() to the +host_match() routine made the code more accurate. Run the "tcpdchk" +program to scan your access control files for any dependencies on +undocumented or obscure language features that are gone. + +- daemon@host patterns trigger on clients that connect to a specific +internet address. This can be useful for service providers that offer +multiple ftp or www archives on different internet addresses, all +belonging to one and the same host (www.foo.com, ftp.bar.com, you get +the idea). Inspired by a discussion with Rop Gonggrijp, Cor Bosman, +and Casper Dik, and earlier discussions with Adrian van Bloois. + +- The new "tcpdchk" program critcizes all your access control rules and +inetd.conf entries. Great for spotting obscure bugs in my own hosts.xxx +files. This program also detects hosts with name/address conflicts and +with other DNS-related problems. See the "tcpdchk.8" manual page. + +- The "tcpdmatch" program replaces the poor old "try" command. The new +program looks in your inetd.conf file and therefore produces much more +accurate predictions. In addition, it detects hosts with name/address +conflicts and with other DNS-related problems. See the "tcpdmatch.8" +manual page. The inetd.conf lookup was suggested by Everett F Batey. + +- In the access control tables, the `=' between option name and value +is no longer required. + +- Added 60-second timeout to the safe_finger command, to cover another +potential problem. Suggested by Peter Wemm. + +- Andrew Maffei provided code that works with WIN-TCP on NCR System V.4 +UNIX. It reportedly works with versions 02.02.01 and 02.03.00. The code +pops off all streams modules above the device driver, pushes the timod +module to get at the peer address, and then restores the streams stack +to the initial state. + +Changes per release 6.3 (Mar 1994) +================================== + +- Keepalives option, to get rid of stuck daemons when people turn off +their PC while still connected. Files: options.c, hosts_options.5. + +- Nice option, to calm down network daemons that take away too much CPU +time. Files: options.c, hosts_options.5. + +- Ultrix perversion: the environ global pointer may be null. The +environment replacement routines now check for this. File: environ.c. + +- Fixed a few places that still assumed the socket is on standard +input. Fixed some error messages that did not provide access control +file name and line number. File: options.c. + +- Just when I was going to release 6.2 I received code for Dynix/PTX. +That code is specific to PTX 2.x, so I'll keep around my generic +PTX code just in case. The difference is in the handling of UDP +services. Files: tli_sequent.[hc]. + +Changes per release 6.2 (Feb 1994) +================================== + +- Resurrected my year-old code to reduce DNS load by appending a dot to +the gethostbyname() argument. This feature is still experimental and it +may go away if it causes more problems than it solves. File: socket.c. + +- Auxiliary code for the Pyramid, BSD universe. Karl Vogel figured out +what was missing: yp_get_default_domain() and vfprintf(). Files: +workarounds.c, vfprintf.c. + +- Improved support for Dynix/PTX. The wrapper should now be able to +deal with all TLI over IP services. File: ptx.c. + +- The try command now uses the hostname that gethostbyaddr() would +return, instead of the hostname returned by gethostbyname(). This can +be significant on systems with NIS that have short host names in the +hosts map. For example, gethostbyname("wzv.win.tue.nl") returns +"wzv.win.tue.nl"; gethostbyaddr(131.155.210.17) returns "wzv", and +that is what we should test with. File: try.c. + +Changes per release 6.1 (Dec 1993) +================================== + +- Re-implemented all environment access routines. Most systems have +putenv() but no setenv(), some systems have setenv() but no putenv(), +and there are even systems that have neither setenv() nor putenv(). The +benefit of all this is that more systems can now be treated in the same +way. File: environ.c. + +- Workaround for a weird problem with DG/UX when the wrapper is run as +nobody (i.e. fingerd). For some reason the ioctl(fd, I_FIND, "sockmod") +call fails even with socket-based applications. The "fix" is to always +assume sockets when the ioctl(fd, I_FIND, "timod") call fails. File: +fromhost.c. Thanks to Paul de Vries (vries@dutentb.et.tudelft.nl) for +helping me to figure out this one. + +- Implemented a workaround for Dynix/PTX and other systems with TLI +that lack some essential support routines. Thanks to Bugs Brouillard +(brouill@hsuseq.humboldt.edu) for the hospitality to try things out. +The trick is to temporarily switch to the socket API to identify the +client, and to switch back to TLI when done. It still does not work +right for basic network services such as telnet. File: fromhost.c. + +- Easy-to-build procedures for SCO UNIX, ConvexOS with UltraNet, EP/IX, +Dynix 3.2, Dynix/PTX. File: Makefile. + +- Variable rfc931 timeout. Files: rfc931.c, options.c, log_tcp.h, try.c. + +- Further simplification of the rfc931 code. File: rfc931.c. + +- The fromhost() interface stinks: I cannot change that, but at least +the from_sock() and from_tli() functions now accept a file descriptor +argument. + +- Fixed a buglet: fromhost() would pass a garbage file descriptor to +the isastream() call. + +- On some systems the finger client program lives in /usr/bsd. File: +safe_finger.c. + +Changes per release 6.0 (Sept 1993) +=================================== + +- Easy build procedures for common platforms (sun, ultrix, aix, hpux +and others). + +- TLI support, System V.4 style (Solaris, DG/UX). + +- Username lookup integrated with the access control language. +Selective username lookups are now the default (was: no username +lookups). + +- A safer finger command for booby traps. This one solves a host of +possible problems with automatic reverse fingers. Thanks, Borja Marcos +(borjam@we.lc.ehu.es) for some inspiring discussions. + +- KNOWN pattern that matches hosts whose name and address are known. + +- Cleanup of diagnostics. Errors in access-control files are now shown +with file name and line number. + +- With AIX 3.2, hostnames longer than 32 would be truncated. This +caused hostname verification failures, so that service would be refused +when paranoid mode was enabled. Found by: Adrian van Bloois +(A.vanBloois@info.nic.surfnet.nl). + +- With some IRIX versions, remote username lookups failed because the +fgets() library function does not handle partial read()s from sockets. +Found by: Daniel O'Callaghan (danny@austin.unimelb.edu.au). + +- Added a DISCLAIMER document to help you satisfy legal departments. + +The extension language module has undergone major revisions and +extensions. Thanks, John P. Rouillard (rouilj@ra.cs.umb.edu) for +discussions, experiments, and for being a good guinea pig. The +extensions are documented in hosts_options.5, and are enabled by +editing the Makefile STYLE macro definition. + +- (Extension language) The ":" separator may now occur within options +as long as it is protected with a backslash. A warning is issued when +a rule ends on ":". + +- (Extension language) Better verification mode. When the `try' command +is run, each option function now explains what it would do. + +- (Extension language) New "allow" and "deny" keywords so you can now +have all rules within a single file. See "nroff -man hosts_options.5" +for examples. + +- (Extension language) "linger" keyword to set the socket linger time +(SO_LINGER). From: Marc Boucher <marc@cam.org>. + +- (Extension language) "severity" keyword to turn the logging noise up +or down. Many sites wanted a means to shut up the program; other sites +wanted to emphasize specific events. Adapted from code contributed +by Dave Mitchell <D.Mitchell@dcs.shef.ac.uk>. + +Changes per release 5.1 (Mar 1993) +================================== + +- The additional protection against source-routing attacks from hosts +that pretend to have someone elses network address has become optional +because it causes kernel panics with SunOS <= 4.1.3. + +Changes per release 5.0 (Mar 1993) +================================== + +- Additional protection against source-routing attacks from hosts that +pretend to have someone elses network address. For example, the address +of a trusted host within your own network. + +- The access control language has been extended with a simple but +powerful operator that greatly simplifies the design of rule sets (ALL: +.foo.edu EXCEPT dialup.foo.edu). Blank lines are permitted, and long +lines can be continued with backslash-newline. + +- All configurable stuff, including path names, has been moved into the +Makefile so that you no longer have to hack source code to just +configure the programs. + +- Ported to Solaris 2. TLI-based applications not yet supported. +Several workarounds for System V bugs. + +- A small loophole in the netgroup lookup code was closed, and the +remote username lookup code was made more portable. + +- Still more documentation. The README file now provides tutorial +sections with introductions to client, server, inetd and syslogd. + +Changes per release 4.3 (Aug 1992) +================================== + +- Some sites reported that connections would be rejected because +localhost != localhost.domain. The host name checking code now +special-cases localhost (problem reported by several sites). + +- The programs now report an error if an existing access control file +cannot be opened (e.g. due to lack of privileges). Until now, the +programs would just pretend that the access control file does not exist +(reported by Darren Reed, avalon@coombs.anu.edu.au). + +- The timeout period for remote userid lookups was upped to 30 seconds, +in order to cope with slow hosts or networks. If this is too long for +you, adjust the TIMEOUT definition in file rfc931.c (problem reported +by several sites). + +- On hosts with more than one IP network interface, remote userid +lookups could use the IP address of the "wrong" local interface. The +problem and its solution were discussed on the rfc931-users mailing +list. Scott Schwartz (schwartz@cs.psu.edu) folded the fix into the +rfc931.c module. + +- The result of % expansion (in shell commands) is now checked for +stuff that may confuse the shell; it is replaced by underscores +(problem reported by Icarus Sparry, I.Sparry@gdr.bath.ac.uk). + +- A portability problem was fixed that caused compile-time problems +on a CRAY (problem reported by Michael Barnett, mikeb@rmit.edu.au). + +Changes per release 4.0 (Jun 1992) +================================== + +1 - network daemons no longer have to live within a common directory +2 - the access control code now uses both the host address and name +3 - an access control pattern that supports netmasks +4 - additional protection against forged host names +5 - a pattern that matches hosts whose name or address lookup fails +6 - an operator that prevents hosts or services from being matched +7 - optional remote username lookup with the RFC 931 protocol +8 - an optional umask to prevent the creation of world-writable files +9 - hooks for access control language extensions +10 - last but not least, thoroughly revised documentation. + +Changes per release 3.0 (Oct 1991) +================================== + +Enhancements over the previous release are: support for datagram (UDP +and RPC) services, and execution of shell commands when a (remote host, +requested service) pair matches a pattern in the access control tables. + +Changes per release 2.0 (May 1991) +================================== + +Enhancements over the previous release are: protection against rlogin +and rsh attacks through compromised domain name servers, optional +netgroup support for systems with NIS (formerly YP), and an extension +of the wild card patterns supported by the access control files. + +Release 1.0 (Jan 1991) diff --git a/contrib/tcp_wrappers/DISCLAIMER b/contrib/tcp_wrappers/DISCLAIMER new file mode 100644 index 000000000000..42d82ca77504 --- /dev/null +++ b/contrib/tcp_wrappers/DISCLAIMER @@ -0,0 +1,16 @@ +/************************************************************************ +* Copyright 1995 by Wietse Venema. All rights reserved. Some individual +* files may be covered by other copyrights. +* +* This material was originally written and compiled by Wietse Venema at +* Eindhoven University of Technology, The Netherlands, in 1990, 1991, +* 1992, 1993, 1994 and 1995. +* +* Redistribution and use in source and binary forms are permitted +* provided that this entire copyright notice is duplicated in all such +* copies. +* +* This software is provided "as is" and without any expressed or implied +* warranties, including, without limitation, the implied warranties of +* merchantibility and fitness for any particular purpose. +************************************************************************/ diff --git a/contrib/tcp_wrappers/Makefile b/contrib/tcp_wrappers/Makefile new file mode 100644 index 000000000000..2906c52ddf37 --- /dev/null +++ b/contrib/tcp_wrappers/Makefile @@ -0,0 +1,889 @@ +# @(#) Makefile 1.23 97/03/21 19:27:20 + +what: + @echo + @echo "Usage: edit the REAL_DAEMON_DIR definition in the Makefile then:" + @echo + @echo " make sys-type" + @echo + @echo "If you are in a hurry you can try instead:" + @echo + @echo " make REAL_DAEMON_DIR=/foo/bar sys-type" + @echo + @echo "And for a version with language extensions enabled:" + @echo + @echo " make REAL_DAEMON_DIR=/foo/bar STYLE=-DPROCESS_OPTIONS sys-type" + @echo + @echo "This Makefile knows about the following sys-types:" + @echo + @echo " generic (most bsd-ish systems with sys5 compatibility)" + @echo " 386bsd aix alpha apollo bsdos convex-ultranet dell-gcc dgux dgux543" + @echo " dynix epix esix freebsd hpux irix4 irix5 irix6 isc iunix" + @echo " linux machten mips(untested) ncrsvr4 netbsd next osf power_unix_211" + @echo " ptx-2.x ptx-generic pyramid sco sco-nis sco-od2 sco-os5 sinix sunos4" + @echo " sunos40 sunos5 sysv4 tandem ultrix unicos7 unicos8 unixware1 unixware2" + @echo " uts215 uxp" + @echo + @echo "If none of these match your environment, edit the system" + @echo "dependencies sections in the Makefile and do a 'make other'." + @echo + +####################################################### +# Choice between easy and advanced installation recipe. +# +# Advanced installation: vendor-provided daemons are left alone, and the +# inetd configuration file is edited. In this case, the REAL_DAEMON_DIR +# macro should reflect the actual directory with (most of) your +# vendor-provided network daemons. These names can be found in the +# inetd.conf file. Usually, the telnet, ftp and finger daemons all live +# in the same directory. +# +# Uncomment the appropriate line if you are going to edit inetd.conf. +# +# Ultrix 4.x SunOS 4.x ConvexOS 10.x Dynix/ptx +#REAL_DAEMON_DIR=/usr/etc +# +# SysV.4 Solaris 2.x OSF AIX +#REAL_DAEMON_DIR=/usr/sbin +# +# BSD 4.4 +#REAL_DAEMON_DIR=/usr/libexec +# +# HP-UX SCO Unicos +#REAL_DAEMON_DIR=/etc + +# Easy installation: vendor-provided network daemons are moved to "some +# other" directory, and the tcpd wrapper fills in the "holes". For this +# mode of operation, the REAL_DAEMON_DIR macro should be set to the "some +# other" directory. The "..." is here for historical reasons only; you +# should probably use some other name. +# +# Uncomment the appropriate line if you are going to move your daemons. +# +# Ultrix 4.x SunOS 4.x ConvexOS 10.x Dynix/ptx +#REAL_DAEMON_DIR=/usr/etc/... +# +# SysV.4 Solaris 2.x OSF AIX +#REAL_DAEMON_DIR=/usr/sbin/... +# +# BSD 4.4 +#REAL_DAEMON_DIR=/usr/libexec/... +# +# HP-UX SCO Unicos +#REAL_DAEMON_DIR=/etc/... + +# End of mandatory section +########################## + +########################################## +# Ready-to-use system-dependent templates. +# +# Ready-to-use templates are available for many systems (see the "echo" +# commands at the start of this Makefile). The templates take care of +# all system dependencies: after editing the REAL_DAEMON_DIR definition +# above, do a "make sunos4" (or whatever system type is appropriate). +# +# If your system is not listed (or something that comes close enough), you +# have to edit the system dependencies section below and do a "make other". +# +# Send templates for other UNIX versions to wietse@wzv.win.tue.nl. + +# This is good for many BSD+SYSV hybrids with NIS (formerly YP). +generic aix osf alpha dynix: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS= RANLIB=ranlib ARFLAGS=rv AUX_OBJ=setenv.o \ + NETGROUP=-DNETGROUP TLI= all + +# Ditto, with vsyslog +sunos4: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS= RANLIB=ranlib ARFLAGS=rv AUX_OBJ=setenv.o \ + NETGROUP=-DNETGROUP VSYSLOG= TLI= all + +# Generic with resolver library. +generic-resolver: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS=-lresolv RANLIB=ranlib ARFLAGS=rv AUX_OBJ=setenv.o \ + NETGROUP=-DNETGROUP TLI= all + +# The NeXT loader needs "-m" or it barfs on redefined library functions. +next: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS=-m RANLIB=ranlib ARFLAGS=rv AUX_OBJ=environ.o \ + NETGROUP=-DNETGROUP TLI= all + +# SunOS for the 386 was frozen at release 4.0.x. +sunos40: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS= RANLIB=ranlib ARFLAGS=rv AUX_OBJ="setenv.o strcasecmp.o" \ + NETGROUP=-DNETGROUP VSYSLOG= TLI= all + +# Ultrix is like aix, next, etc., but has miscd and setenv(). +ultrix: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS= RANLIB=ranlib ARFLAGS=rv AUX_OBJ= \ + NETGROUP=-DNETGROUP TLI= all miscd + +# This works on EP/IX 1.4.3 and will likely work on Mips (reggers@julian.uwo.ca) +epix: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS= RANLIB=ranlib ARFLAGS=rv AUX_OBJ=environ.o \ + NETGROUP=-DNETGROUP TLI= SYSTYPE="-systype bsd43" all + +# Freebsd and linux by default have no NIS. +386bsd netbsd bsdos: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS= RANLIB=ranlib ARFLAGS=rv AUX_OBJ= NETGROUP= TLI= \ + EXTRA_CFLAGS=-DSYS_ERRLIST_DEFINED VSYSLOG= all + +freebsd: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS= RANLIB=ranlib ARFLAGS=rv AUX_OBJ= NETGROUP= TLI= \ + EXTRA_CFLAGS=-DSYS_ERRLIST_DEFINED VSYSLOG= all + +linux: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS= RANLIB=ranlib ARFLAGS=rv AUX_OBJ=setenv.o \ + NETGROUP= TLI= EXTRA_CFLAGS="-DBROKEN_SO_LINGER" all + +# This is good for many SYSV+BSD hybrids with NIS, probably also for HP-UX 7.x. +hpux hpux8 hpux9 hpux10: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS= RANLIB=echo ARFLAGS=rv AUX_OBJ=setenv.o \ + NETGROUP=-DNETGROUP TLI= all + +# ConvexOS-10.x with UltraNet support (ukkonen@csc.fi). +convex-ultranet: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS=-lulsock RANLIB=ranlib ARFLAGS=rv AUX_OBJ=environ.o \ + NETGROUP=-DNETGROUP TLI= all + +# Generic support for the Dynix/PTX version of TLI. +ptx-generic: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS="-lsocket -linet -lnsl" RANLIB=echo ARFLAGS=rv \ + AUX_OBJ="setenv.o strcasecmp.o ptx.o" NETGROUP= TLI=-DPTX all + +# With UDP support optimized for PTX 2.x (timw@sequent.com). +ptx-2.x: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS="-lsocket -linet -lnsl" RANLIB=echo ARFLAGS=rv \ + AUX_OBJ="setenv.o strcasecmp.o tli-sequent.o" NETGROUP= \ + TLI=-DTLI_SEQUENT all + +# IRIX 4.0.x has a special ar(1) flag. +irix4: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS="-lc -lsun" RANLIB=echo ARFLAGS=rvs AUX_OBJ=setenv.o \ + NETGROUP=-DNETGROUP TLI= all + +# IRIX 5.2 is SYSV4 with several broken things (such as -lsocket -lnsl). +irix5: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS=-lsun RANLIB=echo ARFLAGS=rv VSYSLOG= \ + NETGROUP=-DNETGROUP AUX_OBJ=setenv.o TLI= all + +# IRIX 6.2 (tucker@math.unc.edu). Must find a better value than 200000. +irix6: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS= RANLIB=echo ARFLAGS=rv VSYSLOG= \ + NETGROUP=-DNETGROUP EXTRA_CFLAGS="-DBSD=200000" TLI= all + +# SunOS 5.x is another SYSV4 variant. +sunos5: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS="-lsocket -lnsl" RANLIB=echo ARFLAGS=rv VSYSLOG= \ + NETGROUP=-DNETGROUP AUX_OBJ=setenv.o TLI=-DTLI \ + BUGS="$(BUGS) -DSOLARIS_24_GETHOSTBYNAME_BUG" all + +# Generic SYSV40 +esix sysv4: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS="-lsocket -lnsl" RANLIB=echo ARFLAGS=rv \ + NETGROUP=-DNETGROUP AUX_OBJ=setenv.o TLI=-DTLI all + +# DG/UX 5.4.1 and 5.4.2 have an unusual inet_addr() interface. +dgux: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS=-lnsl RANLIB=echo ARFLAGS=rv \ + NETGROUP=-DNETGROUP AUX_OBJ=setenv.o TLI=-DTLI \ + BUGS="$(BUGS) -DINET_ADDR_BUG" all + +dgux543: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS=-lnsl RANLIB=echo ARFLAGS=rv \ + NETGROUP=-DNETGROUP AUX_OBJ=setenv.o TLI=-DTLI all + +# NCR UNIX 02.02.01 and 02.03.00 (Alex Chircop, msu@unimt.mt) +ncrsvr4: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS="-lresolv -lnsl -lsocket" RANLIB=echo ARFLAGS=rv \ + AUX_OBJ="setenv.o strcasecmp.o" NETGROUP= TLI=-DTLI \ + EXTRA_CFLAGS="" FROM_OBJ=ncr.o all + +# Tandem SYSV4 (eqawas@hedgehog.ac.cowan.edu.au) +tandem: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS="-lsocket -lnsl" RANLIB=echo ARFLAGS=rv \ + NETGROUP= AUX_OBJ="setenv.o strcasecmp.o" TLI=-DTLI all + +# Amdahl UTS 2.1.5 (Richard.Richmond@bridge.bst.bls.com) +uts215: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS="-lsocket" RANLIB=echo \ + ARFLAGS=rv AUX_OBJ=setenv.o NETGROUP=-DNO_NETGROUP TLI= all + +# UXP/DS System V.4 clone (vic@uida0.uida.es). +uxp: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS="-L/usr/ucblib -lsocket -lnsl -lucb" \ + RANLIB=echo ARFLAGS=rv NETGROUP=-DNETGROUP \ + AUX_OBJ=setenv.o TLI="-DTLI -DDRS_XTI" all + +# DELL System V.4 Issue 2.2 using gcc (kim@tac.nyc.ny.us, jurban@norden1.com) +dell-gcc: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS="-lsocket -lnsl" RANLIB=ranlib ARFLAGS=rv CC=gcc \ + AUX_OBJ="setenv.o strcasecmp.o" TLI=-DTLI all + +# SCO 3.2v4.1 no frills (jedwards@sol1.solinet.net). +sco: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS="-lsocket -lnsl_s" RANLIB=echo ARFLAGS=rv \ + NETGROUP= AUX_OBJ=setenv.o TLI= all + +# SCO OpenDesktop 2.0, release 3.2 (peter@midnight.com). Please simplify. +sco-od2: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS="-lrpcsvc -lrpc -lyp -lrpc -lrpcsvc -lsocket" \ + RANLIB=echo ARFLAGS=rv AUX_OBJ=setenv.o \ + NETGROUP=-DNETGROUP TLI= all + +# SCO 3.2v4.2 with TCP/IP 1.2.1 (Eduard.Vopicka@vse.cz). Please simplify. +sco-nis: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS="-lyp -lrpc -lsocket -lyp -lc_s -lc" \ + RANLIB=echo ARFLAGS=rv AUX_OBJ=setenv.o \ + NETGROUP=-DNETGROUP TLI= EXTRA_CFLAGS="-nointl -DNO_NETGRENT" all + +# SCO 3.2v5.0.0 OpenServer 5 (bob@odt.handy.com, bill@razorlogic.com) +sco-os5: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS="-lrpcsvc -lsocket" RANLIB=echo ARFLAGS=rv VSYSLOG= \ + AUX_OBJ=setenv.o NETGROUP=-DNETGROUP TLI= all + +# sinix 5.42 setjmp workaround (szrzs023@ub3.ub.uni-kiel.de) +sinix: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS="-lsocket -lnsl -L/usr/ccs/lib -lc -L/usr/ucblib -lucb" \ + RANLIB=echo ARFLAGS=rv AUX_OBJ=setenv.o TLI=-DTLI all + +# Domain SR10.4. Build under bsd, run under either sysv3 or bsd43. +apollo: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS= RANLIB=ranlib ARFLAGS=rv AUX_OBJ=setenv.o \ + NETGROUP=-DNETGROUP TLI= SYSTYPE="-A run,any -A sys,any" all + +# Pyramid OSx 5.1, using the BSD universe. +pyramid: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS= RANLIB=ranlib ARFLAGS=rv AUX_OBJ="environ.o vfprintf.o" \ + STRINGS="-Dstrchr=index -Dstrrchr=rindex -Dmemcmp=bcmp -Dno_memcpy" \ + NETGROUP="-DNETGROUP -DUSE_GETDOMAIN" TLI= all + +# Untested. +mips: + @echo "Warning: some definitions may be wrong." + make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS= RANLIB=ranlib ARFLAGS=rv AUX_OBJ=environ.o \ + NETGROUP=-DNETGROUP TLI= SYSTYPE="-sysname bsd43" all + +# Cray (tested with UNICOS 7.0.4). +unicos7: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS=-lnet RANLIB=echo ARFLAGS=rv \ + EXTRA_CFLAGS=-DINADDR_NONE="\"((unsigned long) -1)\"" \ + AUX_OBJ="setenv.o strcasecmp.o" NETGROUP= TLI= all + +# Unicos 8.x, Cray-YMP (Bruce Kelly). +unicos8: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS= RANLIB=echo AR=bld ARFLAGS=rv \ + AUX_OBJ= NETGROUP= TLI= all + +# Power_UNIX 2.1.1 (amantel@lerc.nasa.gov) +power_unix_211: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS="-lnsl -lsocket -lgen -lresolv" RANLIB=echo ARFLAGS=rv \ + NETGROUP= AUX_OBJ=setenv.o TLI=-DTLI BUGS="$(BUGS)" all + +# ISC (fc@all.net) +isc: + make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS="-linet -lnsl_s -ldbm" RANLIB=echo ARFLAGS=rv \ + AUX_OBJ="setenv.o strcasecmp.o" EXTRA_CFLAGS="-DENOTCONN=ENAVAIL" \ + NETGROUP= TLI= all + +# Interactive UNIX R3.2 version 4.0 (Bobby D. Wright). +iunix: + make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS="-linet -lnsl_s -ldbm" RANLIB=echo ARFLAGS=rv \ + AUX_OBJ=environ.o strcasecmp.o NETGROUP= TLI= all + +# RTU 6.0 on a Masscomp 5400 (ben@piglet.cr.usgs.gov). When using the +# advanced installation, increment argv before actually looking at it. +rtu: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS= RANLIB=ranlib ARFLAGS=rv AUX_OBJ=environ.o \ + NETGROUP= TLI= all + +# Unixware sans NIS (mc@telebase.com). Compiler dislikes strcasecmp.c. +unixware1: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS="-lsocket -lnsl -lc -L/usr/ucblib -lucb" RANLIB=echo ARFLAGS=rv \ + NETGROUP=$(NETGROUP) AUX_OBJ=environ.o TLI=-DTLI all + +unixware2: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS="-lsocket -lnsl -lgen -lc -L/usr/ucblib -lucb" RANLIB=echo \ + ARFLAGS=rv NETGROUP=$(NETGROUP) AUX_OBJ=environ.o TLI=-DTLI all + +u6000: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS="-lsocket -lnsl" RANLIB=echo ARFLAGS=rv \ + NETGROUP=-DNETGROUP AUX_OBJ="setenv.o strcasecmp.o" TLI=-DTLI all + +# MachTen +machten: + @make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \ + LIBS= RANLIB=ranlib ARFLAGS=rv AUX_OBJ=environ.o \ + NETGROUP= TLI= all + +############################################################### +# System dependencies: TLI (transport-level interface) support. +# +# Uncomment the following macro if your system has System V.4-style TLI +# support (/usr/include/sys/timod.h, /etc/netconfig, and the netdir(3) +# routines). +# +#TLI = -DTLI + +############################################################################### +# System dependencies: differences between ranlib(1) and ar(1) implementations. +# +# Some C compilers (Ultrix 4.x) insist that ranlib(1) be run on an object +# library; some don't care as long as the modules are in the right order; +# some systems don't even have a ranlib(1) command. Make your choice. + +RANLIB = ranlib # have ranlib (BSD-ish UNIX) +#RANLIB = echo # no ranlib (SYSV-ish UNIX) + +ARFLAGS = rv # most systems +#ARFLAGS= rvs # IRIX 4.0.x + +AR = ar +#AR = bld # Unicos 8.x + +############################################################################# +# System dependencies: routines that are not present in the system libraries. +# +# If your system library does not have set/putenv() or strcasecmp(), use +# the ones provided with this source distribution. The environ.c module +# implements setenv(), getenv(), and putenv(). + +AUX_OBJ= setenv.o +#AUX_OBJ= environ.o +#AUX_OBJ= environ.o strcasecmp.o + +# Uncomment the following if your C library does not provide the +# strchr/strrchr/memcmp routines, but comes with index/rindex/bcmp. +# +#STRINGS= -Dstrchr=index -Dstrrchr=rindex -Dmemcmp=bcmp -Dno_memcpy + +################################################################# +# System dependencies: selection of non-default object libraries. +# +# Most System V implementations require that you explicitly specify the +# networking libraries. There is no general consensus, though. +# +#LIBS = -lsocket -lnsl # SysV.4 Solaris 2.x +#LIBS = -lsun # IRIX +#LIBS = -lsocket -linet -lnsl -lnfs # PTX +#LIBS = -linet -lnsl_s -ldbm # ISC +#LIBS = -lnet # Unicos 7 +#LIBS = -linet -lsyslog -ldbm +#LIBS = -lsyslog -lsocket -lnsl + +###################################################### +# System dependencies: system-specific compiler flags. +# +# Apollo Domain/OS offers both bsd and sys5 environments, sometimes +# on the same machine. If your Apollo is primarily sys5.3 and also +# has bsd4.3, uncomment the following to build under bsd and run under +# either environment. +# +#SYSTYPE= -A run,any -A sys,any + +# For MIPS RISC/os 4_52.p3, uncomment the following definition. +# +#SYSTYPE= -sysname bsd43 + +################################################## +# System dependencies: working around system bugs. +# +# -DGETPEERNAME_BUG works around a getpeername(2) bug in some versions of +# Apollo or SYSV.4 UNIX: the wrapper would report that all UDP requests +# come from address 0.0.0.0. The workaround does no harm on other systems. +# +# -DBROKEN_FGETS works around an fgets(3) bug in some System V versions +# (IRIX): fgets() gives up too fast when reading from a network socket. +# The workaround does no harm on other systems. +# +# Some UNIX systems (IRIX) make the error of calling the strtok() library +# routine from other library routines such as, e.g., gethostbyname/addr(). +# The result is that hosts can slip through the wrapper allow/deny filters. +# Compile with -DLIBC_CALLS_STRTOK to avoid the vendor's strtok() routine. +# The workaround does no harm on other systems. +# +# DG/UX 5.4.1 comes with an inet_ntoa() function that returns a structure +# instead of a long integer. Compile with -DINET_ADDR_BUG to work around +# this mutant behavour. Fixed in 5.4R3. +# +# Solaris 2.4 gethostbyname(), in DNS through NIS mode, puts only one +# address in the host address list; all other addresses are treated as +# host name aliases. Compile with -DSOLARIS_24_GETHOSTBYNAME_BUG to work +# around this. The workaround does no harm on other Solaris versions. + +BUGS = -DGETPEERNAME_BUG -DBROKEN_FGETS -DLIBC_CALLS_STRTOK +#BUGS = -DGETPEERNAME_BUG -DBROKEN_FGETS -DINET_ADDR_BUG +#BUGS = -DGETPEERNAME_BUG -DBROKEN_FGETS -DSOLARIS_24_GETHOSTBYNAME_BUG + +########################################################################## +# System dependencies: whether or not your system has NIS (or YP) support. +# +# If your system supports NIS or YP-style netgroups, enable the following +# macro definition. Netgroups are used only for host access control. +# +#NETGROUP= -DNETGROUP + +############################################################### +# System dependencies: whether or not your system has vsyslog() +# +# If your system supports vsyslog(), comment out the following definition. +# If in doubt leave it in, it won't harm. + +VSYSLOG = -Dvsyslog=myvsyslog + +# End of the system dependencies. +################################# + +############################## +# Start of the optional stuff. + +########################################### +# Optional: Turning on language extensions +# +# Instead of the default access control language that is documented in +# the hosts_access.5 document, the wrappers can be configured to +# implement an extensible language documented in the hosts_options.5 +# document. This language is implemented by the "options.c" source +# module, which also gives hints on how to add your own extensions. +# Uncomment the next definition to turn on the language extensions +# (examples: allow, deny, banners, twist and spawn). +# +#STYLE = -DPROCESS_OPTIONS # Enable language extensions. + +################################################################ +# Optional: Changing the default disposition of logfile records +# +# By default, logfile entries are written to the same file as used for +# sendmail transaction logs. See your /etc/syslog.conf file for actual +# path names of logfiles. The tutorial section in the README file +# gives a brief introduction to the syslog daemon. +# +# Change the FACILITY definition below if you disagree with the default +# disposition. Some syslog versions (including Ultrix 4.x) do not provide +# this flexibility. +# +# If nothing shows up on your system, it may be that the syslog records +# are sent to a dedicated loghost. It may also be that no syslog daemon +# is running at all. The README file gives pointers to surrogate syslog +# implementations for systems that have no syslog library routines or +# no syslog daemons. When changing the syslog.conf file, remember that +# there must be TABs between fields. +# +# The LOG_XXX names below are taken from the /usr/include/syslog.h file. + +FACILITY= LOG_MAIL # LOG_MAIL is what most sendmail daemons use + +# The syslog priority at which successful connections are logged. + +SEVERITY= LOG_INFO # LOG_INFO is normally not logged to the console + +########################### +# Optional: Reduce DNS load +# +# When looking up the address for a host.domain name, the typical DNS +# code will first append substrings of your own domain, so it tries +# host.domain.your.own.domain, then host.domain.own.domain, and then +# host.domain. The APPEND_DOT feature stops this waste of cycles. It is +# off by default because it causes problems on sites that don't use DNS +# and with Solaris < 2.4. APPEND_DOT will not work with hostnames taken +# from /etc/hosts or from NIS maps. It does work with DNS through NIS. +# +# DOT= -DAPPEND_DOT + +################################################## +# Optional: Always attempt remote username lookups +# +# By default, the wrappers look up the remote username only when the +# access control rules require them to do so. +# +# Username lookups require that the remote host runs a daemon that +# supports an RFC 931 like protocol. Remote user name lookups are not +# possible for UDP-based connections, and can cause noticeable delays +# with connections from non-UNIX PCs. On some systems, remote username +# lookups can trigger a kernel bug, causing loss of service. The README +# file describes how to find out if your UNIX kernel has that problem. +# +# Uncomment the following definition if the wrappers should always +# attempt to get the remote user name. If this is not enabled you can +# still do selective username lookups as documented in the hosts_access.5 +# and hosts_options.5 manual pages (`nroff -man' format). +# +#AUTH = -DALWAYS_RFC931 +# +# The default username lookup timeout is 10 seconds. This may not be long +# enough for slow hosts or networks, but is enough to irritate PC users. + +RFC931_TIMEOUT = 10 + +###################################################### +# Optional: Changing the default file protection mask +# +# On many systems, network daemons and other system processes are started +# with a zero umask value, so that world-writable files may be produced. +# It is a good idea to edit your /etc/rc* files so that they begin with +# an explicit umask setting. On our site we use `umask 022' because it +# does not break anything yet gives adequate protection against tampering. +# +# The following macro specifies the default umask for processes run under +# control of the daemon wrappers. Comment it out only if you are certain +# that inetd and its children are started with a safe umask value. + +UMASK = -DDAEMON_UMASK=022 + +####################################### +# Optional: Turning off access control +# +# By default, host access control is enabled. To disable host access +# control, comment out the following definition. Host access control +# can also be turned off at runtime by providing no or empty access +# control tables. + +ACCESS = -DHOSTS_ACCESS + +######################################################## +# Optional: Changing the access control table pathnames +# +# The HOSTS_ALLOW and HOSTS_DENY macros define where the programs will +# look for access control information. Watch out for the quotes and +# backslashes when you make changes. + +TABLES = -DHOSTS_DENY=\"/etc/hosts.deny\" -DHOSTS_ALLOW=\"/etc/hosts.allow\" + +#################################################### +# Optional: dealing with host name/address conflicts +# +# By default, the software tries to protect against hosts that claim to +# have someone elses host name. This is relevant for network services +# whose authentication depends on host names, such as rsh and rlogin. +# +# With paranoid mode on, connections will be rejected when the host name +# does not match the host address. Connections will also be rejected when +# the host name is available but cannot be verified. +# +# Comment out the following definition if you want more control over such +# requests. When paranoid mode is off and a host name double check fails, +# the client can be matched with the PARANOID access control pattern. +# +# Paranoid mode implies hostname lookup. In order to disable hostname +# lookups altogether, see the next section. + +PARANOID= -DPARANOID + +######################################## +# Optional: turning off hostname lookups +# +# By default, the software always attempts to look up the client +# hostname. With selective hostname lookups, the client hostname +# lookup is postponed until the name is required by an access control +# rule or by a %letter expansion. +# +# In order to perform selective hostname lookups, disable paranoid +# mode (see previous section) and comment out the following definition. + +HOSTNAME= -DALWAYS_HOSTNAME + +############################################# +# Optional: Turning on host ADDRESS checking +# +# Optionally, the software tries to protect against hosts that pretend to +# have someone elses host address. This is relevant for network services +# whose authentication depends on host names, such as rsh and rlogin, +# because the network address is used to look up the remote host name. +# +# The protection is to refuse TCP connections with IP source routing +# options. +# +# This feature cannot be used with SunOS 4.x because of a kernel bug in +# the implementation of the getsockopt() system call. Kernel panics have +# been observed for SunOS 4.1.[1-3]. Symptoms are "BAD TRAP" and "Data +# fault" while executing the tcp_ctloutput() kernel function. +# +# Reportedly, Sun patch 100804-03 or 101790 fixes this for SunOS 4.1.x. +# +# Uncomment the following macro definition if your getsockopt() is OK. +# +# -DKILL_IP_OPTIONS is not needed on modern UNIX systems that can stop +# source-routed traffic in the kernel. Examples: 4.4BSD derivatives, +# Solaris 2.x, and Linux. See your system documentation for details. +# +# KILL_OPT= -DKILL_IP_OPTIONS + +## End configuration options +############################ + +# Protection against weird shells or weird make programs. + +SHELL = /bin/sh +.c.o:; $(CC) $(CFLAGS) -c $*.c + +CFLAGS = -O -DFACILITY=$(FACILITY) $(ACCESS) $(PARANOID) $(NETGROUP) \ + $(BUGS) $(SYSTYPE) $(AUTH) $(UMASK) \ + -DREAL_DAEMON_DIR=\"$(REAL_DAEMON_DIR)\" $(STYLE) $(KILL_OPT) \ + -DSEVERITY=$(SEVERITY) -DRFC931_TIMEOUT=$(RFC931_TIMEOUT) \ + $(UCHAR) $(TABLES) $(STRINGS) $(TLI) $(EXTRA_CFLAGS) $(DOT) \ + $(VSYSLOG) $(HOSTNAME) + +LIB_OBJ= hosts_access.o options.o shell_cmd.o rfc931.o eval.o \ + hosts_ctl.o refuse.o percent_x.o clean_exit.o $(AUX_OBJ) \ + $(FROM_OBJ) fix_options.o socket.o tli.o workarounds.o \ + update.o misc.o diag.o percent_m.o myvsyslog.o + +FROM_OBJ= fromhost.o + +KIT = README miscd.c tcpd.c fromhost.c hosts_access.c shell_cmd.c \ + tcpd.h tcpdmatch.c Makefile hosts_access.5 strcasecmp.c BLURB rfc931.c \ + tcpd.8 eval.c hosts_access.3 hosts_ctl.c percent_x.c options.c \ + clean_exit.c environ.c patchlevel.h fix_options.c workarounds.c \ + socket.c tli.c DISCLAIMER fakelog.c safe_finger.c hosts_options.5 \ + CHANGES try-from.c update.c ptx.c vfprintf.c tli-sequent.c \ + tli-sequent.h misc.c diag.c ncr.c tcpdchk.c percent_m.c \ + myvsyslog.c mystdarg.h printf.ck README.IRIX Banners.Makefile \ + refuse.c tcpdchk.8 setenv.c inetcf.c inetcf.h scaffold.c \ + scaffold.h tcpdmatch.8 README.NIS + +LIB = libwrap.a + +all other: config-check tcpd tcpdmatch try-from safe_finger tcpdchk + +# Invalidate all object files when the compiler options (CFLAGS) have changed. + +config-check: + @set +e; test -n "$(REAL_DAEMON_DIR)" || { make; exit 1; } + @set +e; echo $(CFLAGS) >/tmp/cflags.$$$$ ; \ + if cmp cflags /tmp/cflags.$$$$ ; \ + then rm /tmp/cflags.$$$$ ; \ + else mv /tmp/cflags.$$$$ cflags ; \ + fi >/dev/null 2>/dev/null + +$(LIB): $(LIB_OBJ) + rm -f $(LIB) + $(AR) $(ARFLAGS) $(LIB) $(LIB_OBJ) + -$(RANLIB) $(LIB) + +tcpd: tcpd.o $(LIB) + $(CC) $(CFLAGS) -o $@ tcpd.o $(LIB) $(LIBS) + +miscd: miscd.o $(LIB) + $(CC) $(CFLAGS) -o $@ miscd.o $(LIB) $(LIBS) + +safe_finger: safe_finger.o $(LIB) + $(CC) $(CFLAGS) -o $@ safe_finger.o $(LIB) $(LIBS) + +TCPDMATCH_OBJ = tcpdmatch.o fakelog.o inetcf.o scaffold.o + +tcpdmatch: $(TCPDMATCH_OBJ) $(LIB) + $(CC) $(CFLAGS) -o $@ $(TCPDMATCH_OBJ) $(LIB) $(LIBS) + +try-from: try-from.o fakelog.o $(LIB) + $(CC) $(CFLAGS) -o $@ try-from.o fakelog.o $(LIB) $(LIBS) + +TCPDCHK_OBJ = tcpdchk.o fakelog.o inetcf.o scaffold.o + +tcpdchk: $(TCPDCHK_OBJ) $(LIB) + $(CC) $(CFLAGS) -o $@ $(TCPDCHK_OBJ) $(LIB) $(LIBS) + +shar: $(KIT) + @shar $(KIT) + +kit: $(KIT) + @makekit $(KIT) + +files: + @echo $(KIT) + +archive: + $(ARCHIVE) $(KIT) + +clean: + rm -f tcpd miscd safe_finger tcpdmatch tcpdchk try-from *.[oa] core \ + cflags + +tidy: clean + chmod -R a+r . + chmod 755 . + +# Enable all bells and whistles for linting. + +lint: tcpd_lint miscd_lint match_lint chk_lint + +tcpd_lint: + lint -DFACILITY=LOG_MAIL -DHOSTS_ACCESS -DPARANOID -DNETGROUP \ + -DGETPEERNAME_BUG -DDAEMON_UMASK=022 -DSEVERITY=$(SEVERITY) \ + $(TABLES) -DKILL_IP_OPTIONS -DPROCESS_OPTIONS \ + -DRFC931_TIMEOUT=$(RFC931_TIMEOUT) -DALWAYS_RFC931 \ + -DREAL_DAEMON_DIR=\"$(REAL_DAEMON_DIR)\" \ + -Dvsyslog=myvsyslog \ + tcpd.c fromhost.c socket.c tli.c hosts_access.c \ + shell_cmd.c refuse.c rfc931.c eval.c percent_x.c clean_exit.c \ + options.c setenv.c fix_options.c workarounds.c update.c misc.c \ + diag.c myvsyslog.c percent_m.c + +miscd_lint: + lint -DFACILITY=LOG_MAIL -DHOSTS_ACCESS -DPARANOID -DNETGROUP \ + -DGETPEERNAME_BUG -DDAEMON_UMASK=022 -DSEVERITY=$(SEVERITY) \ + $(TABLES) -DKILL_IP_OPTIONS -DPROCESS_OPTIONS \ + -DRFC931_TIMEOUT=$(RFC931_TIMEOUT) -DALWAYS_RFC931 \ + -DREAL_DAEMON_DIR=\"$(REAL_DAEMON_DIR)\" \ + -Dvsyslog=myvsyslog \ + miscd.c fromhost.c socket.c tli.c hosts_access.c \ + shell_cmd.c refuse.c rfc931.c eval.c percent_x.c clean_exit.c \ + options.c setenv.c fix_options.c workarounds.c update.c misc.c \ + diag.c myvsyslog.c percent_m.c + +match_lint: + lint -DFACILITY=LOG_MAIL -DSEVERITY=$(SEVERITY) -DHOSTS_ACCESS \ + -DPARANOID $(TABLES) -DNETGROUP -DPROCESS_OPTIONS -DRFC931_TIMEOUT=10 \ + -DREAL_DAEMON_DIR=\"$(REAL_DAEMON_DIR)\" \ + -Dvsyslog=myvsyslog \ + tcpdmatch.c hosts_access.c eval.c percent_x.c options.c workarounds.c \ + update.c socket.c misc.c diag.c myvsyslog.c percent_m.c setenv.c \ + inetcf.c scaffold.c + +chk_lint: + lint -DFACILITY=LOG_MAIL -DSEVERITY=$(SEVERITY) -DHOSTS_ACCESS \ + -DPARANOID $(TABLES) -DNETGROUP -DPROCESS_OPTIONS -DRFC931_TIMEOUT=10 \ + -DREAL_DAEMON_DIR=\"$(REAL_DAEMON_DIR)\" \ + -Dvsyslog=myvsyslog \ + tcpdchk.c eval.c percent_x.c options.c update.c workarounds.c \ + setenv.c misc.c diag.c myvsyslog.c percent_m.c inetcf.c scaffold.c + +printfck: + printfck -f printf.ck \ + tcpd.c fromhost.c socket.c tli.c hosts_access.c \ + shell_cmd.c refuse.c rfc931.c eval.c percent_x.c clean_exit.c \ + options.c setenv.c fix_options.c workarounds.c update.c misc.c \ + diag.c myvsyslog.c percent_m.c >aap.c + lint -DFACILITY=LOG_MAIL -DHOSTS_ACCESS -DPARANOID -DNETGROUP \ + -DGETPEERNAME_BUG -DDAEMON_UMASK=022 -DSEVERITY=$(SEVERITY) \ + $(TABLES) -DKILL_IP_OPTIONS -DPROCESS_OPTIONS \ + -DRFC931_TIMEOUT=$(RFC931_TIMEOUT) -DALWAYS_RFC931 \ + -DREAL_DAEMON_DIR=\"$(REAL_DAEMON_DIR)\" -Dvsyslog=myvsyslog aap.c + printfck -f printf.ck \ + tcpdchk.c eval.c percent_x.c options.c update.c workarounds.c \ + setenv.c misc.c diag.c myvsyslog.c percent_m.c inetcf.c scaffold.c \ + >aap.c + lint -DFACILITY=LOG_MAIL -DSEVERITY=$(SEVERITY) -DHOSTS_ACCESS \ + -DPARANOID $(TABLES) -DNETGROUP -DPROCESS_OPTIONS -DRFC931_TIMEOUT=10 \ + -Dvsyslog=myvsyslog -DREAL_DAEMON_DIR=\"$(REAL_DAEMON_DIR)\" + +# Internal compilation dependencies. + +clean_exit.o: cflags +clean_exit.o: tcpd.h +diag.o: cflags +diag.o: mystdarg.h +diag.o: tcpd.h +environ.o: cflags +eval.o: cflags +eval.o: tcpd.h +fakelog.o: cflags +fakelog.o: mystdarg.h +fix_options.o: cflags +fix_options.o: tcpd.h +fromhost.o: cflags +fromhost.o: tcpd.h +hosts_access.o: cflags +hosts_access.o: tcpd.h +hosts_ctl.o: cflags +hosts_ctl.o: tcpd.h +inetcf.o: cflags +inetcf.o: inetcf.h +inetcf.o: tcpd.h +misc.o: cflags +misc.o: tcpd.h +miscd.o: cflags +miscd.o: patchlevel.h +miscd.o: tcpd.h +myvsyslog.o: cflags +myvsyslog.o: mystdarg.h +myvsyslog.o: tcpd.h +ncr.o: cflags +ncr.o: tcpd.h +options.o: cflags +options.o: tcpd.h +percent_m.o: cflags +percent_m.o: mystdarg.h +percent_x.o: cflags +percent_x.o: tcpd.h +ptx.o: cflags +ptx.o: tcpd.h +refuse.o: cflags +refuse.o: tcpd.h +rfc931.o: cflags +rfc931.o: tcpd.h +safe_finger.o: cflags +scaffold.o: cflags +scaffold.o: scaffold.h +scaffold.o: tcpd.h +setenv.o: cflags +shell_cmd.o: cflags +shell_cmd.o: tcpd.h +socket.o: cflags +socket.o: tcpd.h +strcasecmp.o: cflags +tcpd.o: cflags +tcpd.o: patchlevel.h +tcpd.o: tcpd.h +tcpdchk.o: cflags +tcpdchk.o: inetcf.h +tcpdchk.o: scaffold.h +tcpdchk.o: tcpd.h +tcpdmatch.o: cflags +tcpdmatch.o: scaffold.h +tcpdmatch.o: tcpd.h +tli-sequent.o: cflags +tli-sequent.o: tcpd.h +tli-sequent.o: tli-sequent.h +tli.o: cflags +tli.o: tcpd.h +try-from.o: cflags +try-from.o: tcpd.h +update.o: cflags +update.o: mystdarg.h +update.o: tcpd.h +vfprintf.o: cflags +workarounds.o: cflags +workarounds.o: tcpd.h diff --git a/contrib/tcp_wrappers/README b/contrib/tcp_wrappers/README new file mode 100644 index 000000000000..98b6b472a4d5 --- /dev/null +++ b/contrib/tcp_wrappers/README @@ -0,0 +1,1038 @@ +@(#) README 1.30 97/03/21 19:27:21 + +This is the 7.6 version of the TCP/IP daemon wrapper package. + +Thank you for using this program. If you like it, send me a postcard. +My postal address is at the bottom of this file. + +Read the BLURB file for a brief summary of what is new. The CHANGES +file gives a complete account of differences with respect to previous +releases. + +Announcements of new releases of this software are posted to Usenet +(comp.security.unix, comp.unix.admin), to the cert-tools mailing list, +and to a dedicated mailing list. You can subscribe to the dedicated +mailing list by sending an email message to majordomo@wzv.win.tue.nl +with in the body (not subject): subscribe tcp-wrappers-announce. + +Table of contents +----------------- + + 1 - Introduction + 2 - Disclaimer + 3 - Tutorials + 3.1 - How it works + 3.2 - Where the logging information goes + 4 - Features + 4.1 - Access control + 4.2 - Host name spoofing + 4.3 - Host address spoofing + 4.4 - Client username lookups + 4.5 - Language extensions + 4.6 - Multiple ftp/gopher/www archives on one host + 4.7 - Banner messages + 4.8 - Sequence number guessing + 5 - Other works + 5.1 - Related documents + 5.2 - Related software + 6 - Limitations + 6.1 - Known wrapper limitations + 6.2 - Known system software bugs + 7 - Configuration and installation + 7.1 - Easy configuration and installation + 7.2 - Advanced configuration and installation + 7.3 - Daemons with arbitrary path names + 7.4 - Building and testing the access control rules + 7.5 - Other applications + 8 - Acknowledgements + +1 - Introduction +---------------- + +With this package you can monitor and filter incoming requests for the +SYSTAT, FINGER, FTP, TELNET, RLOGIN, RSH, EXEC, TFTP, TALK, and other +network services. + +It supports both 4.3BSD-style sockets and System V.4-style TLI. Praise +yourself lucky if you don't know what that means. + +The package provides tiny daemon wrapper programs that can be installed +without any changes to existing software or to existing configuration +files. The wrappers report the name of the client host and of the +requested service; the wrappers do not exchange information with the +client or server applications, and impose no overhead on the actual +conversation between the client and server applications. + +Optional features are: access control to restrict what systems can +connect to what network daemons; client user name lookups with the RFC +931 etc. protocol; additional protection against hosts that pretend to +have someone elses host name; additional protection against hosts that +pretend to have someone elses host address. + +The programs are very portable. Build procedures are provided for many +common (and not so common) environments, and guidelines are provided in +case your environment is not among them. + +Requirements are that network daemons are spawned by a super server +such as the inetd; a 4.3BSD-style socket programming interface and/or +System V.4-style TLI programming interface; and the availability of a +syslog(3) library and of a syslogd(8) daemon. The wrappers should run +without modification on any system that satisfies these requirements. +Workarounds have been implemented for several common bugs in systems +software. + +What to do if this is your first encounter with the wrapper programs: +1) read the tutorial sections for an introduction to the relevant +concepts and terminology; 2) glance over the security feature sections +in this document; 3) follow the installation instructions (easy or +advanced). I recommend that you first use the default security feature +settings. Run the wrappers for a few days to become familiar with +their logs, before doing anything drastic such as cutting off access or +installing booby traps. + +2 - Disclaimer +-------------- + +The wrapper programs rely on source address information obtained from +network packets. This information is provided by the client host. It is +not 100 percent reliable, although the wrappers do their best to expose +forgeries. + +In the absence of cryptographic protection of message contents, and of +cryptographic authentication of message originators, all data from the +network should be treated with sound scepticism. + +THIS RESTRICTION IS BY NO MEANS SPECIFIC TO THE TCP/IP PROTOCOLS. + +3 - Tutorials +------------- + +The tutorial sections give a gentle introduction to the operation of +the wrapper programs, and introduce some of the terminology that is +used in the remainder of the document: client, server, the inetd and +syslogd daemons, and their configuration files. + +3.1 - How it works +------------------ + +Almost every application of the TCP/IP protocols is based on a client- +server model. For example, when a user invokes the telnet command to +connect to one of your systems, a telnet server process is executed on +the target host. The telnet server process connects the user to a login +process. A few examples of client and server programs are shown in the +table below: + + client server application + -------------------------------- + telnet telnetd remote login + ftp ftpd file transfer + finger fingerd show users + +The usual approach is to run one single daemon process that waits for +all kinds of incoming network connections. Whenever a connection is +established, this daemon (usually called inetd) runs the appropriate +server program and goes back to sleep, waiting for other connections. + +The wrapper programs rely on a simple, but powerful mechanism. Instead +of directly running the desired server program, the inetd is tricked +into running a small wrapper program. The wrapper logs the client host +name or address and performs some additional checks. When all is well, +the wrapper executes the desired server program and goes away. + +The wrapper programs have no interaction with the client user (or with +the client process). Nor do the wrappers interact with the server +application. This has two major advantages: 1) the wrappers are +application-independent, so that the same program can protect many +kinds of network services; 2) no interaction also means that the +wrappers are invisible from outside (at least for authorized users). + +Another important property is that the wrapper programs are active only +when the initial contact between client and server is established. Once +a wrapper has done its work there is no overhead on the client-server +conversation. + +The simple mechanism has one major drawback: the wrappers go away after +the initial contact between client and server processes, so the +wrappers are of little use with network daemons that service more than +one client. The wrappers would only see the first client attempt to +contact such a server. The NFS mount daemon is a typical example of a +daemon that services requests from multiple clients. See the section on +related software for ways to deal with such server programs. + +There are two ways to use the wrapper programs: + +1) The easy way: move network daemons to some other directory and fill + the resulting holes with copies of the wrapper programs. This + approach involves no changes to system configuration files, so there + is very little risk of breaking things. + +2) The advanced way: leave the network daemons alone and modify the + inetd configuration file. For example, an entry such as: + + tftp dgram udp wait root /usr/etc/tcpd in.tftpd -s /tftpboot + + When a tftp request arrives, inetd will run the wrapper program + (tcpd) with a process name `in.tftpd'. This is the name that the + wrapper will use when logging the request and when scanning the + optional access control tables. `in.tftpd' is also the name of the + server program that the wrapper will attempt to run when all is + well. Any arguments (`-s /tftpboot' in this particular example) are + transparently passed on to the server program. + +For an account of the history of the wrapper programs, with real-life +examples, see the section below on related documents. + +3.2 - Where the logging information goes +---------------------------------------- + +The wrapper programs send their logging information to the syslog +daemon (syslogd). The disposition of the wrapper logs is determined by +the syslog configuration file (usually /etc/syslog.conf). Messages are +written to files, to the console, or are forwarded to a @loghost. Some +syslogd versions can even forward messages down a |pipeline. + +Older syslog implementations (still found on Ultrix systems) only +support priority levels ranging from 9 (debug-level messages) to 0 +(alerts). All logging information of the specified priority level or +more urgent is written to the same destination. In the syslog.conf +file, priority levels are specified in numerical form. For example, + + 8/usr/spool/mqueue/syslog + +causes all messages with priority 8 (informational messages), and +anything that is more urgent, to be appended to the file +/usr/spool/mqueue/syslog. + +Newer syslog implementations support message classes in addition to +priority levels. Examples of message classes are: mail, daemon, auth +and news. In the syslog.conf file, priority levels are specified with +symbolic names: debug, info, notice, ..., emerg. For example, + + mail.debug /var/log/syslog + +causes all messages of class mail with priority debug (or more urgent) +to be appended to the /var/log/syslog file. + +By default, the wrapper logs go to the same place as the transaction +logs of the sendmail daemon. The disposition can be changed by editing +the Makefile and/or the syslog.conf file. Send a `kill -HUP' to the +syslogd after changing its configuration file. Remember that syslogd, +just like sendmail, insists on one or more TABs between the left-hand +side and the right-hand side expressions in its configuration file. + +Solaris 2.x note: the syslog daemon depends on the m4 macro processor. +The m4 program is installed as part of the software developer packages. + +Trouble shooting note: when the syslogging does not work as expected, +run the program by hand (`syslogd -d') and see what really happens. + +4 - Features +------------ + +4.1 - Access control +-------------------- + +When compiled with -DHOSTS_ACCESS, the wrapper programs support a +simple form of access control. Access can be controlled per host, per +service, or combinations thereof. The software provides hooks for the +execution of shell commands when an access control rule fires; this +feature may be used to install "booby traps". For details, see the +hosts_access.5 manual page, which is in `nroff -man' format. A later +section describes how you can test your access control rules. + +Access control can also be used to connect clients to the "right" +service. What is right may depend on the requested service, the origin +of the request, and what host address the client connects to. Examples: + +(1) A gopher or www database speaks native language when contacted from + within the country, otherwise it speaks English. + +(2) A service provider offers different ftp, gopher or www services + with different internet hostnames from one host (section 4.6). + +Access control is enabled by default. It can be turned off by editing +the Makefile, or by providing no access control tables. The install +instructions below describe the Makefile editing process. + +The hosts_options.5 manual page (`nroff -man' format) documents an +extended version of the access control language. The extensions are +disabled by default. See the section below on language extensions. + +Later System V implementations provide the Transport Level Interface +(TLI), a network programming interface that performs functions similar +to the Berkeley socket programming interface. Like Berkeley sockets, +TLI was designed to cover multiple protocols, not just Internet. + +When the wrapper discovers that the TLI interface sits on top of a +TCP/IP or UDP/IP conversation it uses this knowledge to provide the +same functions as with traditional socket-based applications. When +some other protocol is used underneath TLI, the host address will be +some universal magic cookie that may not even be usable for access +control purposes. + +4.2 - Host name spoofing +------------------------ + +With some network applications, such as RSH or RLOGIN, the client host +name plays an important role in the authentication process. Host name +information can be reliable when lookups are done from a _local_ hosts +table, provided that the client IP address can be trusted. + +With _distributed_ name services, authentication schemes that rely on +host names become more problematic. The security of your system now may +depend on some far-away DNS (domain name server) outside your own +control. + +The wrapper programs verify the client host name that is returned by +the address->name DNS server, by asking for a second opinion. To this +end, the programs look at the name and addresses that are returned by +the name->address DNS server, which may be an entirely different host. + +If any name or address discrepancies are found, or if the second DNS +opinion is not available, the wrappers assume that one of the two name +servers is lying, and assume that the client host pretends to have +someone elses host name. + +When compiled with -DPARANOID, the wrappers will always attempt to look +up and double check the client host name, and will always refuse +service in case of a host name/address discrepancy. This is a +reasonable policy for most systems. + +When compiled without -DPARANOID, the wrappers by default still perform +hostname lookup. You can match hosts with a name/address discrepancy +with the PARANOID wildcard and decide whether or not to grant service. + +Automatic hostname verification is enabled by default. Automatic +hostname lookups and verification can be turned off by editing the +Makefile. The configuration and installation section below describes +the Makefile editing process. + +4.3 - Host address spoofing +--------------------------- + +While host name spoofing can be found out by asking a second opinion, +it is much harder to find out that a host claims to have someone elses +network address. And since host names are deduced from network +addresses, address spoofing is at least as effective as name spoofing. + +The wrapper programs can give additional protection against hosts that +claim to have an address that lies outside their own network. For +example, some far-away host that claims to be a trusted host within +your own network. Such things are possible even while the impersonated +system is up and running. + +This additional protection is not an invention of my own; it has been +present for at least five years in the BSD rsh and rlogin daemons. +Unfortunately, that feature was added *after* 4.3 BSD came out, so that +very few, if any, UNIX vendors have adopted it. Our site, and many +other ones, has been running these enhanced daemons for several years, +and without any ill effects. + +When the wrapper programs are compiled with -DKILL_IP_OPTIONS, the +programs refuse to service TCP connections with IP source routing +options. -DKILL_IP_OPTIONS is not needed on modern UNIX systems +that can stop source-routed traffic in the kernel. Examples are +4.4BSD derivatives, Solaris 2.x, and Linux. See your system manuals +for details. + +If you are going to use this feature on SunOS 4.1.x you should apply +patch 100804-03+ or 101790-something depending on your SunOS version. +Otherwise you may experience "BAD TRAP" and "Data fault" panics when +the getsockopt() system call is executed after a TCP RESET has been +received. This is a kernel bug, it is not the fault of the wrappers. + +The feature is disabled by default. It can be turned on by editing the +Makefile. The configuration and installation section below describes +the Makefile editing process. + +UDP services do not benefit from this additional protection. With UDP, +all you can be certain of is the network packet's destination address. + +4.4 - Client username lookups +----------------------------- + +The protocol proposed in RFC 931 provides a means to obtain the client +user name from the client host. The requirement is that the client +host runs an RFC 931-compliant daemon. The information provided by such +a daemon is not intended to be used for authentication purposes, but it +can provide additional information about the owner of a TCP connection. + +The RFC 931 protocol has diverged into different directions (IDENT, +TAP, RFC 1413). To add to the confusion, they all use the same network +port. The daemon wrappers implement a common subset of the protocols. + +There are some limitations: the number of hosts that run an RFC 931 (or +compatible) daemon is limited (but growing); client user name lookups +do not work for datagram (UDP) services. More seriously, client user +name lookups can cause noticeable delays with connections from non-UNIX +PCs. Recent PC software seem to have fixed this (for example NCSA +telnet). The wrappers use a 10-second timeout for RFC931 lookups, to +accommodate slow networks and slow hosts. + +By default, the wrappers will do username lookup only when the access +control rules require them to do so (via user@host client patterns, see +the hosts_access.5 manual page) or when the username is needed for +%<letter> expansions. + +You can configure the wrappers to always perform client username +lookups, by editing the Makefile. The client username lookup timeout +period (10 seconds default) can be changed by editing the Makefile. The +installation sections below describe the Makefile editing process. + +On System V with TLI-based network services, client username lookups +will be possible only when the underlying network protocol is TCP/IP. + +4.5 - Language extensions +------------------------- + +The wrappers sport only a limited number of features. This is for a +good reason: programs that run at high privilege levels must be easy to +verify. And the smaller a program, the easier to verify. There is, +however, a provision to add features. + +The options.c module provides a framework for language extensions. +Quite a few extensions have already been implemented; they are +documented in the hosts_options.5 document, which is in `nroff -man' +format. Examples: changing the severity level at which a request for +service is logged; "allow" and "deny" keywords; running a customized +server instead of the standard one; many others. + +The language extensions are not enabled by default because they +introduce an incompatible change to the access control language +syntax. Instructions to enable the extensions are given in the +Makefile. + +4.6 - Multiple ftp/gopher/www archives on one host +-------------------------------------------------- + +Imagine one host with multiple internet addresses. These addresses do +not need to have the same internet hostname. Thus, it is possible to +offer services with different internet hostnames from just one host. + +Service providers can use this to offer organizations a presence on the +"net" with their own internet hostname, even when those organizations +aren't connected to the Internet at all. To the end user it makes no +difference, because applications use internet hostnames. + +There are several ways to assign multiple addresses to one machine. +The nice way is to take an existing network interface and to assign +additional internet addresses with the `ifconfig' command. Examples: + + Solaris 2: ifconfig le0:1 <address> netmask <mask> up + 4.4 BSD: ifconfig en0 alias <address> netmask <mask> + +On other systems one has to increase the number of network interfaces: +either with hardware interfaces, or with pseudo interfaces like SLIP or +PPP. The interfaces do not need to be attached to anything. They just +need to be up and to be assigned a suitable internet address and mask. + +With the wrapper software, `daemon@host' access control patterns can be +used to distinguish requests by the network address that they are aimed +at. Judicious use of the `twist' option (see the hosts_options.5 file, +`nroff -man' format) can guide the requests to the right server. These +can be servers that live in separate chroot areas, or servers modified +to take additional context from the command line, or a combination. + +Another way is to modify gopher or www listeners so that they bind to +only one specific network address. Multiple gopher or www servers can +then be run side by side, each taking requests sent to its respective +network address. + +4.7 - Banner messages +--------------------- + +Some sites are required to present an informational message to users +before they attempt to login. Banner messages can also be useful when +denying service: instead of simply dropping the connection a polite +explanation is given first. Finally, banners can be used to give your +system a more personal touch. + +The wrapper software provides easy-to-use tools to generate pre-login +banners for ftp, telnet, rlogin etc. from a single prototype banner +textfile. Details on banners and on-the-fly %<letter> expansions are +given in the hosts_options.5 manual page (`nroff -man' format). An +example is given in the file Banners.Makefile. + +In order to support banner messages the wrappers have to be built with +language extensions enabled. See the section on language extensions. + +4.8 - Sequence number guessing +------------------------------ + +Recently, systems came under attack from intruders that exploited a +well-known weakness in TCP/IP sequence number generators. This +weakness allows intruders to impersonate trusted hosts. Break-ins have +been reported via the rsh service. In fact, any network service can be +exploited that trusts the client host name or address. + +A long-term solution is to stop using network services that trust the +client host name or address, and to use data encryption instead. + +A short-term solution, as outlined in in CERT advisory CA-95:01, is to +configure network routers so that they discard datagrams from "outside" +with an "inside" source address. This approach is most fruitful when +you do not trust any hosts outside your local network. + +The IDENT (RFC931 etc.) client username lookup protocol can help to +detect host impersonation attacks. Before accepting a client request, +the wrappers can query the client's IDENT server and find out that the +client never sent that request. + +When the client host provides IDENT service, a negative IDENT lookup +result (the client matches `UNKNOWN@host') is strong evidence of a host +impersonation attack. + +A positive IDENT lookup result (the client matches `KNOWN@host') is +less trustworthy. It is possible for an attacker to spoof both the +client request and the IDENT lookup connection, although doing so +should be much harder than spoofing just a client request. Another +possibility is that the client's IDENT server is lying. + +Client username lookups are described in more detail in a previous +section. Pointers to IDENT daemon software are described in the section +on related software. + +5 - Other works +--------------- + +5.1 - Related documents +----------------------- + +The war story behind the tcp wrapper tools is described in: + + W.Z. Venema, "TCP WRAPPER, network monitoring, access control and + booby traps", UNIX Security Symposium III Proceedings (Baltimore), + September 1992. + + ftp.win.tue.nl:/pub/security/tcp_wrapper.ps.Z (postscript) + ftp.win.tue.nl:/pub/security/tcp_wrapper.txt.Z (flat text) + +The same cracker is also described in: + + W.R. Cheswick, "An Evening with Berferd, In Which a Cracker is + Lured, Endured, and Studied", Proceedings of the Winter USENIX + Conference (San Francisco), January 1992. + + research.att.com:/dist/internet_security/berferd.ps + +An updated version of the latter paper appeared in: + + W.R. Cheswick, S.M. Bellovin, "Firewalls and Internet Security", + Addison-Wesley, 1994. + +Discussions on internet firewalls are archived on ftp.greatcircle.com. +Subscribe to the mailing list by sending a message to + + majordomo@greatcircle.com + +With in the body (not subject): subscribe firewalls. + +5.2 - Related software +---------------------- + +Network daemons etc. with enhanced logging capabilities can generate +massive amounts of information: our 150+ workstations generate several +hundred kbytes each day. egrep-based filters can help to suppress some +of the noise. A more powerful tool is the Swatch monitoring system by +Stephen E. Hansen and E. Todd Atkins. Swatch can process log files in +real time and can associate arbitrary actions with patterns; its +applications are by no means restricted to security. Swatch is +available ftp.stanford.edu, directory /general/security-tools/swatch. + +Socks, described in the UNIX Security III proceedings, can be used to +control network traffic from hosts on an internal network, through a +firewall host, to the outer world. Socks consists of a daemon that is +run on the firewall host, and of a library with routines that redirect +application socket calls through the firewall daemon. Socks is +available from s1.gov in /pub/firewalls/socks.tar.Z. + +For a modified Socks version by Ying-Da Lee (ylee@syl.dl.nec.com) try +ftp.nec.com, directory /pub/security/socks.cstc. + +Tcpr is a set of perl scripts by Paul Ziemba that enable you to run ftp +and telnet commands across a firewall. Unlike socks it can be used with +unmodified client software. Available from ftp.alantec.com, /pub/tcpr. + +The TIS firewall toolkit provides a multitude of tools to build your +own internet firewall system. ftp.tis.com, directory /pub/firewalls. + +Versions of rshd and rlogind, modified to report the client user name +in addition to the client host name, are available for anonymous ftp +(ftp.win.tue.nl:/pub/security/logdaemon-XX.tar.Z). These programs are +drop-in replacements for SunOS 4.x, Ultrix 4.x, SunOS 5.x and HP-UX +9.x. This archive also contains ftpd/rexecd/login versions that support +S/Key or SecureNet one-time passwords in addition to traditional UNIX +reusable passwords. + +The securelib shared library by William LeFebvre can be used to control +access to network daemons that are not run under control of the inetd +or that serve more than one client, such as the NFS mount daemon that +runs until the machine goes down. Available from eecs.nwu.edu, file +/pub/securelib.tar. + +xinetd (posted to comp.sources.unix) is an inetd replacement that +provides, among others, logging, username lookup and access control. +However, it does not support the System V TLI services, and involves +much more source code than the daemon wrapper programs. Available +from ftp.uu.net, directory /usenet/comp.sources.unix. + +netlog from Texas A&M relies on the SunOS 4.x /dev/nit interface to +passively watch all TCP and UDP network traffic on a network. The +current version is on net.tamu.edu in /pub/security/TAMU. + +Where shared libraries or router-based packet filtering are not an +option, an alternative portmap daemon can help to prevent hackers +from mounting your NFS file systems using the proxy RPC facility. +ftp.win.tue.nl:/pub/security/portmap-X.shar.Z was tested with SunOS +4.1.X Ultrix 3.0 and Ultrix 4.x, HP-UX 8.x and some version of AIX. The +protection is less effective than that of the securelib library because +portmap is mostly a dictionary service. + +An rpcbind replacement (the Solaris 2.x moral equivalent of portmap) +can be found on ftp.win.tue.nl in /pub/security. It prevents hackers +from mounting your NFS file systems by using the proxy RPC facility. + +Source for a portable RFC 931 (TAP, IDENT, RFC 1413) daemon by Peter +Eriksson is available from ftp.lysator.liu.se:/pub/ident/servers. + +Some TCP/IP implementations come without syslog library. Some come with +the library but have no syslog daemon. A replacement can be found in +ftp.win.tue.nl:/pub/security/surrogate-syslog.tar.Z. The fakesyslog +library that comes with the nntp sources reportedly works well, too. + +6 - Limitations +--------------- + +6.1 - Known wrapper limitations +------------------------------- + +Many UDP (and rpc/udp) daemons linger around for a while after they +have serviced a request, just in case another request comes in. In the +inetd configuration file these daemons are registered with the `wait' +option. Only the request that started such a daemon will be seen by the +wrappers. Such daemons are better protected with the securelib shared +library (see: Related software). + +The wrappers do not work with RPC services over TCP. These services are +registered as rpc/tcp in the inetd configuration file. The only non- +trivial service that is affected by this limitation is rexd, which is +used by the on(1) command. This is no great loss. On most systems, +rexd is less secure than a wildcard in /etc/hosts.equiv. + +Some RPC requests (for example: rwall, rup, rusers) appear to come from +the server host. What happens is that the client broadcasts its request +to all portmap daemons on its network; each portmap daemon forwards the +request to a daemon on its own system. As far as the rwall etc. daemons +know, the request comes from the local host. + +Portmap and RPC (e.g. NIS and NFS) (in)security is a topic in itself. +See the section in this document on related software. + +6.2 - Known system software bugs +-------------------------------- + +Workarounds have been implemented for several bugs in system software. +They are described in the Makefile. Unfortunately, some system software +bugs cannot be worked around. The result is loss of functionality. + +IRIX has so many bugs that it has its own README.IRIX file. + +Older ConvexOS versions come with a broken recvfrom(2) implementation. +This makes it impossible for the daemon wrappers to look up the +client host address (and hence, the name) in case of UDP requests. +A patch is available for ConvexOS 10.1; later releases should be OK. + +With early Solaris (SunOS 5) versions, the syslog daemon will leave +behind zombie processes when writing to logged-in users. Workaround: +increase the syslogd threshold for logging to users, or reduce the +wrapper's logging severity. + +On some systems, the optional RFC 931 etc. client username lookups may +trigger a kernel bug. When a client host connects to your system, and +the RFC 931 connection from your system to that client is rejected by a +router, your kernel may drop all connections with that client. This is +not a bug in the wrapper programs: complain to your vendor, and don't +enable client user name lookups until the bug has been fixed. + +Reportedly, SunOS 4.1.1, Next 2.0a, ISC 3.0 with TCP 1.3, and AIX 3.2.2 +and later are OK. + +Sony News/OS 4.51, HP-UX 8-something and Ultrix 4.3 still have the bug. +Reportedly, a fix for Ultrix is available (CXO-8919). + +The following procedure can be used (from outside the tue.nl domain) to +find out if your kernel has the bug. From the system under test, do: + + % ftp 131.155.70.19 + +This command attempts to make an ftp connection to our anonymous ftp +server (ftp.win.tue.nl). When the connection has been established, run +the following command from the same system under test, while keeping +the ftp connection open: + + % telnet 131.155.70.19 111 + +Do not forget the `111' at the end of the command. This telnet command +attempts to connect to our portmap process. The telnet command should +fail with: "host not reachable", or with a timeout error. If your ftp +connection gets messed up, you have the bug. If the telnet command does +not fail, please let me know a.s.a.p.! + +For those who care, the bug is that the BSD kernel code was not careful +enough with incoming ICMP UNREACHABLE control messages (it ignored the +local and remote port numbers, and therefore zapped *all* connections +with the remote system). The bug is still present in the BSD NET/1 +source release (1989) but apparently has been fixed in BSD NET/2 (1991). + +7 - Configuration and installation +---------------------------------- + +7.1 - Easy configuration and installation +----------------------------------------- + +The "easy" recipe requires no changes to existing software or +configuration files. Basically, you move the daemons that you want to +protect to a different directory and plug the resulting holes with +copies of the wrapper programs. + +If you don't run Ultrix, you won't need the miscd wrapper program. The +miscd daemon implements among others the SYSTAT service, which produces +the same output as the WHO command. + +Type `make' and follow the instructions. The Makefile comes with +ready-to-use templates for many common UNIX implementations (sun, +ultrix, hp-ux, aix, irix,...). + +IRIX has so many bugs that it has its own README.IRIX file. + +When the `make' succeeds the result is five executables (six in case of +Ultrix). + +You can use the `tcpdchk' program to identify the most common problems +in your wrapper and inetd configuration files. + +With the `tcpdmatch' program you can examine how the wrapper would +react to specific requests for service. + +The `safe_finger' command should be used when you implement booby +traps: it gives better protection against nasty stuff that remote +hosts may do in response to your finger probes. + +The `try-from' program tests the host and username lookup code. Run it +from a remote shell command (`rsh host /some/where/try-from') and it +should be able to figure out from what system it is being called. + +The tcpd program can be used to monitor the telnet, finger, ftp, exec, +rsh, rlogin, tftp, talk, comsat and other tcp or udp services that have +a one-to-one mapping onto executable files. + +The tcpd program can also be used for services that are marked as +rpc/udp in the inetd configuration file, but not for rpc/tcp services +such as rexd. You probably do not want to run rexd anyway. On most +systems it is even less secure than a wildcard in /etc/hosts.equiv. + +With System V.4-style systems, the tcpd program can also handle TLI +services. When TCP/IP or UDP/IP is used underneath TLI, tcpd provides +the same functions as with socket-based applications. When some other +protocol is used underneath TLI, functionality will be limited (no +client username lookups, weird network address formats). + +Decide which services you want to monitor. Move the corresponding +vendor-provided daemon programs to the location specified by the +REAL_DAEMON_DIR constant in the Makefile, and fill the holes with +copies of the tcpd program. That is, one copy of (or link to) the tcpd +program for each service that you want to monitor. For example, to +monitor the use of your finger service: + + # mkdir REAL_DAEMON_DIR + # mv /usr/etc/in.fingerd REAL_DAEMON_DIR + # cp tcpd /usr/etc/in.fingerd + +The example applies to SunOS 4. With other UNIX implementations the +network daemons live in /usr/libexec, /usr/sbin or in /etc, or have no +"in." prefix to their names, but you get the idea. + +File protections: the wrapper, all files used by the wrapper, and all +directories in the path leading to those files, should be accessible +but not writable for unprivileged users (mode 755 or mode 555). Do not +install the wrapper set-uid. + +Ultrix only: If you want to monitor the SYSTAT service, move the +vendor-provided miscd daemon to the location specified by the +REAL_DAEMON_DIR macro in the Makefile, and install the miscd wrapper +at the original miscd location. + +In the absence of any access-control tables, the daemon wrappers +will just maintain a record of network connections made to your system. + +7.2 - Advanced configuration and installation +--------------------------------------------- + +The advanced recipe leaves your daemon executables alone, but involves +simple modifications to the inetd configuration file. + +Type `make' and follow the instructions. The Makefile comes with +ready-to-use templates for many common UNIX implementations (sun, +ultrix, hp-ux, aix, irix, ...). + +IRIX users should read the warnings in the README.IRIX file first. + +When the `make' succeeds the result is five executables (six in case of +Ultrix). + +You can use the `tcpdchk' program to identify the most common problems +in your wrapper and inetd configuration files. + +With the `tcpdmatch' program you can examine how the wrapper would +react to specific requests for service. + +The `try-from' program tests the host and username lookup code. Run it +from a remote shell command (`rsh host /some/where/try-from') and it +should be able to figure out from what system it is being called. + +The `safe_finger' command should be used when you implement a booby +trap: it gives better protection against nasty stuff that remote hosts +may do in response to your finger probes. + +The tcpd program can be used to monitor the telnet, finger, ftp, exec, +rsh, rlogin, tftp, talk, comsat and other tcp or udp services that have +a one-to-one mapping onto executable files. + +With System V.4-style systems, the tcpd program can also handle TLI +services. When TCP/IP or UDP/IP is used underneath TLI, tcpd provides +the same functions as with socket-based applications. When some other +protocol is used underneath TLI, functionality will be limited (no +client username lookups, weird network address formats). + +The tcpd program can also be used for services that are marked as +rpc/udp in the inetd configuration file, but not for rpc/tcp services +such as rexd. You probably do not want to run rexd anyway. On most +systems it is even less secure than a wildcard in /etc/hosts.equiv. + +Install the tcpd command in a suitable place. Apollo UNIX users will +want to install it under a different name because the name "tcpd" is +already taken; a suitable name would be "frontd". + +File protections: the wrapper, all files used by the wrapper, and all +directories in the path leading to those files, should be accessible +but not writable for unprivileged users (mode 755 or mode 555). Do not +install the wrapper set-uid. + +Then perform the following edits on the inetd configuration file +(usually /etc/inetd.conf or /etc/inet/inetd.conf): + + finger stream tcp nowait nobody /usr/etc/in.fingerd in.fingerd + ^^^^^^^^^^^^^^^^^^^ +becomes: + + finger stream tcp nowait nobody /usr/etc/tcpd in.fingerd + ^^^^^^^^^^^^^ +Send a `kill -HUP' to the inetd process to make the change effective. +Some IRIX inetd implementations require that you first disable the +finger service (comment out the finger service and `kill -HUP' the +inetd) before you can turn on the modified version. Sending a HUP +twice seems to work just as well for IRIX 5.3, 6.0, 6.0.1 and 6.1. + +AIX note: you may have to execute the `inetimp' command after changing +the inetd configuration file. + +The example applies to SunOS 4. With other UNIX implementations the +network daemons live in /usr/libexec, /usr/sbin, or /etc, the network +daemons have no "in." prefix to their names, or the username field in +the inetd configuration file may be missing. + +When the finger service works as expected you can perform similar +changes for other network services. Do not forget the `kill -HUP'. + +The miscd daemon that comes with Ultrix implements several network +services. It decides what to do by looking at its process name. One of +the services is systat, which is a kind of limited finger service. If +you want to monitor the systat service, install the miscd wrapper in a +suitable place and update the inetd configuration file: + + systat stream tcp nowait /suitable/place/miscd systatd + +Ultrix 4.3 allows you to specify a user id under which the daemon will +be executed. This feature is not documented in the manual pages. Thus, +the example would become: + + systat stream tcp nowait nobody /suitable/place/miscd systatd + +Older Ultrix systems still run all their network daemons as root. + +In the absence of any access-control tables, the daemon wrappers +will just maintain a record of network connections made to your system. + +7.3 - Daemons with arbitrary path names +--------------------------------------- + +The above tcpd examples work fine with network daemons that live in a +common directory, but sometimes that is not practical. Having soft +links all over your file system is not a clean solution, either. + +Instead you can specify, in the inetd configuration file, an absolute +path name for the daemon process name. For example, + + ntalk dgram udp wait root /usr/etc/tcpd /usr/local/lib/ntalkd + +When the daemon process name is an absolute path name, tcpd ignores the +value of the REAL_DAEMON_DIR constant, and uses the last path component +of the daemon process name for logging and for access control. + +7.4 - Building and testing the access control rules +--------------------------------------------------- + +In order to support access control the wrappers must be compiled with +the -DHOSTS_ACCESS option. The access control policy is given in the +form of two tables (default: /etc/hosts.allow and /etc/hosts.deny). +Access control is disabled when there are no access control tables, or +when the tables are empty. + +If you haven't used the wrappers before I recommend that you first run +them a couple of days without any access control restrictions. The +logfile records should give you an idea of the process names and of the +host names that you will have to build into your access control rules. + +The syntax of the access control rules is documented in the file +hosts_access.5, which is in `nroff -man' format. This is a lengthy +document, and no-one expects you to read it right away from beginning +to end. Instead, after reading the introductory section, skip to the +examples at the end so that you get a general idea of the language. +Then you can appreciate the detailed reference sections near the +beginning of the document. + +The examples in the hosts_access.5 document (`nroff -man' format) show +two specific types of access control policy: 1) mostly closed (only +permitting access from a limited number of systems) and 2) mostly open +(permitting access from everyone except a limited number of trouble +makers). You will have to choose what model suits your situation best. +Implementing a mixed policy should not be overly difficult either. + +Optional extensions to the access control language are described in the +hosts_options.5 document (`nroff -man' format). + +The `tcpdchk' program examines all rules in your access control files +and reports any problems it can find. `tcpdchk -v' writes to standard +output a pretty-printed list of all rules. `tcpdchk -d' examines the +hosts.access and hosts.allow files in the current directory. This +program is described in the tcpdchk.8 document (`nroff -man' format). + +The `tcpdmatch' command can be used to try out your local access +control files. The command syntax is: + + tcpdmatch process_name hostname (e.g.: tcpdmatch in.tftpd localhost) + + tcpdmatch process_name address (e.g.: tcpdmatch in.tftpd 127.0.0.1) + +This way you can simulate what decisions will be made, and what actions +will be taken, when hosts connect to your own system. The program is +described in the tcpdmatch.8 document (`nroff -man' format). + +Note 1: `tcpdmatch -d' will look for hosts.{allow,deny} tables in the +current working directory. This is useful for testing new rules without +bothering your users. + +Note 2: you cannot use the `tcpdmatch' command to simulate what happens +when the local system connects to other hosts. + +In order to find out what process name to use, just use the service and +watch the process name that shows up in the logfile. Alternatively, +you can look up the name from the inetd configuration file. Coming back +to the tftp example in the tutorial section above: + + tftp dgram udp wait root /usr/etc/tcpd in.tftpd -s /tftpboot + +This entry causes the inetd to run the wrapper program (tcpd) with a +process name `in.tftpd'. This is the name that the wrapper will use +when scanning the access control tables. Therefore, `in.tftpd' is the +process name that should be given to the `tcpdmatch' command. On your +system the actual inetd.conf entry may differ (tftpd instead of +in.tftpd, and no `root' field), but you get the idea. + +When you specify a host name, the `tcpdmatch' program will use both the +host name and address. This way you can simulate the most common case +where the wrappers know both the host address and the host name. The +`tcpdmatch' program will iterate over all addresses that it can find +for the given host name. + +When you specify a host address instead of a host name, the `tcpdmatch' +program will pretend that the host name is unknown, so that you can +simulate what happens when the wrapper is unable to look up the client +host name. + +7.5 - Other applications +------------------------ + +The access control routines can easily be integrated with other +programs. The hosts_access.3 manual page (`nroff -man' format) +describes the external interface of the libwrap.a library. + +The tcpd program can even be used to control access to the mail +service. This can be useful when you suspect that someone is trying +out some obscure sendmail bug, or when a remote site is misconfigured +and keeps hammering your mail daemon. + +In that case, sendmail should not be run as a stand-alone network +listener, but it should be registered in the inetd configuration file. +For example: + + smtp stream tcp nowait root /usr/etc/tcpd /usr/lib/sendmail -bs + +You will still need to run one sendmail background process to handle +queued-up outgoing mail. A command like: + + /usr/lib/sendmail -q15m + +(no `-bd' flag) should take care of that. You cannot really prevent +people from posting forged mail this way, because there are many +unprotected smtp daemons on the network. + +8 - Acknowledgements +-------------------- + +Many people contributed to the evolution of the programs, by asking +inspiring questions, by suggesting features or bugfixes, or by +submitting source code. Nevertheless, all mistakes and bugs in the +wrappers are my own. + +Thanks to Brendan Kehoe (cs.widener.edu), Heimir Sverrisson (hafro.is) +and Dan Bernstein (kramden.acf.nyu.edu) for feedback on an early +release of this product. The host name/address check was suggested by +John Kimball (src.honeywell.com). Apollo's UNIX environment has some +peculiar quirks: Willem-Jan Withagen (eb.ele.tue.nl), Pieter +Schoenmakers (es.ele.tue.nl) and Charles S. Fuller (wccs.psc.edu) +provided assistance. Hal R. Brand (addvax.llnl.gov) told me how to +get the client IP address in case of datagram-oriented services, and +suggested the optional shell command feature. Shabbir Safdar +(mentor.cc.purdue.edu) provided a first version of a much-needed manual +page. Granville Boman Goza, IV (sei.cmu.edu) suggested to use the +client IP address even when the host name is available. Casper H.S. +Dik (fwi.uva.nl) provided additional insight into DNS spoofing +techniques. The bogus daemon feature was inspired by code from Andrew +Macpherson (BNR Europe Ltd). Steve Bellovin (research.att.com) +confirmed some of my suspicions about the darker sides of TCP/IP +insecurity. Risks of automated fingers were pointed out by Borja Marcos +(we.lc.ehu.es). Brad Plecs (jhuspo.ca.jhu.edu) was kind enough to try +my early TLI code and to work out how DG/UX differs from Solaris. + +John P. Rouillard (cs.umb.edu) deserves special mention for his +persistent, but constructive, nagging about wrong or missing things, +and for trying out and discussing embryonic code or ideas. + +Last but not least, Howard Chu (hanauma.jpl.nasa.gov), Darren Reed +(coombs.anu.edu.au), Icarus Sparry (gdr.bath.ac.uk), Scott Schwartz +(cs.psu.edu), John A. Kunze (violet.berkeley.edu), Daniel Len Schales +(engr.latech.edu), Chris Turbeville (cse.uta.edu), Paul Kranenburg +(cs.few.eur.nl), Marc Boucher (cam.org), Dave Mitchell +(dcs.shef.ac.uk), Andrew Maffei, Adrian van Bloois, Rop Gonggrijp, John +C. Wingenbach, Everett F. Batey and many, many others provided fixes, +code fragments, or ideas for improvements. + + Wietse Venema (wietse@wzv.win.tue.nl) + Department of Mathematics and Computing Science + Eindhoven University of Technology + P.O. Box 513 + 5600 MB Eindhoven + The Netherlands + + Currently visiting IBM T.J. Watson Research, Hawthorne NY, USA. diff --git a/contrib/tcp_wrappers/README.IRIX b/contrib/tcp_wrappers/README.IRIX new file mode 100644 index 000000000000..56d2beafaaab --- /dev/null +++ b/contrib/tcp_wrappers/README.IRIX @@ -0,0 +1,54 @@ +@(#) README.IRIX 1.2 94/12/28 18:45:58 + +In the past few months I received several messages with questions from +people that tried to use my tcp wrapper on IRIX 5.x. Some mysteries +could be solved via email, and then some remained. + +Today I finally had a chance to do some tests on someones IRIX 5.2 +system. Here is my first-hand experience with wrapper release 6.3. + +(1) Inetd is broken. Normally one edits inetd.conf, sends a HUP signal + to inetd and that's it. With IRIX evil things happen: inetd is too + stupid to remember that it is already listening on a port. + + In order to modify an entry in inetd.conf, first comment it out + with a # at the beginning of the line, kill -HUP the inetd, then + uncomment the inetd.conf entry and kill -HUP again. + + Even with this amount of care I have seen inetd messing up, like + calling rusersd when I make a talk connection. Even killing and + restarting inetd does not solve all problems. + + I find it hard to believe, it but the best thing to do with IRIX is + to reboot after changing inetd.conf. + +(2) When tcpd is built according to the irix4 Makefile rules, it + appears to work as expected with TCP-based services such as + fingerd, and with UDP-based services such as ntalk and tftp. + +(3) It does NOT work with RPC over UDP services such as rusersd and + rstatd: the wrapper hangs in the recvfrom() system call, and I + have spent several hours looking for ways to work around it. No + way. After finding that none of the applicable socket primitives + can be made to work (recvfrom recvmsg) I give up. So, the IRIX RPC + services cannot be wrapped until SGI fixes their system so that it + works like everyone elses code (HP Sun Dec AIX and so on). + +(4) I didn't even bother to try the RPC over TCP services. + +(5) When an IRIX 5.2 system is a NIS client, it can have problems with + hosts that have more than one address: the wrapper will see only + one address, and may complain when PARANOID mode is on. The fix is + to change the name service lookup order in /etc/resolv.conf so that + your system tries DNS before NIS (hostresorder bind nis local). + +(6) IRIX 5.2 is not System V.4, and it shows. Do not link with the + -lsocket and -lnsl libraries. They are completely broken, and the + wrapper will be unable to figure out the client internet address. + So, TLI services cannot be wrapped until SGI fixes their system so + that it works the way it is supposed to. + +I am not impressed by the quality of the IRIX system software. There +are many things that work on almost every other system except with IRIX. + + Wietse diff --git a/contrib/tcp_wrappers/README.NIS b/contrib/tcp_wrappers/README.NIS new file mode 100644 index 000000000000..34d39e27cf13 --- /dev/null +++ b/contrib/tcp_wrappers/README.NIS @@ -0,0 +1,207 @@ +@(#) README.NIS 1.2 96/02/11 17:24:52 + +> Problem: I have several [machines] with multiple IP addresses, and +> when they try to connect to a daemon with tcp wrapper, they are often +> rejected. I assume this is due to the -DPARANOID option, and depends +> on which IP address is returned first from the nameserver for a given +> name. This behavior seems to be random, may depend on ordering in +> the YP host map? + +[Note: the situation described below no longer exists. Presently, my +internet gateway uses the same IP address on all interfaces. To avoid +confusion I have removed the old name wzv-gw.win.tue.nl from the DNS. I +have kept the discussion below for educational reasons]. + +NIS was not designed to handle multi-homed hosts. With NIS, each +address should have its own hostname. For example, wzv-gw is my +gateway. It has two interfaces: one connected to the local ethernet, +the other to a serial link. In the NIS it is registered as: + + 131.155.210.23 wzv-gw-ether + 131.155.12.78 wzv-gw-slip + +In principle, wzv-gw could be the official name of one of these +interfaces, or it could be an alias for both. + +The DNS was designed to handle multi-homed hosts. In the DNS my gateway +is registered in zone win.tue.nl, with one name that has two A records: + + wzv-gw IN A 131.155.210.23 + IN A 131.155.12.78 + +And of course there are PTR records in zones 210.155.131.in-addr.arpa +and 12.155.131.in-addr.arpa that point to wzv-gw.win.tue.nl. + +This setup does not cause any problems. You can test your name service +with the two programs below. This is what they say on a local NIS client +(both client and server running SunOS 4.1.3_U1): + + % gethostbyname wzv-gw + Hostname: wzv-gw.win.tue.nl + Aliases: + Addresses: 131.155.210.23 131.155.12.78 + + % gethostbyaddr 131.155.210.23 + Hostname: wzv-gw-ether + Aliases: + Addresses: 131.155.210.23 + + % gethostbyaddr 131.155.12.78 + Hostname: wzv-gw-slip + Aliases: + Addresses: 131.155.12.78 + +Things seem less confusing when seen by a NIS client in a different +domain (both client and server running SunOS 4.1.3_U1): + + % gethostbyname wzv-gw.win.tue.nl + Hostname: wzv-gw.win.tue.nl + Aliases: + Addresses: 131.155.210.23 131.155.12.78 + + % gethostbyaddr 131.155.210.23 + Hostname: wzv-gw.win.tue.nl + Aliases: + Addresses: 131.155.12.78 131.155.210.23 + + % gethostbyaddr 131.155.12.78 + Hostname: wzv-gw.win.tue.nl + Aliases: + Addresses: 131.155.210.23 131.155.12.78 + +Alas, Solaris 2.4 still has problems. This is what I get on a Solaris +2.4 NIS client, with a SunOS 4.1.3_U1 NIS server: + + % gethostbyname wzv-gw.win.tue.nl + Hostname: wzv-gw.win.tue.nl + Aliases: 131.155.210.23 wzv-gw.win.tue.nl + Addresses: 131.155.12.78 + +The tcpd source comes with a workaround for this problem. The +workaround is ugly and is not part of the programs attached below. + + +#! /bin/sh +# This is a shell archive. Remove anything before this line, then unpack +# it by saving it into a file and typing "sh file". To overwrite existing +# files, type "sh file -c". You can also feed this as standard input via +# unshar, or by typing "sh <file", e.g.. If this archive is complete, you +# will see the following message at the end: +# "End of shell archive." +# Contents: gethostbyaddr.c gethostbyname.c +# Wrapped by wietse@wzv on Sun Jan 8 17:08:48 1995 +PATH=/bin:/usr/bin:/usr/ucb ; export PATH +if test -f gethostbyaddr.c -a "${1}" != "-c" ; then + echo shar: Will not over-write existing file \"gethostbyaddr.c\" +else +echo shar: Extracting \"gethostbyaddr.c\" \(1073 characters\) +sed "s/^X//" >gethostbyaddr.c <<'END_OF_gethostbyaddr.c' +X /* +X * gethostbyaddr tester. compile with: +X * +X * cc -o gethostbyaddr gethostbyaddr.c (SunOS 4.x) +X * +X * cc -o gethostbyaddr gethostbyaddr.c -lnsl (SunOS 5.x) +X * +X * run as: gethostbyaddr address +X * +X * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. +X */ +X +X#include <sys/types.h> +X#include <sys/socket.h> +X#include <netinet/in.h> +X#include <arpa/inet.h> +X#include <netdb.h> +X#include <stdio.h> +X +Xmain(argc, argv) +Xint argc; +Xchar **argv; +X{ +X struct hostent *hp; +X long addr; +X +X if (argc != 2) { +X fprintf(stderr, "usage: %s i.p.addres\n", argv[0]); +X exit(1); +X } +X addr = inet_addr(argv[1]); +X if (hp = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET)) { +X printf("Hostname:\t%s\n", hp->h_name); +X printf("Aliases:\t"); +X while (hp->h_aliases[0]) +X printf("%s ", *hp->h_aliases++); +X printf("\n"); +X printf("Addresses:\t"); +X while (hp->h_addr_list[0]) +X printf("%s ", inet_ntoa(*(struct in_addr *) * hp->h_addr_list++)); +X printf("\n"); +X exit(0); +X } +X fprintf(stderr, "host %s not found\n", argv[1]); +X exit(1); +X} +END_OF_gethostbyaddr.c +if test 1073 -ne `wc -c <gethostbyaddr.c`; then + echo shar: \"gethostbyaddr.c\" unpacked with wrong size! +fi +# end of overwriting check +fi +if test -f gethostbyname.c -a "${1}" != "-c" ; then + echo shar: Will not over-write existing file \"gethostbyname.c\" +else +echo shar: Extracting \"gethostbyname.c\" \(999 characters\) +sed "s/^X//" >gethostbyname.c <<'END_OF_gethostbyname.c' +X /* +X * gethostbyname tester. compile with: +X * +X * cc -o gethostbyname gethostbyname.c (SunOS 4.x) +X * +X * cc -o gethostbyname gethostbyname.c -lnsl (SunOS 5.x) +X * +X * run as: gethostbyname hostname +X * +X * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. +X */ +X#include <sys/types.h> +X#include <sys/socket.h> +X#include <netinet/in.h> +X#include <arpa/inet.h> +X#include <netdb.h> +X#include <stdio.h> +X +Xmain(argc, argv) +Xint argc; +Xchar **argv; +X{ +X struct hostent *hp; +X +X if (argc != 2) { +X fprintf(stderr, "usage: %s hostname\n", argv[0]); +X exit(1); +X } +X if (hp = gethostbyname(argv[1])) { +X printf("Hostname:\t%s\n", hp->h_name); +X printf("Aliases:\t"); +X while (hp->h_aliases[0]) +X printf("%s ", *hp->h_aliases++); +X printf("\n"); +X printf("Addresses:\t"); +X while (hp->h_addr_list[0]) +X printf("%s ", inet_ntoa(*(struct in_addr *) * hp->h_addr_list++)); +X printf("\n"); +X exit(0); +X } else { +X fprintf(stderr, "host %s not found\n", argv[1]); +X exit(1); +X } +X} +END_OF_gethostbyname.c +if test 999 -ne `wc -c <gethostbyname.c`; then + echo shar: \"gethostbyname.c\" unpacked with wrong size! +fi +# end of overwriting check +fi +echo shar: End of shell archive. +exit 0 diff --git a/contrib/tcp_wrappers/clean_exit.c b/contrib/tcp_wrappers/clean_exit.c new file mode 100644 index 000000000000..cb9d4f5080d9 --- /dev/null +++ b/contrib/tcp_wrappers/clean_exit.c @@ -0,0 +1,42 @@ + /* + * clean_exit() cleans up and terminates the program. It should be called + * instead of exit() when for some reason the real network daemon will not or + * cannot be run. Reason: in the case of a datagram-oriented service we must + * discard the not-yet received data from the client. Otherwise, inetd will + * see the same datagram again and again, and go into a loop. + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) clean_exit.c 1.4 94/12/28 17:42:19"; +#endif + +#include <stdio.h> + +extern void exit(); + +#include "tcpd.h" + +/* clean_exit - clean up and exit */ + +void clean_exit(request) +struct request_info *request; +{ + + /* + * In case of unconnected protocols we must eat up the not-yet received + * data or inetd will loop. + */ + + if (request->sink) + request->sink(request->fd); + + /* + * Be kind to the inetd. We already reported the problem via the syslogd, + * and there is no need for additional garbage in the logfile. + */ + + sleep(5); + exit(0); +} diff --git a/contrib/tcp_wrappers/diag.c b/contrib/tcp_wrappers/diag.c new file mode 100644 index 000000000000..e0bd792c319d --- /dev/null +++ b/contrib/tcp_wrappers/diag.c @@ -0,0 +1,69 @@ + /* + * Routines to report various classes of problems. Each report is decorated + * with the current context (file name and line number), if available. + * + * tcpd_warn() reports a problem and proceeds. + * + * tcpd_jump() reports a problem and jumps. + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) diag.c 1.1 94/12/28 17:42:20"; +#endif + +/* System libraries */ + +#include <syslog.h> +#include <stdio.h> +#include <setjmp.h> + +/* Local stuff */ + +#include "tcpd.h" +#include "mystdarg.h" + +struct tcpd_context tcpd_context; +jmp_buf tcpd_buf; + +/* tcpd_diag - centralize error reporter */ + +static void tcpd_diag(severity, tag, format, ap) +int severity; +char *tag; +char *format; +va_list ap; +{ + char fmt[BUFSIZ]; + + if (tcpd_context.file) + sprintf(fmt, "%s: %s, line %d: %s", + tag, tcpd_context.file, tcpd_context.line, format); + else + sprintf(fmt, "%s: %s", tag, format); + vsyslog(severity, fmt, ap); +} + +/* tcpd_warn - report problem of some sort and proceed */ + +void VARARGS(tcpd_warn, char *, format) +{ + va_list ap; + + VASTART(ap, char *, format); + tcpd_diag(LOG_ERR, "warning", format, ap); + VAEND(ap); +} + +/* tcpd_jump - report serious problem and jump */ + +void VARARGS(tcpd_jump, char *, format) +{ + va_list ap; + + VASTART(ap, char *, format); + tcpd_diag(LOG_ERR, "error", format, ap); + VAEND(ap); + longjmp(tcpd_buf, AC_ERROR); +} diff --git a/contrib/tcp_wrappers/environ.c b/contrib/tcp_wrappers/environ.c new file mode 100644 index 000000000000..e7f846ddd6b7 --- /dev/null +++ b/contrib/tcp_wrappers/environ.c @@ -0,0 +1,224 @@ +/* + * Many systems have putenv() but no setenv(). Other systems have setenv() + * but no putenv() (MIPS). Still other systems have neither (NeXT). This is a + * re-implementation that hopefully ends all problems. + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) environ.c 1.2 94/03/23 16:09:46"; +#endif + +/* System libraries. */ + +extern char **environ; +extern char *strchr(); +extern char *strcpy(); +extern char *strncpy(); +extern char *malloc(); +extern char *realloc(); +extern int strncmp(); +extern void free(); + +#ifdef no_memcpy +#define memcpy(d,s,l) bcopy(s,d,l) +#else +extern char *memcpy(); +#endif + +/* Local stuff. */ + +static int addenv(); /* append entry to environment */ + +static int allocated = 0; /* environ is, or is not, allocated */ + +#define DO_CLOBBER 1 + +/* namelength - determine length of name in "name=whatever" */ + +static int namelength(name) +char *name; +{ + char *equal; + + equal = strchr(name, '='); + return ((equal == 0) ? strlen(name) : (equal - name)); +} + +/* findenv - given name, locate name=value */ + +static char **findenv(name, len) +char *name; +int len; +{ + char **envp; + + for (envp = environ; envp && *envp; envp++) + if (strncmp(name, *envp, len) == 0 && (*envp)[len] == '=') + return (envp); + return (0); +} + +/* getenv - given name, locate value */ + +char *getenv(name) +char *name; +{ + int len = namelength(name); + char **envp = findenv(name, len); + + return (envp ? *envp + len + 1 : 0); +} + +/* putenv - update or append environment (name,value) pair */ + +int putenv(nameval) +char *nameval; +{ + char *equal = strchr(nameval, '='); + char *value = (equal ? equal : ""); + + return (setenv(nameval, value, DO_CLOBBER)); +} + +/* unsetenv - remove variable from environment */ + +void unsetenv(name) +char *name; +{ + char **envp; + + if ((envp = findenv(name, namelength(name))) != 0) + while (envp[0] = envp[1]) + envp++; +} + +/* setenv - update or append environment (name,value) pair */ + +int setenv(name, value, clobber) +char *name; +char *value; +int clobber; +{ + char *destination; + char **envp; + int l_name; /* length of name part */ + int l_nameval; /* length of name=value */ + + /* Permit name= and =value. */ + + l_name = namelength(name); + envp = findenv(name, l_name); + if (envp != 0 && clobber == 0) + return (0); + if (*value == '=') + value++; + l_nameval = l_name + strlen(value) + 1; + + /* + * Use available memory if the old value is long enough. Never free an + * old name=value entry because it may not be allocated. + */ + + destination = (envp != 0 && strlen(*envp) >= l_nameval) ? + *envp : malloc(l_nameval + 1); + if (destination == 0) + return (-1); + strncpy(destination, name, l_name); + destination[l_name] = '='; + strcpy(destination + l_name + 1, value); + return ((envp == 0) ? addenv(destination) : (*envp = destination, 0)); +} + +/* cmalloc - malloc and copy block of memory */ + +static char *cmalloc(new_len, old, old_len) +char *old; +int old_len; +{ + char *new = malloc(new_len); + + if (new != 0) + memcpy(new, old, old_len); + return (new); +} + +/* addenv - append environment entry */ + +static int addenv(nameval) +char *nameval; +{ + char **envp; + int n_used; /* number of environment entries */ + int l_used; /* bytes used excl. terminator */ + int l_need; /* bytes needed incl. terminator */ + + for (envp = environ; envp && *envp; envp++) + /* void */ ; + n_used = envp - environ; + l_used = n_used * sizeof(*envp); + l_need = l_used + 2 * sizeof(*envp); + + envp = allocated ? + (char **) realloc((char *) environ, l_need) : + (char **) cmalloc(l_need, (char *) environ, l_used); + if (envp == 0) { + return (-1); + } else { + allocated = 1; + environ = envp; + environ[n_used++] = nameval; /* add new entry */ + environ[n_used] = 0; /* terminate list */ + return (0); + } +} + +#ifdef TEST + + /* + * Stand-alone program for test purposes. + */ + +/* printenv - display environment */ + +static void printenv() +{ + char **envp; + + for (envp = environ; envp && *envp; envp++) + printf("%s\n", *envp); +} + +int main(argc, argv) +int argc; +char **argv; +{ + char *cp; + int changed = 0; + + if (argc < 2) { + printf("usage: %s name[=value]...\n", argv[0]); + return (1); + } + while (--argc && *++argv) { + if (argv[0][0] == '-') { /* unsetenv() test */ + unsetenv(argv[0] + 1); + changed = 1; + } else if (strchr(argv[0], '=') == 0) { /* getenv() test */ + cp = getenv(argv[0]); + printf("%s: %s\n", argv[0], cp ? cp : "not found"); + } else { /* putenv() test */ + if (putenv(argv[0])) { + perror("putenv"); + return (1); + } + changed = 1; + } + } + if (changed) + printenv(); + return (0); +} + +#endif /* TEST */ diff --git a/contrib/tcp_wrappers/eval.c b/contrib/tcp_wrappers/eval.c new file mode 100644 index 000000000000..d68358f3b9c3 --- /dev/null +++ b/contrib/tcp_wrappers/eval.c @@ -0,0 +1,136 @@ + /* + * Routines for controlled evaluation of host names, user names, and so on. + * They are, in fact, wrappers around the functions that are specific for + * the sockets or TLI programming interfaces. The request_info and host_info + * structures are used for result cacheing. + * + * These routines allows us to postpone expensive operations until their + * results are really needed. Examples are hostname lookups and double + * checks, or username lookups. Information that cannot be retrieved is + * given the value "unknown" ("paranoid" in case of hostname problems). + * + * When ALWAYS_HOSTNAME is off, hostname lookup is done only when required by + * tcpd paranoid mode, by access control patterns, or by %letter expansions. + * + * When ALWAYS_RFC931 mode is off, user lookup is done only when required by + * access control patterns or %letter expansions. + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) eval.c 1.3 95/01/30 19:51:45"; +#endif + +/* System libraries. */ + +#include <stdio.h> +#include <string.h> + +/* Local stuff. */ + +#include "tcpd.h" + + /* + * When a string has the value STRING_UNKNOWN, it means: don't bother, I + * tried to look up the data but it was unavailable for some reason. When a + * host name has the value STRING_PARANOID it means there was a name/address + * conflict. + */ +char unknown[] = STRING_UNKNOWN; +char paranoid[] = STRING_PARANOID; + +/* eval_user - look up user name */ + +char *eval_user(request) +struct request_info *request; +{ + if (request->user[0] == 0) { + strcpy(request->user, unknown); + if (request->sink == 0 && request->client->sin && request->server->sin) + rfc931(request->client->sin, request->server->sin, request->user); + } + return (request->user); +} + +/* eval_hostaddr - look up printable address */ + +char *eval_hostaddr(host) +struct host_info *host; +{ + if (host->addr[0] == 0) { + strcpy(host->addr, unknown); + if (host->request->hostaddr != 0) + host->request->hostaddr(host); + } + return (host->addr); +} + +/* eval_hostname - look up host name */ + +char *eval_hostname(host) +struct host_info *host; +{ + if (host->name[0] == 0) { + strcpy(host->name, unknown); + if (host->request->hostname != 0) + host->request->hostname(host); + } + return (host->name); +} + +/* eval_hostinfo - return string with host name (preferred) or address */ + +char *eval_hostinfo(host) +struct host_info *host; +{ + char *hostname; + +#ifndef ALWAYS_HOSTNAME /* no implicit host lookups */ + if (host->name[0] == 0) + return (eval_hostaddr(host)); +#endif + hostname = eval_hostname(host); + if (HOSTNAME_KNOWN(hostname)) { + return (host->name); + } else { + return (eval_hostaddr(host)); + } +} + +/* eval_client - return string with as much about the client as we know */ + +char *eval_client(request) +struct request_info *request; +{ + static char both[2 * STRING_LENGTH]; + char *hostinfo = eval_hostinfo(request->client); + +#ifndef ALWAYS_RFC931 /* no implicit user lookups */ + if (request->user[0] == 0) + return (hostinfo); +#endif + if (STR_NE(eval_user(request), unknown)) { + sprintf(both, "%s@%s", request->user, hostinfo); + return (both); + } else { + return (hostinfo); + } +} + +/* eval_server - return string with as much about the server as we know */ + +char *eval_server(request) +struct request_info *request; +{ + static char both[2 * STRING_LENGTH]; + char *host = eval_hostinfo(request->server); + char *daemon = eval_daemon(request); + + if (STR_NE(host, unknown)) { + sprintf(both, "%s@%s", daemon, host); + return (both); + } else { + return (daemon); + } +} diff --git a/contrib/tcp_wrappers/fakelog.c b/contrib/tcp_wrappers/fakelog.c new file mode 100644 index 000000000000..fa9e06e1a20d --- /dev/null +++ b/contrib/tcp_wrappers/fakelog.c @@ -0,0 +1,62 @@ + /* + * This module intercepts syslog() library calls and redirects their output + * to the standard output stream. For interactive testing. + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) fakelog.c 1.3 94/12/28 17:42:21"; +#endif + +#include <stdio.h> + +#include "mystdarg.h" + +/* openlog - dummy */ + +/* ARGSUSED */ + +openlog(name, logopt, facility) +char *name; +int logopt; +int facility; +{ + /* void */ +} + +/* vsyslog - format one record */ + +vsyslog(severity, fmt, ap) +int severity; +char *fmt; +va_list ap; +{ + char buf[BUFSIZ]; + + vprintf(percent_m(buf, fmt), ap); + printf("\n"); + fflush(stdout); +} + +/* syslog - format one record */ + +/* VARARGS */ + +VARARGS(syslog, int, severity) +{ + va_list ap; + char *fmt; + + VASTART(ap, int, severity); + fmt = va_arg(ap, char *); + vsyslog(severity, fmt, ap); + VAEND(ap); +} + +/* closelog - dummy */ + +closelog() +{ + /* void */ +} diff --git a/contrib/tcp_wrappers/fix_options.c b/contrib/tcp_wrappers/fix_options.c new file mode 100644 index 000000000000..b5e81b89bccb --- /dev/null +++ b/contrib/tcp_wrappers/fix_options.c @@ -0,0 +1,109 @@ + /* + * Routine to disable IP-level socket options. This code was taken from 4.4BSD + * rlogind and kernel source, but all mistakes in it are my fault. + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) fix_options.c 1.6 97/04/08 02:29:19"; +#endif + +#include <sys/types.h> +#include <sys/param.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netdb.h> +#include <stdio.h> +#include <syslog.h> + +#ifndef IPOPT_OPTVAL +#define IPOPT_OPTVAL 0 +#define IPOPT_OLEN 1 +#endif + +#include "tcpd.h" + +#define BUFFER_SIZE 512 /* Was: BUFSIZ */ + +/* fix_options - get rid of IP-level socket options */ + +fix_options(request) +struct request_info *request; +{ +#ifdef IP_OPTIONS + unsigned char optbuf[BUFFER_SIZE / 3], *cp; + char lbuf[BUFFER_SIZE], *lp; + int optsize = sizeof(optbuf), ipproto; + struct protoent *ip; + int fd = request->fd; + unsigned int opt; + int optlen; + struct in_addr dummy; + + if ((ip = getprotobyname("ip")) != 0) + ipproto = ip->p_proto; + else + ipproto = IPPROTO_IP; + + if (getsockopt(fd, ipproto, IP_OPTIONS, (char *) optbuf, &optsize) == 0 + && optsize != 0) { + + /* + * Horror! 4.[34] BSD getsockopt() prepends the first-hop destination + * address to the result IP options list when source routing options + * are present (see <netinet/ip_var.h>), but produces no output for + * other IP options. Solaris 2.x getsockopt() does produce output for + * non-routing IP options, and uses the same format as BSD even when + * the space for the destination address is unused. The code below + * does the right thing with 4.[34]BSD derivatives and Solaris 2, but + * may occasionally miss source routing options on incompatible + * systems such as Linux. Their choice. + * + * Look for source routing options. Drop the connection when one is + * found. Just wiping the IP options is insufficient: we would still + * help the attacker by providing a real TCP sequence number, and the + * attacker would still be able to send packets (blind spoofing). I + * discussed this attack with Niels Provos, half a year before the + * attack was described in open mailing lists. + * + * It would be cleaner to just return a yes/no reply and let the caller + * decide how to deal with it. Resident servers should not terminate. + * However I am not prepared to make changes to internal interfaces + * on short notice. + */ +#define ADDR_LEN sizeof(dummy.s_addr) + + for (cp = optbuf + ADDR_LEN; cp < optbuf + optsize; cp += optlen) { + opt = cp[IPOPT_OPTVAL]; + if (opt == IPOPT_LSRR || opt == IPOPT_SSRR) { + syslog(LOG_WARNING, + "refused connect from %s with IP source routing options", + eval_client(request)); + shutdown(fd, 2); + return; + } + if (opt == IPOPT_EOL) + break; + if (opt == IPOPT_NOP) { + optlen = 1; + } else { + optlen = cp[IPOPT_OLEN]; + if (optlen <= 0) /* Do not loop! */ + break; + } + } + lp = lbuf; + for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3) + sprintf(lp, " %2.2x", *cp); + syslog(LOG_NOTICE, + "connect from %s with IP options (ignored):%s", + eval_client(request), lbuf); + if (setsockopt(fd, ipproto, IP_OPTIONS, (char *) 0, optsize) != 0) { + syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m"); + shutdown(fd, 2); + } + } +#endif +} diff --git a/contrib/tcp_wrappers/fromhost.c b/contrib/tcp_wrappers/fromhost.c new file mode 100644 index 000000000000..a46c506e7915 --- /dev/null +++ b/contrib/tcp_wrappers/fromhost.c @@ -0,0 +1,52 @@ + /* + * On socket-only systems, fromhost() is nothing but an alias for the + * socket-specific sock_host() function. + * + * On systems with sockets and TLI, fromhost() determines the type of API + * (sockets, TLI), then invokes the appropriate API-specific routines. + * + * Diagnostics are reported through syslog(3). + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) fromhost.c 1.17 94/12/28 17:42:23"; +#endif + +#if defined(TLI) || defined(PTX) || defined(TLI_SEQUENT) + +/* System libraries. */ + +#include <sys/types.h> +#include <sys/tiuser.h> +#include <stropts.h> + +/* Local stuff. */ + +#include "tcpd.h" + +/* fromhost - find out what network API we should use */ + +void fromhost(request) +struct request_info *request; +{ + + /* + * On systems with streams support the IP network protocol family may be + * accessible via more than one programming interface: Berkeley sockets + * and the Transport Level Interface (TLI). + * + * Thus, we must first find out what programming interface to use: sockets + * or TLI. On some systems, sockets are not part of the streams system, + * so if request->fd is not a stream we simply assume sockets. + */ + + if (ioctl(request->fd, I_FIND, "timod") > 0) { + tli_host(request); + } else { + sock_host(request); + } +} + +#endif /* TLI || PTX || TLI_SEQUENT */ diff --git a/contrib/tcp_wrappers/hosts_access.3 b/contrib/tcp_wrappers/hosts_access.3 new file mode 100644 index 000000000000..1485337ff66e --- /dev/null +++ b/contrib/tcp_wrappers/hosts_access.3 @@ -0,0 +1,93 @@ +.TH HOSTS_ACCESS 3 +.SH NAME +hosts_access, hosts_ctl, request_init, request_set \- access control library +.SH SYNOPSIS +.nf +#include "tcpd.h" + +extern int allow_severity; +extern int deny_severity; + +struct request_info *request_init(request, key, value, ..., 0) +struct request_info *request; + +struct request_info *request_set(request, key, value, ..., 0) +struct request_info *request; + +int hosts_access(request) +struct request_info *request; + +int hosts_ctl(daemon, client_name, client_addr, client_user) +char *daemon; +char *client_name; +char *client_addr; +char *client_user; +.fi +.SH DESCRIPTION +The routines described in this document are part of the \fIlibwrap.a\fR +library. They implement a rule-based access control language with +optional shell commands that are executed when a rule fires. +.PP +request_init() initializes a structure with information about a client +request. request_set() updates an already initialized request +structure. Both functions take a variable-length list of key-value +pairs and return their first argument. The argument lists are +terminated with a zero key value. All string-valued arguments are +copied. The expected keys (and corresponding value types) are: +.IP "RQ_FILE (int)" +The file descriptor associated with the request. +.IP "RQ_CLIENT_NAME (char *)" +The client host name. +.IP "RQ_CLIENT_ADDR (char *)" +A printable representation of the client network address. +.IP "RQ_CLIENT_SIN (struct sockaddr_in *)" +An internal representation of the client network address and port. The +contents of the structure are not copied. +.IP "RQ_SERVER_NAME (char *)" +The hostname associated with the server endpoint address. +.IP "RQ_SERVER_ADDR (char *)" +A printable representation of the server endpoint address. +.IP "RQ_SERVER_SIN (struct sockaddr_in *)" +An internal representation of the server endpoint address and port. +The contents of the structure are not copied. +.IP "RQ_DAEMON (char *)" +The name of the daemon process running on the server host. +.IP "RQ_USER (char *)" +The name of the user on whose behalf the client host makes the request. +.PP +hosts_access() consults the access control tables described in the +\fIhosts_access(5)\fR manual page. When internal endpoint information +is available, host names and client user names are looked up on demand, +using the request structure as a cache. hosts_access() returns zero if +access should be denied. +.PP +hosts_ctl() is a wrapper around the request_init() and hosts_access() +routines with a perhaps more convenient interface (though it does not +pass on enough information to support automated client username +lookups). The client host address, client host name and username +arguments should contain valid data or STRING_UNKNOWN. hosts_ctl() +returns zero if access should be denied. +.PP +The \fIallow_severity\fR and \fIdeny_severity\fR variables determine +how accepted and rejected requests may be logged. They must be provided +by the caller and may be modified by rules in the access control +tables. +.SH DIAGNOSTICS +Problems are reported via the syslog daemon. +.SH SEE ALSO +hosts_access(5), format of the access control tables. +hosts_options(5), optional extensions to the base language. +.SH FILES +/etc/hosts.allow, /etc/hosts.deny, access control tables. +.SH BUGS +hosts_access() uses the strtok() library function. This may interfere +with other code that relies on strtok(). +.SH AUTHOR +.na +.nf +Wietse Venema (wietse@wzv.win.tue.nl) +Department of Mathematics and Computing Science +Eindhoven University of Technology +Den Dolech 2, P.O. Box 513, +5600 MB Eindhoven, The Netherlands +\" @(#) hosts_access.3 1.8 96/02/11 17:01:26 diff --git a/contrib/tcp_wrappers/hosts_access.5 b/contrib/tcp_wrappers/hosts_access.5 new file mode 100644 index 000000000000..5fe1f2969b8c --- /dev/null +++ b/contrib/tcp_wrappers/hosts_access.5 @@ -0,0 +1,378 @@ +.TH HOSTS_ACCESS 5 +.SH NAME +hosts_access \- format of host access control files +.SH DESCRIPTION +This manual page describes a simple access control language that is +based on client (host name/address, user name), and server (process +name, host name/address) patterns. Examples are given at the end. The +impatient reader is encouraged to skip to the EXAMPLES section for a +quick introduction. +.PP +An extended version of the access control language is described in the +\fIhosts_options\fR(5) document. The extensions are turned on at +program build time by building with -DPROCESS_OPTIONS. +.PP +In the following text, \fIdaemon\fR is the the process name of a +network daemon process, and \fIclient\fR is the name and/or address of +a host requesting service. Network daemon process names are specified +in the inetd configuration file. +.SH ACCESS CONTROL FILES +The access control software consults two files. The search stops +at the first match: +.IP \(bu +Access will be granted when a (daemon,client) pair matches an entry in +the \fI/etc/hosts.allow\fR file. +.IP \(bu +Otherwise, access will be denied when a (daemon,client) pair matches an +entry in the \fI/etc/hosts.deny\fR file. +.IP \(bu +Otherwise, access will be granted. +.PP +A non-existing access control file is treated as if it were an empty +file. Thus, access control can be turned off by providing no access +control files. +.SH ACCESS CONTROL RULES +Each access control file consists of zero or more lines of text. These +lines are processed in order of appearance. The search terminates when a +match is found. +.IP \(bu +A newline character is ignored when it is preceded by a backslash +character. This permits you to break up long lines so that they are +easier to edit. +.IP \(bu +Blank lines or lines that begin with a `#\' character are ignored. +This permits you to insert comments and whitespace so that the tables +are easier to read. +.IP \(bu +All other lines should satisfy the following format, things between [] +being optional: +.sp +.ti +3 +daemon_list : client_list [ : shell_command ] +.PP +\fIdaemon_list\fR is a list of one or more daemon process names +(argv[0] values) or wildcards (see below). +.PP +\fIclient_list\fR is a list +of one or more host names, host addresses, patterns or wildcards (see +below) that will be matched against the client host name or address. +.PP +The more complex forms \fIdaemon@host\fR and \fIuser@host\fR are +explained in the sections on server endpoint patterns and on client +username lookups, respectively. +.PP +List elements should be separated by blanks and/or commas. +.PP +With the exception of NIS (YP) netgroup lookups, all access control +checks are case insensitive. +.ne 4 +.SH PATTERNS +The access control language implements the following patterns: +.IP \(bu +A string that begins with a `.\' character. A host name is matched if +the last components of its name match the specified pattern. For +example, the pattern `.tue.nl\' matches the host name +`wzv.win.tue.nl\'. +.IP \(bu +A string that ends with a `.\' character. A host address is matched if +its first numeric fields match the given string. For example, the +pattern `131.155.\' matches the address of (almost) every host on the +Eind\%hoven University network (131.155.x.x). +.IP \(bu +A string that begins with an `@\' character is treated as an NIS +(formerly YP) netgroup name. A host name is matched if it is a host +member of the specified netgroup. Netgroup matches are not supported +for daemon process names or for client user names. +.IP \(bu +An expression of the form `n.n.n.n/m.m.m.m\' is interpreted as a +`net/mask\' pair. A host address is matched if `net\' is equal to the +bitwise AND of the address and the `mask\'. For example, the net/mask +pattern `131.155.72.0/255.255.254.0\' matches every address in the +range `131.155.72.0\' through `131.155.73.255\'. +.SH WILDCARDS +The access control language supports explicit wildcards: +.IP ALL +The universal wildcard, always matches. +.IP LOCAL +Matches any host whose name does not contain a dot character. +.IP UNKNOWN +Matches any user whose name is unknown, and matches any host whose name +\fIor\fR address are unknown. This pattern should be used with care: +host names may be unavailable due to temporary name server problems. A +network address will be unavailable when the software cannot figure out +what type of network it is talking to. +.IP KNOWN +Matches any user whose name is known, and matches any host whose name +\fIand\fR address are known. This pattern should be used with care: +host names may be unavailable due to temporary name server problems. A +network address will be unavailable when the software cannot figure out +what type of network it is talking to. +.IP PARANOID +Matches any host whose name does not match its address. When tcpd is +built with -DPARANOID (default mode), it drops requests from such +clients even before looking at the access control tables. Build +without -DPARANOID when you want more control over such requests. +.ne 6 +.SH OPERATORS +.IP EXCEPT +Intended use is of the form: `list_1 EXCEPT list_2\'; this construct +matches anything that matches \fIlist_1\fR unless it matches +\fIlist_2\fR. The EXCEPT operator can be used in daemon_lists and in +client_lists. The EXCEPT operator can be nested: if the control +language would permit the use of parentheses, `a EXCEPT b EXCEPT c\' +would parse as `(a EXCEPT (b EXCEPT c))\'. +.br +.ne 6 +.SH SHELL COMMANDS +If the first-matched access control rule contains a shell command, that +command is subjected to %<letter> substitutions (see next section). +The result is executed by a \fI/bin/sh\fR child process with standard +input, output and error connected to \fI/dev/null\fR. Specify an `&\' +at the end of the command if you do not want to wait until it has +completed. +.PP +Shell commands should not rely on the PATH setting of the inetd. +Instead, they should use absolute path names, or they should begin with +an explicit PATH=whatever statement. +.PP +The \fIhosts_options\fR(5) document describes an alternative language +that uses the shell command field in a different and incompatible way. +.SH % EXPANSIONS +The following expansions are available within shell commands: +.IP "%a (%A)" +The client (server) host address. +.IP %c +Client information: user@host, user@address, a host name, or just an +address, depending on how much information is available. +.IP %d +The daemon process name (argv[0] value). +.IP "%h (%H)" +The client (server) host name or address, if the host name is +unavailable. +.IP "%n (%N)" +The client (server) host name (or "unknown" or "paranoid"). +.IP %p +The daemon process id. +.IP %s +Server information: daemon@host, daemon@address, or just a daemon name, +depending on how much information is available. +.IP %u +The client user name (or "unknown"). +.IP %% +Expands to a single `%\' character. +.PP +Characters in % expansions that may confuse the shell are replaced by +underscores. +.SH SERVER ENDPOINT PATTERNS +In order to distinguish clients by the network address that they +connect to, use patterns of the form: +.sp +.ti +3 +process_name@host_pattern : client_list ... +.sp +Patterns like these can be used when the machine has different internet +addresses with different internet hostnames. Service providers can use +this facility to offer FTP, GOPHER or WWW archives with internet names +that may even belong to different organizations. See also the `twist' +option in the hosts_options(5) document. Some systems (Solaris, +FreeBSD) can have more than one internet address on one physical +interface; with other systems you may have to resort to SLIP or PPP +pseudo interfaces that live in a dedicated network address space. +.sp +The host_pattern obeys the same syntax rules as host names and +addresses in client_list context. Usually, server endpoint information +is available only with connection-oriented services. +.SH CLIENT USERNAME LOOKUP +When the client host supports the RFC 931 protocol or one of its +descendants (TAP, IDENT, RFC 1413) the wrapper programs can retrieve +additional information about the owner of a connection. Client username +information, when available, is logged together with the client host +name, and can be used to match patterns like: +.PP +.ti +3 +daemon_list : ... user_pattern@host_pattern ... +.PP +The daemon wrappers can be configured at compile time to perform +rule-driven username lookups (default) or to always interrogate the +client host. In the case of rule-driven username lookups, the above +rule would cause username lookup only when both the \fIdaemon_list\fR +and the \fIhost_pattern\fR match. +.PP +A user pattern has the same syntax as a daemon process pattern, so the +same wildcards apply (netgroup membership is not supported). One +should not get carried away with username lookups, though. +.IP \(bu +The client username information cannot be trusted when it is needed +most, i.e. when the client system has been compromised. In general, +ALL and (UN)KNOWN are the only user name patterns that make sense. +.IP \(bu +Username lookups are possible only with TCP-based services, and only +when the client host runs a suitable daemon; in all other cases the +result is "unknown". +.IP \(bu +A well-known UNIX kernel bug may cause loss of service when username +lookups are blocked by a firewall. The wrapper README document +describes a procedure to find out if your kernel has this bug. +.IP \(bu +Username lookups may cause noticeable delays for non-UNIX users. The +default timeout for username lookups is 10 seconds: too short to cope +with slow networks, but long enough to irritate PC users. +.PP +Selective username lookups can alleviate the last problem. For example, +a rule like: +.PP +.ti +3 +daemon_list : @pcnetgroup ALL@ALL +.PP +would match members of the pc netgroup without doing username lookups, +but would perform username lookups with all other systems. +.SH DETECTING ADDRESS SPOOFING ATTACKS +A flaw in the sequence number generator of many TCP/IP implementations +allows intruders to easily impersonate trusted hosts and to break in +via, for example, the remote shell service. The IDENT (RFC931 etc.) +service can be used to detect such and other host address spoofing +attacks. +.PP +Before accepting a client request, the wrappers can use the IDENT +service to find out that the client did not send the request at all. +When the client host provides IDENT service, a negative IDENT lookup +result (the client matches `UNKNOWN@host') is strong evidence of a host +spoofing attack. +.PP +A positive IDENT lookup result (the client matches `KNOWN@host') is +less trustworthy. It is possible for an intruder to spoof both the +client connection and the IDENT lookup, although doing so is much +harder than spoofing just a client connection. It may also be that +the client\'s IDENT server is lying. +.PP +Note: IDENT lookups don\'t work with UDP services. +.SH EXAMPLES +The language is flexible enough that different types of access control +policy can be expressed with a minimum of fuss. Although the language +uses two access control tables, the most common policies can be +implemented with one of the tables being trivial or even empty. +.PP +When reading the examples below it is important to realize that the +allow table is scanned before the deny table, that the search +terminates when a match is found, and that access is granted when no +match is found at all. +.PP +The examples use host and domain names. They can be improved by +including address and/or network/netmask information, to reduce the +impact of temporary name server lookup failures. +.SH MOSTLY CLOSED +In this case, access is denied by default. Only explicitly authorized +hosts are permitted access. +.PP +The default policy (no access) is implemented with a trivial deny +file: +.PP +.ne 2 +/etc/hosts.deny: +.in +3 +ALL: ALL +.PP +This denies all service to all hosts, unless they are permitted access +by entries in the allow file. +.PP +The explicitly authorized hosts are listed in the allow file. +For example: +.PP +.ne 2 +/etc/hosts.allow: +.in +3 +ALL: LOCAL @some_netgroup +.br +ALL: .foobar.edu EXCEPT terminalserver.foobar.edu +.PP +The first rule permits access from hosts in the local domain (no `.\' +in the host name) and from members of the \fIsome_netgroup\fP +netgroup. The second rule permits access from all hosts in the +\fIfoobar.edu\fP domain (notice the leading dot), with the exception of +\fIterminalserver.foobar.edu\fP. +.SH MOSTLY OPEN +Here, access is granted by default; only explicitly specified hosts are +refused service. +.PP +The default policy (access granted) makes the allow file redundant so +that it can be omitted. The explicitly non-authorized hosts are listed +in the deny file. For example: +.PP +/etc/hosts.deny: +.in +3 +ALL: some.host.name, .some.domain +.br +ALL EXCEPT in.fingerd: other.host.name, .other.domain +.PP +The first rule denies some hosts and domains all services; the second +rule still permits finger requests from other hosts and domains. +.SH BOOBY TRAPS +The next example permits tftp requests from hosts in the local domain +(notice the leading dot). Requests from any other hosts are denied. +Instead of the requested file, a finger probe is sent to the offending +host. The result is mailed to the superuser. +.PP +.ne 2 +/etc/hosts.allow: +.in +3 +.nf +in.tftpd: LOCAL, .my.domain +.PP +.ne 2 +/etc/hosts.deny: +.in +3 +.nf +in.tftpd: ALL: (/some/where/safe_finger -l @%h | \\ + /usr/ucb/mail -s %d-%h root) & +.fi +.PP +The safe_finger command comes with the tcpd wrapper and should be +installed in a suitable place. It limits possible damage from data sent +by the remote finger server. It gives better protection than the +standard finger command. +.PP +The expansion of the %h (client host) and %d (service name) sequences +is described in the section on shell commands. +.PP +Warning: do not booby-trap your finger daemon, unless you are prepared +for infinite finger loops. +.PP +On network firewall systems this trick can be carried even further. +The typical network firewall only provides a limited set of services to +the outer world. All other services can be "bugged" just like the above +tftp example. The result is an excellent early-warning system. +.br +.ne 4 +.SH DIAGNOSTICS +An error is reported when a syntax error is found in a host access +control rule; when the length of an access control rule exceeds the +capacity of an internal buffer; when an access control rule is not +terminated by a newline character; when the result of %<letter> +expansion would overflow an internal buffer; when a system call fails +that shouldn\'t. All problems are reported via the syslog daemon. +.SH FILES +.na +.nf +/etc/hosts.allow, (daemon,client) pairs that are granted access. +/etc/hosts.deny, (daemon,client) pairs that are denied access. +.ad +.fi +.SH SEE ALSO +.nf +tcpd(8) tcp/ip daemon wrapper program. +tcpdchk(8), tcpdmatch(8), test programs. +.SH BUGS +If a name server lookup times out, the host name will not be available +to the access control software, even though the host is registered. +.PP +Domain name server lookups are case insensitive; NIS (formerly YP) +netgroup lookups are case sensitive. +.SH AUTHOR +.na +.nf +Wietse Venema (wietse@wzv.win.tue.nl) +Department of Mathematics and Computing Science +Eindhoven University of Technology +Den Dolech 2, P.O. Box 513, +5600 MB Eindhoven, The Netherlands +\" @(#) hosts_access.5 1.20 95/01/30 19:51:46 diff --git a/contrib/tcp_wrappers/hosts_access.c b/contrib/tcp_wrappers/hosts_access.c new file mode 100644 index 000000000000..9bdc7bcd66e9 --- /dev/null +++ b/contrib/tcp_wrappers/hosts_access.c @@ -0,0 +1,331 @@ + /* + * This module implements a simple access control language that is based on + * host (or domain) names, NIS (host) netgroup names, IP addresses (or + * network numbers) and daemon process names. When a match is found the + * search is terminated, and depending on whether PROCESS_OPTIONS is defined, + * a list of options is executed or an optional shell command is executed. + * + * Host and user names are looked up on demand, provided that suitable endpoint + * information is available as sockaddr_in structures or TLI netbufs. As a + * side effect, the pattern matching process may change the contents of + * request structure fields. + * + * Diagnostics are reported through syslog(3). + * + * Compile with -DNETGROUP if your library provides support for netgroups. + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) hosts_access.c 1.21 97/02/12 02:13:22"; +#endif + +/* System libraries. */ + +#include <sys/types.h> +#include <sys/param.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <stdio.h> +#include <syslog.h> +#include <ctype.h> +#include <errno.h> +#include <setjmp.h> +#include <string.h> + +extern char *fgets(); +extern int errno; + +#ifndef INADDR_NONE +#define INADDR_NONE (-1) /* XXX should be 0xffffffff */ +#endif + +/* Local stuff. */ + +#include "tcpd.h" + +/* Error handling. */ + +extern jmp_buf tcpd_buf; + +/* Delimiters for lists of daemons or clients. */ + +static char sep[] = ", \t\r\n"; + +/* Constants to be used in assignments only, not in comparisons... */ + +#define YES 1 +#define NO 0 + + /* + * These variables are globally visible so that they can be redirected in + * verification mode. + */ + +char *hosts_allow_table = HOSTS_ALLOW; +char *hosts_deny_table = HOSTS_DENY; +int hosts_access_verbose = 0; + + /* + * In a long-running process, we are not at liberty to just go away. + */ + +int resident = (-1); /* -1, 0: unknown; +1: yes */ + +/* Forward declarations. */ + +static int table_match(); +static int list_match(); +static int server_match(); +static int client_match(); +static int host_match(); +static int string_match(); +static int masked_match(); + +/* Size of logical line buffer. */ + +#define BUFLEN 2048 + +/* hosts_access - host access control facility */ + +int hosts_access(request) +struct request_info *request; +{ + int verdict; + + /* + * If the (daemon, client) pair is matched by an entry in the file + * /etc/hosts.allow, access is granted. Otherwise, if the (daemon, + * client) pair is matched by an entry in the file /etc/hosts.deny, + * access is denied. Otherwise, access is granted. A non-existent + * access-control file is treated as an empty file. + * + * After a rule has been matched, the optional language extensions may + * decide to grant or refuse service anyway. Or, while a rule is being + * processed, a serious error is found, and it seems better to play safe + * and deny service. All this is done by jumping back into the + * hosts_access() routine, bypassing the regular return from the + * table_match() function calls below. + */ + + if (resident <= 0) + resident++; + verdict = setjmp(tcpd_buf); + if (verdict != 0) + return (verdict == AC_PERMIT); + if (table_match(hosts_allow_table, request)) + return (YES); + if (table_match(hosts_deny_table, request)) + return (NO); + return (YES); +} + +/* table_match - match table entries with (daemon, client) pair */ + +static int table_match(table, request) +char *table; +struct request_info *request; +{ + FILE *fp; + char sv_list[BUFLEN]; /* becomes list of daemons */ + char *cl_list; /* becomes list of clients */ + char *sh_cmd; /* becomes optional shell command */ + int match = NO; + struct tcpd_context saved_context; + + saved_context = tcpd_context; /* stupid compilers */ + + /* + * Between the fopen() and fclose() calls, avoid jumps that may cause + * file descriptor leaks. + */ + + if ((fp = fopen(table, "r")) != 0) { + tcpd_context.file = table; + tcpd_context.line = 0; + while (match == NO && xgets(sv_list, sizeof(sv_list), fp) != 0) { + if (sv_list[strlen(sv_list) - 1] != '\n') { + tcpd_warn("missing newline or line too long"); + continue; + } + if (sv_list[0] == '#' || sv_list[strspn(sv_list, " \t\r\n")] == 0) + continue; + if ((cl_list = split_at(sv_list, ':')) == 0) { + tcpd_warn("missing \":\" separator"); + continue; + } + sh_cmd = split_at(cl_list, ':'); + match = list_match(sv_list, request, server_match) + && list_match(cl_list, request, client_match); + } + (void) fclose(fp); + } else if (errno != ENOENT) { + tcpd_warn("cannot open %s: %m", table); + } + if (match) { + if (hosts_access_verbose > 1) + syslog(LOG_DEBUG, "matched: %s line %d", + tcpd_context.file, tcpd_context.line); + if (sh_cmd) { +#ifdef PROCESS_OPTIONS + process_options(sh_cmd, request); +#else + char cmd[BUFSIZ]; + shell_cmd(percent_x(cmd, sizeof(cmd), sh_cmd, request)); +#endif + } + } + tcpd_context = saved_context; + return (match); +} + +/* list_match - match a request against a list of patterns with exceptions */ + +static int list_match(list, request, match_fn) +char *list; +struct request_info *request; +int (*match_fn) (); +{ + char *tok; + + /* + * Process tokens one at a time. We have exhausted all possible matches + * when we reach an "EXCEPT" token or the end of the list. If we do find + * a match, look for an "EXCEPT" list and recurse to determine whether + * the match is affected by any exceptions. + */ + + for (tok = strtok(list, sep); tok != 0; tok = strtok((char *) 0, sep)) { + if (STR_EQ(tok, "EXCEPT")) /* EXCEPT: give up */ + return (NO); + if (match_fn(tok, request)) { /* YES: look for exceptions */ + while ((tok = strtok((char *) 0, sep)) && STR_NE(tok, "EXCEPT")) + /* VOID */ ; + return (tok == 0 || list_match((char *) 0, request, match_fn) == 0); + } + } + return (NO); +} + +/* server_match - match server information */ + +static int server_match(tok, request) +char *tok; +struct request_info *request; +{ + char *host; + + if ((host = split_at(tok + 1, '@')) == 0) { /* plain daemon */ + return (string_match(tok, eval_daemon(request))); + } else { /* daemon@host */ + return (string_match(tok, eval_daemon(request)) + && host_match(host, request->server)); + } +} + +/* client_match - match client information */ + +static int client_match(tok, request) +char *tok; +struct request_info *request; +{ + char *host; + + if ((host = split_at(tok + 1, '@')) == 0) { /* plain host */ + return (host_match(tok, request->client)); + } else { /* user@host */ + return (host_match(host, request->client) + && string_match(tok, eval_user(request))); + } +} + +/* host_match - match host name and/or address against pattern */ + +static int host_match(tok, host) +char *tok; +struct host_info *host; +{ + char *mask; + + /* + * This code looks a little hairy because we want to avoid unnecessary + * hostname lookups. + * + * The KNOWN pattern requires that both address AND name be known; some + * patterns are specific to host names or to host addresses; all other + * patterns are satisfied when either the address OR the name match. + */ + + if (tok[0] == '@') { /* netgroup: look it up */ +#ifdef NETGROUP + static char *mydomain = 0; + if (mydomain == 0) + yp_get_default_domain(&mydomain); + return (innetgr(tok + 1, eval_hostname(host), (char *) 0, mydomain)); +#else + tcpd_warn("netgroup support is disabled"); /* not tcpd_jump() */ + return (NO); +#endif + } else if (STR_EQ(tok, "KNOWN")) { /* check address and name */ + char *name = eval_hostname(host); + return (STR_NE(eval_hostaddr(host), unknown) && HOSTNAME_KNOWN(name)); + } else if (STR_EQ(tok, "LOCAL")) { /* local: no dots in name */ + char *name = eval_hostname(host); + return (strchr(name, '.') == 0 && HOSTNAME_KNOWN(name)); + } else if ((mask = split_at(tok, '/')) != 0) { /* net/mask */ + return (masked_match(tok, mask, eval_hostaddr(host))); + } else { /* anything else */ + return (string_match(tok, eval_hostaddr(host)) + || (NOT_INADDR(tok) && string_match(tok, eval_hostname(host)))); + } +} + +/* string_match - match string against pattern */ + +static int string_match(tok, string) +char *tok; +char *string; +{ + int n; + + if (tok[0] == '.') { /* suffix */ + n = strlen(string) - strlen(tok); + return (n > 0 && STR_EQ(tok, string + n)); + } else if (STR_EQ(tok, "ALL")) { /* all: match any */ + return (YES); + } else if (STR_EQ(tok, "KNOWN")) { /* not unknown */ + return (STR_NE(string, unknown)); + } else if (tok[(n = strlen(tok)) - 1] == '.') { /* prefix */ + return (STRN_EQ(tok, string, n)); + } else { /* exact match */ + return (STR_EQ(tok, string)); + } +} + +/* masked_match - match address against netnumber/netmask */ + +static int masked_match(net_tok, mask_tok, string) +char *net_tok; +char *mask_tok; +char *string; +{ + unsigned long net; + unsigned long mask; + unsigned long addr; + + /* + * Disallow forms other than dotted quad: the treatment that inet_addr() + * gives to forms with less than four components is inconsistent with the + * access control language. John P. Rouillard <rouilj@cs.umb.edu>. + */ + + if ((addr = dot_quad_addr(string)) == INADDR_NONE) + return (NO); + if ((net = dot_quad_addr(net_tok)) == INADDR_NONE + || (mask = dot_quad_addr(mask_tok)) == INADDR_NONE) { + tcpd_warn("bad net/mask expression: %s/%s", net_tok, mask_tok); + return (NO); /* not tcpd_jump() */ + } + return ((addr & mask) == net); +} diff --git a/contrib/tcp_wrappers/hosts_ctl.c b/contrib/tcp_wrappers/hosts_ctl.c new file mode 100644 index 000000000000..e57f30aaa4a4 --- /dev/null +++ b/contrib/tcp_wrappers/hosts_ctl.c @@ -0,0 +1,38 @@ + /* + * hosts_ctl() combines common applications of the host access control + * library routines. It bundles its arguments then calls the hosts_access() + * access control checker. The host name and user name arguments should be + * empty strings, STRING_UNKNOWN or real data. If a match is found, the + * optional shell command is executed. + * + * Restriction: this interface does not pass enough information to support + * selective remote username lookups or selective hostname double checks. + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) hosts_ctl.c 1.4 94/12/28 17:42:27"; +#endif + +#include <stdio.h> + +#include "tcpd.h" + +/* hosts_ctl - limited interface to the hosts_access() routine */ + +int hosts_ctl(daemon, name, addr, user) +char *daemon; +char *name; +char *addr; +char *user; +{ + struct request_info request; + + return (hosts_access(request_init(&request, + RQ_DAEMON, daemon, + RQ_CLIENT_NAME, name, + RQ_CLIENT_ADDR, addr, + RQ_USER, user, + 0))); +} diff --git a/contrib/tcp_wrappers/hosts_options.5 b/contrib/tcp_wrappers/hosts_options.5 new file mode 100644 index 000000000000..3bd189ee0541 --- /dev/null +++ b/contrib/tcp_wrappers/hosts_options.5 @@ -0,0 +1,172 @@ +.TH HOSTS_OPTIONS 5 +.SH NAME +hosts_options \- host access control language extensions +.SH DESCRIPTION +This document describes optional extensions to the language described +in the hosts_access(5) document. The extensions are enabled at program +build time. For example, by editing the Makefile and turning on the +PROCESS_OPTIONS compile-time option. +.PP +The extensible language uses the following format: +.sp +.ti +3 +daemon_list : client_list : option : option ... +.PP +The first two fields are described in the hosts_access(5) manual page. +The remainder of the rules is a list of zero or more options. Any ":" +characters within options should be protected with a backslash. +.PP +An option is of the form "keyword" or "keyword value". Options are +processed in the specified order. Some options are subjected to +%<letter> substitutions. For the sake of backwards compatibility with +earlier versions, an "=" is permitted between keyword and value. +.SH LOGGING +.IP "severity mail.info" +.IP "severity notice" +Change the severity level at which the event will be logged. Facility +names (such as mail) are optional, and are not supported on systems +with older syslog implementations. The severity option can be used +to emphasize or to ignore specific events. +.SH ACCESS CONTROL +.IP "allow" +.IP "deny" +Grant (deny) service. These options must appear at the end of a rule. +.PP +The \fIallow\fR and \fIdeny\fR keywords make it possible to keep all +access control rules within a single file, for example in the +\fIhosts.allow\fR file. +.sp +To permit access from specific hosts only: +.sp +.ne 2 +.ti +3 +ALL: .friendly.domain: ALLOW +.ti +3 +ALL: ALL: DENY +.sp +To permit access from all hosts except a few trouble makers: +.sp +.ne 2 +.ti +3 +ALL: .bad.domain: DENY +.ti +3 +ALL: ALL: ALLOW +.sp +Notice the leading dot on the domain name patterns. +.SH RUNNING OTHER COMMANDS +.IP "spawn shell_command" +Execute, in a child process, the specified shell command, after +performing the %<letter> expansions described in the hosts_access(5) +manual page. The command is executed with stdin, stdout and stderr +connected to the null device, so that it won\'t mess up the +conversation with the client host. Example: +.sp +.nf +.ti +3 +spawn (/some/where/safe_finger -l @%h | /usr/ucb/mail root) & +.fi +.sp +executes, in a background child process, the shell command "safe_finger +-l @%h | mail root" after replacing %h by the name or address of the +remote host. +.sp +The example uses the "safe_finger" command instead of the regular +"finger" command, to limit possible damage from data sent by the finger +server. The "safe_finger" command is part of the daemon wrapper +package; it is a wrapper around the regular finger command that filters +the data sent by the remote host. +.IP "twist shell_command" +Replace the current process by an instance of the specified shell +command, after performing the %<letter> expansions described in the +hosts_access(5) manual page. Stdin, stdout and stderr are connected to +the client process. This option must appear at the end of a rule. +.sp +To send a customized bounce message to the client instead of +running the real ftp daemon: +.sp +.nf +.ti +3 +in.ftpd : ... : twist /bin/echo 421 Some bounce message +.fi +.sp +For an alternative way to talk to client processes, see the +\fIbanners\fR option below. +.sp +To run /some/other/in.telnetd without polluting its command-line +array or its process environment: +.sp +.nf +.ti +3 +in.telnetd : ... : twist PATH=/some/other; exec in.telnetd +.fi +.sp +Warning: in case of UDP services, do not twist to commands that use +the standard I/O or the read(2)/write(2) routines to communicate with +the client process; UDP requires other I/O primitives. +.SH NETWORK OPTIONS +.IP "keepalive" +Causes the server to periodically send a message to the client. The +connection is considered broken when the client does not respond. The +keepalive option can be useful when users turn off their machine while +it is still connected to a server. The keepalive option is not useful +for datagram (UDP) services. +.IP "linger number_of_seconds" +Specifies how long the kernel will try to deliver not-yet delivered +data after the server process closes a connection. +.SH USERNAME LOOKUP +.IP "rfc931 [ timeout_in_seconds ]" +Look up the client user name with the RFC 931 (TAP, IDENT, RFC 1413) +protocol. This option is silently ignored in case of services based on +transports other than TCP. It requires that the client system runs an +RFC 931 (IDENT, etc.) -compliant daemon, and may cause noticeable +delays with connections from non-UNIX clients. The timeout period is +optional. If no timeout is specified a compile-time defined default +value is taken. +.SH MISCELLANEOUS +.IP "banners /some/directory" +Look for a file in `/some/directory' with the same name as the daemon +process (for example in.telnetd for the telnet service), and copy its +contents to the client. Newline characters are replaced by +carriage-return newline, and %<letter> sequences are expanded (see +the hosts_access(5) manual page). +.sp +The tcp wrappers source code distribution provides a sample makefile +(Banners.Makefile) for convenient banner maintenance. +.sp +Warning: banners are supported for connection-oriented (TCP) network +services only. +.IP "nice [ number ]" +Change the nice value of the process (default 10). Specify a positive +value to spend more CPU resources on other processes. +.IP "setenv name value" +Place a (name, value) pair into the process environment. The value is +subjected to %<letter> expansions and may contain whitespace (but +leading and trailing blanks are stripped off). +.sp +Warning: many network daemons reset their environment before spawning a +login or shell process. +.IP "umask 022" +Like the umask command that is built into the shell. An umask of 022 +prevents the creation of files with group and world write permission. +The umask argument should be an octal number. +.IP "user nobody" +.IP "user nobody.kmem" +Assume the privileges of the "nobody" userid (or user "nobody", group +"kmem"). The first form is useful with inetd implementations that run +all services with root privilege. The second form is useful for +services that need special group privileges only. +.SH DIAGNOSTICS +When a syntax error is found in an access control rule, the error +is reported to the syslog daemon; further options will be ignored, +and service is denied. +.SH SEE ALSO +hosts_access(5), the default access control language +.SH AUTHOR +.na +.nf +Wietse Venema (wietse@wzv.win.tue.nl) +Department of Mathematics and Computing Science +Eindhoven University of Technology +Den Dolech 2, P.O. Box 513, +5600 MB Eindhoven, The Netherlands +\" @(#) hosts_options.5 1.10 94/12/28 17:42:28 diff --git a/contrib/tcp_wrappers/inetcf.c b/contrib/tcp_wrappers/inetcf.c new file mode 100644 index 000000000000..13838db9a4cc --- /dev/null +++ b/contrib/tcp_wrappers/inetcf.c @@ -0,0 +1,317 @@ + /* + * Routines to parse an inetd.conf or tlid.conf file. This would be a great + * job for a PERL script. + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) inetcf.c 1.7 97/02/12 02:13:23"; +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> + +extern int errno; +extern void exit(); + +#include "tcpd.h" +#include "inetcf.h" + + /* + * Network configuration files may live in unusual places. Here are some + * guesses. Shorter names follow longer ones. + */ +char *inet_files[] = { + "/private/etc/inetd.conf", /* NEXT */ + "/etc/inet/inetd.conf", /* SYSV4 */ + "/usr/etc/inetd.conf", /* IRIX?? */ + "/etc/inetd.conf", /* BSD */ + "/etc/net/tlid.conf", /* SYSV4?? */ + "/etc/saf/tlid.conf", /* SYSV4?? */ + "/etc/tlid.conf", /* SYSV4?? */ + 0, +}; + +static void inet_chk(); +static char *base_name(); + + /* + * Structure with everything we know about a service. + */ +struct inet_ent { + struct inet_ent *next; + int type; + char name[1]; +}; + +static struct inet_ent *inet_list = 0; + +static char whitespace[] = " \t\r\n"; + +/* inet_conf - read in and examine inetd.conf (or tlid.conf) entries */ + +char *inet_cfg(conf) +char *conf; +{ + char buf[BUFSIZ]; + FILE *fp; + char *service; + char *protocol; + char *user; + char *path; + char *arg0; + char *arg1; + struct tcpd_context saved_context; + char *percent_m(); + int i; + struct stat st; + + saved_context = tcpd_context; + + /* + * The inetd.conf (or tlid.conf) information is so useful that we insist + * on its availability. When no file is given run a series of educated + * guesses. + */ + if (conf != 0) { + if ((fp = fopen(conf, "r")) == 0) { + fprintf(stderr, percent_m(buf, "open %s: %m\n"), conf); + exit(1); + } + } else { + for (i = 0; inet_files[i] && (fp = fopen(inet_files[i], "r")) == 0; i++) + /* void */ ; + if (fp == 0) { + fprintf(stderr, "Cannot find your inetd.conf or tlid.conf file.\n"); + fprintf(stderr, "Please specify its location.\n"); + exit(1); + } + conf = inet_files[i]; + check_path(conf, &st); + } + + /* + * Process the file. After the 7.0 wrapper release it became clear that + * there are many more inetd.conf formats than the 8 systems that I had + * studied. EP/IX uses a two-line specification for rpc services; HP-UX + * permits long lines to be broken with backslash-newline. + */ + tcpd_context.file = conf; + tcpd_context.line = 0; + while (xgets(buf, sizeof(buf), fp)) { + service = strtok(buf, whitespace); /* service */ + if (service == 0 || *service == '#') + continue; + if (STR_NE(service, "stream") && STR_NE(service, "dgram")) + strtok((char *) 0, whitespace); /* endpoint */ + protocol = strtok((char *) 0, whitespace); + (void) strtok((char *) 0, whitespace); /* wait */ + if ((user = strtok((char *) 0, whitespace)) == 0) + continue; + if (user[0] == '/') { /* user */ + path = user; + } else { /* path */ + if ((path = strtok((char *) 0, whitespace)) == 0) + continue; + } + if (path[0] == '?') /* IRIX optional service */ + path++; + if (STR_EQ(path, "internal")) + continue; + if (path[strspn(path, "-0123456789")] == 0) { + + /* + * ConvexOS puts RPC version numbers before path names. Jukka + * Ukkonen <ukkonen@csc.fi>. + */ + if ((path = strtok((char *) 0, whitespace)) == 0) + continue; + } + if ((arg0 = strtok((char *) 0, whitespace)) == 0) { + tcpd_warn("incomplete line"); + continue; + } + if (arg0[strspn(arg0, "0123456789")] == 0) { + + /* + * We're reading a tlid.conf file, the format is: + * + * ...stuff... path arg_count arguments mod_count modules + */ + if ((arg0 = strtok((char *) 0, whitespace)) == 0) { + tcpd_warn("incomplete line"); + continue; + } + } + if ((arg1 = strtok((char *) 0, whitespace)) == 0) + arg1 = ""; + + inet_chk(protocol, path, arg0, arg1); + } + fclose(fp); + tcpd_context = saved_context; + return (conf); +} + +/* inet_chk - examine one inetd.conf (tlid.conf?) entry */ + +static void inet_chk(protocol, path, arg0, arg1) +char *protocol; +char *path; +char *arg0; +char *arg1; +{ + char daemon[BUFSIZ]; + struct stat st; + int wrap_status = WR_MAYBE; + char *base_name_path = base_name(path); + char *tcpd_proc_name = (arg0[0] == '/' ? base_name(arg0) : arg0); + + /* + * Always warn when the executable does not exist or when it is not + * executable. + */ + if (check_path(path, &st) < 0) { + tcpd_warn("%s: not found: %m", path); + } else if ((st.st_mode & 0100) == 0) { + tcpd_warn("%s: not executable", path); + } + + /* + * Cheat on the miscd tests, nobody uses it anymore. + */ + if (STR_EQ(base_name_path, "miscd")) { + inet_set(arg0, WR_YES); + return; + } + + /* + * While we are here... + */ + if (STR_EQ(tcpd_proc_name, "rexd") || STR_EQ(tcpd_proc_name, "rpc.rexd")) + tcpd_warn("%s may be an insecure service", tcpd_proc_name); + + /* + * The tcpd program gets most of the attention. + */ + if (STR_EQ(base_name_path, "tcpd")) { + + if (STR_EQ(tcpd_proc_name, "tcpd")) + tcpd_warn("%s is recursively calling itself", tcpd_proc_name); + + wrap_status = WR_YES; + + /* + * Check: some sites install the wrapper set-uid. + */ + if ((st.st_mode & 06000) != 0) + tcpd_warn("%s: file is set-uid or set-gid", path); + + /* + * Check: some sites insert tcpd in inetd.conf, instead of replacing + * the daemon pathname. + */ + if (arg0[0] == '/' && STR_EQ(tcpd_proc_name, base_name(arg1))) + tcpd_warn("%s inserted before %s", path, arg0); + + /* + * Check: make sure files exist and are executable. On some systems + * the network daemons are set-uid so we cannot complain. Note that + * tcpd takes the basename only in case of absolute pathnames. + */ + if (arg0[0] == '/') { /* absolute path */ + if (check_path(arg0, &st) < 0) { + tcpd_warn("%s: not found: %m", arg0); + } else if ((st.st_mode & 0100) == 0) { + tcpd_warn("%s: not executable", arg0); + } + } else { /* look in REAL_DAEMON_DIR */ + sprintf(daemon, "%s/%s", REAL_DAEMON_DIR, arg0); + if (check_path(daemon, &st) < 0) { + tcpd_warn("%s: not found in %s: %m", + arg0, REAL_DAEMON_DIR); + } else if ((st.st_mode & 0100) == 0) { + tcpd_warn("%s: not executable", daemon); + } + } + + } else { + + /* + * No tcpd program found. Perhaps they used the "simple installation" + * recipe. Look for a file with the same basename in REAL_DAEMON_DIR. + * Draw some conservative conclusions when a distinct file is found. + */ + sprintf(daemon, "%s/%s", REAL_DAEMON_DIR, arg0); + if (STR_EQ(path, daemon)) { + wrap_status = WR_NOT; + } else if (check_path(daemon, &st) >= 0) { + wrap_status = WR_MAYBE; + } else if (errno == ENOENT) { + wrap_status = WR_NOT; + } else { + tcpd_warn("%s: file lookup: %m", daemon); + wrap_status = WR_MAYBE; + } + } + + /* + * Alas, we cannot wrap rpc/tcp services. + */ + if (wrap_status == WR_YES && STR_EQ(protocol, "rpc/tcp")) + tcpd_warn("%s: cannot wrap rpc/tcp services", tcpd_proc_name); + + inet_set(tcpd_proc_name, wrap_status); +} + +/* inet_set - remember service status */ + +void inet_set(name, type) +char *name; +int type; +{ + struct inet_ent *ip = + (struct inet_ent *) malloc(sizeof(struct inet_ent) + strlen(name)); + + if (ip == 0) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + ip->next = inet_list; + strcpy(ip->name, name); + ip->type = type; + inet_list = ip; +} + +/* inet_get - look up service status */ + +int inet_get(name) +char *name; +{ + struct inet_ent *ip; + + if (inet_list == 0) + return (WR_MAYBE); + + for (ip = inet_list; ip; ip = ip->next) + if (STR_EQ(ip->name, name)) + return (ip->type); + + return (-1); +} + +/* base_name - compute last pathname component */ + +static char *base_name(path) +char *path; +{ + char *cp; + + if ((cp = strrchr(path, '/')) != 0) + path = cp + 1; + return (path); +} diff --git a/contrib/tcp_wrappers/inetcf.h b/contrib/tcp_wrappers/inetcf.h new file mode 100644 index 000000000000..0f1d77cb3d75 --- /dev/null +++ b/contrib/tcp_wrappers/inetcf.h @@ -0,0 +1,14 @@ + /* + * @(#) inetcf.h 1.1 94/12/28 17:42:30 + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +extern char *inet_cfg(); /* read inetd.conf file */ +extern void inet_set(); /* remember internet service */ +extern int inet_get(); /* look up internet service */ + +#define WR_UNKNOWN (-1) /* service unknown */ +#define WR_NOT 1 /* may not be wrapped */ +#define WR_MAYBE 2 /* may be wrapped */ +#define WR_YES 3 /* service is wrapped */ diff --git a/contrib/tcp_wrappers/misc.c b/contrib/tcp_wrappers/misc.c new file mode 100644 index 000000000000..87a765379f43 --- /dev/null +++ b/contrib/tcp_wrappers/misc.c @@ -0,0 +1,87 @@ + /* + * Misc routines that are used by tcpd and by tcpdchk. + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsic[] = "@(#) misc.c 1.2 96/02/11 17:01:29"; +#endif + +#include <sys/types.h> +#include <sys/param.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <stdio.h> +#include <string.h> + +#include "tcpd.h" + +extern char *fgets(); + +#ifndef INADDR_NONE +#define INADDR_NONE (-1) /* XXX should be 0xffffffff */ +#endif + +/* xgets - fgets() with backslash-newline stripping */ + +char *xgets(ptr, len, fp) +char *ptr; +int len; +FILE *fp; +{ + int got; + char *start = ptr; + + while (fgets(ptr, len, fp)) { + got = strlen(ptr); + if (got >= 1 && ptr[got - 1] == '\n') { + tcpd_context.line++; + if (got >= 2 && ptr[got - 2] == '\\') { + got -= 2; + } else { + return (start); + } + } + ptr += got; + len -= got; + ptr[0] = 0; + } + return (ptr > start ? start : 0); +} + +/* split_at - break string at delimiter or return NULL */ + +char *split_at(string, delimiter) +char *string; +int delimiter; +{ + char *cp; + + if ((cp = strchr(string, delimiter)) != 0) + *cp++ = 0; + return (cp); +} + +/* dot_quad_addr - convert dotted quad to internal form */ + +unsigned long dot_quad_addr(str) +char *str; +{ + int in_run = 0; + int runs = 0; + char *cp = str; + + /* Count the number of runs of non-dot characters. */ + + while (*cp) { + if (*cp == '.') { + in_run = 0; + } else if (in_run == 0) { + in_run = 1; + runs++; + } + cp++; + } + return (runs == 4 ? inet_addr(str) : INADDR_NONE); +} diff --git a/contrib/tcp_wrappers/miscd.c b/contrib/tcp_wrappers/miscd.c new file mode 100644 index 000000000000..1ab835c45061 --- /dev/null +++ b/contrib/tcp_wrappers/miscd.c @@ -0,0 +1,120 @@ + /* + * Front end to the ULTRIX miscd service. The front end logs the remote host + * name and then invokes the real miscd daemon. Install as "/usr/etc/miscd", + * after renaming the real miscd daemon to the name defined with the + * REAL_MISCD macro. + * + * Connections and diagnostics are logged through syslog(3). + * + * The Ultrix miscd program implements (among others) the systat service, which + * pipes the output from who(1) to stdout. This information is potentially + * useful to systems crackers. + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) miscd.c 1.10 96/02/11 17:01:30"; +#endif + +/* System libraries. */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <stdio.h> +#include <syslog.h> + +#ifndef MAXPATHNAMELEN +#define MAXPATHNAMELEN BUFSIZ +#endif + +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif + +/* Local stuff. */ + +#include "patchlevel.h" +#include "tcpd.h" + +int allow_severity = SEVERITY; /* run-time adjustable */ +int deny_severity = LOG_WARNING; /* ditto */ + +main(argc, argv) +int argc; +char **argv; +{ + struct request_info request; + char path[MAXPATHNAMELEN]; + + /* Attempt to prevent the creation of world-writable files. */ + +#ifdef DAEMON_UMASK + umask(DAEMON_UMASK); +#endif + + /* + * Open a channel to the syslog daemon. Older versions of openlog() + * require only two arguments. + */ + +#ifdef LOG_MAIL + (void) openlog(argv[0], LOG_PID, FACILITY); +#else + (void) openlog(argv[0], LOG_PID); +#endif + + /* + * Find out the endpoint addresses of this conversation. Host name + * lookups and double checks will be done on demand. + */ + + request_init(&request, RQ_DAEMON, argv[0], RQ_FILE, STDIN_FILENO, 0); + fromhost(&request); + + /* + * Optionally look up and double check the remote host name. Sites + * concerned with security may choose to refuse connections from hosts + * that pretend to have someone elses host name. + */ + +#ifdef PARANOID + if (STR_EQ(eval_hostname(request.client), paranoid)) + refuse(&request); +#endif + + /* + * The BSD rlogin and rsh daemons that came out after 4.3 BSD disallow + * socket options at the IP level. They do so for a good reason. + * Unfortunately, we cannot use this with SunOS 4.1.x because the + * getsockopt() system call can panic the system. + */ + +#ifdef KILL_IP_OPTIONS + fix_options(&request); +#endif + + /* + * Check whether this host can access the service in argv[0]. The + * access-control code invokes optional shell commands as specified in + * the access-control tables. + */ + +#ifdef HOSTS_ACCESS + if (!hosts_access(&request)) + refuse(&request); +#endif + + /* Report request and invoke the real daemon program. */ + + syslog(allow_severity, "connect from %s", eval_client(&request)); + sprintf(path, "%s/miscd", REAL_DAEMON_DIR); + closelog(); + (void) execv(path, argv); + syslog(LOG_ERR, "error: cannot execute %s: %m", path); + clean_exit(&request); + /* NOTREACHED */ +} diff --git a/contrib/tcp_wrappers/mystdarg.h b/contrib/tcp_wrappers/mystdarg.h new file mode 100644 index 000000000000..36bdf696f1a5 --- /dev/null +++ b/contrib/tcp_wrappers/mystdarg.h @@ -0,0 +1,19 @@ + + /* + * What follows is an attempt to unify varargs.h and stdarg.h. I'd rather + * have this than #ifdefs all over the code. + */ + +#ifdef __STDC__ +#include <stdarg.h> +#define VARARGS(func,type,arg) func(type arg, ...) +#define VASTART(ap,type,name) va_start(ap,name) +#define VAEND(ap) va_end(ap) +#else +#include <varargs.h> +#define VARARGS(func,type,arg) func(va_alist) va_dcl +#define VASTART(ap,type,name) {type name; va_start(ap); name = va_arg(ap, type) +#define VAEND(ap) va_end(ap);} +#endif + +extern char *percent_m(); diff --git a/contrib/tcp_wrappers/myvsyslog.c b/contrib/tcp_wrappers/myvsyslog.c new file mode 100644 index 000000000000..20401f1f371b --- /dev/null +++ b/contrib/tcp_wrappers/myvsyslog.c @@ -0,0 +1,33 @@ + /* + * vsyslog() for sites without. In order to enable this code, build with + * -Dvsyslog=myvsyslog. We use a different name so that no accidents will + * happen when vsyslog() exists. On systems with vsyslog(), syslog() is + * typically implemented in terms of vsyslog(). + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) myvsyslog.c 1.1 94/12/28 17:42:33"; +#endif + +#ifdef vsyslog + +#include <stdio.h> + +#include "tcpd.h" +#include "mystdarg.h" + +myvsyslog(severity, format, ap) +int severity; +char *format; +va_list ap; +{ + char fbuf[BUFSIZ]; + char obuf[3 * STRING_LENGTH]; + + vsprintf(obuf, percent_m(fbuf, format), ap); + syslog(severity, "%s", obuf); +} + +#endif diff --git a/contrib/tcp_wrappers/ncr.c b/contrib/tcp_wrappers/ncr.c new file mode 100644 index 000000000000..b903fb85a565 --- /dev/null +++ b/contrib/tcp_wrappers/ncr.c @@ -0,0 +1,81 @@ + /* + * This part for NCR UNIX with is from Andrew Maffei (arm@aqua.whoi.edu). It + * assumes TLI throughout. In order to look up endpoint address information + * we must talk to the "timod" streams module. For some reason "timod" wants + * to sit directly on top of the device driver. Therefore we pop off all + * streams modules except the driver, install the "timod" module so that we + * can figure out network addresses, and then restore the original state. + */ + +#ifndef lint +static char sccsid[] = "@(#) ncr.c 1.1 94/12/28 17:42:34"; +#endif + +#include <sys/types.h> +#include <stdio.h> +#include <syslog.h> +#include <sys/tiuser.h> +#include <stropts.h> +#include <sys/conf.h> + +#include "tcpd.h" + +#define MAX_MODULE_COUNT 10 /* XXX */ + +/* fromhost - tear down the streams stack then rebuild it */ + +void fromhost(request) +struct request_info *request; +{ + int i; + int num_mod; + struct str_list str_list; + struct str_mlist mod_buffer[MAX_MODULE_COUNT]; + int fd = request->fd; + + str_list.sl_nmods = MAX_MODULE_COUNT; + str_list.sl_modlist = &mod_buffer[0]; + + /* + * On systems with WIN streams support we have to be careful about what + * is on the stream we are passed. This code POPs off all modules above + * the pseudo driver, pushes timod, gets the host address information, + * pops timod and then pushes all modules back on the stream. + * + * Some state may be lost in this process. /usr/etc/tlid seems to do special + * things to the stream depending on the TCP port being serviced. (not a + * very nice thing to do!). It is unclear what to do if this code breaks + * - the stream may be left in an unknown condition. + */ + if ((num_mod = ioctl(fd, I_LIST, NULL)) < 0) + tcpd_warn("fromhost: LIST failed: %m"); + if (ioctl(fd, I_LIST, &str_list) < 0) + tcpd_warn("fromhost: LIST failed: %m"); + + /* + * POP stream modules except for the driver. + */ + for (i = 0; i < num_mod - 1; i++) + if (ioctl(fd, I_POP, 0) < 0) + tcpd_warn("fromhost: POP %s: %m", mod_buffer[i].l_name); + + /* + * PUSH timod so that host address ioctls can be executed. + */ + if (ioctl(fd, I_PUSH, "timod") < 0) + tcpd_warn("fromhost: PUSH timod: %m"); + tli_host(request); + + /* + * POP timod, we're done with it now. + */ + if (ioctl(fd, I_POP, 0) < 0) + tcpd_warn("fromhost: POP timod: %m"); + + /* + * Restore stream modules. + */ + for (i = num_mod - 2; i >= 0; i--) + if (ioctl(fd, I_PUSH, mod_buffer[i].l_name) < 0) + tcpd_warn("fromhost: PUSH %s: %m", mod_buffer[i].l_name); +} diff --git a/contrib/tcp_wrappers/options.c b/contrib/tcp_wrappers/options.c new file mode 100644 index 000000000000..4fc6c9468732 --- /dev/null +++ b/contrib/tcp_wrappers/options.c @@ -0,0 +1,621 @@ + /* + * General skeleton for adding options to the access control language. The + * features offered by this module are documented in the hosts_options(5) + * manual page (source file: hosts_options.5, "nroff -man" format). + * + * Notes and warnings for those who want to add features: + * + * In case of errors, abort options processing and deny access. There are too + * many irreversible side effects to make error recovery feasible. For + * example, it makes no sense to continue after we have already changed the + * userid. + * + * In case of errors, do not terminate the process: the routines might be + * called from a long-running daemon that should run forever. Instead, call + * tcpd_jump() which does a non-local goto back into the hosts_access() + * routine. + * + * In case of severe errors, use clean_exit() instead of directly calling + * exit(), or the inetd may loop on an UDP request. + * + * In verification mode (for example, with the "tcpdmatch" command) the + * "dry_run" flag is set. In this mode, an option function should just "say" + * what it is going to do instead of really doing it. + * + * Some option functions do not return (for example, the twist option passes + * control to another program). In verification mode (dry_run flag is set) + * such options should clear the "dry_run" flag to inform the caller of this + * course of action. + */ + +#ifndef lint +static char sccsid[] = "@(#) options.c 1.17 96/02/11 17:01:31"; +#endif + +/* System libraries. */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <netdb.h> +#include <stdio.h> +#include <syslog.h> +#include <pwd.h> +#include <grp.h> +#include <ctype.h> +#include <setjmp.h> +#include <string.h> + +#ifndef MAXPATHNAMELEN +#define MAXPATHNAMELEN BUFSIZ +#endif + +/* Local stuff. */ + +#include "tcpd.h" + +/* Options runtime support. */ + +int dry_run = 0; /* flag set in verification mode */ +extern jmp_buf tcpd_buf; /* tcpd_jump() support */ + +/* Options parser support. */ + +static char whitespace_eq[] = "= \t\r\n"; +#define whitespace (whitespace_eq + 1) + +static char *get_field(); /* chew :-delimited field off string */ +static char *chop_string(); /* strip leading and trailing blanks */ + +/* List of functions that implement the options. Add yours here. */ + +static void user_option(); /* execute "user name.group" option */ +static void group_option(); /* execute "group name" option */ +static void umask_option(); /* execute "umask mask" option */ +static void linger_option(); /* execute "linger time" option */ +static void keepalive_option(); /* execute "keepalive" option */ +static void spawn_option(); /* execute "spawn command" option */ +static void twist_option(); /* execute "twist command" option */ +static void rfc931_option(); /* execute "rfc931" option */ +static void setenv_option(); /* execute "setenv name value" */ +static void nice_option(); /* execute "nice" option */ +static void severity_option(); /* execute "severity value" */ +static void allow_option(); /* execute "allow" option */ +static void deny_option(); /* execute "deny" option */ +static void banners_option(); /* execute "banners path" option */ + +/* Structure of the options table. */ + +struct option { + char *name; /* keyword name, case is ignored */ + void (*func) (); /* function that does the real work */ + int flags; /* see below... */ +}; + +#define NEED_ARG (1<<1) /* option requires argument */ +#define USE_LAST (1<<2) /* option must be last */ +#define OPT_ARG (1<<3) /* option has optional argument */ +#define EXPAND_ARG (1<<4) /* do %x expansion on argument */ + +#define need_arg(o) ((o)->flags & NEED_ARG) +#define opt_arg(o) ((o)->flags & OPT_ARG) +#define permit_arg(o) ((o)->flags & (NEED_ARG | OPT_ARG)) +#define use_last(o) ((o)->flags & USE_LAST) +#define expand_arg(o) ((o)->flags & EXPAND_ARG) + +/* List of known keywords. Add yours here. */ + +static struct option option_table[] = { + "user", user_option, NEED_ARG, + "group", group_option, NEED_ARG, + "umask", umask_option, NEED_ARG, + "linger", linger_option, NEED_ARG, + "keepalive", keepalive_option, 0, + "spawn", spawn_option, NEED_ARG | EXPAND_ARG, + "twist", twist_option, NEED_ARG | EXPAND_ARG | USE_LAST, + "rfc931", rfc931_option, OPT_ARG, + "setenv", setenv_option, NEED_ARG | EXPAND_ARG, + "nice", nice_option, OPT_ARG, + "severity", severity_option, NEED_ARG, + "allow", allow_option, USE_LAST, + "deny", deny_option, USE_LAST, + "banners", banners_option, NEED_ARG, + 0, +}; + +/* process_options - process access control options */ + +void process_options(options, request) +char *options; +struct request_info *request; +{ + char *key; + char *value; + char *curr_opt; + char *next_opt; + struct option *op; + char bf[BUFSIZ]; + + for (curr_opt = get_field(options); curr_opt; curr_opt = next_opt) { + next_opt = get_field((char *) 0); + + /* + * Separate the option into name and value parts. For backwards + * compatibility we ignore exactly one '=' between name and value. + */ + curr_opt = chop_string(curr_opt); + if (*(value = curr_opt + strcspn(curr_opt, whitespace_eq))) { + if (*value != '=') { + *value++ = 0; + value += strspn(value, whitespace); + } + if (*value == '=') { + *value++ = 0; + value += strspn(value, whitespace); + } + } + if (*value == 0) + value = 0; + key = curr_opt; + + /* + * Disallow missing option names (and empty option fields). + */ + if (*key == 0) + tcpd_jump("missing option name"); + + /* + * Lookup the option-specific info and do some common error checks. + * Delegate option-specific processing to the specific functions. + */ + + for (op = option_table; op->name && STR_NE(op->name, key); op++) + /* VOID */ ; + if (op->name == 0) + tcpd_jump("bad option name: \"%s\"", key); + if (!value && need_arg(op)) + tcpd_jump("option \"%s\" requires value", key); + if (value && !permit_arg(op)) + tcpd_jump("option \"%s\" requires no value", key); + if (next_opt && use_last(op)) + tcpd_jump("option \"%s\" must be at end", key); + if (value && expand_arg(op)) + value = chop_string(percent_x(bf, sizeof(bf), value, request)); + if (hosts_access_verbose) + syslog(LOG_DEBUG, "option: %s %s", key, value ? value : ""); + (*(op->func)) (value, request); + } +} + +/* allow_option - grant access */ + +/* ARGSUSED */ + +static void allow_option(value, request) +char *value; +struct request_info *request; +{ + longjmp(tcpd_buf, AC_PERMIT); +} + +/* deny_option - deny access */ + +/* ARGSUSED */ + +static void deny_option(value, request) +char *value; +struct request_info *request; +{ + longjmp(tcpd_buf, AC_DENY); +} + +/* banners_option - expand %<char>, terminate each line with CRLF */ + +static void banners_option(value, request) +char *value; +struct request_info *request; +{ + char path[MAXPATHNAMELEN]; + char ibuf[BUFSIZ]; + char obuf[2 * BUFSIZ]; + struct stat st; + int ch; + FILE *fp; + + sprintf(path, "%s/%s", value, eval_daemon(request)); + if ((fp = fopen(path, "r")) != 0) { + while ((ch = fgetc(fp)) == 0) + write(request->fd, "", 1); + ungetc(ch, fp); + while (fgets(ibuf, sizeof(ibuf) - 1, fp)) { + if (split_at(ibuf, '\n')) + strcat(ibuf, "\r\n"); + percent_x(obuf, sizeof(obuf), ibuf, request); + write(request->fd, obuf, strlen(obuf)); + } + fclose(fp); + } else if (stat(value, &st) < 0) { + tcpd_warn("%s: %m", value); + } +} + +/* group_option - switch group id */ + +/* ARGSUSED */ + +static void group_option(value, request) +char *value; +struct request_info *request; +{ + struct group *grp; + struct group *getgrnam(); + + if ((grp = getgrnam(value)) == 0) + tcpd_jump("unknown group: \"%s\"", value); + endgrent(); + + if (dry_run == 0 && setgid(grp->gr_gid)) + tcpd_jump("setgid(%s): %m", value); +} + +/* user_option - switch user id */ + +/* ARGSUSED */ + +static void user_option(value, request) +char *value; +struct request_info *request; +{ + struct passwd *pwd; + struct passwd *getpwnam(); + char *group; + + if ((group = split_at(value, '.')) != 0) + group_option(group, request); + if ((pwd = getpwnam(value)) == 0) + tcpd_jump("unknown user: \"%s\"", value); + endpwent(); + + if (dry_run == 0 && setuid(pwd->pw_uid)) + tcpd_jump("setuid(%s): %m", value); +} + +/* umask_option - set file creation mask */ + +/* ARGSUSED */ + +static void umask_option(value, request) +char *value; +struct request_info *request; +{ + unsigned mask; + char junk; + + if (sscanf(value, "%o%c", &mask, &junk) != 1 || (mask & 0777) != mask) + tcpd_jump("bad umask value: \"%s\"", value); + (void) umask(mask); +} + +/* spawn_option - spawn a shell command and wait */ + +/* ARGSUSED */ + +static void spawn_option(value, request) +char *value; +struct request_info *request; +{ + if (dry_run == 0) + shell_cmd(value); +} + +/* linger_option - set the socket linger time (Marc Boucher <marc@cam.org>) */ + +/* ARGSUSED */ + +static void linger_option(value, request) +char *value; +struct request_info *request; +{ + struct linger linger; + char junk; + + if (sscanf(value, "%d%c", &linger.l_linger, &junk) != 1 + || linger.l_linger < 0) + tcpd_jump("bad linger value: \"%s\"", value); + if (dry_run == 0) { + linger.l_onoff = (linger.l_linger != 0); + if (setsockopt(request->fd, SOL_SOCKET, SO_LINGER, (char *) &linger, + sizeof(linger)) < 0) + tcpd_warn("setsockopt SO_LINGER %d: %m", linger.l_linger); + } +} + +/* keepalive_option - set the socket keepalive option */ + +/* ARGSUSED */ + +static void keepalive_option(value, request) +char *value; +struct request_info *request; +{ + static int on = 1; + + if (dry_run == 0 && setsockopt(request->fd, SOL_SOCKET, SO_KEEPALIVE, + (char *) &on, sizeof(on)) < 0) + tcpd_warn("setsockopt SO_KEEPALIVE: %m"); +} + +/* nice_option - set nice value */ + +/* ARGSUSED */ + +static void nice_option(value, request) +char *value; +struct request_info *request; +{ + int niceval = 10; + char junk; + + if (value != 0 && sscanf(value, "%d%c", &niceval, &junk) != 1) + tcpd_jump("bad nice value: \"%s\"", value); + if (dry_run == 0 && nice(niceval) < 0) + tcpd_warn("nice(%d): %m", niceval); +} + +/* twist_option - replace process by shell command */ + +static void twist_option(value, request) +char *value; +struct request_info *request; +{ + char *error; + + if (dry_run != 0) { + dry_run = 0; + } else { + if (resident > 0) + tcpd_jump("twist option in resident process"); + + syslog(deny_severity, "twist %s to %s", eval_client(request), value); + + /* Before switching to the shell, set up stdin, stdout and stderr. */ + +#define maybe_dup2(from, to) ((from == to) ? to : (close(to), dup(from))) + + if (maybe_dup2(request->fd, 0) != 0 || + maybe_dup2(request->fd, 1) != 1 || + maybe_dup2(request->fd, 2) != 2) { + error = "twist_option: dup: %m"; + } else { + if (request->fd > 2) + close(request->fd); + (void) execl("/bin/sh", "sh", "-c", value, (char *) 0); + error = "twist_option: /bin/sh: %m"; + } + + /* Something went wrong: we MUST terminate the process. */ + + tcpd_warn(error); + clean_exit(request); + } +} + +/* rfc931_option - look up remote user name */ + +static void rfc931_option(value, request) +char *value; +struct request_info *request; +{ + int timeout; + char junk; + + if (value != 0) { + if (sscanf(value, "%d%c", &timeout, &junk) != 1 || timeout <= 0) + tcpd_jump("bad rfc931 timeout: \"%s\"", value); + rfc931_timeout = timeout; + } + (void) eval_user(request); +} + +/* setenv_option - set environment variable */ + +/* ARGSUSED */ + +static void setenv_option(value, request) +char *value; +struct request_info *request; +{ + char *var_value; + + if (*(var_value = value + strcspn(value, whitespace))) + *var_value++ = 0; + if (setenv(chop_string(value), chop_string(var_value), 1)) + tcpd_jump("memory allocation failure"); +} + + /* + * The severity option goes last because it comes with a huge amount of ugly + * #ifdefs and tables. + */ + +struct syslog_names { + char *name; + int value; +}; + +static struct syslog_names log_fac[] = { +#ifdef LOG_KERN + "kern", LOG_KERN, +#endif +#ifdef LOG_USER + "user", LOG_USER, +#endif +#ifdef LOG_MAIL + "mail", LOG_MAIL, +#endif +#ifdef LOG_DAEMON + "daemon", LOG_DAEMON, +#endif +#ifdef LOG_AUTH + "auth", LOG_AUTH, +#endif +#ifdef LOG_LPR + "lpr", LOG_LPR, +#endif +#ifdef LOG_NEWS + "news", LOG_NEWS, +#endif +#ifdef LOG_UUCP + "uucp", LOG_UUCP, +#endif +#ifdef LOG_CRON + "cron", LOG_CRON, +#endif +#ifdef LOG_LOCAL0 + "local0", LOG_LOCAL0, +#endif +#ifdef LOG_LOCAL1 + "local1", LOG_LOCAL1, +#endif +#ifdef LOG_LOCAL2 + "local2", LOG_LOCAL2, +#endif +#ifdef LOG_LOCAL3 + "local3", LOG_LOCAL3, +#endif +#ifdef LOG_LOCAL4 + "local4", LOG_LOCAL4, +#endif +#ifdef LOG_LOCAL5 + "local5", LOG_LOCAL5, +#endif +#ifdef LOG_LOCAL6 + "local6", LOG_LOCAL6, +#endif +#ifdef LOG_LOCAL7 + "local7", LOG_LOCAL7, +#endif + 0, +}; + +static struct syslog_names log_sev[] = { +#ifdef LOG_EMERG + "emerg", LOG_EMERG, +#endif +#ifdef LOG_ALERT + "alert", LOG_ALERT, +#endif +#ifdef LOG_CRIT + "crit", LOG_CRIT, +#endif +#ifdef LOG_ERR + "err", LOG_ERR, +#endif +#ifdef LOG_WARNING + "warning", LOG_WARNING, +#endif +#ifdef LOG_NOTICE + "notice", LOG_NOTICE, +#endif +#ifdef LOG_INFO + "info", LOG_INFO, +#endif +#ifdef LOG_DEBUG + "debug", LOG_DEBUG, +#endif + 0, +}; + +/* severity_map - lookup facility or severity value */ + +static int severity_map(table, name) +struct syslog_names *table; +char *name; +{ + struct syslog_names *t; + + for (t = table; t->name; t++) + if (STR_EQ(t->name, name)) + return (t->value); + tcpd_jump("bad syslog facility or severity: \"%s\"", name); + /* NOTREACHED */ +} + +/* severity_option - change logging severity for this event (Dave Mitchell) */ + +/* ARGSUSED */ + +static void severity_option(value, request) +char *value; +struct request_info *request; +{ + char *level = split_at(value, '.'); + + allow_severity = deny_severity = level ? + severity_map(log_fac, value) | severity_map(log_sev, level) : + severity_map(log_sev, value); +} + +/* get_field - return pointer to next field in string */ + +static char *get_field(string) +char *string; +{ + static char *last = ""; + char *src; + char *dst; + char *ret; + int ch; + + /* + * This function returns pointers to successive fields within a given + * string. ":" is the field separator; warn if the rule ends in one. It + * replaces a "\:" sequence by ":", without treating the result of + * substitution as field terminator. A null argument means resume search + * where the previous call terminated. This function destroys its + * argument. + * + * Work from explicit source or from memory. While processing \: we + * overwrite the input. This way we do not have to maintain buffers for + * copies of input fields. + */ + + src = dst = ret = (string ? string : last); + if (src[0] == 0) + return (0); + + while (ch = *src) { + if (ch == ':') { + if (*++src == 0) + tcpd_warn("rule ends in \":\""); + break; + } + if (ch == '\\' && src[1] == ':') + src++; + *dst++ = *src++; + } + last = src; + *dst = 0; + return (ret); +} + +/* chop_string - strip leading and trailing blanks from string */ + +static char *chop_string(string) +register char *string; +{ + char *start = 0; + char *end; + char *cp; + + for (cp = string; *cp; cp++) { + if (!isspace(*cp)) { + if (start == 0) + start = cp; + end = cp; + } + } + return (start ? (end[1] = 0, start) : cp); +} diff --git a/contrib/tcp_wrappers/patchlevel.h b/contrib/tcp_wrappers/patchlevel.h new file mode 100644 index 000000000000..c523265e384e --- /dev/null +++ b/contrib/tcp_wrappers/patchlevel.h @@ -0,0 +1,3 @@ +#ifndef lint +static char patchlevel[] = "@(#) patchlevel 7.6 97/03/21 19:27:23"; +#endif diff --git a/contrib/tcp_wrappers/percent_m.c b/contrib/tcp_wrappers/percent_m.c new file mode 100644 index 000000000000..bb11b22dd81f --- /dev/null +++ b/contrib/tcp_wrappers/percent_m.c @@ -0,0 +1,43 @@ + /* + * Replace %m by system error message. + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) percent_m.c 1.1 94/12/28 17:42:37"; +#endif + +#include <stdio.h> +#include <errno.h> +#include <string.h> + +extern int errno; +#ifndef SYS_ERRLIST_DEFINED +extern char *sys_errlist[]; +extern int sys_nerr; +#endif + +#include "mystdarg.h" + +char *percent_m(obuf, ibuf) +char *obuf; +char *ibuf; +{ + char *bp = obuf; + char *cp = ibuf; + + while (*bp = *cp) + if (*cp == '%' && cp[1] == 'm') { + if (errno < sys_nerr && errno > 0) { + strcpy(bp, sys_errlist[errno]); + } else { + sprintf(bp, "Unknown error %d", errno); + } + bp += strlen(bp); + cp += 2; + } else { + bp++, cp++; + } + return (obuf); +} diff --git a/contrib/tcp_wrappers/percent_x.c b/contrib/tcp_wrappers/percent_x.c new file mode 100644 index 000000000000..c95a1ea4141e --- /dev/null +++ b/contrib/tcp_wrappers/percent_x.c @@ -0,0 +1,86 @@ + /* + * percent_x() takes a string and performs %<char> expansions. It aborts the + * program when the expansion would overflow the output buffer. The result + * of %<char> expansion may be passed on to a shell process. For this + * reason, characters with a special meaning to shells are replaced by + * underscores. + * + * Diagnostics are reported through syslog(3). + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) percent_x.c 1.4 94/12/28 17:42:37"; +#endif + +/* System libraries. */ + +#include <stdio.h> +#include <syslog.h> +#include <string.h> + +extern void exit(); + +/* Local stuff. */ + +#include "tcpd.h" + +/* percent_x - do %<char> expansion, abort if result buffer is too small */ + +char *percent_x(result, result_len, string, request) +char *result; +int result_len; +char *string; +struct request_info *request; +{ + char *bp = result; + char *end = result + result_len - 1; /* end of result buffer */ + char *expansion; + int expansion_len; + static char ok_chars[] = "1234567890!@%-_=+:,./\ +abcdefghijklmnopqrstuvwxyz\ +ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + char *str = string; + char *cp; + int ch; + + /* + * Warning: we may be called from a child process or after pattern + * matching, so we cannot use clean_exit() or tcpd_jump(). + */ + + while (*str) { + if (*str == '%' && (ch = str[1]) != 0) { + str += 2; + expansion = + ch == 'a' ? eval_hostaddr(request->client) : + ch == 'A' ? eval_hostaddr(request->server) : + ch == 'c' ? eval_client(request) : + ch == 'd' ? eval_daemon(request) : + ch == 'h' ? eval_hostinfo(request->client) : + ch == 'H' ? eval_hostinfo(request->server) : + ch == 'n' ? eval_hostname(request->client) : + ch == 'N' ? eval_hostname(request->server) : + ch == 'p' ? eval_pid(request) : + ch == 's' ? eval_server(request) : + ch == 'u' ? eval_user(request) : + ch == '%' ? "%" : (tcpd_warn("unrecognized %%%c", ch), ""); + for (cp = expansion; *(cp += strspn(cp, ok_chars)); /* */ ) + *cp = '_'; + expansion_len = cp - expansion; + } else { + expansion = str++; + expansion_len = 1; + } + if (bp + expansion_len >= end) { + tcpd_warn("percent_x: expansion too long: %.30s...", result); + sleep(5); + exit(0); + } + memcpy(bp, expansion, expansion_len); + bp += expansion_len; + } + *bp = 0; + return (result); +} diff --git a/contrib/tcp_wrappers/printf.ck b/contrib/tcp_wrappers/printf.ck new file mode 100644 index 000000000000..d53412b7371b --- /dev/null +++ b/contrib/tcp_wrappers/printf.ck @@ -0,0 +1,3 @@ +syslog 1 0 +tcpd_warn 0 0 +tcpd_jump 0 0 diff --git a/contrib/tcp_wrappers/ptx.c b/contrib/tcp_wrappers/ptx.c new file mode 100644 index 000000000000..b9c312b82cdc --- /dev/null +++ b/contrib/tcp_wrappers/ptx.c @@ -0,0 +1,103 @@ + /* + * The Dynix/PTX TLI implementation is not quite compatible with System V + * Release 4. Some important functions are not present so we are limited to + * IP-based services. + * + * Diagnostics are reported through syslog(3). + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) ptx.c 1.3 94/12/28 17:42:38"; +#endif + +#ifdef PTX + +/* System libraries. */ + +#include <sys/types.h> +#include <sys/tiuser.h> +#include <sys/socket.h> +#include <stropts.h> +#include <netinet/in.h> +#include <netdb.h> +#include <stdio.h> +#include <syslog.h> + +/* Local stuff. */ + +#include "tcpd.h" + +/* Forward declarations. */ + +static void ptx_sink(); + +/* tli_host - determine TLI endpoint info, PTX version */ + +void tli_host(request) +struct request_info *request; +{ + static struct sockaddr_in client; + static struct sockaddr_in server; + + /* + * getpeerinaddr() was suggested by someone at Sequent. It seems to work + * with connection-oriented (TCP) services such as rlogind and telnetd, + * but it returns 0.0.0.0 with datagram (UDP) services. No problem: UDP + * needs special treatment anyway, in case we must refuse service. + */ + + if (getpeerinaddr(request->fd, &client, sizeof(client)) == 0 + && client.sin_addr.s_addr != 0) { + request->client->sin = &client; + if (getmyinaddr(request->fd, &server, sizeof(server)) == 0) { + request->server->sin = &server; + } else { + tcpd_warn("warning: getmyinaddr: %m"); + } + sock_methods(request); + + } else { + + /* + * Another suggestion was to temporarily switch to the socket + * interface, identify the endpoint addresses with socket calls, then + * to switch back to TLI. This seems to works OK with UDP services, + * which is exactly what we should be looking at right now. + */ + +#define SWAP_MODULE(f, old, new) (ioctl(f, I_POP, old), ioctl(f, I_PUSH, new)) + + if (SWAP_MODULE(request->fd, "timod", "sockmod") != 0) + tcpd_warn("replace timod by sockmod: %m"); + sock_host(request); + if (SWAP_MODULE(request->fd, "sockmod", "timod") != 0) + tcpd_warn("replace sockmod by timod: %m"); + if (request->sink != 0) + request->sink = ptx_sink; + } +} + +/* ptx_sink - absorb unreceived IP datagram */ + +static void ptx_sink(fd) +int fd; +{ + char buf[BUFSIZ]; + struct sockaddr sa; + int size = sizeof(sa); + + /* + * Eat up the not-yet received datagram. Where needed, switch to the + * socket programming interface. + */ + + if (ioctl(fd, I_FIND, "timod") != 0) + ioctl(fd, I_POP, "timod"); + if (ioctl(fd, I_FIND, "sockmod") == 0) + ioctl(fd, I_PUSH, "sockmod"); + (void) recvfrom(fd, buf, sizeof(buf), 0, &sa, &size); +} + +#endif /* PTX */ diff --git a/contrib/tcp_wrappers/refuse.c b/contrib/tcp_wrappers/refuse.c new file mode 100644 index 000000000000..ccf80300529e --- /dev/null +++ b/contrib/tcp_wrappers/refuse.c @@ -0,0 +1,32 @@ + /* + * refuse() reports a refused connection, and takes the consequences: in + * case of a datagram-oriented service, the unread datagram is taken from + * the input queue (or inetd would see the same datagram again and again); + * the program is terminated. + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) refuse.c 1.5 94/12/28 17:42:39"; +#endif + +/* System libraries. */ + +#include <stdio.h> +#include <syslog.h> + +/* Local stuff. */ + +#include "tcpd.h" + +/* refuse - refuse request */ + +void refuse(request) +struct request_info *request; +{ + syslog(deny_severity, "refused connect from %s", eval_client(request)); + clean_exit(request); + /* NOTREACHED */ +} + diff --git a/contrib/tcp_wrappers/rfc931.c b/contrib/tcp_wrappers/rfc931.c new file mode 100644 index 000000000000..8176417b8fd6 --- /dev/null +++ b/contrib/tcp_wrappers/rfc931.c @@ -0,0 +1,165 @@ + /* + * rfc931() speaks a common subset of the RFC 931, AUTH, TAP, IDENT and RFC + * 1413 protocols. It queries an RFC 931 etc. compatible daemon on a remote + * host to look up the owner of a connection. The information should not be + * used for authentication purposes. This routine intercepts alarm signals. + * + * Diagnostics are reported through syslog(3). + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) rfc931.c 1.10 95/01/02 16:11:34"; +#endif + +/* System libraries. */ + +#include <stdio.h> +#include <syslog.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <setjmp.h> +#include <signal.h> +#include <string.h> + +/* Local stuff. */ + +#include "tcpd.h" + +#define RFC931_PORT 113 /* Semi-well-known port */ +#define ANY_PORT 0 /* Any old port will do */ + +int rfc931_timeout = RFC931_TIMEOUT;/* Global so it can be changed */ + +static jmp_buf timebuf; + +/* fsocket - open stdio stream on top of socket */ + +static FILE *fsocket(domain, type, protocol) +int domain; +int type; +int protocol; +{ + int s; + FILE *fp; + + if ((s = socket(domain, type, protocol)) < 0) { + tcpd_warn("socket: %m"); + return (0); + } else { + if ((fp = fdopen(s, "r+")) == 0) { + tcpd_warn("fdopen: %m"); + close(s); + } + return (fp); + } +} + +/* timeout - handle timeouts */ + +static void timeout(sig) +int sig; +{ + longjmp(timebuf, sig); +} + +/* rfc931 - return remote user name, given socket structures */ + +void rfc931(rmt_sin, our_sin, dest) +struct sockaddr_in *rmt_sin; +struct sockaddr_in *our_sin; +char *dest; +{ + unsigned rmt_port; + unsigned our_port; + struct sockaddr_in rmt_query_sin; + struct sockaddr_in our_query_sin; + char user[256]; /* XXX */ + char buffer[512]; /* XXX */ + char *cp; + char *result = unknown; + FILE *fp; + + /* + * Use one unbuffered stdio stream for writing to and for reading from + * the RFC931 etc. server. This is done because of a bug in the SunOS + * 4.1.x stdio library. The bug may live in other stdio implementations, + * too. When we use a single, buffered, bidirectional stdio stream ("r+" + * or "w+" mode) we read our own output. Such behaviour would make sense + * with resources that support random-access operations, but not with + * sockets. + */ + + if ((fp = fsocket(AF_INET, SOCK_STREAM, 0)) != 0) { + setbuf(fp, (char *) 0); + + /* + * Set up a timer so we won't get stuck while waiting for the server. + */ + + if (setjmp(timebuf) == 0) { + signal(SIGALRM, timeout); + alarm(rfc931_timeout); + + /* + * Bind the local and remote ends of the query socket to the same + * IP addresses as the connection under investigation. We go + * through all this trouble because the local or remote system + * might have more than one network address. The RFC931 etc. + * client sends only port numbers; the server takes the IP + * addresses from the query socket. + */ + + our_query_sin = *our_sin; + our_query_sin.sin_port = htons(ANY_PORT); + rmt_query_sin = *rmt_sin; + rmt_query_sin.sin_port = htons(RFC931_PORT); + + if (bind(fileno(fp), (struct sockaddr *) & our_query_sin, + sizeof(our_query_sin)) >= 0 && + connect(fileno(fp), (struct sockaddr *) & rmt_query_sin, + sizeof(rmt_query_sin)) >= 0) { + + /* + * Send query to server. Neglect the risk that a 13-byte + * write would have to be fragmented by the local system and + * cause trouble with buggy System V stdio libraries. + */ + + fprintf(fp, "%u,%u\r\n", + ntohs(rmt_sin->sin_port), + ntohs(our_sin->sin_port)); + fflush(fp); + + /* + * Read response from server. Use fgets()/sscanf() so we can + * work around System V stdio libraries that incorrectly + * assume EOF when a read from a socket returns less than + * requested. + */ + + if (fgets(buffer, sizeof(buffer), fp) != 0 + && ferror(fp) == 0 && feof(fp) == 0 + && sscanf(buffer, "%u , %u : USERID :%*[^:]:%255s", + &rmt_port, &our_port, user) == 3 + && ntohs(rmt_sin->sin_port) == rmt_port + && ntohs(our_sin->sin_port) == our_port) { + + /* + * Strip trailing carriage return. It is part of the + * protocol, not part of the data. + */ + + if (cp = strchr(user, '\r')) + *cp = 0; + result = user; + } + } + alarm(0); + } + fclose(fp); + } + STRN_CPY(dest, result, STRING_LENGTH); +} diff --git a/contrib/tcp_wrappers/safe_finger.c b/contrib/tcp_wrappers/safe_finger.c new file mode 100644 index 000000000000..7b8f3cd638a8 --- /dev/null +++ b/contrib/tcp_wrappers/safe_finger.c @@ -0,0 +1,196 @@ + /* + * safe_finger - finger client wrapper that protects against nasty stuff + * from finger servers. Use this program for automatic reverse finger + * probes, not the raw finger command. + * + * Build with: cc -o safe_finger safe_finger.c + * + * The problem: some programs may react to stuff in the first column. Other + * programs may get upset by thrash anywhere on a line. File systems may + * fill up as the finger server keeps sending data. Text editors may bomb + * out on extremely long lines. The finger server may take forever because + * it is somehow wedged. The code below takes care of all this badness. + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) safe_finger.c 1.4 94/12/28 17:42:41"; +#endif + +/* System libraries */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <signal.h> +#include <stdio.h> +#include <ctype.h> +#include <pwd.h> + +extern void exit(); + +/* Local stuff */ + +char path[] = "PATH=/bin:/usr/bin:/usr/ucb:/usr/bsd:/etc:/usr/etc:/usr/sbin"; + +#define TIME_LIMIT 60 /* Do not keep listinging forever */ +#define INPUT_LENGTH 100000 /* Do not keep listinging forever */ +#define LINE_LENGTH 128 /* Editors can choke on long lines */ +#define FINGER_PROGRAM "finger" /* Most, if not all, UNIX systems */ +#define UNPRIV_NAME "nobody" /* Preferred privilege level */ +#define UNPRIV_UGID 32767 /* Default uid and gid */ + +int finger_pid; + +void cleanup(sig) +int sig; +{ + kill(finger_pid, SIGKILL); + exit(0); +} + +main(argc, argv) +int argc; +char **argv; +{ + int c; + int line_length = 0; + int finger_status; + int wait_pid; + int input_count = 0; + struct passwd *pwd; + + /* + * First of all, let's don't run with superuser privileges. + */ + if (getuid() == 0 || geteuid() == 0) { + if ((pwd = getpwnam(UNPRIV_NAME)) && pwd->pw_uid > 0) { + setgid(pwd->pw_gid); + setuid(pwd->pw_uid); + } else { + setgid(UNPRIV_UGID); + setuid(UNPRIV_UGID); + } + } + + /* + * Redirect our standard input through the raw finger command. + */ + if (putenv(path)) { + fprintf(stderr, "%s: putenv: out of memory", argv[0]); + exit(1); + } + argv[0] = FINGER_PROGRAM; + finger_pid = pipe_stdin(argv); + + /* + * Don't wait forever (Peter Wemm <peter@gecko.DIALix.oz.au>). + */ + signal(SIGALRM, cleanup); + (void) alarm(TIME_LIMIT); + + /* + * Main filter loop. + */ + while ((c = getchar()) != EOF) { + if (input_count++ >= INPUT_LENGTH) { /* don't listen forever */ + fclose(stdin); + printf("\n\n Input truncated to %d bytes...\n", input_count - 1); + break; + } + if (c == '\n') { /* good: end of line */ + putchar(c); + line_length = 0; + } else { + if (line_length >= LINE_LENGTH) { /* force end of line */ + printf("\\\n"); + line_length = 0; + } + if (line_length == 0) { /* protect left margin */ + putchar(' '); + line_length++; + } + if (isascii(c) && (isprint(c) || isspace(c))) { /* text */ + if (c == '\\') { + putchar(c); + line_length++; + } + putchar(c); + line_length++; + } else { /* quote all other thash */ + printf("\\%03o", c & 0377); + line_length += 4; + } + } + } + + /* + * Wait until the finger child process has terminated and account for its + * exit status. Which will always be zero on most systems. + */ + while ((wait_pid = wait(&finger_status)) != -1 && wait_pid != finger_pid) + /* void */ ; + return (wait_pid != finger_pid || finger_status != 0); +} + +/* perror_exit - report system error text and terminate */ + +void perror_exit(text) +char *text; +{ + perror(text); + exit(1); +} + +/* pipe_stdin - pipe stdin through program (from my ANSI to OLD C converter) */ + +int pipe_stdin(argv) +char **argv; +{ + int pipefds[2]; + int pid; + int i; + struct stat st; + + /* + * The code that sets up the pipe requires that file descriptors 0,1,2 + * are already open. All kinds of mysterious things will happen if that + * is not the case. The following loops makes sure that descriptors 0,1,2 + * are set up properly. + */ + + for (i = 0; i < 3; i++) { + if (fstat(i, &st) == -1 && open("/dev/null", 2) != i) + perror_exit("open /dev/null"); + } + + /* + * Set up the pipe that interposes the command into our standard input + * stream. + */ + + if (pipe(pipefds)) + perror_exit("pipe"); + + switch (pid = fork()) { + case -1: /* error */ + perror_exit("fork"); + /* NOTREACHED */ + case 0: /* child */ + (void) close(pipefds[0]); /* close reading end */ + (void) close(1); /* connect stdout to pipe */ + if (dup(pipefds[1]) != 1) + perror_exit("dup"); + (void) close(pipefds[1]); /* close redundant fd */ + (void) execvp(argv[0], argv); + perror_exit(argv[0]); + /* NOTREACHED */ + default: /* parent */ + (void) close(pipefds[1]); /* close writing end */ + (void) close(0); /* connect stdin to pipe */ + if (dup(pipefds[0]) != 0) + perror_exit("dup"); + (void) close(pipefds[0]); /* close redundant fd */ + return (pid); + } +} diff --git a/contrib/tcp_wrappers/scaffold.c b/contrib/tcp_wrappers/scaffold.c new file mode 100644 index 000000000000..afce15a64e4d --- /dev/null +++ b/contrib/tcp_wrappers/scaffold.c @@ -0,0 +1,213 @@ + /* + * Routines for testing only. Not really industrial strength. + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccs_id[] = "@(#) scaffold.c 1.6 97/03/21 19:27:24"; +#endif + +/* System libraries. */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <stdio.h> +#include <syslog.h> +#include <setjmp.h> +#include <string.h> + +#ifndef INADDR_NONE +#define INADDR_NONE (-1) /* XXX should be 0xffffffff */ +#endif + +extern char *malloc(); + +/* Application-specific. */ + +#include "tcpd.h" +#include "scaffold.h" + + /* + * These are referenced by the options module and by rfc931.c. + */ +int allow_severity = SEVERITY; +int deny_severity = LOG_WARNING; +int rfc931_timeout = RFC931_TIMEOUT; + +/* dup_hostent - create hostent in one memory block */ + +static struct hostent *dup_hostent(hp) +struct hostent *hp; +{ + struct hostent_block { + struct hostent host; + char *addr_list[1]; + }; + struct hostent_block *hb; + int count; + char *data; + char *addr; + + for (count = 0; hp->h_addr_list[count] != 0; count++) + /* void */ ; + + if ((hb = (struct hostent_block *) malloc(sizeof(struct hostent_block) + + (hp->h_length + sizeof(char *)) * count)) == 0) { + fprintf(stderr, "Sorry, out of memory\n"); + exit(1); + } + memset((char *) &hb->host, 0, sizeof(hb->host)); + hb->host.h_length = hp->h_length; + hb->host.h_addr_list = hb->addr_list; + hb->host.h_addr_list[count] = 0; + data = (char *) (hb->host.h_addr_list + count + 1); + + for (count = 0; (addr = hp->h_addr_list[count]) != 0; count++) { + hb->host.h_addr_list[count] = data + hp->h_length * count; + memcpy(hb->host.h_addr_list[count], addr, hp->h_length); + } + return (&hb->host); +} + +/* find_inet_addr - find all addresses for this host, result to free() */ + +struct hostent *find_inet_addr(host) +char *host; +{ + struct in_addr addr; + struct hostent *hp; + static struct hostent h; + static char *addr_list[2]; + + /* + * Host address: translate it to internal form. + */ + if ((addr.s_addr = dot_quad_addr(host)) != INADDR_NONE) { + h.h_addr_list = addr_list; + h.h_addr_list[0] = (char *) &addr; + h.h_length = sizeof(addr); + return (dup_hostent(&h)); + } + + /* + * Map host name to a series of addresses. Watch out for non-internet + * forms or aliases. The NOT_INADDR() is here in case gethostbyname() has + * been "enhanced" to accept numeric addresses. Make a copy of the + * address list so that later gethostbyXXX() calls will not clobber it. + */ + if (NOT_INADDR(host) == 0) { + tcpd_warn("%s: not an internet address", host); + return (0); + } + if ((hp = gethostbyname(host)) == 0) { + tcpd_warn("%s: host not found", host); + return (0); + } + if (hp->h_addrtype != AF_INET) { + tcpd_warn("%d: not an internet host", hp->h_addrtype); + return (0); + } + if (STR_NE(host, hp->h_name)) { + tcpd_warn("%s: hostname alias", host); + tcpd_warn("(official name: %.*s)", STRING_LENGTH, hp->h_name); + } + return (dup_hostent(hp)); +} + +/* check_dns - give each address thorough workout, return address count */ + +int check_dns(host) +char *host; +{ + struct request_info request; + struct sockaddr_in sin; + struct hostent *hp; + int count; + char *addr; + + if ((hp = find_inet_addr(host)) == 0) + return (0); + request_init(&request, RQ_CLIENT_SIN, &sin, 0); + sock_methods(&request); + memset((char *) &sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + + for (count = 0; (addr = hp->h_addr_list[count]) != 0; count++) { + memcpy((char *) &sin.sin_addr, addr, sizeof(sin.sin_addr)); + + /* + * Force host name and address conversions. Use the request structure + * as a cache. Detect hostname lookup problems. Any name/name or + * name/address conflicts will be reported while eval_hostname() does + * its job. + */ + request_set(&request, RQ_CLIENT_ADDR, "", RQ_CLIENT_NAME, "", 0); + if (STR_EQ(eval_hostname(request.client), unknown)) + tcpd_warn("host address %s->name lookup failed", + eval_hostaddr(request.client)); + } + free((char *) hp); + return (count); +} + +/* dummy function to intercept the real shell_cmd() */ + +/* ARGSUSED */ + +void shell_cmd(command) +char *command; +{ + if (hosts_access_verbose) + printf("command: %s", command); +} + +/* dummy function to intercept the real clean_exit() */ + +/* ARGSUSED */ + +void clean_exit(request) +struct request_info *request; +{ + exit(0); +} + +/* dummy function to intercept the real rfc931() */ + +/* ARGSUSED */ + +void rfc931(request) +struct request_info *request; +{ + strcpy(request->user, unknown); +} + +/* check_path - examine accessibility */ + +int check_path(path, st) +char *path; +struct stat *st; +{ + struct stat stbuf; + char buf[BUFSIZ]; + + if (stat(path, st) < 0) + return (-1); +#ifdef notdef + if (st->st_uid != 0) + tcpd_warn("%s: not owned by root", path); + if (st->st_mode & 020) + tcpd_warn("%s: group writable", path); +#endif + if (st->st_mode & 002) + tcpd_warn("%s: world writable", path); + if (path[0] == '/' && path[1] != 0) { + strrchr(strcpy(buf, path), '/')[0] = 0; + (void) check_path(buf[0] ? buf : "/", &stbuf); + } + return (0); +} diff --git a/contrib/tcp_wrappers/scaffold.h b/contrib/tcp_wrappers/scaffold.h new file mode 100644 index 000000000000..2273f4730b3f --- /dev/null +++ b/contrib/tcp_wrappers/scaffold.h @@ -0,0 +1,9 @@ + /* + * @(#) scaffold.h 1.3 94/12/31 18:19:19 + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +extern struct hostent *find_inet_addr(); +extern int check_dns(); +extern int check_path(); diff --git a/contrib/tcp_wrappers/setenv.c b/contrib/tcp_wrappers/setenv.c new file mode 100644 index 000000000000..03c706280c64 --- /dev/null +++ b/contrib/tcp_wrappers/setenv.c @@ -0,0 +1,34 @@ + /* + * Some systems do not have setenv(). This one is modeled after 4.4 BSD, but + * is implemented in terms of portable primitives only: getenv(), putenv() + * and malloc(). It should therefore be safe to use on every UNIX system. + * + * If clobber == 0, do not overwrite an existing variable. + * + * Returns nonzero if memory allocation fails. + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) setenv.c 1.1 93/03/07 22:47:58"; +#endif + +/* setenv - update or insert environment (name,value) pair */ + +int setenv(name, value, clobber) +char *name; +char *value; +int clobber; +{ + char *malloc(); + char *getenv(); + char *cp; + + if (clobber == 0 && getenv(name) != 0) + return (0); + if ((cp = malloc(strlen(name) + strlen(value) + 2)) == 0) + return (1); + sprintf(cp, "%s=%s", name, value); + return (putenv(cp)); +} diff --git a/contrib/tcp_wrappers/shell_cmd.c b/contrib/tcp_wrappers/shell_cmd.c new file mode 100644 index 000000000000..62d31bc5240d --- /dev/null +++ b/contrib/tcp_wrappers/shell_cmd.c @@ -0,0 +1,92 @@ + /* + * shell_cmd() takes a shell command after %<character> substitutions. The + * command is executed by a /bin/sh child process, with standard input, + * standard output and standard error connected to /dev/null. + * + * Diagnostics are reported through syslog(3). + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) shell_cmd.c 1.5 94/12/28 17:42:44"; +#endif + +/* System libraries. */ + +#include <sys/types.h> +#include <sys/param.h> +#include <signal.h> +#include <stdio.h> +#include <syslog.h> +#include <string.h> + +extern void exit(); + +/* Local stuff. */ + +#include "tcpd.h" + +/* Forward declarations. */ + +static void do_child(); + +/* shell_cmd - execute shell command */ + +void shell_cmd(command) +char *command; +{ + int child_pid; + int wait_pid; + + /* + * Most of the work is done within the child process, to minimize the + * risk of damage to the parent. + */ + + switch (child_pid = fork()) { + case -1: /* error */ + tcpd_warn("cannot fork: %m"); + break; + case 00: /* child */ + do_child(command); + /* NOTREACHED */ + default: /* parent */ + while ((wait_pid = wait((int *) 0)) != -1 && wait_pid != child_pid) + /* void */ ; + } +} + +/* do_child - exec command with { stdin, stdout, stderr } to /dev/null */ + +static void do_child(command) +char *command; +{ + char *error; + int tmp_fd; + + /* + * Systems with POSIX sessions may send a SIGHUP to grandchildren if the + * child exits first. This is sick, sessions were invented for terminals. + */ + + signal(SIGHUP, SIG_IGN); + + /* Set up new stdin, stdout, stderr, and exec the shell command. */ + + for (tmp_fd = 0; tmp_fd < 3; tmp_fd++) + (void) close(tmp_fd); + if (open("/dev/null", 2) != 0) { + error = "open /dev/null: %m"; + } else if (dup(0) != 1 || dup(0) != 2) { + error = "dup: %m"; + } else { + (void) execl("/bin/sh", "sh", "-c", command, (char *) 0); + error = "execl /bin/sh: %m"; + } + + /* Something went wrong. We MUST terminate the child process. */ + + tcpd_warn(error); + _exit(0); +} diff --git a/contrib/tcp_wrappers/socket.c b/contrib/tcp_wrappers/socket.c new file mode 100644 index 000000000000..c659b166698f --- /dev/null +++ b/contrib/tcp_wrappers/socket.c @@ -0,0 +1,235 @@ + /* + * This module determines the type of socket (datagram, stream), the client + * socket address and port, the server socket address and port. In addition, + * it provides methods to map a transport address to a printable host name + * or address. Socket address information results are in static memory. + * + * The result from the hostname lookup method is STRING_PARANOID when a host + * pretends to have someone elses name, or when a host name is available but + * could not be verified. + * + * When lookup or conversion fails the result is set to STRING_UNKNOWN. + * + * Diagnostics are reported through syslog(3). + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) socket.c 1.15 97/03/21 19:27:24"; +#endif + +/* System libraries. */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <stdio.h> +#include <syslog.h> +#include <string.h> + +extern char *inet_ntoa(); + +/* Local stuff. */ + +#include "tcpd.h" + +/* Forward declarations. */ + +static void sock_sink(); + +#ifdef APPEND_DOT + + /* + * Speed up DNS lookups by terminating the host name with a dot. Should be + * done with care. The speedup can give problems with lookups from sources + * that lack DNS-style trailing dot magic, such as local files or NIS maps. + */ + +static struct hostent *gethostbyname_dot(name) +char *name; +{ + char dot_name[MAXHOSTNAMELEN + 1]; + + /* + * Don't append dots to unqualified names. Such names are likely to come + * from local hosts files or from NIS. + */ + + if (strchr(name, '.') == 0 || strlen(name) >= MAXHOSTNAMELEN - 1) { + return (gethostbyname(name)); + } else { + sprintf(dot_name, "%s.", name); + return (gethostbyname(dot_name)); + } +} + +#define gethostbyname gethostbyname_dot +#endif + +/* sock_host - look up endpoint addresses and install conversion methods */ + +void sock_host(request) +struct request_info *request; +{ + static struct sockaddr_in client; + static struct sockaddr_in server; + int len; + char buf[BUFSIZ]; + int fd = request->fd; + + sock_methods(request); + + /* + * Look up the client host address. Hal R. Brand <BRAND@addvax.llnl.gov> + * suggested how to get the client host info in case of UDP connections: + * peek at the first message without actually looking at its contents. We + * really should verify that client.sin_family gets the value AF_INET, + * but this program has already caused too much grief on systems with + * broken library code. + */ + + len = sizeof(client); + if (getpeername(fd, (struct sockaddr *) & client, &len) < 0) { + request->sink = sock_sink; + len = sizeof(client); + if (recvfrom(fd, buf, sizeof(buf), MSG_PEEK, + (struct sockaddr *) & client, &len) < 0) { + tcpd_warn("can't get client address: %m"); + return; /* give up */ + } +#ifdef really_paranoid + memset(buf, 0 sizeof(buf)); +#endif + } + request->client->sin = &client; + + /* + * Determine the server binding. This is used for client username + * lookups, and for access control rules that trigger on the server + * address or name. + */ + + len = sizeof(server); + if (getsockname(fd, (struct sockaddr *) & server, &len) < 0) { + tcpd_warn("getsockname: %m"); + return; + } + request->server->sin = &server; +} + +/* sock_hostaddr - map endpoint address to printable form */ + +void sock_hostaddr(host) +struct host_info *host; +{ + struct sockaddr_in *sin = host->sin; + + if (sin != 0) + STRN_CPY(host->addr, inet_ntoa(sin->sin_addr), sizeof(host->addr)); +} + +/* sock_hostname - map endpoint address to host name */ + +void sock_hostname(host) +struct host_info *host; +{ + struct sockaddr_in *sin = host->sin; + struct hostent *hp; + int i; + + /* + * On some systems, for example Solaris 2.3, gethostbyaddr(0.0.0.0) does + * not fail. Instead it returns "INADDR_ANY". Unfortunately, this does + * not work the other way around: gethostbyname("INADDR_ANY") fails. We + * have to special-case 0.0.0.0, in order to avoid false alerts from the + * host name/address checking code below. + */ + if (sin != 0 && sin->sin_addr.s_addr != 0 + && (hp = gethostbyaddr((char *) &(sin->sin_addr), + sizeof(sin->sin_addr), AF_INET)) != 0) { + + STRN_CPY(host->name, hp->h_name, sizeof(host->name)); + + /* + * Verify that the address is a member of the address list returned + * by gethostbyname(hostname). + * + * Verify also that gethostbyaddr() and gethostbyname() return the same + * hostname, or rshd and rlogind may still end up being spoofed. + * + * On some sites, gethostbyname("localhost") returns "localhost.domain". + * This is a DNS artefact. We treat it as a special case. When we + * can't believe the address list from gethostbyname("localhost") + * we're in big trouble anyway. + */ + + if ((hp = gethostbyname(host->name)) == 0) { + + /* + * Unable to verify that the host name matches the address. This + * may be a transient problem or a botched name server setup. + */ + + tcpd_warn("can't verify hostname: gethostbyname(%s) failed", + host->name); + + } else if (STR_NE(host->name, hp->h_name) + && STR_NE(host->name, "localhost")) { + + /* + * The gethostbyaddr() and gethostbyname() calls did not return + * the same hostname. This could be a nameserver configuration + * problem. It could also be that someone is trying to spoof us. + */ + + tcpd_warn("host name/name mismatch: %s != %.*s", + host->name, STRING_LENGTH, hp->h_name); + + } else { + + /* + * The address should be a member of the address list returned by + * gethostbyname(). We should first verify that the h_addrtype + * field is AF_INET, but this program has already caused too much + * grief on systems with broken library code. + */ + + for (i = 0; hp->h_addr_list[i]; i++) { + if (memcmp(hp->h_addr_list[i], + (char *) &sin->sin_addr, + sizeof(sin->sin_addr)) == 0) + return; /* name is good, keep it */ + } + + /* + * The host name does not map to the initial address. Perhaps + * someone has messed up. Perhaps someone compromised a name + * server. + */ + + tcpd_warn("host name/address mismatch: %s != %.*s", + inet_ntoa(sin->sin_addr), STRING_LENGTH, hp->h_name); + } + strcpy(host->name, paranoid); /* name is bad, clobber it */ + } +} + +/* sock_sink - absorb unreceived IP datagram */ + +static void sock_sink(fd) +int fd; +{ + char buf[BUFSIZ]; + struct sockaddr_in sin; + int size = sizeof(sin); + + /* + * Eat up the not-yet received datagram. Some systems insist on a + * non-zero source address argument in the recvfrom() call below. + */ + + (void) recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) & sin, &size); +} diff --git a/contrib/tcp_wrappers/strcasecmp.c b/contrib/tcp_wrappers/strcasecmp.c new file mode 100644 index 000000000000..a54e82816171 --- /dev/null +++ b/contrib/tcp_wrappers/strcasecmp.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 1987 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)strcasecmp.c 5.6 (Berkeley) 6/27/88"; +#endif /* LIBC_SCCS and not lint */ + +/* Some environments don't define u_char -- WZV */ +#if 0 +#include <sys/types.h> +#else +typedef unsigned char u_char; +#endif + +/* + * This array is designed for mapping upper and lower case letter + * together for a case independent comparison. The mappings are + * based upon ascii character sequences. + */ +static u_char charmap[] = { + '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007', + '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017', + '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027', + '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037', + '\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047', + '\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057', + '\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067', + '\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077', + '\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147', + '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157', + '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167', + '\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137', + '\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147', + '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157', + '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167', + '\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177', + '\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207', + '\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217', + '\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227', + '\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237', + '\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247', + '\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257', + '\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267', + '\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277', + '\300', '\341', '\342', '\343', '\344', '\345', '\346', '\347', + '\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357', + '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367', + '\370', '\371', '\372', '\333', '\334', '\335', '\336', '\337', + '\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347', + '\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357', + '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367', + '\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377', +}; + +strcasecmp(s1, s2) + char *s1, *s2; +{ + register u_char *cm = charmap, + *us1 = (u_char *)s1, + *us2 = (u_char *)s2; + + while (cm[*us1] == cm[*us2++]) + if (*us1++ == '\0') + return(0); + return(cm[*us1] - cm[*--us2]); +} + +strncasecmp(s1, s2, n) + char *s1, *s2; + register int n; +{ + register u_char *cm = charmap, + *us1 = (u_char *)s1, + *us2 = (u_char *)s2; + + while (--n >= 0 && cm[*us1] == cm[*us2++]) + if (*us1++ == '\0') + return(0); + return(n < 0 ? 0 : cm[*us1] - cm[*--us2]); +} diff --git a/contrib/tcp_wrappers/tcpd.8 b/contrib/tcp_wrappers/tcpd.8 new file mode 100644 index 000000000000..351390714be1 --- /dev/null +++ b/contrib/tcp_wrappers/tcpd.8 @@ -0,0 +1,178 @@ +.TH TCPD 8 +.SH NAME +tcpd \- access control facility for internet services +.SH DESCRIPTION +.PP +The \fItcpd\fR program can be set up to monitor incoming requests for +\fItelnet\fR, \fIfinger\fR, \fIftp\fR, \fIexec\fR, \fIrsh\fR, +\fIrlogin\fR, \fItftp\fR, \fItalk\fR, \fIcomsat\fR and other services +that have a one-to-one mapping onto executable files. +.PP +The program supports both 4.3BSD-style sockets and System V.4-style +TLI. Functionality may be limited when the protocol underneath TLI is +not an internet protocol. +.PP +Operation is as follows: whenever a request for service arrives, the +\fIinetd\fP daemon is tricked into running the \fItcpd\fP program +instead of the desired server. \fItcpd\fP logs the request and does +some additional checks. When all is well, \fItcpd\fP runs the +appropriate server program and goes away. +.PP +Optional features are: pattern-based access control, client username +lookups with the RFC 931 etc. protocol, protection against hosts that +pretend to have someone elses host name, and protection against hosts +that pretend to have someone elses network address. +.SH LOGGING +Connections that are monitored by +.I tcpd +are reported through the \fIsyslog\fR(3) facility. Each record contains +a time stamp, the client host name and the name of the requested +service. The information can be useful to detect unwanted activities, +especially when logfile information from several hosts is merged. +.PP +In order to find out where your logs are going, examine the syslog +configuration file, usually /etc/syslog.conf. +.SH ACCESS CONTROL +Optionally, +.I tcpd +supports a simple form of access control that is based on pattern +matching. The access-control software provides hooks for the execution +of shell commands when a pattern fires. For details, see the +\fIhosts_access\fR(5) manual page. +.SH HOST NAME VERIFICATION +The authentication scheme of some protocols (\fIrlogin, rsh\fR) relies +on host names. Some implementations believe the host name that they get +from any random name server; other implementations are more careful but +use a flawed algorithm. +.PP +.I tcpd +verifies the client host name that is returned by the address->name DNS +server by looking at the host name and address that are returned by the +name->address DNS server. If any discrepancy is detected, +.I tcpd +concludes that it is dealing with a host that pretends to have someone +elses host name. +.PP +If the sources are compiled with -DPARANOID, +.I tcpd +will drop the connection in case of a host name/address mismatch. +Otherwise, the hostname can be matched with the \fIPARANOID\fR wildcard, +after which suitable action can be taken. +.SH HOST ADDRESS SPOOFING +Optionally, +.I tcpd +disables source-routing socket options on every connection that it +deals with. This will take care of most attacks from hosts that pretend +to have an address that belongs to someone elses network. UDP services +do not benefit from this protection. This feature must be turned on +at compile time. +.SH RFC 931 +When RFC 931 etc. lookups are enabled (compile-time option) \fItcpd\fR +will attempt to establish the name of the client user. This will +succeed only if the client host runs an RFC 931-compliant daemon. +Client user name lookups will not work for datagram-oriented +connections, and may cause noticeable delays in the case of connections +from PCs. +.SH EXAMPLES +The details of using \fItcpd\fR depend on pathname information that was +compiled into the program. +.SH EXAMPLE 1 +This example applies when \fItcpd\fR expects that the original network +daemons will be moved to an "other" place. +.PP +In order to monitor access to the \fIfinger\fR service, move the +original finger daemon to the "other" place and install tcpd in the +place of the original finger daemon. No changes are required to +configuration files. +.nf +.sp +.in +5 +# mkdir /other/place +# mv /usr/etc/in.fingerd /other/place +# cp tcpd /usr/etc/in.fingerd +.fi +.PP +The example assumes that the network daemons live in /usr/etc. On some +systems, network daemons live in /usr/sbin or in /usr/libexec, or have +no `in.\' prefix to their name. +.SH EXAMPLE 2 +This example applies when \fItcpd\fR expects that the network daemons +are left in their original place. +.PP +In order to monitor access to the \fIfinger\fR service, perform the +following edits on the \fIinetd\fR configuration file (usually +\fI/etc/inetd.conf\fR or \fI/etc/inet/inetd.conf\fR): +.nf +.sp +.ti +5 +finger stream tcp nowait nobody /usr/etc/in.fingerd in.fingerd +.sp +becomes: +.sp +.ti +5 +finger stream tcp nowait nobody /some/where/tcpd in.fingerd +.sp +.fi +.PP +The example assumes that the network daemons live in /usr/etc. On some +systems, network daemons live in /usr/sbin or in /usr/libexec, the +daemons have no `in.\' prefix to their name, or there is no userid +field in the inetd configuration file. +.PP +Similar changes will be needed for the other services that are to be +covered by \fItcpd\fR. Send a `kill -HUP\' to the \fIinetd\fR(8) +process to make the changes effective. AIX users may also have to +execute the `inetimp\' command. +.SH EXAMPLE 3 +In the case of daemons that do not live in a common directory ("secret" +or otherwise), edit the \fIinetd\fR configuration file so that it +specifies an absolute path name for the process name field. For example: +.nf +.sp + ntalk dgram udp wait root /some/where/tcpd /usr/local/lib/ntalkd +.sp +.fi +.PP +Only the last component (ntalkd) of the pathname will be used for +access control and logging. +.SH BUGS +Some UDP (and RPC) daemons linger around for a while after they have +finished their work, in case another request comes in. In the inetd +configuration file these services are registered with the \fIwait\fR +option. Only the request that started such a daemon will be logged. +.PP +The program does not work with RPC services over TCP. These services +are registered as \fIrpc/tcp\fR in the inetd configuration file. The +only non-trivial service that is affected by this limitation is +\fIrexd\fR, which is used by the \fIon(1)\fR command. This is no great +loss. On most systems, \fIrexd\fR is less secure than a wildcard in +/etc/hosts.equiv. +.PP +RPC broadcast requests (for example: \fIrwall, rup, rusers\fR) always +appear to come from the responding host. What happens is that the +client broadcasts the request to all \fIportmap\fR daemons on its +network; each \fIportmap\fR daemon forwards the request to a local +daemon. As far as the \fIrwall\fR etc. daemons know, the request comes +from the local host. +.SH FILES +.PP +The default locations of the host access control tables are: +.PP +/etc/hosts.allow +.br +/etc/hosts.deny +.SH SEE ALSO +.na +.nf +hosts_access(5), format of the tcpd access control tables. +syslog.conf(5), format of the syslogd control file. +inetd.conf(5), format of the inetd control file. +.SH AUTHORS +.na +.nf +Wietse Venema (wietse@wzv.win.tue.nl), +Department of Mathematics and Computing Science, +Eindhoven University of Technology +Den Dolech 2, P.O. Box 513, +5600 MB Eindhoven, The Netherlands +\" @(#) tcpd.8 1.5 96/02/21 16:39:16 diff --git a/contrib/tcp_wrappers/tcpd.c b/contrib/tcp_wrappers/tcpd.c new file mode 100644 index 000000000000..d865b9cce8eb --- /dev/null +++ b/contrib/tcp_wrappers/tcpd.c @@ -0,0 +1,129 @@ + /* + * General front end for stream and datagram IP services. This program logs + * the remote host name and then invokes the real daemon. For example, + * install as /usr/etc/{tftpd,fingerd,telnetd,ftpd,rlogind,rshd,rexecd}, + * after saving the real daemons in the directory specified with the + * REAL_DAEMON_DIR macro. This arrangement requires that the network daemons + * are started by inetd or something similar. Connections and diagnostics + * are logged through syslog(3). + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) tcpd.c 1.10 96/02/11 17:01:32"; +#endif + +/* System libraries. */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <stdio.h> +#include <syslog.h> +#include <string.h> + +#ifndef MAXPATHNAMELEN +#define MAXPATHNAMELEN BUFSIZ +#endif + +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif + +/* Local stuff. */ + +#include "patchlevel.h" +#include "tcpd.h" + +int allow_severity = SEVERITY; /* run-time adjustable */ +int deny_severity = LOG_WARNING; /* ditto */ + +main(argc, argv) +int argc; +char **argv; +{ + struct request_info request; + char path[MAXPATHNAMELEN]; + + /* Attempt to prevent the creation of world-writable files. */ + +#ifdef DAEMON_UMASK + umask(DAEMON_UMASK); +#endif + + /* + * If argv[0] is an absolute path name, ignore REAL_DAEMON_DIR, and strip + * argv[0] to its basename. + */ + + if (argv[0][0] == '/') { + strcpy(path, argv[0]); + argv[0] = strrchr(argv[0], '/') + 1; + } else { + sprintf(path, "%s/%s", REAL_DAEMON_DIR, argv[0]); + } + + /* + * Open a channel to the syslog daemon. Older versions of openlog() + * require only two arguments. + */ + +#ifdef LOG_MAIL + (void) openlog(argv[0], LOG_PID, FACILITY); +#else + (void) openlog(argv[0], LOG_PID); +#endif + + /* + * Find out the endpoint addresses of this conversation. Host name + * lookups and double checks will be done on demand. + */ + + request_init(&request, RQ_DAEMON, argv[0], RQ_FILE, STDIN_FILENO, 0); + fromhost(&request); + + /* + * Optionally look up and double check the remote host name. Sites + * concerned with security may choose to refuse connections from hosts + * that pretend to have someone elses host name. + */ + +#ifdef PARANOID + if (STR_EQ(eval_hostname(request.client), paranoid)) + refuse(&request); +#endif + + /* + * The BSD rlogin and rsh daemons that came out after 4.3 BSD disallow + * socket options at the IP level. They do so for a good reason. + * Unfortunately, we cannot use this with SunOS 4.1.x because the + * getsockopt() system call can panic the system. + */ + +#ifdef KILL_IP_OPTIONS + fix_options(&request); +#endif + + /* + * Check whether this host can access the service in argv[0]. The + * access-control code invokes optional shell commands as specified in + * the access-control tables. + */ + +#ifdef HOSTS_ACCESS + if (!hosts_access(&request)) + refuse(&request); +#endif + + /* Report request and invoke the real daemon program. */ + + syslog(allow_severity, "connect from %s", eval_client(&request)); + closelog(); + (void) execv(path, argv); + syslog(LOG_ERR, "error: cannot execute %s: %m", path); + clean_exit(&request); + /* NOTREACHED */ +} diff --git a/contrib/tcp_wrappers/tcpd.h b/contrib/tcp_wrappers/tcpd.h new file mode 100644 index 000000000000..3eecc9115997 --- /dev/null +++ b/contrib/tcp_wrappers/tcpd.h @@ -0,0 +1,219 @@ + /* + * @(#) tcpd.h 1.5 96/03/19 16:22:24 + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +/* Structure to describe one communications endpoint. */ + +#define STRING_LENGTH 128 /* hosts, users, processes */ + +struct host_info { + char name[STRING_LENGTH]; /* access via eval_hostname(host) */ + char addr[STRING_LENGTH]; /* access via eval_hostaddr(host) */ + struct sockaddr_in *sin; /* socket address or 0 */ + struct t_unitdata *unit; /* TLI transport address or 0 */ + struct request_info *request; /* for shared information */ +}; + +/* Structure to describe what we know about a service request. */ + +struct request_info { + int fd; /* socket handle */ + char user[STRING_LENGTH]; /* access via eval_user(request) */ + char daemon[STRING_LENGTH]; /* access via eval_daemon(request) */ + char pid[10]; /* access via eval_pid(request) */ + struct host_info client[1]; /* client endpoint info */ + struct host_info server[1]; /* server endpoint info */ + void (*sink) (); /* datagram sink function or 0 */ + void (*hostname) (); /* address to printable hostname */ + void (*hostaddr) (); /* address to printable address */ + void (*cleanup) (); /* cleanup function or 0 */ + struct netconfig *config; /* netdir handle */ +}; + +/* Common string operations. Less clutter should be more readable. */ + +#define STRN_CPY(d,s,l) { strncpy((d),(s),(l)); (d)[(l)-1] = 0; } + +#define STRN_EQ(x,y,l) (strncasecmp((x),(y),(l)) == 0) +#define STRN_NE(x,y,l) (strncasecmp((x),(y),(l)) != 0) +#define STR_EQ(x,y) (strcasecmp((x),(y)) == 0) +#define STR_NE(x,y) (strcasecmp((x),(y)) != 0) + + /* + * Initially, all above strings have the empty value. Information that + * cannot be determined at runtime is set to "unknown", so that we can + * distinguish between `unavailable' and `not yet looked up'. A hostname + * that we do not believe in is set to "paranoid". + */ + +#define STRING_UNKNOWN "unknown" /* lookup failed */ +#define STRING_PARANOID "paranoid" /* hostname conflict */ + +extern char unknown[]; +extern char paranoid[]; + +#define HOSTNAME_KNOWN(s) (STR_NE((s),unknown) && STR_NE((s),paranoid)) + +#define NOT_INADDR(s) (s[strspn(s,"01234567890./")] != 0) + +/* Global functions. */ + +#if defined(TLI) || defined(PTX) || defined(TLI_SEQUENT) +extern void fromhost(); /* get/validate client host info */ +#else +#define fromhost sock_host /* no TLI support needed */ +#endif + +extern int hosts_access(); /* access control */ +extern void shell_cmd(); /* execute shell command */ +extern char *percent_x(); /* do %<char> expansion */ +extern void rfc931(); /* client name from RFC 931 daemon */ +extern void clean_exit(); /* clean up and exit */ +extern void refuse(); /* clean up and exit */ +extern char *xgets(); /* fgets() on steroids */ +extern char *split_at(); /* strchr() and split */ +extern unsigned long dot_quad_addr(); /* restricted inet_addr() */ + +/* Global variables. */ + +extern int allow_severity; /* for connection logging */ +extern int deny_severity; /* for connection logging */ +extern char *hosts_allow_table; /* for verification mode redirection */ +extern char *hosts_deny_table; /* for verification mode redirection */ +extern int hosts_access_verbose; /* for verbose matching mode */ +extern int rfc931_timeout; /* user lookup timeout */ +extern int resident; /* > 0 if resident process */ + + /* + * Routines for controlled initialization and update of request structure + * attributes. Each attribute has its own key. + */ + +#ifdef __STDC__ +extern struct request_info *request_init(struct request_info *,...); +extern struct request_info *request_set(struct request_info *,...); +#else +extern struct request_info *request_init(); /* initialize request */ +extern struct request_info *request_set(); /* update request structure */ +#endif + +#define RQ_FILE 1 /* file descriptor */ +#define RQ_DAEMON 2 /* server process (argv[0]) */ +#define RQ_USER 3 /* client user name */ +#define RQ_CLIENT_NAME 4 /* client host name */ +#define RQ_CLIENT_ADDR 5 /* client host address */ +#define RQ_CLIENT_SIN 6 /* client endpoint (internal) */ +#define RQ_SERVER_NAME 7 /* server host name */ +#define RQ_SERVER_ADDR 8 /* server host address */ +#define RQ_SERVER_SIN 9 /* server endpoint (internal) */ + + /* + * Routines for delayed evaluation of request attributes. Each attribute + * type has its own access method. The trivial ones are implemented by + * macros. The other ones are wrappers around the transport-specific host + * name, address, and client user lookup methods. The request_info and + * host_info structures serve as caches for the lookup results. + */ + +extern char *eval_user(); /* client user */ +extern char *eval_hostname(); /* printable hostname */ +extern char *eval_hostaddr(); /* printable host address */ +extern char *eval_hostinfo(); /* host name or address */ +extern char *eval_client(); /* whatever is available */ +extern char *eval_server(); /* whatever is available */ +#define eval_daemon(r) ((r)->daemon) /* daemon process name */ +#define eval_pid(r) ((r)->pid) /* process id */ + +/* Socket-specific methods, including DNS hostname lookups. */ + +extern void sock_host(); /* look up endpoint addresses */ +extern void sock_hostname(); /* translate address to hostname */ +extern void sock_hostaddr(); /* address to printable address */ +#define sock_methods(r) \ + { (r)->hostname = sock_hostname; (r)->hostaddr = sock_hostaddr; } + +/* The System V Transport-Level Interface (TLI) interface. */ + +#if defined(TLI) || defined(PTX) || defined(TLI_SEQUENT) +extern void tli_host(); /* look up endpoint addresses etc. */ +#endif + + /* + * Problem reporting interface. Additional file/line context is reported + * when available. The jump buffer (tcpd_buf) is not declared here, or + * everyone would have to include <setjmp.h>. + */ + +#ifdef __STDC__ +extern void tcpd_warn(char *, ...); /* report problem and proceed */ +extern void tcpd_jump(char *, ...); /* report problem and jump */ +#else +extern void tcpd_warn(); +extern void tcpd_jump(); +#endif + +struct tcpd_context { + char *file; /* current file */ + int line; /* current line */ +}; +extern struct tcpd_context tcpd_context; + + /* + * While processing access control rules, error conditions are handled by + * jumping back into the hosts_access() routine. This is cleaner than + * checking the return value of each and every silly little function. The + * (-1) returns are here because zero is already taken by longjmp(). + */ + +#define AC_PERMIT 1 /* permit access */ +#define AC_DENY (-1) /* deny_access */ +#define AC_ERROR AC_DENY /* XXX */ + + /* + * In verification mode an option function should just say what it would do, + * instead of really doing it. An option function that would not return + * should clear the dry_run flag to inform the caller of this unusual + * behavior. + */ + +extern void process_options(); /* execute options */ +extern int dry_run; /* verification flag */ + +/* Bug workarounds. */ + +#ifdef INET_ADDR_BUG /* inet_addr() returns struct */ +#define inet_addr fix_inet_addr +extern long fix_inet_addr(); +#endif + +#ifdef BROKEN_FGETS /* partial reads from sockets */ +#define fgets fix_fgets +extern char *fix_fgets(); +#endif + +#ifdef RECVFROM_BUG /* no address family info */ +#define recvfrom fix_recvfrom +extern int fix_recvfrom(); +#endif + +#ifdef GETPEERNAME_BUG /* claims success with UDP */ +#define getpeername fix_getpeername +extern int fix_getpeername(); +#endif + +#ifdef SOLARIS_24_GETHOSTBYNAME_BUG /* lists addresses as aliases */ +#define gethostbyname fix_gethostbyname +extern struct hostent *fix_gethostbyname(); +#endif + +#ifdef USE_STRSEP /* libc calls strtok() */ +#define strtok fix_strtok +extern char *fix_strtok(); +#endif + +#ifdef LIBC_CALLS_STRTOK /* libc calls strtok() */ +#define strtok my_strtok +extern char *my_strtok(); +#endif diff --git a/contrib/tcp_wrappers/tcpdchk.8 b/contrib/tcp_wrappers/tcpdchk.8 new file mode 100644 index 000000000000..acc65e6441ad --- /dev/null +++ b/contrib/tcp_wrappers/tcpdchk.8 @@ -0,0 +1,66 @@ +.TH TCPDCHK 8 +.SH NAME +tcpdchk \- tcp wrapper configuration checker +.SH SYNOPSYS +tcpdchk [-a] [-d] [-i inet_conf] [-v] +.SH DESCRIPTION +.PP +\fItcpdchk\fR examines your tcp wrapper configuration and reports all +potential and real problems it can find. The program examines the +\fItcpd\fR access control files (by default, these are +\fI/etc/hosts.allow\fR and \fI/etc/hosts.deny\fR), and compares the +entries in these files against entries in the \fIinetd\fR or \fItlid\fR +network configuration files. +.PP +\fItcpdchk\fR reports problems such as non-existent pathnames; services +that appear in \fItcpd\fR access control rules, but are not controlled +by \fItcpd\fR; services that should not be wrapped; non-existent host +names or non-internet address forms; occurrences of host aliases +instead of official host names; hosts with a name/address conflict; +inappropriate use of wildcard patterns; inappropriate use of NIS +netgroups or references to non-existent NIS netgroups; references to +non-existent options; invalid arguments to options; and so on. +.PP +Where possible, \fItcpdchk\fR provides a helpful suggestion to fix the +problem. +.SH OPTIONS +.IP -a +Report access control rules that permit access without an explicit +ALLOW keyword. This applies only when the extended access control +language is enabled (build with -DPROCESS_OPTIONS). +.IP -d +Examine \fIhosts.allow\fR and \fIhosts.deny\fR files in the current +directory instead of the default ones. +.IP "-i inet_conf" +Specify this option when \fItcpdchk\fR is unable to find your +\fIinetd.conf\fR or \fItlid.conf\fR network configuration file, or when +you suspect that the program uses the wrong one. +.IP -v +Display the contents of each access control rule. Daemon lists, client +lists, shell commands and options are shown in a pretty-printed format; +this makes it easier for you to spot any discrepancies between what you +want and what the program understands. +.SH FILES +.PP +The default locations of the \fItcpd\fR access control tables are: +.PP +/etc/hosts.allow +.br +/etc/hosts.deny +.SH SEE ALSO +.na +.nf +tcpdmatch(8), explain what tcpd would do in specific cases. +hosts_access(5), format of the tcpd access control tables. +hosts_options(5), format of the language extensions. +inetd.conf(5), format of the inetd control file. +tlid.conf(5), format of the tlid control file. +.SH AUTHORS +.na +.nf +Wietse Venema (wietse@wzv.win.tue.nl), +Department of Mathematics and Computing Science, +Eindhoven University of Technology +Den Dolech 2, P.O. Box 513, +5600 MB Eindhoven, The Netherlands +\" @(#) tcpdchk.8 1.3 95/01/08 17:00:30 diff --git a/contrib/tcp_wrappers/tcpdchk.c b/contrib/tcp_wrappers/tcpdchk.c new file mode 100644 index 000000000000..49c5c82c1b44 --- /dev/null +++ b/contrib/tcp_wrappers/tcpdchk.c @@ -0,0 +1,462 @@ + /* + * tcpdchk - examine all tcpd access control rules and inetd.conf entries + * + * Usage: tcpdchk [-a] [-d] [-i inet_conf] [-v] + * + * -a: complain about implicit "allow" at end of rule. + * + * -d: rules in current directory. + * + * -i: location of inetd.conf file. + * + * -v: show all rules. + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) tcpdchk.c 1.8 97/02/12 02:13:25"; +#endif + +/* System libraries. */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <stdio.h> +#include <syslog.h> +#include <setjmp.h> +#include <errno.h> +#include <netdb.h> +#include <string.h> + +extern int errno; +extern void exit(); +extern int optind; +extern char *optarg; + +#ifndef INADDR_NONE +#define INADDR_NONE (-1) /* XXX should be 0xffffffff */ +#endif + +#ifndef S_ISDIR +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif + +/* Application-specific. */ + +#include "tcpd.h" +#include "inetcf.h" +#include "scaffold.h" + + /* + * Stolen from hosts_access.c... + */ +static char sep[] = ", \t\n"; + +#define BUFLEN 2048 + +int resident = 0; +int hosts_access_verbose = 0; +char *hosts_allow_table = HOSTS_ALLOW; +char *hosts_deny_table = HOSTS_DENY; +extern jmp_buf tcpd_buf; + + /* + * Local stuff. + */ +static void usage(); +static void parse_table(); +static void print_list(); +static void check_daemon_list(); +static void check_client_list(); +static void check_daemon(); +static void check_user(); +static int check_host(); +static int reserved_name(); + +#define PERMIT 1 +#define DENY 0 + +#define YES 1 +#define NO 0 + +static int defl_verdict; +static char *myname; +static int allow_check; +static char *inetcf; + +int main(argc, argv) +int argc; +char **argv; +{ + struct request_info request; + struct stat st; + int c; + + myname = argv[0]; + + /* + * Parse the JCL. + */ + while ((c = getopt(argc, argv, "adi:v")) != EOF) { + switch (c) { + case 'a': + allow_check = 1; + break; + case 'd': + hosts_allow_table = "hosts.allow"; + hosts_deny_table = "hosts.deny"; + break; + case 'i': + inetcf = optarg; + break; + case 'v': + hosts_access_verbose++; + break; + default: + usage(); + /* NOTREACHED */ + } + } + if (argc != optind) + usage(); + + /* + * When confusion really strikes... + */ + if (check_path(REAL_DAEMON_DIR, &st) < 0) { + tcpd_warn("REAL_DAEMON_DIR %s: %m", REAL_DAEMON_DIR); + } else if (!S_ISDIR(st.st_mode)) { + tcpd_warn("REAL_DAEMON_DIR %s is not a directory", REAL_DAEMON_DIR); + } + + /* + * Process the inet configuration file (or its moral equivalent). This + * information is used later to find references in hosts.allow/deny to + * unwrapped services, and other possible problems. + */ + inetcf = inet_cfg(inetcf); + if (hosts_access_verbose) + printf("Using network configuration file: %s\n", inetcf); + + /* + * These are not run from inetd but may have built-in access control. + */ + inet_set("portmap", WR_NOT); + inet_set("rpcbind", WR_NOT); + + /* + * Check accessibility of access control files. + */ + (void) check_path(hosts_allow_table, &st); + (void) check_path(hosts_deny_table, &st); + + /* + * Fake up an arbitrary service request. + */ + request_init(&request, + RQ_DAEMON, "daemon_name", + RQ_SERVER_NAME, "server_hostname", + RQ_SERVER_ADDR, "server_addr", + RQ_USER, "user_name", + RQ_CLIENT_NAME, "client_hostname", + RQ_CLIENT_ADDR, "client_addr", + RQ_FILE, 1, + 0); + + /* + * Examine all access-control rules. + */ + defl_verdict = PERMIT; + parse_table(hosts_allow_table, &request); + defl_verdict = DENY; + parse_table(hosts_deny_table, &request); + return (0); +} + +/* usage - explain */ + +static void usage() +{ + fprintf(stderr, "usage: %s [-a] [-d] [-i inet_conf] [-v]\n", myname); + fprintf(stderr, " -a: report rules with implicit \"ALLOW\" at end\n"); + fprintf(stderr, " -d: use allow/deny files in current directory\n"); + fprintf(stderr, " -i: location of inetd.conf file\n"); + fprintf(stderr, " -v: list all rules\n"); + exit(1); +} + +/* parse_table - like table_match(), but examines _all_ entries */ + +static void parse_table(table, request) +char *table; +struct request_info *request; +{ + FILE *fp; + int real_verdict; + char sv_list[BUFLEN]; /* becomes list of daemons */ + char *cl_list; /* becomes list of requests */ + char *sh_cmd; /* becomes optional shell command */ + char buf[BUFSIZ]; + int verdict; + struct tcpd_context saved_context; + + saved_context = tcpd_context; /* stupid compilers */ + + if (fp = fopen(table, "r")) { + tcpd_context.file = table; + tcpd_context.line = 0; + while (xgets(sv_list, sizeof(sv_list), fp)) { + if (sv_list[strlen(sv_list) - 1] != '\n') { + tcpd_warn("missing newline or line too long"); + continue; + } + if (sv_list[0] == '#' || sv_list[strspn(sv_list, " \t\r\n")] == 0) + continue; + if ((cl_list = split_at(sv_list, ':')) == 0) { + tcpd_warn("missing \":\" separator"); + continue; + } + sh_cmd = split_at(cl_list, ':'); + + if (hosts_access_verbose) + printf("\n>>> Rule %s line %d:\n", + tcpd_context.file, tcpd_context.line); + + if (hosts_access_verbose) + print_list("daemons: ", sv_list); + check_daemon_list(sv_list); + + if (hosts_access_verbose) + print_list("clients: ", cl_list); + check_client_list(cl_list); + +#ifdef PROCESS_OPTIONS + real_verdict = defl_verdict; + if (sh_cmd) { + verdict = setjmp(tcpd_buf); + if (verdict != 0) { + real_verdict = (verdict == AC_PERMIT); + } else { + dry_run = 1; + process_options(sh_cmd, request); + if (dry_run == 1 && real_verdict && allow_check) + tcpd_warn("implicit \"allow\" at end of rule"); + } + } else if (defl_verdict && allow_check) { + tcpd_warn("implicit \"allow\" at end of rule"); + } + if (hosts_access_verbose) + printf("access: %s\n", real_verdict ? "granted" : "denied"); +#else + if (sh_cmd) + shell_cmd(percent_x(buf, sizeof(buf), sh_cmd, request)); + if (hosts_access_verbose) + printf("access: %s\n", defl_verdict ? "granted" : "denied"); +#endif + } + (void) fclose(fp); + } else if (errno != ENOENT) { + tcpd_warn("cannot open %s: %m", table); + } + tcpd_context = saved_context; +} + +/* print_list - pretty-print a list */ + +static void print_list(title, list) +char *title; +char *list; +{ + char buf[BUFLEN]; + char *cp; + char *next; + + fputs(title, stdout); + strcpy(buf, list); + + for (cp = strtok(buf, sep); cp != 0; cp = next) { + fputs(cp, stdout); + next = strtok((char *) 0, sep); + if (next != 0) + fputs(" ", stdout); + } + fputs("\n", stdout); +} + +/* check_daemon_list - criticize daemon list */ + +static void check_daemon_list(list) +char *list; +{ + char buf[BUFLEN]; + char *cp; + char *host; + int daemons = 0; + + strcpy(buf, list); + + for (cp = strtok(buf, sep); cp != 0; cp = strtok((char *) 0, sep)) { + if (STR_EQ(cp, "EXCEPT")) { + daemons = 0; + } else { + daemons++; + if ((host = split_at(cp + 1, '@')) != 0 && check_host(host) > 1) { + tcpd_warn("host %s has more than one address", host); + tcpd_warn("(consider using an address instead)"); + } + check_daemon(cp); + } + } + if (daemons == 0) + tcpd_warn("daemon list is empty or ends in EXCEPT"); +} + +/* check_client_list - criticize client list */ + +static void check_client_list(list) +char *list; +{ + char buf[BUFLEN]; + char *cp; + char *host; + int clients = 0; + + strcpy(buf, list); + + for (cp = strtok(buf, sep); cp != 0; cp = strtok((char *) 0, sep)) { + if (STR_EQ(cp, "EXCEPT")) { + clients = 0; + } else { + clients++; + if (host = split_at(cp + 1, '@')) { /* user@host */ + check_user(cp); + check_host(host); + } else { + check_host(cp); + } + } + } + if (clients == 0) + tcpd_warn("client list is empty or ends in EXCEPT"); +} + +/* check_daemon - criticize daemon pattern */ + +static void check_daemon(pat) +char *pat; +{ + if (pat[0] == '@') { + tcpd_warn("%s: daemon name begins with \"@\"", pat); + } else if (pat[0] == '.') { + tcpd_warn("%s: daemon name begins with dot", pat); + } else if (pat[strlen(pat) - 1] == '.') { + tcpd_warn("%s: daemon name ends in dot", pat); + } else if (STR_EQ(pat, "ALL") || STR_EQ(pat, unknown)) { + /* void */ ; + } else if (STR_EQ(pat, "FAIL")) { /* obsolete */ + tcpd_warn("FAIL is no longer recognized"); + tcpd_warn("(use EXCEPT or DENY instead)"); + } else if (reserved_name(pat)) { + tcpd_warn("%s: daemon name may be reserved word", pat); + } else { + switch (inet_get(pat)) { + case WR_UNKNOWN: + tcpd_warn("%s: no such process name in %s", pat, inetcf); + inet_set(pat, WR_YES); /* shut up next time */ + break; + case WR_NOT: + tcpd_warn("%s: service possibly not wrapped", pat); + inet_set(pat, WR_YES); + break; + } + } +} + +/* check_user - criticize user pattern */ + +static void check_user(pat) +char *pat; +{ + if (pat[0] == '@') { /* @netgroup */ + tcpd_warn("%s: user name begins with \"@\"", pat); + } else if (pat[0] == '.') { + tcpd_warn("%s: user name begins with dot", pat); + } else if (pat[strlen(pat) - 1] == '.') { + tcpd_warn("%s: user name ends in dot", pat); + } else if (STR_EQ(pat, "ALL") || STR_EQ(pat, unknown) + || STR_EQ(pat, "KNOWN")) { + /* void */ ; + } else if (STR_EQ(pat, "FAIL")) { /* obsolete */ + tcpd_warn("FAIL is no longer recognized"); + tcpd_warn("(use EXCEPT or DENY instead)"); + } else if (reserved_name(pat)) { + tcpd_warn("%s: user name may be reserved word", pat); + } +} + +/* check_host - criticize host pattern */ + +static int check_host(pat) +char *pat; +{ + char *mask; + int addr_count = 1; + + if (pat[0] == '@') { /* @netgroup */ +#ifdef NO_NETGRENT + /* SCO has no *netgrent() support */ +#else +#ifdef NETGROUP + char *machinep; + char *userp; + char *domainp; + + setnetgrent(pat + 1); + if (getnetgrent(&machinep, &userp, &domainp) == 0) + tcpd_warn("%s: unknown or empty netgroup", pat + 1); + endnetgrent(); +#else + tcpd_warn("netgroup support disabled"); +#endif +#endif + } else if (mask = split_at(pat, '/')) { /* network/netmask */ + if (dot_quad_addr(pat) == INADDR_NONE + || dot_quad_addr(mask) == INADDR_NONE) + tcpd_warn("%s/%s: bad net/mask pattern", pat, mask); + } else if (STR_EQ(pat, "FAIL")) { /* obsolete */ + tcpd_warn("FAIL is no longer recognized"); + tcpd_warn("(use EXCEPT or DENY instead)"); + } else if (reserved_name(pat)) { /* other reserved */ + /* void */ ; + } else if (NOT_INADDR(pat)) { /* internet name */ + if (pat[strlen(pat) - 1] == '.') { + tcpd_warn("%s: domain or host name ends in dot", pat); + } else if (pat[0] != '.') { + addr_count = check_dns(pat); + } + } else { /* numeric form */ + if (STR_EQ(pat, "0.0.0.0") || STR_EQ(pat, "255.255.255.255")) { + /* void */ ; + } else if (pat[0] == '.') { + tcpd_warn("%s: network number begins with dot", pat); + } else if (pat[strlen(pat) - 1] != '.') { + check_dns(pat); + } + } + return (addr_count); +} + +/* reserved_name - determine if name is reserved */ + +static int reserved_name(pat) +char *pat; +{ + return (STR_EQ(pat, unknown) + || STR_EQ(pat, "KNOWN") + || STR_EQ(pat, paranoid) + || STR_EQ(pat, "ALL") + || STR_EQ(pat, "LOCAL")); +} diff --git a/contrib/tcp_wrappers/tcpdmatch.8 b/contrib/tcp_wrappers/tcpdmatch.8 new file mode 100644 index 000000000000..ebd8c7874c3f --- /dev/null +++ b/contrib/tcp_wrappers/tcpdmatch.8 @@ -0,0 +1,98 @@ +.TH TCPDMATCH 8 +.SH NAME +tcpdmatch \- tcp wrapper oracle +.SH SYNOPSYS +tcpdmatch [-d] [-i inet_conf] daemon client +.sp +tcpdmatch [-d] [-i inet_conf] daemon[@server] [user@]client +.SH DESCRIPTION +.PP +\fItcpdmatch\fR predicts how the tcp wrapper would handle a specific +request for service. Examples are given below. +.PP +The program examines the \fItcpd\fR access control tables (default +\fI/etc/hosts.allow\fR and \fI/etc/hosts.deny\fR) and prints its +conclusion. For maximal accuracy, it extracts additional information +from your \fIinetd\fR or \fItlid\fR network configuration file. +.PP +When \fItcpdmatch\fR finds a match in the access control tables, it +identifies the matched rule. In addition, it displays the optional +shell commands or options in a pretty-printed format; this makes it +easier for you to spot any discrepancies between what you want and what +the program understands. +.SH ARGUMENTS +The following two arguments are always required: +.IP daemon +A daemon process name. Typically, the last component of a daemon +executable pathname. +.IP client +A host name or network address, or one of the `unknown' or `paranoid' +wildcard patterns. +.sp +When a client host name is specified, \fItcpdmatch\fR gives a +prediction for each address listed for that client. +.sp +When a client address is specified, \fItcpdmatch\fR predicts what +\fItcpd\fR would do when client name lookup fails. +.PP +Optional information specified with the \fIdaemon@server\fR form: +.IP server +A host name or network address, or one of the `unknown' or `paranoid' +wildcard patterns. The default server name is `unknown'. +.PP +Optional information specified with the \fIuser@client\fR form: +.IP user +A client user identifier. Typically, a login name or a numeric userid. +The default user name is `unknown'. +.SH OPTIONS +.IP -d +Examine \fIhosts.allow\fR and \fIhosts.deny\fR files in the current +directory instead of the default ones. +.IP "-i inet_conf" +Specify this option when \fItcpdmatch\fR is unable to find your +\fIinetd.conf\fR or \fItlid.conf\fR network configuration file, or when +you suspect that the program uses the wrong one. +.SH EXAMPLES +To predict how \fItcpd\fR would handle a telnet request from the local +system: +.sp +.ti +5 +tcpdmatch in.telnetd localhost +.PP +The same request, pretending that hostname lookup failed: +.sp +.ti +5 +tcpdmatch in.telnetd 127.0.0.1 +.PP +To predict what tcpd would do when the client name does not match the +client address: +.sp +.ti +5 +tcpdmatch in.telnetd paranoid +.PP +On some systems, daemon names have no `in.' prefix, or \fItcpdmatch\fR +may need some help to locate the inetd configuration file. +.SH FILES +.PP +The default locations of the \fItcpd\fR access control tables are: +.PP +/etc/hosts.allow +.br +/etc/hosts.deny +.SH SEE ALSO +.na +.nf +tcpdchk(8), tcpd configuration checker +hosts_access(5), format of the tcpd access control tables. +hosts_options(5), format of the language extensions. +inetd.conf(5), format of the inetd control file. +tlid.conf(5), format of the tlid control file. +.SH AUTHORS +.na +.nf +Wietse Venema (wietse@wzv.win.tue.nl), +Department of Mathematics and Computing Science, +Eindhoven University of Technology +Den Dolech 2, P.O. Box 513, +5600 MB Eindhoven, The Netherlands +\" @(#) tcpdmatch.8 1.5 96/02/11 17:01:35 diff --git a/contrib/tcp_wrappers/tcpdmatch.c b/contrib/tcp_wrappers/tcpdmatch.c new file mode 100644 index 000000000000..b1cf75f25cee --- /dev/null +++ b/contrib/tcp_wrappers/tcpdmatch.c @@ -0,0 +1,328 @@ + /* + * tcpdmatch - explain what tcpd would do in a specific case + * + * usage: tcpdmatch [-d] [-i inet_conf] daemon[@host] [user@]host + * + * -d: use the access control tables in the current directory. + * + * -i: location of inetd.conf file. + * + * All errors are reported to the standard error stream, including the errors + * that would normally be reported via the syslog daemon. + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) tcpdmatch.c 1.5 96/02/11 17:01:36"; +#endif + +/* System libraries. */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <stdio.h> +#include <syslog.h> +#include <setjmp.h> +#include <string.h> + +extern void exit(); +extern int optind; +extern char *optarg; + +#ifndef INADDR_NONE +#define INADDR_NONE (-1) /* XXX should be 0xffffffff */ +#endif + +#ifndef S_ISDIR +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif + +/* Application-specific. */ + +#include "tcpd.h" +#include "inetcf.h" +#include "scaffold.h" + +static void usage(); +static void tcpdmatch(); + +/* The main program */ + +int main(argc, argv) +int argc; +char **argv; +{ + struct hostent *hp; + char *myname = argv[0]; + char *client; + char *server; + char *addr; + char *user; + char *daemon; + struct request_info request; + int ch; + char *inetcf = 0; + int count; + struct sockaddr_in server_sin; + struct sockaddr_in client_sin; + struct stat st; + + /* + * Show what rule actually matched. + */ + hosts_access_verbose = 2; + + /* + * Parse the JCL. + */ + while ((ch = getopt(argc, argv, "di:")) != EOF) { + switch (ch) { + case 'd': + hosts_allow_table = "hosts.allow"; + hosts_deny_table = "hosts.deny"; + break; + case 'i': + inetcf = optarg; + break; + default: + usage(myname); + /* NOTREACHED */ + } + } + if (argc != optind + 2) + usage(myname); + + /* + * When confusion really strikes... + */ + if (check_path(REAL_DAEMON_DIR, &st) < 0) { + tcpd_warn("REAL_DAEMON_DIR %s: %m", REAL_DAEMON_DIR); + } else if (!S_ISDIR(st.st_mode)) { + tcpd_warn("REAL_DAEMON_DIR %s is not a directory", REAL_DAEMON_DIR); + } + + /* + * Default is to specify a daemon process name. When daemon@host is + * specified, separate the two parts. + */ + if ((server = split_at(argv[optind], '@')) == 0) + server = unknown; + if (argv[optind][0] == '/') { + daemon = strrchr(argv[optind], '/') + 1; + tcpd_warn("%s: daemon name normalized to: %s", argv[optind], daemon); + } else { + daemon = argv[optind]; + } + + /* + * Default is to specify a client hostname or address. When user@host is + * specified, separate the two parts. + */ + if ((client = split_at(argv[optind + 1], '@')) != 0) { + user = argv[optind + 1]; + } else { + client = argv[optind + 1]; + user = unknown; + } + + /* + * Analyze the inetd (or tlid) configuration file, so that we can warn + * the user about services that may not be wrapped, services that are not + * configured, or services that are wrapped in an incorrect manner. Allow + * for services that are not run from inetd, or that have tcpd access + * control built into them. + */ + inetcf = inet_cfg(inetcf); + inet_set("portmap", WR_NOT); + inet_set("rpcbind", WR_NOT); + switch (inet_get(daemon)) { + case WR_UNKNOWN: + tcpd_warn("%s: no such process name in %s", daemon, inetcf); + break; + case WR_NOT: + tcpd_warn("%s: service possibly not wrapped", daemon); + break; + } + + /* + * Check accessibility of access control files. + */ + (void) check_path(hosts_allow_table, &st); + (void) check_path(hosts_deny_table, &st); + + /* + * Fill in what we have figured out sofar. Use socket and DNS routines + * for address and name conversions. We attach stdout to the request so + * that banner messages will become visible. + */ + request_init(&request, RQ_DAEMON, daemon, RQ_USER, user, RQ_FILE, 1, 0); + sock_methods(&request); + + /* + * If a server hostname is specified, insist that the name maps to at + * most one address. eval_hostname() warns the user about name server + * problems, while using the request.server structure as a cache for host + * address and name conversion results. + */ + if (NOT_INADDR(server) == 0 || HOSTNAME_KNOWN(server)) { + if ((hp = find_inet_addr(server)) == 0) + exit(1); + memset((char *) &server_sin, 0, sizeof(server_sin)); + server_sin.sin_family = AF_INET; + request_set(&request, RQ_SERVER_SIN, &server_sin, 0); + + for (count = 0; (addr = hp->h_addr_list[count]) != 0; count++) { + memcpy((char *) &server_sin.sin_addr, addr, + sizeof(server_sin.sin_addr)); + + /* + * Force evaluation of server host name and address. Host name + * conflicts will be reported while eval_hostname() does its job. + */ + request_set(&request, RQ_SERVER_NAME, "", RQ_SERVER_ADDR, "", 0); + if (STR_EQ(eval_hostname(request.server), unknown)) + tcpd_warn("host address %s->name lookup failed", + eval_hostaddr(request.server)); + } + if (count > 1) { + fprintf(stderr, "Error: %s has more than one address\n", server); + fprintf(stderr, "Please specify an address instead\n"); + exit(1); + } + free((char *) hp); + } else { + request_set(&request, RQ_SERVER_NAME, server, 0); + } + + /* + * If a client address is specified, we simulate the effect of client + * hostname lookup failure. + */ + if (dot_quad_addr(client) != INADDR_NONE) { + request_set(&request, RQ_CLIENT_ADDR, client, 0); + tcpdmatch(&request); + exit(0); + } + + /* + * Perhaps they are testing special client hostname patterns that aren't + * really host names at all. + */ + if (NOT_INADDR(client) && HOSTNAME_KNOWN(client) == 0) { + request_set(&request, RQ_CLIENT_NAME, client, 0); + tcpdmatch(&request); + exit(0); + } + + /* + * Otherwise, assume that a client hostname is specified, and insist that + * the address can be looked up. The reason for this requirement is that + * in real life the client address is available (at least with IP). Let + * eval_hostname() figure out if this host is properly registered, while + * using the request.client structure as a cache for host name and + * address conversion results. + */ + if ((hp = find_inet_addr(client)) == 0) + exit(1); + memset((char *) &client_sin, 0, sizeof(client_sin)); + client_sin.sin_family = AF_INET; + request_set(&request, RQ_CLIENT_SIN, &client_sin, 0); + + for (count = 0; (addr = hp->h_addr_list[count]) != 0; count++) { + memcpy((char *) &client_sin.sin_addr, addr, + sizeof(client_sin.sin_addr)); + + /* + * Force evaluation of client host name and address. Host name + * conflicts will be reported while eval_hostname() does its job. + */ + request_set(&request, RQ_CLIENT_NAME, "", RQ_CLIENT_ADDR, "", 0); + if (STR_EQ(eval_hostname(request.client), unknown)) + tcpd_warn("host address %s->name lookup failed", + eval_hostaddr(request.client)); + tcpdmatch(&request); + if (hp->h_addr_list[count + 1]) + printf("\n"); + } + free((char *) hp); + exit(0); +} + +/* Explain how to use this program */ + +static void usage(myname) +char *myname; +{ + fprintf(stderr, "usage: %s [-d] [-i inet_conf] daemon[@host] [user@]host\n", + myname); + fprintf(stderr, " -d: use allow/deny files in current directory\n"); + fprintf(stderr, " -i: location of inetd.conf file\n"); + exit(1); +} + +/* Print interesting expansions */ + +static void expand(text, pattern, request) +char *text; +char *pattern; +struct request_info *request; +{ + char buf[BUFSIZ]; + + if (STR_NE(percent_x(buf, sizeof(buf), pattern, request), unknown)) + printf("%s %s\n", text, buf); +} + +/* Try out a (server,client) pair */ + +static void tcpdmatch(request) +struct request_info *request; +{ + int verdict; + + /* + * Show what we really know. Suppress uninteresting noise. + */ + expand("client: hostname", "%n", request); + expand("client: address ", "%a", request); + expand("client: username", "%u", request); + expand("server: hostname", "%N", request); + expand("server: address ", "%A", request); + expand("server: process ", "%d", request); + + /* + * Reset stuff that might be changed by options handlers. In dry-run + * mode, extension language routines that would not return should inform + * us of their plan, by clearing the dry_run flag. This is a bit clumsy + * but we must be able to verify hosts with more than one network + * address. + */ + rfc931_timeout = RFC931_TIMEOUT; + allow_severity = SEVERITY; + deny_severity = LOG_WARNING; + dry_run = 1; + + /* + * When paranoid mode is enabled, access is rejected no matter what the + * access control rules say. + */ +#ifdef PARANOID + if (STR_EQ(eval_hostname(request->client), paranoid)) { + printf("access: denied (PARANOID mode)\n\n"); + return; + } +#endif + + /* + * Report the access control verdict. + */ + verdict = hosts_access(request); + printf("access: %s\n", + dry_run == 0 ? "delegated" : + verdict ? "granted" : "denied"); +} diff --git a/contrib/tcp_wrappers/tli-sequent.c b/contrib/tcp_wrappers/tli-sequent.c new file mode 100644 index 000000000000..8858966876a4 --- /dev/null +++ b/contrib/tcp_wrappers/tli-sequent.c @@ -0,0 +1,193 @@ + /* + * Warning - this relies heavily on the TLI implementation in PTX 2.X and will + * probably not work under PTX 4. + * + * Author: Tim Wright, Sequent Computer Systems Ltd., UK. + * + * Modified slightly to conform to the new internal interfaces - Wietse + */ + +#ifndef lint +static char sccsid[] = "@(#) tli-sequent.c 1.1 94/12/28 17:42:51"; +#endif + +#ifdef TLI_SEQUENT + +/* System libraries. */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/tiuser.h> +#include <sys/stream.h> +#include <sys/stropts.h> +#include <sys/tihdr.h> +#include <sys/timod.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <stdio.h> +#include <syslog.h> +#include <errno.h> +#include <string.h> + +extern int errno; +extern char *sys_errlist[]; +extern int sys_nerr; +extern int t_errno; +extern char *t_errlist[]; +extern int t_nerr; + +/* Local stuff. */ + +#include "tcpd.h" +#include "tli-sequent.h" + +/* Forward declarations. */ + +static char *tli_error(); +static void tli_sink(); + +/* tli_host - determine endpoint info */ + +int tli_host(request) +struct request_info *request; +{ + static struct sockaddr_in client; + static struct sockaddr_in server; + struct _ti_user *tli_state_ptr; + union T_primitives *TSI_prim_ptr; + struct strpeek peek; + int len; + + /* + * Use DNS and socket routines for name and address conversions. + */ + + sock_methods(request); + + /* + * Find out the client address using getpeerinaddr(). This call is the + * TLI equivalent to getpeername() under Dynix/ptx. + */ + + len = sizeof(client); + t_sync(request->fd); + if (getpeerinaddr(request->fd, &client, len) < 0) { + tcpd_warn("can't get client address: %s", tli_error()); + return; + } + request->client->sin = &client; + + /* Call TLI utility routine to get information on endpoint */ + if ((tli_state_ptr = _t_checkfd(request->fd)) == NULL) + return; + + if (tli_state_ptr->ti_servtype == T_CLTS) { + /* UDP - may need to get address the hard way */ + if (client.sin_addr.s_addr == 0) { + /* The UDP endpoint is not connected so we didn't get the */ + /* remote address - get it the hard way ! */ + + /* Look at the control part of the top message on the stream */ + /* we don't want to remove it from the stream so we use I_PEEK */ + peek.ctlbuf.maxlen = tli_state_ptr->ti_ctlsize; + peek.ctlbuf.len = 0; + peek.ctlbuf.buf = tli_state_ptr->ti_ctlbuf; + /* Don't even look at the data */ + peek.databuf.maxlen = -1; + peek.databuf.len = 0; + peek.databuf.buf = 0; + peek.flags = 0; + + switch (ioctl(request->fd, I_PEEK, &peek)) { + case -1: + tcpd_warn("can't peek at endpoint: %s", tli_error()); + return; + case 0: + /* No control part - we're hosed */ + tcpd_warn("can't get UDP info: %s", tli_error()); + return; + default: + /* FALL THROUGH */ + ; + } + /* Can we even check the PRIM_type ? */ + if (peek.ctlbuf.len < sizeof(long)) { + tcpd_warn("UDP control info garbage"); + return; + } + TSI_prim_ptr = (union T_primitives *) peek.ctlbuf.buf; + if (TSI_prim_ptr->type != T_UNITDATA_IND) { + tcpd_warn("wrong type for UDP control info"); + return; + } + /* Validate returned unitdata indication packet */ + if ((peek.ctlbuf.len < sizeof(struct T_unitdata_ind)) || + ((TSI_prim_ptr->unitdata_ind.OPT_length != 0) && + (peek.ctlbuf.len < + TSI_prim_ptr->unitdata_ind.OPT_length + + TSI_prim_ptr->unitdata_ind.OPT_offset))) { + tcpd_warn("UDP control info garbaged"); + return; + } + /* Extract the address */ + memcpy(&client, + peek.ctlbuf.buf + TSI_prim_ptr->unitdata_ind.SRC_offset, + TSI_prim_ptr->unitdata_ind.SRC_length); + } + request->sink = tli_sink; + } + if (getmyinaddr(request->fd, &server, len) < 0) + tcpd_warn("can't get local address: %s", tli_error()); + else + request->server->sin = &server; +} + +/* tli_error - convert tli error number to text */ + +static char *tli_error() +{ + static char buf[40]; + + if (t_errno != TSYSERR) { + if (t_errno < 0 || t_errno >= t_nerr) { + sprintf(buf, "Unknown TLI error %d", t_errno); + return (buf); + } else { + return (t_errlist[t_errno]); + } + } else { + if (errno < 0 || errno >= sys_nerr) { + sprintf(buf, "Unknown UNIX error %d", errno); + return (buf); + } else { + return (sys_errlist[errno]); + } + } +} + +/* tli_sink - absorb unreceived datagram */ + +static void tli_sink(fd) +int fd; +{ + struct t_unitdata *unit; + int flags; + + /* + * Something went wrong. Absorb the datagram to keep inetd from looping. + * Allocate storage for address, control and data. If that fails, sleep + * for a couple of seconds in an attempt to keep inetd from looping too + * fast. + */ + + if ((unit = (struct t_unitdata *) t_alloc(fd, T_UNITDATA, T_ALL)) == 0) { + tcpd_warn("t_alloc: %s", tli_error()); + sleep(5); + } else { + (void) t_rcvudata(fd, unit, &flags); + t_free((void *) unit, T_UNITDATA); + } +} + +#endif /* TLI_SEQUENT */ diff --git a/contrib/tcp_wrappers/tli-sequent.h b/contrib/tcp_wrappers/tli-sequent.h new file mode 100644 index 000000000000..4474d3cdb6fa --- /dev/null +++ b/contrib/tcp_wrappers/tli-sequent.h @@ -0,0 +1,13 @@ +#ifdef __STDC__ +#define __P(X) X +#else +#define __P(X) () +#endif + +extern int t_sync __P((int)); +extern char *t_alloc __P((int, int, int)); +extern int t_free __P((char *, int)); +extern int t_rcvudata __P((int, struct t_unitdata *, int *)); +extern int getpeerinaddr __P((int, struct sockaddr_in *, int)); +extern int getmyinaddr __P((int, struct sockaddr_in *, int)); +extern struct _ti_user *_t_checkfd __P((int)); diff --git a/contrib/tcp_wrappers/tli.c b/contrib/tcp_wrappers/tli.c new file mode 100644 index 000000000000..14579d1cbacb --- /dev/null +++ b/contrib/tcp_wrappers/tli.c @@ -0,0 +1,341 @@ + /* + * tli_host() determines the type of transport (connected, connectionless), + * the transport address of a client host, and the transport address of a + * server endpoint. In addition, it provides methods to map a transport + * address to a printable host name or address. Socket address results are + * in static memory; tli structures are allocated from the heap. + * + * The result from the hostname lookup method is STRING_PARANOID when a host + * pretends to have someone elses name, or when a host name is available but + * could not be verified. + * + * Diagnostics are reported through syslog(3). + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) tli.c 1.15 97/03/21 19:27:25"; +#endif + +#ifdef TLI + +/* System libraries. */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/stream.h> +#include <sys/stat.h> +#include <sys/mkdev.h> +#include <sys/tiuser.h> +#include <sys/timod.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <stdio.h> +#include <syslog.h> +#include <errno.h> +#include <netconfig.h> +#include <netdir.h> +#include <string.h> + +extern char *nc_sperror(); +extern int errno; +extern char *sys_errlist[]; +extern int sys_nerr; +extern int t_errno; +extern char *t_errlist[]; +extern int t_nerr; + +/* Local stuff. */ + +#include "tcpd.h" + +/* Forward declarations. */ + +static void tli_endpoints(); +static struct netconfig *tli_transport(); +static void tli_hostname(); +static void tli_hostaddr(); +static void tli_cleanup(); +static char *tli_error(); +static void tli_sink(); + +/* tli_host - look up endpoint addresses and install conversion methods */ + +void tli_host(request) +struct request_info *request; +{ + static struct sockaddr_in client; + static struct sockaddr_in server; + + /* + * If we discover that we are using an IP transport, pretend we never + * were here. Otherwise, use the transport-independent method and stick + * to generic network addresses. XXX hard-coded protocol family name. + */ + + tli_endpoints(request); + if ((request->config = tli_transport(request->fd)) != 0 + && STR_EQ(request->config->nc_protofmly, "inet")) { + if (request->client->unit != 0) { + client = *(struct sockaddr_in *) request->client->unit->addr.buf; + request->client->sin = &client; + } + if (request->server->unit != 0) { + server = *(struct sockaddr_in *) request->server->unit->addr.buf; + request->server->sin = &server; + } + tli_cleanup(request); + sock_methods(request); + } else { + request->hostname = tli_hostname; + request->hostaddr = tli_hostaddr; + request->cleanup = tli_cleanup; + } +} + +/* tli_cleanup - cleanup some dynamically-allocated data structures */ + +static void tli_cleanup(request) +struct request_info *request; +{ + if (request->config != 0) + freenetconfigent(request->config); + if (request->client->unit != 0) + t_free((char *) request->client->unit, T_UNITDATA); + if (request->server->unit != 0) + t_free((char *) request->server->unit, T_UNITDATA); +} + +/* tli_endpoints - determine TLI client and server endpoint information */ + +static void tli_endpoints(request) +struct request_info *request; +{ + struct t_unitdata *server; + struct t_unitdata *client; + int fd = request->fd; + int flags; + + /* + * Determine the client endpoint address. With unconnected services, peek + * at the sender address of the pending protocol data unit without + * popping it off the receive queue. This trick works because only the + * address member of the unitdata structure has been allocated. + * + * Beware of successful returns with zero-length netbufs (for example, + * Solaris 2.3 with ticlts transport). The netdir(3) routines can't + * handle that. Assume connection-less transport when TI_GETPEERNAME + * produces no usable result, even when t_rcvudata() is unable to figure + * out the peer address. Better to hang than to loop. + */ + + if ((client = (struct t_unitdata *) t_alloc(fd, T_UNITDATA, T_ADDR)) == 0) { + tcpd_warn("t_alloc: %s", tli_error()); + return; + } + if (ioctl(fd, TI_GETPEERNAME, &client->addr) < 0 || client->addr.len == 0) { + request->sink = tli_sink; + if (t_rcvudata(fd, client, &flags) < 0 || client->addr.len == 0) { + tcpd_warn("can't get client address: %s", tli_error()); + t_free((void *) client, T_UNITDATA); + return; + } + } + request->client->unit = client; + + /* + * Look up the server endpoint address. This can be used for filtering on + * server address or name, or to look up the client user. + */ + + if ((server = (struct t_unitdata *) t_alloc(fd, T_UNITDATA, T_ADDR)) == 0) { + tcpd_warn("t_alloc: %s", tli_error()); + return; + } + if (ioctl(fd, TI_GETMYNAME, &server->addr) < 0) { + tcpd_warn("TI_GETMYNAME: %m"); + t_free((void *) server, T_UNITDATA); + return; + } + request->server->unit = server; +} + +/* tli_transport - find out TLI transport type */ + +static struct netconfig *tli_transport(fd) +int fd; +{ + struct stat from_client; + struct stat from_config; + void *handlep; + struct netconfig *config; + + /* + * Assuming that the network device is a clone device, we must compare + * the major device number of stdin to the minor device number of the + * devices listed in the netconfig table. + */ + + if (fstat(fd, &from_client) != 0) { + tcpd_warn("fstat(fd %d): %m", fd); + return (0); + } + if ((handlep = setnetconfig()) == 0) { + tcpd_warn("setnetconfig: %m"); + return (0); + } + while (config = getnetconfig(handlep)) { + if (stat(config->nc_device, &from_config) == 0) { + if (minor(from_config.st_rdev) == major(from_client.st_rdev)) + break; + } + } + if (config == 0) { + tcpd_warn("unable to identify transport protocol"); + return (0); + } + + /* + * Something else may clobber our getnetconfig() result, so we'd better + * acquire our private copy. + */ + + if ((config = getnetconfigent(config->nc_netid)) == 0) { + tcpd_warn("getnetconfigent(%s): %s", config->nc_netid, nc_sperror()); + return (0); + } + return (config); +} + +/* tli_hostaddr - map TLI transport address to printable address */ + +static void tli_hostaddr(host) +struct host_info *host; +{ + struct request_info *request = host->request; + struct netconfig *config = request->config; + struct t_unitdata *unit = host->unit; + char *uaddr; + + if (config != 0 && unit != 0 + && (uaddr = taddr2uaddr(config, &unit->addr)) != 0) { + STRN_CPY(host->addr, uaddr, sizeof(host->addr)); + free(uaddr); + } +} + +/* tli_hostname - map TLI transport address to hostname */ + +static void tli_hostname(host) +struct host_info *host; +{ + struct request_info *request = host->request; + struct netconfig *config = request->config; + struct t_unitdata *unit = host->unit; + struct nd_hostservlist *servlist; + + if (config != 0 && unit != 0 + && netdir_getbyaddr(config, &servlist, &unit->addr) == ND_OK) { + + struct nd_hostserv *service = servlist->h_hostservs; + struct nd_addrlist *addr_list; + int found = 0; + + if (netdir_getbyname(config, service, &addr_list) != ND_OK) { + + /* + * Unable to verify that the name matches the address. This may + * be a transient problem or a botched name server setup. We + * decide to play safe. + */ + + tcpd_warn("can't verify hostname: netdir_getbyname(%.*s) failed", + STRING_LENGTH, service->h_host); + + } else { + + /* + * Look up the host address in the address list we just got. The + * comparison is done on the textual representation, because the + * transport address is an opaque structure that may have holes + * with uninitialized garbage. This approach obviously loses when + * the address does not have a textual representation. + */ + + char *uaddr = eval_hostaddr(host); + char *ua; + int i; + + for (i = 0; found == 0 && i < addr_list->n_cnt; i++) { + if ((ua = taddr2uaddr(config, &(addr_list->n_addrs[i]))) != 0) { + found = !strcmp(ua, uaddr); + free(ua); + } + } + netdir_free((void *) addr_list, ND_ADDRLIST); + + /* + * When the host name does not map to the initial address, assume + * someone has compromised a name server. More likely someone + * botched it, but that could be dangerous, too. + */ + + if (found == 0) + tcpd_warn("host name/address mismatch: %s != %.*s", + host->addr, STRING_LENGTH, service->h_host); + } + STRN_CPY(host->name, found ? service->h_host : paranoid, + sizeof(host->name)); + netdir_free((void *) servlist, ND_HOSTSERVLIST); + } +} + +/* tli_error - convert tli error number to text */ + +static char *tli_error() +{ + static char buf[40]; + + if (t_errno != TSYSERR) { + if (t_errno < 0 || t_errno >= t_nerr) { + sprintf(buf, "Unknown TLI error %d", t_errno); + return (buf); + } else { + return (t_errlist[t_errno]); + } + } else { + if (errno < 0 || errno >= sys_nerr) { + sprintf(buf, "Unknown UNIX error %d", errno); + return (buf); + } else { + return (sys_errlist[errno]); + } + } +} + +/* tli_sink - absorb unreceived datagram */ + +static void tli_sink(fd) +int fd; +{ + struct t_unitdata *unit; + int flags; + + /* + * Something went wrong. Absorb the datagram to keep inetd from looping. + * Allocate storage for address, control and data. If that fails, sleep + * for a couple of seconds in an attempt to keep inetd from looping too + * fast. + */ + + if ((unit = (struct t_unitdata *) t_alloc(fd, T_UNITDATA, T_ALL)) == 0) { + tcpd_warn("t_alloc: %s", tli_error()); + sleep(5); + } else { + (void) t_rcvudata(fd, unit, &flags); + t_free((void *) unit, T_UNITDATA); + } +} + +#endif /* TLI */ diff --git a/contrib/tcp_wrappers/try-from.c b/contrib/tcp_wrappers/try-from.c new file mode 100644 index 000000000000..925e144d90b1 --- /dev/null +++ b/contrib/tcp_wrappers/try-from.c @@ -0,0 +1,85 @@ + /* + * This program can be called via a remote shell command to find out if the + * hostname and address are properly recognized, if username lookup works, + * and (SysV only) if the TLI on top of IP heuristics work. + * + * Example: "rsh host /some/where/try-from". + * + * Diagnostics are reported through syslog(3) and redirected to stderr. + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) try-from.c 1.2 94/12/28 17:42:55"; +#endif + +/* System libraries. */ + +#include <sys/types.h> +#include <stdio.h> +#include <syslog.h> +#include <string.h> + +#ifdef TLI +#include <sys/tiuser.h> +#include <stropts.h> +#endif + +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif + +/* Local stuff. */ + +#include "tcpd.h" + +int allow_severity = SEVERITY; /* run-time adjustable */ +int deny_severity = LOG_WARNING; /* ditto */ + +main(argc, argv) +int argc; +char **argv; +{ + struct request_info request; + char buf[BUFSIZ]; + char *cp; + + /* + * Simplify the process name, just like tcpd would. + */ + if ((cp = strrchr(argv[0], '/')) != 0) + argv[0] = cp + 1; + + /* + * Turn on the "IP-underneath-TLI" detection heuristics. + */ +#ifdef TLI + if (ioctl(0, I_FIND, "timod") == 0) + ioctl(0, I_PUSH, "timod"); +#endif /* TLI */ + + /* + * Look up the endpoint information. + */ + request_init(&request, RQ_DAEMON, argv[0], RQ_FILE, STDIN_FILENO, 0); + (void) fromhost(&request); + + /* + * Show some results. Name and address information is looked up when we + * ask for it. + */ + +#define EXPAND(str) percent_x(buf, sizeof(buf), str, &request) + + puts(EXPAND("client address (%%a): %a")); + puts(EXPAND("client hostname (%%n): %n")); + puts(EXPAND("client username (%%u): %u")); + puts(EXPAND("client info (%%c): %c")); + puts(EXPAND("server address (%%A): %A")); + puts(EXPAND("server hostname (%%N): %N")); + puts(EXPAND("server process (%%d): %d")); + puts(EXPAND("server info (%%s): %s")); + + return (0); +} diff --git a/contrib/tcp_wrappers/update.c b/contrib/tcp_wrappers/update.c new file mode 100644 index 000000000000..a76cf2bb2375 --- /dev/null +++ b/contrib/tcp_wrappers/update.c @@ -0,0 +1,119 @@ + /* + * Routines for controlled update/initialization of request structures. + * + * request_init() initializes its argument. Pointers and string-valued members + * are initialized to zero, to indicate that no lookup has been attempted. + * + * request_set() adds information to an already initialized request structure. + * + * Both functions take a variable-length name-value list. + * + * Diagnostics are reported through syslog(3). + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) update.c 1.1 94/12/28 17:42:56"; +#endif + +/* System libraries */ + +#include <stdio.h> +#include <syslog.h> +#include <string.h> + +/* Local stuff. */ + +#include "mystdarg.h" +#include "tcpd.h" + +/* request_fill - request update engine */ + +static struct request_info *request_fill(request, ap) +struct request_info *request; +va_list ap; +{ + int key; + char *ptr; + + while ((key = va_arg(ap, int)) > 0) { + switch (key) { + default: + tcpd_warn("request_fill: invalid key: %d", key); + return (request); + case RQ_FILE: + request->fd = va_arg(ap, int); + continue; + case RQ_CLIENT_SIN: + request->client->sin = va_arg(ap, struct sockaddr_in *); + continue; + case RQ_SERVER_SIN: + request->server->sin = va_arg(ap, struct sockaddr_in *); + continue; + + /* + * All other fields are strings with the same maximal length. + */ + + case RQ_DAEMON: + ptr = request->daemon; + break; + case RQ_USER: + ptr = request->user; + break; + case RQ_CLIENT_NAME: + ptr = request->client->name; + break; + case RQ_CLIENT_ADDR: + ptr = request->client->addr; + break; + case RQ_SERVER_NAME: + ptr = request->server->name; + break; + case RQ_SERVER_ADDR: + ptr = request->server->addr; + break; + } + STRN_CPY(ptr, va_arg(ap, char *), STRING_LENGTH); + } + return (request); +} + +/* request_init - initialize request structure */ + +struct request_info *VARARGS(request_init, struct request_info *, request) +{ + static struct request_info default_info; + struct request_info *r; + va_list ap; + + /* + * Initialize data members. We do not assign default function pointer + * members, to avoid pulling in the whole socket module when it is not + * really needed. + */ + VASTART(ap, struct request_info *, request); + *request = default_info; + request->fd = -1; + strcpy(request->daemon, unknown); + sprintf(request->pid, "%d", getpid()); + request->client->request = request; + request->server->request = request; + r = request_fill(request, ap); + VAEND(ap); + return (r); +} + +/* request_set - update request structure */ + +struct request_info *VARARGS(request_set, struct request_info *, request) +{ + struct request_info *r; + va_list ap; + + VASTART(ap, struct request_info *, request); + r = request_fill(request, ap); + VAEND(ap); + return (r); +} diff --git a/contrib/tcp_wrappers/vfprintf.c b/contrib/tcp_wrappers/vfprintf.c new file mode 100644 index 000000000000..d6f37d59bfe8 --- /dev/null +++ b/contrib/tcp_wrappers/vfprintf.c @@ -0,0 +1,125 @@ + /* + * vfprintf() and vprintf() clones. They will produce unexpected results + * when excessive dynamic ("*") field widths are specified. To be used for + * testing purposes only. + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +static char sccsid[] = "@(#) vfprintf.c 1.2 94/03/23 17:44:46"; +#endif + +#include <stdio.h> +#include <ctype.h> +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +/* vfprintf - print variable-length argument list to stream */ + +int vfprintf(fp, format, ap) +FILE *fp; +char *format; +va_list ap; +{ + char fmt[BUFSIZ]; /* format specifier */ + register char *fmtp; + register char *cp; + int count = 0; + + /* + * Iterate over characters in the format string, picking up arguments + * when format specifiers are found. + */ + + for (cp = format; *cp; cp++) { + if (*cp != '%') { + putc(*cp, fp); /* ordinary character */ + count++; + } else { + + /* + * Format specifiers are handled one at a time, since we can only + * deal with arguments one at a time. Try to determine the end of + * the format specifier. We do not attempt to fully parse format + * strings, since we are ging to let fprintf() do the hard work. + * In regular expression notation, we recognize: + * + * %-?0?([0-9]+|\*)?\.?([0-9]+|\*)?l?[a-z] + * + * which includes some combinations that do not make sense. + */ + + fmtp = fmt; + *fmtp++ = *cp++; + if (*cp == '-') /* left-adjusted field? */ + *fmtp++ = *cp++; + if (*cp == '0') /* zero-padded field? */ + *fmtp++ = *cp++; + if (*cp == '*') { /* dynamic field witdh */ + sprintf(fmtp, "%d", va_arg(ap, int)); + fmtp += strlen(fmtp); + cp++; + } else { + while (isdigit(*cp)) /* hard-coded field width */ + *fmtp++ = *cp++; + } + if (*cp == '.') /* width/precision separator */ + *fmtp++ = *cp++; + if (*cp == '*') { /* dynamic precision */ + sprintf(fmtp, "%d", va_arg(ap, int)); + fmtp += strlen(fmtp); + cp++; + } else { + while (isdigit(*cp)) /* hard-coded precision */ + *fmtp++ = *cp++; + } + if (*cp == 'l') /* long whatever */ + *fmtp++ = *cp++; + if (*cp == 0) /* premature end, punt */ + break; + *fmtp++ = *cp; /* type (checked below) */ + *fmtp = 0; + + /* Execute the format string - let fprintf() do the hard work. */ + + switch (fmtp[-1]) { + case 's': /* string-valued argument */ + count += fprintf(fp, fmt, va_arg(ap, char *)); + break; + case 'c': /* integral-valued argument */ + case 'd': + case 'u': + case 'o': + case 'x': + if (fmtp[-2] == 'l') + count += fprintf(fp, fmt, va_arg(ap, long)); + else + count += fprintf(fp, fmt, va_arg(ap, int)); + break; + case 'e': /* float-valued argument */ + case 'f': + case 'g': + count += fprintf(fp, fmt, va_arg(ap, double)); + break; + default: /* anything else */ + putc(fmtp[-1], fp); + count++; + break; + } + } + } + return (count); +} + +/* vprintf - print variable-length argument list to stdout */ + +vprintf(format, ap) +char *format; +va_list ap; +{ + return (vfprintf(stdout, format, ap)); +} diff --git a/contrib/tcp_wrappers/workarounds.c b/contrib/tcp_wrappers/workarounds.c new file mode 100644 index 000000000000..9ffa247a90ba --- /dev/null +++ b/contrib/tcp_wrappers/workarounds.c @@ -0,0 +1,308 @@ + /* + * Workarounds for known system software bugs. This module provides wrappers + * around library functions and system calls that are known to have problems + * on some systems. Most of these workarounds won't do any harm on regular + * systems. + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#ifndef lint +char sccsid[] = "@(#) workarounds.c 1.6 96/03/19 16:22:25"; +#endif + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <errno.h> +#include <stdio.h> +#include <syslog.h> +#include <string.h> + +extern int errno; + +#include "tcpd.h" + + /* + * Some AIX versions advertise a too small MAXHOSTNAMELEN value (32). + * Result: long hostnames would be truncated, and connections would be + * dropped because of host name verification failures. Adrian van Bloois + * (A.vanBloois@info.nic.surfnet.nl) figured out what was the problem. + */ + +#if (MAXHOSTNAMELEN < 64) +#undef MAXHOSTNAMELEN +#endif + +/* In case not defined in <sys/param.h>. */ + +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 256 /* storage for host name */ +#endif + + /* + * Some DG/UX inet_addr() versions return a struct/union instead of a long. + * You have this problem when the compiler complains about illegal lvalues + * or something like that. The following code fixes this mutant behaviour. + * It should not be enabled on "normal" systems. + * + * Bug reported by ben@piglet.cr.usgs.gov (Rev. Ben A. Mesander). + */ + +#ifdef INET_ADDR_BUG + +#undef inet_addr + +long fix_inet_addr(string) +char *string; +{ + return (inet_addr(string).s_addr); +} + +#endif /* INET_ADDR_BUG */ + + /* + * With some System-V versions, the fgets() library function does not + * account for partial reads from e.g. sockets. The result is that fgets() + * gives up too soon, causing username lookups to fail. Problem first + * reported for IRIX 4.0.5, by Steve Kotsopoulos <steve@ecf.toronto.edu>. + * The following code works around the problem. It does no harm on "normal" + * systems. + */ + +#ifdef BROKEN_FGETS + +#undef fgets + +char *fix_fgets(buf, len, fp) +char *buf; +int len; +FILE *fp; +{ + char *cp = buf; + int c; + + /* + * Copy until the buffer fills up, until EOF, or until a newline is + * found. + */ + while (len > 1 && (c = getc(fp)) != EOF) { + len--; + *cp++ = c; + if (c == '\n') + break; + } + + /* + * Return 0 if nothing was read. This is correct even when a silly buffer + * length was specified. + */ + if (cp > buf) { + *cp = 0; + return (buf); + } else { + return (0); + } +} + +#endif /* BROKEN_FGETS */ + + /* + * With early SunOS 5 versions, recvfrom() does not completely fill in the + * source address structure when doing a non-destructive read. The following + * code works around the problem. It does no harm on "normal" systems. + */ + +#ifdef RECVFROM_BUG + +#undef recvfrom + +int fix_recvfrom(sock, buf, buflen, flags, from, fromlen) +int sock; +char *buf; +int buflen; +int flags; +struct sockaddr *from; +int *fromlen; +{ + int ret; + + /* Assume that both ends of a socket belong to the same address family. */ + + if ((ret = recvfrom(sock, buf, buflen, flags, from, fromlen)) >= 0) { + if (from->sa_family == 0) { + struct sockaddr my_addr; + int my_addr_len = sizeof(my_addr); + + if (getsockname(0, &my_addr, &my_addr_len)) { + tcpd_warn("getsockname: %m"); + } else { + from->sa_family = my_addr.sa_family; + } + } + } + return (ret); +} + +#endif /* RECVFROM_BUG */ + + /* + * The Apollo SR10.3 and some SYSV4 getpeername(2) versions do not return an + * error in case of a datagram-oriented socket. Instead, they claim that all + * UDP requests come from address 0.0.0.0. The following code works around + * the problem. It does no harm on "normal" systems. + */ + +#ifdef GETPEERNAME_BUG + +#undef getpeername + +int fix_getpeername(sock, sa, len) +int sock; +struct sockaddr *sa; +int *len; +{ + int ret; + struct sockaddr_in *sin = (struct sockaddr_in *) sa; + + if ((ret = getpeername(sock, sa, len)) >= 0 + && sa->sa_family == AF_INET + && sin->sin_addr.s_addr == 0) { + errno = ENOTCONN; + return (-1); + } else { + return (ret); + } +} + +#endif /* GETPEERNAME_BUG */ + + /* + * According to Karl Vogel (vogelke@c-17igp.wpafb.af.mil) some Pyramid + * versions have no yp_default_domain() function. We use getdomainname() + * instead. + */ + +#ifdef USE_GETDOMAIN + +int yp_get_default_domain(ptr) +char **ptr; +{ + static char mydomain[MAXHOSTNAMELEN]; + + *ptr = mydomain; + return (getdomainname(mydomain, MAXHOSTNAMELEN)); +} + +#endif /* USE_GETDOMAIN */ + +#ifndef INADDR_NONE +#define INADDR_NONE 0xffffffff +#endif + + /* + * Solaris 2.4 gethostbyname() has problems with multihomed hosts. When + * doing DNS through NIS, only one host address ends up in the address list. + * All other addresses end up in the hostname alias list, interspersed with + * copies of the official host name. This would wreak havoc with tcpd's + * hostname double checks. Below is a workaround that should do no harm when + * accidentally left in. A side effect of the workaround is that address + * list members are no longer properly aligned for structure access. + */ + +#ifdef SOLARIS_24_GETHOSTBYNAME_BUG + +#undef gethostbyname + +struct hostent *fix_gethostbyname(name) +char *name; +{ + struct hostent *hp; + struct in_addr addr; + char **o_addr_list; + char **o_aliases; + char **n_addr_list; + int broken_gethostbyname = 0; + + if ((hp = gethostbyname(name)) && !hp->h_addr_list[1] && hp->h_aliases[1]) { + for (o_aliases = n_addr_list = hp->h_aliases; *o_aliases; o_aliases++) { + if ((addr.s_addr = inet_addr(*o_aliases)) != INADDR_NONE) { + memcpy(*n_addr_list++, (char *) &addr, hp->h_length); + broken_gethostbyname = 1; + } + } + if (broken_gethostbyname) { + o_addr_list = hp->h_addr_list; + memcpy(*n_addr_list++, *o_addr_list, hp->h_length); + *n_addr_list = 0; + hp->h_addr_list = hp->h_aliases; + hp->h_aliases = o_addr_list + 1; + } + } + return (hp); +} + +#endif /* SOLARIS_24_GETHOSTBYNAME_BUG */ + + /* + * Horror! Some FreeBSD 2.0 libc routines call strtok(). Since tcpd depends + * heavily on strtok(), strange things may happen. Workaround: use our + * private strtok(). This has been fixed in the meantime. + */ + +#ifdef USE_STRSEP + +char *fix_strtok(buf, sep) +char *buf; +char *sep; +{ + static char *state; + char *result; + + if (buf) + state = buf; + while ((result = strsep(&state, sep)) && result[0] == 0) + /* void */ ; + return (result); +} + +#endif /* USE_STRSEP */ + + /* + * IRIX 5.3 (and possibly earlier versions, too) library routines call the + * non-reentrant strtok() library routine, causing hosts to slip through + * allow/deny filters. Workaround: don't rely on the vendor and use our own + * strtok() function. FreeBSD 2.0 has a similar problem (fixed in 2.0.5). + */ + +#ifdef LIBC_CALLS_STRTOK + +char *my_strtok(buf, sep) +char *buf; +char *sep; +{ + static char *state; + char *result; + + if (buf) + state = buf; + + /* + * Skip over separator characters and detect end of string. + */ + if (*(state += strspn(state, sep)) == 0) + return (0); + + /* + * Skip over non-separator characters and terminate result. + */ + result = state; + if (*(state += strcspn(state, sep)) != 0) + *state++ = 0; + return (result); +} + +#endif /* LIBC_CALLS_STRTOK */ |