Import from tcd_wrappers_7.4.

This commit is contained in:
cjs 1997-01-11 02:06:52 +00:00
parent 48250187e6
commit 84b4e9c069
9 changed files with 1199 additions and 0 deletions

16
usr.sbin/tcpdchk/Makefile Normal file
View File

@ -0,0 +1,16 @@
# $Netbsd$
PROG= tcpdchk
SRCS= tcpdchk.c fakelog.c inetcf.c scaffold.c percent_m.c
MAN= tcpdchk.8
LDADD= -lwrap
CFLAGS+= -I${.CURDIR}/../../lib/libwrap
CFLAGS+=-DFACILITY=LOG_AUTHPRIV -DSEVERITY=LOG_INFO -DPARANOID
CFLAGS+=-DREAL_DAEMON_DIR=\"/usr/libexec\" -DHOSTS_ACCESS -DDAEMON_UMASK=022
CFLAGS+=-DRFC931_TIMEOUT=10 -DALWAYS_HOSTNAME -DSYS_ERRLIST_DEFINED
CFLAGS+=-DHOSTS_ALLOW=\"/etc/hosts.allow\" -DHOSTS_DENY=\"/etc/hosts.deny\"
CFLAGS+=-DPROCESS_OPTIONS
.include <bsd.prog.mk>

View File

@ -0,0 +1,62 @@
/*
* This module intercepts syslog() library calls and redirects their output
* to the standard output stream. For interactive testing.
*
* Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
*/
#ifndef lint
static char sccsid[] = "@(#) fakelog.c 1.3 94/12/28 17:42:21";
#endif
#include <stdio.h>
#include "mystdarg.h"
/* openlog - dummy */
/* ARGSUSED */
openlog(name, logopt, facility)
char *name;
int logopt;
int facility;
{
/* void */
}
/* vsyslog - format one record */
vsyslog(severity, fmt, ap)
int severity;
char *fmt;
va_list ap;
{
char buf[BUFSIZ];
vprintf(percent_m(buf, fmt), ap);
printf("\n");
fflush(stdout);
}
/* syslog - format one record */
/* VARARGS */
VARARGS(syslog, int, severity)
{
va_list ap;
char *fmt;
VASTART(ap, int, severity);
fmt = va_arg(ap, char *);
vsyslog(severity, fmt, ap);
VAEND(ap);
}
/* closelog - dummy */
closelog()
{
/* void */
}

315
usr.sbin/tcpdchk/inetcf.c Normal file
View File

@ -0,0 +1,315 @@
/*
* Routines to parse an inetd.conf or tlid.conf file. This would be a great
* job for a PERL script.
*
* Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
*/
#ifndef lint
static char sccsid[] = "@(#) inetcf.c 1.6 96/02/11 17:01:29";
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
extern int errno;
extern void exit();
#include "tcpd.h"
#include "inetcf.h"
/*
* Network configuration files may live in unusual places. Here are some
* guesses. Shorter names follow longer ones.
*/
char *inet_files[] = {
"/private/etc/inetd.conf", /* NEXT */
"/etc/inet/inetd.conf", /* SYSV4 */
"/usr/etc/inetd.conf", /* IRIX?? */
"/etc/inetd.conf", /* BSD */
"/etc/net/tlid.conf", /* SYSV4?? */
"/etc/saf/tlid.conf", /* SYSV4?? */
"/etc/tlid.conf", /* SYSV4?? */
0,
};
static void inet_chk();
static char *base_name();
/*
* Structure with everything we know about a service.
*/
struct inet_ent {
struct inet_ent *next;
int type;
char name[1];
};
static struct inet_ent *inet_list = 0;
static char whitespace[] = " \t\r\n";
/* inet_conf - read in and examine inetd.conf (or tlid.conf) entries */
char *inet_cfg(conf)
char *conf;
{
char buf[BUFSIZ];
FILE *fp;
char *service;
char *protocol;
char *user;
char *path;
char *arg0;
char *arg1;
struct tcpd_context saved_context;
char *percent_m();
int i;
struct stat st;
saved_context = tcpd_context;
/*
* The inetd.conf (or tlid.conf) information is so useful that we insist
* on its availability. When no file is given run a series of educated
* guesses.
*/
if (conf != 0) {
if ((fp = fopen(conf, "r")) == 0) {
fprintf(stderr, percent_m(buf, "open %s: %m\n"), conf);
exit(1);
}
} else {
for (i = 0; inet_files[i] && (fp = fopen(inet_files[i], "r")) == 0; i++)
/* void */ ;
if (fp == 0) {
fprintf(stderr, "Cannot find your inetd.conf or tlid.conf file.\n");
fprintf(stderr, "Please specify its location.\n");
exit(1);
}
conf = inet_files[i];
check_path(conf, &st);
}
/*
* Process the file. After the 7.0 wrapper release it became clear that
* there are many more inetd.conf formats than the 8 systems that I had
* studied. EP/IX uses a two-line specification for rpc services; HP-UX
* permits long lines to be broken with backslash-newline.
*/
tcpd_context.file = conf;
tcpd_context.line = 0;
while (xgets(buf, sizeof(buf), fp)) {
service = strtok(buf, whitespace); /* service */
if (service == 0 || *service == '#')
continue;
if (STR_NE(service, "stream") && STR_NE(service, "dgram"))
strtok((char *) 0, whitespace); /* endpoint */
protocol = strtok((char *) 0, whitespace);
(void) strtok((char *) 0, whitespace); /* wait */
if ((user = strtok((char *) 0, whitespace)) == 0)
continue;
if (user[0] == '/') { /* user */
path = user;
} else { /* path */
if ((path = strtok((char *) 0, whitespace)) == 0)
continue;
}
if (STR_EQ(path, "internal"))
continue;
if (path[strspn(path, "-0123456789")] == 0) {
/*
* ConvexOS puts RPC version numbers before path names. Jukka
* Ukkonen <ukkonen@csc.fi>.
*/
if ((path = strtok((char *) 0, whitespace)) == 0)
continue;
}
if ((arg0 = strtok((char *) 0, whitespace)) == 0) {
tcpd_warn("incomplete line");
continue;
}
if (arg0[strspn(arg0, "0123456789")] == 0) {
/*
* We're reading a tlid.conf file, the format is:
*
* ...stuff... path arg_count arguments mod_count modules
*/
if ((arg0 = strtok((char *) 0, whitespace)) == 0) {
tcpd_warn("incomplete line");
continue;
}
}
if ((arg1 = strtok((char *) 0, whitespace)) == 0)
arg1 = "";
inet_chk(protocol, path, arg0, arg1);
}
fclose(fp);
tcpd_context = saved_context;
return (conf);
}
/* inet_chk - examine one inetd.conf (tlid.conf?) entry */
static void inet_chk(protocol, path, arg0, arg1)
char *protocol;
char *path;
char *arg0;
char *arg1;
{
char daemon[BUFSIZ];
struct stat st;
int wrap_status = WR_MAYBE;
char *base_name_path = base_name(path);
char *tcpd_proc_name = (arg0[0] == '/' ? base_name(arg0) : arg0);
/*
* Always warn when the executable does not exist or when it is not
* executable.
*/
if (check_path(path, &st) < 0) {
tcpd_warn("%s: not found: %m", path);
} else if ((st.st_mode & 0100) == 0) {
tcpd_warn("%s: not executable", path);
}
/*
* Cheat on the miscd tests, nobody uses it anymore.
*/
if (STR_EQ(base_name_path, "miscd")) {
inet_set(arg0, WR_YES);
return;
}
/*
* While we are here...
*/
if (STR_EQ(tcpd_proc_name, "rexd") || STR_EQ(tcpd_proc_name, "rpc.rexd"))
tcpd_warn("%s may be an insecure service", tcpd_proc_name);
/*
* The tcpd program gets most of the attention.
*/
if (STR_EQ(base_name_path, "tcpd")) {
if (STR_EQ(tcpd_proc_name, "tcpd"))
tcpd_warn("%s is recursively calling itself", tcpd_proc_name);
wrap_status = WR_YES;
/*
* Check: some sites install the wrapper set-uid.
*/
if ((st.st_mode & 06000) != 0)
tcpd_warn("%s: file is set-uid or set-gid", path);
/*
* Check: some sites insert tcpd in inetd.conf, instead of replacing
* the daemon pathname.
*/
if (arg0[0] == '/' && STR_EQ(tcpd_proc_name, base_name(arg1)))
tcpd_warn("%s inserted before %s", path, arg0);
/*
* Check: make sure files exist and are executable. On some systems
* the network daemons are set-uid so we cannot complain. Note that
* tcpd takes the basename only in case of absolute pathnames.
*/
if (arg0[0] == '/') { /* absolute path */
if (check_path(arg0, &st) < 0) {
tcpd_warn("%s: not found: %m", arg0);
} else if ((st.st_mode & 0100) == 0) {
tcpd_warn("%s: not executable", arg0);
}
} else { /* look in REAL_DAEMON_DIR */
sprintf(daemon, "%s/%s", REAL_DAEMON_DIR, arg0);
if (check_path(daemon, &st) < 0) {
tcpd_warn("%s: not found in %s: %m",
arg0, REAL_DAEMON_DIR);
} else if ((st.st_mode & 0100) == 0) {
tcpd_warn("%s: not executable", daemon);
}
}
} else {
/*
* No tcpd program found. Perhaps they used the "simple installation"
* recipe. Look for a file with the same basename in REAL_DAEMON_DIR.
* Draw some conservative conclusions when a distinct file is found.
*/
sprintf(daemon, "%s/%s", REAL_DAEMON_DIR, arg0);
if (STR_EQ(path, daemon)) {
wrap_status = WR_NOT;
} else if (check_path(daemon, &st) >= 0) {
wrap_status = WR_MAYBE;
} else if (errno == ENOENT) {
wrap_status = WR_NOT;
} else {
tcpd_warn("%s: file lookup: %m", daemon);
wrap_status = WR_MAYBE;
}
}
/*
* Alas, we cannot wrap rpc/tcp services.
*/
if (wrap_status == WR_YES && STR_EQ(protocol, "rpc/tcp"))
tcpd_warn("%s: cannot wrap rpc/tcp services", tcpd_proc_name);
inet_set(tcpd_proc_name, wrap_status);
}
/* inet_set - remember service status */
void inet_set(name, type)
char *name;
int type;
{
struct inet_ent *ip =
(struct inet_ent *) malloc(sizeof(struct inet_ent) + strlen(name));
if (ip == 0) {
fprintf(stderr, "out of memory\n");
exit(1);
}
ip->next = inet_list;
strcpy(ip->name, name);
ip->type = type;
inet_list = ip;
}
/* inet_get - look up service status */
int inet_get(name)
char *name;
{
struct inet_ent *ip;
if (inet_list == 0)
return (WR_MAYBE);
for (ip = inet_list; ip; ip = ip->next)
if (STR_EQ(ip->name, name))
return (ip->type);
return (-1);
}
/* base_name - compute last pathname component */
static char *base_name(path)
char *path;
{
char *cp;
if ((cp = strrchr(path, '/')) != 0)
path = cp + 1;
return (path);
}

14
usr.sbin/tcpdchk/inetcf.h Normal file
View File

@ -0,0 +1,14 @@
/*
* @(#) inetcf.h 1.1 94/12/28 17:42:30
*
* Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
*/
extern char *inet_cfg(); /* read inetd.conf file */
extern void inet_set(); /* remember internet service */
extern int inet_get(); /* look up internet service */
#define WR_UNKNOWN (-1) /* service unknown */
#define WR_NOT 1 /* may not be wrapped */
#define WR_MAYBE 2 /* may be wrapped */
#define WR_YES 3 /* service is wrapped */

View File

@ -0,0 +1,43 @@
/*
* Replace %m by system error message.
*
* Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
*/
#ifndef lint
static char sccsid[] = "@(#) percent_m.c 1.1 94/12/28 17:42:37";
#endif
#include <stdio.h>
#include <errno.h>
#include <string.h>
extern int errno;
#ifndef SYS_ERRLIST_DEFINED
extern char *sys_errlist[];
extern int sys_nerr;
#endif
#include "mystdarg.h"
char *percent_m(obuf, ibuf)
char *obuf;
char *ibuf;
{
char *bp = obuf;
char *cp = ibuf;
while (*bp = *cp)
if (*cp == '%' && cp[1] == 'm') {
if (errno < sys_nerr && errno > 0) {
strcpy(bp, sys_errlist[errno]);
} else {
sprintf(bp, "Unknown error %d", errno);
}
bp += strlen(bp);
cp += 2;
} else {
bp++, cp++;
}
return (obuf);
}

213
usr.sbin/tcpdchk/scaffold.c Normal file
View File

@ -0,0 +1,213 @@
/*
* Routines for testing only. Not really industrial strength.
*
* Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
*/
#ifndef lint
static char sccs_id[] = "@(#) scaffold.c 1.5 95/01/03 09:13:48";
#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>
#ifndef INADDR_NONE
#define INADDR_NONE (-1) /* XXX should be 0xffffffff */
#endif
extern char *malloc();
/* Application-specific. */
#include "tcpd.h"
#include "scaffold.h"
/*
* These are referenced by the options module and by rfc931.c.
*/
int allow_severity = SEVERITY;
int deny_severity = LOG_WARNING;
int rfc931_timeout = RFC931_TIMEOUT;
/* dup_hostent - create hostent in one memory block */
static struct hostent *dup_hostent(hp)
struct hostent *hp;
{
struct hostent_block {
struct hostent host;
char *addr_list[1];
};
struct hostent_block *hb;
int count;
char *data;
char *addr;
for (count = 0; hp->h_addr_list[count] != 0; count++)
/* void */ ;
if ((hb = (struct hostent_block *) malloc(sizeof(struct hostent_block)
+ (hp->h_length + sizeof(char *)) * count)) == 0) {
fprintf(stderr, "Sorry, out of memory\n");
exit(1);
}
memset((char *) &hb->host, 0, sizeof(hb->host));
hb->host.h_length = hp->h_length;
hb->host.h_addr_list = hb->addr_list;
hb->host.h_addr_list[count] = 0;
data = (char *) (hb->host.h_addr_list + count + 1);
for (count = 0; (addr = hp->h_addr_list[count]) != 0; count++) {
hb->host.h_addr_list[count] = data + hp->h_length * count;
memcpy(hb->host.h_addr_list[count], addr, hp->h_length);
}
return (&hb->host);
}
/* find_inet_addr - find all addresses for this host, result to free() */
struct hostent *find_inet_addr(host)
char *host;
{
struct in_addr addr;
struct hostent *hp;
static struct hostent h;
static char *addr_list[2];
/*
* Host address: translate it to internal form.
*/
if ((addr.s_addr = dot_quad_addr(host)) != INADDR_NONE) {
h.h_addr_list = addr_list;
h.h_addr_list[0] = (char *) &addr;
h.h_length = sizeof(addr);
return (dup_hostent(&h));
}
/*
* Map host name to a series of addresses. Watch out for non-internet
* forms or aliases. The NOT_INADDR() is here in case gethostbyname() has
* been "enhanced" to accept numeric addresses. Make a copy of the
* address list so that later gethostbyXXX() calls will not clobber it.
*/
if (NOT_INADDR(host) == 0) {
tcpd_warn("%s: not an internet address", host);
return (0);
}
if ((hp = gethostbyname(host)) == 0) {
tcpd_warn("%s: host not found", host);
return (0);
}
if (hp->h_addrtype != AF_INET) {
tcpd_warn("%d: not an internet host", hp->h_addrtype);
return (0);
}
if (STR_NE(host, hp->h_name)) {
tcpd_warn("%s: hostname alias", host);
tcpd_warn("(official name: %s)", hp->h_name);
}
return (dup_hostent(hp));
}
/* check_dns - give each address thorough workout, return address count */
int check_dns(host)
char *host;
{
struct request_info request;
struct sockaddr_in sin;
struct hostent *hp;
int count;
char *addr;
if ((hp = find_inet_addr(host)) == 0)
return (0);
request_init(&request, RQ_CLIENT_SIN, &sin, 0);
sock_methods(&request);
memset((char *) &sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
for (count = 0; (addr = hp->h_addr_list[count]) != 0; count++) {
memcpy((char *) &sin.sin_addr, addr, sizeof(sin.sin_addr));
/*
* Force host name and address conversions. Use the request structure
* as a cache. Detect hostname lookup problems. Any name/name or
* name/address conflicts will be reported while eval_hostname() does
* its job.
*/
request_set(&request, RQ_CLIENT_ADDR, "", RQ_CLIENT_NAME, "", 0);
if (STR_EQ(eval_hostname(request.client), unknown))
tcpd_warn("host address %s->name lookup failed",
eval_hostaddr(request.client));
}
free((char *) hp);
return (count);
}
/* dummy function to intercept the real shell_cmd() */
/* ARGSUSED */
void shell_cmd(command)
char *command;
{
if (hosts_access_verbose)
printf("command: %s", command);
}
/* dummy function to intercept the real clean_exit() */
/* ARGSUSED */
void clean_exit(request)
struct request_info *request;
{
exit(0);
}
/* dummy function to intercept the real rfc931() */
/* ARGSUSED */
void rfc931(request)
struct request_info *request;
{
strcpy(request->user, unknown);
}
/* check_path - examine accessibility */
int check_path(path, st)
char *path;
struct stat *st;
{
struct stat stbuf;
char buf[BUFSIZ];
if (stat(path, st) < 0)
return (-1);
#ifdef notdef
if (st->st_uid != 0)
tcpd_warn("%s: not owned by root", path);
if (st->st_mode & 020)
tcpd_warn("%s: group writable", path);
#endif
if (st->st_mode & 002)
tcpd_warn("%s: world writable", path);
if (path[0] == '/' && path[1] != 0) {
strrchr(strcpy(buf, path), '/')[0] = 0;
(void) check_path(buf[0] ? buf : "/", &stbuf);
}
return (0);
}

View File

@ -0,0 +1,9 @@
/*
* @(#) scaffold.h 1.3 94/12/31 18:19:19
*
* Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
*/
extern struct hostent *find_inet_addr();
extern int check_dns();
extern int check_path();

View File

@ -0,0 +1,66 @@
.TH TCPDCHK 8
.SH NAME
tcpdchk \- tcp wrapper configuration checker
.SH SYNOPSYS
tcpdchk [-a] [-d] [-i inet_conf] [-v]
.SH DESCRIPTION
.PP
\fItcpdchk\fR examines your tcp wrapper configuration and reports all
potential and real problems it can find. The program examines the
\fItcpd\fR access control files (by default, these are
\fI/etc/hosts.allow\fR and \fI/etc/hosts.deny\fR), and compares the
entries in these files against entries in the \fIinetd\fR or \fItlid\fR
network configuration files.
.PP
\fItcpdchk\fR reports problems such as non-existent pathnames; services
that appear in \fItcpd\fR access control rules, but are not controlled
by \fItcpd\fR; services that should not be wrapped; non-existent host
names or non-internet address forms; occurrences of host aliases
instead of official host names; hosts with a name/address conflict;
inappropriate use of wildcard patterns; inappropriate use of NIS
netgroups or references to non-existent NIS netgroups; references to
non-existent options; invalid arguments to options; and so on.
.PP
Where possible, \fItcpdchk\fR provides a helpful suggestion to fix the
problem.
.SH OPTIONS
.IP -a
Report access control rules that permit access without an explicit
ALLOW keyword. This applies only when the extended access control
language is enabled (build with -DPROCESS_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 \fItcpdchk\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.
.IP -v
Display the contents of each access control rule. Daemon lists, client
lists, shell commands and options are shown 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 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
tcpdmatch(8), explain what tcpd would do in specific cases.
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
\" @(#) tcpdchk.8 1.3 95/01/08 17:00:30

461
usr.sbin/tcpdchk/tcpdchk.c Normal file
View File

@ -0,0 +1,461 @@
/*
* tcpdchk - examine all tcpd access control rules and inetd.conf entries
*
* Usage: tcpdchk [-a] [-d] [-i inet_conf] [-v]
*
* -a: complain about implicit "allow" at end of rule.
*
* -d: rules in current directory.
*
* -i: location of inetd.conf file.
*
* -v: show all rules.
*
* Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
*/
#ifndef lint
static char sccsid[] = "@(#) tcpdchk.c 1.7 96/02/11 17:01:34";
#endif
/* System libraries. */
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <syslog.h>
#include <setjmp.h>
#include <errno.h>
#include <netdb.h>
#include <string.h>
extern int errno;
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"
/*
* Stolen from hosts_access.c...
*/
static char sep[] = ", \t\n";
#define BUFLEN 2048
int resident = 0;
int hosts_access_verbose = 0;
char *hosts_allow_table = HOSTS_ALLOW;
char *hosts_deny_table = HOSTS_DENY;
extern jmp_buf tcpd_buf;
/*
* Local stuff.
*/
static void usage();
static void parse_table();
static void print_list();
static void check_daemon_list();
static void check_client_list();
static void check_daemon();
static void check_user();
static int check_host();
static int reserved_name();
#define PERMIT 1
#define DENY 0
#define YES 1
#define NO 0
static int defl_verdict;
static char *myname;
static int allow_check;
static char *inetcf;
int main(argc, argv)
int argc;
char **argv;
{
struct request_info request;
struct stat st;
int c;
myname = argv[0];
/*
* Parse the JCL.
*/
while ((c = getopt(argc, argv, "adi:v")) != EOF) {
switch (c) {
case 'a':
allow_check = 1;
break;
case 'd':
hosts_allow_table = "hosts.allow";
hosts_deny_table = "hosts.deny";
break;
case 'i':
inetcf = optarg;
break;
case 'v':
hosts_access_verbose++;
break;
default:
usage();
/* NOTREACHED */
}
}
if (argc != optind)
usage();
/*
* 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);
}
/*
* Process the inet configuration file (or its moral equivalent). This
* information is used later to find references in hosts.allow/deny to
* unwrapped services, and other possible problems.
*/
inetcf = inet_cfg(inetcf);
if (hosts_access_verbose)
printf("Using network configuration file: %s\n", inetcf);
/*
* These are not run from inetd but may have built-in access control.
*/
inet_set("portmap", WR_NOT);
inet_set("rpcbind", WR_NOT);
/*
* Check accessibility of access control files.
*/
(void) check_path(hosts_allow_table, &st);
(void) check_path(hosts_deny_table, &st);
/*
* Fake up an arbitrary service request.
*/
request_init(&request,
RQ_DAEMON, "daemon_name",
RQ_SERVER_NAME, "server_hostname",
RQ_SERVER_ADDR, "server_addr",
RQ_USER, "user_name",
RQ_CLIENT_NAME, "client_hostname",
RQ_CLIENT_ADDR, "client_addr",
RQ_FILE, 1,
0);
/*
* Examine all access-control rules.
*/
defl_verdict = PERMIT;
parse_table(hosts_allow_table, &request);
defl_verdict = DENY;
parse_table(hosts_deny_table, &request);
return (0);
}
/* usage - explain */
static void usage()
{
fprintf(stderr, "usage: %s [-a] [-d] [-i inet_conf] [-v]\n", myname);
fprintf(stderr, " -a: report rules with implicit \"ALLOW\" at end\n");
fprintf(stderr, " -d: use allow/deny files in current directory\n");
fprintf(stderr, " -i: location of inetd.conf file\n");
fprintf(stderr, " -v: list all rules\n");
exit(1);
}
/* parse_table - like table_match(), but examines _all_ entries */
static void parse_table(table, request)
char *table;
struct request_info *request;
{
FILE *fp;
int real_verdict;
char sv_list[BUFLEN]; /* becomes list of daemons */
char *cl_list; /* becomes list of requests */
char *sh_cmd; /* becomes optional shell command */
char buf[BUFSIZ];
int verdict;
struct tcpd_context saved_context;
saved_context = tcpd_context; /* stupid compilers */
if (fp = fopen(table, "r")) {
tcpd_context.file = table;
tcpd_context.line = 0;
while (xgets(sv_list, sizeof(sv_list), fp)) {
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, ':');
if (hosts_access_verbose)
printf("\n>>> Rule %s line %d:\n",
tcpd_context.file, tcpd_context.line);
if (hosts_access_verbose)
print_list("daemons: ", sv_list);
check_daemon_list(sv_list);
if (hosts_access_verbose)
print_list("clients: ", cl_list);
check_client_list(cl_list);
#ifdef PROCESS_OPTIONS
real_verdict = defl_verdict;
if (sh_cmd) {
if ((verdict = setjmp(tcpd_buf)) != 0) {
real_verdict = (verdict == AC_PERMIT);
} else {
dry_run = 1;
process_options(sh_cmd, request);
if (dry_run == 1 && real_verdict && allow_check)
tcpd_warn("implicit \"allow\" at end of rule");
}
} else if (defl_verdict && allow_check) {
tcpd_warn("implicit \"allow\" at end of rule");
}
if (hosts_access_verbose)
printf("access: %s\n", real_verdict ? "granted" : "denied");
#else
if (sh_cmd)
shell_cmd(percent_x(buf, sizeof(buf), sh_cmd, request));
if (hosts_access_verbose)
printf("access: %s\n", defl_verdict ? "granted" : "denied");
#endif
}
(void) fclose(fp);
} else if (errno != ENOENT) {
tcpd_warn("cannot open %s: %m", table);
}
tcpd_context = saved_context;
}
/* print_list - pretty-print a list */
static void print_list(title, list)
char *title;
char *list;
{
char buf[BUFLEN];
char *cp;
char *next;
fputs(title, stdout);
strcpy(buf, list);
for (cp = strtok(buf, sep); cp != 0; cp = next) {
fputs(cp, stdout);
next = strtok((char *) 0, sep);
if (next != 0)
fputs(" ", stdout);
}
fputs("\n", stdout);
}
/* check_daemon_list - criticize daemon list */
static void check_daemon_list(list)
char *list;
{
char buf[BUFLEN];
char *cp;
char *host;
int daemons = 0;
strcpy(buf, list);
for (cp = strtok(buf, sep); cp != 0; cp = strtok((char *) 0, sep)) {
if (STR_EQ(cp, "EXCEPT")) {
daemons = 0;
} else {
daemons++;
if ((host = split_at(cp + 1, '@')) != 0 && check_host(host) > 1) {
tcpd_warn("host %s has more than one address", host);
tcpd_warn("(consider using an address instead)");
}
check_daemon(cp);
}
}
if (daemons == 0)
tcpd_warn("daemon list is empty or ends in EXCEPT");
}
/* check_client_list - criticize client list */
static void check_client_list(list)
char *list;
{
char buf[BUFLEN];
char *cp;
char *host;
int clients = 0;
strcpy(buf, list);
for (cp = strtok(buf, sep); cp != 0; cp = strtok((char *) 0, sep)) {
if (STR_EQ(cp, "EXCEPT")) {
clients = 0;
} else {
clients++;
if (host = split_at(cp + 1, '@')) { /* user@host */
check_user(cp);
check_host(host);
} else {
check_host(cp);
}
}
}
if (clients == 0)
tcpd_warn("client list is empty or ends in EXCEPT");
}
/* check_daemon - criticize daemon pattern */
static void check_daemon(pat)
char *pat;
{
if (pat[0] == '@') {
tcpd_warn("%s: daemon name begins with \"@\"", pat);
} else if (pat[0] == '.') {
tcpd_warn("%s: daemon name begins with dot", pat);
} else if (pat[strlen(pat) - 1] == '.') {
tcpd_warn("%s: daemon name ends in dot", pat);
} else if (STR_EQ(pat, "ALL") || STR_EQ(pat, unknown)) {
/* void */ ;
} else if (STR_EQ(pat, "FAIL")) { /* obsolete */
tcpd_warn("FAIL is no longer recognized");
tcpd_warn("(use EXCEPT or DENY instead)");
} else if (reserved_name(pat)) {
tcpd_warn("%s: daemon name may be reserved word", pat);
} else {
switch (inet_get(pat)) {
case WR_UNKNOWN:
tcpd_warn("%s: no such process name in %s", pat, inetcf);
inet_set(pat, WR_YES); /* shut up next time */
break;
case WR_NOT:
tcpd_warn("%s: service possibly not wrapped", pat);
inet_set(pat, WR_YES);
break;
}
}
}
/* check_user - criticize user pattern */
static void check_user(pat)
char *pat;
{
if (pat[0] == '@') { /* @netgroup */
tcpd_warn("%s: user name begins with \"@\"", pat);
} else if (pat[0] == '.') {
tcpd_warn("%s: user name begins with dot", pat);
} else if (pat[strlen(pat) - 1] == '.') {
tcpd_warn("%s: user name ends in dot", pat);
} else if (STR_EQ(pat, "ALL") || STR_EQ(pat, unknown)
|| STR_EQ(pat, "KNOWN")) {
/* void */ ;
} else if (STR_EQ(pat, "FAIL")) { /* obsolete */
tcpd_warn("FAIL is no longer recognized");
tcpd_warn("(use EXCEPT or DENY instead)");
} else if (reserved_name(pat)) {
tcpd_warn("%s: user name may be reserved word", pat);
}
}
/* check_host - criticize host pattern */
static int check_host(pat)
char *pat;
{
char *mask;
int addr_count = 1;
if (pat[0] == '@') { /* @netgroup */
#ifdef NO_NETGRENT
/* SCO has no *netgrent() support */
#else
#ifdef NETGROUP
char *machinep;
char *userp;
char *domainp;
setnetgrent(pat + 1);
if (getnetgrent(&machinep, &userp, &domainp) == 0)
tcpd_warn("%s: unknown or empty netgroup", pat + 1);
endnetgrent();
#else
tcpd_warn("netgroup support disabled");
#endif
#endif
} else if (mask = split_at(pat, '/')) { /* network/netmask */
if (dot_quad_addr(pat) == INADDR_NONE
|| dot_quad_addr(mask) == INADDR_NONE)
tcpd_warn("%s/%s: bad net/mask pattern", pat, mask);
} else if (STR_EQ(pat, "FAIL")) { /* obsolete */
tcpd_warn("FAIL is no longer recognized");
tcpd_warn("(use EXCEPT or DENY instead)");
} else if (reserved_name(pat)) { /* other reserved */
/* void */ ;
} else if (NOT_INADDR(pat)) { /* internet name */
if (pat[strlen(pat) - 1] == '.') {
tcpd_warn("%s: domain or host name ends in dot", pat);
} else if (pat[0] != '.') {
addr_count = check_dns(pat);
}
} else { /* numeric form */
if (STR_EQ(pat, "0.0.0.0") || STR_EQ(pat, "255.255.255.255")) {
/* void */ ;
} else if (pat[0] == '.') {
tcpd_warn("%s: network number begins with dot", pat);
} else if (pat[strlen(pat) - 1] != '.') {
check_dns(pat);
}
}
return (addr_count);
}
/* reserved_name - determine if name is reserved */
static int reserved_name(pat)
char *pat;
{
return (STR_EQ(pat, unknown)
|| STR_EQ(pat, "KNOWN")
|| STR_EQ(pat, paranoid)
|| STR_EQ(pat, "ALL")
|| STR_EQ(pat, "LOCAL"));
}