Add four big changes:

* portmap is now tcp-wrapped (i.e. obeys hosts.{allow,deny})
	  both for lookups (as `portmap') and for forwarded calls to
	  specific services.

	* the new -l flag, analagous to inetd -l, logs all connections
	  to portmap.

	* the new -s flag causes portmap to suid to the user daemon
	  after binding it's port, so that outgoing connections do
	  not come from privileged ports.  This prevents users from
	  using portmap to get a free privileged port.

	* portmap now _only_ accepts SETs and UNSETs on the loopback
	  interface.  In the past, anyone in the world could do all
	  sorts of nasty things to your portmap tables.  Note that
	  our libc already_only_ uses the loopback interface to
	  register rpc ports.

This work is modeled after/partially taken from Wietse Venema's tcp-
wrapped version of the BSD 4.3 portmap.  It has benefitted greatly from my
discussions with Luke, Matt and many others.
This commit is contained in:
jwise 1999-01-11 20:51:09 +00:00
parent 33b1cd5328
commit 2badef4d79
3 changed files with 242 additions and 34 deletions

View File

@ -1,7 +1,11 @@
# $NetBSD: Makefile,v 1.8 1997/10/17 05:40:16 mrg Exp $
# $NetBSD: Makefile,v 1.9 1999/01/11 20:51:09 jwise Exp $
# from: @(#)Makefile 8.1 (Berkeley) 6/6/93
PROG= portmap
MAN= portmap.8
CPPFLAGS+=-DLIBWRAP
LDADD+= -lwrap
DPADD+= ${LIBWRAP}
.include <bsd.prog.mk>

View File

@ -1,4 +1,4 @@
.\" $NetBSD: portmap.8,v 1.4 1997/10/17 12:01:19 lukem Exp $
.\" $NetBSD: portmap.8,v 1.5 1999/01/11 20:51:09 jwise Exp $
.\"
.\" Copyright (c) 1987 Sun Microsystems
.\" Copyright (c) 1990, 1991, 1993
@ -40,13 +40,13 @@
.Sh NAME
.Nm portmap
.Nd
.Tn DARPA
port to
.Tn RPC
program number mapper
program number to
.Tn DARPA
port mapper
.Sh SYNOPSIS
.Nm
.Op Fl d
.Op Fl dls
.Sh DESCRIPTION
.Nm
is a server that converts
@ -88,19 +88,47 @@ like any other daemon.
then logs errors using
.Xr syslog 3 .
.Pp
.Nm
uses
.Xr libwrap
style access control (the
.Pa /etc/hosts.allow
and
.Pa /etc/hosts.deny
files)
to control access to the portmapper itself and control forwarding
of requests. This prevents clients from using
.Nm
to circumvent host-based services in individual services.
.Pp
Option available:
.Bl -tag -width Ds
.It Fl d
(debug) prevents
prevents
.Nm
from running as a daemon,
and causes errors and debugging information
to be printed to the standard error output.
.It Fl l
Turns on libwrap connection logging.
.It Fl s
causes
.Nm
to change to the user daemon as soon as possible.
This causes
.Nm
to use non-privileged ports for outgoing connections, preventing non-privileged
clients from using
.Nm
to connect to services from a privileged port.
.El
.Sh SEE ALSO
.Xr inetd.conf 5 ,
.Xr rpcinfo 8 ,
.Xr inetd 8
.Xr inetd 8 ,
.Xr syslog 3 ,
.Xr hosts_access 3 ,
.Xr hosts_options 3
.Sh BUGS
If
.Nm
@ -109,4 +137,8 @@ crashes, all servers must be restarted.
The
.Nm
command appeared in
.Bx 4.3
.Bx 4.3 .
The security features documented herein derive from work by Wietse Venema
at Eindhoven University of Technology, The Netherlands, and first appeared
in
.Nx 1.4 .

View File

@ -1,4 +1,4 @@
/* $NetBSD: portmap.c,v 1.13 1998/02/10 07:04:21 lukem Exp $ */
/* $NetBSD: portmap.c,v 1.14 1999/01/11 20:51:09 jwise Exp $ */
/*-
* Copyright (c) 1990, 1993
@ -44,7 +44,7 @@ __COPYRIGHT(
#if 0
static char sccsid[] = "@(#)portmap.c 8.1 (Berkeley) 6/6/93";
#else
__RCSID("$NetBSD: portmap.c,v 1.13 1998/02/10 07:04:21 lukem Exp $");
__RCSID("$NetBSD: portmap.c,v 1.14 1999/01/11 20:51:09 jwise Exp $");
#endif
#endif /* not lint */
@ -87,22 +87,45 @@ static char sccsid[] = "@(#)portmap.c 1.32 87/08/06 Copyr 1984 Sun Micro";
* Mountain View, California 94043
*/
/* who to suid to if -s is given */
#define RUN_AS "daemon"
#include <sys/types.h>
#include <sys/time.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
#include <rpc/rpc.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <rpc/rpc.h>
#include <netdb.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <netdb.h>
#ifdef LIBWRAP
# include <tcpd.h>
#ifndef LIBWRAP_ALLOW_FACILITY
# define LIBWRAP_ALLOW_FACILITY LOG_AUTH
#endif
#ifndef LIBWRAP_ALLOW_SEVERITY
# define LIBWRAP_ALLOW_SEVERITY LOG_INFO
#endif
#ifndef LIBWRAP_DENY_FACILITY
# define LIBWRAP_DENY_FACILITY LOG_AUTH
#endif
#ifndef LIBWRAP_DENY_SEVERITY
# define LIBWRAP_DENY_SEVERITY LOG_WARNING
#endif
int allow_severity = LIBWRAP_ALLOW_FACILITY|LIBWRAP_ALLOW_SEVERITY;
int deny_severity = LIBWRAP_DENY_FACILITY|LIBWRAP_DENY_SEVERITY;
#endif
struct encap_parms {
u_int arglen;
@ -121,15 +144,22 @@ static void callit __P((struct svc_req *, SVCXPRT *));
static struct pmaplist *find_service __P((u_long, u_long, u_long));
int main __P((int, char *[]));
void reap __P((int));
void toggle_verboselog __P((int));
void reg_service __P((struct svc_req *, SVCXPRT *));
static bool_t xdr_encap_parms __P((XDR *, struct encap_parms *));
static bool_t xdr_len_opaque_parms __P((XDR *, struct rmtcallargs *));
static bool_t xdr_opaque_parms __P((XDR *, struct rmtcallargs *));
static bool_t xdr_rmtcall_args __P((XDR *, struct rmtcallargs *));
static bool_t xdr_rmtcall_result __P((XDR *, struct rmtcallargs *));
void logit __P((int, struct sockaddr_in *, u_long, u_long, char *));
int check_access __P((struct sockaddr_in *, u_long, u_long));
int is_loopback __P((struct sockaddr_in *));
struct pmaplist *pmaplist;
int debugging = 0;
int runasdaemon = 0;
int verboselog = 0;
int
main(argc, argv)
@ -142,13 +172,21 @@ main(argc, argv)
int len = sizeof(struct sockaddr_in);
struct pmaplist *pml;
while ((c = getopt(argc, argv, "d")) != -1) {
while ((c = getopt(argc, argv, "dls")) != -1) {
switch (c) {
case 'd':
debugging = 1;
break;
case 'l':
verboselog = 1;
break;
case 's':
runasdaemon = 1;
break;
default:
(void) fprintf(stderr, "usage: %s [-d]\n", argv[0]);
exit(1);
@ -215,22 +253,26 @@ main(argc, argv)
(void)svc_register(xprt, PMAPPROG, PMAPVERS, reg_service, FALSE);
(void)signal(SIGCHLD, reap);
(void) signal(SIGUSR1, toggle_verboselog);
if (runasdaemon) {
struct passwd *p;
if((p = getpwnam(RUN_AS)) == NULL) {
syslog(LOG_ERR, "cannot get uid of daemon: %m");
exit(1);
}
if (setuid(p->pw_uid) == -1) {
syslog(LOG_ERR, "setuid to daemon failed: %m");
exit(1);
}
}
svc_run();
syslog(LOG_ERR, "svc_run 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;
@ -267,10 +309,11 @@ reg_service(rqstp, xprt)
/*
* Null proc call
*/
if (!svc_sendreply(xprt, xdr_void, (caddr_t)0) && debugging) {
abort();
}
break;
check_access(svc_getcaller(xprt), rqstp->rq_proc, (u_long) 0);
if (!svc_sendreply(xprt, xdr_void, (caddr_t)0) && debugging) {
abort();
}
break;
case PMAPPROC_SET:
/*
@ -279,6 +322,13 @@ reg_service(rqstp, xprt)
if (!svc_getargs(xprt, xdr_pmap, (caddr_t)&reg))
svcerr_decode(xprt);
else {
if (verboselog)
logit(allow_severity, svc_getcaller(xprt), rqstp->rq_proc, reg.pm_prog, "");
if(!is_loopback(svc_getcaller(xprt))) {
ans = 0;
goto done;
}
/*
* check to see if already used
* find_service returns a hit even if
@ -329,6 +379,12 @@ reg_service(rqstp, xprt)
svcerr_decode(xprt);
else {
ans = 0;
if (verboselog)
logit(allow_severity, svc_getcaller(xprt), rqstp->rq_proc, reg.pm_prog, "");
if(!is_loopback(svc_getcaller(xprt))) {
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)) {
@ -338,7 +394,7 @@ reg_service(rqstp, xprt)
continue;
}
/* found it; pml moves forward, prevpml stays */
ans = 1;
ans = 1;
t = (caddr_t)pml;
pml = pml->pml_next;
if (prevpml == NULL)
@ -362,14 +418,18 @@ reg_service(rqstp, xprt)
if (!svc_getargs(xprt, xdr_pmap, (caddr_t)&reg))
svcerr_decode(xprt);
else {
if (!check_access(svc_getcaller(xprt), rqstp->rq_proc, reg.pm_prog)) {
ans = 0;
/* this is a little bogus -- done is up above, under another case */
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_long, (caddr_t)&port)) &&
debugging) {
if ((!svc_sendreply(xprt, xdr_long, (caddr_t)&port)) && debugging) {
(void) fprintf(stderr, "svc_sendreply\n");
abort();
}
@ -383,8 +443,14 @@ reg_service(rqstp, xprt)
if (!svc_getargs(xprt, xdr_void, NULL))
svcerr_decode(xprt);
else {
if ((!svc_sendreply(xprt, xdr_pmaplist,
(caddr_t)&pmaplist)) && debugging) {
struct pmaplist *p;
if (!check_access(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();
}
@ -403,6 +469,7 @@ reg_service(rqstp, xprt)
break;
default:
check_access(svc_getcaller(xprt), rqstp->rq_proc, (u_long) 0);
svcerr_noproc(xprt);
break;
}
@ -518,6 +585,9 @@ callit(rqstp, xprt)
a.rmt_args.args = buf;
if (!svc_getargs(xprt, xdr_rmtcall_args, (caddr_t)&a))
return;
/* host and service access control */
if (!check_access(svc_getcaller(xprt), rqstp->rq_proc, a.rmt_prog))
return;
if ((pml = find_service(a.rmt_prog, a.rmt_vers,
(u_long)IPPROTO_UDP)) == NULL)
return;
@ -570,3 +640,105 @@ reap(dummy)
;
errno = save_errno;
}
void
toggle_verboselog(dummy)
int dummy;
{
verboselog = !verboselog;
}
int
check_access(addr, proc, prog)
struct sockaddr_in *addr;
u_long proc;
u_long prog;
{
#ifdef LIBWRAP
struct request_info req;
#endif
#ifdef LIBWRAP
request_init(&req, RQ_DAEMON, "portmap", RQ_CLIENT_SIN, addr, 0);
fromhost(&req);
if(!hosts_access(&req)) {
logit(deny_severity, addr, proc, prog, ": request from unauthorized host");
return 0;
}
#endif
if (verboselog)
logit(allow_severity, addr, proc, prog, "");
return 1;
}
int
is_loopback(addr)
struct sockaddr_in *addr;
{
if (addr->sin_addr.s_addr != INADDR_LOOPBACK)
return 1;
return 0;
}
/* logit - report events of interest via the syslog daemon */
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 {
snprintf(progname = progbuf, sizeof(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)
snprintf(procname = procbuf, sizeof(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);
}
}