⚝
One Hat Cyber Team
⚝
Your IP:
216.73.216.19
Server IP:
178.33.27.10
Server:
Linux cpanel.dev-unit.com 3.10.0-1160.108.1.el7.x86_64 #1 SMP Thu Jan 25 16:17:31 UTC 2024 x86_64
Server Software:
Apache/2.4.57 (Unix) OpenSSL/1.0.2k-fips
PHP Version:
8.2.11
Buat File
|
Buat Folder
Eksekusi
Dir :
~
/
lib64
/
perl5
/
vendor_perl
/
Razor2
/
Client
/
View File Name :
Agent.pm
#!/usr/bin/perl -sw ## ## Razor2::Client::Agent -- UI routines for razor agents. ## ## Copyright (c) 2002, Vipul Ved Prakash. All rights reserved. ## This code is free software; you can redistribute it and/or modify ## it under the same terms as Perl itself. ## ## $Id: Agent.pm,v 1.98 2006/10/18 06:15:08 rsoderberg Exp $ package Razor2::Client::Agent; use strict; use Getopt::Long; use IO::File; use Razor2::String qw(fisher_yates_shuffle); use base qw(Razor2::Client::Core); use base qw(Razor2::Client::Config); use base qw(Razor2::Logger); use base qw(Razor2::String); use Razor2::Preproc::Manager; use Data::Dumper; our $PROTOCOL = $Razor2::Client::Version::PROTOCOL; our $VERSION = $Razor2::Client::Version::VERSION; sub new { my ( $class, $breed ) = @_; # For Taint Friendliness delete $ENV{PATH}; delete $ENV{BASH_ENV}; my @valid_program_names = qw( razor-check razor-report razor-revoke razor-admin ); my $ok = 0; foreach (@valid_program_names) { $breed =~ /$_$/ and $ok = $_; } unless ($ok) { if ( $breed =~ /razor-client$/ ) { # We no longer create symlinks, but for backwards compatibility # return success. exit 0; } die "Invalid program name, must be one of: @valid_program_names\n"; } $ok =~ /razor-(.*)$/; my %me = ( name_version => "Razor-Agents v$VERSION", # used in register breed => $1, preproc => new Razor2::Preproc::Manager( no_deHTMLcomment => 1 ), preproc_vr8 => new Razor2::Preproc::Manager( no_deHTML => 1 ), global_razorhome => '/etc/razor', ); return bless \%me, $class; } sub do_conf { my $self = shift; # parse config-related cmd-line args # # identity is parsed later after razorhome is fully resolved if ( $self->{opt}->{config} ) { if ( $self->{opt}->{create_conf} ) { $self->{razorconf} = $self->{opt}->{config}; } elsif ( -r $self->{opt}->{config} ) { $self->{razorconf} = $self->{opt}->{config}; } else { return $self->error("Can't read conf file: $self->{opt}->{config}"); } } if ( $self->{opt}->{razorhome} ) { if ( -d $self->{opt}->{razorhome} ) { $self->{razorhome} = $self->{opt}->{razorhome}; } else { return $self->error("Can't read: $self->{opt}->{razorhome}") unless $self->{opt}->{create_conf}; } # once razorhome is successfully overridden, override the global razorhome as well. $self->{global_razorhome} = $self->{razorhome}; } return unless $self->read_conf(); if ( $self->{opt}->{create_conf} ) { $self->{force_discovery} = 1; $self->{force_bootstrap_discovery} = 1; $self->log( 8, " -create will force complete discovery" ); } if ( $self->{opt}->{force_discovery} ) { $self->{force_discovery} = 1; $self->{force_bootstrap_discovery} = 1; $self->log( 8, " -discover will force complete discovery" ); } if ( $self->{opt}->{debug} && !$self->{opt}->{debuglevel} ) { $self->{conf}->{debuglevel} ||= 9; $self->{conf}->{debuglevel} = 9 if $self->{conf}->{debuglevel} < 9; } # # Note: we start logging before we process '-create' , # so logfile will not go into a newly created razorhome # #my $logto = $self->{opt}->{debug} ? "stdout" : "file:$self->{conf}->{logfile}"; my $logto; if ( $self->{opt}->{debug} ) { $logto = 'stdout'; } elsif ( $self->{conf}->{logfile} eq 'syslog' ) { $logto = 'syslog'; } elsif ( $self->{conf}->{logfile} eq 'sys-syslog' ) { $logto = 'sys-syslog'; } elsif ( $self->{conf}->{logfile} eq 'none' ) { $logto = 'none'; } else { $logto = "file:$self->{conf}->{logfile}"; } if ( exists $self->{conf}->{logfile} ) { my $debuglevel = exists $self->{conf}->{debuglevel} ? $self->{conf}->{debuglevel} : 9; my $logger = new Razor2::Logger( LogDebugLevel => $debuglevel, LogTo => $logto, LogPrefix => $self->{breed}, LogTimestamp => 1, DontDie => 1, Log2FileDir => defined( $self->{conf}->{tmp_dir} ) ? $self->{conf}->{tmp_dir} : "/tmp", ); $self->{logref} = ref($logger) ? $logger : 0; # log error strings at loglevel 11. Pick a high number 'cuz # if its really an error, it will be in errstr for caller $self->{logerrors} = 11; } $self->logobj( 15, "cmd-line options", $self->{opt} ); $self->{preproc}->{rm}->{log} = $self->{logref}; # creates razorhome, and sets $self->{razorhome} if successful return $self->errprefix("Could not create 'razorhome'") unless $self->create_home_conf(); $self->compute_identity; $self->log( 5, "computed razorhome=$self->{razorhome}, conf=$self->{razorconf}, ident=$self->{identity}" ); return 1; } # if a debug log statement requires extra work, check this call before doing it. sub logll { my ( $self, $loglevel ) = @_; return unless $self->{logref}; return 1 if ( $self->{logref}->{LogDebugLevel} >= $loglevel ); return; } sub create_home_conf { my $self = shift; unless ( $self->{opt}->{create_conf} ) { # # if the global razorhome exists, don't create anything # without '-create' option # return 1 if ( -d $self->{global_razorhome} ); # # if there is not global razorhome, # try to create razorhome one anyway. # if it fails, thats ok. # $self->create_home( $self->{razorhome_computed} ); $self->errstrrst; # nuke error string return 1; } # # user passed in 'create' option, so create. # my $rhome = $self->{opt}->{razorhome} ? $self->{opt}->{razorhome} : $self->{razorhome_computed}; if ($rhome) { if ( -d $rhome ) { $self->log( 6, "Not creating razorhome $rhome, already exists" ); } else { return unless $self->create_home($rhome); } } if ( $self->{opt}->{config} ) { # if create and conf specified, exit if write is not successful # $self->{razorconf} = $self->{opt}->{config}; return $self->write_conf(); } else { # else just try and create, if fail ok. # $self->compute_razorconf(); $self->{razorconf} ||= $self->{computed_razorconf}; $self->write_conf(); $self->errstrrst; # nuke error string } return 1; } # wrapper for log sub log { my $self = shift; my $level = shift; my $msg = shift; if ( $self->{logref} ) { return $self->{logref}->log( $level, $msg ); } elsif ( $self->{opt}->{debug} ) { print " Razor-Log: $msg\n" if $self->{opt}->{debug}; } } sub log2file { my $self = shift; return unless $self->{logref}; return $self->{logref}->log2file(@_); } sub doit { my $self = shift; my $args = shift; my $r; $self->log( 2, " $self->{name_version} starting razor-$self->{breed} $self->{args}" ); # $self->log(9,"uname -a: ". `uname -a`) if $self->logll(9); $r = $self->checkit($args) if $self->{breed} eq 'check'; $r = $self->adminit($args) if $self->{breed} eq 'admin'; $r = $self->reportit($args) if $self->{breed} eq 'report'; $r = $self->reportit($args) if $self->{breed} eq 'revoke'; # return exit code # 0, 1 => ok # > 1 => error (caller should prolly print $self->errstr) # if ( $r > 1 ) { my $msg = $self->errstr; $self->log( 1, "razor-$self->{breed} error: " . $msg ); } else { $self->log( 8, "razor-$self->{breed} finished successfully." ); } return $r; } sub _help { my ( $self, $breed ) = @_; chomp( my $all = <<EOFALL); -h Print this usage message. -v Print version number and exit -d Turn on debugging. Logs to stdout. -s Simulate Only. Does not connect to server. -conf=file Use this config file instead of <razorhome>/razor.conf -home=dir Use this as razorhome -ident=file Use this identity file instead of <razorhome>/identity -rs Use this razor server instead of reading .lst EOFALL chomp( my $sigs = <<EOFSIGS); -H Compute and print signature. -S | --sig Accept a signatures to check on the command line -e eng Engine used to compute sig, integer -ep4 val String value required when engine == 4 EOFSIGS chomp( my $mbox = <<EOFMBOX); -M | --mbox Accept a mailbox name on the command line (default) If no filename, mbox, or signatures, input read from stdin. EOFMBOX my %b; $b{check} = <<EOFCHECK; razor-check [options] [ filename | -M mbox | -S signatures | < filename ] $all $sigs $mbox See razor-check(1) manpage for details. EOFCHECK $b{report} = <<EOFREPORT; razor-report [options] [ filename | -M mbox | -S signatures -e engine] $all $sigs $mbox -i file Use identity from this file -f Stay in foreground. -a Authenticate only. Exit 0 if authenticated, 1 if not Stays in foreground. See razor-report(1) manpage for details. EOFREPORT $b{admin} = <<EOFREGISTER; razor-admin [options] [ -register | -create | -discover ] $all -create Create razorhome, does discover, does not register -discover Discover Razor servers: write .lst files -register Register a new identity -user name Request 'name' when registering (requires -register) -pass pass Request 'password' when registering (requires -register) -l Make new identity the the default identity. Used only when registering. See razor-admin(1) manpage for details. EOFREGISTER $b{revoke} = <<EOFREVOKE; razor-revoke [options] filename $all $mbox -i file Use identity from this file -f Stay in foreground. -a Authenticate only. exit 0 if authenticated, 1 if not Stays in foreground. See razor-revoke(1) manpage for details. EOFREVOKE my $future = <<EOFFUTURE; EOFFUTURE return $b{ $self->{breed} }; } # maybe this should be in Client::Config # sub read_options { my ( $self, $agent ) = @_; $self->{args} = join ' ', @ARGV; Getopt::Long::Configure("no_ignore_case"); my %opt; # # These options override what is loaded in config file # the names on the right should match keys in config file # my $ret = GetOptions( 's' => \$opt{simulate}, 'd' => \$opt{debug}, 'verbose' => \$opt{debug}, 'v' => \$opt{version}, 'h' => \$opt{usage}, 'help' => \$opt{usage}, 'H' => \$opt{printhash}, 'C=s' => \$opt{printcleaned}, 'sig=s' => \$opt{sig}, 'S=s' => \$opt{sig}, 'e=s' => \$opt{sigengine}, 'ep4=s' => \$opt{sigep4}, 'mbox' => \$opt{mbox}, 'M' => \$opt{mbox}, 'n' => \$opt{negative}, 'conf=s' => \$opt{config}, 'config=s' => \$opt{config}, 'home=s' => \$opt{razorhome}, 'f' => \$opt{foreground}, 'noml' => \$opt{noml}, 'user=s' => \$opt{user}, 'u=s' => \$opt{user}, 'pass=s' => \$opt{pass}, 'a' => \$opt{authen_only}, 'rs=s' => \$opt{server}, 'server=s' => \$opt{server}, 'r' => \$opt{register}, 'register' => \$opt{register}, 'l' => \$opt{symlink}, 'i=s' => \$opt{identity}, 'ident=s' => \$opt{identity}, 'create' => \$opt{create_conf}, 'logfile=s' => \$opt{logfile}, 'discover' => \$opt{force_discovery}, 'dl=s' => \$opt{debuglevel}, 'debuglevel=s' => \$opt{debuglevel}, 'whitelist=s' => \$opt{whitelist}, 'lm=s' => \$opt{logic_method}, 'le=s' => \$opt{logic_engines}, ); if ( $ret == 0 ) { $self->error("failed to parse command line options.\n"); return; } # remove elements not set in the cmd-line foreach ( keys %opt ) { delete $opt{$_} unless defined $opt{$_}; } if ( $opt{usage} ) { $self->error( $self->_help ); return; } elsif ( $opt{mbox} && $opt{sig} ) { $self->error("--mbox and --sig are mutually exclusive.\n"); return; } elsif ( $opt{sig} && !$opt{sigengine} ) { $self->error("--sig requires -e (engine used to generate sig)\n"); return; # # fixme - require ep4 if -e 4 is used ? # } elsif ( $opt{version} ) { $self->error("Razor Agents $VERSION, protocol version $PROTOCOL"); return; } $self->{opt} = \%opt; return 1; } # returns 0 if match (spam) # returns 1 if no match (legit) # returns 2 if error sub checkit { my $self = shift; my $args = shift; # check for spam. # input can be one of # file - single mail # mbox - many mail # sig - 1 or more sigs # or a filehandle provided via args my $objects; if ( $self->{conf}->{sig} ) { my @sigs; # # cmd-line sigs # # prepare 1 mail object per sig # foreach my $sig ( split ',', $self->{conf}->{sig} ) { $sig =~ s/^\s*//; $sig =~ s/\s*$//; my $hr = { eng => $self->{conf}->{sigengine}, sig => $sig, }; $hr->{ep4} = "7542-10"; $hr->{ep4} = $self->{conf}->{sigep4} if $self->{conf}->{sigep4}; push @sigs, $hr; } $self->log( 5, "received " . ( scalar @sigs ) . " valid cmd-line sigs." ); $objects = $self->prepare_objects( \@sigs ) or return 2; } else { my $mails = $self->parse_mbox($args) or return 2; $objects = $self->prepare_objects($mails) or return 2; # # if mail is whitelisted, its not spam. # flag it so it we don't check it against server # foreach my $obj (@$objects) { if ( $self->local_check($obj) ) { $obj->{skipme} = 1; $obj->{spam} = 0; } else { next; } } } # compute_sigs needs server info like ep4, so get_server_info first $self->get_server_info() or return 2; my $printable_sigs = $self->compute_sigs($objects) or return 2; if ( $self->{opt}->{printhash} ) { my $i = 0; foreach (@$printable_sigs) { if ( $self->{opt}->{sigengine} ) { next unless (/ e$self->{opt}->{sigengine}: /); } print "$_\n"; $i++; } $self->log( 4, "Done. Printed $i sig(s) for " . scalar(@$objects) . " mail(s)" ); } if ( $self->{opt}->{printcleaned} ) { my $totalp = 0; my $totalc = 0; foreach my $obj (@$objects) { my $n = 0; mkdir("$self->{opt}->{printcleaned}/cleaned"); foreach ( $obj->{headers}, @{ $obj->{bodyparts_cleaned} } ) { my $fn = "$self->{opt}->{printcleaned}/cleaned/mail$obj->{id}." . $n++; $self->write_file( $fn, $_ ); $totalc++; } $n = 0; mkdir("$self->{opt}->{printcleaned}/uncleaned"); foreach ( $obj->{headers}, @{ $obj->{bodyparts} } ) { my $fn = "$self->{opt}->{printcleaned}/uncleaned/mail$obj->{id}." . $n++; $self->write_file( $fn, $_ ); $totalp++; } } $self->log( 4, "Done. $totalp uncleaned, $totalc cleaned mails saved in $self->{opt}->{printcleaned}" ); print "Done. $totalp uncleaned, $totalc cleaned mails saved in $self->{opt}->{printcleaned}\n"; return 1; } return 1 if $self->{opt}->{printhash}; # only check good objects my @goodones; # this should be optimized! foreach my $obj (@$objects) { next if $obj->{skipme}; push @goodones, $obj; } unless ( scalar @goodones ) { $self->log( 4, "Done. No valid mail or signatures to check." ); return 1; } if ( $self->{conf}->{simulate} ) { $self->log( 4, "Done. (simulate only)" ); return 1; } # # Connect to catalogue server # $self->{s}->{list} = $self->{s}->{catalogue}; $self->nextserver(); $self->connect() or return 2; # # Check against server # $self->check( \@goodones ) or return 2; $self->disconnect() or return 2; # # print out responses and exit # my $only1check = ( scalar(@$objects) == 1 ) ? 1 : 0; my $has_spam = 0; foreach my $obj (@$objects) { $obj->{spam} = 0 if $obj->{skipme}; $obj->{spam} = 0 unless defined $obj->{spam}; if ( $obj->{spam} > 0 ) { return 0 if $only1check; $has_spam = 1; print $obj->{id} . "\n"; next; } elsif ( $obj->{spam} == 0 ) { return 1 if $only1check; print "-" . $obj->{id} . "\n" if $self->{conf}->{negative}; next; } else { # error # $self->logobj( 1, "bad 'spam' in checkit", $obj ); return 2 if $only1check; print "-" . $obj->{id} . "\n" if $self->{conf}->{negative}; next; } } return 0 if $has_spam; return 1; } # returns 0 if success # returns 2 if error sub adminit { my $self = shift; my $done_something = 0; if ( $self->{opt}->{create_conf} ) { $done_something++; # $self->create_home_conf() is always checked } if ( $self->{opt}->{force_discovery} || $self->{opt}->{create_conf} ) { $done_something++; # get_server_info() calls nextserver() which calls discovery() $self->get_server_info() or return 2; } if ( $self->{opt}->{register} ) { $done_something++; my $r = $self->registerit(); return $r if $r; } unless ($done_something) { $self->error("An option needs to be specified, -h for help."); return 2; } return 0; } # returns 0 if success # returns 2 if error sub registerit { my ( $self, $auto ) = @_; unless ( $self->{razorhome} || $self->{opt}->{identity} ) { $self->errprefix("Unable to register without a valid razorhome or identity"); return 2; } my $ident; if ( exists $self->{opt}->{user} && ( $ident = $self->get_ident ) && $ident->{user} eq $self->{opt}->{user} ) { $self->error("You are already registered as user=$ident->{user} in $self->{razorhome}"); return 2; } if ( $self->{conf}->{simulate} ) { $self->log( 5, "Done - simulate only." ); return 0; } if ( $self->{opt}->{create_conf} ) { $self->log( 3, "Register create successful." ); return 0; } if ($auto) { $self->log( 3, "Write test underway" ); my ($ident) = { user => 'writetest', pass => 'writetest', }; my ($fn); unless ( $fn = $self->save_ident($ident) ) { $self->log( 3, "Unable to write identity to home" ); return 2; } unlink($fn) or return 2; $self->log( 3, "Write test completed" ); } $self->get_server_info() or return 2; $self->connect() or return 2; $self->log( 3, "Attempting to register." ); # attempt to register the user/pass $ident = $self->register_identity( $self->{opt}->{user}, $self->{opt}->{pass} ); $self->disconnect() or return 2; unless ( ref $ident ) { $self->log( 3, "Failed to register identity." ); return 2; } if ( my $fn = $self->save_ident($ident) ) { my $msg = "Register successful. Identity stored in $fn"; $self->log( 3, $msg ); print "$msg\n"; return 0; } else { $self->log( 3, "Register failed." ); return 2; } } # # handles report and revoke # # returns 0 if success # returns 2 if error sub reportit { my ( $self, $args ) = @_; my $ident = $self->get_ident; unless ($ident) { $self->log( 3, "Razor2 identity not found. Attempting to register automatically." ); if ( $self->registerit("auto") ) { $self->log( 3, "Automatic registration failed." ); $self->errprefix( "Bootstrap Error: Your Razor2 identity was not found.\n " . " If you haven't registered, please do so:\n" . " \"razor-admin -register -user=[name/email_address] -pass=[password]\".\n" . " (Further information can be found in the razor-admin(1) manpage)\n" . " If you did register, please ensure your identity symlink (or file) is in order.\n" ); return 2; } $ident = $self->get_ident; unless ($ident) { $self->log( 3, "Unable to load automatically registered identity." ); $self->errprefix( "Bootstrap Error: Your Razor2 identity was not found.\n " . " If you haven't registered, please do so:\n" . " \"razor-admin -register -user=[name/email_address] -pass=[password]\".\n" . " (Further information can be found in the razor-admin(1) manpage)\n" . " If you did register, please ensure your identity symlink (or file) is in order.\n" ); return 2; } } if ( !$self->{opt}{foreground} && ( @ARGV < 1 || $ARGV[0] eq "-" || $ARGV[0] eq "" ) ) { if ( -t STDIN ) { $self->error( "Unable to read from a TTY using STDIN while forked. \n" . "Doing so leads to undefined behaviour in certain shells." ); return 2; } } # background myself unless ( $self->{opt}->{foreground} ) { chdir '/'; fork && return 0; POSIX::setsid; # close 0, 1, 2; } if ( $self->{opt}->{authen_only} ) { $self->authenticate($ident) or return; $self->log( 5, "Done - authenticate only." ); return 0 if $self->{authenticated}; return 2; } my $mails = $self->parse_mbox($args) or return 2; my $objects = $self->prepare_objects($mails) or return 2; # compute_sigs needs server info like ep4, so get_server_info first $self->get_server_info() or return 2; my $printable_sigs = $self->compute_sigs($objects) or return 2; if ( $self->{opt}->{printhash} ) { foreach (@$printable_sigs) { if ( $self->{opt}->{sigengine} ) { next unless (/ e$self->{opt}->{sigengine}: /); } print "$_\n"; } exit 0; } if ( $self->{conf}->{simulate} ) { $self->log( 4, "Done. (simulate only)" ); exit 0; } unless ( scalar @$objects ) { $self->log( 4, "Done. No valid mail or signatures to check." ); exit 1; } $self->{s}->{list} = $self->{s}->{nomination}; $self->nextserver(); $self->connect() or return 2; $self->authenticate($ident) or return 2; $self->report($objects) or return 2; $self->disconnect() or return 2; if ( $self->{opt}->{foreground} ) { foreach my $obj (@$objects) { # my $line = debugobj($obj->{r}); # $line =~ /(\S+=\S+)/s; # could be res=0|1, err=xxx # print "$obj->{id}: $1\n"; #print "$obj->{id}\n" if $obj->{r}->{res} == '1'; } } return 0; } sub parse_mbox { my ( $self, $args ) = @_; my @mails; my @message; my $passed_fh = 0; my $aref; # There are different kinds of mbox formats, we just split on simplest case. # djb defines mbox, mboxrd, mboxcl, mboxcl2 # http://www.qmail.org/qmail-manual-html/man5/mbox.html # # non-mbox support added, thanx to Aaron Hopkins <aaron@die.net> if ( exists $$args{"fh"} ) { @ARGV = (); push @ARGV, $$args{'fh'}; $passed_fh = 1; } elsif ( exists $$args{"aref"} ) { $aref = $$args{"aref"}; } elsif ( !scalar @ARGV ) { push @ARGV, "-"; } if ( $$args{'aref'} ) { my @foo = ( \join '', @{ $$args{'aref'} } ); return \@foo; } foreach my $file (@ARGV) { my $fh = new IO::File; my @message = (); if ( ref $file ) { $fh = $file; } else { open $fh, "<$file" or return $self->error("Can't open $file: $!"); } my $line = <$fh>; next unless $line; if ( $line =~ /^From / ) { $self->log( 8, "reading mbox formatted mail from " . ( $file eq '-' ? "<stdin>" : $file ) ); while (1) { push @message, $line; $line = <$fh>; if ( !defined($line) || $line =~ /^From / ) { push @mails, \join( '', @message ); @message = (); last unless defined $line; } } } else { $self->log( 8, "reading straight RFC822 mail from " . ( $file eq '-' ? "<stdin>" : $file ) ); push @mails, \join( '', map { s/^(>*From )/>$1/; $_ } $line, <$fh> ); } close $fh unless $passed_fh; } my $cnt = scalar @mails; $self->log( 6, "read $cnt mail" . ( $cnt > 1 ? 's' : '' ) ); return \@mails; } sub raise_error { my ( $self, $errstr ) = @_; my $str; if ( ref $self ) { $str = $self->errstr; } $str = $errstr if $errstr; my ($code) = $str =~ /Razor Error (\d+):/; $code = 255 unless $code; print "FATAL: $str"; exit $code; } # returns 1 if mail should be skipped # sub local_check { my ( $self, $obj ) = @_; my ( $headers, $body ) = split /\n\r*\n/, ${ $obj->{orig_mail} }, 2; $headers =~ s/\n\s+//sg; # merge multi-line headers if ( $self->{conf}->{ignorelist} ) { if ( $headers =~ /\n((X-)?List-Id[^\n]+)/i ) { my $listid = $1; my ($line1) = substr( ${ $obj->{orig_mail} }, 0, 50 ) =~ /^([^\n]+)/; $self->log( 5, "Mailing List post; mail " . $obj->{id} . " not spam." ); #$self->log (5,"Mailing List post; mail ". $obj->{id} ." not spam.\n $line1\n $listid"); return 1; } } return 0 if $self->{no_whitelist}; if ( -s $self->{conf}->{whitelist} ) { $self->read_whitelist; foreach my $sh ( keys %{ $self->{whitelist} } ) { if ( $sh ne 'sha1' ) { while ( $headers =~ /^$sh:\s+(.*)$/img ) { last unless $1; my $fc = $1; $self->log( 13, "whitelist checking headers for match $sh: $fc" ); foreach my $address ( @{ $self->{whitelist}->{$sh} } ) { if ( $fc =~ /$address/i ) { $self->log( 3, "ignoring mail $obj->{id}, whitelisted by rule: $sh: $address" ); return 1; } } } } } $self->log( 12, "Whitelist rules did not match mail $obj->{id}" ); } elsif ( $self->{conf}->{whitelist} ) { $self->log( 6, "skipping whitelist file (empty?): $self->{conf}->{whitelist}" ); $self->{no_whitelist} = 1; } return 0; } sub read_whitelist { my ($self) = @_; return if $self->{whitelist}; my %whitelist; my $lines = $self->read_file( $self->{conf}->{whitelist}, 0, 1 ); for (@$lines) { s/^\s*//; next if /^#/; chomp; my ( $type, $value ) = split /\s+/, $_, 2; $type =~ y/A-Z/a-z/ if $type; push @{ $whitelist{$type} }, $value if ( $type && $value ); } $self->{whitelist} = \%whitelist; $self->log( 8, "loaded " . scalar( keys %whitelist ) . " different types of whitelist" ); #$self->logobj (15,"loaded whitelist:", \%whitelist); return 1; } sub logerr { my ( $self, $msg ) = @_; $msg = $self->errstr unless $msg; $self->log( 1, "$self->{breed} error: " . $msg ); return; } # see nextserver() for explanation of how data is stored # sub get_server_info { my $self = shift; unless ( exists $self->{s} ) { $self->{s} = {}; } if ( $self->{opt}->{server} ) { # cmd-line $self->{s}->{list} = [ $self->{opt}->{server} ]; $self->log( 8, "Using cmd-line server ($self->{opt}->{server}), skipping .lst files" ); } else { $self->readservers; } $self->loadservercache; #$self->logobj(6,"find_closest_server server info (before nextserver)", $self->{s}); $self->{loaded_servers} = 1; return $self->nextserver; # this will connect and get state info if not cached } # see nextserver() for explanation of how data is stored # sub readservers { my $self = shift; unless ( exists $self->{s} ) { $self->{s} = {}; } # read .lst files foreach my $lf (qw(discovery nomination catalogue)) { my $h = $self->read_file( $self->{conf}->{"listfile_$lf"}, 0, 1 ) or next; $self->{s}->{$lf} = []; foreach (@$h) { push @{ $self->{s}->{$lf} }, $1 if /^(([^\.\s]+\.)+[^\.\s]+(:\S+)?)/; } if ( defined( $self->{s}->{$lf} ) && ref( $self->{s}->{$lf} ) ) { $self->log( 11, "Read " . scalar( @{ $self->{s}->{$lf} } ) . " from server listfile: " . $self->{conf}->{"listfile_$lf"} ); } } foreach my $lf (qw(discovery nomination catalogue)) { next unless defined( $self->{s}->{$lf} ); next unless ref( $self->{s}->{$lf} ); next unless @{ $self->{s}->{$lf} } > 1; fisher_yates_shuffle( $self->{s}->{$lf} ); } if ( $self->{breed} =~ /^check/ ) { $self->{s}->{list} = $self->{s}->{catalogue}; $self->{s}->{listfile} = $self->{conf}->{listfile_catalogue}; # for discovery() } else { $self->{s}->{list} = $self->{s}->{nomination}; $self->{s}->{listfile} = $self->{conf}->{listfile_nomination}; # for discovery() } } sub loadservercache { my $self = shift; # # Read in server-specific config, using defaults for stuff not found # # NOTE: this reads all server.*.conf files in razor home, not just those in .lst # # load defaults for .lst servers foreach (qw(nomination catalogue)) { next unless $self->{s}->{$_}; foreach my $server ( @{ $self->{s}->{$_} } ) { next if $self->{s}->{allconfs}->{$server}; # avoid repeats $self->{s}->{allconfs}->{$server} = $self->default_server_conf(); $self->log( 9, "Assigning defaults to $server" ); } } my @fns; my $sep = '\.'; $sep = '_' if $^O eq 'VMS'; if ( opendir D, $self->{razorhome} ) { @fns = map { s/_/./g; "$self->{razorhome}/$_"; } grep /^server$sep[\S]+\.conf$/, readdir D; @fns = map { /^(\S+)$/, $1 } @fns; # untaint closedir D; } foreach (@fns) { /server\.(.+)\.conf$/ and my $sn = $1; next unless $sn; $self->{s}->{allconfs}->{$sn} = $self->read_file( $_, $self->{s}->{allconfs}->{$sn} ); if ( $self->{s}->{allconfs}->{$sn} ) { #$self->log(8,"Loaded server specific conf info for $sn"); } else { $self->log( 5, "loadservercache skipping $_" ); } } return $self; } sub writeservers { my $self = shift; unless ( $self->{razorhome} ) { $self->log( 5, "no razorhome, not caching server info to disk" ); return; } foreach ( @{ $self->{s}->{modified_lst} } ) { my $fn = $self->{conf}->{"listfile_$_"}; $self->write_file( $fn, $self->{s}->{$_}, 0, 0, 1 ) || $self->log( 5, "writeservers skipping .lst file: $fn" ); } $self->log( 11, "No bootstrap_discovery (DNS) recently, not recording .lst files" ) unless scalar( @{ $self->{s}->{modified_lst} } ); $self->{s}->{modified_lst} = []; foreach ( @{ $self->{s}->{modified} } ) { my $fn = "$self->{razorhome}/server.$_.conf"; my $header = "#\n# Autogenerated by $self->{name_version}, " . localtime() . "\n"; $self->write_file( $fn, $self->{s}->{allconfs}->{$_}, 0, $header ) || $self->debug("writeservers skipping $fn"); } $self->{s}->{modified} = []; $self->errstrrst; # nuke error string if write errors return $self; } 1;