Compare commits

...

4 Commits

Author SHA1 Message Date
e1260c8a5f * Sat Oct 19 2024 Jean-Philippe Pialasse <jpp@koozali.org> 1.0.0-2.sme
- remove auth_imap patch not needed [SME: 11802]
2024-10-21 23:12:39 -04:00
665ac0774f * Sun Apr 28 2024 Jean-Philippe Pialasse <jpp@koozali.org> 1.0.0-1.sme
- upgrade to last version [SME: 11802]
- reapply our specific patches, rewrite them if necessary
  added qpsmtpd-0.96-bz12450-auth_imap-perport.patch from SME10
- apply last fixes in git since v 1.0.0
  postfix: avoid logging full headers;Load plugins in qpsmtpd-forkserver;
  Fix received_line hook behaviour; Add missing use statement for NetAddr::IP
2024-04-28 23:47:27 -04:00
e908e9a691 * Sun Apr 28 2024 Jean-Philippe Pialasse <jpp@koozali.org> 1.0.0-1.sme
- upgrade to last version [SME: 11802]
- reapply our specific patches, rewrite them if necessary
  added qpsmtpd-0.96-bz12450-auth_imap-perport.patch from SME10
- apply last fixes in git since v 1.0.0
  postfix: avoid logging full headers;Load plugins in qpsmtpd-forkserver;
  Fix received_line hook behaviour; Add missing use statement for NetAddr::IP
2024-04-28 23:46:26 -04:00
0e6c3d3925 Remove LICENSE file 2024-04-04 14:26:25 +01:00
24 changed files with 111 additions and 664 deletions

73
LICENSE
View File

@ -1,73 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
(a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,56 +0,0 @@
diff -Nur -x '*.orig' -x '*.rej' qpsmtpd-0.95/plugins/tls mezzanine_patched_qpsmtpd-0.95/plugins/tls
--- qpsmtpd-0.95/plugins/tls 2016-01-07 19:14:45.099736554 +0100
+++ mezzanine_patched_qpsmtpd-0.95/plugins/tls 2016-01-07 19:14:30.152736181 +0100
@@ -62,6 +62,14 @@
and put a suitable string in config/tls_ciphers (e.g. "DEFAULT" or
"HIGH:MEDIUM")
+=head1 SSL/TLS protocols versions
+
+By default, SSLv2 and SSLv3 are not accepted, leaving only TLSv1,
+TLSv1.1 or TLSv1.2 enabled. You can customize this in config/tls_protocols
+For example, this will also disabled TLSv1, leaving only TLSv1.1 and TLSv1.2
+
+SSLv23:!SSLv2:!SSLv3:!TLSv1
+
=cut
use strict;
@@ -94,6 +102,7 @@
$self->tls_ca($ca);
$self->tls_dhparam($dhparam);
$self->tls_ciphers($self->qp->config('tls_ciphers') || 'HIGH');
+ $self->tls_protocols($self->qp->config('tls_protocols') || 'SSLv23:!SSLv2:!SSLv3');
$self->log(LOGDEBUG, "ciphers: " . $self->tls_ciphers);
@@ -102,7 +111,7 @@
IO::Socket::SSL::SSL_Context->new(
# Disable SSLv2 and SSLv3 to avoid POODLE attacks. This is already
# the default in sufficiently recent versions of IO::Socket::SSL
- SSL_version => 'SSLv23:!SSLv3:!SSLv2',
+ SSL_version => $self->tls_protocols,
SSL_use_cert => 1,
SSL_cert_file => $self->tls_cert,
SSL_key_file => $self->tls_key,
@@ -226,6 +235,7 @@
my $tlssocket =
IO::Socket::SSL->new_from_fd(
fileno(STDIN), '+>',
+ SSL_version => $self->tls_protocols,
SSL_use_cert => 1,
SSL_cert_file => $self->tls_cert,
SSL_key_file => $self->tls_key,
@@ -286,6 +296,12 @@
$self->{_tls_ciphers};
}
+sub tls_protocols {
+ my $self = shift;
+ @_ and $self->{_tls_protocols} = shift;
+ $self->{_tls_protocols};
+}
+
sub ssl_context {
my $self = shift;
@_ and $self->{_ssl_ctx} = shift;

View File

@ -1,13 +0,0 @@
diff -Nur -x '*.orig' -x '*.rej' qpsmtpd-0.95/plugins/spamassassin mezzanine_patched_qpsmtpd-0.95/plugins/spamassassin
--- qpsmtpd-0.95/plugins/spamassassin 2015-02-11 23:00:25.000000000 +0100
+++ mezzanine_patched_qpsmtpd-0.95/plugins/spamassassin 2015-12-16 22:07:17.554311238 +0100
@@ -172,7 +172,8 @@
return DECLINED if $self->is_immune();
- if ($transaction->data_size > 500_000) {
+ my $limit = $self->{_args}->{size_limit} || 500_000;
+ if ($transaction->data_size > $limit) {
$self->log(LOGINFO,
"skip, too large (" . $transaction->data_size . ")");
return DECLINED;

View File

@ -1,11 +0,0 @@
diff -Nur -x '*.orig' -x '*.rej' qpsmtpd-0.96/plugins/dmarc mezzanine_patched_qpsmtpd-0.96/plugins/dmarc
--- qpsmtpd-0.96/plugins/dmarc 2016-05-03 09:10:34.624577605 +0200
+++ mezzanine_patched_qpsmtpd-0.96/plugins/dmarc 2016-05-03 09:10:14.160581205 +0200
@@ -154,6 +154,7 @@
};
};
+ $transaction->notes('dmarc_result', $dmarc->result);
my $disposition = $dmarc->result->disposition;
my $result = $dmarc->result->result;
my $auth_str = "dmarc=$result";

View File

@ -1,12 +0,0 @@
diff -Nur qpsmtpd-0.96/lib/Qpsmtpd.pm qpsmtpd-0.96_bz9460/lib/Qpsmtpd.pm
--- qpsmtpd-0.96/lib/Qpsmtpd.pm 2016-02-16 23:52:02.000000000 +0100
+++ qpsmtpd-0.96_bz9460/lib/Qpsmtpd.pm 2016-05-16 11:52:07.041152848 +0200
@@ -448,7 +448,7 @@
sub address {
my $self = shift;
my $addr = Qpsmtpd::Address->new(@_);
- $addr->qp($self);
+ $addr->qp($self) if $addr;
return $addr;
}

View File

@ -1,12 +0,0 @@
diff -Nur --no-dereference qpsmtpd-0.96.old/qpsmtpd-forkserver qpsmtpd-0.96/qpsmtpd-forkserver
--- qpsmtpd-0.96.old/qpsmtpd-forkserver 2021-11-15 11:59:33.048000000 -0500
+++ qpsmtpd-0.96/qpsmtpd-forkserver 2021-11-15 12:00:46.276000000 -0500
@@ -193,7 +193,7 @@
POSIX::setuid($quid) or die "unable to change uid: $!\n";
$> = $quid;
-#$qpsmtpd->load_plugins;
+$qpsmtpd->load_plugins;
foreach my $addr (@LISTENADDR) {
::log(LOGINFO, "Listening on $addr->{addr}:$addr->{port}");

View File

@ -1,12 +0,0 @@
diff -Nur qpsmtpd-0.96/plugins/karma qpsmtpd-0.96_bz9462/plugins/karma
--- qpsmtpd-0.96/plugins/karma 2016-05-09 23:56:51.145450697 +0200
+++ qpsmtpd-0.96_bz9462/plugins/karma 2016-05-09 23:57:34.484446254 +0200
@@ -449,7 +449,7 @@
my $history = ($nice || 0) - $naughty;
my $log_mess = '';
- if ($karma <= $self->{_args}{strikes}) { # Enough negative strikes ?
+ if ($karma <= -$self->{_args}{strikes}) { # Enough negative strikes ?
$history--;
my $negative_limit = 0 - $self->{_args}{negative};
if ($history <= $negative_limit) {

View File

@ -1,12 +0,0 @@
diff -Nur qpsmtpd-0.96/plugins/dmarc qpsmtpd-0.96_bz9206/plugins/dmarc
--- qpsmtpd-0.96/plugins/dmarc 2016-05-28 10:29:34.469470149 +0200
+++ qpsmtpd-0.96_bz9206/plugins/dmarc 2016-05-28 10:33:36.482470978 +0200
@@ -145,7 +145,7 @@
my $pol;
eval { $pol = $dmarc->result->published; };
if ( $self->{_args}{reporting} && $pol ) {
- if ( $dmarc->has_valid_reporting_uri($pol->rua) ) {
+ if ( $pol->rua && $dmarc->has_valid_reporting_uri($pol->rua) ) {
eval { $dmarc->save_aggregate(); };
$self->log(LOGERROR, $@ ) if $@;
}

View File

@ -1,15 +0,0 @@
diff -Nur -x '*.orig' -x '*.rej' qpsmtpd-0.96/lib/Qpsmtpd/TcpServer.pm mezzanine_patched_qpsmtpd-0.96/lib/Qpsmtpd/TcpServer.pm
--- qpsmtpd-0.96/lib/Qpsmtpd/TcpServer.pm 2016-02-16 23:52:02.000000000 +0100
+++ mezzanine_patched_qpsmtpd-0.96/lib/Qpsmtpd/TcpServer.pm 2016-04-15 19:13:13.873874838 +0200
@@ -120,7 +120,10 @@
while (<STDIN>) {
alarm 0;
$_ =~ s/\r?\n$//s; # advanced chomp
- $self->log(LOGINFO, "dispatching $_");
+ my $log = $_;
+ $log =~ s/AUTH PLAIN (.*)/AUTH PLAIN <hidden credentials>/
+ unless ($self->config('loglevel') || '6') >= 7;
+ $self->log(LOGINFO, "dispatching $log");
$self->connection->notes('original_string', $_);
defined $self->dispatch(split / +/, $_, 2)
or $self->respond(502, "command unrecognized: '$_'");

View File

@ -1,14 +0,0 @@
diff -Nur qpsmtpd-0.96/plugins/dkim qpsmtpd-0.96_bz9480/plugins/dkim
--- qpsmtpd-0.96/plugins/dkim 2016-07-05 22:08:55.700102610 +0200
+++ qpsmtpd-0.96_bz9480/plugins/dkim 2016-07-05 22:11:51.485075880 +0200
@@ -262,7 +262,9 @@
push @data, "selector: " . $dkim->signature->selector if $dkim->signature;
push @data, "result: " . $dkim->result_detail if $dkim->result_detail;
- foreach my $policy ($dkim->policies) {
+ my @policies = eval { $dkim->policies };
+
+ foreach my $policy (@policies) {
next if !$policy;
push @data, "policy: " . $policy->as_string;
push @data, "name: " . $policy->name;

View File

@ -1,62 +0,0 @@
diff -Nur -x '*.orig' -x '*.rej' qpsmtpd-0.96/docs/hooks.md mezzanine_patched_qpsmtpd-0.96/docs/hooks.md
--- qpsmtpd-0.96/docs/hooks.md 2016-02-16 23:52:02.000000000 +0100
+++ mezzanine_patched_qpsmtpd-0.96/docs/hooks.md 2016-05-03 21:56:38.216306190 +0200
@@ -343,26 +343,20 @@
The `data_post_headers` hook is called after the client sends the final .\r\n of
a message and before the message is processed by `data_post`. This hook is
-primarily used by plugins that insert new headers (ex: Received-SPF) and/or
+used by plugins that insert new headers (ex: Received-SPF) and/or
modify headers such as appending to Authentication-Results (SPF, DKIM, DMARC).
When it is desirable to have these header modifications evaluated by filtering
software (spamassassin, dspam, etc.) running on `data_post`, this hook should be
used instead of `data_post`.
-Allowed return codes are
-
-- DENY
-
- Return a hard failure code
+Note that you cannot reject in this hook, use the data_post hook instead
-- DENYSOFT
-
- Return a soft failure code
+Allowed return codes are
-- DENY\_DISCONNECT / DENYSOFT\_DISCONNECT
+- DECLINED
- as above but with disconnect
+ Do nothing
## hook\_data\_post
diff -Nur -x '*.orig' -x '*.rej' qpsmtpd-0.96/plugins/dmarc mezzanine_patched_qpsmtpd-0.96/plugins/dmarc
--- qpsmtpd-0.96/plugins/dmarc 2016-02-16 23:52:02.000000000 +0100
+++ mezzanine_patched_qpsmtpd-0.96/plugins/dmarc 2016-05-03 21:57:11.943312651 +0200
@@ -102,6 +102,7 @@
else {
$self->{_dmarc} = Mail::DMARC::PurePerl->new();
$self->register_hook('data_post_headers', 'check_dmarc');
+ $self->register_hook('data_post', 'reject_dmarc');
};
}
@@ -189,6 +190,13 @@
return DECLINED if $self->is_immune;
$self->adjust_karma(-3);
-# at what point do we reject?
- return $self->get_reject("failed DMARC policy");
+ # Add a mark now so the data_post hook can do the real reject
+ $transaction->notes('reject_dmarc', '1');
+}
+
+sub reject_dmarc {
+ my ($self, $transaction) = @_;
+ return $self->get_reject("failed DMARC policy")
+ if ($transaction->notes('reject_dmarc'));
+ return DECLINED;
}

View File

@ -1,12 +0,0 @@
diff -Nur -x '*.orig' -x '*.rej' qpsmtpd-0.96/plugins/karma_tool mezzanine_patched_qpsmtpd-0.96/plugins/karma_tool
--- qpsmtpd-0.96/plugins/karma_tool 2016-02-16 23:52:02.000000000 +0100
+++ mezzanine_patched_qpsmtpd-0.96/plugins/karma_tool 2016-05-04 22:22:03.913868169 +0200
@@ -63,7 +63,7 @@
my ( $self ) = @_;
return $self->{db} if $self->{db};
$self->{db} = Qpsmtpd::DB->new( name => 'karma' );
- $self->{db}->dir(
+ $self->{db}->candidate_dirs(
$self->{args}{db_dir},
qw( /var/lib/qpsmtpd/karma ./var/db ./config . ) );
my $path = $self->{db}->path;

View File

@ -1,46 +0,0 @@
diff -Nur -x '*.orig' -x '*.rej' qpsmtpd-0.96/plugins/karma mezzanine_patched_qpsmtpd-0.96/plugins/karma
--- qpsmtpd-0.96/plugins/karma 2016-02-16 23:52:02.000000000 +0100
+++ mezzanine_patched_qpsmtpd-0.96/plugins/karma 2016-04-20 18:39:43.299979947 +0200
@@ -50,6 +50,16 @@
penalize a "mostly good" sender. Raising it to 2 reduces that possibility to
improbable.
+=head2 strikes <integer>
+
+How many strikes is needed to consider the mail nice or naughty.
+Various plugins can adjust the karma (see USING KARMA IN OTHER PLUGINS).
+For example, with the default value of 3, if the karma of this message is 3
+or above, the mail is considered to be a nice one. If it's -3 or less, it's
+considered a naughty one. Between -2 and +2 it's neutral
+
+Default: 3
+
=head2 penalty_days <days>
The number of days a naughty sender is refused connections. Use a decimal
@@ -238,6 +248,7 @@
$self->log(LOGERROR, "Bad arguments") if @_ % 2;
$self->{_args} = {@_};
$self->{_args}{negative} ||= 1;
+ $self->{_args}{strikes} ||= 3;
$self->{_args}{penalty_days} ||= 1;
$self->{_args}{reject_type} ||= 'disconnect';
@@ -428,7 +439,7 @@
my $history = ($nice || 0) - $naughty;
my $log_mess = '';
- if ($karma < -2) { # they achieved at least 2 strikes
+ if ($karma <= $self->{_args}{strikes}) { # Enough negative strikes ?
$history--;
my $negative_limit = 0 - $self->{_args}{negative};
if ($history <= $negative_limit) {
@@ -445,7 +456,7 @@
$log_mess = "negative";
}
}
- elsif ($karma > 2) {
+ elsif ($karma >= $self->{_args}{strikes}) {
$nice++;
$log_mess = "positive";
}

View File

@ -1,86 +0,0 @@
diff -Nur -x '*.orig' -x '*.rej' qpsmtpd-0.96/config.sample/karma_tlds mezzanine_patched_qpsmtpd-0.96/config.sample/karma_tlds
--- qpsmtpd-0.96/config.sample/karma_tlds 1970-01-01 01:00:00.000000000 +0100
+++ mezzanine_patched_qpsmtpd-0.96/config.sample/karma_tlds 2016-04-20 20:44:35.881444632 +0200
@@ -0,0 +1,14 @@
+# Karma to apply depending on the tld of the envelope sender
+# Used by the karma plugin
+# Warning: setting karma too low can blacklist the entire tld
+work:-4
+rocks:-3
+ninja:-3
+info:-2
+biz:-2
+pw:-2
+me:-1
+us:-5
+eu:-4
+link:-3
+science:-6
diff -Nur -x '*.orig' -x '*.rej' qpsmtpd-0.96/plugins/karma mezzanine_patched_qpsmtpd-0.96/plugins/karma
--- qpsmtpd-0.96/plugins/karma 2016-04-20 20:44:38.326444593 +0200
+++ mezzanine_patched_qpsmtpd-0.96/plugins/karma 2016-04-20 19:18:31.922041433 +0200
@@ -102,6 +102,20 @@
Adjust the quantity of logging for this plugin. See docs/logging.pod
+=head1 CONFIG FILES
+
+This plugin uses the following configuration files. All are optional.
+
+=head2 karma_tlds
+
+This file can contain semicolon separated tld and the corresponding
+karma adjustment to apply when the envelope sender match. It can be used to
+penalize "spammy" tlds, or to raise the karma from (mostly) good tlds.
+
+jp:-4
+ch:-3
+fr:+1
+
=head1 BENEFITS
Karma reduces the resources wasted by naughty mailers. When used with
@@ -352,18 +366,14 @@
my $full_from = $self->connection->notes('envelope_from');
$self->illegal_envelope_format( $full_from );
- my %spammy_tlds = (
- map { $_ => 4 } qw/ info pw /,
- map { $_ => 3 } qw/ tw biz /,
- map { $_ => 2 } qw/ cl br fr be jp no se sg /,
- );
- foreach my $tld ( keys %spammy_tlds ) {
+ my $karma_tlds = $self->get_karma_tlds() or return DECLINED;
+ foreach my $tld ( keys %$karma_tlds ) {
my $len = length $tld;
- my $score = $spammy_tlds{$tld} or next;
+ my $score = $karma_tlds->{$tld} or next;
$len ++;
if ( $sender->host && ".$tld" eq substr($sender->host,-$len,$len) ) {
- $self->log(LOGINFO, "penalizing .$tld envelope sender");
- $self->adjust_karma(-$score);
+ $self->log(LOGINFO, "adjusting karma for .$tld envelope sender");
+ $self->adjust_karma($score);
}
}
@@ -479,6 +489,19 @@
}
}
+sub get_karma_tlds {
+ my $self = shift;
+
+ my %karma_tlds =
+ map { (split /:/, $_, 2)[0, 1] } $self->qp->config('karma_tlds');
+ if (!%karma_tlds) {
+ $self->log(LOGDEBUG, "no specific karma for tlds defined");
+ return;
+ }
+
+ return \%karma_tlds;
+}
+
sub parse_db_record {
my ($self, $value) = @_;

View File

@ -1,104 +0,0 @@
diff -Nur -x '*.orig' -x '*.rej' qpsmtpd-0.96/plugins/sender_permitted_from mezzanine_patched_qpsmtpd-0.96/plugins/sender_permitted_from
--- qpsmtpd-0.96/plugins/sender_permitted_from 2016-02-16 23:52:02.000000000 +0100
+++ mezzanine_patched_qpsmtpd-0.96/plugins/sender_permitted_from 2016-05-04 18:33:37.510387152 +0200
@@ -37,6 +37,37 @@
SPF levels above 4 are for crusaders who don't mind rejecting some valid mail when the sending server administrator hasn't dotted his i's and crossed his t's. May the deities bless their obsessive little hearts.
+=head2 no_dmarc_policy
+
+When used with the dmarc plugin, you don't want sender_permitted_from to reject anything, because dmarc needs to check the sender's domain policy.
+So you'll most likely have reject 1.
+But then, if the sender's domain has no dmarc policy, you might want to reject solely based on SPF result. This is what this setting is for. A first hook runs at the mail stage and evaluate SPF. Then a second hook runs at the data_post stage (after dmarc), so you have a second chance to reject.
+
+Like reject, you can set a value to indicate how agressive you want to be:
+
+ 0 do not reject (default)
+ 1 reject if SPF record says 'fail'
+ 2 stricter reject. Also rejects 'softfail'
+ 3 reject 'neutral'
+ 4 reject if no SPF records, or a syntax error
+
+Just like reject, the recommanded value is 1. 2 will be a bit more agressive. 3 and 4 will most likely reject some valid emails.
+
+So, for example, you can use something like this:
+
+sender_permetted_from reject 1 no_dmarc_policy 1
+dkim reject 0
+dmarc reject 1 reporting 1
+
+Note this setting will only have effect if:
+
+ * dmarc plugin is used, and loaded after sender_permetted_from in your plugin's config
+ * the reject value is either 1 or 2 (meaning, no reject at the mail stage)
+ * dmarc ran with no error
+ * the sender's domain has no dmarc policy published (that means, no _dmarc DNS entry)
+
+Note that if a domain has a dmarc "p=none" policy, then this setting has no effect. Only if there's no dmarc policy at all it'll be used.
+
=head1 SEE ALSO
http://spf.pobox.com/
@@ -82,8 +113,10 @@
if (!$self->{_args}{reject} && $self->qp->config('spfbehavior')) {
$self->{_args}{reject} = $self->qp->config('spfbehavior');
}
+ $self->{_args}{no_dmarc_policy} ||= 0;
$self->register_hook('mail', 'evaluate_spf');
$self->register_hook('data_post_headers', 'add_spf_header');
+ $self->register_hook('data_post', 'no_dmarc_policy') if $self->{_args}{no_dmarc_policy} > 0;
}
sub evaluate_spf {
@@ -202,6 +235,51 @@
return DECLINED;
}
+sub no_dmarc_policy {
+ my ($self, $transaction) = @_;
+ return DECLINED if $self->is_immune;
+ unless ($self->{_args}{no_dmarc_policy}){
+ return DECLINED;
+ }
+ if ($transaction->notes('spfquery') && $transaction->notes('dmarc_result')){
+ my $spf_result = $transaction->notes('spfquery')->code;
+ my $why = $transaction->notes('spfquery')->local_explanation;
+ my $dmarc_dispo = $transaction->notes('dmarc_result')->disposition;
+ return DECLINED unless $dmarc_dispo eq 'none';
+ my $comment = '';
+ if ($transaction->notes('dmarc_result')->reason &&
+ $transaction->notes('dmarc_result')->reason->[0] &&
+ $transaction->notes('dmarc_result')->reason->[0]->comment){
+ $comment = $transaction->notes('dmarc_result')->reason->[0]->comment;
+ }
+ return DECLINED unless $comment eq 'no policy';
+ # No SPF or syntaxe error: reject if no_dmarc_policy is at least 4
+ if ((!$spf_result || $spf_result =~ m/(?:permerror|error|none)/) && $self->{_args}{no_dmarc_policy} >= 4){
+ $self->log(LOGINFO, "fail, $spf_result, $why");
+ return DENY, "SPF - $spf_result: $why";
+ }
+ # All other reject levels require an SPF code
+ return DECLINED unless $spf_result;
+ # Neutral
+ if ($spf_result eq 'neutral' && $self->{_args}{no_dmarc_policy} >= 3){
+ $self->log(LOGINFO, "fail, $spf_result, $why");
+ return DENY, "SPF - $spf_result: $why";
+ }
+ # Softfail
+ if ($spf_result eq 'softfail' && $self->{_args}{no_dmarc_policy} >= 2){
+ $self->log(LOGINFO, "fail, $spf_result, $why");
+ return DENY, "SPF - $spf_result: $why";
+ }
+ # Fail
+ if ($spf_result eq 'fail' && $self->{_args}{no_dmarc_policy} >= 1){
+ $self->log(LOGINFO, "fail, $spf_result, $why");
+ return DENY, "SPF - $spf_result: $why";
+ }
+ }
+ $self->log(LOGINFO, 'pass');
+ return DECLINED;
+}
+
sub handle_code_none {
my ($self, $reject, $why) = @_;

View File

@ -1,12 +0,0 @@
diff -Nur -x '*.orig' -x '*.rej' qpsmtpd-0.96/lib/Qpsmtpd/Plugin.pm mezzanine_patched_qpsmtpd-0.96/lib/Qpsmtpd/Plugin.pm
--- qpsmtpd-0.96/lib/Qpsmtpd/Plugin.pm 2016-04-25 18:16:52.626824508 +0200
+++ mezzanine_patched_qpsmtpd-0.96/lib/Qpsmtpd/Plugin.pm 2016-04-25 18:16:24.785825492 +0200
@@ -228,7 +228,7 @@
# the naughty plugin will reject later
if ($reject eq 'naughty') {
$self->log(LOGINFO, "fail, NAUGHTY" . $log_mess);
- return $self->store_deferred_reject($smtp_mess);
+ return $self->store_deferred_reject('(' . $self->plugin_name . ') ' . $smtp_mess);
}
# they asked for reject, we give them reject

View File

@ -1,20 +0,0 @@
diff -Nur -x '*.orig' -x '*.rej' qpsmtpd-0.96/plugins/uribl mezzanine_patched_qpsmtpd-0.96/plugins/uribl
--- qpsmtpd-0.96/plugins/uribl 2016-02-16 23:52:02.000000000 +0100
+++ mezzanine_patched_qpsmtpd-0.96/plugins/uribl 2016-04-18 22:10:13.565072798 +0200
@@ -271,6 +271,7 @@
if ($l =~ /(.*)=$/) {
push @qp_continuations, $1;
+ next;
}
elsif (@qp_continuations) {
$l = join('', @qp_continuations, $l);
@@ -498,7 +499,7 @@
return \@matches;
}
-sub hook_data {
+sub hook_data_post {
my ($self, $transaction) = @_;
return DECLINED if $self->is_immune();

View File

@ -1,31 +0,0 @@
diff -Nur qpsmtpd-0.96/plugins/uribl qpsmtpd-0.96_bz9467/plugins/uribl
--- qpsmtpd-0.96/plugins/uribl 2016-07-11 22:28:55.105488861 +0200
+++ qpsmtpd-0.96_bz9467/plugins/uribl 2016-07-11 22:32:14.607442677 +0200
@@ -96,6 +96,9 @@
use Time::HiRes qw(time);
use IO::Select;
+use Data::Validate::Domain;
+
+my $v = Data::Validate::Domain->new;
# ccTLDs that allocate domain names within a strict two-level hierarchy,
# as in *.co.uk
@@ -348,7 +351,7 @@
museum|name|net|org|pro|tel|travel|
[a-zA-Z]{2})
)(?!\w)
- }gix
+ }gix && $v->is_domain($1)
)
{
my $host = lc $1;
@@ -393,7 +396,7 @@
museum|name|net|org|pro|tel|travel|
[a-zA-Z]{2})
)
- }gix
+ }gix && $v->is_domain($1)
)
{
my $host = lc $1;

View File

@ -1,13 +0,0 @@
diff -Nur -x '*.orig' -x '*.rej' qpsmtpd-0.96/plugins/helo mezzanine_patched_qpsmtpd-0.96/plugins/helo
--- qpsmtpd-0.96/plugins/helo 2016-02-16 23:52:02.000000000 +0100
+++ mezzanine_patched_qpsmtpd-0.96/plugins/helo 2016-04-15 19:25:32.953870666 +0200
@@ -521,7 +521,8 @@
my ($dns_name, $helo_name) = @_;
return if !$dns_name;
- return if split(/\./, $dns_name) < 2; # not a FQDN
+ my @dots = split(/\./, $dns_name);
+ return if scalar @dots < 2; # not a FQDN
if ($dns_name eq $helo_name) {
$self->log(LOGDEBUG, "reverse name match");

BIN
qpsmtpd-0.96.tar.gz (Stored with Git LFS)

Binary file not shown.

View File

@ -0,0 +1,30 @@
--- qpsmtpd/plugins/uribl.ori 2024-04-28 22:37:31.090000000 -0400
+++ qpsmtpd/plugins/uribl 2024-04-28 22:40:04.111000000 -0400
@@ -102,6 +102,9 @@
use Time::HiRes qw(time);
use IO::Select;
+use Data::Validate::Domain;
+
+my $v = Data::Validate::Domain->new;
# ccTLDs that allocate domain names within a strict two-level hierarchy,
# as in *.co.uk
@@ -358,7 +361,7 @@
[a-zA-Z0-9](?:[a-zA-Z0-9\-]+\.)+ # hostname
(?:$tlds4regex|[a-zA-Z]{2}) # tld
)(?!\w)
- }gix
+ }gix && $v->is_domain($1)
)
{
my $host = lc $1;
@@ -400,7 +403,7 @@
[a-zA-Z0-9](?:[a-zA-Z0-9\-]+\.)+ # hostname
(?:$tlds4regex|[a-zA-Z]{2}) # tld
)(?!\w)
- }gix
+ }gix && $v->is_domain($1)
)
{
my $host = lc $1;

BIN
qpsmtpd-1.0.0.tar.gz (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,53 @@
diff --git a/lib/Qpsmtpd/Postfix.pm b/lib/Qpsmtpd/Postfix.pm
index 2946bba..6a98816 100644
--- a/lib/Qpsmtpd/Postfix.pm
+++ b/lib/Qpsmtpd/Postfix.pm
@@ -206,7 +206,7 @@ sub inject_mail {
my $hdr = $transaction->header->as_string;
for (split(/\r?\n/, $hdr)) {
- print STDERR "hdr: $_\n";
+ # print STDERR "hdr: $_\n";
$strm->print_msg_line($_);
}
$transaction->body_resetpos;
diff --git a/lib/Qpsmtpd/SMTP.pm b/lib/Qpsmtpd/SMTP.pm
index 70e25c5..913879d 100644
--- a/lib/Qpsmtpd/SMTP.pm
+++ b/lib/Qpsmtpd/SMTP.pm
@@ -860,8 +860,8 @@ sub received_line {
my $header_str;
my ($rc, @received) =
$self->run_hooks("received_line", $smtp, $authheader, $sslheader);
- if ($rc == OK) {
- return join("\n", @received);
+ if ($rc == OK) {
+ $header_str = join("\n", @received);
}
else { # assume $rc == DECLINED
$header_str =
diff --git a/plugins/whitelist b/plugins/whitelist
index 3f9c10e..e79da8e 100644
--- a/plugins/whitelist
+++ b/plugins/whitelist
@@ -99,6 +99,7 @@ use strict;
use warnings;
use Qpsmtpd::Constants;
+use NetAddr::IP;
my $VERSION = 0.02;
diff --git a/qpsmtpd-forkserver b/qpsmtpd-forkserver
index f0f9e58..ef151a6 100755
--- a/qpsmtpd-forkserver
+++ b/qpsmtpd-forkserver
@@ -193,7 +193,7 @@ POSIX::setgid($qgid) or die "unable to change gid: $!\n";
POSIX::setuid($quid) or die "unable to change uid: $!\n";
$> = $quid;
-#$qpsmtpd->load_plugins;
+$qpsmtpd->load_plugins;
foreach my $addr (@LISTENADDR) {
::log(LOGINFO, "Listening on $addr->{addr}:$addr->{port}");

View File

@ -1,8 +1,8 @@
# $Id: qpsmtpd.spec,v 1.30 2021/11/16 23:13:14 jpp Exp $
Name: qpsmtpd
Version: 0.96
Release: 21%{?dist}
Version: 1.0.0
Release: 2%{?dist}
Summary: qpsmtpd + qpsmtpd-apache
License: MIT
Group: System Environment/Daemons
@ -26,33 +26,17 @@ Source4: in.qpsmtpd
Source5: qpsmtpd.conf
Source6: README.selinux
Patch1: qpsmtpd-0.95-spamassassin_size_limit.patch
Patch2: qpsmtpd-0.95-qpsmtpd_forserver_keepalive.patch
Patch3: qpsmtpd-0.95-notls_conf.patch
Patch4: qpsmtpd-0.95-allow_tls_proto_from_conf.patch
Patch5: qpsmtpd-0.96-set_hooks.patch
Patch6: qpsmtpd-0.96-warn_implicit_split.patch
Patch7: qpsmtpd-0.96-dont_log_credentials_except_in_debug.patch
Patch8: qpsmtpd-0.96-uribl_data_post.patch
Patch9: qpsmtpd-0.96-karma_strikes.patch
Patch10: qpsmtpd-0.96-more_badrcptto.patch
Patch11: qpsmtpd-0.96-karma_tlds_conf.patch
Patch12: qpsmtpd-0.96-store_original_plugin_name.patch
Patch13: qpsmtpd-0.96-fix_karma_tool_dir_path.patch
Patch14: qpsmtpd-0.96-dkim_no_sign_for_others_on_symlinks.patch
Patch15: qpsmtpd-0.96-fix_dmarc_reject.patch
Patch16: qpsmtpd-0.96-add_dmarc_result_notes.patch
Patch17: qpsmtpd-0.96-spf_on_no_dmarc_policy.patch
Patch18: qpsmtpd-0.96-check_negative_karma_strikes.patch
Patch19: qpsmtpd-0.96-addr_defined_before_use.patch
Patch20: qpsmtpd-0.96-check_rua_is_defined.patch
Patch21: qpsmtpd-0.96-remove_karma_rcpt_handler.patch
Patch22: qpsmtpd-0.96-eval_dkim_policies.patch
Patch23: qpsmtpd-0.96-uribl_validate_domains.patch
Patch24: qpsmtpd-0.96-bz10112-whitelist.patch
Patch25: qpsmtpd-0.96-SME10139-Message-Id.patch
Patch26: qpsmtpd-0.96-bz10290-spamassassin-fetchmail.patch
Patch27: qpsmtpd-0.96-bz10387-load_plugins-on-start.patch
Patch1: qpsmtpd-0.95-qpsmtpd_forserver_keepalive.patch
Patch2: qpsmtpd-0.95-notls_conf.patch
Patch3: qpsmtpd-0.96-set_hooks.patch
Patch4: qpsmtpd-0.96-more_badrcptto.patch
Patch5: qpsmtpd-0.96-dkim_no_sign_for_others_on_symlinks.patch
Patch6: qpsmtpd-0.96-remove_karma_rcpt_handler.patch
Patch7: qpsmtpd-1.0.0-uribl_validate_domains.patch
Patch8: qpsmtpd-0.96-bz10112-whitelist.patch
Patch9: qpsmtpd-0.96-SME10139-Message-Id.patch
Patch10: qpsmtpd-0.96-bz10290-spamassassin-fetchmail.patch
Patch11: qpsmtpd-v1.0.0-20240428.patch
%description
qpsmtpd is a flexible smtpd daemon written in Perl. Apart from the core
@ -75,6 +59,7 @@ that turns Apache into an SMTP server using Qpsmtpd.
%prep
%setup -q
%patch1 -p1
%patch2 -p1
%patch3 -p1
@ -86,22 +71,6 @@ that turns Apache into an SMTP server using Qpsmtpd.
%patch9 -p1
%patch10 -p1
%patch11 -p1
%patch12 -p1
%patch13 -p1
%patch14 -p1
%patch15 -p1
%patch16 -p1
%patch17 -p1
%patch18 -p1
%patch19 -p1
%patch20 -p1
%patch21 -p1
%patch22 -p1
%patch23 -p1
%patch24 -p1
%patch25 -p1
%patch26 -p1
%patch27 -p1
%build
CFLAGS="$RPM_OPT_FLAGS" perl Makefile.PL INSTALLDIRS="vendor" PREFIX="%{buildroot}%{_prefix}"
@ -174,6 +143,17 @@ fi
%changelog
* Sat Oct 19 2024 Jean-Philippe Pialasse <jpp@koozali.org> 1.0.0-2.sme
- remove auth_imap patch not needed [SME: 11802]
* Sun Apr 28 2024 Jean-Philippe Pialasse <jpp@koozali.org> 1.0.0-1.sme
- upgrade to last version [SME: 11802]
- reapply our specific patches, rewrite them if necessary
added qpsmtpd-0.96-bz12450-auth_imap-perport.patch from SME10
- apply last fixes in git since v 1.0.0
postfix: avoid logging full headers;Load plugins in qpsmtpd-forkserver;
Fix received_line hook behaviour; Add missing use statement for NetAddr::IP
* Fri Jul 14 2023 BogusDateBot
- Eliminated rpmbuild "bogus date" warnings due to inconsistent weekday,
by assuming the date is correct and changing the weekday.