raised default max msg size in clamdscan from 128k
added max_size on config, so it's likely to get noticed, since even 1M is probably too low for most sites. This should likely default to the same as databytes?
This commit is contained in:
parent
1731542647
commit
cc6ab49474
@ -91,9 +91,9 @@ spamassassin reject 12
|
|||||||
# dspam must run after spamassassin for the learn_from_sa feature to work
|
# dspam must run after spamassassin for the learn_from_sa feature to work
|
||||||
dspam autolearn spamassassin reject 0.95
|
dspam autolearn spamassassin reject 0.95
|
||||||
|
|
||||||
# run the clamav virus checking plugin
|
# run the clamav virus checking plugin (max size in Kb)
|
||||||
# virus/clamav
|
# virus/clamav
|
||||||
# virus/clamdscan deny_viruses yes scan_all 1
|
# virus/clamdscan deny_viruses yes max_size 1024
|
||||||
|
|
||||||
naughty reject data
|
naughty reject data
|
||||||
|
|
||||||
|
@ -64,7 +64,9 @@ https://github.com/qpsmtpd-dev/qpsmtpd-dev/wiki/DMARC-FAQ
|
|||||||
|
|
||||||
=head1 TODO
|
=head1 TODO
|
||||||
|
|
||||||
2. provide dmarc feedback to domains that request it
|
provide dmarc feedback to domains that request it
|
||||||
|
|
||||||
|
reject messages with multiple From: headers
|
||||||
|
|
||||||
=head1 AUTHORS
|
=head1 AUTHORS
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ sub hook_mail_pre {
|
|||||||
unless ($addr =~ /^<.*>$/) {
|
unless ($addr =~ /^<.*>$/) {
|
||||||
$self->log(LOGINFO, "added MAIL angle brackets");
|
$self->log(LOGINFO, "added MAIL angle brackets");
|
||||||
$addr = '<' . $addr . '>';
|
$addr = '<' . $addr . '>';
|
||||||
|
$self->adjust_karma(-1);
|
||||||
}
|
}
|
||||||
return (OK, $addr);
|
return (OK, $addr);
|
||||||
}
|
}
|
||||||
@ -35,6 +36,7 @@ sub hook_rcpt_pre {
|
|||||||
unless ($addr =~ /^<.*>$/) {
|
unless ($addr =~ /^<.*>$/) {
|
||||||
$self->log(LOGINFO, "added RCPT angle brackets");
|
$self->log(LOGINFO, "added RCPT angle brackets");
|
||||||
$addr = '<' . $addr . '>';
|
$addr = '<' . $addr . '>';
|
||||||
|
$self->adjust_karma(-1);
|
||||||
}
|
}
|
||||||
return (OK, $addr);
|
return (OK, $addr);
|
||||||
}
|
}
|
||||||
|
@ -77,6 +77,12 @@ Default: perm
|
|||||||
|
|
||||||
Adjust the quantity of logging for this plugin. See docs/logging.pod
|
Adjust the quantity of logging for this plugin. See docs/logging.pod
|
||||||
|
|
||||||
|
=head1 TODO
|
||||||
|
|
||||||
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
https://tools.ietf.org/html/rfc5322
|
||||||
|
|
||||||
=head1 AUTHOR
|
=head1 AUTHOR
|
||||||
|
|
||||||
2012 - Matt Simerson
|
2012 - Matt Simerson
|
||||||
@ -130,36 +136,59 @@ sub hook_data_post {
|
|||||||
return $self->get_reject("Headers are missing", "missing headers");
|
return $self->get_reject("Headers are missing", "missing headers");
|
||||||
};
|
};
|
||||||
|
|
||||||
return (DECLINED, "immune") if $self->is_immune();
|
return DECLINED if $self->is_immune();
|
||||||
|
|
||||||
foreach my $h (@required_headers) {
|
my $errors = $self->has_required_headers( $header );
|
||||||
next if $header->get($h);
|
$errors += $self->has_singular_headers( $header );
|
||||||
$self->adjust_karma(-1);
|
|
||||||
return $self->get_reject("We require a valid $h header",
|
|
||||||
"no $h header");
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach my $h (@singular_headers) {
|
|
||||||
next if !$header->get($h); # doesn't exist
|
|
||||||
my @qty = $header->get($h);
|
|
||||||
next if @qty == 1; # only 1 header
|
|
||||||
$self->adjust_karma(-1);
|
|
||||||
return
|
|
||||||
$self->get_reject(
|
|
||||||
"Only one $h header allowed. See RFC 5322, Section 3.6",
|
|
||||||
"too many $h headers",);
|
|
||||||
}
|
|
||||||
|
|
||||||
my $err_msg = $self->invalid_date_range();
|
my $err_msg = $self->invalid_date_range();
|
||||||
if ($err_msg) {
|
if ($err_msg) {
|
||||||
$self->adjust_karma(-1);
|
|
||||||
return $self->get_reject($err_msg, $err_msg);
|
return $self->get_reject($err_msg, $err_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( $errors ) {
|
||||||
|
return $self->get_reject($self->get_reject_type(),
|
||||||
|
"RFC 5322 validation errors" );
|
||||||
|
};
|
||||||
|
|
||||||
$self->log(LOGINFO, 'pass');
|
$self->log(LOGINFO, 'pass');
|
||||||
return (DECLINED);
|
return (DECLINED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub has_required_headers {
|
||||||
|
my ($self, $header) = @_;
|
||||||
|
|
||||||
|
my $errors;
|
||||||
|
foreach my $h (@required_headers) {
|
||||||
|
next if $header->get($h);
|
||||||
|
$errors++;
|
||||||
|
$self->adjust_karma(-1);
|
||||||
|
$self->is_naughty(1) if $self->{args}{reject};
|
||||||
|
$self->store_deferred_reject("We require a valid $h header");
|
||||||
|
$self->log(LOGINFO, "fail, no $h header" );
|
||||||
|
}
|
||||||
|
return $errors;
|
||||||
|
};
|
||||||
|
|
||||||
|
sub has_singular_headers {
|
||||||
|
my ($self, $header) = @_;
|
||||||
|
|
||||||
|
my $errors;
|
||||||
|
foreach my $h (@singular_headers) {
|
||||||
|
next if !$header->get($h); # doesn't exist
|
||||||
|
my @qty = $header->get($h);
|
||||||
|
next if @qty == 1; # only 1 header
|
||||||
|
$errors++;
|
||||||
|
$self->adjust_karma(-1);
|
||||||
|
$self->is_naughty(1) if $self->{args}{reject};
|
||||||
|
$self->store_deferred_reject(
|
||||||
|
"Only one $h header allowed. See RFC 5322, Section 3.6",
|
||||||
|
);
|
||||||
|
$self->log(LOGINFO, "fail, too many $h headers" );
|
||||||
|
}
|
||||||
|
return $errors;
|
||||||
|
};
|
||||||
|
|
||||||
sub invalid_date_range {
|
sub invalid_date_range {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
@ -175,12 +204,14 @@ sub invalid_date_range {
|
|||||||
my $past = $self->{_args}{past};
|
my $past = $self->{_args}{past};
|
||||||
if ($past && $ts < time - ($past * 24 * 3600)) {
|
if ($past && $ts < time - ($past * 24 * 3600)) {
|
||||||
$self->log(LOGINFO, "fail, date too old ($date)");
|
$self->log(LOGINFO, "fail, date too old ($date)");
|
||||||
|
$self->adjust_karma(-1);
|
||||||
return "The Date header is too far in the past";
|
return "The Date header is too far in the past";
|
||||||
}
|
}
|
||||||
|
|
||||||
my $future = $self->{_args}{future};
|
my $future = $self->{_args}{future};
|
||||||
if ($future && $ts > time + ($future * 24 * 3600)) {
|
if ($future && $ts > time + ($future * 24 * 3600)) {
|
||||||
$self->log(LOGINFO, "fail, date in future ($date)");
|
$self->log(LOGINFO, "fail, date in future ($date)");
|
||||||
|
$self->adjust_karma(-1);
|
||||||
return "The Date header is too far in the future";
|
return "The Date header is too far in the future";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,6 +203,12 @@ this prohibition applies to the matching of the parameter to its IP
|
|||||||
address only; see Section 7.9 for a more extensive discussion of
|
address only; see Section 7.9 for a more extensive discussion of
|
||||||
rejecting incoming connections or mail messages.
|
rejecting incoming connections or mail messages.
|
||||||
|
|
||||||
|
=head1 TODO
|
||||||
|
|
||||||
|
is_forged_literal, if the forged IP is an internal IP, it's likely one
|
||||||
|
of our clients that should have authenticated. Perhaps when we check back
|
||||||
|
later in data_post, if they have added relay_client, then give back the
|
||||||
|
karma.
|
||||||
|
|
||||||
=head1 AUTHOR
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
@ -244,6 +244,7 @@ sub register {
|
|||||||
|
|
||||||
#$self->prune_db(); # keep the DB compact
|
#$self->prune_db(); # keep the DB compact
|
||||||
$self->register_hook('connect', 'connect_handler');
|
$self->register_hook('connect', 'connect_handler');
|
||||||
|
$self->register_hook('mail_pre', 'from_handler');
|
||||||
$self->register_hook('rcpt_pre', 'rcpt_handler');
|
$self->register_hook('rcpt_pre', 'rcpt_handler');
|
||||||
$self->register_hook('data', 'data_handler');
|
$self->register_hook('data', 'data_handler');
|
||||||
$self->register_hook('data_post', 'data_handler');
|
$self->register_hook('data_post', 'data_handler');
|
||||||
@ -271,7 +272,7 @@ sub hook_pre_connection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
my ($penalty_start_ts, $naughty, $nice, $connects) =
|
my ($penalty_start_ts, $naughty, $nice, $connects) =
|
||||||
$self->parse_value($tied->{$key});
|
$self->parse_db_record($tied->{$key});
|
||||||
$self->calc_karma($naughty, $nice);
|
$self->calc_karma($naughty, $nice);
|
||||||
return $self->cleanup_and_return($tied, $lock);
|
return $self->cleanup_and_return($tied, $lock);
|
||||||
}
|
}
|
||||||
@ -297,7 +298,7 @@ sub connect_handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
my ($penalty_start_ts, $naughty, $nice, $connects) =
|
my ($penalty_start_ts, $naughty, $nice, $connects) =
|
||||||
$self->parse_value($tied->{$key});
|
$self->parse_db_record($tied->{$key});
|
||||||
my $summary = "$naughty naughty, $nice nice, $connects connects";
|
my $summary = "$naughty naughty, $nice nice, $connects connects";
|
||||||
my $karma = $self->calc_karma($naughty, $nice);
|
my $karma = $self->calc_karma($naughty, $nice);
|
||||||
|
|
||||||
@ -321,25 +322,47 @@ sub connect_handler {
|
|||||||
return $self->get_reject($mess, $karma);
|
return $self->get_reject($mess, $karma);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub from_handler {
|
||||||
|
my ($self, $transaction, $addr) = @_;
|
||||||
|
|
||||||
|
# test if sender has placed an illegal (RFC (2)821) space in envelope from
|
||||||
|
my $full_from = $self->connection->notes('envelope_from');
|
||||||
|
$self->illegal_envelope_format( $full_from );
|
||||||
|
|
||||||
|
return DECLINED;
|
||||||
|
};
|
||||||
|
|
||||||
sub rcpt_handler {
|
sub rcpt_handler {
|
||||||
my ($self, $transaction, $addr) = @_;
|
my ($self, $transaction, $addr) = @_;
|
||||||
|
|
||||||
|
$self->illegal_envelope_format(
|
||||||
|
$self->connection->notes('envelope_rcpt'),
|
||||||
|
);
|
||||||
|
|
||||||
|
my $count = $self->connection->notes('recipient_count') || 0;
|
||||||
|
$count++;
|
||||||
|
if ( $count > 1 ) {
|
||||||
|
$self->log(LOGINFO, "recipients c: $count ($addr)");
|
||||||
|
$self->connection->notes('recipient_count', $count);
|
||||||
|
};
|
||||||
|
|
||||||
return DECLINED if $self->is_immune();
|
return DECLINED if $self->is_immune();
|
||||||
|
|
||||||
my $recipients = scalar $self->transaction->recipients or do {
|
my $recipients = scalar $self->transaction->recipients or do {
|
||||||
$self->log(LOGDEBUG, "info, no recipient count");
|
$self->log(LOGDEBUG, "info, no recipient count");
|
||||||
return DECLINED;
|
return DECLINED;
|
||||||
};
|
};
|
||||||
|
$self->log(LOGINFO, "recipients t: $recipients ($addr)");
|
||||||
|
|
||||||
my $history = $self->connection->notes('karma_history');
|
my $history = $self->connection->notes('karma_history');
|
||||||
if ( $history > 0 ) {
|
if ( $history > 0 ) {
|
||||||
$self->log(LOGDEBUG, "info, good history");
|
$self->log(LOGINFO, "info, good history");
|
||||||
return DECLINED;
|
return DECLINED;
|
||||||
};
|
};
|
||||||
|
|
||||||
my $karma = $self->connection->notes('karma');
|
my $karma = $self->connection->notes('karma');
|
||||||
if ( $karma > 0 ) {
|
if ( $karma > 0 ) {
|
||||||
$self->log(LOGDEBUG, "info, good connection");
|
$self->log(LOGINFO, "info, good connection");
|
||||||
return DECLINED;
|
return DECLINED;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -376,7 +399,7 @@ sub disconnect_handler {
|
|||||||
my $key = $self->get_db_key();
|
my $key = $self->get_db_key();
|
||||||
|
|
||||||
my ($penalty_start_ts, $naughty, $nice, $connects) =
|
my ($penalty_start_ts, $naughty, $nice, $connects) =
|
||||||
$self->parse_value($tied->{$key});
|
$self->parse_db_record($tied->{$key});
|
||||||
my $history = ($nice || 0) - $naughty;
|
my $history = ($nice || 0) - $naughty;
|
||||||
my $log_mess = '';
|
my $log_mess = '';
|
||||||
|
|
||||||
@ -410,7 +433,17 @@ sub disconnect_handler {
|
|||||||
return $self->cleanup_and_return($tied, $lock);
|
return $self->cleanup_and_return($tied, $lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub parse_value {
|
sub illegal_envelope_format {
|
||||||
|
my ($self, $addr) = @_;
|
||||||
|
|
||||||
|
# test if envelope address has an illegal (RFC (2)821) space
|
||||||
|
if ( uc substr($addr,0,6) ne 'FROM:<' && uc substr($addr,0,4) ne 'TO:<' ) {
|
||||||
|
$self->log(LOGINFO, "illegal envelope address format: $addr" );
|
||||||
|
$self->adjust_karma(-1);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
sub parse_db_record {
|
||||||
my ($self, $value) = @_;
|
my ($self, $value) = @_;
|
||||||
|
|
||||||
my $penalty_start_ts = my $naughty = my $nice = my $connects = 0;
|
my $penalty_start_ts = my $naughty = my $nice = my $connects = 0;
|
||||||
|
@ -123,8 +123,8 @@ sub register {
|
|||||||
|
|
||||||
# Set some sensible defaults
|
# Set some sensible defaults
|
||||||
$self->{'_args'}{'deny_viruses'} ||= 'yes';
|
$self->{'_args'}{'deny_viruses'} ||= 'yes';
|
||||||
$self->{'_args'}{'max_size'} ||= 128;
|
$self->{'_args'}{'max_size'} ||= 1024;
|
||||||
$self->{'_args'}{'scan_all'} ||= 0;
|
$self->{'_args'}{'scan_all'} ||= 1;
|
||||||
for my $setting ('deny_viruses', 'defer_on_error') {
|
for my $setting ('deny_viruses', 'defer_on_error') {
|
||||||
next unless $self->{'_args'}{$setting};
|
next unless $self->{'_args'}{$setting};
|
||||||
if (lc $self->{'_args'}{$setting} eq 'no') {
|
if (lc $self->{'_args'}{$setting} eq 'no') {
|
||||||
|
Loading…
Reference in New Issue
Block a user