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.
5 The code is used here with permission.
7 The code has been considerably changed from the original. Bug reports
8 should be sent to samba@samba.org
10 Updated for IPv6 by Jeremy Allison (C) 2007.
18 /* masked_match - match address against netnumber/netmask */
19 static bool masked_match(const char *tok
, const char *slash
, const char *s
)
21 struct sockaddr_storage ss_mask
;
22 struct sockaddr_storage ss_tok
;
23 struct sockaddr_storage ss_host
;
24 char *tok_copy
= NULL
;
26 if (!interpret_string_addr(&ss_host
, s
, 0)) {
31 /* IPv6 address - remove braces. */
32 tok_copy
= SMB_STRDUP(tok
+1);
36 /* Remove the terminating ']' */
37 tok_copy
[PTR_DIFF(slash
,tok
)-1] = '\0';
39 tok_copy
= SMB_STRDUP(tok
);
43 /* Remove the terminating '/' */
44 tok_copy
[PTR_DIFF(slash
,tok
)] = '\0';
47 if (!interpret_string_addr(&ss_tok
, tok_copy
, 0)) {
54 if (strlen(slash
+ 1) > 2) {
55 if (!interpret_string_addr(&ss_mask
, slash
+1, 0)) {
60 unsigned long val
= strtoul(slash
+1, &endp
, 0);
61 if (slash
+1 == endp
|| (endp
&& *endp
!= '\0')) {
64 if (!make_netmask(&ss_mask
, &ss_tok
, val
)) {
69 return same_net((struct sockaddr
*)(void *)&ss_host
,
70 (struct sockaddr
*)(void *)&ss_tok
,
71 (struct sockaddr
*)(void *)&ss_mask
);
74 /* string_match - match string s against token tok */
75 static bool string_match(const char *tok
,const char *s
)
81 /* Return true if a token has the magic value "ALL". Return
82 * true if the token is "FAIL". If the token starts with a "."
83 * (domain name), return true if it matches the last fields of
84 * the string. If the token has the magic value "LOCAL",
85 * return true if the string does not contain a "."
86 * character. If the token ends on a "." (network number),
87 * return true if it matches the first fields of the
88 * string. If the token begins with a "@" (netgroup name),
89 * return true if the string is a (host) member of the
90 * netgroup. Return true if the token fully matches the
91 * string. If the token is a netnumber/netmask pair, return
92 * true if the address is a member of the specified subnet.
95 if (tok
[0] == '.') { /* domain: match last fields */
96 if ((str_len
= strlen(s
)) > (tok_len
= strlen(tok
))
97 && strequal(tok
, s
+ str_len
- tok_len
)) {
100 } else if (tok
[0] == '@') { /* netgroup: look it up */
103 char *mydomain
= NULL
;
104 char *hostname
= NULL
;
105 bool netgroup_ok
= false;
108 NULL
, SINGLETON_CACHE
,
109 data_blob_string_const_null("yp_default_domain"),
112 SMB_ASSERT(tmp
.length
> 0);
113 mydomain
= (tmp
.data
[0] == '\0')
114 ? NULL
: (char *)tmp
.data
;
117 yp_get_default_domain(&mydomain
);
120 NULL
, SINGLETON_CACHE
,
121 data_blob_string_const_null("yp_default_domain"),
122 data_blob_string_const_null(mydomain
?mydomain
:""));
126 DEBUG(0,("Unable to get default yp domain. "
127 "Try without it.\n"));
129 if (!(hostname
= SMB_STRDUP(s
))) {
130 DEBUG(1,("out of memory for strdup!\n"));
134 netgroup_ok
= innetgr(tok
+ 1, hostname
, (char *) 0, mydomain
);
136 DEBUG(5,("looking for %s of domain %s in netgroup %s gave %s\n",
138 mydomain
?mydomain
:"(ANY)",
140 BOOLSTR(netgroup_ok
)));
147 DEBUG(0,("access: netgroup support is not configured\n"));
150 } else if (strequal(tok
, "ALL")) { /* all: match any */
152 } else if (strequal(tok
, "FAIL")) { /* fail: match any */
154 } else if (strequal(tok
, "LOCAL")) { /* local: no dots */
155 if (strchr_m(s
, '.') == 0 && !strequal(s
, "unknown")) {
158 } else if (strequal(tok
, s
)) { /* match host name or address */
160 } else if (tok
[(tok_len
= strlen(tok
)) - 1] == '.') { /* network */
161 if (strncmp(tok
, s
, tok_len
) == 0) {
164 } else if ((cut
= strchr_m(tok
, '/')) != 0) { /* netnumber/netmask */
165 if ((isdigit(s
[0]) && strchr_m(tok
, '.') != NULL
) ||
166 (tok
[0] == '[' && cut
> tok
&& cut
[-1] == ']') ||
167 ((isxdigit(s
[0]) || s
[0] == ':') &&
168 strchr_m(tok
, ':') != NULL
)) {
170 * [IPv6:addr]/netmask or IPv6:addr/netmask */
171 return masked_match(tok
, cut
, s
);
173 } else if (strchr_m(tok
, '*') != 0 || strchr_m(tok
, '?')) {
174 return unix_wild_match(tok
, s
);
179 /* client_match - match host name and address against token */
180 bool client_match(const char *tok
, const void *item
)
182 const char **client
= (const char **)item
;
185 * Try to match the address first. If that fails, try to match the host
189 if (string_match(tok
, client
[ADDR_INDEX
])) {
193 if (strnequal(client
[ADDR_INDEX
],"::ffff:",7) &&
194 !strnequal(tok
, "::ffff:",7)) {
195 /* client[ADDR_INDEX] is an IPv4 mapped to IPv6, but
196 * the list item is not. Try and match the IPv4 part of
197 * address only. This will happen a lot on IPv6 enabled
198 * systems with IPv4 allow/deny lists in smb.conf.
201 if (string_match(tok
, (client
[ADDR_INDEX
])+7)) {
206 if (client
[NAME_INDEX
][0] != 0) {
207 if (string_match(tok
, client
[NAME_INDEX
])) {
215 /* list_match - match an item against a list of tokens with exceptions */
216 bool list_match(const char **list
,const void *item
,
217 bool (*match_fn
)(const char *, const void *))
226 * Process tokens one at a time. We have exhausted all possible matches
227 * when we reach an "EXCEPT" token or the end of the list. If we do find
228 * a match, look for an "EXCEPT" list and recurse to determine whether
229 * the match is affected by any exceptions.
232 for (; *list
; list
++) {
233 if (strequal(*list
, "EXCEPT")) {
234 /* EXCEPT: give up */
237 if ((match
= (*match_fn
) (*list
, item
))) {
242 /* Process exceptions to true or FAIL matches. */
244 if (match
!= false) {
245 while (*list
&& !strequal(*list
, "EXCEPT")) {
249 for (; *list
; list
++) {
250 if ((*match_fn
) (*list
, item
)) {
251 /* Exception Found */
260 /* return true if access should be allowed */
261 static bool allow_access_internal(const char **deny_list
,
262 const char **allow_list
,
266 const char *client
[2];
268 client
[NAME_INDEX
] = cname
;
269 client
[ADDR_INDEX
] = caddr
;
271 /* if it is loopback then always allow unless specifically denied */
272 if (strcmp(caddr
, "127.0.0.1") == 0 || strcmp(caddr
, "::1") == 0) {
274 * If 127.0.0.1 matches both allow and deny then allow.
275 * Patch from Steve Langasek vorlon@netexpress.net.
278 list_match(deny_list
,client
,client_match
) &&
280 !list_match(allow_list
,client
, client_match
))) {
286 /* if theres no deny list and no allow list then allow access */
287 if ((!deny_list
|| *deny_list
== 0) &&
288 (!allow_list
|| *allow_list
== 0)) {
292 /* if there is an allow list but no deny list then allow only hosts
294 if (!deny_list
|| *deny_list
== 0) {
295 return(list_match(allow_list
,client
,client_match
));
298 /* if theres a deny list but no allow list then allow
299 all hosts not on the deny list */
300 if (!allow_list
|| *allow_list
== 0) {
301 return(!list_match(deny_list
,client
,client_match
));
304 /* if there are both types of list then allow all hosts on the
306 if (list_match(allow_list
,(const char *)client
,client_match
)) {
310 /* if there are both types of list and it's not on the allow then
311 allow it if its not on the deny */
312 if (list_match(deny_list
,(const char *)client
,client_match
)) {
319 /* return true if access should be allowed */
320 bool allow_access(const char **deny_list
,
321 const char **allow_list
,
326 char *nc_cname
= smb_xstrdup(cname
);
327 char *nc_caddr
= smb_xstrdup(caddr
);
329 ret
= allow_access_internal(deny_list
, allow_list
, nc_cname
, nc_caddr
);
332 ("%s connection from %s (%s)\n",
333 ret
? "Allowed" : "Denied", nc_cname
, nc_caddr
));