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

Revision 11717, 9.4 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  * This module implements a simple access control language that is based on
3  * host (or domain) names, NIS (host) netgroup names, IP addresses (or
4  * network numbers) and daemon process names. When a match is found the
5  * search is terminated, and depending on whether PROCESS_OPTIONS is defined,
6  * a list of options is executed or an optional shell command is executed.
7  *
8  * Host and user names are looked up on demand, provided that suitable endpoint
9  * information is available as sockaddr_in structures or TLI netbufs. As a
10  * side effect, the pattern matching process may change the contents of
11  * request structure fields.
12  *
13  * Diagnostics are reported through syslog(3).
14  *
15  * Compile with -DNETGROUP if your library provides support for netgroups.
16  *
17  * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
18  */
19
20#ifndef lint
21static char sccsid[] = "@(#) hosts_access.c 1.21 97/02/12 02:13:22";
22#endif
23
24/* System libraries. */
25
26#include <sys/types.h>
27#include <sys/param.h>
28#include <netinet/in.h>
29#include <arpa/inet.h>
30#include <stdio.h>
31#include <syslog.h>
32#include <ctype.h>
33#include <errno.h>
34#include <setjmp.h>
35#include <string.h>
36
37extern char *fgets();
38extern int errno;
39
40#ifndef INADDR_NONE
41#define INADDR_NONE     (-1)            /* XXX should be 0xffffffff */
42#endif
43
44/* Local stuff. */
45
46#include "tcpd.h"
47
48/* Error handling. */
49
50extern jmp_buf tcpd_buf;
51
52/* Delimiters for lists of daemons or clients. */
53
54static char sep[] = ", \t\r\n";
55
56/* Constants to be used in assignments only, not in comparisons... */
57
58#define YES             1
59#define NO              0
60
61 /*
62  * These variables are globally visible so that they can be redirected in
63  * verification mode.
64  */
65
66char   *hosts_allow_table = HOSTS_ALLOW;
67char   *hosts_deny_table = HOSTS_DENY;
68int     hosts_access_verbose = 0;
69
70 /*
71  * In a long-running process, we are not at liberty to just go away.
72  */
73
74int     resident = (-1);                /* -1, 0: unknown; +1: yes */
75
76/* Forward declarations. */
77
78static int table_match();
79static int list_match();
80static int server_match();
81static int client_match();
82static int host_match();
83static int string_match();
84static int masked_match();
85
86/* Size of logical line buffer. */
87
88#define BUFLEN 2048
89
90/* hosts_access - host access control facility */
91
92int     hosts_access(request)
93struct request_info *request;
94{
95    int     verdict;
96
97    /*
98     * If the (daemon, client) pair is matched by an entry in the file
99     * /etc/hosts.allow, access is granted. Otherwise, if the (daemon,
100     * client) pair is matched by an entry in the file /etc/hosts.deny,
101     * access is denied. Otherwise, access is granted. A non-existent
102     * access-control file is treated as an empty file.
103     *
104     * After a rule has been matched, the optional language extensions may
105     * decide to grant or refuse service anyway. Or, while a rule is being
106     * processed, a serious error is found, and it seems better to play safe
107     * and deny service. All this is done by jumping back into the
108     * hosts_access() routine, bypassing the regular return from the
109     * table_match() function calls below.
110     */
111
112    if (resident <= 0)
113        resident++;
114    verdict = setjmp(tcpd_buf);
115    if (verdict != 0)
116        return (verdict == AC_PERMIT);
117    if (table_match(hosts_allow_table, request))
118        return (YES);
119    if (table_match(hosts_deny_table, request))
120        return (NO);
121    return (YES);
122}
123
124/* table_match - match table entries with (daemon, client) pair */
125
126static int table_match(table, request)
127char   *table;
128struct request_info *request;
129{
130    FILE   *fp;
131    char    sv_list[BUFLEN];            /* becomes list of daemons */
132    char   *cl_list;                    /* becomes list of clients */
133    char   *sh_cmd;                     /* becomes optional shell command */
134    int     match = NO;
135    struct tcpd_context saved_context;
136
137    saved_context = tcpd_context;               /* stupid compilers */
138
139    /*
140     * Between the fopen() and fclose() calls, avoid jumps that may cause
141     * file descriptor leaks.
142     */
143
144    if ((fp = fopen(table, "r")) != 0) {
145        tcpd_context.file = table;
146        tcpd_context.line = 0;
147        while (match == NO && xgets(sv_list, sizeof(sv_list), fp) != 0) {
148            if (sv_list[strlen(sv_list) - 1] != '\n') {
149                tcpd_warn("missing newline or line too long");
150                continue;
151            }
152            if (sv_list[0] == '#' || sv_list[strspn(sv_list, " \t\r\n")] == 0)
153                continue;
154            if ((cl_list = split_at(sv_list, ':')) == 0) {
155                tcpd_warn("missing \":\" separator");
156                continue;
157            }
158            sh_cmd = split_at(cl_list, ':');
159            match = list_match(sv_list, request, server_match)
160                && list_match(cl_list, request, client_match);
161        }
162        (void) fclose(fp);
163    } else if (errno != ENOENT) {
164        tcpd_warn("cannot open %s: %m", table);
165    }
166    if (match) {
167        if (hosts_access_verbose > 1)
168            syslog(LOG_DEBUG, "matched:  %s line %d",
169                   tcpd_context.file, tcpd_context.line);
170        if (sh_cmd) {
171#ifdef PROCESS_OPTIONS
172            process_options(sh_cmd, request);
173#else
174            char    cmd[BUFSIZ];
175            shell_cmd(percent_x(cmd, sizeof(cmd), sh_cmd, request));
176#endif
177        }
178    }
179    tcpd_context = saved_context;
180    return (match);
181}
182
183/* list_match - match a request against a list of patterns with exceptions */
184
185static int list_match(list, request, match_fn)
186char   *list;
187struct request_info *request;
188int   (*match_fn) ();
189{
190    char   *tok;
191
192    /*
193     * Process tokens one at a time. We have exhausted all possible matches
194     * when we reach an "EXCEPT" token or the end of the list. If we do find
195     * a match, look for an "EXCEPT" list and recurse to determine whether
196     * the match is affected by any exceptions.
197     */
198
199    for (tok = strtok(list, sep); tok != 0; tok = strtok((char *) 0, sep)) {
200        if (STR_EQ(tok, "EXCEPT"))              /* EXCEPT: give up */
201            return (NO);
202        if (match_fn(tok, request)) {           /* YES: look for exceptions */
203            while ((tok = strtok((char *) 0, sep)) && STR_NE(tok, "EXCEPT"))
204                 /* VOID */ ;
205            return (tok == 0 || list_match((char *) 0, request, match_fn) == 0);
206        }
207    }
208    return (NO);
209}
210
211/* server_match - match server information */
212
213static int server_match(tok, request)
214char   *tok;
215struct request_info *request;
216{
217    char   *host;
218
219    if ((host = split_at(tok + 1, '@')) == 0) { /* plain daemon */
220        return (string_match(tok, eval_daemon(request)));
221    } else {                                    /* daemon@host */
222        return (string_match(tok, eval_daemon(request))
223                && host_match(host, request->server));
224    }
225}
226
227/* client_match - match client information */
228
229static int client_match(tok, request)
230char   *tok;
231struct request_info *request;
232{
233    char   *host;
234
235    if ((host = split_at(tok + 1, '@')) == 0) { /* plain host */
236        return (host_match(tok, request->client));
237    } else {                                    /* user@host */
238        return (host_match(host, request->client)
239                && string_match(tok, eval_user(request)));
240    }
241}
242
243/* host_match - match host name and/or address against pattern */
244
245static int host_match(tok, host)
246char   *tok;
247struct host_info *host;
248{
249    char   *mask;
250
251    /*
252     * This code looks a little hairy because we want to avoid unnecessary
253     * hostname lookups.
254     *
255     * The KNOWN pattern requires that both address AND name be known; some
256     * patterns are specific to host names or to host addresses; all other
257     * patterns are satisfied when either the address OR the name match.
258     */
259
260    if (tok[0] == '@') {                        /* netgroup: look it up */
261#ifdef  NETGROUP
262        static char *mydomain = 0;
263        if (mydomain == 0)
264            yp_get_default_domain(&mydomain);
265        return (innetgr(tok + 1, eval_hostname(host), (char *) 0, mydomain));
266#else
267        tcpd_warn("netgroup support is disabled");      /* not tcpd_jump() */
268        return (NO);
269#endif
270    } else if (STR_EQ(tok, "KNOWN")) {          /* check address and name */
271        char   *name = eval_hostname(host);
272        return (STR_NE(eval_hostaddr(host), unknown) && HOSTNAME_KNOWN(name));
273    } else if (STR_EQ(tok, "LOCAL")) {          /* local: no dots in name */
274        char   *name = eval_hostname(host);
275        return (strchr(name, '.') == 0 && HOSTNAME_KNOWN(name));
276    } else if ((mask = split_at(tok, '/')) != 0) {      /* net/mask */
277        return (masked_match(tok, mask, eval_hostaddr(host)));
278    } else {                                    /* anything else */
279        return (string_match(tok, eval_hostaddr(host))
280            || (NOT_INADDR(tok) && string_match(tok, eval_hostname(host))));
281    }
282}
283
284/* string_match - match string against pattern */
285
286static int string_match(tok, string)
287char   *tok;
288char   *string;
289{
290    int     n;
291
292    if (tok[0] == '.') {                        /* suffix */
293        n = strlen(string) - strlen(tok);
294        return (n > 0 && STR_EQ(tok, string + n));
295    } else if (STR_EQ(tok, "ALL")) {            /* all: match any */
296        return (YES);
297    } else if (STR_EQ(tok, "KNOWN")) {          /* not unknown */
298        return (STR_NE(string, unknown));
299    } else if (tok[(n = strlen(tok)) - 1] == '.') {     /* prefix */
300        return (STRN_EQ(tok, string, n));
301    } else {                                    /* exact match */
302        return (STR_EQ(tok, string));
303    }
304}
305
306/* masked_match - match address against netnumber/netmask */
307
308static int masked_match(net_tok, mask_tok, string)
309char   *net_tok;
310char   *mask_tok;
311char   *string;
312{
313    unsigned long net;
314    unsigned long mask;
315    unsigned long addr;
316
317    /*
318     * Disallow forms other than dotted quad: the treatment that inet_addr()
319     * gives to forms with less than four components is inconsistent with the
320     * access control language. John P. Rouillard <rouilj@cs.umb.edu>.
321     */
322
323    if ((addr = dot_quad_addr(string)) == INADDR_NONE)
324        return (NO);
325    if ((net = dot_quad_addr(net_tok)) == INADDR_NONE
326        || (mask = dot_quad_addr(mask_tok)) == INADDR_NONE) {
327        tcpd_warn("bad net/mask expression: %s/%s", net_tok, mask_tok);
328        return (NO);                            /* not tcpd_jump() */
329    }
330    return ((addr & mask) == net);
331}
Note: See TracBrowser for help on using the repository browser.