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
2001-04-15 01:26:45 +04:00
should be sent to samba @ 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
*/
# include "includes.h"
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. */
tok_copy = SMB_STRDUP ( tok + 1 ) ;
if ( ! tok_copy ) {
return false ;
}
/* Remove the terminating ']' */
tok_copy [ PTR_DIFF ( slash , tok ) - 1 ] = ' \0 ' ;
} else {
tok_copy = SMB_STRDUP ( tok ) ;
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 {
2007-10-16 22:33:37 +04:00
char * endp = NULL ;
unsigned long val = strtoul ( slash + 1 , & endp , 0 ) ;
if ( slash + 1 = = endp | | ( endp & & * endp ! = ' \0 ' ) ) {
return false ;
}
if ( ! make_netmask ( & ss_mask , & ss_tok , val ) ) {
return false ;
}
2002-03-27 04:56:31 +03:00
}
2007-10-16 22:33:37 +04:00
return same_net ( & ss_host , & ss_tok , & 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 ) )
2007-10-16 22:33:37 +04:00
& & strequal ( tok , s + str_len - tok_len ) ) {
return true ;
}
1998-08-10 11:29:57 +04:00
} else if ( tok [ 0 ] = = ' @ ' ) { /* netgroup: look it up */
# ifdef HAVE_NETGROUP
2007-12-20 18:05:57 +03:00
DATA_BLOB tmp ;
char * mydomain = NULL ;
1998-08-10 11:29:57 +04:00
char * hostname = NULL ;
2007-10-16 22:33:37 +04:00
bool netgroup_ok = false ;
1998-08-10 11:29:57 +04:00
2007-12-20 18:05:57 +03:00
if ( memcache_lookup (
NULL , SINGLETON_CACHE ,
data_blob_string_const ( " yp_default_domain " ) ,
& tmp ) ) {
SMB_ASSERT ( tmp . length > 0 ) ;
mydomain = ( tmp . data [ 0 ] = = ' \0 ' )
? NULL : ( char * ) tmp . data ;
}
else {
2002-11-13 02:20:50 +03:00
yp_get_default_domain ( & mydomain ) ;
1998-08-10 11:29:57 +04:00
2007-12-20 18:05:57 +03:00
memcache_add (
NULL , SINGLETON_CACHE ,
data_blob_string_const ( " yp_default_domain " ) ,
data_blob_string_const ( mydomain ? mydomain : " " ) ) ;
}
1998-08-10 11:29:57 +04:00
if ( ! mydomain ) {
2007-10-16 22:33:37 +04:00
DEBUG ( 0 , ( " Unable to get default yp domain. "
" Try without it. \n " ) ) ;
1998-08-10 11:29:57 +04:00
}
2004-12-07 21:25:53 +03:00
if ( ! ( hostname = SMB_STRDUP ( s ) ) ) {
1998-08-10 11:29:57 +04:00
DEBUG ( 1 , ( " out of memory for strdup! \n " ) ) ;
2007-10-16 22:33:37 +04:00
return false ;
1998-08-10 11:29:57 +04:00
}
2007-10-16 22:33:37 +04:00
1998-08-10 11:29:57 +04:00
netgroup_ok = innetgr ( tok + 1 , hostname , ( char * ) 0 , mydomain ) ;
2007-10-16 22:33:37 +04:00
DEBUG ( 5 , ( " looking for %s of domain %s in netgroup %s gave %s \n " ,
1998-08-10 11:29:57 +04:00
hostname ,
2007-10-16 22:33:37 +04:00
mydomain ? mydomain : " (ANY) " ,
1998-08-10 11:29:57 +04:00
tok + 1 ,
BOOLSTR ( netgroup_ok ) ) ) ;
2001-09-17 06:19:44 +04:00
SAFE_FREE ( hostname ) ;
2007-10-16 22:33:37 +04:00
2002-11-13 02:20:50 +03:00
if ( netgroup_ok )
2007-10-16 22:33:37 +04:00
return true ;
1998-08-10 11:29:57 +04:00
# else
DEBUG ( 0 , ( " access: netgroup support is not configured \n " ) ) ;
2007-10-16 22:33:37 +04:00
return false ;
1998-08-10 11:29:57 +04:00
# endif
2003-10-23 03:38:20 +04:00
} else if ( strequal ( tok , " ALL " ) ) { /* all: match any */
2007-10-16 22:33:37 +04:00
return true ;
2003-10-23 03:38:20 +04:00
} else if ( strequal ( tok , " FAIL " ) ) { /* fail: match any */
2007-10-16 22:33:37 +04:00
return true ;
2003-10-23 03:38:20 +04:00
} else if ( strequal ( tok , " LOCAL " ) ) { /* local: no dots */
2007-10-16 22:33:37 +04:00
if ( strchr_m ( s , ' . ' ) = = 0 & & ! strequal ( s , " unknown " ) ) {
return true ;
}
2003-12-01 22:25:41 +03:00
} else if ( strequal ( 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
{
2005-05-31 17:46:45 +04:00
const char * * client = ( const char * * ) item ;
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
2008-03-07 01:44:07 +03:00
if ( string_match ( tok , client [ ADDR_INDEX ] ) ) {
return true ;
}
if ( strnequal ( client [ ADDR_INDEX ] , " ::ffff: " , 7 ) & &
! strnequal ( tok , " ::ffff: " , 7 ) ) {
/* client[ADDR_INDEX] is an IPv4 mapped to IPv6, but
* the list item is not . Try and match the IPv4 part of
* address only . This will happen a lot on IPv6 enabled
* systems with IPv4 allow / deny lists in smb . conf .
* Bug # 5311. JRA .
*/
if ( string_match ( tok , ( client [ ADDR_INDEX ] ) + 7 ) ) {
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 */
2007-10-16 22:33:37 +04:00
static bool list_match ( const char * * list , const void * item ,
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 + + ) {
2007-10-16 22:33:37 +04:00
if ( strequal ( * list , " EXCEPT " ) ) {
/* 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 ) {
while ( * list & & ! strequal ( * 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
}
2002-07-15 14:35:28 +04:00
/* return true if access should be allowed */
2007-10-16 22:33:37 +04:00
bool allow_access ( const char * * deny_list ,
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 ) ;
SAFE_FREE ( nc_cname ) ;
SAFE_FREE ( nc_caddr ) ;
return ret ;
}
2007-10-16 22:33:37 +04:00
/* return true if the char* contains ip addrs only. Used to avoid
name lookup calls */
2002-11-13 02:20:50 +03:00
2007-10-16 22:33:37 +04:00
static bool only_ipaddrs_in_list ( const char * * list )
2001-03-27 04:03:49 +04:00
{
2007-10-16 22:33:37 +04:00
bool only_ip = true ;
if ( ! list ) {
return true ;
}
2002-11-13 02:20:50 +03:00
for ( ; * list ; list + + ) {
2001-03-27 04:03:49 +04:00
/* factor out the special strings */
2007-10-16 22:33:37 +04:00
if ( strequal ( * list , " ALL " ) | | strequal ( * list , " FAIL " ) | |
2003-10-23 03:38:20 +04:00
strequal ( * list , " EXCEPT " ) ) {
2001-03-27 04:03:49 +04:00
continue ;
}
2007-10-16 22:33:37 +04:00
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 .
2001-03-27 04:03:49 +04:00
*/
2003-08-15 05:42:30 +04:00
if ( ( strchr_m ( * list , ' / ' ) ) = = NULL ) {
2007-10-16 22:33:37 +04:00
only_ip = false ;
DEBUG ( 3 , ( " only_ipaddrs_in_list: list has "
" non-ip address (%s) \n " ,
* list ) ) ;
2001-03-27 04:03:49 +04:00
break ;
}
}
}
2007-10-16 22:33:37 +04:00
2001-03-27 04:03:49 +04:00
return only_ip ;
}
1998-08-10 11:29:57 +04:00
/* return true if access should be allowed to a service for a socket */
2007-10-16 22:33:37 +04:00
bool check_access ( int sock , const char * * allow_list , const char * * deny_list )
1996-05-04 11:50:46 +04:00
{
2007-10-16 22:33:37 +04:00
bool ret = false ;
bool only_ip = false ;
2002-11-13 02:20:50 +03:00
if ( ( ! deny_list | | * deny_list = = 0 ) & & ( ! allow_list | | * allow_list = = 0 ) )
2007-10-16 22:33:37 +04:00
ret = true ;
1996-05-04 11:50:46 +04:00
2002-11-13 02:20:50 +03:00
if ( ! ret ) {
2007-11-04 04:15:45 +03:00
char addr [ INET6_ADDRSTRLEN ] ;
2007-10-16 22:33:37 +04:00
/* Bypass name resolution 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 ,
" " ,
2007-11-04 04:41:26 +03:00
get_peer_addr ( sock , addr , sizeof ( addr ) ) ) ;
2002-11-13 02:20:50 +03:00
} else {
2007-10-16 22:33:37 +04:00
DEBUG ( 3 , ( " check_access: hostnames in "
" host allow/deny list. \n " ) ) ;
ret = allow_access ( deny_list ,
allow_list ,
get_peer_name ( sock , true ) ,
2007-11-04 04:41:26 +03:00
get_peer_addr ( sock , addr , sizeof ( addr ) ) ) ;
2001-03-27 04:03:49 +04:00
}
2007-10-16 22:33:37 +04:00
2002-11-13 02:20:50 +03:00
if ( ret ) {
1998-08-10 11:29:57 +04:00
DEBUG ( 2 , ( " Allowed connection from %s (%s) \n " ,
2007-10-16 22:33:37 +04:00
only_ip ? " " : get_peer_name ( sock , true ) ,
2007-11-04 04:41:26 +03:00
get_peer_addr ( sock , addr , sizeof ( addr ) ) ) ) ;
2002-11-13 02:20:50 +03:00
} else {
1998-08-10 11:29:57 +04:00
DEBUG ( 0 , ( " Denied connection from %s (%s) \n " ,
2007-10-16 22:33:37 +04:00
only_ip ? " " : get_peer_name ( sock , true ) ,
2007-11-04 04:41:26 +03:00
get_peer_addr ( sock , addr , sizeof ( addr ) ) ) ) ;
1998-08-10 11:29:57 +04:00
}
}
1996-05-04 11:50:46 +04:00
1998-08-10 11:29:57 +04:00
return ( ret ) ;
}