1/*
2   This module is an adaption of code from the tcpd-1.4 package written
3   by Wietse Venema, Eindhoven University of Technology, The Netherlands.
4
5   The code is used here with permission.
6
7   The code has been considerably changed from the original. Bug reports
8   should be sent to samba-bugs@samba.org
9*/
10
11#include "includes.h"
12
13extern int DEBUGLEVEL;
14
15/* Delimiters for lists of daemons or clients. */
16static char *sep = ", \t";
17
18#define	FAIL		(-1)
19
20/* masked_match - match address against netnumber/netmask */
21static int masked_match(char *tok, char *slash, char *s)
22{
23	uint32 net;
24	uint32 mask;
25	uint32 addr;
26
27	if ((addr = interpret_addr(s)) == INADDR_NONE)
28		return (False);
29	*slash = 0;
30	net = interpret_addr(tok);
31	*slash = '/';
32	if (net == INADDR_NONE ||
33	    (mask = interpret_addr(slash + 1)) == INADDR_NONE) {
34		DEBUG(0,("access: bad net/mask access control: %s\n", tok));
35		return (False);
36	}
37	return ((addr & mask) == net);
38}
39
40/* string_match - match string against token */
41static int string_match(char *tok,char *s, char *invalid_char)
42{
43	size_t     tok_len;
44	size_t     str_len;
45	char   *cut;
46
47	*invalid_char = '\0';
48
49	/* Return True if a token has the magic value "ALL". Return
50	 * FAIL if the token is "FAIL". If the token starts with a "."
51	 * (domain name), return True if it matches the last fields of
52	 * the string. If the token has the magic value "LOCAL",
53	 * return True if the string does not contain a "."
54	 * character. If the token ends on a "." (network number),
55	 * return True if it matches the first fields of the
56	 * string. If the token begins with a "@" (netgroup name),
57	 * return True if the string is a (host) member of the
58	 * netgroup. Return True if the token fully matches the
59	 * string. If the token is a netnumber/netmask pair, return
60	 * True if the address is a member of the specified subnet.  */
61
62	if (tok[0] == '.') {			/* domain: match last fields */
63		if ((str_len = strlen(s)) > (tok_len = strlen(tok))
64		    && strcasecmp(tok, s + str_len - tok_len) == 0)
65			return (True);
66	} else if (tok[0] == '@') { /* netgroup: look it up */
67#ifdef	HAVE_NETGROUP
68		static char *mydomain = NULL;
69		char *hostname = NULL;
70		BOOL netgroup_ok = False;
71
72		if (!mydomain) yp_get_default_domain(&mydomain);
73
74		if (!mydomain) {
75			DEBUG(0,("Unable to get default yp domain.\n"));
76			return False;
77		}
78		if (!(hostname = strdup(s))) {
79			DEBUG(1,("out of memory for strdup!\n"));
80			return False;
81		}
82
83		netgroup_ok = innetgr(tok + 1, hostname, (char *) 0, mydomain);
84
85		DEBUG(5,("looking for %s of domain %s in netgroup %s gave %s\n",
86			 hostname,
87			 mydomain,
88			 tok+1,
89			 BOOLSTR(netgroup_ok)));
90
91		free(hostname);
92
93		if (netgroup_ok) return(True);
94#else
95		DEBUG(0,("access: netgroup support is not configured\n"));
96		return (False);
97#endif
98	} else if (strcasecmp(tok, "ALL") == 0) {	/* all: match any */
99		return (True);
100	} else if (strcasecmp(tok, "FAIL") == 0) {	/* fail: match any */
101		return (FAIL);
102	} else if (strcasecmp(tok, "LOCAL") == 0) {	/* local: no dots */
103		if (strchr(s, '.') == 0 && strcasecmp(s, "unknown") != 0)
104			return (True);
105	} else if (!strcasecmp(tok, s)) {   /* match host name or address */
106		return (True);
107	} else if (tok[(tok_len = strlen(tok)) - 1] == '.') {	/* network */
108		if (strncmp(tok, s, tok_len) == 0)
109			return (True);
110	} else if ((cut = strchr(tok, '/')) != 0) {	/* netnumber/netmask */
111		if (isdigit((int)s[0]) && masked_match(tok, cut, s))
112			return (True);
113	} else if (strchr(tok, '*') != 0) {
114		*invalid_char = '*';
115	} else if (strchr(tok, '?') != 0) {
116		*invalid_char = '?';
117	}
118	return (False);
119}
120
121
122/* client_match - match host name and address against token */
123static int client_match(char *tok,char *item)
124{
125    char **client = (char **)item;
126    int     match;
127	char invalid_char = '\0';
128
129    /*
130     * Try to match the address first. If that fails, try to match the host
131     * name if available.
132     */
133
134    if ((match = string_match(tok, client[1], &invalid_char)) == 0) {
135		if(invalid_char)
136			DEBUG(0,("client_match: address match failing due to invalid character '%c' found in \
137token '%s' in an allow/deny hosts line.\n", invalid_char, tok ));
138
139		if (client[0][0] != 0)
140			match = string_match(tok, client[0], &invalid_char);
141
142		if(invalid_char)
143			DEBUG(0,("client_match: address match failing due to invalid character '%c' found in \
144token '%s' in an allow/deny hosts line.\n", invalid_char, tok ));
145	}
146
147    return (match);
148}
149
150/* list_match - match an item against a list of tokens with exceptions */
151/* (All modifications are marked with the initials "jkf") */
152static int list_match(char *list,char *item, int (*match_fn)(char *, char *))
153{
154    char   *tok;
155    char   *listcopy;		/* jkf */
156    int     match = False;
157
158    /*
159     * jkf@soton.ac.uk -- 31 August 1994 -- Stop list_match()
160     * overwriting the list given as its first parameter.
161     */
162
163    /* jkf -- can get called recursively with NULL list */
164    listcopy = (list == 0) ? (char *)0 : strdup(list);
165
166    /*
167     * Process tokens one at a time. We have exhausted all possible matches
168     * when we reach an "EXCEPT" token or the end of the list. If we do find
169     * a match, look for an "EXCEPT" list and recurse to determine whether
170     * the match is affected by any exceptions.
171     */
172
173    for (tok = strtok(listcopy, sep); tok ; tok = strtok(NULL, sep)) {
174	if (strcasecmp(tok, "EXCEPT") == 0)	/* EXCEPT: give up */
175	    break;
176	if ((match = (*match_fn) (tok, item)))	/* True or FAIL */
177	    break;
178    }
179    /* Process exceptions to True or FAIL matches. */
180
181    if (match != False) {
182	while ((tok = strtok((char *) 0, sep)) && strcasecmp(tok, "EXCEPT"))
183	     /* VOID */ ;
184	if (tok == 0 || list_match((char *) 0, item, match_fn) == False) {
185	    if (listcopy != 0) free(listcopy); /* jkf */
186	    return (match);
187	}
188    }
189
190    if (listcopy != 0) free(listcopy); /* jkf */
191    return (False);
192}
193
194
195/* return true if access should be allowed */
196BOOL allow_access(char *deny_list,char *allow_list,
197		  char *cname,char *caddr)
198{
199	char *client[2];
200
201	client[0] = cname;
202	client[1] = caddr;
203
204	/* if it is loopback then always allow unless specifically denied */
205	if (strcmp(caddr, "127.0.0.1") == 0) {
206		if (deny_list &&
207		    list_match(deny_list,(char *)client,client_match)) {
208			return False;
209		}
210		return True;
211	}
212
213	/* if theres no deny list and no allow list then allow access */
214	if ((!deny_list || *deny_list == 0) &&
215	    (!allow_list || *allow_list == 0)) {
216		return(True);
217	}
218
219	/* if there is an allow list but no deny list then allow only hosts
220	   on the allow list */
221	if (!deny_list || *deny_list == 0)
222		return(list_match(allow_list,(char *)client,client_match));
223
224	/* if theres a deny list but no allow list then allow
225	   all hosts not on the deny list */
226	if (!allow_list || *allow_list == 0)
227		return(!list_match(deny_list,(char *)client,client_match));
228
229	/* if there are both type of list then allow all hosts on the
230           allow list */
231	if (list_match(allow_list,(char *)client,client_match))
232		return (True);
233
234	/* if there are both type of list and it's not on the allow then
235	   allow it if its not on the deny */
236	if (list_match(deny_list,(char *)client,client_match))
237		return (False);
238
239	return (True);
240}
241
242/* return true if access should be allowed to a service for a socket */
243BOOL check_access(int sock, char *allow_list, char *deny_list)
244{
245	BOOL ret = False;
246
247	if (deny_list) deny_list = strdup(deny_list);
248	if (allow_list) allow_list = strdup(allow_list);
249
250	if ((!deny_list || *deny_list==0) && (!allow_list || *allow_list==0)) {
251		ret = True;
252	}
253
254	if (!ret) {
255		if (allow_access(deny_list,allow_list,
256				 client_name(sock),client_addr(sock))) {
257			DEBUG(2,("Allowed connection from %s (%s)\n",
258				 client_name(sock),client_addr(sock)));
259			ret = True;
260		} else {
261			DEBUG(0,("Denied connection from %s (%s)\n",
262				 client_name(sock),client_addr(sock)));
263		}
264	}
265
266	if (deny_list) free(deny_list);
267	if (allow_list) free(allow_list);
268	return(ret);
269}
270