2012-04-17 20:09:50 +00:00
/*
* charybdis : A slightly useful ircd .
* blacklist . c : Manages DNS blacklist entries and lookups
*
* Copyright ( C ) 2006 - 2008 charybdis development team
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are
* met :
*
* 1. Redistributions of source code must retain the above copyright notice ,
* this list of conditions and the following disclaimer .
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
*
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ` ` AS IS ' ' AND ANY EXPRESS OR
* IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT ,
* INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES
* ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION )
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT ,
* STRICT LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE .
*
*/
/* To configure/use, add a block to the general{} section of your atheme.conf
* like this :
*
* blacklists {
* " dnsbl.dronebl.org " ;
* " rbl.efnetrbl.org " ;
* } ;
*/
2012-10-04 05:01:29 +00:00
# include "atheme-compat.h"
2012-04-17 20:09:50 +00:00
# include "conf.h"
DECLARE_MODULE_V1
(
" contrib/dnsbl " , false , _modinit , _moddeinit ,
PACKAGE_STRING ,
" Atheme Development Group <http://www.atheme.org> "
) ;
mowgli_list_t blacklist_list = { NULL , NULL , 0 } ;
mowgli_patricia_t * * os_set_cmdtree ;
static char * action = NULL ;
/* A configured DNSBL */
struct Blacklist {
unsigned int status ; /* If CONF_ILLEGAL, delete when no clients */
int refcount ;
char host [ IRCD_RES_HOSTLEN + 1 ] ;
unsigned int hits ;
time_t lastwarning ;
} ;
/* A lookup in progress for a particular DNSBL for a particular client */
struct BlacklistClient {
struct Blacklist * blacklist ;
user_t * u ;
dns_query_t dns_query ;
mowgli_node_t node ;
} ;
struct dnsbl_exempt_ {
char * ip ;
time_t exempt_ts ;
char * creator ;
char * reason ;
} ;
typedef struct dnsbl_exempt_ dnsbl_exempt_t ;
mowgli_list_t dnsbl_elist ;
static void os_cmd_set_dnsblaction ( sourceinfo_t * si , int parc , char * parv [ ] ) ;
static void dnsbl_hit ( user_t * u , struct Blacklist * blptr ) ;
static void os_cmd_dnsblexempt ( sourceinfo_t * si , int parc , char * parv [ ] ) ;
static void os_cmd_dnsblscan ( sourceinfo_t * si , int parc , char * parv [ ] ) ;
static void write_dnsbl_exempt_db ( database_handle_t * db ) ;
static void db_h_ble ( database_handle_t * db , const char * type ) ;
static void lookup_blacklists ( user_t * u ) ;
command_t os_set_dnsblaction = { " DNSBLACTION " , N_ ( " Changes what happens to a user when they hit a DNSBL. " ) , PRIV_USER_ADMIN , 1 , os_cmd_set_dnsblaction , { . path = " contrib/set_dnsblaction " } } ;
command_t os_dnsblexempt = { " DNSBLEXEMPT " , N_ ( " Manage the list of IP's exempt from DNSBL checking. " ) , PRIV_USER_ADMIN , 3 , os_cmd_dnsblexempt , { . path = " contrib/dnsblexempt " } } ;
command_t os_dnsblscan = { " DNSBLSCAN " , N_ ( " Manually scan if a user is in a DNSBL. " ) , PRIV_USER_ADMIN , 1 , os_cmd_dnsblscan , { . path = " contrib/dnsblscan " } } ;
static inline mowgli_list_t * dnsbl_queries ( user_t * u )
{
mowgli_list_t * l ;
return_val_if_fail ( u ! = NULL , NULL ) ;
l = privatedata_get ( u , " dnsbl:queries " ) ;
if ( l ! = NULL )
return l ;
l = mowgli_list_create ( ) ;
privatedata_set ( u , " dnsbl:queries " , l ) ;
return l ;
}
static void os_cmd_set_dnsblaction ( sourceinfo_t * si , int parc , char * parv [ ] )
{
char * act = parv [ 0 ] ;
if ( ! act )
{
command_fail ( si , fault_needmoreparams , STR_INSUFFICIENT_PARAMS , " DNSBLACTION " ) ;
command_fail ( si , fault_needmoreparams , _ ( " Syntax: SET DNSBLACTION <action> " ) ) ;
return ;
}
if ( ! strcasecmp ( " SNOOP " , act ) | | ! strcasecmp ( " KLINE " , act ) | | ! strcasecmp ( " NOTIFY " , act ) )
{
action = sstrdup ( act ) ;
command_success_nodata ( si , _ ( " DNSBLACTION successfully set to \2 %s \2 " ) , act ) ;
logcommand ( si , CMDLOG_ADMIN , " SET:DNSBLACTION: \2 %s \2 " , act ) ;
return ;
}
else if ( ! strcasecmp ( " NONE " , act ) )
{
action = NULL ;
command_success_nodata ( si , _ ( " DNSBLACTION successfully set to \2 %s \2 " ) , act ) ;
logcommand ( si , CMDLOG_ADMIN , " SET:DNSBLACTION: \2 %s \2 " , act ) ;
return ;
}
else
{
command_fail ( si , fault_badparams , _ ( " Invalid action given. " ) ) ;
return ;
}
}
static void os_cmd_dnsblexempt ( sourceinfo_t * si , int parc , char * parv [ ] )
{
char * command = parv [ 0 ] ;
char * ip = parv [ 1 ] ;
char * reason = parv [ 2 ] ;
mowgli_node_t * n , * tn ;
dnsbl_exempt_t * de ;
if ( ! command )
{
command_fail ( si , fault_needmoreparams , STR_INSUFFICIENT_PARAMS , " DNSBLEXEMPT " ) ;
command_fail ( si , fault_needmoreparams , _ ( " Syntax: DNSBLEXEMPT ADD|DEL|LIST [ip] [reason] " ) ) ;
return ;
}
if ( ! strcasecmp ( " ADD " , command ) )
{
if ( ! ip | | ! reason )
{
command_fail ( si , fault_needmoreparams , STR_INSUFFICIENT_PARAMS , " DNSBLEXEMPT " ) ;
command_fail ( si , fault_needmoreparams , _ ( " Syntax: DNSBLEXEMPT ADD <ip> <reason> " ) ) ;
return ;
}
MOWGLI_ITER_FOREACH ( n , dnsbl_elist . head )
{
de = n - > data ;
if ( ! irccasecmp ( de - > ip , ip ) )
2014-07-08 22:08:51 +00:00
{
2012-04-17 20:09:50 +00:00
command_success_nodata ( si , _ ( " \2 %s \2 has already been entered into the DNSBL exempts list. " ) , ip ) ;
return ;
}
}
de = smalloc ( sizeof ( dnsbl_exempt_t ) ) ;
de - > exempt_ts = CURRTIME ; ;
de - > creator = sstrdup ( get_source_name ( si ) ) ;
de - > reason = sstrdup ( reason ) ;
de - > ip = sstrdup ( ip ) ;
mowgli_node_add ( de , mowgli_node_create ( ) , & dnsbl_elist ) ;
command_success_nodata ( si , _ ( " You have added \2 %s \2 to the DNSBL exempts list. " ) , ip ) ;
logcommand ( si , CMDLOG_ADMIN , " DNSBL:EXEMPT:ADD: \2 %s \2 \2 %s \2 " , ip , reason ) ;
}
else if ( ! strcasecmp ( " DEL " , command ) )
{
if ( ! ip )
{
command_fail ( si , fault_needmoreparams , STR_INSUFFICIENT_PARAMS , " DNSBLEXEMPT " ) ;
command_fail ( si , fault_needmoreparams , _ ( " Syntax: DNSBLEXEMPT DEL <ip> " ) ) ;
return ;
}
MOWGLI_ITER_FOREACH_SAFE ( n , tn , dnsbl_elist . head )
{
de = n - > data ;
if ( ! irccasecmp ( de - > ip , ip ) )
{
logcommand ( si , CMDLOG_SET , " DNSBL:EXEMPT:DEL: \2 %s \2 " , de - > ip ) ;
command_success_nodata ( si , _ ( " DNSBL Exempt IP \2 %s \2 has been deleted. " ) , de - > ip ) ;
mowgli_node_delete ( n , & dnsbl_elist ) ;
free ( de - > creator ) ;
free ( de - > reason ) ;
free ( de - > ip ) ;
free ( de ) ;
return ;
}
}
command_success_nodata ( si , _ ( " IP \2 %s \2 not found in DNSBL Exempt database. " ) , ip ) ;
}
else if ( ! strcasecmp ( " LIST " , command ) )
{
char buf [ BUFSIZE ] ;
struct tm tm ;
MOWGLI_ITER_FOREACH ( n , dnsbl_elist . head )
{
de = n - > data ;
tm = * localtime ( & de - > exempt_ts ) ;
strftime ( buf , BUFSIZE , TIME_FORMAT , & tm ) ;
command_success_nodata ( si , " IP: \2 %s \2 Reason: \2 %s \2 (%s - %s) " ,
de - > ip , de - > reason , de - > creator , buf ) ;
}
command_success_nodata ( si , " End of list. " ) ;
logcommand ( si , CMDLOG_GET , " DNSBL:EXEMPT:LIST " ) ;
}
else
{
command_fail ( si , fault_needmoreparams , STR_INVALID_PARAMS , " DNSBLEXEMPT " ) ;
command_fail ( si , fault_needmoreparams , _ ( " Syntax: DNSBLEXEMPT ADD|DEL|LIST [ip] [reason] " ) ) ;
return ;
}
}
static void os_cmd_dnsblscan ( sourceinfo_t * si , int parc , char * parv [ ] )
{
char * user = parv [ 0 ] ;
user_t * u ;
if ( ! user )
{
command_fail ( si , fault_needmoreparams , STR_INSUFFICIENT_PARAMS , " DNSBLSCAN " ) ;
command_fail ( si , fault_needmoreparams , _ ( " Syntax: DNSBLSCAN <user> " ) ) ;
return ;
}
if ( ( u = user_find_named ( user ) ) )
{
lookup_blacklists ( u ) ;
logcommand ( si , CMDLOG_ADMIN , " DNSBLSCAN: %s " , user ) ;
command_success_nodata ( si , " %s has been scanned. " , user ) ;
return ;
}
else
{
command_fail ( si , fault_badparams , " User %s is not on the network, you can not scan them. " , user ) ;
return ;
}
}
/* private interfaces */
static struct Blacklist * find_blacklist ( char * name )
{
mowgli_node_t * n ;
MOWGLI_ITER_FOREACH ( n , blacklist_list . head )
{
struct Blacklist * blptr = ( struct Blacklist * ) n - > data ;
if ( ! strcasecmp ( blptr - > host , name ) )
return blptr ;
}
return NULL ;
}
static void blacklist_dns_callback ( void * vptr , dns_reply_t * reply )
{
struct BlacklistClient * blcptr = ( struct BlacklistClient * ) vptr ;
int listed = 0 ;
mowgli_list_t * l ;
if ( blcptr = = NULL )
return ;
if ( blcptr - > u = = NULL )
{
free ( blcptr ) ;
return ;
}
if ( reply ! = NULL )
{
/* only accept 127.x.y.z as a listing */
if ( reply - > addr . saddr . sa . sa_family = = AF_INET & &
! memcmp ( & ( ( struct sockaddr_in * ) & reply - > addr ) - > sin_addr , " \177 " , 1 ) )
listed + + ;
else if ( blcptr - > blacklist - > lastwarning + 3600 < CURRTIME )
{
slog ( LG_DEBUG ,
" Garbage reply from blacklist %s " ,
blcptr - > blacklist - > host ) ;
blcptr - > blacklist - > lastwarning = CURRTIME ;
}
}
/* they have a blacklist entry for this client */
if ( listed )
{
dnsbl_hit ( blcptr - > u , blcptr - > blacklist ) ;
return ;
}
l = dnsbl_queries ( blcptr - > u ) ;
mowgli_node_delete ( & blcptr - > node , l ) ;
free ( blcptr ) ;
}
/* XXX: no IPv6 implementation, not to concerned right now though. */
static void initiate_blacklist_dnsquery ( struct Blacklist * blptr , user_t * u )
{
struct BlacklistClient * blcptr = malloc ( sizeof ( struct BlacklistClient ) ) ;
char buf [ IRCD_RES_HOSTLEN + 1 ] ;
int ip [ 4 ] ;
mowgli_list_t * l ;
blcptr - > blacklist = blptr ;
blcptr - > u = u ;
blcptr - > dns_query . ptr = blcptr ;
blcptr - > dns_query . callback = blacklist_dns_callback ;
/* A sscanf worked fine for chary for many years, it'll be fine here */
2014-07-08 22:08:51 +00:00
sscanf ( u - > ip , " %d.%d.%d.%d " , & ip [ 3 ] , & ip [ 2 ] , & ip [ 1 ] , & ip [ 0 ] ) ;
2012-04-17 20:09:50 +00:00
/* becomes 2.0.0.127.torbl.ahbl.org or whatever */
snprintf ( buf , sizeof buf , " %d.%d.%d.%d.%s " , ip [ 0 ] , ip [ 1 ] , ip [ 2 ] , ip [ 3 ] , blptr - > host ) ;
gethost_byname_type ( buf , & blcptr - > dns_query , T_A ) ;
l = dnsbl_queries ( u ) ;
mowgli_node_add ( blcptr , & blcptr - > node , l ) ;
blptr - > refcount + + ;
}
/* public interfaces */
static struct Blacklist * new_blacklist ( char * name )
{
struct Blacklist * blptr ;
if ( name = = NULL )
return NULL ;
blptr = find_blacklist ( name ) ;
if ( blptr = = NULL )
{
blptr = malloc ( sizeof ( struct Blacklist ) ) ;
mowgli_node_add ( blptr , mowgli_node_create ( ) , & blacklist_list ) ;
}
mowgli_strlcpy ( blptr - > host , name , IRCD_RES_HOSTLEN + 1 ) ;
blptr - > lastwarning = 0 ;
return blptr ;
}
static void lookup_blacklists ( user_t * u )
{
mowgli_node_t * n ;
MOWGLI_ITER_FOREACH ( n , blacklist_list . head )
{
struct Blacklist * blptr = ( struct Blacklist * ) n - > data ;
blptr - > status = 0 ;
if ( u = = NULL )
return ;
initiate_blacklist_dnsquery ( blptr , u ) ;
}
}
/* This appears to be unnecessary on Atheme and only causes crashes so #if 0
* it out , at least for now . - - jdhore
*/
#if 0
static void abort_blacklist_queries ( user_t * u )
{
mowgli_node_t * n , * tn ;
mowgli_list_t * l ;
struct BlacklistClient * blcptr ;
if ( u = = NULL )
return ;
l = dnsbl_queries ( u ) ;
MOWGLI_ITER_FOREACH_SAFE ( n , tn , l - > head )
{
blcptr = n - > data ;
mowgli_node_delete ( & blcptr - > node , l ) ;
unref_blacklist ( blcptr - > blacklist ) ;
delete_resolver_queries ( & blcptr - > dns_query ) ;
free ( blcptr ) ;
}
}
# endif
static void destroy_blacklists ( void )
{
mowgli_node_t * n , * tn ;
struct Blacklist * blptr ;
MOWGLI_ITER_FOREACH_SAFE ( n , tn , blacklist_list . head )
{
blptr = n - > data ;
blptr - > hits = 0 ; /* keep it simple and consistent */
free ( n - > data ) ;
mowgli_node_delete ( n , & blacklist_list ) ;
mowgli_node_free ( n ) ;
}
}
static int dnsbl_config_handler ( mowgli_config_file_entry_t * ce )
{
mowgli_config_file_entry_t * cce ;
MOWGLI_ITER_FOREACH ( cce , ce - > entries )
{
char * line = sstrdup ( cce - > varname ) ;
new_blacklist ( line ) ;
}
return 0 ;
}
static void dnsbl_config_purge ( void * unused )
{
destroy_blacklists ( ) ;
}
static void check_dnsbls ( hook_user_nick_t * data )
{
user_t * u = data - > u ;
mowgli_node_t * n ;
if ( ! u )
return ;
if ( is_internal_client ( u ) )
return ;
if ( ! action )
return ;
MOWGLI_ITER_FOREACH ( n , dnsbl_elist . head )
{
dnsbl_exempt_t * de = n - > data ;
if ( ! irccasecmp ( de - > ip , u - > ip ) )
return ;
}
lookup_blacklists ( u ) ;
}
static void dnsbl_hit ( user_t * u , struct Blacklist * blptr )
{
service_t * svs ;
svs = service_find ( " operserv " ) ;
if ( ! strcasecmp ( " SNOOP " , action ) )
{
slog ( LG_INFO , " DNSBL: \2 %s \2 !%s@%s [%s] is listed in DNS Blacklist %s. " , u - > nick , u - > user , u - > host , u - > gecos , blptr - > host ) ;
/* abort_blacklist_queries(u); */
return ;
}
else if ( ! strcasecmp ( " NOTIFY " , action ) )
{
slog ( LG_INFO , " DNSBL: \2 %s \2 !%s@%s [%s] is listed in DNS Blacklist %s. " , u - > nick , u - > user , u - > host , u - > gecos , blptr - > host ) ;
notice ( svs - > nick , u - > nick , " Your IP address %s is listed in DNS Blacklist %s " , u - > ip , blptr - > host ) ;
/* abort_blacklist_queries(u); */
return ;
}
else if ( ! strcasecmp ( " KLINE " , action ) )
{
slog ( LG_INFO , " DNSBL: k-lining \2 %s \2 !%s@%s [%s] who is listed in DNS Blacklist %s. " , u - > nick , u - > user , u - > host , u - > gecos , blptr - > host ) ;
/* abort_blacklist_queries(u); */
notice ( svs - > nick , u - > nick , " Your IP address %s is listed in DNS Blacklist %s " , u - > ip , blptr - > host ) ;
kline_sts ( " * " , " * " , u - > host , 86400 , " Banned (DNS Blacklist) " ) ;
return ;
}
}
static void osinfo_hook ( sourceinfo_t * si )
{
mowgli_node_t * n ;
if ( action )
command_success_nodata ( si , " Action taken when a user is an a DNSBL: %s " , action ) ;
2014-07-08 22:08:51 +00:00
else
2012-04-17 20:09:50 +00:00
command_success_nodata ( si , " Action taken when a user is an a DNSBL: %s " , " None " ) ;
MOWGLI_ITER_FOREACH ( n , blacklist_list . head )
{
struct Blacklist * blptr = ( struct Blacklist * ) n - > data ;
command_success_nodata ( si , " Blacklist(s): %s " , blptr - > host ) ;
}
}
static void write_dnsbl_exempt_db ( database_handle_t * db )
{
mowgli_node_t * n ;
MOWGLI_ITER_FOREACH ( n , dnsbl_elist . head )
{
dnsbl_exempt_t * de = n - > data ;
db_start_row ( db , " BW " ) ;
db_write_word ( db , de - > ip ) ;
db_write_time ( db , de - > exempt_ts ) ;
db_write_word ( db , de - > creator ) ;
db_write_word ( db , de - > reason ) ;
db_commit_row ( db ) ;
}
}
static void db_h_ble ( database_handle_t * db , const char * type )
{
const char * ip = db_sread_word ( db ) ;
time_t exempt_ts = db_sread_time ( db ) ;
const char * creator = db_sread_word ( db ) ;
const char * reason = db_sread_word ( db ) ;
dnsbl_exempt_t * de = smalloc ( sizeof ( dnsbl_exempt_t ) ) ;
de - > ip = sstrdup ( ip ) ;
de - > exempt_ts = exempt_ts ;
de - > creator = sstrdup ( creator ) ;
de - > reason = sstrdup ( reason ) ;
mowgli_node_add ( de , mowgli_node_create ( ) , & dnsbl_elist ) ;
}
void
_modinit ( module_t * m )
{
MODULE_TRY_REQUEST_SYMBOL ( m , os_set_cmdtree , " operserv/set " , " os_set_cmdtree " ) ;
if ( ! module_find_published ( " backend/opensex " ) )
{
slog ( LG_INFO , " Module %s requires use of the OpenSEX database backend, refusing to load. " , m - > name ) ;
m - > mflags = MODTYPE_FAIL ;
return ;
}
hook_add_db_write ( write_dnsbl_exempt_db ) ;
db_register_type_handler ( " BLE " , db_h_ble ) ;
service_named_bind_command ( " operserv " , & os_dnsblexempt ) ;
service_named_bind_command ( " operserv " , & os_dnsblscan ) ;
hook_add_event ( " config_purge " ) ;
hook_add_config_purge ( dnsbl_config_purge ) ;
hook_add_event ( " user_add " ) ;
hook_add_user_add ( check_dnsbls ) ;
hook_add_event ( " operserv_info " ) ;
hook_add_operserv_info ( osinfo_hook ) ;
add_dupstr_conf_item ( " dnsbl_action " , & conf_gi_table , 0 , & action , NULL ) ;
add_conf_item ( " BLACKLISTS " , & conf_gi_table , dnsbl_config_handler ) ;
command_add ( & os_set_dnsblaction , * os_set_cmdtree ) ;
}
void
_moddeinit ( module_unload_intent_t intent )
{
hook_del_db_write ( write_dnsbl_exempt_db ) ;
hook_del_user_add ( check_dnsbls ) ;
hook_del_config_purge ( dnsbl_config_purge ) ;
hook_del_operserv_info ( osinfo_hook ) ;
db_unregister_type_handler ( " BLE " ) ;
del_conf_item ( " dnsbl_action " , & conf_gi_table ) ;
del_conf_item ( " BLACKLISTS " , & conf_gi_table ) ;
command_delete ( & os_set_dnsblaction , * os_set_cmdtree ) ;
service_named_unbind_command ( " operserv " , & os_dnsblexempt ) ;
service_named_unbind_command ( " operserv " , & os_dnsblscan ) ;
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim : ts = 8
* vim : sw = 8
* vim : noexpandtab
*/