Import NetBSD libwrap

Change-Id: Ib8b979fb9a96c2048b8ea93038944c743048bff4
This commit is contained in:
David van Moolenbroek 2017-02-17 11:40:49 +00:00
parent c2a43b2200
commit f1fab66e7d
29 changed files with 3730 additions and 1 deletions

View File

@ -936,6 +936,9 @@
./usr/lib/libutil.so.7 minix-base ./usr/lib/libutil.so.7 minix-base
./usr/lib/libutil.so.7.21 minix-base obsolete ./usr/lib/libutil.so.7.21 minix-base obsolete
./usr/lib/libutil.so.7.23 minix-base ./usr/lib/libutil.so.7.23 minix-base
./usr/lib/libwrap.so minix-base
./usr/lib/libwrap.so.1 minix-base
./usr/lib/libwrap.so.1.0 minix-base
./usr/lib/libz.so minix-base ./usr/lib/libz.so minix-base
./usr/lib/libz.so.1 minix-base ./usr/lib/libz.so.1 minix-base
./usr/lib/libz.so.1.0 minix-base ./usr/lib/libz.so.1.0 minix-base

View File

@ -1789,6 +1789,7 @@
./usr/include/sysexits.h minix-comp ./usr/include/sysexits.h minix-comp
./usr/include/syslog.h minix-comp ./usr/include/syslog.h minix-comp
./usr/include/tar.h minix-comp ./usr/include/tar.h minix-comp
./usr/include/tcpd.h minix-comp
./usr/include/term.h minix-comp ./usr/include/term.h minix-comp
./usr/include/termcap.h minix-comp ./usr/include/termcap.h minix-comp
./usr/include/termios.h minix-comp ./usr/include/termios.h minix-comp
@ -1944,6 +1945,7 @@
./usr/lib/bc/libvboxfs.a minix-comp bitcode ./usr/lib/bc/libvboxfs.a minix-comp bitcode
./usr/lib/bc/libvirtio.a minix-comp bitcode ./usr/lib/bc/libvirtio.a minix-comp bitcode
./usr/lib/bc/libvtreefs.a minix-comp bitcode ./usr/lib/bc/libvtreefs.a minix-comp bitcode
./usr/lib/bc/libwrap.a minix-comp bitcode
./usr/lib/bc/libz.a minix-comp bitcode ./usr/lib/bc/libz.a minix-comp bitcode
./usr/lib/i18n/bc minix-comp bitcode ./usr/lib/i18n/bc minix-comp bitcode
./usr/lib/i18n/bc/libBIG5.a minix-comp bitcode ./usr/lib/i18n/bc/libBIG5.a minix-comp bitcode
@ -2117,6 +2119,8 @@
./usr/lib/libutil_pic.a minix-comp ./usr/lib/libutil_pic.a minix-comp
./usr/lib/libvtreefs.a minix-comp ./usr/lib/libvtreefs.a minix-comp
./usr/lib/libvtreefs_pic.a minix-comp ./usr/lib/libvtreefs_pic.a minix-comp
./usr/lib/libwrap.a minix-comp
./usr/lib/libwrap_pic.a minix-comp
./usr/lib/libz.a minix-comp ./usr/lib/libz.a minix-comp
./usr/lib/libz_pic.a minix-comp ./usr/lib/libz_pic.a minix-comp
./usr/libexec/cc1 minix-comp gcccmds ./usr/libexec/cc1 minix-comp gcccmds

View File

@ -110,6 +110,7 @@
./usr/lib/libusb_g.a minix-debug debuglib ./usr/lib/libusb_g.a minix-debug debuglib
./usr/lib/libutil_g.a minix-debug debuglib ./usr/lib/libutil_g.a minix-debug debuglib
./usr/lib/libvtreefs_g.a minix-debug debuglib ./usr/lib/libvtreefs_g.a minix-debug debuglib
./usr/lib/libwrap_g.a minix-debug debuglib
./usr/lib/libz_g.a minix-debug debuglib ./usr/lib/libz_g.a minix-debug debuglib
./usr/libdata minix-debug ./usr/libdata minix-debug
./usr/libdata/debug minix-debug ./usr/libdata/debug minix-debug
@ -567,6 +568,7 @@
./usr/libdata/debug/usr/lib/libsys.so.0.0.debug minix-debug debug ./usr/libdata/debug/usr/lib/libsys.so.0.0.debug minix-debug debug
./usr/libdata/debug/usr/lib/libterminfo.so.1.0.debug minix-debug debug ./usr/libdata/debug/usr/lib/libterminfo.so.1.0.debug minix-debug debug
./usr/libdata/debug/usr/lib/libutil.so.7.23.debug minix-debug debug ./usr/libdata/debug/usr/lib/libutil.so.7.23.debug minix-debug debug
./usr/libdata/debug/usr/lib/libwrap.so.1.0.debug minix-debug debug
./usr/libdata/debug/usr/lib/libz.so.1.0.debug minix-debug debug ./usr/libdata/debug/usr/lib/libz.so.1.0.debug minix-debug debug
./usr/libdata/debug/usr/libexec minix-debug ./usr/libdata/debug/usr/libexec minix-debug
./usr/libdata/debug/usr/libexec/atf-check.debug minix-debug debug ./usr/libdata/debug/usr/libexec/atf-check.debug minix-debug debug

View File

@ -1920,6 +1920,8 @@
./usr/man/man3/history_init.3 minix-man ./usr/man/man3/history_init.3 minix-man
./usr/man/man3/hline.3 minix-man ./usr/man/man3/hline.3 minix-man
./usr/man/man3/hostalias.3 minix-man ./usr/man/man3/hostalias.3 minix-man
./usr/man/man3/hosts_access.3 minix-man
./usr/man/man3/hosts_ctl.3 minix-man
./usr/man/man3/hsearch.3 minix-man ./usr/man/man3/hsearch.3 minix-man
./usr/man/man3/hsearch_r.3 minix-man ./usr/man/man3/hsearch_r.3 minix-man
./usr/man/man3/hstrerror.3 minix-man ./usr/man/man3/hstrerror.3 minix-man
@ -2671,6 +2673,8 @@
./usr/man/man3/remque.3 minix-man ./usr/man/man3/remque.3 minix-man
./usr/man/man3/remquo.3 minix-man ./usr/man/man3/remquo.3 minix-man
./usr/man/man3/remquof.3 minix-man ./usr/man/man3/remquof.3 minix-man
./usr/man/man3/request_init.3 minix-man
./usr/man/man3/request_set.3 minix-man
./usr/man/man3/res_close.3 minix-man ./usr/man/man3/res_close.3 minix-man
./usr/man/man3/res_findzonecut.3 minix-man ./usr/man/man3/res_findzonecut.3 minix-man
./usr/man/man3/res_getservers.3 minix-man ./usr/man/man3/res_getservers.3 minix-man
@ -3285,6 +3289,10 @@
./usr/man/man5/gettytab.5 minix-man ./usr/man/man5/gettytab.5 minix-man
./usr/man/man5/group.5 minix-man ./usr/man/man5/group.5 minix-man
./usr/man/man5/hosts.5 minix-man ./usr/man/man5/hosts.5 minix-man
./usr/man/man5/hosts.allow.5 minix-man
./usr/man/man5/hosts.deny.5 minix-man
./usr/man/man5/hosts_access.5 minix-man
./usr/man/man5/hosts_options.5 minix-man
./usr/man/man5/http_status.5 minix-man obsolete ./usr/man/man5/http_status.5 minix-man obsolete
./usr/man/man5/httpd.conf.5 minix-man obsolete ./usr/man/man5/httpd.conf.5 minix-man obsolete
./usr/man/man5/info.5 minix-man ./usr/man/man5/info.5 minix-man

View File

@ -81,7 +81,7 @@ SUBDIR+= \
libpci libprop \ libpci libprop \
libpuffs librmt \ libpuffs librmt \
libterminfo \ libterminfo \
libutil libz libutil libwrap libz
.if !defined(BSD_MK_COMPAT_FILE) .if !defined(BSD_MK_COMPAT_FILE)
#SUBDIR+= libkern #SUBDIR+= libkern

17
lib/libwrap/DISCLAIMER Normal file
View File

@ -0,0 +1,17 @@
/* $NetBSD: DISCLAIMER,v 1.3 2001/06/02 05:20:56 itojun Exp $ */
/************************************************************************
* Copyright 1995 by Wietse Venema. All rights reserved. Some individual
* files may be covered by other copyrights.
*
* This material was originally written and compiled by Wietse Venema at
* Eindhoven University of Technology, The Netherlands, in 1990, 1991,
* 1992, 1993, 1994 and 1995.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that this entire copyright notice
* is duplicated in all such copies.
*
* This software is provided "as is" and without any expressed or implied
* warranties, including, without limitation, the implied warranties of
* merchantibility and fitness for any particular purpose.
************************************************************************/

22
lib/libwrap/Makefile Normal file
View File

@ -0,0 +1,22 @@
# $NetBSD: Makefile,v 1.10 2007/05/28 12:06:22 tls Exp $
USE_FORT?= yes # network server
LIB= wrap
SRCS= hosts_access.c options.c shell_cmd.c rfc931.c eval.c hosts_ctl.c \
refuse.c percent_x.c clean_exit.c fix_options.c socket.c \
update.c misc.c diag.c
MAN= hosts_access.3 hosts_access.5 hosts_options.5
MLINKS+=hosts_access.5 hosts.allow.5
MLINKS+=hosts_access.5 hosts.deny.5
MLINKS+=hosts_access.3 hosts_ctl.3
MLINKS+=hosts_access.3 request_init.3
MLINKS+=hosts_access.3 request_set.3
INCS= tcpd.h
INCSDIR=/usr/include
.include "Makefile.cflags"
.include <bsd.lib.mk>

View File

@ -0,0 +1,23 @@
# $NetBSD: Makefile.cflags,v 1.8 2005/01/10 02:58:58 lukem Exp $
.include <bsd.own.mk>
CPPFLAGS+=-DFACILITY=LOG_AUTHPRIV -DSEVERITY=LOG_INFO
CPPFLAGS+=-DREAL_DAEMON_DIR=\"/usr/libexec\" -DHOSTS_ACCESS -DDAEMON_UMASK=022
CPPFLAGS+=-DRFC931_TIMEOUT=10 -DALWAYS_HOSTNAME
CPPFLAGS+=-DHOSTS_ALLOW=\"/etc/hosts.allow\" -DHOSTS_DENY=\"/etc/hosts.deny\"
CPPFLAGS+=-DPROCESS_OPTIONS
.if (${USE_YP} != "no")
CPPFLAGS+=-DNETGROUP
.endif
# -DPARANOID is not used by libwrap, only by programs that use it.
# in this case inetd does not use it (probably rightly so) and so
# we don't want to use it in wrapper-related utilities (such as
# tcpdmatch) that include this file.
#CPPFLAGS+=-DPARANOID
.if (${USE_INET6} != "no")
CPPFLAGS+=-DINET6
.endif

49
lib/libwrap/clean_exit.c Normal file
View File

@ -0,0 +1,49 @@
/* $NetBSD: clean_exit.c,v 1.5 2012/03/21 10:10:37 matt Exp $ */
/*
* clean_exit() cleans up and terminates the program. It should be called
* instead of exit() when for some reason the real network daemon will not or
* cannot be run. Reason: in the case of a datagram-oriented service we must
* discard the not-yet received data from the client. Otherwise, inetd will
* see the same datagram again and again, and go into a loop.
*
* Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
*/
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#) clean_exit.c 1.4 94/12/28 17:42:19";
#else
__RCSID("$NetBSD: clean_exit.c,v 1.5 2012/03/21 10:10:37 matt Exp $");
#endif
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "tcpd.h"
/* clean_exit - clean up and exit */
void
clean_exit(struct request_info *request)
{
/*
* In case of unconnected protocols we must eat up the not-yet received
* data or inetd will loop.
*/
if (request->sink)
request->sink(request->fd);
/*
* Be kind to the inetd. We already reported the problem via the syslogd,
* and there is no need for additional garbage in the logfile.
*/
sleep(5);
exit(0);
}

93
lib/libwrap/diag.c Normal file
View File

@ -0,0 +1,93 @@
/* $NetBSD: diag.c,v 1.10 2012/03/22 22:58:15 joerg Exp $ */
/*
* Routines to report various classes of problems. Each report is decorated
* with the current context (file name and line number), if available.
*
* tcpd_warn() reports a problem and proceeds.
*
* tcpd_jump() reports a problem and jumps.
*
* Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
*/
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#) diag.c 1.1 94/12/28 17:42:20";
#else
__RCSID("$NetBSD: diag.c,v 1.10 2012/03/22 22:58:15 joerg Exp $");
#endif
#endif
/* System libraries */
#include <syslog.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <string.h>
#include <errno.h>
/* Local stuff */
#include "tcpd.h"
struct tcpd_context tcpd_context;
jmp_buf tcpd_buf;
static void tcpd_diag(int, const char *, const char *, va_list)
__printflike(3,0);
/* tcpd_diag - centralize error reporter */
static void
tcpd_diag(int severity, const char *tag, const char *fmt, va_list ap)
{
char *buf;
int oerrno;
/* save errno in case we need it */
oerrno = errno;
if (vasprintf(&buf, fmt, ap) == -1)
buf = __UNCONST(fmt);
errno = oerrno;
/* contruct the tag for the log entry */
if (tcpd_context.file)
syslog(severity, "%s: %s, line %d: %s",
tag, tcpd_context.file, tcpd_context.line, buf);
else
syslog(severity, "%s: %s", tag, buf);
if (buf != fmt)
free(buf);
}
/* tcpd_warn - report problem of some sort and proceed */
void
tcpd_warn(const char *format, ...)
{
va_list ap;
va_start(ap, format);
tcpd_diag(LOG_ERR, "warning", format, ap);
va_end(ap);
}
/* tcpd_jump - report serious problem and jump */
void
tcpd_jump(const char *format, ...)
{
va_list ap;
va_start(ap, format);
tcpd_diag(LOG_ERR, "error", format, ap);
va_end(ap);
longjmp(tcpd_buf, AC_ERROR);
}

143
lib/libwrap/eval.c Normal file
View File

@ -0,0 +1,143 @@
/* $NetBSD: eval.c,v 1.7 2012/03/21 10:10:37 matt Exp $ */
/*
* Routines for controlled evaluation of host names, user names, and so on.
* They are, in fact, wrappers around the functions that are specific for
* the sockets or TLI programming interfaces. The request_info and host_info
* structures are used for result cacheing.
*
* These routines allows us to postpone expensive operations until their
* results are really needed. Examples are hostname lookups and double
* checks, or username lookups. Information that cannot be retrieved is
* given the value "unknown" ("paranoid" in case of hostname problems).
*
* When ALWAYS_HOSTNAME is off, hostname lookup is done only when required by
* tcpd paranoid mode, by access control patterns, or by %letter expansions.
*
* When ALWAYS_RFC931 mode is off, user lookup is done only when required by
* access control patterns or %letter expansions.
*
* Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
*/
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#) eval.c 1.3 95/01/30 19:51:45";
#else
__RCSID("$NetBSD: eval.c,v 1.7 2012/03/21 10:10:37 matt Exp $");
#endif
#endif
/* System libraries. */
#include <stdio.h>
#include <string.h>
/* Local stuff. */
#include "tcpd.h"
/*
* When a string has the value STRING_UNKNOWN, it means: don't bother, I
* tried to look up the data but it was unavailable for some reason. When a
* host name has the value STRING_PARANOID it means there was a name/address
* conflict.
*/
char unknown[] = STRING_UNKNOWN;
char paranoid[] = STRING_PARANOID;
/* eval_user - look up user name */
char *
eval_user(struct request_info *request)
{
if (request->user[0] == 0) {
(void)strlcpy(request->user, unknown, sizeof(request->user));
if (request->sink == 0 && request->client->sin && request->server->sin)
rfc931(request->client->sin, request->server->sin, request->user);
}
return (request->user);
}
/* eval_hostaddr - look up printable address */
char *
eval_hostaddr(struct host_info *host)
{
if (host->addr[0] == 0) {
(void)strlcpy(host->addr, unknown, sizeof(host->addr));
if (host->request->hostaddr != 0)
host->request->hostaddr(host);
}
return (host->addr);
}
/* eval_hostname - look up host name */
char *
eval_hostname(struct host_info *host)
{
if (host->name[0] == 0) {
(void)strlcpy(host->name, unknown, sizeof(host->name));
if (host->request->hostname != 0)
host->request->hostname(host);
}
return (host->name);
}
/* eval_hostinfo - return string with host name (preferred) or address */
char *
eval_hostinfo(struct host_info *host)
{
char *hostname;
#ifndef ALWAYS_HOSTNAME /* no implicit host lookups */
if (host->name[0] == 0)
return (eval_hostaddr(host));
#endif
hostname = eval_hostname(host);
if (HOSTNAME_KNOWN(hostname)) {
return (host->name);
} else {
return (eval_hostaddr(host));
}
}
/* eval_client - return string with as much about the client as we know */
char *
eval_client(struct request_info *request)
{
static char both[2 * STRING_LENGTH];
char *hostinfo = eval_hostinfo(request->client);
#ifndef ALWAYS_RFC931 /* no implicit user lookups */
if (request->user[0] == 0)
return (hostinfo);
#endif
if (STR_NE(eval_user(request), unknown)) {
(void)snprintf(both, sizeof both, "%s@%s", request->user, hostinfo);
return (both);
} else {
return (hostinfo);
}
}
/* eval_server - return string with as much about the server as we know */
char *
eval_server(struct request_info *request)
{
static char both[2 * STRING_LENGTH];
char *host = eval_hostinfo(request->server);
char *daemon = eval_daemon(request);
if (STR_NE(host, unknown)) {
(void)snprintf(both, sizeof both, "%s@%s", daemon, host);
return (both);
} else {
return (daemon);
}
}

146
lib/libwrap/fix_options.c Normal file
View File

@ -0,0 +1,146 @@
/* $NetBSD: fix_options.c,v 1.11 2012/03/21 10:10:37 matt Exp $ */
/*
* Routine to disable IP-level socket options. This code was taken from 4.4BSD
* rlogind and kernel source, but all mistakes in it are my fault.
*
* Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
*/
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#) fix_options.c 1.6 97/04/08 02:29:19";
#else
__RCSID("$NetBSD: fix_options.c,v 1.11 2012/03/21 10:10:37 matt Exp $");
#endif
#endif
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netdb.h>
#include <stdio.h>
#include <syslog.h>
#include <stdlib.h>
#include <unistd.h>
#ifndef IPOPT_OPTVAL
#define IPOPT_OPTVAL 0
#define IPOPT_OLEN 1
#endif
#include "tcpd.h"
#define BUFFER_SIZE 512 /* Was: BUFSIZ */
/* fix_options - get rid of IP-level socket options */
void
fix_options(struct request_info *request)
{
#ifdef IP_OPTIONS
unsigned char optbuf[BUFFER_SIZE / 3], *cp;
char lbuf[BUFFER_SIZE], *lp;
int ipproto;
socklen_t optsize = sizeof(optbuf);
struct protoent *ip;
int fd = request->fd;
int len = sizeof lbuf;
unsigned int opt;
int optlen;
struct in_addr dummy;
struct sockaddr_storage ss;
socklen_t sslen;
/*
* check if this is AF_INET socket
* XXX IPv6 support?
*/
sslen = sizeof(ss);
if (getsockname(fd, (struct sockaddr *)(void *)&ss, &sslen) < 0) {
syslog(LOG_ERR, "getsockname: %m");
clean_exit(request);
}
if (ss.ss_family != AF_INET)
return;
if ((ip = getprotobyname("ip")) != 0)
ipproto = ip->p_proto;
else
ipproto = IPPROTO_IP;
if (getsockopt(fd, ipproto, IP_OPTIONS, optbuf, &optsize) == 0
&& optsize != 0) {
/*
* Horror! 4.[34] BSD getsockopt() prepends the first-hop destination
* address to the result IP options list when source routing options
* are present (see <netinet/ip_var.h>), but produces no output for
* other IP options. Solaris 2.x getsockopt() does produce output for
* non-routing IP options, and uses the same format as BSD even when
* the space for the destination address is unused. The code below
* does the right thing with 4.[34]BSD derivatives and Solaris 2, but
* may occasionally miss source routing options on incompatible
* systems such as Linux. Their choice.
*
* Look for source routing options. Drop the connection when one is
* found. Just wiping the IP options is insufficient: we would still
* help the attacker by providing a real TCP sequence number, and the
* attacker would still be able to send packets (blind spoofing). I
* discussed this attack with Niels Provos, half a year before the
* attack was described in open mailing lists.
*
* It would be cleaner to just return a yes/no reply and let the caller
* decide how to deal with it. Resident servers should not terminate.
* However I am not prepared to make changes to internal interfaces
* on short notice.
*/
#define ADDR_LEN sizeof(dummy.s_addr)
for (cp = optbuf + ADDR_LEN; cp < optbuf + optsize; cp += optlen) {
opt = cp[IPOPT_OPTVAL];
if (opt == IPOPT_LSRR || opt == IPOPT_SSRR) {
syslog(LOG_WARNING,
"refused connect from %s with IP source routing options",
eval_client(request));
shutdown(fd, 2);
return;
}
if (opt == IPOPT_EOL)
break;
if (opt == IPOPT_NOP) {
optlen = 1;
} else if (&cp[IPOPT_OLEN] < optbuf + optsize) {
optlen = cp[IPOPT_OLEN];
if (optlen < 2 || cp + optlen >= optbuf + optsize) {
syslog(LOG_WARNING,
"refused connect from %s with malformed IP options",
eval_client(request));
shutdown(fd, 2);
return;
}
} else {
syslog(LOG_WARNING,
"refused connect from %s with malformed IP options",
eval_client(request));
shutdown(fd, 2);
return;
}
}
lp = lbuf;
for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3)
len -= snprintf(lp, len, " %2.2x", *cp);
syslog(LOG_NOTICE,
"connect from %s with IP options (ignored):%s",
eval_client(request), lbuf);
if (setsockopt(fd, ipproto, IP_OPTIONS, (char *) 0, optsize) != 0) {
syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m");
shutdown(fd, 2);
}
}
#endif
}

View File

@ -0,0 +1,96 @@
.\" $NetBSD: hosts_access.3,v 1.7 2002/10/01 19:38:46 wiz Exp $
.\"
.TH HOSTS_ACCESS 3
.SH NAME
hosts_access, hosts_ctl, request_init, request_set \- access control library
.SH SYNOPSIS
.nf
#include "tcpd.h"
extern int allow_severity;
extern int deny_severity;
struct request_info *request_init(request, key, value, ..., 0)
struct request_info *request;
struct request_info *request_set(request, key, value, ..., 0)
struct request_info *request;
int hosts_access(request)
struct request_info *request;
int hosts_ctl(daemon, client_name, client_addr, client_user)
char *daemon;
char *client_name;
char *client_addr;
char *client_user;
.fi
.SH DESCRIPTION
The routines described in this document are part
of the \fIlibwrap.a\fR library.
They implement a rule-based access control language with
optional shell commands that are executed when a rule fires.
.PP
request_init() initializes a structure with
information about a client request.
request_set() updates an already initialized request structure.
Both functions take a variable-length list of key-value
pairs and return their first argument.
The argument lists are terminated with a zero key value.
All string-valued arguments are copied.
The expected keys (and corresponding value types) are:
.IP "RQ_FILE (int)"
The file descriptor associated with the request.
.IP "RQ_CLIENT_NAME (char *)"
The client host name.
.IP "RQ_CLIENT_ADDR (char *)"
A printable representation of the client network address.
.IP "RQ_CLIENT_SIN (struct sockaddr_in *)"
An internal representation of the client network address and port.
The contents of the structure are not copied.
.IP "RQ_SERVER_NAME (char *)"
The hostname associated with the server endpoint address.
.IP "RQ_SERVER_ADDR (char *)"
A printable representation of the server endpoint address.
.IP "RQ_SERVER_SIN (struct sockaddr_in *)"
An internal representation of the server endpoint address and port.
The contents of the structure are not copied.
.IP "RQ_DAEMON (char *)"
The name of the daemon process running on the server host.
.IP "RQ_USER (char *)"
The name of the user on whose behalf the client host makes the request.
.PP
hosts_access() consults the access control tables described in the
\fIhosts_access(5)\fR manual page.
When internal endpoint information is available, host names
and client user names are looked up on demand,
using the request structure as a cache.
hosts_access() returns zero if access should be denied.
.PP
hosts_ctl() is a wrapper around the request_init() and hosts_access()
routines with a perhaps more convenient interface (though it does not
pass on enough information to support automated client username lookups).
The client host address, client host name and username
arguments should contain valid data or STRING_UNKNOWN.
hosts_ctl() returns zero if access should be denied.
.PP
The \fIallow_severity\fR and \fIdeny_severity\fR variables determine
how accepted and rejected requests may be logged.
They must be provided by the caller and may be modified
by rules in the access control tables.
.SH DIAGNOSTICS
Problems are reported via the syslog daemon.
.SH SEE ALSO
hosts_access(5), format of the access control tables.
hosts_options(5), optional extensions to the base language.
.SH FILES
/etc/hosts.allow, /etc/hosts.deny, access control tables.
.SH AUTHOR
.na
.nf
Wietse Venema (wietse@wzv.win.tue.nl)
Department of Mathematics and Computing Science
Eindhoven University of Technology
Den Dolech 2, P.O. Box 513,
5600 MB Eindhoven, The Netherlands
\" @(#) hosts_access.3 1.8 96/02/11 17:01:26

424
lib/libwrap/hosts_access.5 Normal file
View File

@ -0,0 +1,424 @@
.TH HOSTS_ACCESS 5
.SH NAME
hosts_access,
hosts.allow,
hosts.deny \- format of host access control files
.SH DESCRIPTION
This manual page describes a simple access control language that is
based on client (host name/address, user name), and server (process
name, host name/address) patterns.
Examples are given at the end.
The impatient reader is encouraged to skip to the EXAMPLES section for a
quick introduction.
.PP
Note that in a `stock' installation of the tcp_wrappers package, a
program called \fItcpd\fR is called from \fI/etc/inetd.conf\fR, and
this program performs the wrapper checks and then executes the daemon.
In NetBSD \fIinetd\fR(8) has been modified to perform this check
internally, and so tcpd is neither used nor supplied.
.PP
Also note that libwrap under NetBSD uses the extensions to the access
control language as described in the \fIhosts_options\fR(5).
.PP
In the following text, \fIdaemon\fR is the process name of a
network daemon process, and \fIclient\fR is the name and/or address of
a host requesting service.
Network daemon process names are specified in the inetd configuration file.
.SH "ACCESS CONTROL FILES"
The access control software consults two files.
The search stops at the first match:
.IP \(bu
Access will be granted when a (daemon,client) pair matches an entry in
the \fI/etc/hosts.allow\fR file.
.IP \(bu
Otherwise, access will be denied when a (daemon,client) pair matches an
entry in the \fI/etc/hosts.deny\fR file.
.IP \(bu
Otherwise, access will be granted.
.PP
A non-existing access control file is treated as if it were an empty
file.
Thus, access control can be turned off by providing no access
control files.
.SH "ACCESS CONTROL RULES"
Each access control file consists of zero or more lines of text.
These lines are processed in order of appearance.
The search terminates when a match is found.
.IP \(bu
A newline character is ignored when it is preceded by a backslash character.
This permits you to break up long lines so that they are easier to edit.
\fBWARNING:\fP The total length of an entry can be no
more than 2047 characters long including the final newline.
.IP \(bu
Blank lines or lines that begin with a `#\' character are ignored.
This permits you to insert comments and whitespace so that the tables
are easier to read.
.IP \(bu
All other lines should satisfy the following format, things between []
being optional:
.sp
.ti +3
daemon_list : client_list : option : option ...
.PP
\fIdaemon_list\fR is a list of one or more daemon process names
(argv[0] values) or wildcards (see below).
.PP
\fIclient_list\fR is a list
of one or more host names, host addresses, patterns or wildcards (see
below) that will be matched against the client host name or address.
When a client_list item needs to include colon character (for IPv6 addresses),
the item needs to be wrapped with square bracket.
.PP
The more complex forms \fIdaemon@host\fR and \fIuser@host\fR are
explained in the sections on server endpoint patterns and on client
username lookups, respectively.
.PP
List elements should be separated by blanks and/or commas.
.PP
With the exception of NIS (YP) netgroup lookups, all access control
checks are case insensitive.
.ne 4
.SH PATTERNS
The access control language implements the following patterns:
.IP \(bu
A string that begins with a `.\' character.
A host name is matched if
the last components of its name match the specified pattern.
For example, the pattern `.tue.nl\' matches the host name
`wzv.win.tue.nl\'.
.IP \(bu
A string that ends with a `.\' character.
A host address is matched if
its first numeric fields match the given string.
For example, the
pattern `131.155.\' matches the address of (almost) every host on the
Eind\%hoven University network (131.155.x.x).
.IP \(bu
A string that begins with an `@\' character is treated as an NIS
(formerly YP) netgroup name.
A host name is matched if it is a host member of the specified netgroup.
Netgroup matches are not supported
for daemon process names or for client user names.
.IP \(bu
An expression of the form `n.n.n.n/m.m.m.m\' is interpreted as a
`net/mask\' pair.
A host address is matched if `net\' is equal to the
bitwise AND of the address and the `mask\'.
For example, the net/mask
pattern `131.155.72.0/255.255.254.0\' matches every address in the
range `131.155.72.0\' through `131.155.73.255\'.
Note that `m.m.m.m\' portion must always be specified.
.IP \(bu
An expression of the form `ipv6-addr/ipv6-mask\' is interpreted as
masked IPv6 address match, just like masked IPv4 address match (see above).
Note that `ipv6-mask\' portion must always be specified.
.IP \(bu
An expression of the form `ipv6-addr/prefixlen\' is interpreted as
masked IPv6 address match (with mask specified by numeric prefixlen),
just like masked IPv4 address match (see above).
Note that `prefixlen\' portion must always be specified.
.IP \(bu
A string that begins with a `/\' character is treated as a file
name. A host name or address is matched if it matches any host name
or address pattern listed in the named file. The file format is
zero or more lines with zero or more host name or address patterns
separated by whitespace. A file name pattern can be used anywhere
a host name or address pattern can be used.
.SH WILDCARDS
The access control language supports explicit wildcards:
.IP ALL
The universal wildcard, always matches.
.IP LOCAL
Matches any host whose name does not contain a dot character.
.IP UNKNOWN
Matches any user whose name is unknown, and matches any host whose name
\fIor\fR address are unknown.
This pattern should be used with care:
host names may be unavailable due to temporary name server problems.
A network address will be unavailable when the software cannot figure out
what type of network it is talking to.
.IP KNOWN
Matches any user whose name is known, and matches any host whose name
\fIand\fR address are known.
This pattern should be used with care:
host names may be unavailable due to temporary name server problems.
A network address will be unavailable when the software cannot figure out
what type of network it is talking to.
.IP PARANOID
Matches any host whose name does not match its address.
Note that unlike the default mode of \fItcpd\fR, NetBSD \fIinetd\fR
does not automatically drop these requests; you must explicitly
drop them in your \fI/etc/hosts.allow\fR or \fI/etc/hosts.deny\fR file.
.IP "{RBL}.\fIdomain\fR"
Matches any host whose reversed address appears in the DNS under
\fIdomain\fR.
The primary such domain used for blocking unsolicited
commercial e-mail (spam) is `.rbl.maps.vix.com\'.
.ne 6
.SH OPERATORS
.IP EXCEPT
Intended use is of the form: `list_1 EXCEPT list_2\'; this construct
matches anything that matches \fIlist_1\fR unless it matches
\fIlist_2\fR.
The EXCEPT operator can be used in daemon_lists and in client_lists.
The EXCEPT operator can be nested: if the control
language would permit the use of parentheses, `a EXCEPT b EXCEPT c\'
would parse as `(a EXCEPT (b EXCEPT c))\'.
.br
.ne 6
.SH % EXPANSIONS
The following expansions are available within some options:
.IP "%a (%A)"
The client (server) host address.
.IP %c
Client information: user@host, user@address, a host name, or just an
address, depending on how much information is available.
.IP %d
The daemon process name (argv[0] value).
.IP "%h (%H)"
The client (server) host name or address, if the host name is
unavailable.
.IP "%n (%N)"
The client (server) host name (or "unknown" or "paranoid").
.IP %p
The daemon process id.
.IP %s
Server information: daemon@host, daemon@address, or just a daemon name,
depending on how much information is available.
.IP %u
The client user name (or "unknown").
.IP %%
Expands to a single `%\' character.
.PP
Characters in % expansions that may confuse the shell are replaced by
underscores.
.SH "SERVER ENDPOINT PATTERNS"
In order to distinguish clients by the network address that they
connect to, use patterns of the form:
.sp
.ti +3
process_name@host_pattern : client_list ...
.sp
Patterns like these can be used when the machine has different internet
addresses with different internet hostnames.
Service providers can use
this facility to offer FTP, GOPHER or WWW archives with internet names
that may even belong to different organizations.
See also the `twist' option in the hosts_options(5) document.
Some systems (Solaris, FreeBSD, NetBSD) can have more than one
internet address on one physical interface; with other systems
you may have to resort to SLIP or PPP pseudo interfaces that
live in a dedicated network address space.
.sp
The host_pattern obeys the same syntax rules as host names and
addresses in client_list context.
Usually, server endpoint information
is available only with connection-oriented services.
.SH "CLIENT USERNAME LOOKUP"
When the client host supports the RFC 931 protocol or one of its
descendants (TAP, IDENT, RFC 1413) the wrapper programs can retrieve
additional information about the owner of a connection.
Client username information, when available,
is logged together with the client host
name, and can be used to match patterns like:
.PP
.ti +3
daemon_list : ... user_pattern@host_pattern ...
.PP
The daemon wrappers can be configured at compile time to perform
rule-driven username lookups (default) or to always interrogate the
client host.
In the case of rule-driven username lookups, the above
rule would cause username lookup only when both the \fIdaemon_list\fR
and the \fIhost_pattern\fR match.
.PP
A user pattern has the same syntax as a daemon process pattern, so the
same wildcards apply (netgroup membership is not supported).
One should not get carried away with username lookups, though.
.IP \(bu
The client username information cannot be trusted when it is needed
most, i.e. when the client system has been compromised.
In general,
ALL and (UN)KNOWN are the only user name patterns that make sense.
.IP \(bu
Username lookups are possible only with TCP-based services, and only
when the client host runs a suitable daemon; in all other cases the
result is "unknown".
.IP \(bu
A well-known UNIX kernel bug may cause loss of service when username
lookups are blocked by a firewall.
The wrapper README document
describes a procedure to find out if your kernel has this bug.
.IP \(bu
Username lookups may cause noticeable delays for non-UNIX users.
The default timeout for username lookups is 10 seconds: too short to cope
with slow networks, but long enough to irritate PC users.
.PP
Selective username lookups can alleviate the last problem.
For example, a rule like:
.PP
.ti +3
daemon_list : @pcnetgroup ALL@ALL
.PP
would match members of the pc netgroup without doing username lookups,
but would perform username lookups with all other systems.
.SH "DETECTING ADDRESS SPOOFING ATTACKS"
A flaw in the sequence number generator of many TCP/IP implementations
allows intruders to easily impersonate trusted hosts and to break in
via, for example, the remote shell service.
The IDENT (RFC 931 etc.) service can be used to detect such and
other host address spoofing attacks.
.PP
Before accepting a client request, the wrappers can use the IDENT
service to find out that the client did not send the request at all.
When the client host provides IDENT service, a negative IDENT lookup
result (the client matches `UNKNOWN@host') is strong evidence of a host
spoofing attack.
.PP
A positive IDENT lookup result (the client matches `KNOWN@host') is
less trustworthy.
It is possible for an intruder to spoof both the
client connection and the IDENT lookup, although doing so is much
harder than spoofing just a client connection.
It may also be that the client\'s IDENT server is lying.
.PP
Note: IDENT lookups don\'t work with UDP services.
.SH EXAMPLES
The language is flexible enough that different types of access control
policy can be expressed with a minimum of fuss.
Although the language
uses two access control tables, the most common policies can be
implemented with one of the tables being trivial or even empty.
.PP
When reading the examples below it is important to realize that the
allow table is scanned before the deny table, that the search
terminates when a match is found, and that access is granted when no
match is found at all.
.PP
The examples use host and domain names.
They can be improved by
including address and/or network/netmask information, to reduce the
impact of temporary name server lookup failures.
.SH "MOSTLY CLOSED"
In this case, access is denied by default.
Only explicitly authorized hosts are permitted access.
.PP
The default policy (no access) is implemented with a trivial deny file:
.PP
.ne 2
/etc/hosts.deny:
.in +3
ALL: ALL
.PP
This denies all service to all hosts, unless they are permitted access
by entries in the allow file.
.PP
The explicitly authorized hosts are listed in the allow file.
For example:
.PP
.ne 2
/etc/hosts.allow:
.in +3
ALL: LOCAL @some_netgroup
.br
ALL: .foobar.edu EXCEPT terminalserver.foobar.edu
.PP
The first rule permits access from hosts in the local domain (no `.\'
in the host name) and from members of the \fIsome_netgroup\fP netgroup.
The second rule permits access from all hosts in the
\fIfoobar.edu\fP domain (notice the leading dot), with the exception of
\fIterminalserver.foobar.edu\fP.
.SH "MOSTLY OPEN"
Here, access is granted by default; only explicitly specified hosts are
refused service.
.PP
The default policy (access granted) makes the allow file redundant so
that it can be omitted.
The explicitly non-authorized hosts are listed in the deny file.
For example:
.PP
/etc/hosts.deny:
.in +3
ALL: some.host.name, .some.domain
.br
ALL EXCEPT in.fingerd: other.host.name, .other.domain
.PP
The first rule denies some hosts and domains all services; the second
rule still permits finger requests from other hosts and domains.
.SH "BOOBY TRAPS"
The next example permits tftp requests from hosts in the local domain
(notice the leading dot).
Requests from any other hosts are denied.
Instead of the requested file, a finger probe is sent to the offending host.
The result is mailed to the superuser.
.PP
.ne 2
/etc/hosts.allow:
.in +3
.nf
in.tftpd: LOCAL, .my.domain
.PP
.ne 2
/etc/hosts.deny:
.in +3
.nf
in.tftpd: ALL: spawn (/some/where/safe_finger -l @%h | \\
/usr/ucb/mail -s %d-%h root) \*[Am]
.fi
.PP
(The safe_finger command can be gotten from the tcp_wrappers package and
installed in a suitable place.
It limits possible damage from data sent by the remote finger server.
It gives better protection than the standard finger command.)
.PP
The expansion of the %h (client host) and %d (service name) sequences
is described in the section on shell commands.
.PP
Warning: do not booby-trap your finger daemon, unless you are prepared
for infinite finger loops.
.PP
On network firewall systems this trick can be carried even further.
The typical network firewall only provides a limited set of services to
the outer world.
All other services can be "bugged" just like the above tftp example.
The result is an excellent early-warning system.
.br
.ne 4
.SH DIAGNOSTICS
An error is reported when a syntax error is found in a host access
control rule; when the length of an access control rule exceeds the
capacity of an internal buffer; when an access control rule is not
terminated by a newline character; when the result of %\*[Lt]letter\*[Gt]
expansion would overflow an internal buffer; when a system call fails
that shouldn\'t.
All problems are reported via the syslog daemon.
.SH FILES
.na
.nf
/etc/hosts.allow, (daemon,client) pairs that are granted access.
/etc/hosts.deny, (daemon,client) pairs that are denied access.
.ad
.fi
.SH "SEE ALSO"
.nf
hosts_options(5), hosts_access(3)
tcpdchk(8), tcpdmatch(8), test programs.
.SH BUGS
If a name server lookup times out, the host name will not be available
to the access control software, even though the host is registered.
.PP
Domain name server lookups are case insensitive; NIS (formerly YP)
netgroup lookups are case sensitive.
.PP
The total length of an entry can be no more than 2047 characters long,
including the final newline.
.SH AUTHOR
.na
.nf
Wietse Venema (wietse@wzv.win.tue.nl)
Department of Mathematics and Computing Science
Eindhoven University of Technology
Den Dolech 2, P.O. Box 513,
5600 MB Eindhoven, The Netherlands
.\" @(#) hosts_access.5 1.20 95/01/30 19:51:46
.\" $NetBSD: hosts_access.5,v 1.16 2008/12/18 20:16:52 christos Exp $

569
lib/libwrap/hosts_access.c Normal file
View File

@ -0,0 +1,569 @@
/* $NetBSD: hosts_access.c,v 1.20 2012/03/21 10:10:37 matt Exp $ */
/*
* This module implements a simple access control language that is based on
* host (or domain) names, NIS (host) netgroup names, IP addresses (or
* network numbers) and daemon process names. When a match is found the
* search is terminated, and depending on whether PROCESS_OPTIONS is defined,
* a list of options is executed or an optional shell command is executed.
*
* Host and user names are looked up on demand, provided that suitable endpoint
* information is available as sockaddr_in structures or TLI netbufs. As a
* side effect, the pattern matching process may change the contents of
* request structure fields.
*
* Diagnostics are reported through syslog(3).
*
* Compile with -DNETGROUP if your library provides support for netgroups.
*
* Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
*/
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#) hosts_access.c 1.21 97/02/12 02:13:22";
#else
__RCSID("$NetBSD: hosts_access.c,v 1.20 2012/03/21 10:10:37 matt Exp $");
#endif
#endif
/* System libraries. */
#include <sys/types.h>
#include <sys/param.h>
#ifdef INET6
#include <sys/socket.h>
#endif
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <ctype.h>
#include <errno.h>
#include <setjmp.h>
#include <string.h>
#include <netdb.h>
#ifdef NETGROUP
#include <netgroup.h>
#include <rpcsvc/ypclnt.h>
#endif
/* Local stuff. */
#include "tcpd.h"
/* Error handling. */
extern jmp_buf tcpd_buf;
/* Delimiters for lists of daemons or clients. */
static char sep[] = ", \t\r\n";
/* Constants to be used in assignments only, not in comparisons... */
#define YES 1
#define NO 0
/*
* These variables are globally visible so that they can be redirected in
* verification mode.
*/
const char *hosts_allow_table = HOSTS_ALLOW;
const char *hosts_deny_table = HOSTS_DENY;
int hosts_access_verbose = 0;
/*
* In a long-running process, we are not at liberty to just go away.
*/
int resident = (-1); /* -1, 0: unknown; +1: yes */
/* Forward declarations. */
static int table_match(const char *, struct request_info *);
static int list_match(char *, struct request_info *,
int (*)(char *, struct request_info *));
static int server_match(char *, struct request_info *);
static int client_match(char *, struct request_info *);
static int host_match(char *, struct host_info *);
static int hostfile_match(char *, struct host_info *);
static int rbl_match(char *, char *);
static int string_match(char *, char *);
static int masked_match(char *, char *, char *);
static int masked_match4(char *, char *, char *);
#ifdef INET6
static int masked_match6(char *, char *, char *);
#endif
/* Size of logical line buffer. */
#define BUFLEN 2048
/* hosts_access - host access control facility */
int
hosts_access(struct request_info *request)
{
int verdict;
/*
* If the (daemon, client) pair is matched by an entry in the file
* /etc/hosts.allow, access is granted. Otherwise, if the (daemon,
* client) pair is matched by an entry in the file /etc/hosts.deny,
* access is denied. Otherwise, access is granted. A non-existent
* access-control file is treated as an empty file.
*
* After a rule has been matched, the optional language extensions may
* decide to grant or refuse service anyway. Or, while a rule is being
* processed, a serious error is found, and it seems better to play safe
* and deny service. All this is done by jumping back into the
* hosts_access() routine, bypassing the regular return from the
* table_match() function calls below.
*/
if (resident <= 0)
resident++;
verdict = setjmp(tcpd_buf);
if (verdict != 0)
return (verdict == AC_PERMIT);
if (table_match(hosts_allow_table, request))
return (YES);
if (table_match(hosts_deny_table, request))
return (NO);
return (YES);
}
/* table_match - match table entries with (daemon, client) pair */
static int
table_match(const char *table, struct request_info *request)
{
FILE *fp;
char sv_list[BUFLEN]; /* becomes list of daemons */
char *cl_list; /* becomes list of clients */
char *sh_cmd = NULL; /* becomes optional shell command */
int match = NO;
struct tcpd_context saved_context;
saved_context = tcpd_context; /* stupid compilers */
/*
* Between the fopen() and fclose() calls, avoid jumps that may cause
* file descriptor leaks.
*/
if ((fp = fopen(table, "r")) != 0) {
tcpd_context.file = table;
tcpd_context.line = 0;
while (match == NO && xgets(sv_list, sizeof(sv_list), fp) != 0) {
if (sv_list[strlen(sv_list) - 1] != '\n') {
tcpd_warn("missing newline or line too long");
continue;
}
if (sv_list[0] == '#' || sv_list[strspn(sv_list, " \t\r\n")] == 0)
continue;
if ((cl_list = split_at(sv_list, ':')) == 0) {
tcpd_warn("missing \":\" separator");
continue;
}
sh_cmd = split_at(cl_list, ':');
match = list_match(sv_list, request, server_match)
&& list_match(cl_list, request, client_match);
}
(void) fclose(fp);
} else if (errno != ENOENT) {
tcpd_warn("cannot open %s: %m", table);
}
if (match) {
if (hosts_access_verbose > 1)
syslog(LOG_DEBUG, "matched: %s line %d",
tcpd_context.file, tcpd_context.line);
if (sh_cmd) {
#ifdef PROCESS_OPTIONS
process_options(sh_cmd, request);
#else
char cmd[BUFSIZ];
shell_cmd(percent_x(cmd, sizeof(cmd), sh_cmd, request));
#endif
}
}
tcpd_context = saved_context;
return (match);
}
/* list_match - match a request against a list of patterns with exceptions */
static int
list_match(char *list, struct request_info *request,
int (*match_fn)(char *, struct request_info *))
{
char *tok;
static char *last;
int l;
/*
* Process tokens one at a time. We have exhausted all possible matches
* when we reach an "EXCEPT" token or the end of the list. If we do find
* a match, look for an "EXCEPT" list and recurse to determine whether
* the match is affected by any exceptions.
*/
for (tok = strtok_r(list, sep, &last); tok != 0;
tok = strtok_r(NULL, sep, &last)) {
if (STR_EQ(tok, "EXCEPT")) /* EXCEPT: give up */
return (NO);
l = strlen(tok);
if (*tok == '[' && tok[l - 1] == ']') {
tok[l - 1] = '\0';
tok++;
}
if (match_fn(tok, request)) { /* YES: look for exceptions */
while ((tok = strtok_r(NULL, sep, &last)) && STR_NE(tok, "EXCEPT"))
/* VOID */ ;
return (tok == 0 || list_match(NULL, request, match_fn) == 0);
}
}
return (NO);
}
/* server_match - match server information */
static int
server_match(char *tok, struct request_info *request)
{
char *host;
if ((host = split_at(tok + 1, '@')) == 0) { /* plain daemon */
return (string_match(tok, eval_daemon(request)));
} else { /* daemon@host */
return (string_match(tok, eval_daemon(request))
&& host_match(host, request->server));
}
}
/* client_match - match client information */
static int
client_match(char *tok, struct request_info *request)
{
char *host;
if ((host = split_at(tok + 1, '@')) == 0) { /* plain host */
return (host_match(tok, request->client));
} else { /* user@host */
return (host_match(host, request->client)
&& string_match(tok, eval_user(request)));
}
}
/* host_match - match host name and/or address against pattern */
static int
host_match(char *tok, struct host_info *host)
{
char *mask;
/*
* This code looks a little hairy because we want to avoid unnecessary
* hostname lookups.
*
* The KNOWN pattern requires that both address AND name be known; some
* patterns are specific to host names or to host addresses; all other
* patterns are satisfied when either the address OR the name match.
*/
if (tok[0] == '@') { /* netgroup: look it up */
#ifdef NETGROUP
static char *mydomain = 0;
if (mydomain == 0)
yp_get_default_domain(&mydomain);
return (innetgr(tok + 1, eval_hostname(host), NULL, mydomain));
#else
tcpd_warn("netgroup support is disabled"); /* not tcpd_jump() */
return (NO);
#endif
} else if (tok[0] == '/') { /* /file hack */
return (hostfile_match(tok, host));
} else if (STR_EQ(tok, "KNOWN")) { /* check address and name */
char *name = eval_hostname(host);
return (STR_NE(eval_hostaddr(host), unknown) && HOSTNAME_KNOWN(name));
} else if (STR_EQ(tok, "LOCAL")) { /* local: no dots in name */
char *name = eval_hostname(host);
return (strchr(name, '.') == 0 && HOSTNAME_KNOWN(name));
} else if (strncmp(tok, "{RBL}.", 6) == 0) { /* RBL lookup in domain */
return rbl_match(tok+6, eval_hostaddr(host));
} else if ((mask = split_at(tok, '/')) != 0) { /* net/mask */
return (masked_match(tok, mask, eval_hostaddr(host)));
} else { /* anything else */
return (string_match(tok, eval_hostaddr(host))
|| (NOT_INADDR(tok) && string_match(tok, eval_hostname(host))));
}
}
/* hostfile_match - look up host patterns from file */
static int
hostfile_match(char *path, struct host_info *host)
{
char tok[BUFSIZ];
int match = NO;
FILE *fp;
if ((fp = fopen(path, "r")) != 0) {
while (fscanf(fp, "%s", tok) == 1 && !(match = host_match(tok, host)))
/* void */ ;
fclose(fp);
} else if (errno != ENOENT) {
tcpd_warn("open %s: %m", path);
}
return (match);
}
/* rbl_match() - match host by looking up in RBL domain */
static int
rbl_match(
char *rbl_domain, /* RBL domain */
char *rbl_hostaddr) /* hostaddr */
{
char *rbl_name;
unsigned long host_address;
int ret = NO;
size_t len = strlen(rbl_domain) + (4 * 4) + 2;
if (dot_quad_addr(rbl_hostaddr, &host_address) != 0) {
tcpd_warn("unable to convert %s to address", rbl_hostaddr);
return (NO);
}
host_address = ntohl(host_address);
/* construct the rbl name to look up */
if ((rbl_name = malloc(len)) == NULL) {
tcpd_jump("not enough memory to build RBL name for %s in %s", rbl_hostaddr, rbl_domain);
/* NOTREACHED */
}
snprintf(rbl_name, len, "%u.%u.%u.%u.%s",
(unsigned int) ((host_address) & 0xff),
(unsigned int) ((host_address >> 8) & 0xff),
(unsigned int) ((host_address >> 16) & 0xff),
(unsigned int) ((host_address >> 24) & 0xff),
rbl_domain);
/* look it up */
if (gethostbyname(rbl_name) != NULL) {
/* successful lookup - they're on the RBL list */
ret = YES;
}
free(rbl_name);
return ret;
}
/* string_match - match string against pattern */
static int
string_match(char *tok, char *string)
{
int n;
if (tok[0] == '.') { /* suffix */
n = strlen(string) - strlen(tok);
return (n > 0 && STR_EQ(tok, string + n));
} else if (STR_EQ(tok, "ALL")) { /* all: match any */
return (YES);
} else if (STR_EQ(tok, "KNOWN")) { /* not unknown */
return (STR_NE(string, unknown));
} else if (tok[(n = strlen(tok)) - 1] == '.') { /* prefix */
return (STRN_EQ(tok, string, n));
} else { /* exact match */
return (STR_EQ(tok, string));
}
}
/* masked_match - match address against netnumber/netmask */
static int
masked_match(char *net_tok, char *mask_tok, char *string)
{
#ifndef INET6
return masked_match4(net_tok, mask_tok, string);
#else
/*
* masked_match4() is kept just for supporting shortened IPv4 address form.
* If we could get rid of shortened IPv4 form, we could just always use
* masked_match6().
*/
if (dot_quad_addr(net_tok, NULL) != -1 &&
dot_quad_addr(mask_tok, NULL) != -1 &&
dot_quad_addr(string, NULL) != -1) {
return masked_match4(net_tok, mask_tok, string);
} else
return masked_match6(net_tok, mask_tok, string);
#endif
}
static int
masked_match4(char *net_tok, char *mask_tok, char *string)
{
unsigned long net;
unsigned long mask;
unsigned long addr;
/*
* Disallow forms other than dotted quad: the treatment that inet_addr()
* gives to forms with less than four components is inconsistent with the
* access control language. John P. Rouillard <rouilj@cs.umb.edu>.
*/
if (dot_quad_addr(string, &addr) != 0)
return (NO);
if (dot_quad_addr(net_tok, &net) != 0 ||
dot_quad_addr(mask_tok, &mask) != 0) {
tcpd_warn("bad net/mask expression: %s/%s", net_tok, mask_tok);
return (NO); /* not tcpd_jump() */
}
if ((net & ~mask) != 0)
tcpd_warn("host bits not all zero in %s/%s", net_tok, mask_tok);
return ((addr & mask) == net);
}
#ifdef INET6
static int
masked_match6(char *net_tok, char *mask_tok, char *string)
{
union {
struct sockaddr sa;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
} net, mask, addr;
struct addrinfo hints, *res;
unsigned long masklen;
char *ep;
size_t i;
char *np, *mp, *ap;
size_t alen;
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM; /*dummy*/
hints.ai_flags = AI_NUMERICHOST;
if (getaddrinfo(net_tok, "0", &hints, &res) == 0) {
if (res->ai_addrlen > sizeof(net) || res->ai_next) {
freeaddrinfo(res);
return NO;
}
memcpy(&net, res->ai_addr, res->ai_addrlen);
freeaddrinfo(res);
} else
return NO;
memset(&hints, 0, sizeof(hints));
hints.ai_family = net.sa.sa_family;
hints.ai_socktype = SOCK_DGRAM; /*dummy*/
hints.ai_flags = AI_NUMERICHOST;
ep = NULL;
if (getaddrinfo(mask_tok, "0", &hints, &res) == 0) {
if (res->ai_family == AF_INET6 &&
((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id) {
freeaddrinfo(res);
return NO;
}
if (res->ai_addrlen > sizeof(mask) || res->ai_next) {
freeaddrinfo(res);
return NO;
}
memcpy(&mask, res->ai_addr, res->ai_addrlen);
freeaddrinfo(res);
} else {
ep = NULL;
masklen = strtoul(mask_tok, &ep, 10);
if (ep && !*ep) {
memset(&mask, 0, sizeof(mask));
mask.sa.sa_family = net.sa.sa_family;
mask.sa.sa_len = net.sa.sa_len;
switch (mask.sa.sa_family) {
case AF_INET:
mp = (char *)&mask.sin.sin_addr;
alen = sizeof(mask.sin.sin_addr);
break;
case AF_INET6:
mp = (char *)&mask.sin6.sin6_addr;
alen = sizeof(mask.sin6.sin6_addr);
break;
default:
return NO;
}
if (masklen / 8 > alen)
return NO;
memset(mp, 0xff, masklen / 8);
if (masklen % 8)
mp[masklen / 8] = 0xff00 >> (masklen % 8);
} else
return NO;
}
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM; /*dummy*/
hints.ai_flags = AI_NUMERICHOST;
if (getaddrinfo(string, "0", &hints, &res) == 0) {
if (res->ai_addrlen > sizeof(addr) || res->ai_next) {
freeaddrinfo(res);
return NO;
}
/* special case - IPv4 mapped address */
if (net.sa.sa_family == AF_INET && res->ai_family == AF_INET6 &&
IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr)) {
memset(&addr, 0, sizeof(addr));
addr.sa.sa_family = net.sa.sa_family;
addr.sa.sa_len = net.sa.sa_len;
memcpy(&addr.sin.sin_addr,
&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr.s6_addr[12],
sizeof(addr.sin.sin_addr));
} else
memcpy(&addr, res->ai_addr, res->ai_addrlen);
freeaddrinfo(res);
} else
return NO;
if (net.sa.sa_family != mask.sa.sa_family ||
net.sa.sa_family != addr.sa.sa_family) {
return NO;
}
switch (net.sa.sa_family) {
case AF_INET:
np = (char *)&net.sin.sin_addr;
mp = (char *)&mask.sin.sin_addr;
ap = (char *)&addr.sin.sin_addr;
alen = sizeof(net.sin.sin_addr);
break;
case AF_INET6:
np = (char *)&net.sin6.sin6_addr;
mp = (char *)&mask.sin6.sin6_addr;
ap = (char *)&addr.sin6.sin6_addr;
alen = sizeof(net.sin6.sin6_addr);
break;
default:
return NO;
}
for (i = 0; i < alen; i++)
if (np[i] & ~mp[i]) {
tcpd_warn("host bits not all zero in %s/%s", net_tok, mask_tok);
break;
}
for (i = 0; i < alen; i++)
ap[i] &= mp[i];
if (addr.sa.sa_family == AF_INET6 && addr.sin6.sin6_scope_id &&
addr.sin6.sin6_scope_id != net.sin6.sin6_scope_id)
return NO;
return (memcmp(ap, np, alen) == 0);
}
#endif

42
lib/libwrap/hosts_ctl.c Normal file
View File

@ -0,0 +1,42 @@
/* $NetBSD: hosts_ctl.c,v 1.5 2012/03/21 10:10:37 matt Exp $ */
/*
* hosts_ctl() combines common applications of the host access control
* library routines. It bundles its arguments then calls the hosts_access()
* access control checker. The host name and user name arguments should be
* empty strings, STRING_UNKNOWN or real data. If a match is found, the
* optional shell command is executed.
*
* Restriction: this interface does not pass enough information to support
* selective remote username lookups or selective hostname double checks.
*
* Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
*/
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#) hosts_ctl.c 1.4 94/12/28 17:42:27";
#else
__RCSID("$NetBSD: hosts_ctl.c,v 1.5 2012/03/21 10:10:37 matt Exp $");
#endif
#endif
#include <stdio.h>
#include "tcpd.h"
/* hosts_ctl - limited interface to the hosts_access() routine */
int
hosts_ctl(char *daemon, char *name, char *addr, char *user)
{
struct request_info request;
return (hosts_access(request_init(&request,
RQ_DAEMON, daemon,
RQ_CLIENT_NAME, name,
RQ_CLIENT_ADDR, addr,
RQ_USER, user,
0)));
}

189
lib/libwrap/hosts_options.5 Normal file
View File

@ -0,0 +1,189 @@
.\" $NetBSD: hosts_options.5,v 1.8 2002/10/01 19:38:46 wiz Exp $
.\"
.TH HOSTS_OPTIONS 5
.SH NAME
hosts_options \- host access control language extensions
.SH DESCRIPTION
This document describes optional extensions to the language described
in the hosts_access(5) document.
The extensions are enabled at program build time.
For example, by editing the Makefile and turning on the
PROCESS_OPTIONS compile-time option.
.PP
The extensible language uses the following format:
.sp
.ti +3
daemon_list : client_list : option : option ...
.PP
The first two fields are described in the hosts_access(5) manual page.
The remainder of the rules is a list of zero or more options.
Any ":" characters within options should be protected with a backslash.
.PP
An option is of the form "keyword" or "keyword value".
Options are processed in the specified order.
Some options are subjected to
%\*[Lt]letter\*[Gt] substitutions.
For the sake of backwards compatibility with
earlier versions, an "=" is permitted between keyword and value.
.SH LOGGING
.IP "severity mail.info"
.IP "severity notice"
Change the severity level at which the event will be logged.
Facility names (such as mail) are optional, and are not supported on systems
with older syslog implementations.
The severity option can be used to emphasize or to ignore specific events.
.SH ACCESS CONTROL
.IP "allow"
.IP "deny"
Grant (deny) service.
These options must appear at the end of a rule.
.PP
The \fIallow\fR and \fIdeny\fR keywords make it possible to keep all
access control rules within a single file, for example in the
\fIhosts.allow\fR file.
.sp
To permit access from specific hosts only:
.sp
.ne 2
.ti +3
ALL: .friendly.domain: ALLOW
.ti +3
ALL: ALL: DENY
.sp
To permit access from all hosts except a few trouble makers:
.sp
.ne 2
.ti +3
ALL: .bad.domain: DENY
.ti +3
ALL: ALL: ALLOW
.sp
Notice the leading dot on the domain name patterns.
.SH RUNNING OTHER COMMANDS
.IP "spawn shell_command"
Execute, in a child process, the specified shell command, after
performing the %\*[Lt]letter\*[Gt] expansions described in the hosts_access(5)
manual page.
The command is executed with stdin, stdout and stderr
connected to the null device, so that it won\'t mess up the
conversation with the client host.
Example:
.sp
.nf
.ti +3
spawn (/some/where/safe_finger -l @%h | /usr/ucb/mail root) \*[Am]
.fi
.sp
executes, in a background child process, the shell command "safe_finger
-l @%h | mail root" after replacing %h by the name or address of the
remote host.
.sp
The example uses the "safe_finger" command instead of the regular
"finger" command, to limit possible damage from data sent by the finger server.
The "safe_finger" command is part of the daemon wrapper
package; it is a wrapper around the regular finger command that filters
the data sent by the remote host.
.IP "twist shell_command"
Replace the current process by an instance of the specified shell
command, after performing the %\*[Lt]letter\*[Gt] expansions described in the
hosts_access(5) manual page.
Stdin, stdout and stderr are connected to the client process.
This option must appear at the end of a rule.
.sp
To send a customized bounce message to the client instead of
running the real ftp daemon:
.sp
.nf
.ti +3
in.ftpd : ... : twist /bin/echo 421 Some bounce message
.fi
.sp
For an alternative way to talk to client processes, see the
\fIbanners\fR option below.
.sp
To run /some/other/in.telnetd without polluting its command-line
array or its process environment:
.sp
.nf
.ti +3
in.telnetd : ... : twist PATH=/some/other; exec in.telnetd
.fi
.sp
Warning: in case of UDP services, do not twist to commands that use
the standard I/O or the read(2)/write(2) routines to communicate with
the client process; UDP requires other I/O primitives.
.SH NETWORK OPTIONS
.IP "keepalive"
Causes the server to periodically send a message to the client.
The connection is considered broken when the client does not respond.
The keepalive option can be useful when users turn off their
machine while it is still connected to a server.
The keepalive option is not useful for datagram (UDP) services.
.IP "linger number_of_seconds"
Specifies how long the kernel will try to deliver not-yet delivered
data after the server process closes a connection.
.SH USERNAME LOOKUP
.IP "rfc931 [ timeout_in_seconds ]"
Look up the client user name with the RFC 931 (TAP, IDENT, RFC 1413)
protocol.
This option is silently ignored in case of services based on
transports other than TCP.
It requires that the client system runs an RFC 931 (IDENT, etc.)
-compliant daemon, and may cause noticeable
delays with connections from non-UNIX clients.
The timeout period is optional.
If no timeout is specified a compile-time defined default
value is taken.
.SH MISCELLANEOUS
.IP "banners /some/directory"
Look for a file in `/some/directory' with the same name as the daemon
process (for example in.telnetd for the telnet service), and copy its
contents to the client.
Newline characters are replaced by carriage-return newline,
and %\*[Lt]letter\*[Gt] sequences are expanded (see
the hosts_access(5) manual page).
.sp
The tcp wrappers source code distribution provides a sample makefile
(Banners.Makefile) for convenient banner maintenance.
.sp
Warning: banners are supported for connection-oriented (TCP) network
services only.
.IP "nice [ number ]"
Change the nice value of the process (default 10).
Specify a positive value to spend more CPU resources on other processes.
.IP "setenv name value"
Place a (name, value) pair into the process environment.
The value is subjected to %\*[Lt]letter\*[Gt] expansions and
may contain whitespace (but leading and trailing blanks are stripped off).
.sp
Warning: many network daemons reset their environment before spawning a
login or shell process.
.IP "umask 022"
Like the umask command that is built into the shell.
An umask of 022 prevents the creation of files with group
and world write permission.
The umask argument should be an octal number.
.IP "user nobody"
.IP "user nobody.kmem"
Assume the privileges of the "nobody" userid (or user "nobody", group
"kmem").
The first form is useful with inetd implementations that run
all services with root privilege.
The second form is useful for services that need
special group privileges only.
.SH DIAGNOSTICS
When a syntax error is found in an access control rule, the error
is reported to the syslog daemon; further options will be ignored,
and service is denied.
.SH SEE ALSO
hosts_access(3)
hosts_access(5), the default access control language
.SH AUTHOR
.na
.nf
Wietse Venema (wietse@wzv.win.tue.nl)
Department of Mathematics and Computing Science
Eindhoven University of Technology
Den Dolech 2, P.O. Box 513,
5600 MB Eindhoven, The Netherlands
\" @(#) hosts_options.5 1.10 94/12/28 17:42:28

56
lib/libwrap/libwrap2netbsd Executable file
View File

@ -0,0 +1,56 @@
#! /bin/sh
#
# $NetBSD: libwrap2netbsd,v 1.6 2008/05/29 14:51:25 mrg Exp $
#
# Copyright (c) 1996 Matthew R. Green
# 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.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
# libwrap2netbsd: convert a libwrap tcp_wrappers source tree into a
# netbsd libwrap source tree, ready for importing.
if [ $# -ne 2 ]; then echo "libwrap2netbsd src dest"; exit 1; fi
r=$1
d=$2/libwrap
echo preparing directory $d
rm -rf $d
mkdir -p $d
# lame to have these files duplicated. but, what the hell.
echo copying tree from $r into $d
cd $r
src='hosts_access.c options.c shell_cmd.c rfc931.c eval.c hosts_ctl.c
refuse.c percent_x.c clean_exit.c fix_options.c socket.c workarounds.c
update.c misc.c diag.c'
man='hosts_access.3 hosts_access.5 hosts_options.5'
inc='tcpd.h mystdarg.h'
misc='DISCLAIMER'
pax -rvw $src $man $inc $misc $d
cd $d
echo done
exit 0

92
lib/libwrap/misc.c Normal file
View File

@ -0,0 +1,92 @@
/* $NetBSD: misc.c,v 1.10 2012/03/21 10:10:37 matt Exp $ */
/*
* Misc routines that are used by tcpd and by tcpdchk.
*
* Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
*/
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsic[] = "@(#) misc.c 1.2 96/02/11 17:01:29";
#else
__RCSID("$NetBSD: misc.c,v 1.10 2012/03/21 10:10:37 matt Exp $");
#endif
#endif
#include <sys/types.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include "tcpd.h"
/* xgets - fgets() with backslash-newline stripping */
char *
xgets(char *ptr, int len, FILE *fp)
{
int got;
char *start = ptr;
while (len > 1 && fgets(ptr, len, fp)) {
got = strlen(ptr);
if (got >= 1 && ptr[got - 1] == '\n') {
tcpd_context.line++;
if (got >= 2 && ptr[got - 2] == '\\') {
got -= 2;
} else {
return (start);
}
}
ptr += got;
len -= got;
ptr[0] = 0;
}
return (ptr > start ? start : 0);
}
/* split_at - break string at delimiter or return NULL */
char *
split_at(char *string, int delimiter)
{
char *cp;
int bracket;
bracket = 0;
for (cp = string; cp && *cp; cp++) {
switch (*cp) {
case '[':
bracket++;
break;
case ']':
bracket--;
break;
default:
if (bracket == 0 && *cp == delimiter) {
*cp++ = 0;
return cp;
}
break;
}
}
return NULL;
}
/* dot_quad_addr - convert dotted quad to internal form */
int
dot_quad_addr(char *str, unsigned long *addr)
{
struct in_addr a;
if (!inet_aton(str, &a))
return -1;
if (addr)
*addr = a.s_addr;
return 0;
}

18
lib/libwrap/mystdarg.h Normal file
View File

@ -0,0 +1,18 @@
/* $NetBSD: mystdarg.h,v 1.2 1997/10/09 21:20:37 christos Exp $ */
/*
* What follows is an attempt to unify varargs.h and stdarg.h. I'd rather
* have this than #ifdefs all over the code.
*/
#ifdef __STDC__
#include <stdarg.h>
#define VARARGS(func,type,arg) func(type arg, ...)
#define VASTART(ap,type,name) va_start(ap,name)
#define VAEND(ap) va_end(ap)
#else
#include <varargs.h>
#define VARARGS(func,type,arg) func(va_alist) va_dcl
#define VASTART(ap,type,name) {type name; va_start(ap); name = va_arg(ap, type)
#define VAEND(ap) va_end(ap);}
#endif

634
lib/libwrap/options.c Normal file
View File

@ -0,0 +1,634 @@
/* $NetBSD: options.c,v 1.16 2012/03/22 22:59:43 joerg Exp $ */
/*
* General skeleton for adding options to the access control language. The
* features offered by this module are documented in the hosts_options(5)
* manual page (source file: hosts_options.5, "nroff -man" format).
*
* Notes and warnings for those who want to add features:
*
* In case of errors, abort options processing and deny access. There are too
* many irreversible side effects to make error recovery feasible. For
* example, it makes no sense to continue after we have already changed the
* userid.
*
* In case of errors, do not terminate the process: the routines might be
* called from a long-running daemon that should run forever. Instead, call
* tcpd_jump() which does a non-local goto back into the hosts_access()
* routine.
*
* In case of severe errors, use clean_exit() instead of directly calling
* exit(), or the inetd may loop on an UDP request.
*
* In verification mode (for example, with the "tcpdmatch" command) the
* "dry_run" flag is set. In this mode, an option function should just "say"
* what it is going to do instead of really doing it.
*
* Some option functions do not return (for example, the twist option passes
* control to another program). In verification mode (dry_run flag is set)
* such options should clear the "dry_run" flag to inform the caller of this
* course of action.
*/
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#) options.c 1.17 96/02/11 17:01:31";
#else
__RCSID("$NetBSD: options.c,v 1.16 2012/03/22 22:59:43 joerg Exp $");
#endif
#endif
/* System libraries. */
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <syslog.h>
#include <unistd.h>
#include <stdlib.h>
#include <pwd.h>
#include <grp.h>
#include <ctype.h>
#include <setjmp.h>
#include <string.h>
/* Local stuff. */
#include "tcpd.h"
/* Options runtime support. */
int dry_run = 0; /* flag set in verification mode */
extern jmp_buf tcpd_buf; /* tcpd_jump() support */
/* Options parser support. */
static char whitespace_eq[] = "= \t\r\n";
#define whitespace (whitespace_eq + 1)
static char *get_field /* chew :-delimited field off string */
(char *);
static char *chop_string /* strip leading and trailing blanks */
(char *);
struct syslog_names;
static int severity_map
(const struct syslog_names *, char *);
/* List of functions that implement the options. Add yours here. */
static void user_option /* execute "user name.group" option */
(char *, struct request_info *);
static void group_option /* execute "group name" option */
(char *, struct request_info *);
static void umask_option /* execute "umask mask" option */
(char *, struct request_info *);
static void linger_option /* execute "linger time" option */
(char *, struct request_info *);
static void keepalive_option /* execute "keepalive" option */
(char *, struct request_info *);
static void spawn_option /* execute "spawn command" option */
(char *, struct request_info *);
static void twist_option /* execute "twist command" option */
(char *, struct request_info *);
static void rfc931_option /* execute "rfc931" option */
(char *, struct request_info *);
static void setenv_option /* execute "setenv name value" */
(char *, struct request_info *);
static void nice_option /* execute "nice" option */
(char *, struct request_info *);
static void severity_option /* execute "severity value" */
(char *, struct request_info *);
__dead static void allow_option /* execute "allow" option */
(char *, struct request_info *);
__dead static void deny_option /* execute "deny" option */
(char *, struct request_info *);
static void banners_option /* execute "banners path" option */
(char *, struct request_info *);
/* Structure of the options table. */
struct option {
const char *name; /* keyword name, case is ignored */
void (*func) /* function that does the real work */
(char *, struct request_info *);
int flags; /* see below... */
};
#define NEED_ARG (1<<1) /* option requires argument */
#define USE_LAST (1<<2) /* option must be last */
#define OPT_ARG (1<<3) /* option has optional argument */
#define EXPAND_ARG (1<<4) /* do %x expansion on argument */
#define need_arg(o) ((o)->flags & NEED_ARG)
#define opt_arg(o) ((o)->flags & OPT_ARG)
#define permit_arg(o) ((o)->flags & (NEED_ARG | OPT_ARG))
#define use_last(o) ((o)->flags & USE_LAST)
#define expand_arg(o) ((o)->flags & EXPAND_ARG)
/* List of known keywords. Add yours here. */
static struct option option_table[] = {
{ "user", user_option, NEED_ARG },
{ "group", group_option, NEED_ARG },
{ "umask", umask_option, NEED_ARG },
{ "linger", linger_option, NEED_ARG },
{ "keepalive", keepalive_option, 0 },
{ "spawn", spawn_option, NEED_ARG | EXPAND_ARG },
{ "twist", twist_option, NEED_ARG | EXPAND_ARG | USE_LAST },
{ "rfc931", rfc931_option, OPT_ARG },
{ "setenv", setenv_option, NEED_ARG | EXPAND_ARG },
{ "nice", nice_option, OPT_ARG },
{ "severity", severity_option, NEED_ARG },
{ "allow", allow_option, USE_LAST },
{ "deny", deny_option, USE_LAST },
{ "banners", banners_option, NEED_ARG },
{ NULL, NULL, 0 }
};
/* process_options - process access control options */
void
process_options(char *options, struct request_info *request)
{
char *key;
char *value;
char *curr_opt;
char *next_opt;
struct option *op;
char bf[BUFSIZ];
for (curr_opt = get_field(options); curr_opt; curr_opt = next_opt) {
next_opt = get_field((char *) 0);
/*
* Separate the option into name and value parts. For backwards
* compatibility we ignore exactly one '=' between name and value.
*/
curr_opt = chop_string(curr_opt);
if (*(value = curr_opt + strcspn(curr_opt, whitespace_eq))) {
if (*value != '=') {
*value++ = 0;
value += strspn(value, whitespace);
}
if (*value == '=') {
*value++ = 0;
value += strspn(value, whitespace);
}
}
if (*value == 0)
value = 0;
key = curr_opt;
/*
* Disallow missing option names (and empty option fields).
*/
if (*key == 0)
tcpd_jump("missing option name");
/*
* Lookup the option-specific info and do some common error checks.
* Delegate option-specific processing to the specific functions.
*/
for (op = option_table; op->name && STR_NE(op->name, key); op++)
/* VOID */ ;
if (op->name == 0)
tcpd_jump("bad option name: \"%s\"", key);
if (!value && need_arg(op))
tcpd_jump("option \"%s\" requires value", key);
if (value && !permit_arg(op))
tcpd_jump("option \"%s\" requires no value", key);
if (next_opt && use_last(op))
tcpd_jump("option \"%s\" must be at end", key);
if (value && expand_arg(op))
value = chop_string(percent_x(bf, sizeof(bf), value, request));
if (hosts_access_verbose)
syslog(LOG_DEBUG, "option: %s %s", key, value ? value : "");
(*(op->func)) (value, request);
}
}
/* allow_option - grant access */
/* ARGSUSED */
static void
allow_option(char *value, struct request_info *request)
{
longjmp(tcpd_buf, AC_PERMIT);
}
/* deny_option - deny access */
/* ARGSUSED */
static void
deny_option(char *value, struct request_info *request)
{
longjmp(tcpd_buf, AC_DENY);
}
/* banners_option - expand %<char>, terminate each line with CRLF */
static void
banners_option(char *value, struct request_info *request)
{
char path[MAXPATHLEN];
char ibuf[BUFSIZ];
char obuf[2 * BUFSIZ];
struct stat st;
int ch;
FILE *fp;
(void)snprintf(path, sizeof path, "%s/%s", value, eval_daemon(request));
if ((fp = fopen(path, "r")) != 0) {
while ((ch = fgetc(fp)) == 0)
write(request->fd, "", 1);
ungetc(ch, fp);
while (fgets(ibuf, sizeof(ibuf) - 2, fp)) {
if (split_at(ibuf, '\n'))
strcat(ibuf, "\r\n"); /* XXX strcat is safe */
percent_x(obuf, sizeof(obuf), ibuf, request);
write(request->fd, obuf, strlen(obuf));
}
fclose(fp);
} else if (stat(value, &st) < 0) {
tcpd_warn("%s: %m", value);
}
}
/* group_option - switch group id */
/* ARGSUSED */
static void
group_option(char *value, struct request_info *request)
{
struct group grs, *grp;
char grbuf[1024];
(void)getgrnam_r(value, &grs, grbuf, sizeof(grbuf), &grp);
if (grp == NULL)
tcpd_jump("unknown group: \"%s\"", value);
if (dry_run == 0 && setgid(grp->gr_gid))
tcpd_jump("setgid(%s): %m", value);
}
/* user_option - switch user id */
/* ARGSUSED */
static void
user_option(char *value, struct request_info *request)
{
struct passwd *pwd, pws;
char *group;
char pwbuf[1024];
if ((group = split_at(value, '.')) != 0)
group_option(group, request);
(void)getpwnam_r(value, &pws, pwbuf, sizeof(pwbuf), &pwd);
if (pwd == NULL)
tcpd_jump("unknown user: \"%s\"", value);
if (dry_run == 0 && setuid(pwd->pw_uid))
tcpd_jump("setuid(%s): %m", value);
}
/* umask_option - set file creation mask */
/* ARGSUSED */
static void
umask_option(char *value, struct request_info *request)
{
unsigned mask;
char junk;
if (sscanf(value, "%o%c", &mask, &junk) != 1 || (mask & 0777) != mask)
tcpd_jump("bad umask value: \"%s\"", value);
(void) umask(mask);
}
/* spawn_option - spawn a shell command and wait */
/* ARGSUSED */
static void
spawn_option(char *value, struct request_info *request)
{
if (dry_run == 0)
shell_cmd(value);
}
/* linger_option - set the socket linger time (Marc Boucher <marc@cam.org>) */
/* ARGSUSED */
static void
linger_option(char *value, struct request_info *request)
{
struct linger linger;
char junk;
if (sscanf(value, "%d%c", &linger.l_linger, &junk) != 1
|| linger.l_linger < 0)
tcpd_jump("bad linger value: \"%s\"", value);
if (dry_run == 0) {
linger.l_onoff = (linger.l_linger != 0);
if (setsockopt(request->fd, SOL_SOCKET, SO_LINGER, (char *) &linger,
sizeof(linger)) < 0)
tcpd_warn("setsockopt SO_LINGER %d: %m", linger.l_linger);
}
}
/* keepalive_option - set the socket keepalive option */
/* ARGSUSED */
static void
keepalive_option(char *value, struct request_info *request)
{
static int on = 1;
if (dry_run == 0 && setsockopt(request->fd, SOL_SOCKET, SO_KEEPALIVE,
(char *) &on, sizeof(on)) < 0)
tcpd_warn("setsockopt SO_KEEPALIVE: %m");
}
/* nice_option - set nice value */
/* ARGSUSED */
static void
nice_option(char *value, struct request_info *request)
{
int niceval = 10;
char junk;
if (value != 0 && sscanf(value, "%d%c", &niceval, &junk) != 1)
tcpd_jump("bad nice value: \"%s\"", value);
if (dry_run == 0 && nice(niceval) < 0)
tcpd_warn("nice(%d): %m", niceval);
}
/* twist_option - replace process by shell command */
static void
twist_option(char *value, struct request_info *request)
{
if (dry_run != 0) {
dry_run = 0;
} else {
if (resident > 0)
tcpd_jump("twist option in resident process");
syslog(deny_severity, "twist %s to %s", eval_client(request), value);
/* Before switching to the shell, set up stdin, stdout and stderr. */
#define maybe_dup2(from, to) ((from == to) ? to : (close(to), dup(from)))
if (maybe_dup2(request->fd, 0) != 0 ||
maybe_dup2(request->fd, 1) != 1 ||
maybe_dup2(request->fd, 2) != 2) {
tcpd_warn("twist_option: dup: %m");
} else {
if (request->fd > 2)
close(request->fd);
(void) execl("/bin/sh", "sh", "-c", value, (char *) 0);
tcpd_warn("twist_option: /bin/sh: %m");
}
/* Something went wrong: we MUST terminate the process. */
clean_exit(request);
}
}
/* rfc931_option - look up remote user name */
static void
rfc931_option(char *value, struct request_info *request)
{
int timeout;
char junk;
if (value != 0) {
if (sscanf(value, "%d%c", &timeout, &junk) != 1 || timeout <= 0)
tcpd_jump("bad rfc931 timeout: \"%s\"", value);
rfc931_timeout = timeout;
}
(void) eval_user(request);
}
/* setenv_option - set environment variable */
/* ARGSUSED */
static void
setenv_option(char *value, struct request_info *request)
{
char *var_value;
if (*(var_value = value + strcspn(value, whitespace)))
*var_value++ = 0;
if (setenv(chop_string(value), chop_string(var_value), 1))
tcpd_jump("memory allocation failure");
}
/*
* The severity option goes last because it comes with a huge amount of ugly
* #ifdefs and tables.
*/
struct syslog_names {
const char *name;
int value;
};
static const struct syslog_names log_fac[] = {
#ifdef LOG_KERN
{ "kern", LOG_KERN },
#endif
#ifdef LOG_USER
{ "user", LOG_USER },
#endif
#ifdef LOG_MAIL
{ "mail", LOG_MAIL },
#endif
#ifdef LOG_DAEMON
{ "daemon", LOG_DAEMON },
#endif
#ifdef LOG_AUTH
{ "auth", LOG_AUTH },
#endif
#ifdef LOG_LPR
{ "lpr", LOG_LPR },
#endif
#ifdef LOG_NEWS
{ "news", LOG_NEWS },
#endif
#ifdef LOG_UUCP
{ "uucp", LOG_UUCP },
#endif
#ifdef LOG_CRON
{ "cron", LOG_CRON },
#endif
#ifdef LOG_AUTHPRIV
{ "authpriv", LOG_AUTHPRIV },
#endif
#ifdef LOG_FTP
{ "ftp", LOG_FTP },
#endif
#ifdef LOG_LOCAL0
{ "local0", LOG_LOCAL0 },
#endif
#ifdef LOG_LOCAL1
{ "local1", LOG_LOCAL1 },
#endif
#ifdef LOG_LOCAL2
{ "local2", LOG_LOCAL2 },
#endif
#ifdef LOG_LOCAL3
{ "local3", LOG_LOCAL3 },
#endif
#ifdef LOG_LOCAL4
{ "local4", LOG_LOCAL4 },
#endif
#ifdef LOG_LOCAL5
{ "local5", LOG_LOCAL5 },
#endif
#ifdef LOG_LOCAL6
{ "local6", LOG_LOCAL6 },
#endif
#ifdef LOG_LOCAL7
{ "local7", LOG_LOCAL7 },
#endif
{ NULL, 0 }
};
static const struct syslog_names log_sev[] = {
#ifdef LOG_EMERG
{ "emerg", LOG_EMERG },
#endif
#ifdef LOG_ALERT
{ "alert", LOG_ALERT },
#endif
#ifdef LOG_CRIT
{ "crit", LOG_CRIT },
#endif
#ifdef LOG_ERR
{ "err", LOG_ERR },
#endif
#ifdef LOG_WARNING
{ "warning", LOG_WARNING },
#endif
#ifdef LOG_NOTICE
{ "notice", LOG_NOTICE },
#endif
#ifdef LOG_INFO
{ "info", LOG_INFO },
#endif
#ifdef LOG_DEBUG
{ "debug", LOG_DEBUG },
#endif
{ NULL, 0 }
};
/* severity_map - lookup facility or severity value */
static int
severity_map(const struct syslog_names *table, char *name)
{
const struct syslog_names *t;
for (t = table; t->name; t++)
if (STR_EQ(t->name, name))
return (t->value);
tcpd_jump("bad syslog facility or severity: \"%s\"", name);
/* NOTREACHED */
return -1;
}
/* severity_option - change logging severity for this event (Dave Mitchell) */
/* ARGSUSED */
static void
severity_option(char *value, struct request_info *request)
{
char *level = split_at(value, '.');
allow_severity = deny_severity = level ?
severity_map(log_fac, value) | severity_map(log_sev, level) :
severity_map(log_sev, value);
}
/* get_field - return pointer to next field in string */
static char *
get_field(char *string)
{
static char nul = '\0';
static char *last = &nul;
char *src;
char *dst;
char *ret;
int ch;
/*
* This function returns pointers to successive fields within a given
* string. ":" is the field separator; warn if the rule ends in one. It
* replaces a "\:" sequence by ":", without treating the result of
* substitution as field terminator. A null argument means resume search
* where the previous call terminated. This function destroys its
* argument.
*
* Work from explicit source or from memory. While processing \: we
* overwrite the input. This way we do not have to maintain buffers for
* copies of input fields.
*/
src = dst = ret = (string ? string : last);
if (src[0] == 0)
return (0);
while ((ch = *src) != '\0') {
if (ch == ':') {
if (*++src == 0)
tcpd_warn("rule ends in \":\"");
break;
}
if (ch == '\\' && src[1] == ':')
src++;
*dst++ = *src++;
}
last = src;
*dst = 0;
return (ret);
}
/* chop_string - strip leading and trailing blanks from string */
static char *
chop_string(register char *string)
{
char *start = NULL;
char *end = NULL;
char *cp;
for (cp = string; *cp; cp++) {
if (!isspace((unsigned char) *cp)) {
if (start == 0)
start = cp;
end = cp;
}
}
return (start ? (end[1] = 0, start) : cp);
}

92
lib/libwrap/percent_x.c Normal file
View File

@ -0,0 +1,92 @@
/* $NetBSD: percent_x.c,v 1.5 2012/03/21 10:10:37 matt Exp $ */
/*
* percent_x() takes a string and performs %<char> expansions. It aborts the
* program when the expansion would overflow the output buffer. The result
* of %<char> expansion may be passed on to a shell process. For this
* reason, characters with a special meaning to shells are replaced by
* underscores.
*
* Diagnostics are reported through syslog(3).
*
* Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
*/
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#) percent_x.c 1.4 94/12/28 17:42:37";
#else
__RCSID("$NetBSD: percent_x.c,v 1.5 2012/03/21 10:10:37 matt Exp $");
#endif
#endif
/* System libraries. */
#include <stdio.h>
#include <syslog.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
/* Local stuff. */
#include "tcpd.h"
/* percent_x - do %<char> expansion, abort if result buffer is too small */
char *
percent_x(char *result, int result_len, char *string,
struct request_info *request)
{
char *bp = result;
char *end = result + result_len - 1; /* end of result buffer */
char *expansion;
size_t expansion_len;
static const char ok_chars[] = "1234567890!@%-_=+:,./"
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char *str = string;
char *cp;
int ch;
/*
* Warning: we may be called from a child process or after pattern
* matching, so we cannot use clean_exit() or tcpd_jump().
*/
while (*str) {
if (*str == '%' && (ch = str[1]) != 0) {
str += 2;
expansion =
ch == 'a' ? eval_hostaddr(request->client) :
ch == 'A' ? eval_hostaddr(request->server) :
ch == 'c' ? eval_client(request) :
ch == 'd' ? eval_daemon(request) :
ch == 'h' ? eval_hostinfo(request->client) :
ch == 'H' ? eval_hostinfo(request->server) :
ch == 'n' ? eval_hostname(request->client) :
ch == 'N' ? eval_hostname(request->server) :
ch == 'p' ? eval_pid(request) :
ch == 's' ? eval_server(request) :
ch == 'u' ? eval_user(request) :
ch == '%' ? __UNCONST("%")
: (tcpd_warn("unrecognized %%%c", ch), __UNCONST(""));
for (cp = expansion; *(cp += strspn(cp, ok_chars)); /* */ )
*cp = '_';
expansion_len = cp - expansion;
} else {
expansion = str++;
expansion_len = 1;
}
if (bp + expansion_len >= end) {
tcpd_warn("percent_x: expansion too long: %.30s...", result);
sleep(5);
exit(0);
}
memcpy(bp, expansion, expansion_len);
bp += expansion_len;
}
*bp = 0;
return (result);
}

39
lib/libwrap/refuse.c Normal file
View File

@ -0,0 +1,39 @@
/* $NetBSD: refuse.c,v 1.5 2012/03/21 10:10:37 matt Exp $ */
/*
* refuse() reports a refused connection, and takes the consequences: in
* case of a datagram-oriented service, the unread datagram is taken from
* the input queue (or inetd would see the same datagram again and again);
* the program is terminated.
*
* Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
*/
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#) refuse.c 1.5 94/12/28 17:42:39";
#else
__RCSID("$NetBSD: refuse.c,v 1.5 2012/03/21 10:10:37 matt Exp $");
#endif
#endif
/* System libraries. */
#include <stdio.h>
#include <syslog.h>
/* Local stuff. */
#include "tcpd.h"
/* refuse - refuse request */
void
refuse(struct request_info *request)
{
syslog(deny_severity, "refused connect from %s", eval_client(request));
clean_exit(request);
/* NOTREACHED */
}

237
lib/libwrap/rfc931.c Normal file
View File

@ -0,0 +1,237 @@
/* $NetBSD: rfc931.c,v 1.10 2012/03/22 22:59:43 joerg Exp $ */
/*
* rfc931() speaks a common subset of the RFC 931, AUTH, TAP, IDENT and RFC
* 1413 protocols. It queries an RFC 931 etc. compatible daemon on a remote
* host to look up the owner of a connection. The information should not be
* used for authentication purposes. This routine intercepts alarm signals.
*
* Diagnostics are reported through syslog(3).
*
* Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
*/
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#) rfc931.c 1.10 95/01/02 16:11:34";
#else
__RCSID("$NetBSD: rfc931.c,v 1.10 2012/03/22 22:59:43 joerg Exp $");
#endif
#endif
/* System libraries. */
#include <stdio.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>
#include <setjmp.h>
#include <signal.h>
#include <string.h>
/* Local stuff. */
#include "tcpd.h"
#define RFC931_PORT 113 /* Semi-well-known port */
#define ANY_PORT 0 /* Any old port will do */
int rfc931_timeout = RFC931_TIMEOUT;/* Global so it can be changed */
static jmp_buf timebuf;
static FILE *fsocket(int, int, int);
static void timeout(int) __dead;
/* fsocket - open stdio stream on top of socket */
static FILE *
fsocket(int domain, int type, int protocol)
{
int s;
FILE *fp;
if ((s = socket(domain, type, protocol)) < 0) {
tcpd_warn("socket: %m");
return (0);
} else {
if ((fp = fdopen(s, "r+")) == 0) {
tcpd_warn("fdopen: %m");
close(s);
}
return (fp);
}
}
/* timeout - handle timeouts */
static void
timeout(int sig)
{
longjmp(timebuf, sig);
}
/* rfc931 - return remote user name, given socket structures */
void
rfc931(struct sockaddr *rmt_sin, struct sockaddr *our_sin, char *dest)
{
unsigned rmt_port;
unsigned our_port;
struct sockaddr_storage rmt_query_sin;
struct sockaddr_storage our_query_sin;
char user[256]; /* XXX */
char buffer[512]; /* XXX */
char *cp;
char *result = unknown;
FILE *fp;
volatile int salen;
u_short * volatile rmt_portp;
u_short * volatile our_portp;
/* address family must be the same */
if (rmt_sin->sa_family != our_sin->sa_family) {
strlcpy(dest, result, STRING_LENGTH);
return;
}
switch (rmt_sin->sa_family) {
case AF_INET:
salen = sizeof(struct sockaddr_in);
rmt_portp = &(((struct sockaddr_in *)rmt_sin)->sin_port);
break;
#ifdef INET6
case AF_INET6:
salen = sizeof(struct sockaddr_in6);
rmt_portp = &(((struct sockaddr_in6 *)rmt_sin)->sin6_port);
break;
#endif
default:
strlcpy(dest, result, STRING_LENGTH);
return;
}
switch (our_sin->sa_family) {
case AF_INET:
our_portp = &(((struct sockaddr_in *)our_sin)->sin_port);
break;
#ifdef INET6
case AF_INET6:
our_portp = &(((struct sockaddr_in6 *)our_sin)->sin6_port);
break;
#endif
default:
strlcpy(dest, result, STRING_LENGTH);
return;
}
#ifdef __GNUC__
(void)&result; /* Avoid longjmp clobbering */
(void)&fp; /* XXX gcc */
#endif
/*
* Use one unbuffered stdio stream for writing to and for reading from
* the RFC931 etc. server. This is done because of a bug in the SunOS
* 4.1.x stdio library. The bug may live in other stdio implementations,
* too. When we use a single, buffered, bidirectional stdio stream ("r+"
* or "w+" mode) we read our own output. Such behaviour would make sense
* with resources that support random-access operations, but not with
* sockets.
*/
if ((fp = fsocket(rmt_sin->sa_family, SOCK_STREAM, 0)) != 0) {
setbuf(fp, (char *) 0);
/*
* Set up a timer so we won't get stuck while waiting for the server.
*/
if (setjmp(timebuf) == 0) {
signal(SIGALRM, timeout);
alarm(rfc931_timeout);
/*
* Bind the local and remote ends of the query socket to the same
* IP addresses as the connection under investigation. We go
* through all this trouble because the local or remote system
* might have more than one network address. The RFC931 etc.
* client sends only port numbers; the server takes the IP
* addresses from the query socket.
*/
memcpy(&our_query_sin, our_sin, salen);
switch (our_query_sin.ss_family) {
case AF_INET:
((struct sockaddr_in *)&our_query_sin)->sin_port =
htons(ANY_PORT);
break;
#ifdef INET6
case AF_INET6:
((struct sockaddr_in6 *)&our_query_sin)->sin6_port =
htons(ANY_PORT);
break;
#endif
}
memcpy(&rmt_query_sin, rmt_sin, salen);
switch (rmt_query_sin.ss_family) {
case AF_INET:
((struct sockaddr_in *)&rmt_query_sin)->sin_port =
htons(RFC931_PORT);
break;
#ifdef INET6
case AF_INET6:
((struct sockaddr_in6 *)&rmt_query_sin)->sin6_port =
htons(RFC931_PORT);
break;
#endif
}
if (bind(fileno(fp), (struct sockaddr *) & our_query_sin,
salen) >= 0 &&
connect(fileno(fp), (struct sockaddr *) & rmt_query_sin,
salen) >= 0) {
/*
* Send query to server. Neglect the risk that a 13-byte
* write would have to be fragmented by the local system and
* cause trouble with buggy System V stdio libraries.
*/
fprintf(fp, "%u,%u\r\n",
ntohs(*rmt_portp),
ntohs(*our_portp));
fflush(fp);
/*
* Read response from server. Use fgets()/sscanf() so we can
* work around System V stdio libraries that incorrectly
* assume EOF when a read from a socket returns less than
* requested.
*/
if (fgets(buffer, sizeof(buffer), fp) != 0
&& ferror(fp) == 0 && feof(fp) == 0
&& sscanf(buffer, "%u , %u : USERID :%*[^:]:%255s",
&rmt_port, &our_port, user) == 3
&& ntohs(*rmt_portp) == rmt_port
&& ntohs(*our_portp) == our_port) {
/*
* Strip trailing carriage return. It is part of the
* protocol, not part of the data.
*/
if ((cp = strchr(user, '\r')) != NULL)
*cp = '\0';
result = user;
}
}
alarm(0);
}
fclose(fp);
}
strlcpy(dest, result, STRING_LENGTH);
}

98
lib/libwrap/shell_cmd.c Normal file
View File

@ -0,0 +1,98 @@
/* $NetBSD: shell_cmd.c,v 1.7 2012/03/22 22:59:43 joerg Exp $ */
/*
* shell_cmd() takes a shell command after %<character> substitutions. The
* command is executed by a /bin/sh child process, with standard input,
* standard output and standard error connected to /dev/null.
*
* Diagnostics are reported through syslog(3).
*
* Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
*/
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#) shell_cmd.c 1.5 94/12/28 17:42:44";
#else
__RCSID("$NetBSD: shell_cmd.c,v 1.7 2012/03/22 22:59:43 joerg Exp $");
#endif
#endif
/* System libraries. */
#include <sys/types.h>
#include <sys/param.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <syslog.h>
#include <string.h>
/* Local stuff. */
#include "tcpd.h"
/* Forward declarations. */
static void do_child(char *) __dead;
/* shell_cmd - execute shell command */
void
shell_cmd(char *command)
{
int child_pid;
int wait_pid;
/*
* Most of the work is done within the child process, to minimize the
* risk of damage to the parent.
*/
switch (child_pid = fork()) {
case -1: /* error */
tcpd_warn("cannot fork: %m");
break;
case 00: /* child */
do_child(command);
/* NOTREACHED */
default: /* parent */
while ((wait_pid = wait((int *) 0)) != -1 && wait_pid != child_pid)
/* void */ ;
}
}
/* do_child - exec command with { stdin, stdout, stderr } to /dev/null */
static void
do_child(char *command)
{
int tmp_fd;
/*
* Systems with POSIX sessions may send a SIGHUP to grandchildren if the
* child exits first. This is sick, sessions were invented for terminals.
*/
signal(SIGHUP, SIG_IGN);
/* Set up new stdin, stdout, stderr, and exec the shell command. */
for (tmp_fd = 0; tmp_fd < 3; tmp_fd++)
(void) close(tmp_fd);
if (open("/dev/null", 2) != 0) {
tcpd_warn("open /dev/null: %m");
} else if (dup(0) != 1 || dup(0) != 2) {
tcpd_warn("dup: %m");
} else {
(void) execl("/bin/sh", "sh", "-c", command, (char *) 0);
tcpd_warn("execl /bin/sh: %m");
}
/* Something went wrong. We MUST terminate the child process. */
_exit(0);
}

View File

@ -0,0 +1,5 @@
# $NetBSD: shlib_version,v 1.6 2009/01/11 03:07:50 christos Exp $
# Remember to update distrib/sets/lists/base/shl.* when changing
#
major=1
minor=0

291
lib/libwrap/socket.c Normal file
View File

@ -0,0 +1,291 @@
/* $NetBSD: socket.c,v 1.19 2012/03/21 10:10:37 matt Exp $ */
/*
* This module determines the type of socket (datagram, stream), the client
* socket address and port, the server socket address and port. In addition,
* it provides methods to map a transport address to a printable host name
* or address. Socket address information results are in static memory.
*
* The result from the hostname lookup method is STRING_PARANOID when a host
* pretends to have someone elses name, or when a host name is available but
* could not be verified.
*
* When lookup or conversion fails the result is set to STRING_UNKNOWN.
*
* Diagnostics are reported through syslog(3).
*
* Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
*/
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#) socket.c 1.15 97/03/21 19:27:24";
#else
__RCSID("$NetBSD: socket.c,v 1.19 2012/03/21 10:10:37 matt Exp $");
#endif
#endif
/* System libraries. */
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <syslog.h>
#include <string.h>
#include <arpa/inet.h>
/* Local stuff. */
#include "tcpd.h"
/* Forward declarations. */
#ifdef APPEND_DOT
static const char *append_dot __P((const char *));
#endif
static void sock_sink __P((int));
#ifdef APPEND_DOT
/*
* Speed up DNS lookups by terminating the host name with a dot. Should be
* done with care. The speedup can give problems with lookups from sources
* that lack DNS-style trailing dot magic, such as local files or NIS maps.
*/
static const char *
append_dot(name)
const char *name;
{
static char hbuf[MAXHOSTNAMELEN + 1];
/*
* Don't append dots to unqualified names. Such names are likely to come
* from local hosts files or from NIS.
*/
if (strchr(name, '.') == 0 || strlen(name) + 2 > sizeof(hbuf))
strlcpy(hbuf, name, sizeof(hbuf));
else
(void)snprintf(hbuf, sizeof(hbuf), "%s.", name);
return hbuf;
}
#endif
/* sock_host - look up endpoint addresses and install conversion methods */
void
sock_host(struct request_info *request)
{
static struct sockaddr_storage client;
static struct sockaddr_storage server;
socklen_t len;
char buf[BUFSIZ];
int fd = request->fd;
sock_methods(request);
/*
* Look up the client host address. Hal R. Brand <BRAND@addvax.llnl.gov>
* suggested how to get the client host info in case of UDP connections:
* peek at the first message without actually looking at its contents. We
* really should verify that client.sin_family gets the value AF_INET,
* but this program has already caused too much grief on systems with
* broken library code.
*
* XXX the last sentence is untrue as we support AF_INET6 as well :-)
*/
if (request->client->sin == NULL) {
len = sizeof(client);
if (getpeername(fd, (struct sockaddr *)(void *)& client, &len) < 0) {
request->sink = sock_sink;
len = sizeof(client);
if (recvfrom(fd, buf, sizeof(buf), MSG_PEEK,
(struct sockaddr *) & client, &len) < 0) {
tcpd_warn("can't get client address: %m");
return; /* give up */
}
#ifdef really_paranoid
memset(buf, 0, sizeof(buf));
#endif
}
request->client->sin = (struct sockaddr *)&client;
}
/*
* Determine the server binding. This is used for client username
* lookups, and for access control rules that trigger on the server
* address or name.
*/
if (request->server->sin == NULL) {
len = sizeof(server);
if (getsockname(fd, (struct sockaddr *) & server, &len) < 0) {
tcpd_warn("getsockname: %m");
return;
}
request->server->sin = (struct sockaddr *)&server;
}
}
/* sock_hostaddr - map endpoint address to printable form */
void
sock_hostaddr(struct host_info *host)
{
struct sockaddr *sa = host->sin;
if (!sa)
return;
host->addr[0] = '\0';
getnameinfo(sa, sa->sa_len, host->addr, sizeof(host->addr),
NULL, 0, NI_NUMERICHOST);
}
/* sock_hostname - map endpoint address to host name */
void
sock_hostname(struct host_info *host)
{
struct sockaddr *sa = host->sin;
char h1[NI_MAXHOST], h2[NI_MAXHOST];
struct addrinfo hints, *res, *res0;
#ifdef INET6
struct sockaddr_in tmp;
#endif
if (!sa)
return;
#ifdef INET6
/* special case on reverse lookup: mapped addr. I hate it */
if (sa->sa_family == AF_INET6 &&
IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)sa)->sin6_addr)) {
memset(&tmp, 0, sizeof(tmp));
tmp.sin_family = AF_INET;
tmp.sin_len = sizeof(struct sockaddr_in);
memcpy(&tmp.sin_addr,
&((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr[12], 4);
sa = (struct sockaddr *)&tmp;
}
#endif
if (getnameinfo(sa, sa->sa_len, h1, sizeof(h1), NULL, 0,
NI_NUMERICHOST) != 0) {
return;
}
if (getnameinfo(sa, sa->sa_len, host->name, sizeof(host->name), NULL, 0,
NI_NAMEREQD) == 0) {
/*
* if reverse lookup result looks like a numeric hostname,
* someone is trying to trick us by PTR record like following:
* 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5
*/
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_DGRAM; /*dummy*/
hints.ai_flags = AI_NUMERICHOST;
#ifdef APPEND_DOT
if (getaddrinfo(append_dot(host->name), "0", &hints, &res0) == 0)
#else
if (getaddrinfo(host->name, "0", &hints, &res0) == 0)
#endif
{
tcpd_warn("Nasty PTR record is configured");
freeaddrinfo(res0);
/* name is bad, clobber it */
(void)strlcpy(host->name, paranoid, sizeof(host->name));
return;
}
/*
* Verify that the address is a member of the address list returned
* by getaddrinfo(hostname).
*
* Verify also that getnameinfo() and getaddrinfo() return the same
* hostname, or rshd and rlogind may still end up being spoofed.
*
* On some sites, getaddrinfo("localhost") returns "localhost.domain".
* This is a DNS artefact. We treat it as a special case. When we
* can't believe the address list from getaddrinfo("localhost")
* we're in big trouble anyway.
*/
memset(&hints, 0, sizeof(hints));
hints.ai_family = sa->sa_family;
hints.ai_socktype = SOCK_DGRAM; /*dummy*/
hints.ai_flags = AI_CANONNAME;
#ifdef APPEND_DOT
if (getaddrinfo(append_dot(host->name), "0", &hints, &res0) != 0)
#else
if (getaddrinfo(host->name, "0", &hints, &res0) != 0)
#endif
{
/*
* Unable to verify that the host name matches the address. This
* may be a transient problem or a botched name server setup.
*/
tcpd_warn("can't verify hostname: getaddrinfo(%s, %d) failed",
host->name, hints.ai_family);
} else if (res0->ai_canonname &&
STR_NE(host->name, res0->ai_canonname) &&
STR_NE(host->name, "localhost")) {
/*
* The getnameinfo() and getaddrinfo() calls did not return
* the same hostname. This could be a nameserver configuration
* problem. It could also be that someone is trying to spoof us.
*/
tcpd_warn("host name/name mismatch: %s != %s",
host->name, res0->ai_canonname);
freeaddrinfo(res0);
} else {
/*
* The address should be a member of the address list returned by
* getaddrinfo().
*/
for (res = res0; res; res = res->ai_next) {
if (getnameinfo(res->ai_addr, res->ai_addrlen, h2, sizeof(h2),
NULL, 0, NI_NUMERICHOST) != 0) {
continue;
}
if (STR_EQ(h1, h2)) {
freeaddrinfo(res0);
return;
}
}
/*
* The host name does not map to the initial address. Perhaps
* someone has messed up. Perhaps someone compromised a name
* server.
*/
tcpd_warn("host name/address mismatch: %s != %s", h1,
res0->ai_canonname ? res0->ai_canonname : "?");
freeaddrinfo(res0);
}
/* name is bad, clobber it */
(void)strlcpy(host->name, paranoid, sizeof(host->name));
}
}
/* sock_sink - absorb unreceived IP datagram */
static void
sock_sink(int fd)
{
char buf[BUFSIZ];
struct sockaddr_storage ss;
socklen_t size = sizeof(ss);
/*
* Eat up the not-yet received datagram. Some systems insist on a
* non-zero source address argument in the recvfrom() call below.
*/
(void) recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) & ss, &size);
}

209
lib/libwrap/tcpd.h Normal file
View File

@ -0,0 +1,209 @@
/* $NetBSD: tcpd.h,v 1.14 2012/03/22 22:59:43 joerg Exp $ */
/*
* @(#) tcpd.h 1.5 96/03/19 16:22:24
*
* Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
*/
#include <sys/cdefs.h>
#include <stdio.h>
/* Structure to describe one communications endpoint. */
#define STRING_LENGTH 128 /* hosts, users, processes */
struct host_info {
char name[STRING_LENGTH]; /* access via eval_hostname(host) */
char addr[STRING_LENGTH]; /* access via eval_hostaddr(host) */
struct sockaddr *sin; /* socket address or 0 */
struct t_unitdata *unit; /* TLI transport address or 0 */
struct request_info *request; /* for shared information */
};
/* Structure to describe what we know about a service request. */
struct request_info {
int fd; /* socket handle */
char user[STRING_LENGTH]; /* access via eval_user(request) */
char daemon[STRING_LENGTH]; /* access via eval_daemon(request) */
char pid[10]; /* access via eval_pid(request) */
struct host_info client[1]; /* client endpoint info */
struct host_info server[1]; /* server endpoint info */
void (*sink)(int); /* datagram sink function or 0 */
void (*hostname)(struct host_info *); /* address to printable hostname */
void (*hostaddr)(struct host_info *); /* address to printable address */
void (*cleanup)(void); /* cleanup function or 0 */
struct netconfig *config; /* netdir handle */
};
/* Common string operations. Less clutter should be more readable. */
#define STRN_CPY(d,s,l) { strncpy((d),(s),(l)); (d)[(l)-1] = 0; }
#define STRN_EQ(x,y,l) (strncasecmp((x),(y),(l)) == 0)
#define STRN_NE(x,y,l) (strncasecmp((x),(y),(l)) != 0)
#define STR_EQ(x,y) (strcasecmp((x),(y)) == 0)
#define STR_NE(x,y) (strcasecmp((x),(y)) != 0)
/*
* Initially, all above strings have the empty value. Information that
* cannot be determined at runtime is set to "unknown", so that we can
* distinguish between `unavailable' and `not yet looked up'. A hostname
* that we do not believe in is set to "paranoid".
*/
#define STRING_UNKNOWN "unknown" /* lookup failed */
#define STRING_PARANOID "paranoid" /* hostname conflict */
__BEGIN_DECLS
extern char unknown[];
extern char paranoid[];
__END_DECLS
#define HOSTNAME_KNOWN(s) (STR_NE((s),unknown) && STR_NE((s),paranoid))
#define NOT_INADDR(s) (s[strspn(s,"01234567890./")] != 0)
/* Global functions. */
__BEGIN_DECLS
#define fromhost sock_host /* no TLI support needed */
extern int hosts_access /* access control */
(struct request_info *);
extern int hosts_ctl /* limited interface to hosts_access */
(char *, char *, char *, char *);
extern void shell_cmd /* execute shell command */
(char *);
extern char *percent_x /* do %<char> expansion */
(char *, int, char *, struct request_info *);
extern void rfc931 /* client name from RFC 931 daemon */
(struct sockaddr *, struct sockaddr *, char *);
__dead extern void clean_exit /* clean up and exit */
(struct request_info *);
__dead extern void refuse /* clean up and exit */
(struct request_info *);
extern char *xgets /* fgets() on steroids */
(char *, int, FILE *);
extern char *split_at /* strchr() and split */
(char *, int);
extern int dot_quad_addr /* restricted inet_aton() */
(char *, unsigned long *);
/* Global variables. */
extern int allow_severity; /* for connection logging */
extern int deny_severity; /* for connection logging */
extern const char *hosts_allow_table; /* for verification mode redirection */
extern const char *hosts_deny_table; /* for verification mode redirection */
extern int hosts_access_verbose; /* for verbose matching mode */
extern int rfc931_timeout; /* user lookup timeout */
extern int resident; /* > 0 if resident process */
/*
* Routines for controlled initialization and update of request structure
* attributes. Each attribute has its own key.
*/
extern struct request_info *request_init /* initialize request */
(struct request_info *,...);
extern struct request_info *request_set /* update request structure */
(struct request_info *,...);
#define RQ_FILE 1 /* file descriptor */
#define RQ_DAEMON 2 /* server process (argv[0]) */
#define RQ_USER 3 /* client user name */
#define RQ_CLIENT_NAME 4 /* client host name */
#define RQ_CLIENT_ADDR 5 /* client host address */
#define RQ_CLIENT_SIN 6 /* client endpoint (internal) */
#define RQ_SERVER_NAME 7 /* server host name */
#define RQ_SERVER_ADDR 8 /* server host address */
#define RQ_SERVER_SIN 9 /* server endpoint (internal) */
/*
* Routines for delayed evaluation of request attributes. Each attribute
* type has its own access method. The trivial ones are implemented by
* macros. The other ones are wrappers around the transport-specific host
* name, address, and client user lookup methods. The request_info and
* host_info structures serve as caches for the lookup results.
*/
extern char *eval_user /* client user */
(struct request_info *);
extern char *eval_hostname /* printable hostname */
(struct host_info *);
extern char *eval_hostaddr /* printable host address */
(struct host_info *);
extern char *eval_hostinfo /* host name or address */
(struct host_info *);
extern char *eval_client /* whatever is available */
(struct request_info *);
extern char *eval_server /* whatever is available */
(struct request_info *);
#define eval_daemon(r) ((r)->daemon) /* daemon process name */
#define eval_pid(r) ((r)->pid) /* process id */
/* Socket-specific methods, including DNS hostname lookups. */
extern void sock_host /* look up endpoint addresses */
(struct request_info *);
extern void sock_hostname /* translate address to hostname */
(struct host_info *);
extern void sock_hostaddr /* address to printable address */
(struct host_info *);
#define sock_methods(r) \
{ (r)->hostname = sock_hostname; (r)->hostaddr = sock_hostaddr; }
/* The System V Transport-Level Interface (TLI) interface. */
#if defined(TLI) || defined(PTX) || defined(TLI_SEQUENT)
extern void tli_host /* look up endpoint addresses etc. */
(struct request_info *);
#endif
/*
* Problem reporting interface. Additional file/line context is reported
* when available. The jump buffer (tcpd_buf) is not declared here, or
* everyone would have to include <setjmp.h>.
*/
/* Report problem and proceed */
void tcpd_warn(const char *, ...) __printflike(1, 2);
/* Report problem and jump */
void tcpd_jump(const char *, ...) __dead __printflike(1, 2);
__END_DECLS
struct tcpd_context {
const char *file; /* current file */
int line; /* current line */
};
__BEGIN_DECLS
extern struct tcpd_context tcpd_context;
__END_DECLS
/*
* While processing access control rules, error conditions are handled by
* jumping back into the hosts_access() routine. This is cleaner than
* checking the return value of each and every silly little function. The
* (-1) returns are here because zero is already taken by longjmp().
*/
#define AC_PERMIT 1 /* permit access */
#define AC_DENY (-1) /* deny_access */
#define AC_ERROR AC_DENY /* XXX */
/*
* In verification mode an option function should just say what it would do,
* instead of really doing it. An option function that would not return
* should clear the dry_run flag to inform the caller of this unusual
* behavior.
*/
__BEGIN_DECLS
extern void process_options /* execute options */
(char *, struct request_info *);
extern int dry_run; /* verification flag */
extern void fix_options /* get rid of IP-level socket options */
(struct request_info *);
__END_DECLS

128
lib/libwrap/update.c Normal file
View File

@ -0,0 +1,128 @@
/* $NetBSD: update.c,v 1.9 2012/03/21 10:10:37 matt Exp $ */
/*
* Routines for controlled update/initialization of request structures.
*
* request_init() initializes its argument. Pointers and string-valued members
* are initialized to zero, to indicate that no lookup has been attempted.
*
* request_set() adds information to an already initialized request structure.
*
* Both functions take a variable-length name-value list.
*
* Diagnostics are reported through syslog(3).
*
* Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
*/
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#) update.c 1.1 94/12/28 17:42:56";
#else
__RCSID("$NetBSD: update.c,v 1.9 2012/03/21 10:10:37 matt Exp $");
#endif
#endif
/* System libraries */
#include <stdio.h>
#include <syslog.h>
#include <string.h>
#include <unistd.h>
/* Local stuff. */
#include "mystdarg.h"
#include "tcpd.h"
static struct request_info *request_fill(struct request_info *, va_list);
/* request_fill - request update engine */
static struct request_info *
request_fill(struct request_info *request, va_list ap)
{
int key;
char *ptr;
while ((key = va_arg(ap, int)) > 0) {
switch (key) {
default:
tcpd_warn("request_fill: invalid key: %d", key);
return (request);
case RQ_FILE:
request->fd = va_arg(ap, int);
continue;
case RQ_CLIENT_SIN:
request->client->sin = va_arg(ap, struct sockaddr *);
continue;
case RQ_SERVER_SIN:
request->server->sin = va_arg(ap, struct sockaddr *);
continue;
/*
* All other fields are strings with the same maximal length.
*/
case RQ_DAEMON:
ptr = request->daemon;
break;
case RQ_USER:
ptr = request->user;
break;
case RQ_CLIENT_NAME:
ptr = request->client->name;
break;
case RQ_CLIENT_ADDR:
ptr = request->client->addr;
break;
case RQ_SERVER_NAME:
ptr = request->server->name;
break;
case RQ_SERVER_ADDR:
ptr = request->server->addr;
break;
}
strlcpy(ptr, va_arg(ap, char *), STRING_LENGTH);
}
return (request);
}
/* request_init - initialize request structure */
struct request_info *VARARGS(request_init, struct request_info *, request)
{
static struct request_info default_info;
struct request_info *r;
va_list ap;
/*
* Initialize data members. We do not assign default function pointer
* members, to avoid pulling in the whole socket module when it is not
* really needed.
*/
VASTART(ap, struct request_info *, request);
*request = default_info;
request->fd = -1;
(void)strlcpy(request->daemon, unknown, sizeof(request->daemon));
(void)snprintf(request->pid, sizeof(request->pid), "%d", getpid());
request->client->request = request;
request->server->request = request;
r = request_fill(request, ap);
VAEND(ap);
return (r);
}
/* request_set - update request structure */
struct request_info *VARARGS(request_set, struct request_info *, request)
{
struct request_info *r;
va_list ap;
VASTART(ap, struct request_info *, request);
r = request_fill(request, ap);
VAEND(ap);
return (r);
}