build: commit all the waf build files in the tree
[nivanova/samba-autobuild/.git] / source4 / lib / socket / access.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    check access rules for socket connections
5
6    Copyright (C) Andrew Tridgell 2004
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22
23 /* 
24    This module is an adaption of code from the tcpd-1.4 package written
25    by Wietse Venema, Eindhoven University of Technology, The Netherlands.
26
27    The code is used here with permission.
28
29    The code has been considerably changed from the original. Bug reports
30    should be sent to samba@samba.org
31 */
32
33 #include "includes.h"
34 #include "system/network.h"
35 #include "lib/socket/socket.h"
36 #include "system/locale.h"
37 #include "lib/util/util_net.h"
38
39 #define FAIL            (-1)
40 #define ALLONES  ((uint32_t)0xFFFFFFFF)
41
42 /* masked_match - match address against netnumber/netmask */
43 static bool masked_match(TALLOC_CTX *mem_ctx, const char *tok, const char *slash, const char *s)
44 {
45         uint32_t net;
46         uint32_t mask;
47         uint32_t addr;
48         char *tok_cpy;
49
50         if ((addr = interpret_addr(s)) == INADDR_NONE)
51                 return false;
52
53         tok_cpy = talloc_strdup(mem_ctx, tok);
54         tok_cpy[PTR_DIFF(slash,tok)] = '\0';
55         net = interpret_addr(tok_cpy);
56         talloc_free(tok_cpy);
57
58         if (strlen(slash + 1) > 2) {
59                 mask = interpret_addr(slash + 1);
60         } else {
61                 mask = (uint32_t)((ALLONES >> atoi(slash + 1)) ^ ALLONES);
62                 /* convert to network byte order */
63                 mask = htonl(mask);
64         }
65
66         if (net == INADDR_NONE || mask == INADDR_NONE) {
67                 DEBUG(0,("access: bad net/mask access control: %s\n", tok));
68                 return false;
69         }
70         
71         return (addr & mask) == (net & mask);
72 }
73
74 /* string_match - match string against token */
75 static bool string_match(TALLOC_CTX *mem_ctx, const char *tok,const char *s, char *invalid_char)
76 {
77         size_t     tok_len;
78         size_t     str_len;
79         const char   *cut;
80
81         *invalid_char = '\0';
82
83         /* Return true if a token has the magic value "ALL". Return
84          * FAIL if the token is "FAIL". If the token starts with a "."
85          * (domain name), return true if it matches the last fields of
86          * the string. If the token has the magic value "LOCAL",
87          * return true if the string does not contain a "."
88          * character. If the token ends on a "." (network number),
89          * return true if it matches the first fields of the
90          * string. If the token begins with a "@" (netgroup name),
91          * return true if the string is a (host) member of the
92          * netgroup. Return true if the token fully matches the
93          * string. If the token is a netnumber/netmask pair, return
94          * true if the address is a member of the specified subnet.  
95          */
96
97         if (tok[0] == '.') {                    /* domain: match last fields */
98                 if ((str_len = strlen(s)) > (tok_len = strlen(tok))
99                     && strcasecmp(tok, s + str_len - tok_len)==0) {
100                         return true;
101                 }
102         } else if (tok[0] == '@') { /* netgroup: look it up */
103                 DEBUG(0,("access: netgroup support is not available\n"));
104                 return false;
105         } else if (strcmp(tok, "ALL")==0) {     /* all: match any */
106                 return true;
107         } else if (strcmp(tok, "FAIL")==0) {    /* fail: match any */
108                 return FAIL;
109         } else if (strcmp(tok, "LOCAL")==0) {   /* local: no dots */
110                 if (strchr(s, '.') == 0 && strcasecmp(s, "unknown") != 0) {
111                         return true;
112                 }
113         } else if (strcasecmp(tok, s)==0) {   /* match host name or address */
114                 return true;
115         } else if (tok[(tok_len = strlen(tok)) - 1] == '.') {   /* network */
116                 if (strncmp(tok, s, tok_len) == 0)
117                         return true;
118         } else if ((cut = strchr(tok, '/')) != 0) {     /* netnumber/netmask */
119                 if (isdigit((int)s[0]) && masked_match(mem_ctx, tok, cut, s))
120                         return true;
121         } else if (strchr(tok, '*') != 0) {
122                 *invalid_char = '*';
123         } else if (strchr(tok, '?') != 0) {
124                 *invalid_char = '?';
125         }
126         return false;
127 }
128
129 struct client_addr {
130         const char *cname;
131         const char *caddr;
132 };
133
134 /* client_match - match host name and address against token */
135 static bool client_match(TALLOC_CTX *mem_ctx, const char *tok, struct client_addr *client)
136 {
137         bool match;
138         char invalid_char = '\0';
139
140         /*
141          * Try to match the address first. If that fails, try to match the host
142          * name if available.
143          */
144
145         if ((match = string_match(mem_ctx, tok, client->caddr, &invalid_char)) == 0) {
146                 if(invalid_char)
147                         DEBUG(0,("client_match: address match failing due to invalid character '%c' found in \
148 token '%s' in an allow/deny hosts line.\n", invalid_char, tok ));
149
150                 if (client->cname[0] != 0)
151                         match = string_match(mem_ctx, tok, client->cname, &invalid_char);
152
153                 if(invalid_char)
154                         DEBUG(0,("client_match: address match failing due to invalid character '%c' found in \
155 token '%s' in an allow/deny hosts line.\n", invalid_char, tok ));
156         }
157
158         return (match);
159 }
160
161 /* list_match - match an item against a list of tokens with exceptions */
162 static bool list_match(TALLOC_CTX *mem_ctx, const char **list, struct client_addr *client)
163 {
164         bool match = false;
165
166         if (!list)
167                 return false;
168
169         /*
170          * Process tokens one at a time. We have exhausted all possible matches
171          * when we reach an "EXCEPT" token or the end of the list. If we do find
172          * a match, look for an "EXCEPT" list and recurse to determine whether
173          * the match is affected by any exceptions.
174          */
175
176         for (; *list ; list++) {
177                 if (strcmp(*list, "EXCEPT")==0) /* EXCEPT: give up */
178                         break;
179                 if ((match = client_match(mem_ctx, *list, client)))     /* true or FAIL */
180                         break;
181         }
182
183         /* Process exceptions to true or FAIL matches. */
184         if (match != false) {
185                 while (*list  && strcmp(*list, "EXCEPT")!=0)
186                         list++;
187
188                 for (; *list; list++) {
189                         if (client_match(mem_ctx, *list, client)) /* Exception Found */
190                                 return false;
191                 }
192         }
193
194         return match;
195 }
196
197 /* return true if access should be allowed */
198 static bool allow_access_internal(TALLOC_CTX *mem_ctx,
199                                   const char **deny_list,const char **allow_list,
200                                   const char *cname, const char *caddr)
201 {
202         struct client_addr client;
203
204         client.cname = cname;
205         client.caddr = caddr;
206
207         /* if it is loopback then always allow unless specifically denied */
208         if (strcmp(caddr, "127.0.0.1") == 0) {
209                 /*
210                  * If 127.0.0.1 matches both allow and deny then allow.
211                  * Patch from Steve Langasek vorlon@netexpress.net.
212                  */
213                 if (deny_list && 
214                         list_match(mem_ctx, deny_list, &client) &&
215                                 (!allow_list ||
216                                 !list_match(mem_ctx, allow_list, &client))) {
217                         return false;
218                 }
219                 return true;
220         }
221
222         /* if theres no deny list and no allow list then allow access */
223         if ((!deny_list || *deny_list == 0) && 
224             (!allow_list || *allow_list == 0)) {
225                 return true;  
226         }
227
228         /* if there is an allow list but no deny list then allow only hosts
229            on the allow list */
230         if (!deny_list || *deny_list == 0)
231                 return list_match(mem_ctx, allow_list, &client);
232
233         /* if theres a deny list but no allow list then allow
234            all hosts not on the deny list */
235         if (!allow_list || *allow_list == 0)
236                 return !list_match(mem_ctx, deny_list, &client);
237
238         /* if there are both types of list then allow all hosts on the
239            allow list */
240         if (list_match(mem_ctx, allow_list, &client))
241                 return true;
242
243         /* if there are both types of list and it's not on the allow then
244            allow it if its not on the deny */
245         if (list_match(mem_ctx, deny_list, &client))
246                 return false;
247         
248         return true;
249 }
250
251 /* return true if access should be allowed */
252 bool allow_access(TALLOC_CTX *mem_ctx,
253                   const char **deny_list, const char **allow_list,
254                   const char *cname, const char *caddr)
255 {
256         bool ret;
257         char *nc_cname = talloc_strdup(mem_ctx, cname);
258         char *nc_caddr = talloc_strdup(mem_ctx, caddr);
259
260         if (!nc_cname || !nc_caddr) {
261                 return false;
262         }
263         
264         ret = allow_access_internal(mem_ctx, deny_list, allow_list, nc_cname, nc_caddr);
265
266         talloc_free(nc_cname);
267         talloc_free(nc_caddr);
268
269         return ret;
270 }
271
272 /* return true if the char* contains ip addrs only.  Used to avoid 
273 gethostbyaddr() calls */
274
275 static bool only_ipaddrs_in_list(const char** list)
276 {
277         bool only_ip = true;
278         
279         if (!list)
280                 return true;
281                         
282         for (; *list ; list++) {
283                 /* factor out the special strings */
284                 if (strcmp(*list, "ALL")==0 || 
285                     strcmp(*list, "FAIL")==0 || 
286                     strcmp(*list, "EXCEPT")==0) {
287                         continue;
288                 }
289                 
290                 if (!is_ipaddress(*list)) {
291                         /* 
292                          * if we failed, make sure that it was not because the token
293                          * was a network/netmask pair.  Only network/netmask pairs
294                          * have a '/' in them
295                          */
296                         if ((strchr(*list, '/')) == NULL) {
297                                 only_ip = false;
298                                 DEBUG(3,("only_ipaddrs_in_list: list has non-ip address (%s)\n", *list));
299                                 break;
300                         }
301                 }
302         }
303         
304         return only_ip;
305 }
306
307 /* return true if access should be allowed to a service for a socket */
308 bool socket_check_access(struct socket_context *sock, 
309                          const char *service_name,
310                          const char **allow_list, const char **deny_list)
311 {
312         bool ret;
313         const char *name="";
314         struct socket_address *addr;
315         TALLOC_CTX *mem_ctx;
316
317         if ((!deny_list  || *deny_list==0) && 
318             (!allow_list || *allow_list==0)) {
319                 return true;
320         }
321
322         mem_ctx = talloc_init("socket_check_access");
323         if (!mem_ctx) {
324                 return false;
325         }
326
327         addr = socket_get_peer_addr(sock, mem_ctx);
328         if (!addr) {
329                 DEBUG(0,("socket_check_access: Denied connection from unknown host: could not get peer address from kernel\n"));
330                 talloc_free(mem_ctx);
331                 return false;
332         }
333
334         /* bypass gethostbyaddr() calls if the lists only contain IP addrs */
335         if (!only_ipaddrs_in_list(allow_list) || 
336             !only_ipaddrs_in_list(deny_list)) {
337                 name = socket_get_peer_name(sock, mem_ctx);
338                 if (!name) {
339                         name = addr->addr;
340                 }
341         }
342
343         if (!addr) {
344                 DEBUG(0,("socket_check_access: Denied connection from unknown host\n"));
345                 talloc_free(mem_ctx);
346                 return false;
347         }
348
349         ret = allow_access(mem_ctx, deny_list, allow_list, name, addr->addr);
350         
351         if (ret) {
352                 DEBUG(2,("socket_check_access: Allowed connection to '%s' from %s (%s)\n", 
353                          service_name, name, addr->addr));
354         } else {
355                 DEBUG(0,("socket_check_access: Denied connection to '%s' from %s (%s)\n", 
356                          service_name, name, addr->addr));
357         }
358
359         talloc_free(mem_ctx);
360
361         return ret;
362 }