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