]> git.neil.brown.name Git - portmap.git/commitdiff
Initial checkin of portmap_beta5 portmap_5beta
authorNeil Brown <neilb@notabene.brown>
Fri, 20 Apr 2007 00:35:59 +0000 (10:35 +1000)
committerNeil Brown <neilb@notabene.brown>
Fri, 20 Apr 2007 00:35:59 +0000 (10:35 +1000)
Both the current Debian and SuSE distros have identical .tar.gz
containing these files.

14 files changed:
BLURB [new file with mode: 0644]
CHANGES [new file with mode: 0644]
Makefile [new file with mode: 0644]
Makefile.shlib [new file with mode: 0644]
README [new file with mode: 0644]
daemon.c [new file with mode: 0644]
from_local.c [new file with mode: 0644]
get_myaddress.c [new file with mode: 0644]
pmap_check.c [new file with mode: 0644]
pmap_check.h [new file with mode: 0644]
pmap_dump.c [new file with mode: 0644]
pmap_set.c [new file with mode: 0644]
portmap.c [new file with mode: 0644]
strerror.c [new file with mode: 0644]

diff --git a/BLURB b/BLURB
new file mode 100644 (file)
index 0000000..ce28af8
--- /dev/null
+++ b/BLURB
@@ -0,0 +1,42 @@
+@(#) BLURB 1.5 96/07/06 23:09:45
+
+This is the fifth replacement portmapper release.
+
+There is an increasing interest in access control for the NIS, mount
+and other RPC-based services that are normally registered with the
+portmap process. Possible attacks on RPC daemons involve:
+
+    - theft of NIS (YP) password files
+
+    - ypset to force hosts to bind to a rogue NIS (YP) server
+
+    - theft of NFS file handles
+
+My contribution is a replacement portmap program, derived from source
+code in the RPCSRC 4.0 and the TIRPC source distributions.  Access
+control (optional) is in the style of my tcp wrapper (log_tcp) package.
+
+Supported platforms: this program is known to work with all SunOS 4.x
+releases. With some Makefile editing it should also work on Ultrix 4.x,
+HP-UX 9.x, AIX 3.x and AIX 4.x, and Digital UNIX (OSF/1).
+
+Solaris 2.x and other System V.4 UNIXes should use use my rpcbind
+replacement (ftp.win.tue.nl:/pub/security/rpcbind_*.tar.Z).
+
+This portmap version attempts to close all portmap security problems
+that are known to me.  The README file gives a complete list of
+security features.
+
+Without the availability of portmap source, possible alternatives are
+1) packet filtering with a smart router (which we do anyway); 2)
+linking the portmap executable against the securelib shared library.
+Linking RPC daemons against the securelib library is a good idea,
+anyway.
+
+The source is available for anonymous FTP from ftp.win.tue.nl directory
+/pub/security/portmap_*.tar.gz.
+
+       Wietse Venema (wietse@wzv.win.tue.nl)
+       Mathematics and Computing Science
+       Eindhoven University of Technology
+       The Netherlands
diff --git a/CHANGES b/CHANGES
new file mode 100644 (file)
index 0000000..448475b
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,26 @@
+@(#) CHANGES 1.2 96/07/06 23:06:17
+
+Changes with release 5 (July 1996)
+
+Unprivileged clients can no longer unset or set the nfsd port.
+
+The really desperate can force all set/unset requests to arrive via the
+loopback interface and block set/unset requests from outside. Besides
+changes to the portmapper, this requires changes to system libraries,
+to statically linked rpc servers, to the kernel configuration (no IP
+source routing), and perhaps even to system startup procedures. Not for
+the faint of hart.
+
+Changes with release 4 (May 1996)
+
+The old code could not handle more than 16 interface addresses per
+host. With virtual hosting, a system can have more than 16 addresses.
+We now allocate memory dynamically.
+
+Support for AIX 4.1. Just like 4.4 BSD, it has variable-length sockaddr
+structures. Build with -DHAS_SA_LEN.
+
+Support for NextStep 3.2. This is a pre-posix system without setsid().
+
+Support for Digital UNIX on the Alpha. On these machines, long and int
+are not interchangeable. 
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..3f7cb21
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,154 @@
+# @(#) Makefile 1.6 96/07/06 23:06:17
+
+####################################
+### Beginning of configurable stuff.
+
+# By default, logfile entries are written to the same file as used for
+# sendmail transaction logs. Change the definition of the following macro
+# if you disagree. See `man 3 syslog' for examples. Some syslog versions
+# do not provide this flexibility.
+#
+FACILITY=LOG_MAIL
+
+# To disable tcp-wrapper style access control, comment out the following
+# macro definitions.  Access control can also be turned off by providing
+# no access control tables. The local system, since it runs the portmap
+# daemon, is always treated as an authorized host.
+
+HOSTS_ACCESS= -DHOSTS_ACCESS
+WRAP_LIB = $(WRAP_DIR)/libwrap.a
+
+# Comment out if your RPC library does not allocate privileged ports for
+# requests from processes with root privilege, or the new portmap will
+# always reject requests to register/unregister services on privileged
+# ports. You can find out by running "rpcinfo -p"; if all mountd and NIS
+# daemons use a port >= 1024 you should probably disable the next line.
+
+CHECK_PORT = -DCHECK_PORT
+
+# Warning: troublesome feature ahead!! Enable only when you are really
+# desperate!!
+#
+# It is possible to prevent an attacker from manipulating your portmapper
+# tables from outside with requests that contain spoofed source addresses.
+# The countermeasure is to force all rpc servers to register and
+# unregister with the portmapper via the loopback network interface,
+# instead of via the primary network interface that every host can talk
+# to. For this countermeasure to work it is necessary to uncomment the
+# LOOPBACK definition below, and to take the following additional steps:
+# 
+# (1) Modify the libc library (or librpc if you have one) and replace
+# get_myaddress() by a version that selects the loopback address instead
+# of the primary network interface address. A suitable version is
+# provided in the file get_myaddress.c. This forces rpc servers to send
+# all set/unset requests to the loopback address.
+# 
+# (2) Rebuild all statically-linked rpc servers with the modified
+# library.
+# 
+# (3) Disable IP source routing in the kernel (otherwise an outside
+# attacker can still send requests that appear to come from the local
+# machine).
+# 
+# Instead of (1) it may be sufficient to run the rpc servers with a
+# preload shared object that implements the alternate get_myaddress()
+# behavior (see Makefile.shlib). You still need to disable IP source
+# routing, though.
+#
+# I warned you, you need to be really desperate to do this. It is
+# probably much easier to just block port UDP and TCP ports 111 on
+# your routers.
+#
+# LOOPBACK = -DLOOPBACK_SETUNSET
+
+# When the portmapper cannot find any local interfaces (it will complain
+# to the syslog daemon) your system probably has variable-length socket
+# address structures (struct sockaddr has a sa_len component; examples:
+# AIX 4.1 and 4.4BSD). Uncomment next macro definition in that case.
+#
+# SA_LEN = -DHAS_SA_LEN                # AIX 4.x, BSD 4.4, FreeBSD, NetBSD
+
+# With verbose logging on, HP-UX 9.x and AIX 4.1 leave zombies behind when
+# SIGCHLD is not ignored. Enable next macro for a fix.
+#
+# ZOMBIES = -DIGNORE_SIGCHLD   # AIX 4.x, HP-UX 9.x
+
+# Uncomment the following macro if your system does not have u_long.
+#
+# ULONG        =-Du_long="unsigned long"
+
+# Later versions of the tcp wrapper (log_tcp package) come with a
+# libwrap.a object library. WRAP_DIR should specify the directory with
+# that library.
+
+WRAP_DIR= ../tcp_wrappers
+
+# Auxiliary object files that may be missing from your C library.
+#
+AUX    = daemon.o strerror.o
+
+# NEXTSTEP is a little different. The following seems to work with NS 3.2
+#
+# SETPGRP      =-DUSE_SETPGRP00
+# LIBS = -m
+# NSARCHS      = -arch m68k -arch i386 -arch hppa
+
+# Auxiliary libraries that you may have to specify
+#
+# LIBS = -lrpc
+
+# Comment out if your compiler talks ANSI and understands const
+#
+CONST   = -Dconst=
+
+### End of configurable stuff.
+##############################
+
+SHELL  = /bin/sh
+
+COPT   = $(CONST) -Dperror=xperror $(HOSTS_ACCESS) $(CHECK_PORT) \
+       $(SYS) -DFACILITY=$(FACILITY) $(ULONG) $(ZOMBIES) $(SA_LEN) \
+       $(LOOPBACK) $(SETPGRP)
+CFLAGS = $(COPT) -O $(NSARCHS)
+OBJECTS        = portmap.o pmap_check.o from_local.o $(AUX)
+
+all:   portmap pmap_dump pmap_set
+
+portmap: $(OBJECTS) $(WRAP_DIR)/libwrap.a
+       $(CC) $(CFLAGS) -o $@ $(OBJECTS) $(WRAP_LIB) $(LIBS)
+
+pmap_dump: pmap_dump.c
+       $(CC) $(CFLAGS) -o $@ $? $(LIBS)
+
+pmap_set: pmap_set.c
+       $(CC) $(CFLAGS) -o $@ $? $(LIBS)
+
+from_local: from_local.c
+       cc $(CFLAGS) -DTEST -o $@ from_local.c
+
+get_myaddress: get_myaddress.c
+       cc $(CFLAGS) -DTEST -o $@ get_myaddress.c $(LIBS)
+
+lint:  
+       lint $(COPT) $(OBJECTS:%.o=%.c)
+
+clean:
+       rm -f *.o portmap pmap_dump pmap_set from_local get_myaddress \
+           get_myaddress.so core
+
+tidy:  clean
+       chmod 755 . ; chmod -R a+r .
+
+deps:
+       @$(CC) -M $(CFLAGS) *.c | grep -v /usr/include |sed 's/\.\///'
+
+daemon.o: daemon.c
+from_local.o: from_local.c
+get_myaddress.o: get_myaddress.c
+pmap_check.o: pmap_check.c
+pmap_check.o: pmap_check.h Makefile
+pmap_dump.o: pmap_dump.c
+pmap_set.o: pmap_set.c
+portmap.o: portmap.c
+portmap.o: pmap_check.h Makefile
+strerror.o: strerror.c
diff --git a/Makefile.shlib b/Makefile.shlib
new file mode 100644 (file)
index 0000000..26b83b9
--- /dev/null
@@ -0,0 +1,46 @@
+# @(#) Makefile.shlib 1.1 96/07/06 23:00:53
+#
+# Warning: don't do this unless you are really desperate!!
+#
+# Makefile to build a shared object that forces RPC servers to register
+# and unregister with the portmapper through the loopback interface
+# instead of via the primary network interface address.
+#
+# This is a desperate attempt to prevent an attacker from using source
+# address spoofing to manipulate your portmapper tables. For this to be
+# effective you need to build the portmapper with -DLOOPBACK_SETUNSET,
+# and you need to disable IP source routing in the UNIX kernel.
+#
+# Quick summary of what to do to trick your rpc servers into cooperation:
+#
+# 1 - In the text below, uncomment the SH_CC and SH_LD definitions that are
+# appropriate for your environment. Then type:
+#
+#      make -f Makefile.shcc
+#
+# 2 - Install the get_myaddress.so shared object in a suitable place, for
+# example in the /usr/local/lib directory.
+#
+# 3 - Edit your system startup files so that the rpc servers use the
+# get_myaddress.so shared object. For several environments, the text below
+# gives an example in bourne-shell syntax of how how to start an rpc server.
+
+# SunOS 4
+# /bin/sh syntax: LD_PRELOAD=/some/where/get_myaddress.so rpcserver...
+SH_CC  = cc -pic
+SH_LD  = ld -assert pure-text
+
+# NetBSD, FreeBSD
+# /bin/sh syntax: LD_PRELOAD=/some/where/get_myaddress.so rpcserver...
+#SH_CC = cc -fpic
+#SH_LD = ld -Bshareable        
+
+# Digital UNIX
+# /bin/sh syntax: _RLD_LIST=/some/where/get_myaddress.so:DEFAULT rpcserver...
+#SH_CC = cc -pic
+#SH_LD = ld -shared
+
+# Build the shared object
+get_myaddress.so: get_myaddress.c
+       $(SH_CC) -c get_myaddress.c
+       $(SH_LD) -o get_myaddress.so get_myaddress.o
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..c2b0c0b
--- /dev/null
+++ b/README
@@ -0,0 +1,214 @@
+@(#) README 1.7 96/07/06 23:06:19
+
+This is the README file for the 5th enhanced portmapper release.
+
+Description
+-----------
+
+This README describes a replacement portmapper that prevents theft of
+NIS (YP), NFS, and other sensitive information via the portmapper.  As
+an option, the program supports access control in the style of the tcp
+wrapper (log_tcp) package.
+
+Like all portmappers, this one is intended to be started at boot time.
+Daemons that offer RPC services tell the portmapper on what port they
+listen. Unlike the well-known services registered with the inetd, RPC
+network port numbers may change each time the system is booted.
+Whenever a client wants to use an RPC service it is supposed to first
+ask the portmapper on what port the corresponding daemon is listening.
+The rpcinfo command can tell you what RPC services your system offers.
+
+As described in the features section below, the replacement portmapper
+can prevent undesirable client-server interactions.  In some cases,
+better or equivalent alternatives are available:
+
+    The SunOS portmap that is provided with patch id 100482-02 should
+    close the same security holes.  In addition, it provides an YPSERV
+    daemon with its own access control list. This is better than just
+    portmapper access control.
+
+    The "securelib" shared library (eecs.nwu.edu:/pub/securelib.tar)
+    implements access control for all kinds of (RPC) services, not
+    just the portmapper.
+
+However, vendors still ship portmap implementations that allow anyone
+to read or modify its tables and that will happily forward any request
+so that it appears to come from the local system.
+
+Features
+--------
+
+- optional: host access control. The local host is always considered
+authorized. Access control requires the libwrap.a library that comes
+with recent tcp wrapper (log_tcp) implementations.
+
+- requests to change the portmap tables are accepted only when they
+come from the local system.
+
+- optional: requests to (un)register services that listen on privileged
+ports (port < 1024) are accepted only when the requests themselves come
+from a privileged port. This feature is optional because of older RPC
+implementations.
+
+- requests that are forwarded by the portmapper will be forwarded
+through an unprivileged port.
+
+- the portmapper refuses to forward requests to rpc daemons that do (or
+should) verify the origin of each request: when the portmapper forwards
+a request it appears to come from the local machine. At present, the
+portmapper refuses to forward all RPC calls to itself, and most RPC
+calls to the NFS mountd/nfsd daemons, and to the NIS daemons.
+
+- the really desperate can harden the portmapper even more by requiring
+that requests to modify its tables arrive via the loopback network
+interface, instead of via the primary network interface that every host
+can talk to. The cost is high: besides changes to the portmapper, this
+requires changes to system libraries, to statically-linked rpc servers,
+to the kernel to disable IP source routing, and perhaps even to system
+startup procedures.  Don't do this unless you're desperate.  Details
+are given in the Makefile.
+
+Restrictions
+------------
+
+Limiting access to the portmapper does not protect you from direct
+attacks on the rpc daemons; the main task of portmap is to maintain a
+table of available RPC services and of the network ports that they are
+listening on. The securelib can be used to protect individual RPC
+daemons, and the latest SunOS portmap+NIS fix already protects the NIS
+daemons and implements limited forwarding.
+
+On the other hand, even though a portmapper with access control only
+makes an attack more difficult, it still provides an excellent early
+warning system.
+
+Origin and portability
+----------------------
+
+The sources in this distribution are derived from code on the second
+BSD networking tape, which was derived from Sun's RPCSRC 4.0 code, and
+from Sun's TIRPC (transport-independent rpc) distribution. 
+
+The code compiles fine with SunOS 4.1.x, Ultrix 4.x, HP-UX 9.x, AIX 3.x
+and AIX 4.x, and Digital UNIX (OSF/1). See the notes in the Makefile.
+
+Solaris 2.x (and other true System V.4 clones) use a different program
+called rpcbind. I have written a replacement for that program, too.
+The primary achive is ftp.win.tue.nl:/pub/security/rpcbind_xx.tar.Z.
+
+Installation
+------------
+
+(1) Follow the instructions in the Makefile, then build the portmap and
+auxiliary executables.
+
+(2) Before killing the present portmap process, save the present
+portmapper tables using the command:
+
+       ./pmap_dump >table
+
+If you kill the portmap process without saving its tables you will have
+to reboot the machine.
+
+Note: the information in the portmap tables is dynamic: For example, it
+will be different after each reboot. On a Sun, it even changes each
+time a windowing system is started that uses the selection service.
+
+(3) Kill the running portmap process and start the new portmap
+program.  Then (still as root) initialize the portmap tables with:
+
+       ./pmap_set <table
+
+(4) If you get error messages of the form: "not registered: xxxx",
+disable the CHECK_PORT feature in the Makefile, remove pmap_check.o and
+rebuild the portmap program.  Then proceed with step 3.
+
+If the portmapper complains that it cannot find all machine interfaces
+you will have to rebuild it with -DHAS_SA_LEN set (see Makefile). You
+can test this with the "from_local" command (to build: make from_local).
+
+In order to revert to the original portmap daemon, kill off the running
+one, restart the original portmapper and reload its tables using the
+"pmap_set" command as shown above.
+
+Access control:
+---------------
+
+By default, host access control is enabled. However, the host that runs
+the portmapper is always considered authorized. The host access control
+tables are never consulted with requests from the local system itself;
+they are always consulted with requests from other hosts.
+
+In order to avoid deadlocks, the portmap program does not attempt to
+look up the remote host name or user name, nor will it try to match NIS
+netgroups. The upshot of all this is that only network number patterns
+will work for portmap access control.
+
+Sample entries for the host access-control files are:
+
+    /etc/hosts.allow:
+       portmap: your.sub.net.number/your.sub.net.mask
+       portmap: 255.255.255.255 0.0.0.0
+
+    /etc/hosts.deny
+       portmap: ALL: (/some/where/safe_finger -l @%h | mail root) &
+
+The syntax of the access-control files is described in the
+hosts_access.5 manual page that comes with the tcp wrapper (log_tcp)
+sources.  The safe_finger command comes with later wrapper releases.
+
+The first line in the hosts.allow file permits access from all systems
+within your own subnet. Some rpc services rely on broadcasts and will
+contact your portmapper anyway; and once an intruder has access to your
+local network segment you're already in deep trouble.
+
+The second line in the hosts.allow file may be needed if there are
+any PC-NFS systems on your network segment.
+
+For security reasons, the portmap process drops root privilegs after
+initialization. The access control files should therefore be readable
+for group or world.
+
+Testing:
+--------
+
+Normally, only rejected requests will be reported via the syslog
+daemon.  Logging is done in a child process, in order to avoid
+possible deadlock in case the logging code needs assistance from
+the portmapper.
+
+By default, the portmapper will be utterly silent. In fact, the portmap
+daemon is not consulted that often. Sending a SIGINT signal to the
+portmap process will enable the logging of all requests. 
+
+Another way to enable verbose logging is to start the daemon with the
+"-v" option. See above, steps (2) and later, on how to stop and restart
+the portmapper without having to reboot.
+
+Warning:  with some HP-UX and AIX versions, when verbose logging is on,
+the system fills up with zombie processes. This can be fixed by
+compiling with -DIGNORE_SIGCHLD (see instructions in the Makefile).
+
+With verbose logging turned on, requests such as "ypcat" or "rpcinfo
+-p" should show up with log file entries such as:
+
+ MMM dd hh:mm:ss hostname portmap[pid]: connect from x.x.x.x to getport(ypserv)
+ MMM dd hh:mm:ss hostname portmap[pid]: connect from y.y.y.y to dump() 
+
+Send SIGINT to the portmapper to turn the verbose logging off.
+
+Acknowledgements
+----------------
+
+Casper H.S. Dik (casper@fwi.uva.nl) provided valuable information on
+RPC security and tested an intermediate version of the portmapper with
+SunOS 4.1.2.  Lyford D. Rich (rich@ece.nps.navy.mil) was helpful with
+porting the daemon to Ultrix 3.x. Lionel Cons (cons@dxcern.cern.ch)
+solved the HP-UX problem. Fabrice Gonton (Fabrice.Gonton@sagem.fr)
+figured out how to make the program work on AIX 4.1, and Michael
+Matthews took care of the DEC Alpha platform.
+
+       Wietse Venema (wietse@wzv.win.tue.nl)
+       Mathematics and Computing Science
+       Eindhoven University of Technology
+       The Netherlands
diff --git a/daemon.c b/daemon.c
new file mode 100644 (file)
index 0000000..4f265aa
--- /dev/null
+++ b/daemon.c
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)daemon.c   5.3 (Berkeley) 12/28/90";
+#endif /* LIBC_SCCS and not lint */
+
+#include <fcntl.h>
+
+/* From unistd.h */
+#define STDIN_FILENO   0
+#define STDOUT_FILENO  1
+#define STDERR_FILENO  2
+
+/* From paths.h */
+#define _PATH_DEVNULL  "/dev/null"
+
+daemon(nochdir, noclose)
+       int nochdir, noclose;
+{
+       int cpid;
+
+       if ((cpid = fork()) == -1)
+               return (-1);
+       if (cpid)
+               exit(0);
+       (void) setsid();
+       if (!nochdir)
+               (void) chdir("/");
+       if (!noclose) {
+               int devnull = open(_PATH_DEVNULL, O_RDWR, 0);
+
+               if (devnull != -1) {
+                       (void) dup2(devnull, STDIN_FILENO);
+                       (void) dup2(devnull, STDOUT_FILENO);
+                       (void) dup2(devnull, STDERR_FILENO);
+                       if (devnull > 2)
+                               (void) close(devnull);
+               }
+       }
+       return(0);
+}
diff --git a/from_local.c b/from_local.c
new file mode 100644 (file)
index 0000000..3f64405
--- /dev/null
@@ -0,0 +1,185 @@
+ /*
+  * Check if an address belongs to the local system. Adapted from:
+  * 
+  * @(#)pmap_svc.c 1.32 91/03/11 Copyright 1984,1990 Sun Microsystems, Inc.
+  * @(#)get_myaddress.c  2.1 88/07/29 4.0 RPCSRC.
+  */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part.  Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user or with the express written consent of
+ * Sun Microsystems, Inc.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#) from_local.c 1.3 96/05/31 15:52:57";
+#endif
+
+#ifdef TEST
+#undef perror
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <syslog.h>
+
+#ifndef TRUE
+#define        TRUE    1
+#define FALSE  0
+#endif
+
+ /*
+  * With virtual hosting, each hardware network interface can have multiple
+  * network addresses. On such machines the number of machine addresses can
+  * be surprisingly large.
+  */
+static int num_local;
+static int num_addrs;
+static struct in_addr *addrs;
+
+/* grow_addrs - extend list of local interface addresses */
+
+static int grow_addrs()
+{
+    struct in_addr *new_addrs;
+    int     new_num;
+
+    /*
+     * Keep the previous result if we run out of memory. The system would
+     * really get hosed if we simply give up.
+     */
+    new_num = (addrs == 0) ? 1 : num_addrs + num_addrs;
+    new_addrs = (struct in_addr *) malloc(sizeof(*addrs) * new_num);
+    if (new_addrs == 0) {
+       perror("portmap: out of memory");
+       return (0);
+    } else {
+       if (addrs != 0) {
+           memcpy((char *) new_addrs, (char *) addrs,
+                  sizeof(*addrs) * num_addrs);
+           free((char *) addrs);
+       }
+       num_addrs = new_num;
+       addrs = new_addrs;
+       return (1);
+    }
+}
+
+/* find_local - find all IP addresses for this host */
+
+find_local()
+{
+    struct ifconf ifc;
+    struct ifreq ifreq;
+    struct ifreq *ifr;
+    struct ifreq *the_end;
+    int     sock;
+    char    buf[BUFSIZ];
+
+    /*
+     * Get list of network interfaces. We use a huge buffer to allow for the
+     * presence of non-IP interfaces.
+     */
+
+    if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
+       perror("socket");
+       return (0);
+    }
+    ifc.ifc_len = sizeof(buf);
+    ifc.ifc_buf = buf;
+    if (ioctl(sock, SIOCGIFCONF, (char *) &ifc) < 0) {
+       perror("SIOCGIFCONF");
+       (void) close(sock);
+       return (0);
+    }
+    /* Get IP address of each active IP network interface. */
+
+    the_end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+    num_local = 0;
+    for (ifr = ifc.ifc_req; ifr < the_end; ifr++) {
+       if (ifr->ifr_addr.sa_family == AF_INET) {       /* IP net interface */
+           ifreq = *ifr;
+           if (ioctl(sock, SIOCGIFFLAGS, (char *) &ifreq) < 0) {
+               perror("SIOCGIFFLAGS");
+           } else if (ifreq.ifr_flags & IFF_UP) {      /* active interface */
+               if (ioctl(sock, SIOCGIFADDR, (char *) &ifreq) < 0) {
+                   perror("SIOCGIFADDR");
+               } else {
+                   if (num_local >= num_addrs)
+                       if (grow_addrs() == 0)
+                           break;
+                   addrs[num_local++] = ((struct sockaddr_in *)
+                                         & ifreq.ifr_addr)->sin_addr;
+               }
+           }
+       }
+       /* Support for variable-length addresses. */
+#ifdef HAS_SA_LEN
+       ifr = (struct ifreq *) ((caddr_t) ifr
+                     + ifr->ifr_addr.sa_len - sizeof(struct sockaddr));
+#endif
+    }
+    (void) close(sock);
+    return (num_local);
+}
+
+/* from_local - determine whether request comes from the local system */
+
+from_local(addr)
+struct sockaddr_in *addr;
+{
+    int     i;
+
+    if (addrs == 0 && find_local() == 0)
+       syslog(LOG_ERR, "cannot find any active local network interfaces");
+
+    for (i = 0; i < num_local; i++) {
+       if (memcmp((char *) &(addr->sin_addr), (char *) &(addrs[i]),
+                  sizeof(struct in_addr)) == 0)
+           return (TRUE);
+    }
+    return (FALSE);
+}
+
+#ifdef TEST
+
+main()
+{
+    char   *inet_ntoa();
+    int     i;
+
+    find_local();
+    for (i = 0; i < num_local; i++)
+       printf("%s\n", inet_ntoa(addrs[i]));
+}
+
+#endif
diff --git a/get_myaddress.c b/get_myaddress.c
new file mode 100644 (file)
index 0000000..ef5dcf5
--- /dev/null
@@ -0,0 +1,47 @@
+ /*
+  * get_myaddress - alternative version that picks the loopback interface.
+  * 
+  * Author: Wietse Venema (wietse@wzv.win.tue.nl), dept. of Mathematics and
+  * Computing Science, Eindhoven University of Technology, The Netherlands.
+  */
+
+#ifndef lint
+static char sccsid[] = "@(#) get_myaddress.c 1.1 23:00:53";
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+
+#ifndef INADDR_LOOPBACK
+#define INADDR_LOOPBACK ntohl(inet_addr("127.0.0.1"))
+#endif
+
+void    get_myaddress(addrp)
+struct sockaddr_in *addrp;
+{
+    memset((char *) addrp, 0, sizeof(*addrp));
+    addrp->sin_family = AF_INET;
+    addrp->sin_port = htons(PMAPPORT);
+    addrp->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+}
+
+#ifdef TEST
+
+#include <stdio.h>
+
+main(argc, argv)
+int     argc;
+char  **argv;
+{
+    struct sockaddr_in addr;
+
+    get_myaddress(&addr);
+    printf("%s\n", inet_ntoa(addr.sin_addr));
+    exit(0);
+}
+
+#endif
diff --git a/pmap_check.c b/pmap_check.c
new file mode 100644 (file)
index 0000000..265d347
--- /dev/null
@@ -0,0 +1,291 @@
+ /*
+  * pmap_check - additional portmap security.
+  * 
+  * Always reject non-local requests to update the portmapper tables.
+  * 
+  * Refuse to forward mount requests to the nfs mount daemon. Otherwise, the
+  * requests would appear to come from the local system, and nfs export
+  * restrictions could be bypassed.
+  * 
+  * Refuse to forward requests to the nfsd process.
+  * 
+  * Refuse to forward requests to NIS (YP) daemons; The only exception is the
+  * YPPROC_DOMAIN_NONACK broadcast rpc call that is used to establish initial
+  * contact with the NIS server.
+  * 
+  * Always allocate an unprivileged port when forwarding a request.
+  * 
+  * If compiled with -DCHECK_PORT, require that requests to register or
+  * unregister a privileged port come from a privileged port. This makes it
+  * more difficult to replace a critical service by a trojan. Also, require
+  * that requests to set/unset the NFSD port come form a privileged port.
+  * 
+  * If compiled with -DHOSTS_ACCESS, reject requests from hosts that are not
+  * authorized by the /etc/hosts.{allow,deny} files. The local system is
+  * always treated as an authorized host. The access control tables are never
+  * consulted for requests from the local system, and are always consulted
+  * for requests from other hosts. Access control is based on IP addresses
+  * only; attempts to map an address to a host name might cause the
+  * portmapper to hang.
+  * 
+  * Author: Wietse Venema (wietse@wzv.win.tue.nl), dept. of Mathematics and
+  * Computing Science, Eindhoven University of Technology, The Netherlands.
+  */
+
+#ifndef lint
+static char sccsid[] = "@(#) pmap_check.c 1.8 96/07/07 10:49:10";
+#endif
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <sys/signal.h>
+#ifdef SYSV40
+#include <netinet/in.h>
+#include <rpc/rpcent.h>
+#endif
+
+extern char *inet_ntoa();
+
+#include "pmap_check.h"
+
+/* Explicit #defines in case the include files are not available. */
+
+#define NFSPROG                ((u_long) 100003)
+#define MOUNTPROG      ((u_long) 100005)
+#define        YPXPROG         ((u_long) 100069)
+#define YPPROG          ((u_long) 100004)
+#define YPPROC_DOMAIN_NONACK ((u_long) 2)
+#define MOUNTPROC_MNT  ((u_long) 1)
+#define NFS_PORT       2049
+
+static void logit();
+static void toggle_verboselog();
+int     verboselog = 0;
+int     allow_severity = LOG_INFO;
+int     deny_severity = LOG_WARNING;
+
+/* A handful of macros for "readability". */
+
+#define        good_client(a) hosts_ctl("portmap", "", inet_ntoa(a->sin_addr), "")
+
+#define reserved_port(p) (IPPORT_RESERVED/2 < (p) && (p) < IPPORT_RESERVED)
+
+#define unreserved_port(p) (IPPORT_RESERVED <= (p) && (p) != NFS_PORT)
+
+#define        legal_port(a,p) \
+  (reserved_port(ntohs((a)->sin_port)) || unreserved_port(p))
+
+#define log_bad_port(addr, proc, prog) \
+  logit(deny_severity, addr, proc, prog, ": request from unprivileged port")
+
+#define log_bad_host(addr, proc, prog) \
+  logit(deny_severity, addr, proc, prog, ": request from unauthorized host")
+
+#define log_bad_owner(addr, proc, prog) \
+  logit(deny_severity, addr, proc, prog, ": request from non-local host")
+
+#define        log_no_forward(addr, proc, prog) \
+  logit(deny_severity, addr, proc, prog, ": request not forwarded")
+
+#define log_client(addr, proc, prog) \
+  logit(allow_severity, addr, proc, prog, "")
+
+/* check_startup - additional startup code */
+
+void    check_startup()
+{
+
+    /*
+     * Give up root privileges so that we can never allocate a privileged
+     * port when forwarding an rpc request.
+     */
+    if (setuid(1) == -1) {
+       syslog(LOG_ERR, "setuid(1) failed: %m");
+       exit(1);
+    }
+    (void) signal(SIGINT, toggle_verboselog);
+}
+
+/* check_default - additional checks for NULL, DUMP, GETPORT and unknown */
+
+check_default(addr, proc, prog)
+struct sockaddr_in *addr;
+u_long  proc;
+u_long  prog;
+{
+#ifdef HOSTS_ACCESS
+    if (!(from_local(addr) || good_client(addr))) {
+       log_bad_host(addr, proc, prog);
+       return (FALSE);
+    }
+#endif
+    if (verboselog)
+       log_client(addr, proc, prog);
+    return (TRUE);
+}
+
+/* check_privileged_port - additional checks for privileged-port updates */
+
+check_privileged_port(addr, proc, prog, port)
+struct sockaddr_in *addr;
+u_long  proc;
+u_long  prog;
+u_long  port;
+{
+#ifdef CHECK_PORT
+    if (!legal_port(addr, port)) {
+       log_bad_port(addr, proc, prog);
+       return (FALSE);
+    }
+#endif
+    return (TRUE);
+}
+
+/* check_setunset - additional checks for update requests */
+
+#ifdef LOOPBACK_SETUNSET
+
+check_setunset(xprt, ludp_xprt, ltcp_xprt, proc, prog, port)
+SVCXPRT *xprt;
+SVCXPRT *ludp_xprt;
+SVCXPRT *ltcp_xprt;
+u_long  proc;
+u_long  prog;
+u_long  port;
+{
+    struct sockaddr_in *addr = svc_getcaller(xprt);
+
+    if (xprt != ludp_xprt && xprt != ltcp_xprt) {
+#ifdef HOSTS_ACCESS
+       (void) good_client(addr);               /* because of side effects */
+#endif
+       log_bad_owner(addr, proc, prog);
+       return (FALSE);
+    }
+    if (port && !check_privileged_port(addr, proc, prog, port))
+       return (FALSE);
+    if (verboselog)
+       log_client(addr, proc, prog);
+    return (TRUE);
+}
+
+#else
+
+check_setunset(addr, proc, prog, port)
+struct sockaddr_in *addr;
+u_long  proc;
+u_long  prog;
+u_long  port;
+{
+    if (!from_local(addr)) {
+#ifdef HOSTS_ACCESS
+       (void) good_client(addr);               /* because of side effects */
+#endif
+       log_bad_owner(addr, proc, prog);
+       return (FALSE);
+    }
+    if (port && !check_privileged_port(addr, proc, prog, port))
+       return (FALSE);
+    if (verboselog)
+       log_client(addr, proc, prog);
+    return (TRUE);
+}
+
+#endif
+
+/* check_callit - additional checks for forwarded requests */
+
+check_callit(addr, proc, prog, aproc)
+struct sockaddr_in *addr;
+u_long  proc;
+u_long  prog;
+u_long  aproc;
+{
+#ifdef HOSTS_ACCESS
+    if (!(from_local(addr) || good_client(addr))) {
+       log_bad_host(addr, proc, prog);
+       return (FALSE);
+    }
+#endif
+    if (prog == PMAPPROG || prog == NFSPROG || prog == YPXPROG ||
+       (prog == MOUNTPROG && aproc == MOUNTPROC_MNT) ||
+       (prog == YPPROG && aproc != YPPROC_DOMAIN_NONACK)) {
+       log_no_forward(addr, proc, prog);
+       return (FALSE);
+    }
+    if (verboselog)
+       log_client(addr, proc, prog);
+    return (TRUE);
+}
+
+/* toggle_verboselog - toggle verbose logging flag */
+
+static void toggle_verboselog(sig)
+int     sig;
+{
+    (void) signal(sig, toggle_verboselog);
+    verboselog = !verboselog;
+}
+
+/* logit - report events of interest via the syslog daemon */
+
+static void logit(severity, addr, procnum, prognum, text)
+int     severity;
+struct sockaddr_in *addr;
+u_long  procnum;
+u_long  prognum;
+char   *text;
+{
+    char   *procname;
+    char    procbuf[4 * sizeof(u_long)];
+    char   *progname;
+    char    progbuf[4 * sizeof(u_long)];
+    struct rpcent *rpc;
+    struct proc_map {
+       u_long  code;
+       char   *proc;
+    };
+    struct proc_map *procp;
+    static struct proc_map procmap[] = {
+       PMAPPROC_CALLIT, "callit",
+       PMAPPROC_DUMP, "dump",
+       PMAPPROC_GETPORT, "getport",
+       PMAPPROC_NULL, "null",
+       PMAPPROC_SET, "set",
+       PMAPPROC_UNSET, "unset",
+       0, 0,
+    };
+
+    /*
+     * Fork off a process or the portmap daemon might hang while
+     * getrpcbynumber() or syslog() does its thing.
+     */
+
+    if (fork() == 0) {
+
+       /* Try to map program number to name. */
+
+       if (prognum == 0) {
+           progname = "";
+       } else if (rpc = getrpcbynumber((int) prognum)) {
+           progname = rpc->r_name;
+       } else {
+           sprintf(progname = progbuf, "%lu", prognum);
+       }
+
+       /* Try to map procedure number to name. */
+
+       for (procp = procmap; procp->proc && procp->code != procnum; procp++)
+            /* void */ ;
+       if ((procname = procp->proc) == 0)
+           sprintf(procname = procbuf, "%lu", (u_long) procnum);
+
+       /* Write syslog record. */
+
+       syslog(severity, "connect from %s to %s(%s)%s",
+              inet_ntoa(addr->sin_addr), procname, progname, text);
+       exit(0);
+    }
+}
diff --git a/pmap_check.h b/pmap_check.h
new file mode 100644 (file)
index 0000000..2a08b61
--- /dev/null
@@ -0,0 +1,18 @@
+/* @(#) pmap_check.h 1.4 96/07/06 23:06:22 */
+
+extern int from_local();
+extern void check_startup();
+extern int check_default();
+extern int check_setunset();
+extern int check_privileged_port();
+extern int check_callit();
+extern int verboselog;
+extern int allow_severity;
+extern int deny_severity;
+
+#ifdef LOOPBACK_SETUNSET
+#define CHECK_SETUNSET check_setunset
+#else
+#define CHECK_SETUNSET(xprt,ludp,ltcp,proc,prog,port) \
+       check_setunset(svc_getcaller(xprt),proc,prog,port)
+#endif
diff --git a/pmap_dump.c b/pmap_dump.c
new file mode 100644 (file)
index 0000000..2bddcbb
--- /dev/null
@@ -0,0 +1,63 @@
+ /*
+  * pmap_dump - dump portmapper table in format readable by pmap_set
+  * 
+  * Author: Wietse Venema (wietse@wzv.win.tue.nl), dept. of Mathematics and
+  * Computing Science, Eindhoven University of Technology, The Netherlands.
+  */
+
+#ifndef lint
+static char sccsid[] = "@(#) pmap_dump.c 1.1 92/06/11 22:53:15";
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#ifdef SYSV40
+#include <netinet/in.h>
+#include <rpc/rpcent.h>
+#else
+#include <netdb.h>
+#endif
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/pmap_prot.h>
+
+static char *protoname();
+
+main(argc, argv)
+int     argc;
+char  **argv;
+{
+    struct sockaddr_in addr;
+    register struct pmaplist *list;
+    register struct rpcent *rpc;
+
+    get_myaddress(&addr);
+
+    for (list = pmap_getmaps(&addr); list; list = list->pml_next) {
+       rpc = getrpcbynumber((int) list->pml_map.pm_prog);
+       printf("%10lu %4lu %5s %6lu  %s\n",
+              list->pml_map.pm_prog,
+              list->pml_map.pm_vers,
+              protoname(list->pml_map.pm_prot),
+              list->pml_map.pm_port,
+              rpc ? rpc->r_name : "");
+    }
+#undef perror
+    return (fclose(stdout) ? (perror(argv[0]), 1) : 0);
+}
+
+static char *protoname(proto)
+u_long  proto;
+{
+    static char buf[BUFSIZ];
+
+    switch (proto) {
+    case IPPROTO_UDP:
+       return ("udp");
+    case IPPROTO_TCP:
+       return ("tcp");
+    default:
+       sprintf(buf, "%lu", proto);
+       return (buf);
+    }
+}
diff --git a/pmap_set.c b/pmap_set.c
new file mode 100644 (file)
index 0000000..bde07cb
--- /dev/null
@@ -0,0 +1,67 @@
+ /*
+  * pmap_set - set portmapper table from data produced by pmap_dump
+  * 
+  * Author: Wietse Venema (wietse@wzv.win.tue.nl), dept. of Mathematics and
+  * Computing Science, Eindhoven University of Technology, The Netherlands.
+  */
+
+#ifndef lint
+static char sccsid[] = "@(#) pmap_set.c 1.2 96/07/06 23:06:23";
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#ifdef SYSV40
+#include <netinet/in.h>
+#endif
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+
+main(argc, argv)
+int     argc;
+char  **argv;
+{
+    char    buf[BUFSIZ];
+    u_long  prog;
+    u_long  vers;
+    int     prot;
+    unsigned port;
+
+    while (fgets(buf, sizeof(buf), stdin)) {
+       if (parse_line(buf, &prog, &vers, &prot, &port) == 0) {
+           fprintf(stderr, "%s: malformed line: %s", argv[0], buf);
+           return (1);
+       }
+       if (pmap_set(prog, vers, prot, (unsigned short) port) == 0)
+           fprintf(stderr, "not registered: %s", buf);
+    }
+    return (0);
+}
+
+/* parse_line - convert line to numbers */
+
+parse_line(buf, prog, vers, prot, port)
+char   *buf;
+u_long *prog;
+u_long *vers;
+int    *prot;
+unsigned *port;
+{
+    char    proto_name[BUFSIZ];
+
+    if (sscanf(buf, "%lu %lu %s %u", prog, vers, proto_name, port) != 4) {
+       return (0);
+    }
+    if (strcmp(proto_name, "tcp") == 0) {
+       *prot = IPPROTO_TCP;
+       return (1);
+    }
+    if (strcmp(proto_name, "udp") == 0) {
+       *prot = IPPROTO_UDP;
+       return (1);
+    }
+    if (sscanf(proto_name, "%d", prot) == 1) {
+       return (1);
+    }
+    return (0);
+}
diff --git a/portmap.c b/portmap.c
new file mode 100644 (file)
index 0000000..65a26d2
--- /dev/null
+++ b/portmap.c
@@ -0,0 +1,692 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1990 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#) portmap.c 1.6 96/07/06 23:06:23";
+#endif /* not lint */
+
+/*
+@(#)portmap.c  2.3 88/08/11 4.0 RPCSRC
+static char sccsid[] = "@(#)portmap.c 1.32 87/08/06 Copyr 1984 Sun Micro";
+*/
+
+/*
+ * portmap.c, Implements the program,version to port number mapping for
+ * rpc.
+ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part.  Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ * 
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ * 
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ * 
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ * 
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ * 
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <sys/signal.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#ifdef SYSV40
+#include <netinet/in.h>
+#endif
+
+extern char *strerror();
+#include <stdlib.h>
+
+#ifndef LOG_PERROR
+#define LOG_PERROR 0
+#endif
+
+#ifndef LOG_DAEMON
+#define LOG_DAEMON 0
+#endif
+
+/* Older SYSV. */
+#if !defined(SIGCHLD) && defined(SIGCLD)
+#define SIGCHLD      SIGCLD
+#endif
+
+#ifndef svc_getcaller          /* SYSV4 */
+#  define svc_getcaller svc_getrpccaller
+#endif
+
+#ifdef USE_SETPGRP00
+#define setsid() setpgrp(0,0)
+#endif
+
+void reg_service();
+void reap();
+static void callit();
+struct pmaplist *pmaplist;
+int debugging = 0;
+extern int errno;
+
+#include "pmap_check.h"
+
+ /*
+  * How desperate can one be. It is possible to prevent an attacker from
+  * manipulating your portmapper tables from outside with requests that
+  * contain spoofed source address information. The countermeasure is to
+  * force all rpc servers to register and unregister with the portmapper via
+  * the loopback network interface, instead of via the primary network
+  * interface that every host can talk to. For this countermeasure to work it
+  * is necessary to #define LOOPBACK_SETUNSET, to disable source routing in
+  * the kernel, and to modify libc so that get_myaddress() chooses the
+  * loopback interface address.
+  */
+
+#ifdef LOOPBACK_SETUNSET
+static SVCXPRT *ludpxprt, *ltcpxprt;
+static int on = 1;
+#ifndef INADDR_LOOPBACK
+#define INADDR_LOOPBACK ntohl(inet_addr("127.0.0.1"))
+#endif
+#endif
+
+main(argc, argv)
+       int argc;
+       char **argv;
+{
+       SVCXPRT *xprt;
+       int sock, c;
+       struct sockaddr_in addr;
+       int len = sizeof(struct sockaddr_in);
+       register struct pmaplist *pml;
+
+       while ((c = getopt(argc, argv, "dv")) != EOF) {
+               switch (c) {
+
+               case 'd':
+                       debugging = 1;
+                       break;
+
+               case 'v':
+                       verboselog = 1;
+                       break;
+
+               default:
+                       (void) fprintf(stderr, "usage: %s [-dv]\n", argv[0]);
+                       (void) fprintf(stderr, "-d: debugging mode\n");
+                       (void) fprintf(stderr, "-v: verbose logging\n");
+                       exit(1);
+               }
+       }
+
+       if (!debugging && daemon(0, 0)) {
+               (void) fprintf(stderr, "portmap: fork: %s", strerror(errno));
+               exit(1);
+       }
+
+#ifdef LOG_MAIL
+       openlog("portmap", debugging ? LOG_PID | LOG_PERROR : LOG_PID,
+           FACILITY);
+#else
+       openlog("portmap", debugging ? LOG_PID | LOG_PERROR : LOG_PID);
+#endif
+
+       if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+               syslog(LOG_ERR, "cannot create udp socket: %m");
+               exit(1);
+       }
+#ifdef LOOPBACK_SETUNSET
+       setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on);
+#endif
+
+       memset((char *) &addr, 0, sizeof(addr));
+       addr.sin_addr.s_addr = 0;
+       addr.sin_family = AF_INET;
+       addr.sin_port = htons(PMAPPORT);
+       if (bind(sock, (struct sockaddr *)&addr, len) != 0) {
+               syslog(LOG_ERR, "cannot bind udp: %m");
+               exit(1);
+       }
+
+       if ((xprt = svcudp_create(sock)) == (SVCXPRT *)NULL) {
+               syslog(LOG_ERR, "couldn't do udp_create");
+               exit(1);
+       }
+       /* make an entry for ourself */
+       pml = (struct pmaplist *)malloc((u_int)sizeof(struct pmaplist));
+       pml->pml_next = 0;
+       pml->pml_map.pm_prog = PMAPPROG;
+       pml->pml_map.pm_vers = PMAPVERS;
+       pml->pml_map.pm_prot = IPPROTO_UDP;
+       pml->pml_map.pm_port = PMAPPORT;
+       pmaplist = pml;
+
+       if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
+               syslog(LOG_ERR, "cannot create tcp socket: %m");
+               exit(1);
+       }
+#ifdef LOOPBACK_SETUNSET
+       setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on);
+#endif
+       if (bind(sock, (struct sockaddr *)&addr, len) != 0) {
+               syslog(LOG_ERR, "cannot bind udp: %m");
+               exit(1);
+       }
+       if ((xprt = svctcp_create(sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE))
+           == (SVCXPRT *)NULL) {
+               syslog(LOG_ERR, "couldn't do tcp_create");
+               exit(1);
+       }
+       /* make an entry for ourself */
+       pml = (struct pmaplist *)malloc((u_int)sizeof(struct pmaplist));
+       pml->pml_map.pm_prog = PMAPPROG;
+       pml->pml_map.pm_vers = PMAPVERS;
+       pml->pml_map.pm_prot = IPPROTO_TCP;
+       pml->pml_map.pm_port = PMAPPORT;
+       pml->pml_next = pmaplist;
+       pmaplist = pml;
+
+#ifdef LOOPBACK_SETUNSET
+       if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+               syslog(LOG_ERR, "cannot create udp socket: %m");
+               exit(1);
+       }
+       setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on);
+
+       addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+       if (bind(sock, (struct sockaddr *)&addr, len) != 0) {
+               syslog(LOG_ERR, "cannot bind udp: %m");
+               exit(1);
+       }
+
+       if ((ludpxprt = svcudp_create(sock)) == (SVCXPRT *)NULL) {
+               syslog(LOG_ERR, "couldn't do udp_create");
+               exit(1);
+       }
+       if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
+               syslog(LOG_ERR, "cannot create tcp socket: %m");
+               exit(1);
+       }
+       setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on);
+       if (bind(sock, (struct sockaddr *)&addr, len) != 0) {
+               syslog(LOG_ERR, "cannot bind tcp: %m");
+               exit(1);
+       }
+       if ((ltcpxprt = svctcp_create(sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE))
+           == (SVCXPRT *)NULL) {
+               syslog(LOG_ERR, "couldn't do tcp_create");
+               exit(1);
+       }
+#endif
+
+       (void)svc_register(xprt, PMAPPROG, PMAPVERS, reg_service, FALSE);
+
+       /* additional initializations */
+       check_startup();
+#ifdef IGNORE_SIGCHLD                  /* Lionel Cons <cons@dxcern.cern.ch> */
+       (void)signal(SIGCHLD, SIG_IGN);
+#else
+       (void)signal(SIGCHLD, reap);
+#endif
+       svc_run();
+       syslog(LOG_ERR, "run_svc returned unexpectedly");
+       abort();
+}
+
+#ifndef lint
+/* need to override perror calls in rpc library */
+void
+perror(what)
+       const char *what;
+{
+
+       syslog(LOG_ERR, "%s: %m", what);
+}
+#endif
+
+static struct pmaplist *
+find_service(prog, vers, prot)
+       u_long prog, vers, prot;
+{
+       register struct pmaplist *hit = NULL;
+       register struct pmaplist *pml;
+
+       for (pml = pmaplist; pml != NULL; pml = pml->pml_next) {
+               if ((pml->pml_map.pm_prog != prog) ||
+                       (pml->pml_map.pm_prot != prot))
+                       continue;
+               hit = pml;
+               if (pml->pml_map.pm_vers == vers)
+                   break;
+       }
+       return (hit);
+}
+
+/* 
+ * 1 OK, 0 not
+ */
+void
+reg_service(rqstp, xprt)
+       struct svc_req *rqstp;
+       SVCXPRT *xprt;
+{
+       struct pmap reg;
+       struct pmaplist *pml, *prevpml, *fnd;
+       int ans, port;
+       caddr_t t;
+       
+       /*
+        * Later wrappers change the logging severity on the fly. Reset to
+        * defaults before handling the next request.
+        */
+       allow_severity = LOG_INFO;
+       deny_severity = LOG_WARNING;
+
+       if (debugging)
+               (void) fprintf(stderr, "server: about do a switch\n");
+       switch (rqstp->rq_proc) {
+
+       case PMAPPROC_NULL:
+               /*
+                * Null proc call
+                */
+               /* remote host authorization check */
+               check_default(svc_getcaller(xprt), rqstp->rq_proc, (u_long) 0);
+               if (!svc_sendreply(xprt, xdr_void, (caddr_t)0) && debugging) {
+                       abort();
+               }
+               break;
+
+       case PMAPPROC_SET:
+               /*
+                * Set a program,version to port mapping
+                */
+               if (!svc_getargs(xprt, xdr_pmap, &reg))
+                       svcerr_decode(xprt);
+               else {
+                       /* reject non-local requests, protect priv. ports */
+                       if (!CHECK_SETUNSET(xprt, ludpxprt, ltcpxprt,
+                           rqstp->rq_proc, reg.pm_prog, reg.pm_port)) {
+                               ans = 0;
+                               goto done;
+                       } 
+                       /*
+                        * check to see if already used
+                        * find_service returns a hit even if
+                        * the versions don't match, so check for it
+                        */
+                       fnd = find_service(reg.pm_prog, reg.pm_vers, reg.pm_prot);
+                       if (fnd && fnd->pml_map.pm_vers == reg.pm_vers) {
+                               if (fnd->pml_map.pm_port == reg.pm_port) {
+                                       ans = 1;
+                                       goto done;
+                               }
+                               else {
+                                       ans = 0;
+                                       goto done;
+                               }
+                       } else {
+                               /* 
+                                * add to END of list
+                                */
+                               pml = (struct pmaplist *)
+                                   malloc((u_int)sizeof(struct pmaplist));
+                               pml->pml_map = reg;
+                               pml->pml_next = 0;
+                               if (pmaplist == 0) {
+                                       pmaplist = pml;
+                               } else {
+                                       for (fnd= pmaplist; fnd->pml_next != 0;
+                                           fnd = fnd->pml_next);
+                                       fnd->pml_next = pml;
+                               }
+                               ans = 1;
+                       }
+               done:
+                       if ((!svc_sendreply(xprt, xdr_int, (caddr_t)&ans)) &&
+                           debugging) {
+                               (void) fprintf(stderr, "svc_sendreply\n");
+                               abort();
+                       }
+               }
+               break;
+
+       case PMAPPROC_UNSET:
+               /*
+                * Remove a program,version to port mapping.
+                */
+               if (!svc_getargs(xprt, xdr_pmap, &reg))
+                       svcerr_decode(xprt);
+               else {
+                       ans = 0;
+                       /* reject non-local requests */
+                       if (!CHECK_SETUNSET(xprt, ludpxprt, ltcpxprt,
+                           rqstp->rq_proc, reg.pm_prog, (u_long) 0))
+                               goto done;
+                       for (prevpml = NULL, pml = pmaplist; pml != NULL; ) {
+                               if ((pml->pml_map.pm_prog != reg.pm_prog) ||
+                                       (pml->pml_map.pm_vers != reg.pm_vers)) {
+                                       /* both pml & prevpml move forwards */
+                                       prevpml = pml;
+                                       pml = pml->pml_next;
+                                       continue;
+                               }
+                               /* found it; pml moves forward, prevpml stays */
+                               /* privileged port check */
+                               if (!check_privileged_port(svc_getcaller(xprt), 
+                                   rqstp->rq_proc, 
+                                   reg.pm_prog, 
+                                   pml->pml_map.pm_port)) {
+                                       ans = 0;
+                                       break;
+                               }
+                               ans = 1;
+                               t = (caddr_t)pml;
+                               pml = pml->pml_next;
+                               if (prevpml == NULL)
+                                       pmaplist = pml;
+                               else
+                                       prevpml->pml_next = pml;
+                               free(t);
+                       }
+                       if ((!svc_sendreply(xprt, xdr_int, (caddr_t)&ans)) &&
+                           debugging) {
+                               (void) fprintf(stderr, "svc_sendreply\n");
+                               abort();
+                       }
+               }
+               break;
+
+       case PMAPPROC_GETPORT:
+               /*
+                * Lookup the mapping for a program,version and return its port
+                */
+               if (!svc_getargs(xprt, xdr_pmap, &reg))
+                       svcerr_decode(xprt);
+               else {
+                       /* remote host authorization check */
+                       if (!check_default(svc_getcaller(xprt), 
+                           rqstp->rq_proc, 
+                           reg.pm_prog)) {
+                               ans = 0;
+                               goto done;
+                       }
+                       fnd = find_service(reg.pm_prog, reg.pm_vers, reg.pm_prot);
+                       if (fnd)
+                               port = fnd->pml_map.pm_port;
+                       else
+                               port = 0;
+                       if ((!svc_sendreply(xprt, xdr_int, (caddr_t)&port)) &&
+                           debugging) {
+                               (void) fprintf(stderr, "svc_sendreply\n");
+                               abort();
+                       }
+               }
+               break;
+
+       case PMAPPROC_DUMP:
+               /*
+                * Return the current set of mapped program,version
+                */
+               if (!svc_getargs(xprt, xdr_void, NULL))
+                       svcerr_decode(xprt);
+               else {
+                       /* remote host authorization check */
+                       struct pmaplist *p;
+                       if (!check_default(svc_getcaller(xprt), 
+                           rqstp->rq_proc, (u_long) 0)) {
+                               p = 0;  /* send empty list */
+                       } else {
+                               p = pmaplist;
+                       }
+                       if ((!svc_sendreply(xprt, xdr_pmaplist,
+                           (caddr_t)&p)) && debugging) {
+                               (void) fprintf(stderr, "svc_sendreply\n");
+                               abort();
+                       }
+               }
+               break;
+
+       case PMAPPROC_CALLIT:
+               /*
+                * Calls a procedure on the local machine.  If the requested
+                * procedure is not registered this procedure does not return
+                * error information!!
+                * This procedure is only supported on rpc/udp and calls via 
+                * rpc/udp.  It passes null authentication parameters.
+                */
+               callit(rqstp, xprt);
+               break;
+
+       default:
+               /* remote host authorization check */
+               check_default(svc_getcaller(xprt), rqstp->rq_proc, (u_long) 0);
+               svcerr_noproc(xprt);
+               break;
+       }
+}
+
+
+/*
+ * Stuff for the rmtcall service
+ */
+#define ARGSIZE 9000
+
+struct encap_parms {
+       u_int arglen;
+       char *args;
+};
+
+static bool_t
+xdr_encap_parms(xdrs, epp)
+       XDR *xdrs;
+       struct encap_parms *epp;
+{
+
+       return (xdr_bytes(xdrs, &(epp->args), &(epp->arglen), ARGSIZE));
+}
+
+struct rmtcallargs {
+       u_long  rmt_prog;
+       u_long  rmt_vers;
+       u_long  rmt_port;
+       u_long  rmt_proc;
+       struct encap_parms rmt_args;
+};
+
+static bool_t
+xdr_rmtcall_args(xdrs, cap)
+       register XDR *xdrs;
+       register struct rmtcallargs *cap;
+{
+
+       /* does not get a port number */
+       if (xdr_u_long(xdrs, &(cap->rmt_prog)) &&
+           xdr_u_long(xdrs, &(cap->rmt_vers)) &&
+           xdr_u_long(xdrs, &(cap->rmt_proc))) {
+               return (xdr_encap_parms(xdrs, &(cap->rmt_args)));
+       }
+       return (FALSE);
+}
+
+static bool_t
+xdr_rmtcall_result(xdrs, cap)
+       register XDR *xdrs;
+       register struct rmtcallargs *cap;
+{
+       if (xdr_u_long(xdrs, &(cap->rmt_port)))
+               return (xdr_encap_parms(xdrs, &(cap->rmt_args)));
+       return (FALSE);
+}
+
+/*
+ * only worries about the struct encap_parms part of struct rmtcallargs.
+ * The arglen must already be set!!
+ */
+static bool_t
+xdr_opaque_parms(xdrs, cap)
+       XDR *xdrs;
+       struct rmtcallargs *cap;
+{
+
+       return (xdr_opaque(xdrs, cap->rmt_args.args, cap->rmt_args.arglen));
+}
+
+/*
+ * This routine finds and sets the length of incoming opaque paraters
+ * and then calls xdr_opaque_parms.
+ */
+static bool_t
+xdr_len_opaque_parms(xdrs, cap)
+       register XDR *xdrs;
+       struct rmtcallargs *cap;
+{
+       register u_int beginpos, lowpos, highpos, currpos, pos;
+
+       beginpos = lowpos = pos = xdr_getpos(xdrs);
+       highpos = lowpos + ARGSIZE;
+       while ((int)(highpos - lowpos) >= 0) {
+               currpos = (lowpos + highpos) / 2;
+               if (xdr_setpos(xdrs, currpos)) {
+                       pos = currpos;
+                       lowpos = currpos + 1;
+               } else {
+                       highpos = currpos - 1;
+               }
+       }
+       xdr_setpos(xdrs, beginpos);
+       cap->rmt_args.arglen = pos - beginpos;
+       return (xdr_opaque_parms(xdrs, cap));
+}
+
+/*
+ * Call a remote procedure service
+ * This procedure is very quiet when things go wrong.
+ * The proc is written to support broadcast rpc.  In the broadcast case,
+ * a machine should shut-up instead of complain, less the requestor be
+ * overrun with complaints at the expense of not hearing a valid reply ...
+ *
+ * This now forks so that the program & process that it calls can call 
+ * back to the portmapper.
+ */
+static void
+callit(rqstp, xprt)
+       struct svc_req *rqstp;
+       SVCXPRT *xprt;
+{
+       struct rmtcallargs a;
+       struct pmaplist *pml;
+       u_short port;
+       struct sockaddr_in me;
+       int pid, so = -1;
+       CLIENT *client;
+       struct authunix_parms *au = (struct authunix_parms *)rqstp->rq_clntcred;
+       struct timeval timeout;
+       char buf[ARGSIZE];
+
+       timeout.tv_sec = 5;
+       timeout.tv_usec = 0;
+       a.rmt_args.args = buf;
+       if (!svc_getargs(xprt, xdr_rmtcall_args, &a))
+               return;
+       /* host and service access control */
+       if (!check_callit(svc_getcaller(xprt), 
+           rqstp->rq_proc, a.rmt_prog, a.rmt_proc))
+               return;
+       if ((pml = find_service(a.rmt_prog, a.rmt_vers,
+           (u_long)IPPROTO_UDP)) == NULL)
+               return;
+       /*
+        * fork a child to do the work.  Parent immediately returns.
+        * Child exits upon completion.
+        */
+       if ((pid = fork()) != 0) {
+               if (pid < 0)
+                       syslog(LOG_ERR, "CALLIT (prog %lu): fork: %m",
+                           a.rmt_prog);
+               return;
+       }
+       port = pml->pml_map.pm_port;
+       get_myaddress(&me);
+       me.sin_port = htons(port);
+       client = clntudp_create(&me, a.rmt_prog, a.rmt_vers, timeout, &so);
+       if (client != (CLIENT *)NULL) {
+               if (rqstp->rq_cred.oa_flavor == AUTH_UNIX) {
+                       client->cl_auth = authunix_create(au->aup_machname,
+                          au->aup_uid, au->aup_gid, au->aup_len, au->aup_gids);
+               }
+               a.rmt_port = (u_long)port;
+               if (clnt_call(client, a.rmt_proc, xdr_opaque_parms, &a,
+                   xdr_len_opaque_parms, &a, timeout) == RPC_SUCCESS) {
+                       svc_sendreply(xprt, xdr_rmtcall_result, (caddr_t)&a);
+               }
+               AUTH_DESTROY(client->cl_auth);
+               clnt_destroy(client);
+       }
+       (void)close(so);
+       exit(0);
+}
+
+void
+reap()
+{
+       while (wait3((int *)NULL, WNOHANG, (struct rusage *)NULL) > 0);
+}
diff --git a/strerror.c b/strerror.c
new file mode 100644 (file)
index 0000000..5aea7e7
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strerror.c 5.6 (Berkeley) 5/4/91";
+#endif /* LIBC_SCCS and not lint */
+
+#include <string.h>
+
+char *
+strerror(num)
+       int num;
+{
+       extern int sys_nerr;
+       extern char *sys_errlist[];
+#define        UPREFIX "Unknown error: "
+       static char ebuf[40] = UPREFIX;         /* 64-bit number + slop */
+       register unsigned int errnum;
+       register char *p, *t;
+       char tmp[40];
+
+       errnum = num;                           /* convert to unsigned */
+       if (errnum < sys_nerr)
+               return(sys_errlist[errnum]);
+
+       /* Do this by hand, so we don't include stdio(3). */
+       t = tmp;
+       do {
+               *t++ = "0123456789"[errnum % 10];
+       } while (errnum /= 10);
+       for (p = ebuf + sizeof(UPREFIX) - 1;;) {
+               *p++ = *--t;
+               if (t <= tmp)
+                       break;
+       }
+       return(ebuf);
+}