diff options
Diffstat (limited to 'libexec/tcpd/tcpdmatch/tcpdmatch.c')
-rw-r--r-- | libexec/tcpd/tcpdmatch/tcpdmatch.c | 333 |
1 files changed, 333 insertions, 0 deletions
diff --git a/libexec/tcpd/tcpdmatch/tcpdmatch.c b/libexec/tcpd/tcpdmatch/tcpdmatch.c new file mode 100644 index 00000000000..fb4fd1c242f --- /dev/null +++ b/libexec/tcpd/tcpdmatch/tcpdmatch.c @@ -0,0 +1,333 @@ +/* $OpenBSD: tcpdmatch.c,v 1.1 1997/02/26 06:17:10 downsj Exp $ */ + + /* + * 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 +#if 0 +static char sccsid[] = "@(#) tcpdmatch.c 1.5 96/02/11 17:01:36"; +#else +static char rcsid[] = "$OpenBSD: tcpdmatch.c,v 1.1 1997/02/26 06:17:10 downsj Exp $"; +#endif +#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> +#include <unistd.h> +#include <stdlib.h> + +#include <tcpd.h> + +#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 "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"); +} |