Import from tcd_wrappers_7.4.
This commit is contained in:
parent
48250187e6
commit
84b4e9c069
16
usr.sbin/tcpdchk/Makefile
Normal file
16
usr.sbin/tcpdchk/Makefile
Normal 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>
|
62
usr.sbin/tcpdchk/fakelog.c
Normal file
62
usr.sbin/tcpdchk/fakelog.c
Normal 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
315
usr.sbin/tcpdchk/inetcf.c
Normal 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
14
usr.sbin/tcpdchk/inetcf.h
Normal 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 */
|
43
usr.sbin/tcpdchk/percent_m.c
Normal file
43
usr.sbin/tcpdchk/percent_m.c
Normal 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
213
usr.sbin/tcpdchk/scaffold.c
Normal 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);
|
||||
}
|
9
usr.sbin/tcpdchk/scaffold.h
Normal file
9
usr.sbin/tcpdchk/scaffold.h
Normal 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();
|
66
usr.sbin/tcpdchk/tcpdchk.8
Normal file
66
usr.sbin/tcpdchk/tcpdchk.8
Normal 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
461
usr.sbin/tcpdchk/tcpdchk.c
Normal 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"));
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user