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.
19 /* masked_match - match address against netnumber/netmask */
20 static bool masked_match(const char *tok
, const char *slash
, const char *s
)
22 struct sockaddr_storage ss_mask
;
23 struct sockaddr_storage ss_tok
;
24 struct sockaddr_storage ss_host
;
25 char *tok_copy
= NULL
;
27 if (!interpret_string_addr(&ss_host
, s
, 0)) {
32 /* IPv6 address - remove braces. */
33 tok_copy
= SMB_STRDUP(tok
+1);
37 /* Remove the terminating ']' */
38 tok_copy
[PTR_DIFF(slash
,tok
)-1] = '\0';
40 tok_copy
= SMB_STRDUP(tok
);
44 /* Remove the terminating '/' */
45 tok_copy
[PTR_DIFF(slash
,tok
)] = '\0';
48 if (!interpret_string_addr(&ss_tok
, tok_copy
, 0)) {
55 if (strlen(slash
+ 1) > 2) {
56 if (!interpret_string_addr(&ss_mask
, slash
+1, 0)) {
61 unsigned long val
= strtoul(slash
+1, &endp
, 0);
62 if (slash
+1 == endp
|| (endp
&& *endp
!= '\0')) {
65 if (!make_netmask(&ss_mask
, &ss_tok
, val
)) {
70 return same_net((struct sockaddr
*)(void *)&ss_host
,
71 (struct sockaddr
*)(void *)&ss_tok
,
72 (struct sockaddr
*)(void *)&ss_mask
);
75 /* string_match - match string s against token tok */
76 static bool string_match(const char *tok
,const char *s
)
82 /* Return true if a token has the magic value "ALL". Return
83 * true 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 && strequal(tok
, s
+ str_len
- tok_len
)) {
101 } else if (tok
[0] == '@') { /* netgroup: look it up */
104 char *mydomain
= NULL
;
105 char *hostname
= NULL
;
106 bool netgroup_ok
= false;
109 NULL
, SINGLETON_CACHE
,
110 data_blob_string_const_null("yp_default_domain"),
113 SMB_ASSERT(tmp
.length
> 0);
114 mydomain
= (tmp
.data
[0] == '\0')
115 ? NULL
: (char *)tmp
.data
;
118 yp_get_default_domain(&mydomain
);
121 NULL
, SINGLETON_CACHE
,
122 data_blob_string_const_null("yp_default_domain"),
123 data_blob_string_const_null(mydomain
?mydomain
:""));
127 DEBUG(0,("Unable to get default yp domain. "
128 "Try without it.\n"));
130 if (!(hostname
= SMB_STRDUP(s
))) {
131 DEBUG(1,("out of memory for strdup!\n"));
135 netgroup_ok
= innetgr(tok
+ 1, hostname
, (char *) 0, mydomain
);
137 DEBUG(5,("looking for %s of domain %s in netgroup %s gave %s\n",
139 mydomain
?mydomain
:"(ANY)",
141 BOOLSTR(netgroup_ok
)));
148 DEBUG(0,("access: netgroup support is not configured\n"));
151 } else if (strequal(tok
, "ALL")) { /* all: match any */
153 } else if (strequal(tok
, "FAIL")) { /* fail: match any */
155 } else if (strequal(tok
, "LOCAL")) { /* local: no dots */
156 if (strchr_m(s
, '.') == 0 && !strequal(s
, "unknown")) {
159 } else if (strequal(tok
, s
)) { /* match host name or address */
161 } else if (tok
[(tok_len
= strlen(tok
)) - 1] == '.') { /* network */
162 if (strncmp(tok
, s
, tok_len
) == 0) {
165 } else if ((cut
= strchr_m(tok
, '/')) != 0) { /* netnumber/netmask */
166 if ((isdigit(s
[0]) && strchr_m(tok
, '.') != NULL
) ||
167 (tok
[0] == '[' && cut
> tok
&& cut
[-1] == ']') ||
168 ((isxdigit(s
[0]) || s
[0] == ':') &&
169 strchr_m(tok
, ':') != NULL
)) {
171 * [IPv6:addr]/netmask or IPv6:addr/netmask */
172 return masked_match(tok
, cut
, s
);
174 } else if (strchr_m(tok
, '*') != 0 || strchr_m(tok
, '?')) {
175 return unix_wild_match(tok
, s
);
180 /* client_match - match host name and address against token */
181 bool client_match(const char *tok
, const void *item
)
183 const char **client
= (const char **)item
;
186 * Try to match the address first. If that fails, try to match the host
190 if (string_match(tok
, client
[ADDR_INDEX
])) {
194 if (strnequal(client
[ADDR_INDEX
],"::ffff:",7) &&
195 !strnequal(tok
, "::ffff:",7)) {
196 /* client[ADDR_INDEX] is an IPv4 mapped to IPv6, but
197 * the list item is not. Try and match the IPv4 part of
198 * address only. This will happen a lot on IPv6 enabled
199 * systems with IPv4 allow/deny lists in smb.conf.
202 if (string_match(tok
, (client
[ADDR_INDEX
])+7)) {
207 if (client
[NAME_INDEX
][0] != 0) {
208 if (string_match(tok
, client
[NAME_INDEX
])) {
216 /* list_match - match an item against a list of tokens with exceptions */
217 bool list_match(const char **list
,const void *item
,
218 bool (*match_fn
)(const char *, const void *))
227 * Process tokens one at a time. We have exhausted all possible matches
228 * when we reach an "EXCEPT" token or the end of the list. If we do find
229 * a match, look for an "EXCEPT" list and recurse to determine whether
230 * the match is affected by any exceptions.
233 for (; *list
; list
++) {
234 if (strequal(*list
, "EXCEPT")) {
235 /* EXCEPT: give up */
238 if ((match
= (*match_fn
) (*list
, item
))) {
243 /* Process exceptions to true or FAIL matches. */
245 if (match
!= false) {
246 while (*list
&& !strequal(*list
, "EXCEPT")) {
250 for (; *list
; list
++) {
251 if ((*match_fn
) (*list
, item
)) {
252 /* Exception Found */
261 /* return true if access should be allowed */
262 static bool allow_access_internal(const char **deny_list
,
263 const char **allow_list
,
267 const char *client
[2];
269 client
[NAME_INDEX
] = cname
;
270 client
[ADDR_INDEX
] = caddr
;
272 /* if it is loopback then always allow unless specifically denied */
273 if (strcmp(caddr
, "127.0.0.1") == 0 || strcmp(caddr
, "::1") == 0) {
275 * If 127.0.0.1 matches both allow and deny then allow.
276 * Patch from Steve Langasek vorlon@netexpress.net.
279 list_match(deny_list
,client
,client_match
) &&
281 !list_match(allow_list
,client
, client_match
))) {
287 /* if theres no deny list and no allow list then allow access */
288 if ((!deny_list
|| *deny_list
== 0) &&
289 (!allow_list
|| *allow_list
== 0)) {
293 /* if there is an allow list but no deny list then allow only hosts
295 if (!deny_list
|| *deny_list
== 0) {
296 return(list_match(allow_list
,client
,client_match
));
299 /* if theres a deny list but no allow list then allow
300 all hosts not on the deny list */
301 if (!allow_list
|| *allow_list
== 0) {
302 return(!list_match(deny_list
,client
,client_match
));
305 /* if there are both types of list then allow all hosts on the
307 if (list_match(allow_list
,(const char *)client
,client_match
)) {
311 /* if there are both types of list and it's not on the allow then
312 allow it if its not on the deny */
313 if (list_match(deny_list
,(const char *)client
,client_match
)) {
320 /* return true if access should be allowed */
321 bool allow_access(const char **deny_list
,
322 const char **allow_list
,
327 char *nc_cname
= smb_xstrdup(cname
);
328 char *nc_caddr
= smb_xstrdup(caddr
);
330 ret
= allow_access_internal(deny_list
, allow_list
, nc_cname
, nc_caddr
);
333 ("%s connection from %s (%s)\n",
334 ret
? "Allowed" : "Denied", nc_cname
, nc_caddr
));