| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
# <@LICENSE> |
|
2
|
|
|
|
|
|
|
# Licensed to the Apache Software Foundation (ASF) under one or more |
|
3
|
|
|
|
|
|
|
# contributor license agreements. See the NOTICE file distributed with |
|
4
|
|
|
|
|
|
|
# this work for additional information regarding copyright ownership. |
|
5
|
|
|
|
|
|
|
# The ASF licenses this file to you under the Apache License, Version 2.0 |
|
6
|
|
|
|
|
|
|
# (the "License"); you may not use this file except in compliance with |
|
7
|
|
|
|
|
|
|
# the License. You may obtain a copy of the License at: |
|
8
|
|
|
|
|
|
|
# |
|
9
|
|
|
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0 |
|
10
|
|
|
|
|
|
|
# |
|
11
|
|
|
|
|
|
|
# Unless required by applicable law or agreed to in writing, software |
|
12
|
|
|
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS, |
|
13
|
|
|
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
14
|
|
|
|
|
|
|
# See the License for the specific language governing permissions and |
|
15
|
|
|
|
|
|
|
# limitations under the License. |
|
16
|
|
|
|
|
|
|
# </@LICENSE> |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=head1 NAME |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
Mail::SpamAssassin::Plugin::SPF - perform SPF verification tests |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
loadplugin Mail::SpamAssassin::Plugin::SPF |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
This plugin checks a message against Sender Policy Framework (SPF) |
|
29
|
|
|
|
|
|
|
records published by the domain owners in DNS to fight email address |
|
30
|
|
|
|
|
|
|
forgery and make it easier to identify spams. |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
=cut |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
use Mail::SpamAssassin::Plugin; |
|
36
|
22
|
|
|
22
|
|
152
|
use Mail::SpamAssassin::Logger; |
|
|
22
|
|
|
|
|
41
|
|
|
|
22
|
|
|
|
|
681
|
|
|
37
|
22
|
|
|
22
|
|
119
|
use Mail::SpamAssassin::Timeout; |
|
|
22
|
|
|
|
|
49
|
|
|
|
22
|
|
|
|
|
1384
|
|
|
38
|
22
|
|
|
22
|
|
170
|
use strict; |
|
|
22
|
|
|
|
|
49
|
|
|
|
22
|
|
|
|
|
682
|
|
|
39
|
22
|
|
|
22
|
|
127
|
use warnings; |
|
|
22
|
|
|
|
|
61
|
|
|
|
22
|
|
|
|
|
531
|
|
|
40
|
22
|
|
|
22
|
|
113
|
# use bytes; |
|
|
22
|
|
|
|
|
55
|
|
|
|
22
|
|
|
|
|
810
|
|
|
41
|
|
|
|
|
|
|
use re 'taint'; |
|
42
|
22
|
|
|
22
|
|
142
|
|
|
|
22
|
|
|
|
|
40
|
|
|
|
22
|
|
|
|
|
85635
|
|
|
43
|
|
|
|
|
|
|
our @ISA = qw(Mail::SpamAssassin::Plugin); |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
# constructor: register the eval rule |
|
46
|
|
|
|
|
|
|
my $class = shift; |
|
47
|
|
|
|
|
|
|
my $mailsaobject = shift; |
|
48
|
63
|
|
|
63
|
1
|
229
|
|
|
49
|
63
|
|
|
|
|
143
|
# some boilerplate... |
|
50
|
|
|
|
|
|
|
$class = ref($class) || $class; |
|
51
|
|
|
|
|
|
|
my $self = $class->SUPER::new($mailsaobject); |
|
52
|
63
|
|
33
|
|
|
389
|
bless ($self, $class); |
|
53
|
63
|
|
|
|
|
384
|
|
|
54
|
63
|
|
|
|
|
178
|
$self->register_eval_rule ("check_for_spf_pass"); |
|
55
|
|
|
|
|
|
|
$self->register_eval_rule ("check_for_spf_neutral"); |
|
56
|
63
|
|
|
|
|
352
|
$self->register_eval_rule ("check_for_spf_none"); |
|
57
|
63
|
|
|
|
|
201
|
$self->register_eval_rule ("check_for_spf_fail"); |
|
58
|
63
|
|
|
|
|
186
|
$self->register_eval_rule ("check_for_spf_softfail"); |
|
59
|
63
|
|
|
|
|
181
|
$self->register_eval_rule ("check_for_spf_permerror"); |
|
60
|
63
|
|
|
|
|
215
|
$self->register_eval_rule ("check_for_spf_temperror"); |
|
61
|
63
|
|
|
|
|
207
|
$self->register_eval_rule ("check_for_spf_helo_pass"); |
|
62
|
63
|
|
|
|
|
200
|
$self->register_eval_rule ("check_for_spf_helo_neutral"); |
|
63
|
63
|
|
|
|
|
202
|
$self->register_eval_rule ("check_for_spf_helo_none"); |
|
64
|
63
|
|
|
|
|
170
|
$self->register_eval_rule ("check_for_spf_helo_fail"); |
|
65
|
63
|
|
|
|
|
178
|
$self->register_eval_rule ("check_for_spf_helo_softfail"); |
|
66
|
63
|
|
|
|
|
182
|
$self->register_eval_rule ("check_for_spf_helo_permerror"); |
|
67
|
63
|
|
|
|
|
205
|
$self->register_eval_rule ("check_for_spf_helo_temperror"); |
|
68
|
63
|
|
|
|
|
183
|
$self->register_eval_rule ("check_for_spf_whitelist_from"); |
|
69
|
63
|
|
|
|
|
187
|
$self->register_eval_rule ("check_for_def_spf_whitelist_from"); |
|
70
|
63
|
|
|
|
|
173
|
|
|
71
|
63
|
|
|
|
|
169
|
$self->set_config($mailsaobject->{conf}); |
|
72
|
|
|
|
|
|
|
|
|
73
|
63
|
|
|
|
|
293
|
return $self; |
|
74
|
|
|
|
|
|
|
} |
|
75
|
63
|
|
|
|
|
660
|
|
|
76
|
|
|
|
|
|
|
########################################################################### |
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
my($self, $conf) = @_; |
|
79
|
|
|
|
|
|
|
my @cmds; |
|
80
|
|
|
|
|
|
|
|
|
81
|
63
|
|
|
63
|
0
|
159
|
=head1 USER SETTINGS |
|
82
|
63
|
|
|
|
|
135
|
|
|
83
|
|
|
|
|
|
|
=over 4 |
|
84
|
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=item whitelist_from_spf user@example.com |
|
86
|
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
Works similarly to whitelist_from, except that in addition to matching |
|
88
|
|
|
|
|
|
|
a sender address, a check against the domain's SPF record must pass. |
|
89
|
|
|
|
|
|
|
The first parameter is an address to whitelist, and the second is a string |
|
90
|
|
|
|
|
|
|
to match the relay's rDNS. |
|
91
|
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
Just like whitelist_from, multiple addresses per line, separated by spaces, |
|
93
|
|
|
|
|
|
|
are OK. Multiple C<whitelist_from_spf> lines are also OK. |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
The headers checked for whitelist_from_spf addresses are the same headers |
|
96
|
|
|
|
|
|
|
used for SPF checks (Envelope-From, Return-Path, X-Envelope-From, etc). |
|
97
|
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
Since this whitelist requires an SPF check to be made, network tests must be |
|
99
|
|
|
|
|
|
|
enabled. It is also required that your trust path be correctly configured. |
|
100
|
|
|
|
|
|
|
See the section on C<trusted_networks> for more info on trust paths. |
|
101
|
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
e.g. |
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
whitelist_from_spf joe@example.com fred@example.com |
|
105
|
|
|
|
|
|
|
whitelist_from_spf *@example.com |
|
106
|
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
=item def_whitelist_from_spf user@example.com |
|
108
|
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
Same as C<whitelist_from_spf>, but used for the default whitelist entries |
|
110
|
|
|
|
|
|
|
in the SpamAssassin distribution. The whitelist score is lower, because |
|
111
|
|
|
|
|
|
|
these are often targets for spammer spoofing. |
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
=item unwhitelist_from_spf user@example.com |
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
Used to remove a C<whitelist_from_spf> or C<def_whitelist_from_spf> entry. |
|
116
|
|
|
|
|
|
|
The specified email address has to match exactly the address previously used. |
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
Useful for removing undesired default entries from a distributed configuration |
|
119
|
|
|
|
|
|
|
by a local or site-specific configuration or by C<user_prefs>. |
|
120
|
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
=cut |
|
122
|
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
push (@cmds, { |
|
124
|
|
|
|
|
|
|
setting => 'whitelist_from_spf', |
|
125
|
|
|
|
|
|
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_ADDRLIST |
|
126
|
63
|
|
|
|
|
270
|
}); |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
push (@cmds, { |
|
129
|
|
|
|
|
|
|
setting => 'def_whitelist_from_spf', |
|
130
|
|
|
|
|
|
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_ADDRLIST |
|
131
|
63
|
|
|
|
|
205
|
}); |
|
132
|
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
push (@cmds, { |
|
134
|
|
|
|
|
|
|
setting => 'unwhitelist_from_spf', |
|
135
|
|
|
|
|
|
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_ADDRLIST, |
|
136
|
|
|
|
|
|
|
code => sub { |
|
137
|
|
|
|
|
|
|
my ($self, $key, $value, $line) = @_; |
|
138
|
|
|
|
|
|
|
unless (defined $value && $value !~ /^$/) { |
|
139
|
|
|
|
|
|
|
return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE; |
|
140
|
0
|
|
|
0
|
|
0
|
} |
|
141
|
0
|
0
|
0
|
|
|
0
|
unless ($value =~ /^(?:\S+(?:\s+\S+)*)$/) { |
|
142
|
0
|
|
|
|
|
0
|
return $Mail::SpamAssassin::Conf::INVALID_VALUE; |
|
143
|
|
|
|
|
|
|
} |
|
144
|
0
|
0
|
|
|
|
0
|
$self->{parser}->remove_from_addrlist('whitelist_from_spf', |
|
145
|
0
|
|
|
|
|
0
|
split (/\s+/, $value)); |
|
146
|
|
|
|
|
|
|
$self->{parser}->remove_from_addrlist('def_whitelist_from_spf', |
|
147
|
0
|
|
|
|
|
0
|
split (/\s+/, $value)); |
|
148
|
|
|
|
|
|
|
} |
|
149
|
0
|
|
|
|
|
0
|
}); |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
=back |
|
152
|
63
|
|
|
|
|
557
|
|
|
153
|
|
|
|
|
|
|
=head1 ADMINISTRATOR OPTIONS |
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
=over 4 |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
=item spf_timeout n (default: 5) |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
How many seconds to wait for an SPF query to complete, before scanning |
|
160
|
|
|
|
|
|
|
continues without the SPF result. A numeric value is optionally suffixed |
|
161
|
|
|
|
|
|
|
by a time unit (s, m, h, d, w, indicating seconds (default), minutes, hours, |
|
162
|
|
|
|
|
|
|
days, weeks). |
|
163
|
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
=cut |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
push (@cmds, { |
|
167
|
|
|
|
|
|
|
setting => 'spf_timeout', |
|
168
|
|
|
|
|
|
|
is_admin => 1, |
|
169
|
63
|
|
|
|
|
360
|
default => 5, |
|
170
|
|
|
|
|
|
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_DURATION |
|
171
|
|
|
|
|
|
|
}); |
|
172
|
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=item do_not_use_mail_spf (0|1) (default: 0) |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
By default the plugin will try to use the Mail::SPF module for SPF checks if |
|
176
|
|
|
|
|
|
|
it can be loaded. If Mail::SPF cannot be used the plugin will fall back to |
|
177
|
|
|
|
|
|
|
using the legacy Mail::SPF::Query module if it can be loaded. |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
Use this option to stop the plugin from using Mail::SPF and cause it to try to |
|
180
|
|
|
|
|
|
|
use Mail::SPF::Query instead. |
|
181
|
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
=cut |
|
183
|
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
push(@cmds, { |
|
185
|
|
|
|
|
|
|
setting => 'do_not_use_mail_spf', |
|
186
|
|
|
|
|
|
|
is_admin => 1, |
|
187
|
63
|
|
|
|
|
282
|
default => 0, |
|
188
|
|
|
|
|
|
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_BOOL, |
|
189
|
|
|
|
|
|
|
}); |
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=item do_not_use_mail_spf_query (0|1) (default: 0) |
|
192
|
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
As above, but instead stop the plugin from trying to use Mail::SPF::Query and |
|
194
|
|
|
|
|
|
|
cause it to only try to use Mail::SPF. |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
=cut |
|
197
|
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
push(@cmds, { |
|
199
|
|
|
|
|
|
|
setting => 'do_not_use_mail_spf_query', |
|
200
|
|
|
|
|
|
|
is_admin => 1, |
|
201
|
63
|
|
|
|
|
241
|
default => 0, |
|
202
|
|
|
|
|
|
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_BOOL, |
|
203
|
|
|
|
|
|
|
}); |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
=item ignore_received_spf_header (0|1) (default: 0) |
|
206
|
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
By default, to avoid unnecessary DNS lookups, the plugin will try to use the |
|
208
|
|
|
|
|
|
|
SPF results found in any C<Received-SPF> headers it finds in the message that |
|
209
|
|
|
|
|
|
|
could only have been added by an internal relay. |
|
210
|
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
Set this option to 1 to ignore any C<Received-SPF> headers present and to have |
|
212
|
|
|
|
|
|
|
the plugin perform the SPF check itself. |
|
213
|
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
Note that unless the plugin finds an C<identity=helo>, or some unsupported |
|
215
|
|
|
|
|
|
|
identity, it will assume that the result is a mfrom SPF check result. The |
|
216
|
|
|
|
|
|
|
only identities supported are C<mfrom>, C<mailfrom> and C<helo>. |
|
217
|
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
=cut |
|
219
|
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
push(@cmds, { |
|
221
|
|
|
|
|
|
|
setting => 'ignore_received_spf_header', |
|
222
|
|
|
|
|
|
|
is_admin => 1, |
|
223
|
63
|
|
|
|
|
274
|
default => 0, |
|
224
|
|
|
|
|
|
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_BOOL, |
|
225
|
|
|
|
|
|
|
}); |
|
226
|
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
=item use_newest_received_spf_header (0|1) (default: 0) |
|
228
|
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
By default, when using C<Received-SPF> headers, the plugin will attempt to use |
|
230
|
|
|
|
|
|
|
the oldest (bottom most) C<Received-SPF> headers, that were added by internal |
|
231
|
|
|
|
|
|
|
relays, that it can parse results from since they are the most likely to be |
|
232
|
|
|
|
|
|
|
accurate. This is done so that if you have an incoming mail setup where one |
|
233
|
|
|
|
|
|
|
of your primary MXes doesn't know about a secondary MX (or your MXes don't |
|
234
|
|
|
|
|
|
|
know about some sort of forwarding relay that SA considers trusted+internal) |
|
235
|
|
|
|
|
|
|
but SA is aware of the actual domain boundary (internal_networks setting) SA |
|
236
|
|
|
|
|
|
|
will use the results that are most accurate. |
|
237
|
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
Use this option to start with the newest (top most) C<Received-SPF> headers, |
|
239
|
|
|
|
|
|
|
working downwards until results are successfully parsed. |
|
240
|
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
=cut |
|
242
|
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
push(@cmds, { |
|
244
|
|
|
|
|
|
|
setting => 'use_newest_received_spf_header', |
|
245
|
|
|
|
|
|
|
is_admin => 1, |
|
246
|
63
|
|
|
|
|
285
|
default => 0, |
|
247
|
|
|
|
|
|
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_BOOL, |
|
248
|
|
|
|
|
|
|
}); |
|
249
|
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
$conf->{parser}->register_commands(\@cmds); |
|
251
|
|
|
|
|
|
|
} |
|
252
|
|
|
|
|
|
|
|
|
253
|
63
|
|
|
|
|
303
|
|
|
254
|
|
|
|
|
|
|
=item has_check_for_spf_errors |
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
Adds capability check for "if can()" for check_for_spf_permerror, check_for_spf_temperror, check_for_spf_helo_permerror and check_for_spf_helo_permerror |
|
257
|
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
=cut |
|
259
|
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
# SPF support |
|
262
|
|
|
|
|
|
|
my ($self, $scanner) = @_; |
|
263
|
0
|
|
|
0
|
1
|
0
|
$self->_check_spf ($scanner, 0) unless $scanner->{spf_checked}; |
|
264
|
|
|
|
|
|
|
$scanner->{spf_pass}; |
|
265
|
|
|
|
|
|
|
} |
|
266
|
|
|
|
|
|
|
|
|
267
|
4
|
|
|
4
|
0
|
11
|
my ($self, $scanner) = @_; |
|
268
|
4
|
50
|
|
|
|
12
|
$self->_check_spf ($scanner, 0) unless $scanner->{spf_checked}; |
|
269
|
4
|
|
|
|
|
67
|
$scanner->{spf_neutral}; |
|
270
|
|
|
|
|
|
|
} |
|
271
|
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
my ($self, $scanner) = @_; |
|
273
|
4
|
|
|
4
|
0
|
11
|
$self->_check_spf ($scanner, 0) unless $scanner->{spf_checked}; |
|
274
|
4
|
50
|
|
|
|
24
|
$scanner->{spf_none}; |
|
275
|
4
|
|
|
|
|
83
|
} |
|
276
|
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
my ($self, $scanner) = @_; |
|
278
|
|
|
|
|
|
|
$self->_check_spf ($scanner, 0) unless $scanner->{spf_checked}; |
|
279
|
0
|
|
|
0
|
0
|
0
|
if ($scanner->{spf_failure_comment}) { |
|
280
|
0
|
0
|
|
|
|
0
|
$scanner->test_log ($scanner->{spf_failure_comment}); |
|
281
|
0
|
|
|
|
|
0
|
} |
|
282
|
|
|
|
|
|
|
$scanner->{spf_fail}; |
|
283
|
|
|
|
|
|
|
} |
|
284
|
|
|
|
|
|
|
|
|
285
|
4
|
|
|
4
|
0
|
12
|
my ($self, $scanner) = @_; |
|
286
|
4
|
50
|
|
|
|
17
|
$self->_check_spf ($scanner, 0) unless $scanner->{spf_checked}; |
|
287
|
4
|
50
|
|
|
|
12
|
$scanner->{spf_softfail}; |
|
288
|
0
|
|
|
|
|
0
|
} |
|
289
|
|
|
|
|
|
|
|
|
290
|
4
|
|
|
|
|
68
|
my ($self, $scanner) = @_; |
|
291
|
|
|
|
|
|
|
$self->_check_spf ($scanner, 0) unless $scanner->{spf_checked}; |
|
292
|
|
|
|
|
|
|
$scanner->{spf_permerror}; |
|
293
|
|
|
|
|
|
|
} |
|
294
|
4
|
|
|
4
|
0
|
8
|
|
|
295
|
4
|
50
|
|
|
|
13
|
my ($self, $scanner) = @_; |
|
296
|
4
|
|
|
|
|
58
|
$self->_check_spf ($scanner, 0) unless $scanner->{spf_checked}; |
|
297
|
|
|
|
|
|
|
$scanner->{spf_temperror}; |
|
298
|
|
|
|
|
|
|
} |
|
299
|
|
|
|
|
|
|
|
|
300
|
0
|
|
|
0
|
0
|
0
|
my ($self, $scanner) = @_; |
|
301
|
0
|
0
|
|
|
|
0
|
$self->_check_spf ($scanner, 1) unless $scanner->{spf_helo_checked}; |
|
302
|
0
|
|
|
|
|
0
|
$scanner->{spf_helo_pass}; |
|
303
|
|
|
|
|
|
|
} |
|
304
|
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
my ($self, $scanner) = @_; |
|
306
|
0
|
|
|
0
|
0
|
0
|
$self->_check_spf ($scanner, 1) unless $scanner->{spf_helo_checked}; |
|
307
|
0
|
0
|
|
|
|
0
|
$scanner->{spf_helo_neutral}; |
|
308
|
0
|
|
|
|
|
0
|
} |
|
309
|
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
my ($self, $scanner) = @_; |
|
311
|
|
|
|
|
|
|
$self->_check_spf ($scanner, 1) unless $scanner->{spf_helo_checked}; |
|
312
|
4
|
|
|
4
|
0
|
12
|
$scanner->{spf_helo_none}; |
|
313
|
4
|
50
|
|
|
|
14
|
} |
|
314
|
4
|
|
|
|
|
67
|
|
|
315
|
|
|
|
|
|
|
my ($self, $scanner) = @_; |
|
316
|
|
|
|
|
|
|
$self->_check_spf ($scanner, 1) unless $scanner->{spf_helo_checked}; |
|
317
|
|
|
|
|
|
|
if ($scanner->{spf_helo_failure_comment}) { |
|
318
|
4
|
|
|
4
|
0
|
9
|
$scanner->test_log ($scanner->{spf_helo_failure_comment}); |
|
319
|
4
|
50
|
|
|
|
21
|
} |
|
320
|
4
|
|
|
|
|
82
|
$scanner->{spf_helo_fail}; |
|
321
|
|
|
|
|
|
|
} |
|
322
|
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
my ($self, $scanner) = @_; |
|
324
|
0
|
|
|
0
|
0
|
0
|
$self->_check_spf ($scanner, 1) unless $scanner->{spf_helo_checked}; |
|
325
|
0
|
0
|
|
|
|
0
|
$scanner->{spf_helo_softfail}; |
|
326
|
0
|
|
|
|
|
0
|
} |
|
327
|
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
my ($self, $scanner) = @_; |
|
329
|
|
|
|
|
|
|
$self->_check_spf ($scanner, 1) unless $scanner->{spf_helo_checked}; |
|
330
|
4
|
|
|
4
|
0
|
9
|
$scanner->{spf_helo_permerror}; |
|
331
|
4
|
50
|
|
|
|
11
|
} |
|
332
|
4
|
50
|
|
|
|
10
|
|
|
333
|
0
|
|
|
|
|
0
|
my ($self, $scanner) = @_; |
|
334
|
|
|
|
|
|
|
$self->_check_spf ($scanner, 1) unless $scanner->{spf_helo_checked}; |
|
335
|
4
|
|
|
|
|
60
|
$scanner->{spf_helo_temperror}; |
|
336
|
|
|
|
|
|
|
} |
|
337
|
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
my ($self, $scanner) = @_; |
|
339
|
4
|
|
|
4
|
0
|
9
|
$self->_check_spf_whitelist($scanner) unless $scanner->{spf_whitelist_from_checked}; |
|
340
|
4
|
50
|
|
|
|
13
|
$scanner->{spf_whitelist_from}; |
|
341
|
4
|
|
|
|
|
62
|
} |
|
342
|
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
my ($self, $scanner) = @_; |
|
344
|
|
|
|
|
|
|
$self->_check_def_spf_whitelist($scanner) unless $scanner->{def_spf_whitelist_from_checked}; |
|
345
|
0
|
|
|
0
|
0
|
0
|
$scanner->{def_spf_whitelist_from}; |
|
346
|
0
|
0
|
|
|
|
0
|
} |
|
347
|
0
|
|
|
|
|
0
|
|
|
348
|
|
|
|
|
|
|
my ($self, $scanner, $ishelo) = @_; |
|
349
|
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
my $timer = $self->{main}->time_method("check_spf"); |
|
351
|
0
|
|
|
0
|
0
|
0
|
|
|
352
|
0
|
0
|
|
|
|
0
|
# we can re-use results from any *INTERNAL* Received-SPF header in the message... |
|
353
|
0
|
|
|
|
|
0
|
# we can't use results from trusted but external hosts since (i) spf checks are |
|
354
|
|
|
|
|
|
|
# supposed to be done "on the domain boundary", (ii) even if an external header |
|
355
|
|
|
|
|
|
|
# has a result that matches what we would get, the check was probably done on a |
|
356
|
|
|
|
|
|
|
# different envelope (like the apache.org list servers checking the ORCPT and |
|
357
|
81
|
|
|
81
|
0
|
194
|
# then using a new envelope to send the mail from the list) and (iii) if the |
|
358
|
81
|
50
|
|
|
|
409
|
# checks are being done right and the envelope isn't being changed it's 99% |
|
359
|
81
|
|
|
|
|
1216
|
# likely that the trusted+external host really should be defined as part of your |
|
360
|
|
|
|
|
|
|
# internal network |
|
361
|
|
|
|
|
|
|
if ($scanner->{conf}->{ignore_received_spf_header}) { |
|
362
|
|
|
|
|
|
|
dbg("spf: ignoring any Received-SPF headers from internal hosts, by admin setting"); |
|
363
|
81
|
|
|
81
|
0
|
195
|
} elsif ($scanner->{checked_for_received_spf_header}) { |
|
364
|
81
|
50
|
|
|
|
428
|
dbg("spf: already checked for Received-SPF headers, proceeding with DNS based checks"); |
|
365
|
81
|
|
|
|
|
1269
|
} else { |
|
366
|
|
|
|
|
|
|
$scanner->{checked_for_received_spf_header} = 1; |
|
367
|
|
|
|
|
|
|
dbg("spf: checking to see if the message has a Received-SPF header that we can use"); |
|
368
|
|
|
|
|
|
|
|
|
369
|
8
|
|
|
8
|
|
17
|
my @internal_hdrs = split("\n", $scanner->get('ALL-INTERNAL')); |
|
370
|
|
|
|
|
|
|
unless ($scanner->{conf}->{use_newest_received_spf_header}) { |
|
371
|
8
|
|
|
|
|
30
|
# look for the LAST (earliest in time) header, it'll be the most accurate |
|
372
|
|
|
|
|
|
|
@internal_hdrs = reverse(@internal_hdrs); |
|
373
|
|
|
|
|
|
|
} else { |
|
374
|
|
|
|
|
|
|
dbg("spf: starting with the newest Received-SPF headers first"); |
|
375
|
|
|
|
|
|
|
} |
|
376
|
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
foreach my $hdr (@internal_hdrs) { |
|
378
|
|
|
|
|
|
|
local($1,$2); |
|
379
|
|
|
|
|
|
|
if ($hdr =~ /^received-spf:/i) { |
|
380
|
|
|
|
|
|
|
dbg("spf: found a Received-SPF header added by an internal host: $hdr"); |
|
381
|
|
|
|
|
|
|
|
|
382
|
8
|
50
|
|
|
|
29
|
# old version: |
|
|
|
100
|
|
|
|
|
|
|
383
|
0
|
|
|
|
|
0
|
# Received-SPF: pass (herse.apache.org: domain of spamassassin@dostech.ca |
|
384
|
|
|
|
|
|
|
# designates 69.61.78.188 as permitted sender) |
|
385
|
4
|
|
|
|
|
15
|
|
|
386
|
|
|
|
|
|
|
# new version: |
|
387
|
4
|
|
|
|
|
8
|
# Received-SPF: pass (dostech.ca: 69.61.78.188 is authorized to use |
|
388
|
4
|
|
|
|
|
12
|
# 'spamassassin@dostech.ca' in 'mfrom' identity (mechanism 'mx' matched)) |
|
389
|
|
|
|
|
|
|
# receiver=FC5-VPC; identity=mfrom; envelope-from="spamassassin@dostech.ca"; |
|
390
|
4
|
|
|
|
|
19
|
# helo=smtp.dostech.net; client-ip=69.61.78.188 |
|
391
|
4
|
50
|
|
|
|
14
|
|
|
392
|
|
|
|
|
|
|
# Received-SPF: pass (dostech.ca: 69.61.78.188 is authorized to use 'dostech.ca' |
|
393
|
4
|
|
|
|
|
10
|
# in 'helo' identity (mechanism 'mx' matched)) receiver=FC5-VPC; identity=helo; |
|
394
|
|
|
|
|
|
|
# helo=dostech.ca; client-ip=69.61.78.188 |
|
395
|
0
|
|
|
|
|
0
|
|
|
396
|
|
|
|
|
|
|
# http://www.openspf.org/RFC_4408#header-field |
|
397
|
|
|
|
|
|
|
# wtf - for some reason something is sticking an extra space between the header name and field value |
|
398
|
4
|
|
|
|
|
8
|
if ($hdr =~ /^received-spf:\s*(pass|neutral|(?:soft)?fail|(?:temp|perm)error|none)\b(?:.*\bidentity=(\S+?);?\b)?/i) { |
|
399
|
17
|
|
|
|
|
33
|
my $result = lc($1); |
|
400
|
17
|
50
|
|
|
|
55
|
|
|
|
|
50
|
|
|
|
|
|
|
401
|
0
|
|
|
|
|
0
|
my $identity = ''; # we assume it's a mfrom check if we can't tell otherwise |
|
402
|
|
|
|
|
|
|
if (defined $2) { |
|
403
|
|
|
|
|
|
|
$identity = lc($2); |
|
404
|
|
|
|
|
|
|
if ($identity eq 'mfrom' || $identity eq 'mailfrom') { |
|
405
|
|
|
|
|
|
|
next if $scanner->{spf_checked}; |
|
406
|
|
|
|
|
|
|
$identity = ''; |
|
407
|
|
|
|
|
|
|
} elsif ($identity eq 'helo') { |
|
408
|
|
|
|
|
|
|
next if $scanner->{spf_helo_checked}; |
|
409
|
|
|
|
|
|
|
$identity = 'helo_'; |
|
410
|
|
|
|
|
|
|
} else { |
|
411
|
|
|
|
|
|
|
dbg("spf: found unknown identity value, cannot use: $identity"); |
|
412
|
|
|
|
|
|
|
next; # try the next Received-SPF header, if any |
|
413
|
|
|
|
|
|
|
} |
|
414
|
|
|
|
|
|
|
} else { |
|
415
|
|
|
|
|
|
|
next if $scanner->{spf_checked}; |
|
416
|
|
|
|
|
|
|
} |
|
417
|
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
# we'd set these if we actually did the check |
|
419
|
0
|
0
|
|
|
|
0
|
$scanner->{"spf_${identity}checked"} = 1; |
|
420
|
0
|
|
|
|
|
0
|
$scanner->{"spf_${identity}pass"} = 0; |
|
421
|
|
|
|
|
|
|
$scanner->{"spf_${identity}neutral"} = 0; |
|
422
|
0
|
|
|
|
|
0
|
$scanner->{"spf_${identity}none"} = 0; |
|
423
|
0
|
0
|
|
|
|
0
|
$scanner->{"spf_${identity}fail"} = 0; |
|
424
|
0
|
|
|
|
|
0
|
$scanner->{"spf_${identity}softfail"} = 0; |
|
425
|
0
|
0
|
0
|
|
|
0
|
$scanner->{"spf_${identity}temperror"} = 0; |
|
|
|
0
|
|
|
|
|
|
|
426
|
0
|
0
|
|
|
|
0
|
$scanner->{"spf_${identity}permerror"} = 0; |
|
427
|
0
|
|
|
|
|
0
|
$scanner->{"spf_${identity}failure_comment"} = undef; |
|
428
|
|
|
|
|
|
|
|
|
429
|
0
|
0
|
|
|
|
0
|
# and the result |
|
430
|
0
|
|
|
|
|
0
|
$scanner->{"spf_${identity}${result}"} = 1; |
|
431
|
|
|
|
|
|
|
dbg("spf: re-using %s result from Received-SPF header: %s", |
|
432
|
0
|
|
|
|
|
0
|
($identity ? 'helo' : 'mfrom'), $result); |
|
433
|
0
|
|
|
|
|
0
|
|
|
434
|
|
|
|
|
|
|
# if we've got *both* the mfrom and helo results we're done |
|
435
|
|
|
|
|
|
|
return if ($scanner->{spf_checked} && $scanner->{spf_helo_checked}); |
|
436
|
0
|
0
|
|
|
|
0
|
|
|
437
|
|
|
|
|
|
|
} else { |
|
438
|
|
|
|
|
|
|
dbg("spf: could not parse result from existing Received-SPF header"); |
|
439
|
|
|
|
|
|
|
} |
|
440
|
0
|
|
|
|
|
0
|
|
|
441
|
0
|
|
|
|
|
0
|
} elsif ($hdr =~ /^Authentication-Results:.*;\s*SPF\s*=\s*([^;]*)/i) { |
|
442
|
0
|
|
|
|
|
0
|
dbg("spf: found an Authentication-Results header added by an internal host: $hdr"); |
|
443
|
0
|
|
|
|
|
0
|
|
|
444
|
0
|
|
|
|
|
0
|
# RFC 5451 header parser - added by D. Stussy 2010-09-09: |
|
445
|
0
|
|
|
|
|
0
|
# Authentication-Results: mail.example.com; SPF=none smtp.mailfrom=example.org (comment) |
|
446
|
0
|
|
|
|
|
0
|
|
|
447
|
0
|
|
|
|
|
0
|
my $tmphdr = $1; |
|
448
|
0
|
|
|
|
|
0
|
if ($tmphdr =~ /^(pass|neutral|(?:hard|soft)?fail|(?:temp|perm)error|none)(?:[^;]*?\bsmtp\.(\S+)\s*=[^;]+)?/i) { |
|
449
|
|
|
|
|
|
|
my $result = lc($1); |
|
450
|
|
|
|
|
|
|
$result = 'fail' if $result eq 'hardfail'; # RFC5451 permits this |
|
451
|
0
|
|
|
|
|
0
|
|
|
452
|
0
|
0
|
|
|
|
0
|
my $identity = ''; # we assume it's a mfrom check if we can't tell otherwise |
|
453
|
|
|
|
|
|
|
if (defined $2) { |
|
454
|
|
|
|
|
|
|
$identity = lc($2); |
|
455
|
|
|
|
|
|
|
if ($identity eq 'mfrom' || $identity eq 'mailfrom') { |
|
456
|
0
|
0
|
0
|
|
|
0
|
next if $scanner->{spf_checked}; |
|
457
|
|
|
|
|
|
|
$identity = ''; |
|
458
|
|
|
|
|
|
|
} elsif ($identity eq 'helo') { |
|
459
|
0
|
|
|
|
|
0
|
next if $scanner->{spf_helo_checked}; |
|
460
|
|
|
|
|
|
|
$identity = 'helo_'; |
|
461
|
|
|
|
|
|
|
} else { |
|
462
|
|
|
|
|
|
|
dbg("spf: found unknown identity value, cannot use: $identity"); |
|
463
|
0
|
|
|
|
|
0
|
next; # try the next Authentication-Results header, if any |
|
464
|
|
|
|
|
|
|
} |
|
465
|
|
|
|
|
|
|
} else { |
|
466
|
|
|
|
|
|
|
next if $scanner->{spf_checked}; |
|
467
|
|
|
|
|
|
|
} |
|
468
|
0
|
|
|
|
|
0
|
|
|
469
|
0
|
0
|
|
|
|
0
|
# we'd set these if we actually did the check |
|
470
|
0
|
|
|
|
|
0
|
$scanner->{"spf_${identity}checked"} = 1; |
|
471
|
0
|
0
|
|
|
|
0
|
$scanner->{"spf_${identity}pass"} = 0; |
|
472
|
|
|
|
|
|
|
$scanner->{"spf_${identity}neutral"} = 0; |
|
473
|
0
|
|
|
|
|
0
|
$scanner->{"spf_${identity}none"} = 0; |
|
474
|
0
|
0
|
|
|
|
0
|
$scanner->{"spf_${identity}fail"} = 0; |
|
475
|
0
|
|
|
|
|
0
|
$scanner->{"spf_${identity}softfail"} = 0; |
|
476
|
0
|
0
|
0
|
|
|
0
|
$scanner->{"spf_${identity}temperror"} = 0; |
|
|
|
0
|
|
|
|
|
|
|
477
|
0
|
0
|
|
|
|
0
|
$scanner->{"spf_${identity}permerror"} = 0; |
|
478
|
0
|
|
|
|
|
0
|
$scanner->{"spf_${identity}failure_comment"} = undef; |
|
479
|
|
|
|
|
|
|
|
|
480
|
0
|
0
|
|
|
|
0
|
# and the result |
|
481
|
0
|
|
|
|
|
0
|
$scanner->{"spf_${identity}${result}"} = 1; |
|
482
|
|
|
|
|
|
|
dbg("spf: re-using %s result from Authentication-Results header: %s", |
|
483
|
0
|
|
|
|
|
0
|
($identity ? 'helo' : 'mfrom'), $result); |
|
484
|
0
|
|
|
|
|
0
|
|
|
485
|
|
|
|
|
|
|
# if we've got *both* the mfrom and helo results we're done |
|
486
|
|
|
|
|
|
|
return if ($scanner->{spf_checked} && $scanner->{spf_helo_checked}); |
|
487
|
0
|
0
|
|
|
|
0
|
|
|
488
|
|
|
|
|
|
|
} else { |
|
489
|
|
|
|
|
|
|
dbg("spf: could not parse result from existing Authentication-Results header"); |
|
490
|
|
|
|
|
|
|
} |
|
491
|
0
|
|
|
|
|
0
|
} |
|
492
|
0
|
|
|
|
|
0
|
} |
|
493
|
0
|
|
|
|
|
0
|
# we can return if we've found the one we're being asked to get |
|
494
|
0
|
|
|
|
|
0
|
return if ( ($ishelo && $scanner->{spf_helo_checked}) || |
|
495
|
0
|
|
|
|
|
0
|
(!$ishelo && $scanner->{spf_checked}) ); |
|
496
|
0
|
|
|
|
|
0
|
} |
|
497
|
0
|
|
|
|
|
0
|
|
|
498
|
0
|
|
|
|
|
0
|
# abort if dns or an spf module isn't available |
|
499
|
0
|
|
|
|
|
0
|
return unless $scanner->is_dns_available(); |
|
500
|
|
|
|
|
|
|
return if $self->{no_spf_module}; |
|
501
|
|
|
|
|
|
|
|
|
502
|
0
|
|
|
|
|
0
|
# select the SPF module we're going to use |
|
503
|
0
|
0
|
|
|
|
0
|
unless (defined $self->{has_mail_spf}) { |
|
504
|
|
|
|
|
|
|
my $eval_stat; |
|
505
|
|
|
|
|
|
|
eval { |
|
506
|
|
|
|
|
|
|
die("Mail::SPF disabled by admin setting\n") if $scanner->{conf}->{do_not_use_mail_spf}; |
|
507
|
0
|
0
|
0
|
|
|
0
|
|
|
508
|
|
|
|
|
|
|
require Mail::SPF; |
|
509
|
|
|
|
|
|
|
if (!defined $Mail::SPF::VERSION || $Mail::SPF::VERSION < 2.001) { |
|
510
|
0
|
|
|
|
|
0
|
die "Mail::SPF 2.001 or later required, this is ". |
|
511
|
|
|
|
|
|
|
(defined $Mail::SPF::VERSION ? $Mail::SPF::VERSION : 'unknown')."\n"; |
|
512
|
|
|
|
|
|
|
} |
|
513
|
|
|
|
|
|
|
# Mail::SPF::Server can be re-used, and we get to use our own resolver object! |
|
514
|
|
|
|
|
|
|
$self->{spf_server} = Mail::SPF::Server->new( |
|
515
|
|
|
|
|
|
|
hostname => $scanner->get_tag('HOSTNAME'), |
|
516
|
4
|
0
|
33
|
|
|
40
|
dns_resolver => $self->{main}->{resolver}, |
|
|
|
|
33
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
517
|
|
|
|
|
|
|
max_dns_interactive_terms => 20); |
|
518
|
|
|
|
|
|
|
# Bug 7112: max_dns_interactive_terms defaults to 10, but even 14 is |
|
519
|
|
|
|
|
|
|
# not enough for ebay.com, setting it to 15 NOTE: raising to 20 per bug 7182 |
|
520
|
8
|
50
|
|
|
|
29
|
1; |
|
521
|
8
|
50
|
|
|
|
21
|
} or do { |
|
522
|
|
|
|
|
|
|
$eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat; |
|
523
|
|
|
|
|
|
|
}; |
|
524
|
8
|
100
|
|
|
|
30
|
|
|
525
|
1
|
|
|
|
|
3
|
if (!defined($eval_stat)) { |
|
526
|
|
|
|
|
|
|
dbg("spf: using Mail::SPF for SPF checks"); |
|
527
|
1
|
50
|
|
|
|
3
|
$self->{has_mail_spf} = 1; |
|
528
|
|
|
|
|
|
|
} else { |
|
529
|
1
|
|
|
|
|
391
|
# strip the @INC paths... users are going to see it and think there's a problem even though |
|
530
|
1
|
50
|
33
|
|
|
41484
|
# we're going to fall back to Mail::SPF::Query (which will display the same paths if it fails) |
|
531
|
0
|
0
|
|
|
|
0
|
$eval_stat =~ s#^Can't locate Mail/SPFd.pm in \@INC .*#Can't locate Mail/SPFd.pm#; |
|
532
|
|
|
|
|
|
|
dbg("spf: cannot load Mail::SPF module or create Mail::SPF::Server object: $eval_stat"); |
|
533
|
|
|
|
|
|
|
dbg("spf: attempting to use legacy Mail::SPF::Query module instead"); |
|
534
|
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
undef $eval_stat; |
|
536
|
|
|
|
|
|
|
eval { |
|
537
|
|
|
|
|
|
|
die("Mail::SPF::Query disabled by admin setting\n") if $scanner->{conf}->{do_not_use_mail_spf_query}; |
|
538
|
1
|
|
|
|
|
8
|
|
|
539
|
|
|
|
|
|
|
require Mail::SPF::Query; |
|
540
|
|
|
|
|
|
|
if (!defined $Mail::SPF::Query::VERSION || $Mail::SPF::Query::VERSION < 1.996) { |
|
541
|
1
|
|
|
|
|
190
|
die "Mail::SPF::Query 1.996 or later required, this is ". |
|
542
|
1
|
50
|
|
|
|
2
|
(defined $Mail::SPF::Query::VERSION ? $Mail::SPF::Query::VERSION : 'unknown')."\n"; |
|
543
|
0
|
0
|
|
|
|
0
|
} |
|
|
0
|
|
|
|
|
0
|
|
|
544
|
|
|
|
|
|
|
1; |
|
545
|
|
|
|
|
|
|
} or do { |
|
546
|
1
|
50
|
|
|
|
4
|
$eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat; |
|
547
|
1
|
|
|
|
|
5
|
}; |
|
548
|
1
|
|
|
|
|
3
|
|
|
549
|
|
|
|
|
|
|
if (!defined($eval_stat)) { |
|
550
|
|
|
|
|
|
|
dbg("spf: using Mail::SPF::Query for SPF checks"); |
|
551
|
|
|
|
|
|
|
$self->{has_mail_spf} = 0; |
|
552
|
0
|
|
|
|
|
0
|
} else { |
|
553
|
0
|
|
|
|
|
0
|
dbg("spf: cannot load Mail::SPF::Query module: $eval_stat"); |
|
554
|
0
|
|
|
|
|
0
|
dbg("spf: one of Mail::SPF or Mail::SPF::Query is required for SPF checks, SPF checks disabled"); |
|
555
|
|
|
|
|
|
|
$self->{no_spf_module} = 1; |
|
556
|
0
|
|
|
|
|
0
|
return; |
|
557
|
|
|
|
|
|
|
} |
|
558
|
0
|
0
|
|
|
|
0
|
} |
|
559
|
|
|
|
|
|
|
} |
|
560
|
0
|
|
|
|
|
0
|
|
|
561
|
0
|
0
|
0
|
|
|
0
|
|
|
562
|
0
|
0
|
|
|
|
0
|
# skip SPF checks if the A/MX records are nonexistent for the From |
|
563
|
|
|
|
|
|
|
# domain, anyway, to avoid crappy messages from slowing us down |
|
564
|
|
|
|
|
|
|
# (bug 3016) |
|
565
|
0
|
|
|
|
|
0
|
return if $scanner->check_for_from_dns(); |
|
566
|
0
|
0
|
|
|
|
0
|
|
|
567
|
0
|
0
|
|
|
|
0
|
if ($ishelo) { |
|
|
0
|
|
|
|
|
0
|
|
|
568
|
|
|
|
|
|
|
# SPF HELO-checking variant |
|
569
|
|
|
|
|
|
|
$scanner->{spf_helo_checked} = 1; |
|
570
|
0
|
0
|
|
|
|
0
|
$scanner->{spf_helo_pass} = 0; |
|
571
|
0
|
|
|
|
|
0
|
$scanner->{spf_helo_neutral} = 0; |
|
572
|
0
|
|
|
|
|
0
|
$scanner->{spf_helo_none} = 0; |
|
573
|
|
|
|
|
|
|
$scanner->{spf_helo_fail} = 0; |
|
574
|
0
|
|
|
|
|
0
|
$scanner->{spf_helo_softfail} = 0; |
|
575
|
0
|
|
|
|
|
0
|
$scanner->{spf_helo_permerror} = 0; |
|
576
|
0
|
|
|
|
|
0
|
$scanner->{spf_helo_temperror} = 0; |
|
577
|
0
|
|
|
|
|
0
|
$scanner->{spf_helo_failure_comment} = undef; |
|
578
|
|
|
|
|
|
|
} else { |
|
579
|
|
|
|
|
|
|
# SPF on envelope sender (where possible) |
|
580
|
|
|
|
|
|
|
$scanner->{spf_checked} = 1; |
|
581
|
|
|
|
|
|
|
$scanner->{spf_pass} = 0; |
|
582
|
|
|
|
|
|
|
$scanner->{spf_neutral} = 0; |
|
583
|
|
|
|
|
|
|
$scanner->{spf_none} = 0; |
|
584
|
|
|
|
|
|
|
$scanner->{spf_fail} = 0; |
|
585
|
|
|
|
|
|
|
$scanner->{spf_softfail} = 0; |
|
586
|
8
|
50
|
|
|
|
25
|
$scanner->{spf_permerror} = 0; |
|
587
|
|
|
|
|
|
|
$scanner->{spf_temperror} = 0; |
|
588
|
8
|
100
|
|
|
|
21
|
$scanner->{spf_failure_comment} = undef; |
|
589
|
|
|
|
|
|
|
} |
|
590
|
4
|
|
|
|
|
9
|
|
|
591
|
4
|
|
|
|
|
41
|
my $lasthop = $self->_get_relay($scanner); |
|
592
|
4
|
|
|
|
|
9
|
if (!defined $lasthop) { |
|
593
|
4
|
|
|
|
|
9
|
dbg("spf: no suitable relay for spf use found, skipping SPF%s check", |
|
594
|
4
|
|
|
|
|
8
|
$ishelo ? '-helo' : ''); |
|
595
|
4
|
|
|
|
|
8
|
return; |
|
596
|
4
|
|
|
|
|
8
|
} |
|
597
|
4
|
|
|
|
|
7
|
|
|
598
|
4
|
|
|
|
|
10
|
my $ip = $lasthop->{ip}; # always present |
|
599
|
|
|
|
|
|
|
my $helo = $lasthop->{helo}; # could be missing |
|
600
|
|
|
|
|
|
|
$scanner->{sender} = '' unless $scanner->{sender_got}; |
|
601
|
4
|
|
|
|
|
10
|
|
|
602
|
4
|
|
|
|
|
9
|
if ($ishelo) { |
|
603
|
4
|
|
|
|
|
9
|
unless ($helo) { |
|
604
|
4
|
|
|
|
|
7
|
dbg("spf: cannot check HELO, HELO value unknown"); |
|
605
|
4
|
|
|
|
|
9
|
return; |
|
606
|
4
|
|
|
|
|
9
|
} |
|
607
|
4
|
|
|
|
|
8
|
dbg("spf: checking HELO (helo=$helo, ip=$ip)"); |
|
608
|
4
|
|
|
|
|
9
|
} else { |
|
609
|
4
|
|
|
|
|
9
|
$self->_get_sender($scanner) unless $scanner->{sender_got}; |
|
610
|
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
# TODO: we're supposed to use the helo domain as the sender identity (for |
|
612
|
8
|
|
|
|
|
23
|
# mfrom checks) if the sender is the null sender, however determining that |
|
613
|
8
|
50
|
|
|
|
19
|
# it's the null sender, and not just a failure to get the envelope isn't |
|
614
|
8
|
100
|
|
|
|
28
|
# exactly trivial... so for now we'll just skip the check |
|
615
|
|
|
|
|
|
|
|
|
616
|
8
|
|
|
|
|
16
|
if (!$scanner->{sender}) { |
|
617
|
|
|
|
|
|
|
# we already dbg'd that we couldn't get an Envelope-From and can't do SPF |
|
618
|
|
|
|
|
|
|
return; |
|
619
|
0
|
|
|
|
|
0
|
} |
|
620
|
0
|
|
|
|
|
0
|
dbg("spf: checking EnvelopeFrom (helo=%s, ip=%s, envfrom=%s)", |
|
621
|
0
|
0
|
|
|
|
0
|
($helo ? $helo : ''), $ip, $scanner->{sender}); |
|
622
|
|
|
|
|
|
|
} |
|
623
|
0
|
0
|
|
|
|
0
|
|
|
624
|
0
|
0
|
|
|
|
0
|
# this test could probably stand to be more strict, but try to test |
|
625
|
0
|
|
|
|
|
0
|
# any invalid HELO hostname formats with a header rule |
|
626
|
0
|
|
|
|
|
0
|
if ($ishelo && ($helo =~ /^[\[!]?\d+\.\d+\.\d+\.\d+[\]!]?$/ || $helo =~ /^[^.]+$/)) { |
|
627
|
|
|
|
|
|
|
dbg("spf: cannot check HELO of '$helo', skipping"); |
|
628
|
0
|
|
|
|
|
0
|
return; |
|
629
|
|
|
|
|
|
|
} |
|
630
|
0
|
0
|
|
|
|
0
|
|
|
631
|
|
|
|
|
|
|
if ($helo && $scanner->server_failed_to_respond_for_domain($helo)) { |
|
632
|
|
|
|
|
|
|
dbg("spf: we had a previous timeout on '$helo', skipping"); |
|
633
|
|
|
|
|
|
|
return; |
|
634
|
|
|
|
|
|
|
} |
|
635
|
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
|
|
637
|
0
|
0
|
|
|
|
0
|
my ($result, $comment, $text, $err); |
|
638
|
|
|
|
|
|
|
|
|
639
|
0
|
|
|
|
|
0
|
# use Mail::SPF if it was available, otherwise use the legacy Mail::SPF::Query |
|
640
|
|
|
|
|
|
|
if ($self->{has_mail_spf}) { |
|
641
|
|
|
|
|
|
|
|
|
642
|
0
|
0
|
|
|
|
0
|
# TODO: currently we won't get to here for a mfrom check with a null sender |
|
643
|
|
|
|
|
|
|
my $identity = $ishelo ? $helo : ($scanner->{sender}); # || $helo); |
|
644
|
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
unless ($identity) { |
|
646
|
|
|
|
|
|
|
dbg("spf: cannot determine %s identity, skipping %s SPF check", |
|
647
|
0
|
0
|
0
|
|
|
0
|
($ishelo ? 'helo' : 'mfrom'), ($ishelo ? 'helo' : 'mfrom') ); |
|
|
|
|
0
|
|
|
|
|
|
648
|
0
|
|
|
|
|
0
|
return; |
|
649
|
0
|
|
|
|
|
0
|
} |
|
650
|
|
|
|
|
|
|
$helo ||= 'unknown'; # only used for macro expansion in the mfrom explanation |
|
651
|
|
|
|
|
|
|
|
|
652
|
0
|
0
|
0
|
|
|
0
|
my $request; |
|
653
|
0
|
|
|
|
|
0
|
eval { |
|
654
|
0
|
|
|
|
|
0
|
$request = Mail::SPF::Request->new( scope => $ishelo ? 'helo' : 'mfrom', |
|
655
|
|
|
|
|
|
|
identity => $identity, |
|
656
|
|
|
|
|
|
|
ip_address => $ip, |
|
657
|
|
|
|
|
|
|
helo_identity => $helo ); |
|
658
|
0
|
|
|
|
|
0
|
1; |
|
659
|
|
|
|
|
|
|
} or do { |
|
660
|
|
|
|
|
|
|
my $eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat; |
|
661
|
0
|
0
|
|
|
|
0
|
dbg("spf: cannot create Mail::SPF::Request object: $eval_stat"); |
|
662
|
|
|
|
|
|
|
return; |
|
663
|
|
|
|
|
|
|
}; |
|
664
|
0
|
0
|
|
|
|
0
|
|
|
665
|
|
|
|
|
|
|
my $timeout = $scanner->{conf}->{spf_timeout}; |
|
666
|
0
|
0
|
|
|
|
0
|
|
|
667
|
0
|
0
|
|
|
|
0
|
my $timer = Mail::SpamAssassin::Timeout->new( |
|
|
|
0
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
{ secs => $timeout, deadline => $scanner->{master_deadline} }); |
|
669
|
0
|
|
|
|
|
0
|
$err = $timer->run_and_catch(sub { |
|
670
|
|
|
|
|
|
|
|
|
671
|
0
|
|
0
|
|
|
0
|
my $query = $self->{spf_server}->process($request); |
|
672
|
|
|
|
|
|
|
|
|
673
|
0
|
|
|
|
|
0
|
$result = $query->code; |
|
674
|
|
|
|
|
|
|
$comment = $query->authority_explanation if $query->can("authority_explanation"); |
|
675
|
0
|
0
|
|
|
|
0
|
$text = $query->text; |
|
676
|
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
}); |
|
678
|
|
|
|
|
|
|
|
|
679
|
0
|
|
|
|
|
0
|
|
|
680
|
0
|
0
|
|
|
|
0
|
} else { |
|
681
|
0
|
0
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
682
|
0
|
|
|
|
|
0
|
if (!$helo) { |
|
683
|
0
|
|
|
|
|
0
|
dbg("spf: cannot get HELO, cannot use Mail::SPF::Query, consider installing Mail::SPF"); |
|
684
|
|
|
|
|
|
|
return; |
|
685
|
|
|
|
|
|
|
} |
|
686
|
0
|
|
|
|
|
0
|
|
|
687
|
|
|
|
|
|
|
# TODO: if we start doing checks on the null sender using the helo domain |
|
688
|
|
|
|
|
|
|
# be sure to fix this so that it uses the correct sender identity |
|
689
|
0
|
|
|
|
|
0
|
my $query; |
|
690
|
|
|
|
|
|
|
eval { |
|
691
|
|
|
|
|
|
|
$query = Mail::SPF::Query->new (ip => $ip, |
|
692
|
0
|
|
|
0
|
|
0
|
sender => $scanner->{sender}, |
|
693
|
|
|
|
|
|
|
helo => $helo, |
|
694
|
0
|
|
|
|
|
0
|
debug => 0, |
|
695
|
0
|
0
|
|
|
|
0
|
trusted => 0); |
|
696
|
0
|
|
|
|
|
0
|
1; |
|
697
|
|
|
|
|
|
|
} or do { |
|
698
|
0
|
|
|
|
|
0
|
my $eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat; |
|
699
|
|
|
|
|
|
|
dbg("spf: cannot create Mail::SPF::Query object: $eval_stat"); |
|
700
|
|
|
|
|
|
|
return; |
|
701
|
|
|
|
|
|
|
}; |
|
702
|
|
|
|
|
|
|
|
|
703
|
0
|
0
|
|
|
|
0
|
my $timeout = $scanner->{conf}->{spf_timeout}; |
|
704
|
0
|
|
|
|
|
0
|
|
|
705
|
0
|
|
|
|
|
0
|
my $timer = Mail::SpamAssassin::Timeout->new( |
|
706
|
|
|
|
|
|
|
{ secs => $timeout, deadline => $scanner->{master_deadline} }); |
|
707
|
|
|
|
|
|
|
$err = $timer->run_and_catch(sub { |
|
708
|
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
($result, $comment) = $query->result(); |
|
710
|
0
|
|
|
|
|
0
|
|
|
711
|
|
|
|
|
|
|
}); |
|
712
|
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
} # end of differences between Mail::SPF and Mail::SPF::Query |
|
714
|
0
|
|
|
|
|
0
|
|
|
715
|
|
|
|
|
|
|
if ($err) { |
|
716
|
|
|
|
|
|
|
chomp $err; |
|
717
|
0
|
|
|
|
|
0
|
warn("spf: lookup failed: $err\n"); |
|
718
|
0
|
0
|
|
|
|
0
|
return 0; |
|
719
|
0
|
0
|
|
|
|
0
|
} |
|
|
0
|
|
|
|
|
0
|
|
|
720
|
0
|
|
|
|
|
0
|
|
|
721
|
0
|
|
|
|
|
0
|
|
|
722
|
|
|
|
|
|
|
$result ||= 'timeout'; # bug 5077 |
|
723
|
|
|
|
|
|
|
$comment ||= ''; |
|
724
|
0
|
|
|
|
|
0
|
$comment =~ s/\s+/ /gs; # no newlines please |
|
725
|
|
|
|
|
|
|
$text ||= ''; |
|
726
|
|
|
|
|
|
|
$text =~ s/\s+/ /gs; # no newlines please |
|
727
|
0
|
|
|
|
|
0
|
|
|
728
|
|
|
|
|
|
|
if ($ishelo) { |
|
729
|
|
|
|
|
|
|
if ($result eq 'pass') { $scanner->{spf_helo_pass} = 1; } |
|
730
|
0
|
|
|
0
|
|
0
|
elsif ($result eq 'neutral') { $scanner->{spf_helo_neutral} = 1; } |
|
731
|
|
|
|
|
|
|
elsif ($result eq 'none') { $scanner->{spf_helo_none} = 1; } |
|
732
|
0
|
|
|
|
|
0
|
elsif ($result eq 'fail') { $scanner->{spf_helo_fail} = 1; } |
|
733
|
|
|
|
|
|
|
elsif ($result eq 'softfail') { $scanner->{spf_helo_softfail} = 1; } |
|
734
|
|
|
|
|
|
|
elsif ($result eq 'permerror') { $scanner->{spf_helo_permerror} = 1; } |
|
735
|
|
|
|
|
|
|
elsif ($result eq 'temperror') { $scanner->{spf_helo_temperror} = 1; } |
|
736
|
0
|
0
|
|
|
|
0
|
elsif ($result eq 'error') { $scanner->{spf_helo_temperror} = 1; } |
|
737
|
0
|
|
|
|
|
0
|
|
|
738
|
0
|
|
|
|
|
0
|
if ($result eq 'fail') { # RFC 7208 6.2 |
|
739
|
0
|
|
|
|
|
0
|
$scanner->{spf_helo_failure_comment} = "SPF failed: $comment"; |
|
740
|
|
|
|
|
|
|
} |
|
741
|
|
|
|
|
|
|
} else { |
|
742
|
|
|
|
|
|
|
if ($result eq 'pass') { $scanner->{spf_pass} = 1; } |
|
743
|
0
|
|
0
|
|
|
0
|
elsif ($result eq 'neutral') { $scanner->{spf_neutral} = 1; } |
|
744
|
0
|
|
0
|
|
|
0
|
elsif ($result eq 'none') { $scanner->{spf_none} = 1; } |
|
745
|
0
|
|
|
|
|
0
|
elsif ($result eq 'fail') { $scanner->{spf_fail} = 1; } |
|
746
|
0
|
|
0
|
|
|
0
|
elsif ($result eq 'softfail') { $scanner->{spf_softfail} = 1; } |
|
747
|
0
|
|
|
|
|
0
|
elsif ($result eq 'permerror') { $scanner->{spf_permerror} = 1; } |
|
748
|
|
|
|
|
|
|
elsif ($result eq 'temperror') { $scanner->{spf_temperror} = 1; } |
|
749
|
0
|
0
|
|
|
|
0
|
elsif ($result eq 'error') { $scanner->{spf_temperror} = 1; } |
|
750
|
0
|
0
|
|
|
|
0
|
|
|
|
0
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
751
|
0
|
|
|
|
|
0
|
if ($result eq 'fail') { # RFC 7208 6.2 |
|
752
|
0
|
|
|
|
|
0
|
$scanner->{spf_failure_comment} = "SPF failed: $comment"; |
|
753
|
0
|
|
|
|
|
0
|
} |
|
754
|
0
|
|
|
|
|
0
|
} |
|
755
|
0
|
|
|
|
|
0
|
|
|
756
|
0
|
|
|
|
|
0
|
dbg("spf: query for $scanner->{sender}/$ip/$helo: result: $result, comment: $comment, text: $text"); |
|
757
|
0
|
|
|
|
|
0
|
} |
|
758
|
|
|
|
|
|
|
|
|
759
|
0
|
0
|
|
|
|
0
|
my ($self, $scanner) = @_; |
|
760
|
0
|
|
|
|
|
0
|
|
|
761
|
|
|
|
|
|
|
# dos: first external relay, not first untrusted |
|
762
|
|
|
|
|
|
|
return $scanner->{relays_external}->[0]; |
|
763
|
0
|
0
|
|
|
|
0
|
} |
|
|
0
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
764
|
0
|
|
|
|
|
0
|
|
|
765
|
0
|
|
|
|
|
0
|
my ($self, $scanner) = @_; |
|
766
|
0
|
|
|
|
|
0
|
my $sender; |
|
767
|
0
|
|
|
|
|
0
|
|
|
768
|
0
|
|
|
|
|
0
|
$scanner->{sender_got} = 1; |
|
769
|
0
|
|
|
|
|
0
|
$scanner->{sender} = ''; |
|
770
|
0
|
|
|
|
|
0
|
|
|
771
|
|
|
|
|
|
|
my $relay = $self->_get_relay($scanner); |
|
772
|
0
|
0
|
|
|
|
0
|
if (defined $relay) { |
|
773
|
0
|
|
|
|
|
0
|
$sender = $relay->{envfrom}; |
|
774
|
|
|
|
|
|
|
} |
|
775
|
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
if ($sender) { |
|
777
|
0
|
|
|
|
|
0
|
dbg("spf: found Envelope-From in first external Received header"); |
|
778
|
|
|
|
|
|
|
} |
|
779
|
|
|
|
|
|
|
else { |
|
780
|
|
|
|
|
|
|
# We cannot use the env-from data, since it went through 1 or more relays |
|
781
|
85
|
|
|
85
|
|
192
|
# since the untrusted sender and they may have rewritten it. |
|
782
|
|
|
|
|
|
|
if ($scanner->{num_relays_trusted} > 0 && !$scanner->{conf}->{always_trust_envelope_sender}) { |
|
783
|
|
|
|
|
|
|
dbg("spf: relayed through one or more trusted relays, cannot use header-based Envelope-From, skipping"); |
|
784
|
85
|
|
|
|
|
217
|
return; |
|
785
|
|
|
|
|
|
|
} |
|
786
|
|
|
|
|
|
|
|
|
787
|
|
|
|
|
|
|
# we can (apparently) use whatever the current Envelope-From was, |
|
788
|
77
|
|
|
77
|
|
169
|
# from the Return-Path, X-Envelope-From, or whatever header. |
|
789
|
77
|
|
|
|
|
157
|
# it's better to get it from Received though, as that is updated |
|
790
|
|
|
|
|
|
|
# hop-by-hop. |
|
791
|
77
|
|
|
|
|
209
|
$sender = $scanner->get("EnvelopeFrom:addr"); |
|
792
|
77
|
|
|
|
|
199
|
} |
|
793
|
|
|
|
|
|
|
|
|
794
|
77
|
|
|
|
|
246
|
if (!$sender) { |
|
795
|
77
|
100
|
|
|
|
186
|
dbg("spf: cannot get Envelope-From, cannot use SPF"); |
|
796
|
14
|
|
|
|
|
34
|
return; # avoid setting $scanner->{sender} to undef |
|
797
|
|
|
|
|
|
|
} |
|
798
|
|
|
|
|
|
|
|
|
799
|
77
|
50
|
|
|
|
203
|
return $scanner->{sender} = lc $sender; |
|
800
|
0
|
|
|
|
|
0
|
} |
|
801
|
|
|
|
|
|
|
|
|
802
|
|
|
|
|
|
|
my ($self, $scanner) = @_; |
|
803
|
|
|
|
|
|
|
|
|
804
|
|
|
|
|
|
|
$scanner->{spf_whitelist_from_checked} = 1; |
|
805
|
77
|
100
|
66
|
|
|
377
|
$scanner->{spf_whitelist_from} = 0; |
|
806
|
28
|
|
|
|
|
99
|
|
|
807
|
28
|
|
|
|
|
55
|
# if we've already checked for an SPF PASS and didn't get it don't waste time |
|
808
|
|
|
|
|
|
|
# checking to see if the sender address is in the spf whitelist |
|
809
|
|
|
|
|
|
|
if ($scanner->{spf_checked} && !$scanner->{spf_pass}) { |
|
810
|
|
|
|
|
|
|
dbg("spf: whitelist_from_spf: already checked spf and didn't get pass, skipping whitelist check"); |
|
811
|
|
|
|
|
|
|
return; |
|
812
|
|
|
|
|
|
|
} |
|
813
|
|
|
|
|
|
|
|
|
814
|
49
|
|
|
|
|
157
|
$self->_get_sender($scanner) unless $scanner->{sender_got}; |
|
815
|
|
|
|
|
|
|
|
|
816
|
|
|
|
|
|
|
unless ($scanner->{sender}) { |
|
817
|
49
|
100
|
|
|
|
177
|
dbg("spf: spf_whitelist_from: could not find usable envelope sender"); |
|
818
|
48
|
|
|
|
|
184
|
return; |
|
819
|
48
|
|
|
|
|
108
|
} |
|
820
|
|
|
|
|
|
|
|
|
821
|
|
|
|
|
|
|
$scanner->{spf_whitelist_from} = $self->_wlcheck($scanner,'whitelist_from_spf'); |
|
822
|
1
|
|
|
|
|
7
|
if (!$scanner->{spf_whitelist_from}) { |
|
823
|
|
|
|
|
|
|
$scanner->{spf_whitelist_from} = $self->_wlcheck($scanner, 'whitelist_auth'); |
|
824
|
|
|
|
|
|
|
} |
|
825
|
|
|
|
|
|
|
|
|
826
|
81
|
|
|
81
|
|
161
|
# if the message doesn't pass SPF validation, it can't pass an SPF whitelist |
|
827
|
|
|
|
|
|
|
if ($scanner->{spf_whitelist_from}) { |
|
828
|
81
|
|
|
|
|
180
|
if ($self->check_for_spf_pass($scanner)) { |
|
829
|
81
|
|
|
|
|
192
|
dbg("spf: whitelist_from_spf: $scanner->{sender} is in user's WHITELIST_FROM_SPF and passed SPF check"); |
|
830
|
|
|
|
|
|
|
} else { |
|
831
|
|
|
|
|
|
|
dbg("spf: whitelist_from_spf: $scanner->{sender} is in user's WHITELIST_FROM_SPF but failed SPF check"); |
|
832
|
|
|
|
|
|
|
$scanner->{spf_whitelist_from} = 0; |
|
833
|
81
|
100
|
66
|
|
|
251
|
} |
|
834
|
4
|
|
|
|
|
15
|
} else { |
|
835
|
4
|
|
|
|
|
8
|
dbg("spf: whitelist_from_spf: $scanner->{sender} is not in user's WHITELIST_FROM_SPF"); |
|
836
|
|
|
|
|
|
|
} |
|
837
|
|
|
|
|
|
|
} |
|
838
|
77
|
100
|
|
|
|
273
|
|
|
839
|
|
|
|
|
|
|
my ($self, $scanner) = @_; |
|
840
|
77
|
100
|
|
|
|
206
|
|
|
841
|
76
|
|
|
|
|
210
|
$scanner->{def_spf_whitelist_from_checked} = 1; |
|
842
|
76
|
|
|
|
|
225
|
$scanner->{def_spf_whitelist_from} = 0; |
|
843
|
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
# if we've already checked for an SPF PASS and didn't get it don't waste time |
|
845
|
1
|
|
|
|
|
6
|
# checking to see if the sender address is in the spf whitelist |
|
846
|
1
|
50
|
|
|
|
4
|
if ($scanner->{spf_checked} && !$scanner->{spf_pass}) { |
|
847
|
1
|
|
|
|
|
3
|
dbg("spf: def_spf_whitelist_from: already checked spf and didn't get pass, skipping whitelist check"); |
|
848
|
|
|
|
|
|
|
return; |
|
849
|
|
|
|
|
|
|
} |
|
850
|
|
|
|
|
|
|
|
|
851
|
1
|
50
|
|
|
|
4
|
$self->_get_sender($scanner) unless $scanner->{sender_got}; |
|
852
|
0
|
0
|
|
|
|
0
|
|
|
853
|
0
|
|
|
|
|
0
|
unless ($scanner->{sender}) { |
|
854
|
|
|
|
|
|
|
dbg("spf: def_spf_whitelist_from: could not find usable envelope sender"); |
|
855
|
0
|
|
|
|
|
0
|
return; |
|
856
|
0
|
|
|
|
|
0
|
} |
|
857
|
|
|
|
|
|
|
|
|
858
|
|
|
|
|
|
|
$scanner->{def_spf_whitelist_from} = $self->_wlcheck($scanner,'def_whitelist_from_spf'); |
|
859
|
1
|
|
|
|
|
9
|
if (!$scanner->{def_spf_whitelist_from}) { |
|
860
|
|
|
|
|
|
|
$scanner->{def_spf_whitelist_from} = $self->_wlcheck($scanner, 'def_whitelist_auth'); |
|
861
|
|
|
|
|
|
|
} |
|
862
|
|
|
|
|
|
|
|
|
863
|
|
|
|
|
|
|
# if the message doesn't pass SPF validation, it can't pass an SPF whitelist |
|
864
|
81
|
|
|
81
|
|
195
|
if ($scanner->{def_spf_whitelist_from}) { |
|
865
|
|
|
|
|
|
|
if ($self->check_for_spf_pass($scanner)) { |
|
866
|
81
|
|
|
|
|
168
|
dbg("spf: def_whitelist_from_spf: $scanner->{sender} is in DEF_WHITELIST_FROM_SPF and passed SPF check"); |
|
867
|
81
|
|
|
|
|
160
|
} else { |
|
868
|
|
|
|
|
|
|
dbg("spf: def_whitelist_from_spf: $scanner->{sender} is in DEF_WHITELIST_FROM_SPF but failed SPF check"); |
|
869
|
|
|
|
|
|
|
$scanner->{def_spf_whitelist_from} = 0; |
|
870
|
|
|
|
|
|
|
} |
|
871
|
81
|
100
|
66
|
|
|
258
|
} else { |
|
872
|
4
|
|
|
|
|
15
|
dbg("spf: def_whitelist_from_spf: $scanner->{sender} is not in DEF_WHITELIST_FROM_SPF"); |
|
873
|
4
|
|
|
|
|
11
|
} |
|
874
|
|
|
|
|
|
|
} |
|
875
|
|
|
|
|
|
|
|
|
876
|
77
|
100
|
|
|
|
275
|
my ($self, $scanner, $param) = @_; |
|
877
|
|
|
|
|
|
|
if (defined ($scanner->{conf}->{$param}->{$scanner->{sender}})) { |
|
878
|
77
|
100
|
|
|
|
288
|
return 1; |
|
879
|
76
|
|
|
|
|
202
|
} else { |
|
880
|
76
|
|
|
|
|
119
|
study $scanner->{sender}; # study is a no-op since perl 5.16.0 |
|
881
|
|
|
|
|
|
|
foreach my $regexp (values %{$scanner->{conf}->{$param}}) { |
|
882
|
|
|
|
|
|
|
if ($scanner->{sender} =~ qr/$regexp/i) { |
|
883
|
1
|
|
|
|
|
4
|
return 1; |
|
884
|
1
|
50
|
|
|
|
3
|
} |
|
885
|
1
|
|
|
|
|
3
|
} |
|
886
|
|
|
|
|
|
|
} |
|
887
|
|
|
|
|
|
|
return 0; |
|
888
|
|
|
|
|
|
|
} |
|
889
|
1
|
50
|
|
|
|
4
|
|
|
890
|
0
|
0
|
|
|
|
0
|
########################################################################### |
|
891
|
0
|
|
|
|
|
0
|
|
|
892
|
|
|
|
|
|
|
1; |
|
893
|
0
|
|
|
|
|
0
|
|
|
894
|
0
|
|
|
|
|
0
|
=back |
|
895
|
|
|
|
|
|
|
|
|
896
|
|
|
|
|
|
|
=cut |