source: vendor/current/source4/lib/socket/access.c

Last change on this file was 988, checked in by Silvan Scherrer, 9 years ago

Samba Server: update vendor to version 4.4.3

File size: 10.1 KB
Line 
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-technical@lists.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 */
43static 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 */
75static 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
129struct client_addr {
130 const char *cname;
131 const char *caddr;
132};
133
134/* client_match - match host name and address against token */
135static 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 \
148token '%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 \
155token '%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 */
162static 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 */
198static 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 */
252bool socket_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
273gethostbyaddr() calls */
274
275static 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 */
308bool 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 = socket_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}
Note: See TracBrowser for help on using the repository browser.