Import from tcp_wrappers_7.4.

This commit is contained in:
cjs 1997-01-11 02:09:50 +00:00
parent 84b4e9c069
commit fbb40475d5
3 changed files with 438 additions and 0 deletions

@ -0,0 +1,12 @@
# $Netbsd$
PROG= tcpdmatch
SRCS= tcpdmatch.c fakelog.c inetcf.c scaffold.c percent_m.c
MAN= tcpdmatch.8
.PATH: ${.CURDIR}/../tcpdchk
CFLAGS+= -I${.CURDIR}/../tcpdchk -I${.CURDIR}/../../lib/libwrap
LDADD= -lwrap
.include "${.CURDIR}/../../lib/libwrap/Makefile.cflags"
.include <bsd.prog.mk>

@ -0,0 +1,98 @@
.TH TCPDMATCH 8
.SH NAME
tcpdmatch \- tcp wrapper oracle
.SH SYNOPSYS
tcpdmatch [-d] [-i inet_conf] daemon client
.sp
tcpdmatch [-d] [-i inet_conf] daemon[@server] [user@]client
.SH DESCRIPTION
.PP
\fItcpdmatch\fR predicts how the tcp wrapper would handle a specific
request for service. Examples are given below.
.PP
The program examines the \fItcpd\fR access control tables (default
\fI/etc/hosts.allow\fR and \fI/etc/hosts.deny\fR) and prints its
conclusion. For maximal accuracy, it extracts additional information
from your \fIinetd\fR or \fItlid\fR network configuration file.
.PP
When \fItcpdmatch\fR finds a match in the access control tables, it
identifies the matched rule. In addition, it displays the optional
shell commands or options in a pretty-printed format; this makes it
easier for you to spot any discrepancies between what you want and what
the program understands.
.SH ARGUMENTS
The following two arguments are always required:
.IP daemon
A daemon process name. Typically, the last component of a daemon
executable pathname.
.IP client
A host name or network address, or one of the `unknown' or `paranoid'
wildcard patterns.
.sp
When a client host name is specified, \fItcpdmatch\fR gives a
prediction for each address listed for that client.
.sp
When a client address is specified, \fItcpdmatch\fR predicts what
\fItcpd\fR would do when client name lookup fails.
.PP
Optional information specified with the \fIdaemon@server\fR form:
.IP server
A host name or network address, or one of the `unknown' or `paranoid'
wildcard patterns. The default server name is `unknown'.
.PP
Optional information specified with the \fIuser@client\fR form:
.IP user
A client user identifier. Typically, a login name or a numeric userid.
The default user name is `unknown'.
.SH OPTIONS
.IP -d
Examine \fIhosts.allow\fR and \fIhosts.deny\fR files in the current
directory instead of the default ones.
.IP "-i inet_conf"
Specify this option when \fItcpdmatch\fR is unable to find your
\fIinetd.conf\fR or \fItlid.conf\fR network configuration file, or when
you suspect that the program uses the wrong one.
.SH EXAMPLES
To predict how \fItcpd\fR would handle a telnet request from the local
system:
.sp
.ti +5
tcpdmatch in.telnetd localhost
.PP
The same request, pretending that hostname lookup failed:
.sp
.ti +5
tcpdmatch in.telnetd 127.0.0.1
.PP
To predict what tcpd would do when the client name does not match the
client address:
.sp
.ti +5
tcpdmatch in.telnetd paranoid
.PP
On some systems, daemon names have no `in.' prefix, or \fItcpdmatch\fR
may need some help to locate the inetd configuration file.
.SH FILES
.PP
The default locations of the \fItcpd\fR access control tables are:
.PP
/etc/hosts.allow
.br
/etc/hosts.deny
.SH SEE ALSO
.na
.nf
tcpdchk(8), tcpd configuration checker
hosts_access(5), format of the tcpd access control tables.
hosts_options(5), format of the language extensions.
inetd.conf(5), format of the inetd control file.
tlid.conf(5), format of the tlid control file.
.SH AUTHORS
.na
.nf
Wietse Venema (wietse@wzv.win.tue.nl),
Department of Mathematics and Computing Science,
Eindhoven University of Technology
Den Dolech 2, P.O. Box 513,
5600 MB Eindhoven, The Netherlands
\" @(#) tcpdmatch.8 1.5 96/02/11 17:01:35

@ -0,0 +1,328 @@
/*
* tcpdmatch - explain what tcpd would do in a specific case
*
* usage: tcpdmatch [-d] [-i inet_conf] daemon[@host] [user@]host
*
* -d: use the access control tables in the current directory.
*
* -i: location of inetd.conf file.
*
* All errors are reported to the standard error stream, including the errors
* that would normally be reported via the syslog daemon.
*
* Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
*/
#ifndef lint
static char sccsid[] = "@(#) tcpdmatch.c 1.5 96/02/11 17:01:36";
#endif
/* System libraries. */
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <syslog.h>
#include <setjmp.h>
#include <string.h>
extern void exit();
extern int optind;
extern char *optarg;
#ifndef INADDR_NONE
#define INADDR_NONE (-1) /* XXX should be 0xffffffff */
#endif
#ifndef S_ISDIR
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
/* Application-specific. */
#include "tcpd.h"
#include "inetcf.h"
#include "scaffold.h"
static void usage();
static void tcpdmatch();
/* The main program */
int main(argc, argv)
int argc;
char **argv;
{
struct hostent *hp;
char *myname = argv[0];
char *client;
char *server;
char *addr;
char *user;
char *daemon;
struct request_info request;
int ch;
char *inetcf = 0;
int count;
struct sockaddr_in server_sin;
struct sockaddr_in client_sin;
struct stat st;
/*
* Show what rule actually matched.
*/
hosts_access_verbose = 2;
/*
* Parse the JCL.
*/
while ((ch = getopt(argc, argv, "di:")) != EOF) {
switch (ch) {
case 'd':
hosts_allow_table = "hosts.allow";
hosts_deny_table = "hosts.deny";
break;
case 'i':
inetcf = optarg;
break;
default:
usage(myname);
/* NOTREACHED */
}
}
if (argc != optind + 2)
usage(myname);
/*
* When confusion really strikes...
*/
if (check_path(REAL_DAEMON_DIR, &st) < 0) {
tcpd_warn("REAL_DAEMON_DIR %s: %m", REAL_DAEMON_DIR);
} else if (!S_ISDIR(st.st_mode)) {
tcpd_warn("REAL_DAEMON_DIR %s is not a directory", REAL_DAEMON_DIR);
}
/*
* Default is to specify a daemon process name. When daemon@host is
* specified, separate the two parts.
*/
if ((server = split_at(argv[optind], '@')) == 0)
server = unknown;
if (argv[optind][0] == '/') {
daemon = strrchr(argv[optind], '/') + 1;
tcpd_warn("%s: daemon name normalized to: %s", argv[optind], daemon);
} else {
daemon = argv[optind];
}
/*
* Default is to specify a client hostname or address. When user@host is
* specified, separate the two parts.
*/
if ((client = split_at(argv[optind + 1], '@')) != 0) {
user = argv[optind + 1];
} else {
client = argv[optind + 1];
user = unknown;
}
/*
* Analyze the inetd (or tlid) configuration file, so that we can warn
* the user about services that may not be wrapped, services that are not
* configured, or services that are wrapped in an incorrect manner. Allow
* for services that are not run from inetd, or that have tcpd access
* control built into them.
*/
inetcf = inet_cfg(inetcf);
inet_set("portmap", WR_NOT);
inet_set("rpcbind", WR_NOT);
switch (inet_get(daemon)) {
case WR_UNKNOWN:
tcpd_warn("%s: no such process name in %s", daemon, inetcf);
break;
case WR_NOT:
tcpd_warn("%s: service possibly not wrapped", daemon);
break;
}
/*
* Check accessibility of access control files.
*/
(void) check_path(hosts_allow_table, &st);
(void) check_path(hosts_deny_table, &st);
/*
* Fill in what we have figured out sofar. Use socket and DNS routines
* for address and name conversions. We attach stdout to the request so
* that banner messages will become visible.
*/
request_init(&request, RQ_DAEMON, daemon, RQ_USER, user, RQ_FILE, 1, 0);
sock_methods(&request);
/*
* If a server hostname is specified, insist that the name maps to at
* most one address. eval_hostname() warns the user about name server
* problems, while using the request.server structure as a cache for host
* address and name conversion results.
*/
if (NOT_INADDR(server) == 0 || HOSTNAME_KNOWN(server)) {
if ((hp = find_inet_addr(server)) == 0)
exit(1);
memset((char *) &server_sin, 0, sizeof(server_sin));
server_sin.sin_family = AF_INET;
request_set(&request, RQ_SERVER_SIN, &server_sin, 0);
for (count = 0; (addr = hp->h_addr_list[count]) != 0; count++) {
memcpy((char *) &server_sin.sin_addr, addr,
sizeof(server_sin.sin_addr));
/*
* Force evaluation of server host name and address. Host name
* conflicts will be reported while eval_hostname() does its job.
*/
request_set(&request, RQ_SERVER_NAME, "", RQ_SERVER_ADDR, "", 0);
if (STR_EQ(eval_hostname(request.server), unknown))
tcpd_warn("host address %s->name lookup failed",
eval_hostaddr(request.server));
}
if (count > 1) {
fprintf(stderr, "Error: %s has more than one address\n", server);
fprintf(stderr, "Please specify an address instead\n");
exit(1);
}
free((char *) hp);
} else {
request_set(&request, RQ_SERVER_NAME, server, 0);
}
/*
* If a client address is specified, we simulate the effect of client
* hostname lookup failure.
*/
if (dot_quad_addr(client) != INADDR_NONE) {
request_set(&request, RQ_CLIENT_ADDR, client, 0);
tcpdmatch(&request);
exit(0);
}
/*
* Perhaps they are testing special client hostname patterns that aren't
* really host names at all.
*/
if (NOT_INADDR(client) && HOSTNAME_KNOWN(client) == 0) {
request_set(&request, RQ_CLIENT_NAME, client, 0);
tcpdmatch(&request);
exit(0);
}
/*
* Otherwise, assume that a client hostname is specified, and insist that
* the address can be looked up. The reason for this requirement is that
* in real life the client address is available (at least with IP). Let
* eval_hostname() figure out if this host is properly registered, while
* using the request.client structure as a cache for host name and
* address conversion results.
*/
if ((hp = find_inet_addr(client)) == 0)
exit(1);
memset((char *) &client_sin, 0, sizeof(client_sin));
client_sin.sin_family = AF_INET;
request_set(&request, RQ_CLIENT_SIN, &client_sin, 0);
for (count = 0; (addr = hp->h_addr_list[count]) != 0; count++) {
memcpy((char *) &client_sin.sin_addr, addr,
sizeof(client_sin.sin_addr));
/*
* Force evaluation of client host name and address. Host name
* conflicts will be reported while eval_hostname() does its job.
*/
request_set(&request, RQ_CLIENT_NAME, "", RQ_CLIENT_ADDR, "", 0);
if (STR_EQ(eval_hostname(request.client), unknown))
tcpd_warn("host address %s->name lookup failed",
eval_hostaddr(request.client));
tcpdmatch(&request);
if (hp->h_addr_list[count + 1])
printf("\n");
}
free((char *) hp);
exit(0);
}
/* Explain how to use this program */
static void usage(myname)
char *myname;
{
fprintf(stderr, "usage: %s [-d] [-i inet_conf] daemon[@host] [user@]host\n",
myname);
fprintf(stderr, " -d: use allow/deny files in current directory\n");
fprintf(stderr, " -i: location of inetd.conf file\n");
exit(1);
}
/* Print interesting expansions */
static void expand(text, pattern, request)
char *text;
char *pattern;
struct request_info *request;
{
char buf[BUFSIZ];
if (STR_NE(percent_x(buf, sizeof(buf), pattern, request), unknown))
printf("%s %s\n", text, buf);
}
/* Try out a (server,client) pair */
static void tcpdmatch(request)
struct request_info *request;
{
int verdict;
/*
* Show what we really know. Suppress uninteresting noise.
*/
expand("client: hostname", "%n", request);
expand("client: address ", "%a", request);
expand("client: username", "%u", request);
expand("server: hostname", "%N", request);
expand("server: address ", "%A", request);
expand("server: process ", "%d", request);
/*
* Reset stuff that might be changed by options handlers. In dry-run
* mode, extension language routines that would not return should inform
* us of their plan, by clearing the dry_run flag. This is a bit clumsy
* but we must be able to verify hosts with more than one network
* address.
*/
rfc931_timeout = RFC931_TIMEOUT;
allow_severity = SEVERITY;
deny_severity = LOG_WARNING;
dry_run = 1;
/*
* When paranoid mode is enabled, access is rejected no matter what the
* access control rules say.
*/
#ifdef PARANOID
if (STR_EQ(eval_hostname(request->client), paranoid)) {
printf("access: denied (PARANOID mode)\n\n");
return;
}
#endif
/*
* Report the access control verdict.
*/
verdict = hosts_access(request);
printf("access: %s\n",
dry_run == 0 ? "delegated" :
verdict ? "granted" : "denied");
}