r4088: Get medieval on our ass about malloc.... :-). Take control of all our allocation
[samba.git] / source3 / lib / access.c
index d2286e2515ee09f3cd22d8f540013189b9ef756a..fcc795d1f266c15f80f3160990b4595a00a1f9e9 100644 (file)
@@ -5,44 +5,55 @@
    The code is used here with permission.
 
    The code has been considerably changed from the original. Bug reports
-   should be sent to samba-bugs@samba.anu.edu.au
+   should be sent to samba@samba.org
 */
 
 #include "includes.h"
 
-extern int DEBUGLEVEL;
-
-/* Delimiters for lists of daemons or clients. */
-static char *sep = ", \t";
-
 #define        FAIL            (-1)
 
+#define ALLONES  ((uint32)0xFFFFFFFF)
+
 /* masked_match - match address against netnumber/netmask */
-static int masked_match(char *tok, char *slash, char *s)
+static BOOL masked_match(const char *tok, const char *slash, const char *s)
 {
        uint32 net;
        uint32 mask;
        uint32 addr;
+       fstring tok_cpy;
 
        if ((addr = interpret_addr(s)) == INADDR_NONE)
                return (False);
-       *slash = 0;
-       net = interpret_addr(tok);
-       *slash = '/';
-       if (net == INADDR_NONE || 
-           (mask = interpret_addr(slash + 1)) == INADDR_NONE) {
-               DEBUG(0,("access: bad net/mask access control: %s", tok));
+
+       fstrcpy(tok_cpy, tok);
+       tok_cpy[PTR_DIFF(slash,tok)] = '\0';
+       net = interpret_addr(tok_cpy);
+       tok_cpy[PTR_DIFF(slash,tok)] = '/';
+
+        if (strlen(slash + 1) > 2) {
+                mask = interpret_addr(slash + 1);
+        } else {
+               mask = (uint32)((ALLONES >> atoi(slash + 1)) ^ ALLONES);
+               /* convert to network byte order */
+               mask = htonl(mask);
+        }
+
+       if (net == INADDR_NONE || mask == INADDR_NONE) {
+               DEBUG(0,("access: bad net/mask access control: %s\n", tok));
                return (False);
        }
-       return ((addr & mask) == net);
+       
+       return ((addr & mask) == (net & mask));
 }
 
 /* string_match - match string against token */
-static int string_match(char *tok,char *s)
+static BOOL string_match(const char *tok,const char *s, char *invalid_char)
 {
-       int     tok_len;
-       int     str_len;
-       char   *cut;
+       size_t     tok_len;
+       size_t     str_len;
+       const char   *cut;
+
+       *invalid_char = '\0';
 
        /* Return True if a token has the magic value "ALL". Return
         * FAIL if the token is "FAIL". If the token starts with a "."
@@ -55,11 +66,12 @@ static int string_match(char *tok,char *s)
         * return True if the string is a (host) member of the
         * netgroup. Return True if the token fully matches the
         * string. If the token is a netnumber/netmask pair, return
-        * True if the address is a member of the specified subnet.  */
+        * True if the address is a member of the specified subnet.  
+        */
 
        if (tok[0] == '.') {                    /* domain: match last fields */
                if ((str_len = strlen(s)) > (tok_len = strlen(tok))
-                   && strcasecmp(tok, s + str_len - tok_len) == 0)
+                   && strequal(tok, s + str_len - tok_len))
                        return (True);
        } else if (tok[0] == '@') { /* netgroup: look it up */
 #ifdef HAVE_NETGROUP
@@ -67,13 +79,14 @@ static int string_match(char *tok,char *s)
                char *hostname = NULL;
                BOOL netgroup_ok = False;
 
-               if (!mydomain) yp_get_default_domain(&mydomain);
+               if (!mydomain)
+                       yp_get_default_domain(&mydomain);
 
                if (!mydomain) {
                        DEBUG(0,("Unable to get default yp domain.\n"));
                        return False;
                }
-               if (!(hostname = strdup(s))) {
+               if (!(hostname = SMB_STRDUP(s))) {
                        DEBUG(1,("out of memory for strdup!\n"));
                        return False;
                }
@@ -86,104 +99,126 @@ static int string_match(char *tok,char *s)
                         tok+1,
                         BOOLSTR(netgroup_ok)));
 
-               free(hostname);
+               SAFE_FREE(hostname);
       
-               if (netgroup_ok) return(True);
+               if (netgroup_ok)
+                       return(True);
 #else
                DEBUG(0,("access: netgroup support is not configured\n"));
                return (False);
 #endif
-       } else if (strcasecmp(tok, "ALL") == 0) {       /* all: match any */
+       } else if (strequal(tok, "ALL")) {      /* all: match any */
                return (True);
-       } else if (strcasecmp(tok, "FAIL") == 0) {      /* fail: match any */
+       } else if (strequal(tok, "FAIL")) {     /* fail: match any */
                return (FAIL);
-       } else if (strcasecmp(tok, "LOCAL") == 0) {     /* local: no dots */
-               if (strchr(s, '.') == 0 && strcasecmp(s, "unknown") != 0)
+       } else if (strequal(tok, "LOCAL")) {    /* local: no dots */
+               if (strchr_m(s, '.') == 0 && !strequal(s, "unknown"))
                        return (True);
-       } else if (!strcasecmp(tok, s)) {   /* match host name or address */
+       } else if (strequal(tok, s)) {   /* match host name or address */
                return (True);
        } else if (tok[(tok_len = strlen(tok)) - 1] == '.') {   /* network */
                if (strncmp(tok, s, tok_len) == 0)
                        return (True);
-       } else if ((cut = strchr(tok, '/')) != 0) {     /* netnumber/netmask */
-               if (isdigit(s[0]) && masked_match(tok, cut, s))
+       } else if ((cut = strchr_m(tok, '/')) != 0) {   /* netnumber/netmask */
+               if (isdigit((int)s[0]) && masked_match(tok, cut, s))
                        return (True);
+       } else if (strchr_m(tok, '*') != 0) {
+               *invalid_char = '*';
+       } else if (strchr_m(tok, '?') != 0) {
+               *invalid_char = '?';
        }
        return (False);
 }
 
-
 /* client_match - match host name and address against token */
-static int client_match(char *tok,char *item)
+static BOOL client_match(const char *tok, const char *item)
 {
-    char **client = (char **)item;
-    int     match;
-
-    /*
-     * Try to match the address first. If that fails, try to match the host
-     * name if available.
-     */
-
-    if ((match = string_match(tok, client[1])) == 0)
-       if (client[0][0] != 0)
-           match = string_match(tok, client[0]);
-    return (match);
+       const char **client = (const char **)item;
+       BOOL match;
+       char invalid_char = '\0';
+
+       /*
+        * Try to match the address first. If that fails, try to match the host
+        * name if available.
+        */
+
+       if ((match = string_match(tok, client[1], &invalid_char)) == 0) {
+               if(invalid_char)
+                       DEBUG(0,("client_match: address match failing due to invalid character '%c' found in \
+token '%s' in an allow/deny hosts line.\n", invalid_char, tok ));
+
+               if (client[0][0] != 0)
+                       match = string_match(tok, client[0], &invalid_char);
+
+               if(invalid_char)
+                       DEBUG(0,("client_match: address match failing due to invalid character '%c' found in \
+token '%s' in an allow/deny hosts line.\n", invalid_char, tok ));
+       }
+
+       return (match);
 }
 
 /* list_match - match an item against a list of tokens with exceptions */
-/* (All modifications are marked with the initials "jkf") */
-static int list_match(char *list,char *item, int (*match_fn)(char *, char *))
+static BOOL list_match(const char **list,const char *item,
+               BOOL (*match_fn)(const char *, const char *))
 {
-    char   *tok;
-    char   *listcopy;          /* jkf */
-    int     match = False;
-
-    /*
-     * jkf@soton.ac.uk -- 31 August 1994 -- Stop list_match()
-     * overwriting the list given as its first parameter.
-     */
-
-    /* jkf -- can get called recursively with NULL list */
-    listcopy = (list == 0) ? (char *)0 : strdup(list);
-
-    /*
-     * Process tokens one at a time. We have exhausted all possible matches
-     * when we reach an "EXCEPT" token or the end of the list. If we do find
-     * a match, look for an "EXCEPT" list and recurse to determine whether
-     * the match is affected by any exceptions.
-     */
-
-    for (tok = strtok(listcopy, sep); tok ; tok = strtok(NULL, sep)) {
-       if (strcasecmp(tok, "EXCEPT") == 0)     /* EXCEPT: give up */
-           break;
-       if ((match = (*match_fn) (tok, item)))  /* True or FAIL */
-           break;
-    }
-    /* Process exceptions to True or FAIL matches. */
-
-    if (match != False) {
-       while ((tok = strtok((char *) 0, sep)) && strcasecmp(tok, "EXCEPT"))
-            /* VOID */ ;
-       if (tok == 0 || list_match((char *) 0, item, match_fn) == False) {
-           if (listcopy != 0) free(listcopy); /* jkf */
-           return (match);
+       BOOL match = False;
+
+       if (!list)
+               return False;
+
+       /*
+        * Process tokens one at a time. We have exhausted all possible matches
+        * when we reach an "EXCEPT" token or the end of the list. If we do find
+        * a match, look for an "EXCEPT" list and recurse to determine whether
+        * the match is affected by any exceptions.
+        */
+
+       for (; *list ; list++) {
+               if (strequal(*list, "EXCEPT"))  /* EXCEPT: give up */
+                       break;
+               if ((match = (*match_fn) (*list, item)))        /* True or FAIL */
+                       break;
        }
-    }
+       /* Process exceptions to True or FAIL matches. */
 
-    if (listcopy != 0) free(listcopy); /* jkf */
-    return (False);
-}
+       if (match != False) {
+               while (*list  && !strequal(*list, "EXCEPT"))
+                       list++;
 
+               for (; *list; list++) {
+                       if ((*match_fn) (*list, item)) /* Exception Found */
+                               return False;
+               }
+       }
+
+       return (match);
+}
 
 /* return true if access should be allowed */
-BOOL allow_access(char *deny_list,char *allow_list,
-                 char *cname,char *caddr)
+static BOOL allow_access_internal(const char **deny_list,const char **allow_list,
+                       const char *cname, const char *caddr)
 {
-       char *client[2];
+       const char *client[2];
 
        client[0] = cname;
        client[1] = caddr;  
 
+       /* if it is loopback then always allow unless specifically denied */
+       if (strcmp(caddr, "127.0.0.1") == 0) {
+               /*
+                * If 127.0.0.1 matches both allow and deny then allow.
+                * Patch from Steve Langasek vorlon@netexpress.net.
+                */
+               if (deny_list && 
+                       list_match(deny_list,(const char *)client,client_match) &&
+                               (!allow_list ||
+                               !list_match(allow_list,(const char *)client, client_match))) {
+                       return False;
+               }
+               return True;
+       }
+
        /* if theres no deny list and no allow list then allow access */
        if ((!deny_list || *deny_list == 0) && 
            (!allow_list || *allow_list == 0)) {
@@ -193,51 +228,106 @@ BOOL allow_access(char *deny_list,char *allow_list,
        /* if there is an allow list but no deny list then allow only hosts
           on the allow list */
        if (!deny_list || *deny_list == 0)
-               return(list_match(allow_list,(char *)client,client_match));
+               return(list_match(allow_list,(const char *)client,client_match));
 
        /* if theres a deny list but no allow list then allow
           all hosts not on the deny list */
        if (!allow_list || *allow_list == 0)
-               return(!list_match(deny_list,(char *)client,client_match));
+               return(!list_match(deny_list,(const char *)client,client_match));
 
-       /* if there are both type of list then allow all hosts on the
+       /* if there are both types of list then allow all hosts on the
            allow list */
-       if (list_match(allow_list,(char *)client,client_match))
+       if (list_match(allow_list,(const char *)client,client_match))
                return (True);
 
-       /* if there are both type of list and it's not on the allow then
+       /* if there are both types of list and it's not on the allow then
           allow it if its not on the deny */
-       if (list_match(deny_list,(char *)client,client_match))
+       if (list_match(deny_list,(const char *)client,client_match))
                return (False);
        
        return (True);
 }
 
+/* return true if access should be allowed */
+BOOL allow_access(const char **deny_list, const char **allow_list,
+                 const char *cname, const char *caddr)
+{
+       BOOL ret;
+       char *nc_cname = smb_xstrdup(cname);
+       char *nc_caddr = smb_xstrdup(caddr);
+       
+       ret = allow_access_internal(deny_list, allow_list, nc_cname, nc_caddr);
+
+       SAFE_FREE(nc_cname);
+       SAFE_FREE(nc_caddr);
+       return ret;
+}
+
+/* return true if the char* contains ip addrs only.  Used to avoid 
+gethostbyaddr() calls */
+
+static BOOL only_ipaddrs_in_list(const char** list)
+{
+       BOOL only_ip = True;
+       
+       if (!list)
+               return True;
+                       
+       for (; *list ; list++) {
+               /* factor out the special strings */
+               if (strequal(*list, "ALL") || strequal(*list, "FAIL") || 
+                   strequal(*list, "EXCEPT")) {
+                       continue;
+               }
+               
+               if (!is_ipaddress(*list)) {
+                       /* 
+                        * if we failed, make sure that it was not because the token
+                        * was a network/netmask pair.  Only network/netmask pairs
+                        * have a '/' in them
+                        */
+                       if ((strchr_m(*list, '/')) == NULL) {
+                               only_ip = False;
+                               DEBUG(3,("only_ipaddrs_in_list: list has non-ip address (%s)\n", *list));
+                               break;
+                       }
+               }
+       }
+       
+       return only_ip;
+}
+
 /* return true if access should be allowed to a service for a socket */
-BOOL check_access(int sock, char *allow_list, char *deny_list)
+BOOL check_access(int sock, const char **allow_list, const char **deny_list)
 {
        BOOL ret = False;
+       BOOL only_ip = False;
        
-       if (deny_list) deny_list = strdup(deny_list);
-       if (allow_list) allow_list = strdup(allow_list);
-
-       if ((!deny_list || *deny_list==0) && (!allow_list || *allow_list==0)) {
+       if ((!deny_list || *deny_list==0) && (!allow_list || *allow_list==0))
                ret = True;
-       }
 
        if (!ret) {
-               if (allow_access(deny_list,allow_list,
-                                client_name(sock),client_addr(sock))) {
+               /* bypass gethostbyaddr() calls if the lists only contain IP addrs */
+               if (only_ipaddrs_in_list(allow_list) && only_ipaddrs_in_list(deny_list)) {
+                       only_ip = True;
+                       DEBUG (3, ("check_access: no hostnames in host allow/deny list.\n"));
+                       ret = allow_access(deny_list,allow_list, "", get_peer_addr(sock));
+               } else {
+                       DEBUG (3, ("check_access: hostnames in host allow/deny list.\n"));
+                       ret = allow_access(deny_list,allow_list, get_peer_name(sock,True),
+                                          get_peer_addr(sock));
+               }
+               
+               if (ret) {
                        DEBUG(2,("Allowed connection from %s (%s)\n",
-                                client_name(sock),client_addr(sock)));
-                       ret = True;
+                                only_ip ? "" : get_peer_name(sock,True),
+                                get_peer_addr(sock)));
                } else {
                        DEBUG(0,("Denied connection from %s (%s)\n",
-                                client_name(sock),client_addr(sock)));
+                                only_ip ? "" : get_peer_name(sock,True),
+                                get_peer_addr(sock)));
                }
        }
 
-       if (deny_list) free(deny_list);
-       if (allow_list) free(allow_list);
        return(ret);
 }