1996-05-04 11:50:46 +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
1996-05-04 11:50:46 +04:00
*/
# include "includes.h"
# define FAIL (-1)
1998-08-10 11:29:57 +04:00
/* masked_match - match address against netnumber/netmask */
static int masked_match ( char * tok , char * slash , char * s )
1996-05-04 11:50:46 +04:00
{
1998-08-10 11:29:57 +04:00
uint32 net ;
uint32 mask ;
uint32 addr ;
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 ) {
1998-09-17 23:16:12 +04:00
DEBUG ( 0 , ( " access: bad net/mask access control: %s \n " , tok ) ) ;
1998-08-10 11:29:57 +04:00
return ( False ) ;
1996-05-04 11:50:46 +04:00
}
1998-08-10 11:29:57 +04:00
return ( ( addr & mask ) = = net ) ;
1996-05-04 11:50:46 +04:00
}
1998-08-10 11:29:57 +04:00
/* string_match - match string against token */
1999-12-13 16:27:58 +03:00
static int string_match ( char * tok , char * s , char * invalid_char )
1996-05-04 11:50:46 +04:00
{
1999-12-13 16:27:58 +03:00
size_t tok_len ;
size_t str_len ;
1998-08-10 11:29:57 +04:00
char * cut ;
1999-12-13 16:27:58 +03:00
* invalid_char = ' \0 ' ;
1998-08-10 11:29:57 +04:00
/* Return True if a token has the magic value "ALL". Return
* FAIL if the token is " FAIL " . If the token starts with a " . "
* ( domain name ) , return True if it matches the last fields of
* the string . If the token has the magic value " LOCAL " ,
* return True if the string does not contain a " . "
* character . If the token ends on a " . " ( network number ) ,
* return True if it matches the first fields of the
* string . If the token begins with a " @ " ( netgroup name ) ,
* 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
2001-10-29 11:26:45 +03:00
* True if the address is a member of the specified subnet .
*/
1998-08-10 11:29:57 +04:00
if ( tok [ 0 ] = = ' . ' ) { /* domain: match last fields */
if ( ( str_len = strlen ( s ) ) > ( tok_len = strlen ( tok ) )
& & strcasecmp ( tok , s + str_len - tok_len ) = = 0 )
return ( True ) ;
} else if ( tok [ 0 ] = = ' @ ' ) { /* netgroup: look it up */
# ifdef HAVE_NETGROUP
static char * mydomain = NULL ;
char * hostname = NULL ;
BOOL netgroup_ok = False ;
if ( ! mydomain ) yp_get_default_domain ( & mydomain ) ;
if ( ! mydomain ) {
DEBUG ( 0 , ( " Unable to get default yp domain. \n " ) ) ;
return False ;
}
if ( ! ( hostname = strdup ( s ) ) ) {
DEBUG ( 1 , ( " out of memory for strdup! \n " ) ) ;
return False ;
}
netgroup_ok = innetgr ( tok + 1 , hostname , ( char * ) 0 , mydomain ) ;
DEBUG ( 5 , ( " looking for %s of domain %s in netgroup %s gave %s \n " ,
hostname ,
mydomain ,
tok + 1 ,
BOOLSTR ( netgroup_ok ) ) ) ;
2001-09-17 06:19:44 +04:00
SAFE_FREE ( hostname ) ;
1998-08-10 11:29:57 +04:00
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 */
return ( True ) ;
} else if ( strcasecmp ( tok , " FAIL " ) = = 0 ) { /* fail: match any */
return ( FAIL ) ;
} else if ( strcasecmp ( tok , " LOCAL " ) = = 0 ) { /* local: no dots */
2001-07-04 11:36:09 +04:00
if ( strchr_m ( s , ' . ' ) = = 0 & & strcasecmp ( s , " unknown " ) ! = 0 )
1998-08-10 11:29:57 +04:00
return ( True ) ;
} else if ( ! strcasecmp ( 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 ) ;
2001-07-04 11:36:09 +04:00
} else if ( ( cut = strchr_m ( tok , ' / ' ) ) ! = 0 ) { /* netnumber/netmask */
1998-08-15 05:19:26 +04:00
if ( isdigit ( ( int ) s [ 0 ] ) & & masked_match ( tok , cut , s ) )
1998-08-10 11:29:57 +04:00
return ( True ) ;
2001-07-04 11:36:09 +04:00
} else if ( strchr_m ( tok , ' * ' ) ! = 0 ) {
1999-12-13 16:27:58 +03:00
* invalid_char = ' * ' ;
2001-07-04 11:36:09 +04:00
} else if ( strchr_m ( tok , ' ? ' ) ! = 0 ) {
1999-12-13 16:27:58 +03:00
* invalid_char = ' ? ' ;
1998-08-10 11:29:57 +04:00
}
return ( False ) ;
}
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 */
static int client_match ( char * tok , char * item )
{
char * * client = ( char * * ) item ;
int match ;
1999-12-13 16:27:58 +03:00
char invalid_char = ' \0 ' ;
1996-05-04 11:50:46 +04:00
1998-08-10 11:29:57 +04: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
1999-12-13 16:27:58 +03:00
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 ));
}
1998-08-10 11:29:57 +04:00
return ( match ) ;
1996-05-04 11:50:46 +04:00
}
/* list_match - match an item against a list of tokens with exceptions */
2001-06-20 20:54:32 +04:00
static int list_match ( char * * list , char * item , int ( * match_fn ) ( char * , char * ) )
1996-05-04 11:50:46 +04:00
{
1998-08-10 11:29:57 +04:00
int match = False ;
1996-05-04 11:50:46 +04:00
2001-06-20 20:54:32 +04:00
if ( ! list ) return False ;
1996-05-04 11:50:46 +04: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 .
*/
2001-06-20 20:54:32 +04:00
for ( ; * list ; list + + ) {
if ( strcasecmp ( * list , " EXCEPT " ) = = 0 ) /* EXCEPT: give up */
1996-05-04 11:50:46 +04:00
break ;
2001-06-20 20:54:32 +04:00
if ( ( match = ( * match_fn ) ( * list , item ) ) ) /* True or FAIL */
1996-05-04 11:50:46 +04:00
break ;
}
1998-08-10 11:29:57 +04:00
/* Process exceptions to True or FAIL matches. */
1996-05-04 11:50:46 +04:00
1998-08-10 11:29:57 +04:00
if ( match ! = False ) {
2001-06-20 20:54:32 +04:00
while ( * list & & strcasecmp ( * list , " EXCEPT " ) )
list + + ;
for ( ; * list ; list + + ) {
if ( ( * match_fn ) ( * list , item ) ) /* Exception Found */
return False ;
1996-05-04 11:50:46 +04:00
}
}
2001-06-20 20:54:32 +04:00
return ( match ) ;
1996-05-04 11:50:46 +04:00
}
1998-08-10 11:29:57 +04:00
/* return true if access should be allowed */
2001-06-20 20:54:32 +04:00
BOOL allow_access ( char * * deny_list , char * * allow_list ,
1998-08-10 11:29:57 +04:00
char * cname , char * caddr )
1996-05-04 11:50:46 +04:00
{
1998-08-10 11:29:57 +04:00
char * client [ 2 ] ;
1996-05-04 11:50:46 +04:00
1998-08-10 11:29:57 +04:00
client [ 0 ] = cname ;
client [ 1 ] = 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 */
if ( strcmp ( caddr , " 127.0.0.1 " ) = = 0 ) {
if ( deny_list & &
list_match ( deny_list , ( char * ) client , client_match ) ) {
return False ;
}
return True ;
}
1998-08-10 11:29:57 +04:00
/* if theres no deny list and no allow list then allow access */
if ( ( ! deny_list | | * deny_list = = 0 ) & &
( ! allow_list | | * allow_list = = 0 ) ) {
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 */
if ( ! deny_list | | * deny_list = = 0 )
return ( list_match ( allow_list , ( 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 ) ) ;
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 */
if ( list_match ( allow_list , ( char * ) client , client_match ) )
return ( True ) ;
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 */
if ( list_match ( deny_list , ( char * ) client , client_match ) )
return ( False ) ;
return ( True ) ;
1996-05-04 11:50:46 +04:00
}
2001-03-27 04:03:49 +04:00
/* return true if the char* contains ip addrs only. Used to avoid
gethostbyaddr ( ) calls */
2001-06-20 20:54:32 +04:00
static BOOL only_ipaddrs_in_list ( char * * list )
2001-03-27 04:03:49 +04:00
{
BOOL only_ip = True ;
2001-06-20 20:54:32 +04:00
if ( ! list ) return True ;
2001-03-27 04:03:49 +04:00
2001-06-20 20:54:32 +04:00
for ( ; * list ; list + + )
2001-03-27 04:03:49 +04:00
{
/* factor out the special strings */
2001-06-20 20:54:32 +04:00
if ( ! strcasecmp ( * list , " ALL " ) | | ! strcasecmp ( * list , " FAIL " ) | |
! strcasecmp ( * list , " EXCEPT " ) )
2001-03-27 04:03:49 +04:00
{
continue ;
}
2001-06-20 20:54:32 +04:00
if ( ! is_ipaddress ( * list ) )
2001-03-27 04:03:49 +04:00
{
char * p ;
/*
2001-04-20 03:13:38 +04:00
* if we failed , make sure that it was not because the token
2001-03-27 04:03:49 +04:00
* was a network / netmask pair . Only network / netmask pairs
* have a ' / ' in them
*/
2001-07-04 11:36:09 +04:00
if ( ( p = strchr_m ( * list , ' / ' ) ) = = NULL )
2001-03-27 04:03:49 +04:00
{
only_ip = False ;
2001-06-20 20:54:32 +04:00
DEBUG ( 3 , ( " only_ipaddrs_in_list: list has non-ip address (%s) \n " , * list ) ) ;
2001-03-27 04:03:49 +04:00
break ;
}
}
}
return only_ip ;
}
1998-08-10 11:29:57 +04:00
/* return true if access should be allowed to a service for a socket */
2001-06-20 20:54:32 +04:00
BOOL check_access ( int sock , char * * allow_list , char * * deny_list )
1996-05-04 11:50:46 +04:00
{
1998-08-10 11:29:57 +04:00
BOOL ret = False ;
2001-03-27 04:03:49 +04:00
BOOL only_ip = False ;
1998-08-10 11:29:57 +04:00
2001-03-27 04:03:49 +04:00
if ( ( ! deny_list | | * deny_list = = 0 ) & & ( ! allow_list | | * allow_list = = 0 ) )
{
1998-08-10 11:29:57 +04:00
ret = True ;
}
1996-05-04 11:50:46 +04:00
2001-03-27 04:03:49 +04:00
if ( ! ret )
{
/* 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_socket_addr ( sock ) ) ;
}
else
{
DEBUG ( 3 , ( " check_access: hostnames in host allow/deny list. \n " ) ) ;
ret = allow_access ( deny_list , allow_list , get_socket_name ( sock ) ,
get_socket_addr ( sock ) ) ;
}
if ( ret )
{
1998-08-10 11:29:57 +04:00
DEBUG ( 2 , ( " Allowed connection from %s (%s) \n " ,
2001-03-27 04:03:49 +04:00
only_ip ? " " : get_socket_name ( sock ) ,
get_socket_addr ( sock ) ) ) ;
}
else
{
1998-08-10 11:29:57 +04:00
DEBUG ( 0 , ( " Denied connection from %s (%s) \n " ,
2001-03-27 04:03:49 +04:00
only_ip ? " " : get_socket_name ( sock ) ,
get_socket_addr ( sock ) ) ) ;
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 ) ;
}