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