2 Unix SMB/CIFS implementation.
4 check access rules for socket connections
6 Copyright (C) Andrew Tridgell 2004
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.
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.
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/>.
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.
27 The code is used here with permission.
29 The code has been considerably changed from the original. Bug reports
30 should be sent to samba@samba.org
34 #include "system/network.h"
35 #include "lib/socket/socket.h"
36 #include "system/locale.h"
39 #define ALLONES ((uint32_t)0xFFFFFFFF)
41 /* masked_match - match address against netnumber/netmask */
42 static bool masked_match(TALLOC_CTX
*mem_ctx
, const char *tok
, const char *slash
, const char *s
)
49 if ((addr
= interpret_addr(s
)) == INADDR_NONE
)
52 tok_cpy
= talloc_strdup(mem_ctx
, tok
);
53 tok_cpy
[PTR_DIFF(slash
,tok
)] = '\0';
54 net
= interpret_addr(tok_cpy
);
57 if (strlen(slash
+ 1) > 2) {
58 mask
= interpret_addr(slash
+ 1);
60 mask
= (uint32_t)((ALLONES
>> atoi(slash
+ 1)) ^ ALLONES
);
61 /* convert to network byte order */
65 if (net
== INADDR_NONE
|| mask
== INADDR_NONE
) {
66 DEBUG(0,("access: bad net/mask access control: %s\n", tok
));
70 return (addr
& mask
) == (net
& mask
);
73 /* string_match - match string against token */
74 static bool string_match(TALLOC_CTX
*mem_ctx
, const char *tok
,const char *s
, char *invalid_char
)
82 /* Return true if a token has the magic value "ALL". Return
83 * FAIL if the token is "FAIL". If the token starts with a "."
84 * (domain name), return true if it matches the last fields of
85 * the string. If the token has the magic value "LOCAL",
86 * return true if the string does not contain a "."
87 * character. If the token ends on a "." (network number),
88 * return true if it matches the first fields of the
89 * string. If the token begins with a "@" (netgroup name),
90 * return true if the string is a (host) member of the
91 * netgroup. Return true if the token fully matches the
92 * string. If the token is a netnumber/netmask pair, return
93 * true if the address is a member of the specified subnet.
96 if (tok
[0] == '.') { /* domain: match last fields */
97 if ((str_len
= strlen(s
)) > (tok_len
= strlen(tok
))
98 && strcasecmp(tok
, s
+ str_len
- tok_len
)==0) {
101 } else if (tok
[0] == '@') { /* netgroup: look it up */
102 DEBUG(0,("access: netgroup support is not available\n"));
104 } else if (strcmp(tok
, "ALL")==0) { /* all: match any */
106 } else if (strcmp(tok
, "FAIL")==0) { /* fail: match any */
108 } else if (strcmp(tok
, "LOCAL")==0) { /* local: no dots */
109 if (strchr(s
, '.') == 0 && strcasecmp(s
, "unknown") != 0) {
112 } else if (strcasecmp(tok
, s
)==0) { /* match host name or address */
114 } else if (tok
[(tok_len
= strlen(tok
)) - 1] == '.') { /* network */
115 if (strncmp(tok
, s
, tok_len
) == 0)
117 } else if ((cut
= strchr(tok
, '/')) != 0) { /* netnumber/netmask */
118 if (isdigit((int)s
[0]) && masked_match(mem_ctx
, tok
, cut
, s
))
120 } else if (strchr(tok
, '*') != 0) {
122 } else if (strchr(tok
, '?') != 0) {
133 /* client_match - match host name and address against token */
134 static bool client_match(TALLOC_CTX
*mem_ctx
, const char *tok
, struct client_addr
*client
)
137 char invalid_char
= '\0';
140 * Try to match the address first. If that fails, try to match the host
144 if ((match
= string_match(mem_ctx
, tok
, client
->caddr
, &invalid_char
)) == 0) {
146 DEBUG(0,("client_match: address match failing due to invalid character '%c' found in \
147 token '%s' in an allow/deny hosts line.\n", invalid_char
, tok
));
149 if (client
->cname
[0] != 0)
150 match
= string_match(mem_ctx
, tok
, client
->cname
, &invalid_char
);
153 DEBUG(0,("client_match: address match failing due to invalid character '%c' found in \
154 token '%s' in an allow/deny hosts line.\n", invalid_char
, tok
));
160 /* list_match - match an item against a list of tokens with exceptions */
161 static bool list_match(TALLOC_CTX
*mem_ctx
, const char **list
, struct client_addr
*client
)
169 * Process tokens one at a time. We have exhausted all possible matches
170 * when we reach an "EXCEPT" token or the end of the list. If we do find
171 * a match, look for an "EXCEPT" list and recurse to determine whether
172 * the match is affected by any exceptions.
175 for (; *list
; list
++) {
176 if (strcmp(*list
, "EXCEPT")==0) /* EXCEPT: give up */
178 if ((match
= client_match(mem_ctx
, *list
, client
))) /* true or FAIL */
182 /* Process exceptions to true or FAIL matches. */
183 if (match
!= false) {
184 while (*list
&& strcmp(*list
, "EXCEPT")!=0)
187 for (; *list
; list
++) {
188 if (client_match(mem_ctx
, *list
, client
)) /* Exception Found */
196 /* return true if access should be allowed */
197 static bool allow_access_internal(TALLOC_CTX
*mem_ctx
,
198 const char **deny_list
,const char **allow_list
,
199 const char *cname
, const char *caddr
)
201 struct client_addr client
;
203 client
.cname
= cname
;
204 client
.caddr
= caddr
;
206 /* if it is loopback then always allow unless specifically denied */
207 if (strcmp(caddr
, "127.0.0.1") == 0) {
209 * If 127.0.0.1 matches both allow and deny then allow.
210 * Patch from Steve Langasek vorlon@netexpress.net.
213 list_match(mem_ctx
, deny_list
, &client
) &&
215 !list_match(mem_ctx
, allow_list
, &client
))) {
221 /* if theres no deny list and no allow list then allow access */
222 if ((!deny_list
|| *deny_list
== 0) &&
223 (!allow_list
|| *allow_list
== 0)) {
227 /* if there is an allow list but no deny list then allow only hosts
229 if (!deny_list
|| *deny_list
== 0)
230 return list_match(mem_ctx
, allow_list
, &client
);
232 /* if theres a deny list but no allow list then allow
233 all hosts not on the deny list */
234 if (!allow_list
|| *allow_list
== 0)
235 return !list_match(mem_ctx
, deny_list
, &client
);
237 /* if there are both types of list then allow all hosts on the
239 if (list_match(mem_ctx
, allow_list
, &client
))
242 /* if there are both types of list and it's not on the allow then
243 allow it if its not on the deny */
244 if (list_match(mem_ctx
, deny_list
, &client
))
250 /* return true if access should be allowed */
251 bool allow_access(TALLOC_CTX
*mem_ctx
,
252 const char **deny_list
, const char **allow_list
,
253 const char *cname
, const char *caddr
)
256 char *nc_cname
= talloc_strdup(mem_ctx
, cname
);
257 char *nc_caddr
= talloc_strdup(mem_ctx
, caddr
);
259 if (!nc_cname
|| !nc_caddr
) {
263 ret
= allow_access_internal(mem_ctx
, deny_list
, allow_list
, nc_cname
, nc_caddr
);
265 talloc_free(nc_cname
);
266 talloc_free(nc_caddr
);
271 /* return true if the char* contains ip addrs only. Used to avoid
272 gethostbyaddr() calls */
274 static bool only_ipaddrs_in_list(const char** list
)
281 for (; *list
; list
++) {
282 /* factor out the special strings */
283 if (strcmp(*list
, "ALL")==0 ||
284 strcmp(*list
, "FAIL")==0 ||
285 strcmp(*list
, "EXCEPT")==0) {
289 if (!is_ipaddress(*list
)) {
291 * if we failed, make sure that it was not because the token
292 * was a network/netmask pair. Only network/netmask pairs
295 if ((strchr(*list
, '/')) == NULL
) {
297 DEBUG(3,("only_ipaddrs_in_list: list has non-ip address (%s)\n", *list
));
306 /* return true if access should be allowed to a service for a socket */
307 bool socket_check_access(struct socket_context
*sock
,
308 const char *service_name
,
309 const char **allow_list
, const char **deny_list
)
313 struct socket_address
*addr
;
316 if ((!deny_list
|| *deny_list
==0) &&
317 (!allow_list
|| *allow_list
==0)) {
321 mem_ctx
= talloc_init("socket_check_access");
326 addr
= socket_get_peer_addr(sock
, mem_ctx
);
328 DEBUG(0,("socket_check_access: Denied connection from unknown host: could not get peer address from kernel\n"));
329 talloc_free(mem_ctx
);
333 /* bypass gethostbyaddr() calls if the lists only contain IP addrs */
334 if (!only_ipaddrs_in_list(allow_list
) ||
335 !only_ipaddrs_in_list(deny_list
)) {
336 name
= socket_get_peer_name(sock
, mem_ctx
);
343 DEBUG(0,("socket_check_access: Denied connection from unknown host\n"));
344 talloc_free(mem_ctx
);
348 ret
= allow_access(mem_ctx
, deny_list
, allow_list
, name
, addr
->addr
);
351 DEBUG(2,("socket_check_access: Allowed connection to '%s' from %s (%s)\n",
352 service_name
, name
, addr
->addr
));
354 DEBUG(0,("socket_check_access: Denied connection to '%s' from %s (%s)\n",
355 service_name
, name
, addr
->addr
));
358 talloc_free(mem_ctx
);