From: Neil Brown Date: Fri, 20 Apr 2007 00:35:59 +0000 (+1000) Subject: Initial checkin of portmap_beta5 X-Git-Tag: portmap_5beta^0 X-Git-Url: http://git.neil.brown.name/?a=commitdiff_plain;h=7f5d4a4ea6e6fc88d325744262d7101712032174;p=portmap.git Initial checkin of portmap_beta5 Both the current Debian and SuSE distros have identical .tar.gz containing these files. --- 7f5d4a4ea6e6fc88d325744262d7101712032174 diff --git a/BLURB b/BLURB new file mode 100644 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 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 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 index 0000000..26b83b9 --- /dev/null +++ b/Makefile.shlib @@ -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 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 + +/* 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 index 0000000..3f64405 --- /dev/null +++ b/from_local.c @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..ef5dcf5 --- /dev/null +++ b/get_myaddress.c @@ -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 +#include +#include +#include +#include +#include + +#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 + +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 index 0000000..265d347 --- /dev/null +++ b/pmap_check.c @@ -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 +#include +#include +#include +#include +#ifdef SYSV40 +#include +#include +#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 index 0000000..2a08b61 --- /dev/null +++ b/pmap_check.h @@ -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 index 0000000..2bddcbb --- /dev/null +++ b/pmap_dump.c @@ -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 +#include +#ifdef SYSV40 +#include +#include +#else +#include +#endif +#include +#include +#include + +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 index 0000000..bde07cb --- /dev/null +++ b/pmap_set.c @@ -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 +#include +#ifdef SYSV40 +#include +#endif +#include +#include + +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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef SYSV40 +#include +#endif + +extern char *strerror(); +#include + +#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 */ + (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, ®)) + 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, ®)) + 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, ®)) + 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 index 0000000..5aea7e7 --- /dev/null +++ b/strerror.c @@ -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 + +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); +}