source: trunk/third/tcp_wrappers/tcpdmatch.c @ 11717

Revision 11717, 9.0 KB checked in by danw, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r11716, which included commits to RCS files with non-trunk default branches.
Line 
1 /*
2  * tcpdmatch - explain what tcpd would do in a specific case
3  *
4  * usage: tcpdmatch [-d] [-i inet_conf] daemon[@host] [user@]host
5  *
6  * -d: use the access control tables in the current directory.
7  *
8  * -i: location of inetd.conf file.
9  *
10  * All errors are reported to the standard error stream, including the errors
11  * that would normally be reported via the syslog daemon.
12  *
13  * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
14  */
15
16#ifndef lint
17static char sccsid[] = "@(#) tcpdmatch.c 1.5 96/02/11 17:01:36";
18#endif
19
20/* System libraries. */
21
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <sys/socket.h>
25#include <netinet/in.h>
26#include <arpa/inet.h>
27#include <netdb.h>
28#include <stdio.h>
29#include <syslog.h>
30#include <setjmp.h>
31#include <string.h>
32
33extern void exit();
34extern int optind;
35extern char *optarg;
36
37#ifndef INADDR_NONE
38#define INADDR_NONE     (-1)            /* XXX should be 0xffffffff */
39#endif
40
41#ifndef S_ISDIR
42#define S_ISDIR(m)      (((m) & S_IFMT) == S_IFDIR)
43#endif
44
45/* Application-specific. */
46
47#include "tcpd.h"
48#include "inetcf.h"
49#include "scaffold.h"
50
51static void usage();
52static void tcpdmatch();
53
54/* The main program */
55
56int     main(argc, argv)
57int     argc;
58char  **argv;
59{
60    struct hostent *hp;
61    char   *myname = argv[0];
62    char   *client;
63    char   *server;
64    char   *addr;
65    char   *user;
66    char   *daemon;
67    struct request_info request;
68    int     ch;
69    char   *inetcf = 0;
70    int     count;
71    struct sockaddr_in server_sin;
72    struct sockaddr_in client_sin;
73    struct stat st;
74
75    /*
76     * Show what rule actually matched.
77     */
78    hosts_access_verbose = 2;
79
80    /*
81     * Parse the JCL.
82     */
83    while ((ch = getopt(argc, argv, "di:")) != EOF) {
84        switch (ch) {
85        case 'd':
86            hosts_allow_table = "hosts.allow";
87            hosts_deny_table = "hosts.deny";
88            break;
89        case 'i':
90            inetcf = optarg;
91            break;
92        default:
93            usage(myname);
94            /* NOTREACHED */
95        }
96    }
97    if (argc != optind + 2)
98        usage(myname);
99
100    /*
101     * When confusion really strikes...
102     */
103    if (check_path(REAL_DAEMON_DIR, &st) < 0) {
104        tcpd_warn("REAL_DAEMON_DIR %s: %m", REAL_DAEMON_DIR);
105    } else if (!S_ISDIR(st.st_mode)) {
106        tcpd_warn("REAL_DAEMON_DIR %s is not a directory", REAL_DAEMON_DIR);
107    }
108
109    /*
110     * Default is to specify a daemon process name. When daemon@host is
111     * specified, separate the two parts.
112     */
113    if ((server = split_at(argv[optind], '@')) == 0)
114        server = unknown;
115    if (argv[optind][0] == '/') {
116        daemon = strrchr(argv[optind], '/') + 1;
117        tcpd_warn("%s: daemon name normalized to: %s", argv[optind], daemon);
118    } else {
119        daemon = argv[optind];
120    }
121
122    /*
123     * Default is to specify a client hostname or address. When user@host is
124     * specified, separate the two parts.
125     */
126    if ((client = split_at(argv[optind + 1], '@')) != 0) {
127        user = argv[optind + 1];
128    } else {
129        client = argv[optind + 1];
130        user = unknown;
131    }
132
133    /*
134     * Analyze the inetd (or tlid) configuration file, so that we can warn
135     * the user about services that may not be wrapped, services that are not
136     * configured, or services that are wrapped in an incorrect manner. Allow
137     * for services that are not run from inetd, or that have tcpd access
138     * control built into them.
139     */
140    inetcf = inet_cfg(inetcf);
141    inet_set("portmap", WR_NOT);
142    inet_set("rpcbind", WR_NOT);
143    switch (inet_get(daemon)) {
144    case WR_UNKNOWN:
145        tcpd_warn("%s: no such process name in %s", daemon, inetcf);
146        break;
147    case WR_NOT:
148        tcpd_warn("%s: service possibly not wrapped", daemon);
149        break;
150    }
151
152    /*
153     * Check accessibility of access control files.
154     */
155    (void) check_path(hosts_allow_table, &st);
156    (void) check_path(hosts_deny_table, &st);
157
158    /*
159     * Fill in what we have figured out sofar. Use socket and DNS routines
160     * for address and name conversions. We attach stdout to the request so
161     * that banner messages will become visible.
162     */
163    request_init(&request, RQ_DAEMON, daemon, RQ_USER, user, RQ_FILE, 1, 0);
164    sock_methods(&request);
165
166    /*
167     * If a server hostname is specified, insist that the name maps to at
168     * most one address. eval_hostname() warns the user about name server
169     * problems, while using the request.server structure as a cache for host
170     * address and name conversion results.
171     */
172    if (NOT_INADDR(server) == 0 || HOSTNAME_KNOWN(server)) {
173        if ((hp = find_inet_addr(server)) == 0)
174            exit(1);
175        memset((char *) &server_sin, 0, sizeof(server_sin));
176        server_sin.sin_family = AF_INET;
177        request_set(&request, RQ_SERVER_SIN, &server_sin, 0);
178
179        for (count = 0; (addr = hp->h_addr_list[count]) != 0; count++) {
180            memcpy((char *) &server_sin.sin_addr, addr,
181                   sizeof(server_sin.sin_addr));
182
183            /*
184             * Force evaluation of server host name and address. Host name
185             * conflicts will be reported while eval_hostname() does its job.
186             */
187            request_set(&request, RQ_SERVER_NAME, "", RQ_SERVER_ADDR, "", 0);
188            if (STR_EQ(eval_hostname(request.server), unknown))
189                tcpd_warn("host address %s->name lookup failed",
190                          eval_hostaddr(request.server));
191        }
192        if (count > 1) {
193            fprintf(stderr, "Error: %s has more than one address\n", server);
194            fprintf(stderr, "Please specify an address instead\n");
195            exit(1);
196        }
197        free((char *) hp);
198    } else {
199        request_set(&request, RQ_SERVER_NAME, server, 0);
200    }
201
202    /*
203     * If a client address is specified, we simulate the effect of client
204     * hostname lookup failure.
205     */
206    if (dot_quad_addr(client) != INADDR_NONE) {
207        request_set(&request, RQ_CLIENT_ADDR, client, 0);
208        tcpdmatch(&request);
209        exit(0);
210    }
211
212    /*
213     * Perhaps they are testing special client hostname patterns that aren't
214     * really host names at all.
215     */
216    if (NOT_INADDR(client) && HOSTNAME_KNOWN(client) == 0) {
217        request_set(&request, RQ_CLIENT_NAME, client, 0);
218        tcpdmatch(&request);
219        exit(0);
220    }
221
222    /*
223     * Otherwise, assume that a client hostname is specified, and insist that
224     * the address can be looked up. The reason for this requirement is that
225     * in real life the client address is available (at least with IP). Let
226     * eval_hostname() figure out if this host is properly registered, while
227     * using the request.client structure as a cache for host name and
228     * address conversion results.
229     */
230    if ((hp = find_inet_addr(client)) == 0)
231        exit(1);
232    memset((char *) &client_sin, 0, sizeof(client_sin));
233    client_sin.sin_family = AF_INET;
234    request_set(&request, RQ_CLIENT_SIN, &client_sin, 0);
235
236    for (count = 0; (addr = hp->h_addr_list[count]) != 0; count++) {
237        memcpy((char *) &client_sin.sin_addr, addr,
238               sizeof(client_sin.sin_addr));
239
240        /*
241         * Force evaluation of client host name and address. Host name
242         * conflicts will be reported while eval_hostname() does its job.
243         */
244        request_set(&request, RQ_CLIENT_NAME, "", RQ_CLIENT_ADDR, "", 0);
245        if (STR_EQ(eval_hostname(request.client), unknown))
246            tcpd_warn("host address %s->name lookup failed",
247                      eval_hostaddr(request.client));
248        tcpdmatch(&request);
249        if (hp->h_addr_list[count + 1])
250            printf("\n");
251    }
252    free((char *) hp);
253    exit(0);
254}
255
256/* Explain how to use this program */
257
258static void usage(myname)
259char   *myname;
260{
261    fprintf(stderr, "usage: %s [-d] [-i inet_conf] daemon[@host] [user@]host\n",
262            myname);
263    fprintf(stderr, "   -d: use allow/deny files in current directory\n");
264    fprintf(stderr, "   -i: location of inetd.conf file\n");
265    exit(1);
266}
267
268/* Print interesting expansions */
269
270static void expand(text, pattern, request)
271char   *text;
272char   *pattern;
273struct request_info *request;
274{
275    char    buf[BUFSIZ];
276
277    if (STR_NE(percent_x(buf, sizeof(buf), pattern, request), unknown))
278        printf("%s %s\n", text, buf);
279}
280
281/* Try out a (server,client) pair */
282
283static void tcpdmatch(request)
284struct request_info *request;
285{
286    int     verdict;
287
288    /*
289     * Show what we really know. Suppress uninteresting noise.
290     */
291    expand("client:   hostname", "%n", request);
292    expand("client:   address ", "%a", request);
293    expand("client:   username", "%u", request);
294    expand("server:   hostname", "%N", request);
295    expand("server:   address ", "%A", request);
296    expand("server:   process ", "%d", request);
297
298    /*
299     * Reset stuff that might be changed by options handlers. In dry-run
300     * mode, extension language routines that would not return should inform
301     * us of their plan, by clearing the dry_run flag. This is a bit clumsy
302     * but we must be able to verify hosts with more than one network
303     * address.
304     */
305    rfc931_timeout = RFC931_TIMEOUT;
306    allow_severity = SEVERITY;
307    deny_severity = LOG_WARNING;
308    dry_run = 1;
309
310    /*
311     * When paranoid mode is enabled, access is rejected no matter what the
312     * access control rules say.
313     */
314#ifdef PARANOID
315    if (STR_EQ(eval_hostname(request->client), paranoid)) {
316        printf("access:   denied (PARANOID mode)\n\n");
317        return;
318    }
319#endif
320
321    /*
322     * Report the access control verdict.
323     */
324    verdict = hosts_access(request);
325    printf("access:   %s\n",
326           dry_run == 0 ? "delegated" :
327           verdict ? "granted" : "denied");
328}
Note: See TracBrowser for help on using the repository browser.