root/source4/lib/socket/access.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. masked_match
  2. string_match
  3. client_match
  4. list_match
  5. allow_access_internal
  6. allow_access
  7. only_ipaddrs_in_list
  8. socket_check_access

   1 /* 
   2    Unix SMB/CIFS implementation.
   3 
   4    check access rules for socket connections
   5 
   6    Copyright (C) Andrew Tridgell 2004
   7    
   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.
  12    
  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.
  17    
  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/>.
  20 */
  21 
  22 
  23 /* 
  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.
  26 
  27    The code is used here with permission.
  28 
  29    The code has been considerably changed from the original. Bug reports
  30    should be sent to samba@samba.org
  31 */
  32 
  33 #include "includes.h"
  34 #include "system/network.h"
  35 #include "lib/socket/socket.h"
  36 #include "system/locale.h"
  37 
  38 #define FAIL            (-1)
  39 #define ALLONES  ((uint32_t)0xFFFFFFFF)
  40 
  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)
     /* [<][>][^][v][top][bottom][index][help] */
  43 {
  44         uint32_t net;
  45         uint32_t mask;
  46         uint32_t addr;
  47         char *tok_cpy;
  48 
  49         if ((addr = interpret_addr(s)) == INADDR_NONE)
  50                 return false;
  51 
  52         tok_cpy = talloc_strdup(mem_ctx, tok);
  53         tok_cpy[PTR_DIFF(slash,tok)] = '\0';
  54         net = interpret_addr(tok_cpy);
  55         talloc_free(tok_cpy);
  56 
  57         if (strlen(slash + 1) > 2) {
  58                 mask = interpret_addr(slash + 1);
  59         } else {
  60                 mask = (uint32_t)((ALLONES >> atoi(slash + 1)) ^ ALLONES);
  61                 /* convert to network byte order */
  62                 mask = htonl(mask);
  63         }
  64 
  65         if (net == INADDR_NONE || mask == INADDR_NONE) {
  66                 DEBUG(0,("access: bad net/mask access control: %s\n", tok));
  67                 return false;
  68         }
  69         
  70         return (addr & mask) == (net & mask);
  71 }
  72 
  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)
     /* [<][>][^][v][top][bottom][index][help] */
  75 {
  76         size_t     tok_len;
  77         size_t     str_len;
  78         const char   *cut;
  79 
  80         *invalid_char = '\0';
  81 
  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.  
  94          */
  95 
  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) {
  99                         return true;
 100                 }
 101         } else if (tok[0] == '@') { /* netgroup: look it up */
 102                 DEBUG(0,("access: netgroup support is not available\n"));
 103                 return false;
 104         } else if (strcmp(tok, "ALL")==0) {     /* all: match any */
 105                 return true;
 106         } else if (strcmp(tok, "FAIL")==0) {    /* fail: match any */
 107                 return FAIL;
 108         } else if (strcmp(tok, "LOCAL")==0) {   /* local: no dots */
 109                 if (strchr(s, '.') == 0 && strcasecmp(s, "unknown") != 0) {
 110                         return true;
 111                 }
 112         } else if (strcasecmp(tok, s)==0) {   /* match host name or address */
 113                 return true;
 114         } else if (tok[(tok_len = strlen(tok)) - 1] == '.') {   /* network */
 115                 if (strncmp(tok, s, tok_len) == 0)
 116                         return true;
 117         } else if ((cut = strchr(tok, '/')) != 0) {     /* netnumber/netmask */
 118                 if (isdigit((int)s[0]) && masked_match(mem_ctx, tok, cut, s))
 119                         return true;
 120         } else if (strchr(tok, '*') != 0) {
 121                 *invalid_char = '*';
 122         } else if (strchr(tok, '?') != 0) {
 123                 *invalid_char = '?';
 124         }
 125         return false;
 126 }
 127 
 128 struct client_addr {
 129         const char *cname;
 130         const char *caddr;
 131 };
 132 
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 135 {
 136         bool match;
 137         char invalid_char = '\0';
 138 
 139         /*
 140          * Try to match the address first. If that fails, try to match the host
 141          * name if available.
 142          */
 143 
 144         if ((match = string_match(mem_ctx, tok, client->caddr, &invalid_char)) == 0) {
 145                 if(invalid_char)
 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 ));
 148 
 149                 if (client->cname[0] != 0)
 150                         match = string_match(mem_ctx, tok, client->cname, &invalid_char);
 151 
 152                 if(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 ));
 155         }
 156 
 157         return (match);
 158 }
 159 
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 162 {
 163         bool match = false;
 164 
 165         if (!list)
 166                 return false;
 167 
 168         /*
 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.
 173          */
 174 
 175         for (; *list ; list++) {
 176                 if (strcmp(*list, "EXCEPT")==0) /* EXCEPT: give up */
 177                         break;
 178                 if ((match = client_match(mem_ctx, *list, client)))     /* true or FAIL */
 179                         break;
 180         }
 181 
 182         /* Process exceptions to true or FAIL matches. */
 183         if (match != false) {
 184                 while (*list  && strcmp(*list, "EXCEPT")!=0)
 185                         list++;
 186 
 187                 for (; *list; list++) {
 188                         if (client_match(mem_ctx, *list, client)) /* Exception Found */
 189                                 return false;
 190                 }
 191         }
 192 
 193         return match;
 194 }
 195 
 196 /* return true if access should be allowed */
 197 static bool allow_access_internal(TALLOC_CTX *mem_ctx,
     /* [<][>][^][v][top][bottom][index][help] */
 198                                   const char **deny_list,const char **allow_list,
 199                                   const char *cname, const char *caddr)
 200 {
 201         struct client_addr client;
 202 
 203         client.cname = cname;
 204         client.caddr = caddr;
 205 
 206         /* if it is loopback then always allow unless specifically denied */
 207         if (strcmp(caddr, "127.0.0.1") == 0) {
 208                 /*
 209                  * If 127.0.0.1 matches both allow and deny then allow.
 210                  * Patch from Steve Langasek vorlon@netexpress.net.
 211                  */
 212                 if (deny_list && 
 213                         list_match(mem_ctx, deny_list, &client) &&
 214                                 (!allow_list ||
 215                                 !list_match(mem_ctx, allow_list, &client))) {
 216                         return false;
 217                 }
 218                 return true;
 219         }
 220 
 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)) {
 224                 return true;  
 225         }
 226 
 227         /* if there is an allow list but no deny list then allow only hosts
 228            on the allow list */
 229         if (!deny_list || *deny_list == 0)
 230                 return list_match(mem_ctx, allow_list, &client);
 231 
 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);
 236 
 237         /* if there are both types of list then allow all hosts on the
 238            allow list */
 239         if (list_match(mem_ctx, allow_list, &client))
 240                 return true;
 241 
 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))
 245                 return false;
 246         
 247         return true;
 248 }
 249 
 250 /* return true if access should be allowed */
 251 bool allow_access(TALLOC_CTX *mem_ctx,
     /* [<][>][^][v][top][bottom][index][help] */
 252                   const char **deny_list, const char **allow_list,
 253                   const char *cname, const char *caddr)
 254 {
 255         bool ret;
 256         char *nc_cname = talloc_strdup(mem_ctx, cname);
 257         char *nc_caddr = talloc_strdup(mem_ctx, caddr);
 258 
 259         if (!nc_cname || !nc_caddr) {
 260                 return false;
 261         }
 262         
 263         ret = allow_access_internal(mem_ctx, deny_list, allow_list, nc_cname, nc_caddr);
 264 
 265         talloc_free(nc_cname);
 266         talloc_free(nc_caddr);
 267 
 268         return ret;
 269 }
 270 
 271 /* return true if the char* contains ip addrs only.  Used to avoid 
 272 gethostbyaddr() calls */
 273 
 274 static bool only_ipaddrs_in_list(const char** list)
     /* [<][>][^][v][top][bottom][index][help] */
 275 {
 276         bool only_ip = true;
 277         
 278         if (!list)
 279                 return true;
 280                         
 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) {
 286                         continue;
 287                 }
 288                 
 289                 if (!is_ipaddress(*list)) {
 290                         /* 
 291                          * if we failed, make sure that it was not because the token
 292                          * was a network/netmask pair.  Only network/netmask pairs
 293                          * have a '/' in them
 294                          */
 295                         if ((strchr(*list, '/')) == NULL) {
 296                                 only_ip = false;
 297                                 DEBUG(3,("only_ipaddrs_in_list: list has non-ip address (%s)\n", *list));
 298                                 break;
 299                         }
 300                 }
 301         }
 302         
 303         return only_ip;
 304 }
 305 
 306 /* return true if access should be allowed to a service for a socket */
 307 bool socket_check_access(struct socket_context *sock, 
     /* [<][>][^][v][top][bottom][index][help] */
 308                          const char *service_name,
 309                          const char **allow_list, const char **deny_list)
 310 {
 311         bool ret;
 312         const char *name="";
 313         struct socket_address *addr;
 314         TALLOC_CTX *mem_ctx;
 315 
 316         if ((!deny_list  || *deny_list==0) && 
 317             (!allow_list || *allow_list==0)) {
 318                 return true;
 319         }
 320 
 321         mem_ctx = talloc_init("socket_check_access");
 322         if (!mem_ctx) {
 323                 return false;
 324         }
 325 
 326         addr = socket_get_peer_addr(sock, mem_ctx);
 327         if (!addr) {
 328                 DEBUG(0,("socket_check_access: Denied connection from unknown host: could not get peer address from kernel\n"));
 329                 talloc_free(mem_ctx);
 330                 return false;
 331         }
 332 
 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);
 337                 if (!name) {
 338                         name = addr->addr;
 339                 }
 340         }
 341 
 342         if (!addr) {
 343                 DEBUG(0,("socket_check_access: Denied connection from unknown host\n"));
 344                 talloc_free(mem_ctx);
 345                 return false;
 346         }
 347 
 348         ret = allow_access(mem_ctx, deny_list, allow_list, name, addr->addr);
 349         
 350         if (ret) {
 351                 DEBUG(2,("socket_check_access: Allowed connection to '%s' from %s (%s)\n", 
 352                          service_name, name, addr->addr));
 353         } else {
 354                 DEBUG(0,("socket_check_access: Denied connection to '%s' from %s (%s)\n", 
 355                          service_name, name, addr->addr));
 356         }
 357 
 358         talloc_free(mem_ctx);
 359 
 360         return ret;
 361 }

/* [<][>][^][v][top][bottom][index][help] */