2007-10-16 22:33:37 +04:00
/*
1998-08-10 11:29:57 +04:00
This module is an adaption of code from the tcpd - 1.4 package written
by Wietse Venema , Eindhoven University of Technology , The Netherlands .
1996-05-04 11:50:46 +04:00
1998-08-10 11:29:57 +04:00
The code is used here with permission .
1996-05-04 11:50:46 +04:00
1998-08-10 11:29:57 +04:00
The code has been considerably changed from the original . Bug reports
2015-02-10 02:26:56 +03:00
should be sent to samba - technical @ lists . samba . org
2007-10-16 22:33:37 +04:00
Updated for IPv6 by Jeremy Allison ( C ) 2007.
1996-05-04 11:50:46 +04:00
*/
2016-11-16 19:15:27 +03:00
# include "replace.h"
# include "system/locale.h"
# include "lib/util/debug.h"
2014-07-17 14:58:34 +04:00
# include "../lib/util/memcache.h"
2011-05-02 06:20:21 +04:00
# include "lib/socket/interfaces.h"
2016-11-11 04:02:08 +03:00
# include "lib/util/samba_util.h"
2016-11-11 04:15:20 +03:00
# include "lib/util/util_net.h"
# include "lib/util/samba_util.h"
# include "lib/util/memory.h"
# include "lib/util/access.h"
# include "lib/util/unix_match.h"
2020-07-03 09:11:20 +03:00
# include "lib/util/smb_strtox.h"
1996-05-04 11:50:46 +04:00
2007-10-16 22:33:37 +04:00
# define NAME_INDEX 0
# define ADDR_INDEX 1
2002-03-27 04:56:31 +03:00
1998-08-10 11:29:57 +04:00
/* masked_match - match address against netnumber/netmask */
2007-10-16 22:33:37 +04:00
static bool masked_match ( const char * tok , const char * slash , const char * s )
1996-05-04 11:50:46 +04:00
{
2007-10-16 22:33:37 +04:00
struct sockaddr_storage ss_mask ;
struct sockaddr_storage ss_tok ;
struct sockaddr_storage ss_host ;
char * tok_copy = NULL ;
1998-08-10 11:29:57 +04:00
2007-10-16 22:33:37 +04:00
if ( ! interpret_string_addr ( & ss_host , s , 0 ) ) {
return false ;
}
2002-11-13 02:20:50 +03:00
2007-10-16 22:33:37 +04:00
if ( * tok = = ' [ ' ) {
/* IPv6 address - remove braces. */
2016-11-11 04:02:08 +03:00
tok_copy = smb_xstrdup ( tok + 1 ) ;
2007-10-16 22:33:37 +04:00
if ( ! tok_copy ) {
return false ;
}
/* Remove the terminating ']' */
tok_copy [ PTR_DIFF ( slash , tok ) - 1 ] = ' \0 ' ;
} else {
2016-11-11 04:02:08 +03:00
tok_copy = smb_xstrdup ( tok ) ;
2007-10-16 22:33:37 +04:00
if ( ! tok_copy ) {
return false ;
}
/* Remove the terminating '/' */
tok_copy [ PTR_DIFF ( slash , tok ) ] = ' \0 ' ;
}
if ( ! interpret_string_addr ( & ss_tok , tok_copy , 0 ) ) {
SAFE_FREE ( tok_copy ) ;
return false ;
}
SAFE_FREE ( tok_copy ) ;
2002-03-27 04:56:31 +03:00
if ( strlen ( slash + 1 ) > 2 ) {
2007-10-26 04:17:46 +04:00
if ( ! interpret_string_addr ( & ss_mask , slash + 1 , 0 ) ) {
2007-10-16 22:33:37 +04:00
return false ;
}
2002-03-27 04:56:31 +03:00
} else {
2019-01-30 10:33:02 +03:00
int error = 0 ;
unsigned long val ;
2019-06-04 09:57:03 +03:00
val = smb_strtoul ( slash + 1 ,
NULL ,
0 ,
& error ,
SMB_STR_FULL_STR_CONV ) ;
if ( error ! = 0 ) {
2007-10-16 22:33:37 +04:00
return false ;
}
if ( ! make_netmask ( & ss_mask , & ss_tok , val ) ) {
return false ;
}
2002-03-27 04:56:31 +03:00
}
2010-08-14 16:26:20 +04:00
return same_net ( ( struct sockaddr * ) ( void * ) & ss_host ,
( struct sockaddr * ) ( void * ) & ss_tok ,
( struct sockaddr * ) ( void * ) & ss_mask ) ;
1996-05-04 11:50:46 +04:00
}
2007-10-16 22:33:37 +04:00
/* string_match - match string s against token tok */
static bool string_match ( const char * tok , const char * s )
1996-05-04 11:50:46 +04:00
{
1999-12-13 16:27:58 +03:00
size_t tok_len ;
size_t str_len ;
2002-11-13 02:20:50 +03:00
const char * cut ;
1998-08-10 11:29:57 +04:00
2007-10-16 22:33:37 +04:00
/* Return true if a token has the magic value "ALL". Return
* true if the token is " FAIL " . If the token starts with a " . "
* ( domain name ) , return true if it matches the last fields of
1998-08-10 11:29:57 +04:00
* the string . If the token has the magic value " LOCAL " ,
2007-10-16 22:33:37 +04:00
* return true if the string does not contain a " . "
1998-08-10 11:29:57 +04:00
* character . If the token ends on a " . " ( network number ) ,
2007-10-16 22:33:37 +04:00
* return true if it matches the first fields of the
1998-08-10 11:29:57 +04:00
* string . If the token begins with a " @ " ( netgroup name ) ,
2007-10-16 22:33:37 +04:00
* return true if the string is a ( host ) member of the
* netgroup . Return true if the token fully matches the
1998-08-10 11:29:57 +04:00
* string . If the token is a netnumber / netmask pair , return
2007-10-16 22:33:37 +04:00
* true if the address is a member of the specified subnet .
2001-10-29 11:26:45 +03:00
*/
1998-08-10 11:29:57 +04:00
if ( tok [ 0 ] = = ' . ' ) { /* domain: match last fields */
if ( ( str_len = strlen ( s ) ) > ( tok_len = strlen ( tok ) )
2016-11-11 04:07:11 +03:00
& & strequal_m ( tok , s + str_len - tok_len ) ) {
2007-10-16 22:33:37 +04:00
return true ;
}
2022-06-03 18:48:21 +03:00
} else if ( tok [ 0 ] = = ' @ ' ) { /* netgroup: look it up */
2022-07-14 16:21:09 +03:00
# if defined(HAVE_NETGROUP) && defined(HAVE_INNETGR)
2022-06-03 18:48:21 +03:00
DATA_BLOB tmp ;
char * mydomain = NULL ;
char * hostname = NULL ;
bool netgroup_ok = false ;
char nis_domain_buf [ 256 ] ;
if ( memcache_lookup (
NULL , SINGLETON_CACHE ,
data_blob_string_const_null ( " yp_default_domain " ) ,
& tmp ) ) {
SMB_ASSERT ( tmp . length > 0 ) ;
mydomain = ( tmp . data [ 0 ] = = ' \0 ' )
? NULL : ( char * ) tmp . data ;
} else {
if ( getdomainname ( nis_domain_buf ,
sizeof ( nis_domain_buf ) ) = = 0 ) {
mydomain = & nis_domain_buf [ 0 ] ;
memcache_add ( NULL ,
SINGLETON_CACHE ,
data_blob_string_const_null (
" yp_default_domain " ) ,
data_blob_string_const_null (
mydomain ) ) ;
} else {
mydomain = NULL ;
}
}
if ( ! mydomain ) {
DEBUG ( 0 , ( " Unable to get default yp domain. "
" Try without it. \n " ) ) ;
}
if ( ! ( hostname = smb_xstrdup ( s ) ) ) {
DEBUG ( 1 , ( " out of memory for strdup! \n " ) ) ;
return false ;
}
netgroup_ok = innetgr ( tok + 1 , hostname , ( char * ) 0 , mydomain ) ;
DBG_INFO ( " %s %s of domain %s in netgroup %s \n " ,
netgroup_ok ? " Found " : " Could not find " ,
hostname ,
mydomain ? mydomain : " (ANY) " ,
tok + 1 ) ;
SAFE_FREE ( hostname ) ;
if ( netgroup_ok )
return true ;
# else
DEBUG ( 0 , ( " access: netgroup support is not configured \n " ) ) ;
return false ;
# endif
2016-11-11 04:07:11 +03:00
} else if ( strequal_m ( tok , " ALL " ) ) { /* all: match any */
2007-10-16 22:33:37 +04:00
return true ;
2016-11-11 04:07:11 +03:00
} else if ( strequal_m ( tok , " FAIL " ) ) { /* fail: match any */
2007-10-16 22:33:37 +04:00
return true ;
2016-11-11 04:07:11 +03:00
} else if ( strequal_m ( tok , " LOCAL " ) ) { /* local: no dots */
if ( strchr_m ( s , ' . ' ) = = 0 & & ! strequal_m ( s , " unknown " ) ) {
2007-10-16 22:33:37 +04:00
return true ;
}
2016-11-11 04:07:11 +03:00
} else if ( strequal_m ( tok , s ) ) { /* match host name or address */
2007-10-16 22:33:37 +04:00
return true ;
1998-08-10 11:29:57 +04:00
} else if ( tok [ ( tok_len = strlen ( tok ) ) - 1 ] = = ' . ' ) { /* network */
2007-10-16 22:33:37 +04:00
if ( strncmp ( tok , s , tok_len ) = = 0 ) {
return true ;
}
2001-07-04 11:36:09 +04:00
} else if ( ( cut = strchr_m ( tok , ' / ' ) ) ! = 0 ) { /* netnumber/netmask */
2007-10-17 03:01:13 +04:00
if ( ( isdigit ( s [ 0 ] ) & & strchr_m ( tok , ' . ' ) ! = NULL ) | |
2007-10-16 22:33:37 +04:00
( tok [ 0 ] = = ' [ ' & & cut > tok & & cut [ - 1 ] = = ' ] ' ) | |
( ( isxdigit ( s [ 0 ] ) | | s [ 0 ] = = ' : ' ) & &
strchr_m ( tok , ' : ' ) ! = NULL ) ) {
/* IPv4/netmask or
* [ IPv6 : addr ] / netmask or IPv6 : addr / netmask */
return masked_match ( tok , cut , s ) ;
}
} else if ( strchr_m ( tok , ' * ' ) ! = 0 | | strchr_m ( tok , ' ? ' ) ) {
return unix_wild_match ( tok , s ) ;
1998-08-10 11:29:57 +04:00
}
2007-10-16 22:33:37 +04:00
return false ;
1998-08-10 11:29:57 +04:00
}
1996-05-04 11:50:46 +04:00
1998-08-10 11:29:57 +04:00
/* client_match - match host name and address against token */
2008-08-09 02:03:23 +04:00
bool client_match ( const char * tok , const void * item )
1998-08-10 11:29:57 +04:00
{
2014-02-26 23:16:26 +04:00
const char * * client = discard_const_p ( const char * , item ) ;
2011-04-24 23:20:19 +04:00
const char * tok_addr = tok ;
const char * cli_addr = client [ ADDR_INDEX ] ;
/*
* tok and client [ ADDR_INDEX ] can be an IPv4 mapped to IPv6 ,
* we try and match the IPv4 part of address only .
* Bug # 5311 and # 7383.
*/
2016-11-11 04:09:33 +03:00
if ( strncasecmp_m ( tok_addr , " ::ffff: " , 7 ) = = 0 ) {
2011-04-24 23:20:19 +04:00
tok_addr + = 7 ;
}
2016-11-11 04:09:33 +03:00
if ( strncasecmp_m ( cli_addr , " ::ffff: " , 7 ) = = 0 ) {
2011-04-24 23:20:19 +04:00
cli_addr + = 7 ;
}
1996-05-04 11:50:46 +04:00
2002-11-13 02:20:50 +03:00
/*
* Try to match the address first . If that fails , try to match the host
* name if available .
*/
1996-05-04 11:50:46 +04:00
2011-04-24 23:20:19 +04:00
if ( string_match ( tok_addr , cli_addr ) ) {
2008-03-07 01:44:07 +03:00
return true ;
}
if ( client [ NAME_INDEX ] [ 0 ] ! = 0 ) {
if ( string_match ( tok , client [ NAME_INDEX ] ) ) {
return true ;
2007-10-16 22:33:37 +04:00
}
1999-12-13 16:27:58 +03:00
}
2008-03-07 01:44:07 +03:00
return false ;
1996-05-04 11:50:46 +04:00
}
/* list_match - match an item against a list of tokens with exceptions */
2008-08-09 02:05:38 +04:00
bool list_match ( const char * * list , const void * item ,
2007-10-16 22:33:37 +04:00
bool ( * match_fn ) ( const char * , const void * ) )
1996-05-04 11:50:46 +04:00
{
2007-10-16 22:33:37 +04:00
bool match = false ;
2002-11-13 02:20:50 +03:00
2007-10-16 22:33:37 +04:00
if ( ! list ) {
return false ;
}
2002-11-13 02:20:50 +03:00
/*
* 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 + + ) {
2016-11-11 04:07:11 +03:00
if ( strequal_m ( * list , " EXCEPT " ) ) {
2007-10-16 22:33:37 +04:00
/* EXCEPT: give up */
2002-11-13 02:20:50 +03:00
break ;
2007-10-16 22:33:37 +04:00
}
if ( ( match = ( * match_fn ) ( * list , item ) ) ) {
/* true or FAIL */
2002-11-13 02:20:50 +03:00
break ;
2007-10-16 22:33:37 +04:00
}
1996-05-04 11:50:46 +04:00
}
2007-10-16 22:33:37 +04:00
/* Process exceptions to true or FAIL matches. */
1996-05-04 11:50:46 +04:00
2007-10-16 22:33:37 +04:00
if ( match ! = false ) {
2016-11-11 04:07:11 +03:00
while ( * list & & ! strequal_m ( * list , " EXCEPT " ) ) {
2002-11-13 02:20:50 +03:00
list + + ;
2007-10-16 22:33:37 +04:00
}
1996-05-04 11:50:46 +04:00
2002-11-13 02:20:50 +03:00
for ( ; * list ; list + + ) {
2007-10-16 22:33:37 +04:00
if ( ( * match_fn ) ( * list , item ) ) {
/* Exception Found */
return false ;
}
2002-11-13 02:20:50 +03:00
}
}
2007-10-16 22:33:37 +04:00
return match ;
2002-11-13 02:20:50 +03:00
}
1996-05-04 11:50:46 +04:00
1998-08-10 11:29:57 +04:00
/* return true if access should be allowed */
2007-10-16 22:33:37 +04:00
static bool allow_access_internal ( const char * * deny_list ,
const char * * allow_list ,
const char * cname ,
const char * caddr )
1996-05-04 11:50:46 +04:00
{
2002-11-13 02:20:50 +03:00
const char * client [ 2 ] ;
1996-05-04 11:50:46 +04:00
2007-10-16 22:33:37 +04:00
client [ NAME_INDEX ] = cname ;
client [ ADDR_INDEX ] = caddr ;
1996-05-04 11:50:46 +04:00
1999-12-13 16:27:58 +03:00
/* if it is loopback then always allow unless specifically denied */
2007-10-16 22:33:37 +04:00
if ( strcmp ( caddr , " 127.0.0.1 " ) = = 0 | | strcmp ( caddr , " ::1 " ) = = 0 ) {
2002-01-18 06:08:40 +03:00
/*
* If 127.0 .0 .1 matches both allow and deny then allow .
* Patch from Steve Langasek vorlon @ netexpress . net .
*/
2007-10-16 22:33:37 +04:00
if ( deny_list & &
list_match ( deny_list , client , client_match ) & &
2002-01-18 06:08:40 +03:00
( ! allow_list | |
2007-10-16 22:33:37 +04:00
! list_match ( allow_list , client , client_match ) ) ) {
return false ;
1999-12-13 16:27:58 +03:00
}
2007-10-16 22:33:37 +04:00
return true ;
1999-12-13 16:27:58 +03:00
}
1998-08-10 11:29:57 +04:00
/* if theres no deny list and no allow list then allow access */
2007-10-16 22:33:37 +04:00
if ( ( ! deny_list | | * deny_list = = 0 ) & &
1998-08-10 11:29:57 +04:00
( ! allow_list | | * allow_list = = 0 ) ) {
2007-10-16 22:33:37 +04:00
return true ;
1996-05-04 11:50:46 +04:00
}
1998-08-10 11:29:57 +04:00
/* if there is an allow list but no deny list then allow only hosts
on the allow list */
2007-10-16 22:33:37 +04:00
if ( ! deny_list | | * deny_list = = 0 ) {
return ( list_match ( allow_list , client , client_match ) ) ;
}
1998-08-10 11:29:57 +04:00
/* if theres a deny list but no allow list then allow
all hosts not on the deny list */
2007-10-16 22:33:37 +04:00
if ( ! allow_list | | * allow_list = = 0 ) {
return ( ! list_match ( deny_list , client , client_match ) ) ;
}
1998-08-10 11:29:57 +04:00
2001-10-29 11:26:45 +03:00
/* if there are both types of list then allow all hosts on the
1998-08-10 11:29:57 +04:00
allow list */
2007-10-16 22:33:37 +04:00
if ( list_match ( allow_list , ( const char * ) client , client_match ) ) {
return true ;
}
1998-08-10 11:29:57 +04:00
2001-10-29 11:26:45 +03:00
/* if there are both types of list and it's not on the allow then
1998-08-10 11:29:57 +04:00
allow it if its not on the deny */
2007-10-16 22:33:37 +04:00
if ( list_match ( deny_list , ( const char * ) client , client_match ) ) {
return false ;
}
return true ;
1996-05-04 11:50:46 +04:00
}
2016-11-14 22:48:32 +03:00
/* return true if access should be allowed - doesn't print log message */
bool allow_access_nolog ( const char * * deny_list ,
2007-10-16 22:33:37 +04:00
const char * * allow_list ,
const char * cname ,
const char * caddr )
2002-07-15 14:35:28 +04:00
{
2007-10-16 22:33:37 +04:00
bool ret ;
2002-07-15 14:35:28 +04:00
char * nc_cname = smb_xstrdup ( cname ) ;
char * nc_caddr = smb_xstrdup ( caddr ) ;
2007-10-16 22:33:37 +04:00
2002-07-15 14:35:28 +04:00
ret = allow_access_internal ( deny_list , allow_list , nc_cname , nc_caddr ) ;
2016-11-14 22:48:32 +03:00
SAFE_FREE ( nc_cname ) ;
SAFE_FREE ( nc_caddr ) ;
return ret ;
}
/* return true if access should be allowed - prints log message */
bool allow_access ( const char * * deny_list ,
const char * * allow_list ,
const char * cname ,
const char * caddr )
{
bool ret ;
ret = allow_access_nolog ( deny_list , allow_list , cname , caddr ) ;
2010-08-18 18:48:20 +04:00
DEBUG ( ret ? 3 : 0 ,
( " %s connection from %s (%s) \n " ,
2016-11-14 22:48:32 +03:00
ret ? " Allowed " : " Denied " , cname , caddr ) ) ;
2010-08-18 18:48:20 +04:00
2002-07-15 14:35:28 +04:00
return ret ;
}