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

Revision 11717, 11.1 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  * tcpdchk - examine all tcpd access control rules and inetd.conf entries
3  *
4  * Usage: tcpdchk [-a] [-d] [-i inet_conf] [-v]
5  *
6  * -a: complain about implicit "allow" at end of rule.
7  *
8  * -d: rules in current directory.
9  *
10  * -i: location of inetd.conf file.
11  *
12  * -v: show all rules.
13  *
14  * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
15  */
16
17#ifndef lint
18static char sccsid[] = "@(#) tcpdchk.c 1.8 97/02/12 02:13:25";
19#endif
20
21/* System libraries. */
22
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <netinet/in.h>
26#include <arpa/inet.h>
27#include <stdio.h>
28#include <syslog.h>
29#include <setjmp.h>
30#include <errno.h>
31#include <netdb.h>
32#include <string.h>
33
34extern int errno;
35extern void exit();
36extern int optind;
37extern char *optarg;
38
39#ifndef INADDR_NONE
40#define INADDR_NONE     (-1)            /* XXX should be 0xffffffff */
41#endif
42
43#ifndef S_ISDIR
44#define S_ISDIR(m)      (((m) & S_IFMT) == S_IFDIR)
45#endif
46
47/* Application-specific. */
48
49#include "tcpd.h"
50#include "inetcf.h"
51#include "scaffold.h"
52
53 /*
54  * Stolen from hosts_access.c...
55  */
56static char sep[] = ", \t\n";
57
58#define BUFLEN 2048
59
60int     resident = 0;
61int     hosts_access_verbose = 0;
62char   *hosts_allow_table = HOSTS_ALLOW;
63char   *hosts_deny_table = HOSTS_DENY;
64extern jmp_buf tcpd_buf;
65
66 /*
67  * Local stuff.
68  */
69static void usage();
70static void parse_table();
71static void print_list();
72static void check_daemon_list();
73static void check_client_list();
74static void check_daemon();
75static void check_user();
76static int check_host();
77static int reserved_name();
78
79#define PERMIT  1
80#define DENY    0
81
82#define YES     1
83#define NO      0
84
85static int defl_verdict;
86static char *myname;
87static int allow_check;
88static char *inetcf;
89
90int     main(argc, argv)
91int     argc;
92char  **argv;
93{
94    struct request_info request;
95    struct stat st;
96    int     c;
97
98    myname = argv[0];
99
100    /*
101     * Parse the JCL.
102     */
103    while ((c = getopt(argc, argv, "adi:v")) != EOF) {
104        switch (c) {
105        case 'a':
106            allow_check = 1;
107            break;
108        case 'd':
109            hosts_allow_table = "hosts.allow";
110            hosts_deny_table = "hosts.deny";
111            break;
112        case 'i':
113            inetcf = optarg;
114            break;
115        case 'v':
116            hosts_access_verbose++;
117            break;
118        default:
119            usage();
120            /* NOTREACHED */
121        }
122    }
123    if (argc != optind)
124        usage();
125
126    /*
127     * When confusion really strikes...
128     */
129    if (check_path(REAL_DAEMON_DIR, &st) < 0) {
130        tcpd_warn("REAL_DAEMON_DIR %s: %m", REAL_DAEMON_DIR);
131    } else if (!S_ISDIR(st.st_mode)) {
132        tcpd_warn("REAL_DAEMON_DIR %s is not a directory", REAL_DAEMON_DIR);
133    }
134
135    /*
136     * Process the inet configuration file (or its moral equivalent). This
137     * information is used later to find references in hosts.allow/deny to
138     * unwrapped services, and other possible problems.
139     */
140    inetcf = inet_cfg(inetcf);
141    if (hosts_access_verbose)
142        printf("Using network configuration file: %s\n", inetcf);
143
144    /*
145     * These are not run from inetd but may have built-in access control.
146     */
147    inet_set("portmap", WR_NOT);
148    inet_set("rpcbind", WR_NOT);
149
150    /*
151     * Check accessibility of access control files.
152     */
153    (void) check_path(hosts_allow_table, &st);
154    (void) check_path(hosts_deny_table, &st);
155
156    /*
157     * Fake up an arbitrary service request.
158     */
159    request_init(&request,
160                 RQ_DAEMON, "daemon_name",
161                 RQ_SERVER_NAME, "server_hostname",
162                 RQ_SERVER_ADDR, "server_addr",
163                 RQ_USER, "user_name",
164                 RQ_CLIENT_NAME, "client_hostname",
165                 RQ_CLIENT_ADDR, "client_addr",
166                 RQ_FILE, 1,
167                 0);
168
169    /*
170     * Examine all access-control rules.
171     */
172    defl_verdict = PERMIT;
173    parse_table(hosts_allow_table, &request);
174    defl_verdict = DENY;
175    parse_table(hosts_deny_table, &request);
176    return (0);
177}
178
179/* usage - explain */
180
181static void usage()
182{
183    fprintf(stderr, "usage: %s [-a] [-d] [-i inet_conf] [-v]\n", myname);
184    fprintf(stderr, "   -a: report rules with implicit \"ALLOW\" at end\n");
185    fprintf(stderr, "   -d: use allow/deny files in current directory\n");
186    fprintf(stderr, "   -i: location of inetd.conf file\n");
187    fprintf(stderr, "   -v: list all rules\n");
188    exit(1);
189}
190
191/* parse_table - like table_match(), but examines _all_ entries */
192
193static void parse_table(table, request)
194char   *table;
195struct request_info *request;
196{
197    FILE   *fp;
198    int     real_verdict;
199    char    sv_list[BUFLEN];            /* becomes list of daemons */
200    char   *cl_list;                    /* becomes list of requests */
201    char   *sh_cmd;                     /* becomes optional shell command */
202    char    buf[BUFSIZ];
203    int     verdict;
204    struct tcpd_context saved_context;
205
206    saved_context = tcpd_context;               /* stupid compilers */
207
208    if (fp = fopen(table, "r")) {
209        tcpd_context.file = table;
210        tcpd_context.line = 0;
211        while (xgets(sv_list, sizeof(sv_list), fp)) {
212            if (sv_list[strlen(sv_list) - 1] != '\n') {
213                tcpd_warn("missing newline or line too long");
214                continue;
215            }
216            if (sv_list[0] == '#' || sv_list[strspn(sv_list, " \t\r\n")] == 0)
217                continue;
218            if ((cl_list = split_at(sv_list, ':')) == 0) {
219                tcpd_warn("missing \":\" separator");
220                continue;
221            }
222            sh_cmd = split_at(cl_list, ':');
223
224            if (hosts_access_verbose)
225                printf("\n>>> Rule %s line %d:\n",
226                       tcpd_context.file, tcpd_context.line);
227
228            if (hosts_access_verbose)
229                print_list("daemons:  ", sv_list);
230            check_daemon_list(sv_list);
231
232            if (hosts_access_verbose)
233                print_list("clients:  ", cl_list);
234            check_client_list(cl_list);
235
236#ifdef PROCESS_OPTIONS
237            real_verdict = defl_verdict;
238            if (sh_cmd) {
239                verdict = setjmp(tcpd_buf);
240                if (verdict != 0) {
241                    real_verdict = (verdict == AC_PERMIT);
242                } else {
243                    dry_run = 1;
244                    process_options(sh_cmd, request);
245                    if (dry_run == 1 && real_verdict && allow_check)
246                        tcpd_warn("implicit \"allow\" at end of rule");
247                }
248            } else if (defl_verdict && allow_check) {
249                tcpd_warn("implicit \"allow\" at end of rule");
250            }
251            if (hosts_access_verbose)
252                printf("access:   %s\n", real_verdict ? "granted" : "denied");
253#else
254            if (sh_cmd)
255                shell_cmd(percent_x(buf, sizeof(buf), sh_cmd, request));
256            if (hosts_access_verbose)
257                printf("access:   %s\n", defl_verdict ? "granted" : "denied");
258#endif
259        }
260        (void) fclose(fp);
261    } else if (errno != ENOENT) {
262        tcpd_warn("cannot open %s: %m", table);
263    }
264    tcpd_context = saved_context;
265}
266
267/* print_list - pretty-print a list */
268
269static void print_list(title, list)
270char   *title;
271char   *list;
272{
273    char    buf[BUFLEN];
274    char   *cp;
275    char   *next;
276
277    fputs(title, stdout);
278    strcpy(buf, list);
279
280    for (cp = strtok(buf, sep); cp != 0; cp = next) {
281        fputs(cp, stdout);
282        next = strtok((char *) 0, sep);
283        if (next != 0)
284            fputs(" ", stdout);
285    }
286    fputs("\n", stdout);
287}
288
289/* check_daemon_list - criticize daemon list */
290
291static void check_daemon_list(list)
292char   *list;
293{
294    char    buf[BUFLEN];
295    char   *cp;
296    char   *host;
297    int     daemons = 0;
298
299    strcpy(buf, list);
300
301    for (cp = strtok(buf, sep); cp != 0; cp = strtok((char *) 0, sep)) {
302        if (STR_EQ(cp, "EXCEPT")) {
303            daemons = 0;
304        } else {
305            daemons++;
306            if ((host = split_at(cp + 1, '@')) != 0 && check_host(host) > 1) {
307                tcpd_warn("host %s has more than one address", host);
308                tcpd_warn("(consider using an address instead)");
309            }
310            check_daemon(cp);
311        }
312    }
313    if (daemons == 0)
314        tcpd_warn("daemon list is empty or ends in EXCEPT");
315}
316
317/* check_client_list - criticize client list */
318
319static void check_client_list(list)
320char   *list;
321{
322    char    buf[BUFLEN];
323    char   *cp;
324    char   *host;
325    int     clients = 0;
326
327    strcpy(buf, list);
328
329    for (cp = strtok(buf, sep); cp != 0; cp = strtok((char *) 0, sep)) {
330        if (STR_EQ(cp, "EXCEPT")) {
331            clients = 0;
332        } else {
333            clients++;
334            if (host = split_at(cp + 1, '@')) { /* user@host */
335                check_user(cp);
336                check_host(host);
337            } else {
338                check_host(cp);
339            }
340        }
341    }
342    if (clients == 0)
343        tcpd_warn("client list is empty or ends in EXCEPT");
344}
345
346/* check_daemon - criticize daemon pattern */
347
348static void check_daemon(pat)
349char   *pat;
350{
351    if (pat[0] == '@') {
352        tcpd_warn("%s: daemon name begins with \"@\"", pat);
353    } else if (pat[0] == '.') {
354        tcpd_warn("%s: daemon name begins with dot", pat);
355    } else if (pat[strlen(pat) - 1] == '.') {
356        tcpd_warn("%s: daemon name ends in dot", pat);
357    } else if (STR_EQ(pat, "ALL") || STR_EQ(pat, unknown)) {
358         /* void */ ;
359    } else if (STR_EQ(pat, "FAIL")) {           /* obsolete */
360        tcpd_warn("FAIL is no longer recognized");
361        tcpd_warn("(use EXCEPT or DENY instead)");
362    } else if (reserved_name(pat)) {
363        tcpd_warn("%s: daemon name may be reserved word", pat);
364    } else {
365        switch (inet_get(pat)) {
366        case WR_UNKNOWN:
367            tcpd_warn("%s: no such process name in %s", pat, inetcf);
368            inet_set(pat, WR_YES);              /* shut up next time */
369            break;
370        case WR_NOT:
371            tcpd_warn("%s: service possibly not wrapped", pat);
372            inet_set(pat, WR_YES);
373            break;
374        }
375    }
376}
377
378/* check_user - criticize user pattern */
379
380static void check_user(pat)
381char   *pat;
382{
383    if (pat[0] == '@') {                        /* @netgroup */
384        tcpd_warn("%s: user name begins with \"@\"", pat);
385    } else if (pat[0] == '.') {
386        tcpd_warn("%s: user name begins with dot", pat);
387    } else if (pat[strlen(pat) - 1] == '.') {
388        tcpd_warn("%s: user name ends in dot", pat);
389    } else if (STR_EQ(pat, "ALL") || STR_EQ(pat, unknown)
390               || STR_EQ(pat, "KNOWN")) {
391         /* void */ ;
392    } else if (STR_EQ(pat, "FAIL")) {           /* obsolete */
393        tcpd_warn("FAIL is no longer recognized");
394        tcpd_warn("(use EXCEPT or DENY instead)");
395    } else if (reserved_name(pat)) {
396        tcpd_warn("%s: user name may be reserved word", pat);
397    }
398}
399
400/* check_host - criticize host pattern */
401
402static int check_host(pat)
403char   *pat;
404{
405    char   *mask;
406    int     addr_count = 1;
407
408    if (pat[0] == '@') {                        /* @netgroup */
409#ifdef NO_NETGRENT
410        /* SCO has no *netgrent() support */
411#else
412#ifdef NETGROUP
413        char   *machinep;
414        char   *userp;
415        char   *domainp;
416
417        setnetgrent(pat + 1);
418        if (getnetgrent(&machinep, &userp, &domainp) == 0)
419            tcpd_warn("%s: unknown or empty netgroup", pat + 1);
420        endnetgrent();
421#else
422        tcpd_warn("netgroup support disabled");
423#endif
424#endif
425    } else if (mask = split_at(pat, '/')) {     /* network/netmask */
426        if (dot_quad_addr(pat) == INADDR_NONE
427            || dot_quad_addr(mask) == INADDR_NONE)
428            tcpd_warn("%s/%s: bad net/mask pattern", pat, mask);
429    } else if (STR_EQ(pat, "FAIL")) {           /* obsolete */
430        tcpd_warn("FAIL is no longer recognized");
431        tcpd_warn("(use EXCEPT or DENY instead)");
432    } else if (reserved_name(pat)) {            /* other reserved */
433         /* void */ ;
434    } else if (NOT_INADDR(pat)) {               /* internet name */
435        if (pat[strlen(pat) - 1] == '.') {
436            tcpd_warn("%s: domain or host name ends in dot", pat);
437        } else if (pat[0] != '.') {
438            addr_count = check_dns(pat);
439        }
440    } else {                                    /* numeric form */
441        if (STR_EQ(pat, "0.0.0.0") || STR_EQ(pat, "255.255.255.255")) {
442            /* void */ ;
443        } else if (pat[0] == '.') {
444            tcpd_warn("%s: network number begins with dot", pat);
445        } else if (pat[strlen(pat) - 1] != '.') {
446            check_dns(pat);
447        }
448    }
449    return (addr_count);
450}
451
452/* reserved_name - determine if name is reserved */
453
454static int reserved_name(pat)
455char   *pat;
456{
457    return (STR_EQ(pat, unknown)
458            || STR_EQ(pat, "KNOWN")
459            || STR_EQ(pat, paranoid)
460            || STR_EQ(pat, "ALL")
461            || STR_EQ(pat, "LOCAL"));
462}
Note: See TracBrowser for help on using the repository browser.