Line data Source code
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-technical@lists.samba.org
9 :
10 : Updated for IPv6 by Jeremy Allison (C) 2007.
11 : */
12 :
13 : #include "replace.h"
14 : #include "system/locale.h"
15 : #include "lib/util/debug.h"
16 : #include "../lib/util/memcache.h"
17 : #include "lib/socket/interfaces.h"
18 : #include "lib/util/samba_util.h"
19 : #include "lib/util/util_net.h"
20 : #include "lib/util/samba_util.h"
21 : #include "lib/util/memory.h"
22 : #include "lib/util/access.h"
23 : #include "lib/util/unix_match.h"
24 : #include "lib/util/smb_strtox.h"
25 :
26 : #define NAME_INDEX 0
27 : #define ADDR_INDEX 1
28 :
29 : /* masked_match - match address against netnumber/netmask */
30 0 : static bool masked_match(const char *tok, const char *slash, const char *s)
31 : {
32 0 : struct sockaddr_storage ss_mask;
33 0 : struct sockaddr_storage ss_tok;
34 0 : struct sockaddr_storage ss_host;
35 0 : char *tok_copy = NULL;
36 :
37 0 : if (!interpret_string_addr(&ss_host, s, 0)) {
38 0 : return false;
39 : }
40 :
41 0 : if (*tok == '[') {
42 : /* IPv6 address - remove braces. */
43 0 : tok_copy = smb_xstrdup(tok+1);
44 0 : if (!tok_copy) {
45 0 : return false;
46 : }
47 : /* Remove the terminating ']' */
48 0 : tok_copy[PTR_DIFF(slash,tok)-1] = '\0';
49 : } else {
50 0 : tok_copy = smb_xstrdup(tok);
51 0 : if (!tok_copy) {
52 0 : return false;
53 : }
54 : /* Remove the terminating '/' */
55 0 : tok_copy[PTR_DIFF(slash,tok)] = '\0';
56 : }
57 :
58 0 : if (!interpret_string_addr(&ss_tok, tok_copy, 0)) {
59 0 : SAFE_FREE(tok_copy);
60 0 : return false;
61 : }
62 :
63 0 : SAFE_FREE(tok_copy);
64 :
65 0 : if (strlen(slash + 1) > 2) {
66 0 : if (!interpret_string_addr(&ss_mask, slash+1, 0)) {
67 0 : return false;
68 : }
69 : } else {
70 0 : int error = 0;
71 0 : unsigned long val;
72 :
73 0 : val = smb_strtoul(slash+1,
74 : NULL,
75 : 0,
76 : &error,
77 : SMB_STR_FULL_STR_CONV);
78 0 : if (error != 0) {
79 0 : return false;
80 : }
81 0 : if (!make_netmask(&ss_mask, &ss_tok, val)) {
82 0 : return false;
83 : }
84 : }
85 :
86 0 : return same_net((struct sockaddr *)(void *)&ss_host,
87 : (struct sockaddr *)(void *)&ss_tok,
88 : (struct sockaddr *)(void *)&ss_mask);
89 : }
90 :
91 : /* string_match - match string s against token tok */
92 13 : static bool string_match(const char *tok,const char *s)
93 : {
94 0 : size_t tok_len;
95 0 : size_t str_len;
96 0 : const char *cut;
97 :
98 : /* Return true if a token has the magic value "ALL". Return
99 : * true if the token is "FAIL". If the token starts with a "."
100 : * (domain name), return true if it matches the last fields of
101 : * the string. If the token has the magic value "LOCAL",
102 : * return true if the string does not contain a "."
103 : * character. If the token ends on a "." (network number),
104 : * return true if it matches the first fields of the
105 : * string. If the token begins with a "@" (netgroup name),
106 : * return true if the string is a (host) member of the
107 : * netgroup. Return true if the token fully matches the
108 : * string. If the token is a netnumber/netmask pair, return
109 : * true if the address is a member of the specified subnet.
110 : */
111 :
112 13 : if (tok[0] == '.') { /* domain: match last fields */
113 0 : if ((str_len = strlen(s)) > (tok_len = strlen(tok))
114 0 : && strequal_m(tok, s + str_len - tok_len)) {
115 0 : return true;
116 : }
117 13 : } else if (tok[0] == '@') { /* netgroup: look it up */
118 : #if defined(HAVE_NETGROUP) && defined(HAVE_INNETGR)
119 0 : DATA_BLOB tmp;
120 0 : char *mydomain = NULL;
121 0 : char *hostname = NULL;
122 0 : bool netgroup_ok = false;
123 0 : char nis_domain_buf[256];
124 :
125 0 : if (memcache_lookup(
126 : NULL, SINGLETON_CACHE,
127 : data_blob_string_const_null("yp_default_domain"),
128 : &tmp)) {
129 :
130 0 : SMB_ASSERT(tmp.length > 0);
131 0 : mydomain = (tmp.data[0] == '\0')
132 0 : ? NULL : (char *)tmp.data;
133 : } else {
134 0 : if (getdomainname(nis_domain_buf,
135 : sizeof(nis_domain_buf)) == 0) {
136 0 : mydomain = &nis_domain_buf[0];
137 0 : memcache_add(NULL,
138 : SINGLETON_CACHE,
139 : data_blob_string_const_null(
140 : "yp_default_domain"),
141 : data_blob_string_const_null(
142 : mydomain));
143 : } else {
144 0 : mydomain = NULL;
145 : }
146 : }
147 :
148 0 : if (!mydomain) {
149 0 : DEBUG(0,("Unable to get default yp domain. "
150 : "Try without it.\n"));
151 : }
152 0 : if (!(hostname = smb_xstrdup(s))) {
153 0 : DEBUG(1,("out of memory for strdup!\n"));
154 0 : return false;
155 : }
156 :
157 0 : netgroup_ok = innetgr(tok + 1, hostname, (char *) 0, mydomain);
158 :
159 0 : DBG_INFO("%s %s of domain %s in netgroup %s\n",
160 : netgroup_ok ? "Found" : "Could not find",
161 : hostname,
162 : mydomain?mydomain:"(ANY)",
163 : tok+1);
164 :
165 0 : SAFE_FREE(hostname);
166 :
167 0 : if (netgroup_ok)
168 0 : return true;
169 : #else
170 : DEBUG(0,("access: netgroup support is not configured\n"));
171 : return false;
172 : #endif
173 13 : } else if (strequal_m(tok, "ALL")) { /* all: match any */
174 0 : return true;
175 13 : } else if (strequal_m(tok, "FAIL")) { /* fail: match any */
176 0 : return true;
177 13 : } else if (strequal_m(tok, "LOCAL")) { /* local: no dots */
178 0 : if (strchr_m(s, '.') == 0 && !strequal_m(s, "unknown")) {
179 0 : return true;
180 : }
181 13 : } else if (strequal_m(tok, s)) { /* match host name or address */
182 2 : return true;
183 11 : } else if (tok[(tok_len = strlen(tok)) - 1] == '.') { /* network */
184 2 : if (strncmp(tok, s, tok_len) == 0) {
185 1 : return true;
186 : }
187 9 : } else if ((cut = strchr_m(tok, '/')) != 0) { /* netnumber/netmask */
188 0 : if ((isdigit(s[0]) && strchr_m(tok, '.') != NULL) ||
189 0 : (tok[0] == '[' && cut > tok && cut[-1] == ']') ||
190 0 : ((isxdigit(s[0]) || s[0] == ':') &&
191 0 : strchr_m(tok, ':') != NULL)) {
192 : /* IPv4/netmask or
193 : * [IPv6:addr]/netmask or IPv6:addr/netmask */
194 0 : return masked_match(tok, cut, s);
195 : }
196 9 : } else if (strchr_m(tok, '*') != 0 || strchr_m(tok, '?')) {
197 0 : return unix_wild_match(tok, s);
198 : }
199 10 : return false;
200 : }
201 :
202 : /* client_match - match host name and address against token */
203 13 : bool client_match(const char *tok, const void *item)
204 : {
205 13 : const char **client = discard_const_p(const char *, item);
206 13 : const char *tok_addr = tok;
207 13 : const char *cli_addr = client[ADDR_INDEX];
208 :
209 : /*
210 : * tok and client[ADDR_INDEX] can be an IPv4 mapped to IPv6,
211 : * we try and match the IPv4 part of address only.
212 : * Bug #5311 and #7383.
213 : */
214 :
215 13 : if (strncasecmp_m(tok_addr, "::ffff:", 7) == 0) {
216 0 : tok_addr += 7;
217 : }
218 :
219 13 : if (strncasecmp_m(cli_addr, "::ffff:", 7) == 0) {
220 0 : cli_addr += 7;
221 : }
222 :
223 : /*
224 : * Try to match the address first. If that fails, try to match the host
225 : * name if available.
226 : */
227 :
228 13 : if (string_match(tok_addr, cli_addr)) {
229 3 : return true;
230 : }
231 :
232 10 : if (client[NAME_INDEX][0] != 0) {
233 0 : if (string_match(tok, client[NAME_INDEX])) {
234 0 : return true;
235 : }
236 : }
237 :
238 10 : return false;
239 : }
240 :
241 : /* list_match - match an item against a list of tokens with exceptions */
242 8 : bool list_match(const char **list,const void *item,
243 : bool (*match_fn)(const char *, const void *))
244 : {
245 8 : bool match = false;
246 :
247 8 : if (!list) {
248 0 : return false;
249 : }
250 :
251 : /*
252 : * Process tokens one at a time. We have exhausted all possible matches
253 : * when we reach an "EXCEPT" token or the end of the list. If we do find
254 : * a match, look for an "EXCEPT" list and recurse to determine whether
255 : * the match is affected by any exceptions.
256 : */
257 :
258 18 : for (; *list ; list++) {
259 13 : if (strequal_m(*list, "EXCEPT")) {
260 : /* EXCEPT: give up */
261 0 : break;
262 : }
263 13 : if ((match = (*match_fn) (*list, item))) {
264 : /* true or FAIL */
265 3 : break;
266 : }
267 : }
268 : /* Process exceptions to true or FAIL matches. */
269 :
270 8 : if (match != false) {
271 7 : while (*list && !strequal_m(*list, "EXCEPT")) {
272 4 : list++;
273 : }
274 :
275 3 : for (; *list; list++) {
276 0 : if ((*match_fn) (*list, item)) {
277 : /* Exception Found */
278 0 : return false;
279 : }
280 : }
281 : }
282 :
283 8 : return match;
284 : }
285 :
286 : /* return true if access should be allowed */
287 81995 : static bool allow_access_internal(const char **deny_list,
288 : const char **allow_list,
289 : const char *cname,
290 : const char *caddr)
291 : {
292 1659 : const char *client[2];
293 :
294 81995 : client[NAME_INDEX] = cname;
295 81995 : client[ADDR_INDEX] = caddr;
296 :
297 : /* if it is loopback then always allow unless specifically denied */
298 81995 : if (strcmp(caddr, "127.0.0.1") == 0 || strcmp(caddr, "::1") == 0) {
299 : /*
300 : * If 127.0.0.1 matches both allow and deny then allow.
301 : * Patch from Steve Langasek vorlon@netexpress.net.
302 : */
303 3 : if (deny_list &&
304 1 : list_match(deny_list,client,client_match) &&
305 0 : (!allow_list ||
306 0 : !list_match(allow_list,client, client_match))) {
307 0 : return false;
308 : }
309 2 : return true;
310 : }
311 :
312 : /* if there's no deny list and no allow list then allow access */
313 81993 : if ((!deny_list || *deny_list == 0) &&
314 3 : (!allow_list || *allow_list == 0)) {
315 80327 : return true;
316 : }
317 :
318 : /* if there is an allow list but no deny list then allow only hosts
319 : on the allow list */
320 7 : if (!deny_list || *deny_list == 0) {
321 3 : return(list_match(allow_list,client,client_match));
322 : }
323 :
324 : /* if there's a deny list but no allow list then allow
325 : all hosts not on the deny list */
326 4 : if (!allow_list || *allow_list == 0) {
327 4 : return(!list_match(deny_list,client,client_match));
328 : }
329 :
330 : /* if there are both types of list then allow all hosts on the
331 : allow list */
332 0 : if (list_match(allow_list,(const char *)client,client_match)) {
333 0 : return true;
334 : }
335 :
336 : /* if there are both types of list and it's not on the allow then
337 : allow it if its not on the deny */
338 0 : if (list_match(deny_list,(const char *)client,client_match)) {
339 0 : return false;
340 : }
341 :
342 0 : return true;
343 : }
344 :
345 : /* return true if access should be allowed - doesn't print log message */
346 81995 : bool allow_access_nolog(const char **deny_list,
347 : const char **allow_list,
348 : const char *cname,
349 : const char *caddr)
350 : {
351 1659 : bool ret;
352 81995 : char *nc_cname = smb_xstrdup(cname);
353 81995 : char *nc_caddr = smb_xstrdup(caddr);
354 :
355 81995 : ret = allow_access_internal(deny_list, allow_list, nc_cname, nc_caddr);
356 :
357 81995 : SAFE_FREE(nc_cname);
358 81995 : SAFE_FREE(nc_caddr);
359 81995 : return ret;
360 : }
361 :
362 : /* return true if access should be allowed - prints log message */
363 81995 : bool allow_access(const char **deny_list,
364 : const char **allow_list,
365 : const char *cname,
366 : const char *caddr)
367 : {
368 1659 : bool ret;
369 :
370 81995 : ret = allow_access_nolog(deny_list, allow_list, cname, caddr);
371 :
372 81995 : DEBUG(ret ? 3 : 0,
373 : ("%s connection from %s (%s)\n",
374 : ret ? "Allowed" : "Denied", cname, caddr));
375 :
376 81995 : return ret;
377 : }
|