~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source3/lib/access.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
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.
 
4
 
 
5
   The code is used here with permission.
 
6
 
 
7
   The code has been considerably changed from the original. Bug reports
 
8
   should be sent to samba@samba.org
 
9
 
 
10
   Updated for IPv6 by Jeremy Allison (C) 2007.
 
11
*/
 
12
 
 
13
#include "includes.h"
 
14
 
 
15
#define NAME_INDEX 0
 
16
#define ADDR_INDEX 1
 
17
 
 
18
/* masked_match - match address against netnumber/netmask */
 
19
static bool masked_match(const char *tok, const char *slash, const char *s)
 
20
{
 
21
        struct sockaddr_storage ss_mask;
 
22
        struct sockaddr_storage ss_tok;
 
23
        struct sockaddr_storage ss_host;
 
24
        char *tok_copy = NULL;
 
25
 
 
26
        if (!interpret_string_addr(&ss_host, s, 0)) {
 
27
                return false;
 
28
        }
 
29
 
 
30
        if (*tok == '[') {
 
31
                /* IPv6 address - remove braces. */
 
32
                tok_copy = SMB_STRDUP(tok+1);
 
33
                if (!tok_copy) {
 
34
                        return false;
 
35
                }
 
36
                /* Remove the terminating ']' */
 
37
                tok_copy[PTR_DIFF(slash,tok)-1] = '\0';
 
38
        } else {
 
39
                tok_copy = SMB_STRDUP(tok);
 
40
                if (!tok_copy) {
 
41
                        return false;
 
42
                }
 
43
                /* Remove the terminating '/' */
 
44
                tok_copy[PTR_DIFF(slash,tok)] = '\0';
 
45
        }
 
46
 
 
47
        if (!interpret_string_addr(&ss_tok, tok_copy, 0)) {
 
48
                SAFE_FREE(tok_copy);
 
49
                return false;
 
50
        }
 
51
 
 
52
        SAFE_FREE(tok_copy);
 
53
 
 
54
        if (strlen(slash + 1) > 2) {
 
55
                if (!interpret_string_addr(&ss_mask, slash+1, 0)) {
 
56
                        return false;
 
57
                }
 
58
        } else {
 
59
                char *endp = NULL;
 
60
                unsigned long val = strtoul(slash+1, &endp, 0);
 
61
                if (slash+1 == endp || (endp && *endp != '\0')) {
 
62
                        return false;
 
63
                }
 
64
                if (!make_netmask(&ss_mask, &ss_tok, val)) {
 
65
                        return false;
 
66
                }
 
67
        }
 
68
 
 
69
        return same_net((struct sockaddr *)&ss_host, (struct sockaddr *)&ss_tok, (struct sockaddr *)&ss_mask);
 
70
}
 
71
 
 
72
/* string_match - match string s against token tok */
 
73
static bool string_match(const char *tok,const char *s)
 
74
{
 
75
        size_t     tok_len;
 
76
        size_t     str_len;
 
77
        const char   *cut;
 
78
 
 
79
        /* Return true if a token has the magic value "ALL". Return
 
80
         * true if the token is "FAIL". If the token starts with a "."
 
81
         * (domain name), return true if it matches the last fields of
 
82
         * the string. If the token has the magic value "LOCAL",
 
83
         * return true if the string does not contain a "."
 
84
         * character. If the token ends on a "." (network number),
 
85
         * return true if it matches the first fields of the
 
86
         * string. If the token begins with a "@" (netgroup name),
 
87
         * return true if the string is a (host) member of the
 
88
         * netgroup. Return true if the token fully matches the
 
89
         * string. If the token is a netnumber/netmask pair, return
 
90
         * true if the address is a member of the specified subnet.
 
91
         */
 
92
 
 
93
        if (tok[0] == '.') {                    /* domain: match last fields */
 
94
                if ((str_len = strlen(s)) > (tok_len = strlen(tok))
 
95
                    && strequal(tok, s + str_len - tok_len)) {
 
96
                        return true;
 
97
                }
 
98
        } else if (tok[0] == '@') { /* netgroup: look it up */
 
99
#ifdef  HAVE_NETGROUP
 
100
                DATA_BLOB tmp;
 
101
                char *mydomain = NULL;
 
102
                char *hostname = NULL;
 
103
                bool netgroup_ok = false;
 
104
 
 
105
                if (memcache_lookup(
 
106
                            NULL, SINGLETON_CACHE,
 
107
                            data_blob_string_const_null("yp_default_domain"),
 
108
                            &tmp)) {
 
109
 
 
110
                        SMB_ASSERT(tmp.length > 0);
 
111
                        mydomain = (tmp.data[0] == '\0')
 
112
                                ? NULL : (char *)tmp.data;
 
113
                }
 
114
                else {
 
115
                        yp_get_default_domain(&mydomain);
 
116
 
 
117
                        memcache_add(
 
118
                                NULL, SINGLETON_CACHE,
 
119
                                data_blob_string_const_null("yp_default_domain"),
 
120
                                data_blob_string_const_null(mydomain?mydomain:""));
 
121
                }
 
122
 
 
123
                if (!mydomain) {
 
124
                        DEBUG(0,("Unable to get default yp domain. "
 
125
                                "Try without it.\n"));
 
126
                }
 
127
                if (!(hostname = SMB_STRDUP(s))) {
 
128
                        DEBUG(1,("out of memory for strdup!\n"));
 
129
                        return false;
 
130
                }
 
131
 
 
132
                netgroup_ok = innetgr(tok + 1, hostname, (char *) 0, mydomain);
 
133
 
 
134
                DEBUG(5,("looking for %s of domain %s in netgroup %s gave %s\n",
 
135
                         hostname,
 
136
                         mydomain?mydomain:"(ANY)",
 
137
                         tok+1,
 
138
                         BOOLSTR(netgroup_ok)));
 
139
 
 
140
                SAFE_FREE(hostname);
 
141
 
 
142
                if (netgroup_ok)
 
143
                        return true;
 
144
#else
 
145
                DEBUG(0,("access: netgroup support is not configured\n"));
 
146
                return false;
 
147
#endif
 
148
        } else if (strequal(tok, "ALL")) {      /* all: match any */
 
149
                return true;
 
150
        } else if (strequal(tok, "FAIL")) {     /* fail: match any */
 
151
                return true;
 
152
        } else if (strequal(tok, "LOCAL")) {    /* local: no dots */
 
153
                if (strchr_m(s, '.') == 0 && !strequal(s, "unknown")) {
 
154
                        return true;
 
155
                }
 
156
        } else if (strequal(tok, s)) {   /* match host name or address */
 
157
                return true;
 
158
        } else if (tok[(tok_len = strlen(tok)) - 1] == '.') {   /* network */
 
159
                if (strncmp(tok, s, tok_len) == 0) {
 
160
                        return true;
 
161
                }
 
162
        } else if ((cut = strchr_m(tok, '/')) != 0) {   /* netnumber/netmask */
 
163
                if ((isdigit(s[0]) && strchr_m(tok, '.') != NULL) ||
 
164
                        (tok[0] == '[' && cut > tok && cut[-1] == ']') ||
 
165
                        ((isxdigit(s[0]) || s[0] == ':') &&
 
166
                                strchr_m(tok, ':') != NULL)) {
 
167
                        /* IPv4/netmask or
 
168
                         * [IPv6:addr]/netmask or IPv6:addr/netmask */
 
169
                        return masked_match(tok, cut, s);
 
170
                }
 
171
        } else if (strchr_m(tok, '*') != 0 || strchr_m(tok, '?')) {
 
172
                return unix_wild_match(tok, s);
 
173
        }
 
174
        return false;
 
175
}
 
176
 
 
177
/* client_match - match host name and address against token */
 
178
bool client_match(const char *tok, const void *item)
 
179
{
 
180
        const char **client = (const char **)item;
 
181
 
 
182
        /*
 
183
         * Try to match the address first. If that fails, try to match the host
 
184
         * name if available.
 
185
         */
 
186
 
 
187
        if (string_match(tok, client[ADDR_INDEX])) {
 
188
                return true;
 
189
        }
 
190
 
 
191
        if (strnequal(client[ADDR_INDEX],"::ffff:",7) &&
 
192
                        !strnequal(tok, "::ffff:",7)) {
 
193
                /* client[ADDR_INDEX] is an IPv4 mapped to IPv6, but
 
194
                 * the list item is not. Try and match the IPv4 part of
 
195
                 * address only. This will happen a lot on IPv6 enabled
 
196
                 * systems with IPv4 allow/deny lists in smb.conf.
 
197
                 * Bug #5311. JRA.
 
198
                 */
 
199
                if (string_match(tok, (client[ADDR_INDEX])+7)) {
 
200
                        return true;
 
201
                }
 
202
        }
 
203
 
 
204
        if (client[NAME_INDEX][0] != 0) {
 
205
                if (string_match(tok, client[NAME_INDEX])) {
 
206
                        return true;
 
207
                }
 
208
        }
 
209
 
 
210
        return false;
 
211
}
 
212
 
 
213
/* list_match - match an item against a list of tokens with exceptions */
 
214
bool list_match(const char **list,const void *item,
 
215
                bool (*match_fn)(const char *, const void *))
 
216
{
 
217
        bool match = false;
 
218
 
 
219
        if (!list) {
 
220
                return false;
 
221
        }
 
222
 
 
223
        /*
 
224
         * Process tokens one at a time. We have exhausted all possible matches
 
225
         * when we reach an "EXCEPT" token or the end of the list. If we do find
 
226
         * a match, look for an "EXCEPT" list and recurse to determine whether
 
227
         * the match is affected by any exceptions.
 
228
         */
 
229
 
 
230
        for (; *list ; list++) {
 
231
                if (strequal(*list, "EXCEPT")) {
 
232
                        /* EXCEPT: give up */
 
233
                        break;
 
234
                }
 
235
                if ((match = (*match_fn) (*list, item))) {
 
236
                        /* true or FAIL */
 
237
                        break;
 
238
                }
 
239
        }
 
240
        /* Process exceptions to true or FAIL matches. */
 
241
 
 
242
        if (match != false) {
 
243
                while (*list  && !strequal(*list, "EXCEPT")) {
 
244
                        list++;
 
245
                }
 
246
 
 
247
                for (; *list; list++) {
 
248
                        if ((*match_fn) (*list, item)) {
 
249
                                /* Exception Found */
 
250
                                return false;
 
251
                        }
 
252
                }
 
253
        }
 
254
 
 
255
        return match;
 
256
}
 
257
 
 
258
/* return true if access should be allowed */
 
259
static bool allow_access_internal(const char **deny_list,
 
260
                                const char **allow_list,
 
261
                                const char *cname,
 
262
                                const char *caddr)
 
263
{
 
264
        const char *client[2];
 
265
 
 
266
        client[NAME_INDEX] = cname;
 
267
        client[ADDR_INDEX] = caddr;
 
268
 
 
269
        /* if it is loopback then always allow unless specifically denied */
 
270
        if (strcmp(caddr, "127.0.0.1") == 0 || strcmp(caddr, "::1") == 0) {
 
271
                /*
 
272
                 * If 127.0.0.1 matches both allow and deny then allow.
 
273
                 * Patch from Steve Langasek vorlon@netexpress.net.
 
274
                 */
 
275
                if (deny_list &&
 
276
                        list_match(deny_list,client,client_match) &&
 
277
                                (!allow_list ||
 
278
                                !list_match(allow_list,client, client_match))) {
 
279
                        return false;
 
280
                }
 
281
                return true;
 
282
        }
 
283
 
 
284
        /* if theres no deny list and no allow list then allow access */
 
285
        if ((!deny_list || *deny_list == 0) &&
 
286
            (!allow_list || *allow_list == 0)) {
 
287
                return true;
 
288
        }
 
289
 
 
290
        /* if there is an allow list but no deny list then allow only hosts
 
291
           on the allow list */
 
292
        if (!deny_list || *deny_list == 0) {
 
293
                return(list_match(allow_list,client,client_match));
 
294
        }
 
295
 
 
296
        /* if theres a deny list but no allow list then allow
 
297
           all hosts not on the deny list */
 
298
        if (!allow_list || *allow_list == 0) {
 
299
                return(!list_match(deny_list,client,client_match));
 
300
        }
 
301
 
 
302
        /* if there are both types of list then allow all hosts on the
 
303
           allow list */
 
304
        if (list_match(allow_list,(const char *)client,client_match)) {
 
305
                return true;
 
306
        }
 
307
 
 
308
        /* if there are both types of list and it's not on the allow then
 
309
           allow it if its not on the deny */
 
310
        if (list_match(deny_list,(const char *)client,client_match)) {
 
311
                return false;
 
312
        }
 
313
 
 
314
        return true;
 
315
}
 
316
 
 
317
/* return true if access should be allowed */
 
318
bool allow_access(const char **deny_list,
 
319
                const char **allow_list,
 
320
                const char *cname,
 
321
                const char *caddr)
 
322
{
 
323
        bool ret;
 
324
        char *nc_cname = smb_xstrdup(cname);
 
325
        char *nc_caddr = smb_xstrdup(caddr);
 
326
 
 
327
        ret = allow_access_internal(deny_list, allow_list, nc_cname, nc_caddr);
 
328
 
 
329
        SAFE_FREE(nc_cname);
 
330
        SAFE_FREE(nc_caddr);
 
331
        return ret;
 
332
}
 
333
 
 
334
/* return true if the char* contains ip addrs only.  Used to avoid
 
335
name lookup calls */
 
336
 
 
337
static bool only_ipaddrs_in_list(const char **list)
 
338
{
 
339
        bool only_ip = true;
 
340
 
 
341
        if (!list) {
 
342
                return true;
 
343
        }
 
344
 
 
345
        for (; *list ; list++) {
 
346
                /* factor out the special strings */
 
347
                if (strequal(*list, "ALL") || strequal(*list, "FAIL") ||
 
348
                    strequal(*list, "EXCEPT")) {
 
349
                        continue;
 
350
                }
 
351
 
 
352
                if (!is_ipaddress(*list)) {
 
353
                        /*
 
354
                         * If we failed, make sure that it was not because
 
355
                         * the token was a network/netmask pair. Only
 
356
                         * network/netmask pairs have a '/' in them.
 
357
                         */
 
358
                        if ((strchr_m(*list, '/')) == NULL) {
 
359
                                only_ip = false;
 
360
                                DEBUG(3,("only_ipaddrs_in_list: list has "
 
361
                                        "non-ip address (%s)\n",
 
362
                                        *list));
 
363
                                break;
 
364
                        }
 
365
                }
 
366
        }
 
367
 
 
368
        return only_ip;
 
369
}
 
370
 
 
371
/* return true if access should be allowed to a service for a socket */
 
372
bool check_access(int sock, const char **allow_list, const char **deny_list)
 
373
{
 
374
        bool ret = false;
 
375
        bool only_ip = false;
 
376
 
 
377
        if ((!deny_list || *deny_list==0) && (!allow_list || *allow_list==0))
 
378
                ret = true;
 
379
 
 
380
        if (!ret) {
 
381
                char addr[INET6_ADDRSTRLEN];
 
382
 
 
383
                /* Bypass name resolution calls if the lists
 
384
                 * only contain IP addrs */
 
385
                if (only_ipaddrs_in_list(allow_list) &&
 
386
                                only_ipaddrs_in_list(deny_list)) {
 
387
                        only_ip = true;
 
388
                        DEBUG (3, ("check_access: no hostnames "
 
389
                                "in host allow/deny list.\n"));
 
390
                        ret = allow_access(deny_list,
 
391
                                        allow_list,
 
392
                                        "",
 
393
                                        get_peer_addr(sock,addr,sizeof(addr)));
 
394
                } else {
 
395
                        DEBUG (3, ("check_access: hostnames in "
 
396
                                "host allow/deny list.\n"));
 
397
                        ret = allow_access(deny_list,
 
398
                                        allow_list,
 
399
                                        get_peer_name(sock,true),
 
400
                                        get_peer_addr(sock,addr,sizeof(addr)));
 
401
                }
 
402
 
 
403
                if (ret) {
 
404
                        DEBUG(2,("Allowed connection from %s (%s)\n",
 
405
                                 only_ip ? "" : get_peer_name(sock,true),
 
406
                                 get_peer_addr(sock,addr,sizeof(addr))));
 
407
                } else {
 
408
                        DEBUG(0,("Denied connection from %s (%s)\n",
 
409
                                 only_ip ? "" : get_peer_name(sock,true),
 
410
                                 get_peer_addr(sock,addr,sizeof(addr))));
 
411
                }
 
412
        }
 
413
 
 
414
        return(ret);
 
415
}