LCOV - code coverage report
Current view: top level - lib/util - access.c (source / functions) Hit Total Coverage
Test: coverage report for fix-15632 9995c5c2 Lines: 64 150 42.7 %
Date: 2024-04-13 12:30:31 Functions: 6 7 85.7 %

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

Generated by: LCOV version 1.14