-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathqmail-lint-0.55
369 lines (337 loc) · 13.2 KB
/
qmail-lint-0.55
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
#!/usr/bin/perl
# examine the qmail configuration to see if any common errors exist.
# usage: qmail-lint [-v]
# -v -- print an explanation of the reports the first time one is printed.
# Copyright 1999, Russell Nelson <[email protected]> for publication
# in the forthcoming O'Reilly & Associates book on qmail. Permission
# to redistribute in unmodified or modified form granted subject to
# the condition that this notice be retained, and modifications be
# identified as such.
# See http://www.qmail.org for updates to this program.
# CONFIGURATION:
$qmail = "/var/qmail"; # conf-qmail
$auto_patrn = 002; # conf-patrn
# CHANGES:
#Jan 25 1999 released 0.50: First public release
# Contributions from Andrea Paolini <[email protected]>:
# 1. Skip comments in virtualdomains.
# 2. Strip username from virtualdomains when checking if hostnames are
# defined in rcpthosts.
# 3. Correct misspelling (?). Changed "=~ split(/@/)" to "= split(/@/)";
# 4. Permit to mix in control/virtualdomains generic virtual domains and
# virtual users for the same domain.
#Jan 26 1999 release 0.51
# Keith Burdis <[email protected]> suggested ignoring blank lines in vdoms.
# Peter Haworth <[email protected]> caught an error in verbose().
#Feb 21 1999 release 0.52
# Check for a dot in a .qmail filename.
#Apr 20 1999 release 0.53
# Make sure that queue/lock/trigger has the right permissions.
#Oct 15 1999 release 0.54
# Giles Lean <[email protected]> suggested ignoring a null LHS in vdoms.
#Jan 8 2000 release 0.55
use Getopt::Std;
getopts('v');
die "usage: qmail-lint [-v]\n" if @ARGV;
# if the -v option is set, print the verbose explanation.
sub verbose {
return unless $opt_v;
my $name = shift;
return if $verbose_printed{$name}++;
seek(DATA,0,0);
while(<DATA>) {
next unless m/^$name/;
while(<DATA>) {
last unless /^ /;
print $_;
}
}
}
chdir($qmail) or die;
# read control/locals
if (open(F, "<control/locals")) {
while(<F>) {
chomp;
push(@locals,$_);
next if m"^#";
$locals{$_} = "";
}
} else {
print "Warning: No hosts are considered local. You will be unable to receive mail here.\n";
&verbose("NHL");
}
# read control/virtualdomains
if (open(F, "<control/virtualdomains")) {
while(<F>) {
chomp;
push(@virtualdomains,$_);
next if m"^#" or m/^\s*$/;
if (split(/:/) < 2) {
print "Warning: Line $. in control/virtualdomains has no colon:\n";
print " $_\n";
&verbose(NOCOLON);
}
if (defined($virtualdomains{$_[0]})) {
if ($virtualdomains{$_[0]} != $_[1]) {
print "Error: Line $. in control/virtualdomains redefines $_[0] to a different value:\n";
print " $_\n";
&verbose(TWOVDOMS);
} else {
print "Warning: Line $. in control/virtualdomains redefines $_[0]:\n";
print " $_\n";
&verbose(TWODOMSDIFF);
}
}
$virtualdomains{$_[0]} = $_[1];
}
}
# double-plus ungood if they have no rcpthosts file
if (open(F, "<control/rcpthosts")) {
while(<F>) {
chomp;
next if m"^#";
$rcpthosts{$_} = "";
}
} else {
print "Warning: Without a control/rcpthosts file, you are open to relay abuse by spammers.\n";
&verbose(NORCPTHOSTS);
}
# it's cool if they don't have a morercpthosts file.
if (open(F, "<control/morercpthosts")) {
while(<F>) {
chomp;
next if m"^#";
$rcpthosts{$_} = "";
}
}
# a host in locals AND in virtualdomains. This is beyond the pale.
if (%virtualdomains) {
foreach (@locals) {
if (defined($virtualdomains{$_})) {
print "Error: a host $_ in locals also appears in virtualdomains.\n";
&verbose(LOCALVDOM);
}
}
}
# A host in locals but not in rcpthosts. How odd.
if (%rcpthosts) {
foreach (@locals) {
next if m"^#";
next if defined($rcpthosts{$_});
s/^.*?\././;
do {
last if defined($rcpthosts{$_});
} while (s/^\. # starting with a dot,
.*? # followed by anything
( \. | $ ) # followed by a dot or end of line,
/$1/x); # replace it with the dot or end of line part.
next if defined($rcpthosts{$_});
print "Warning: a host $_ in locals does not appear in rcpthosts.\n";
&verbose(LOCALNORCPT);
}
}
# A host in virtualdomains but not in rcpthosts. How odd.
if (%rcpthosts) {
foreach (@virtualdomains) {
next if m"^#";
($host, $redirect) = split(/:/);
$_ = $host;
$_ =~ s/^.+@//; # Strip username
next if defined($rcpthosts{$_});
s/^.*?\././;
do {
last if defined($rcpthosts{$_});
} while (s/^\..*? ( \. | $ ) /$1/x);
next if defined($rcpthosts{$_});
next if $_ eq '';
print "Warning: a host $host in virtualdomains does not appear in rcpthosts.\n";
&verbose(LOCALNORCPT);
}
}
# a host named in a USER@HOST:MAP virtualdomains line should be remote, not local or virtual.
foreach (keys %virtualdomains) {
($user, $host) = split(/@/);
next unless $user and $host;
if (defined($locals{$host})) {
print "Warning: a host named in a USER\@HOST:MAP virtualdomains line should be remote, not local.\n";
print "$_\n";
&verbose(LOCALUSERHOSTMAP);
&verbose(USERHOSTMAP);
}
if (defined($virtualdomains{$host})) {
print "Warning: a host named in a USER\@HOST:MAP virtualdomains line should be remote, not virtual.\n";
print "$_\n";
&verbose(VDOMUSERHOSTMAP);
&verbose(USERHOSTMAP);
}
}
if (-f "users/assign") {
print "Warning: users/assign checking not implemented.\n";
exit;
}
# Read all of alias's .qmail files
opendir(D, "$qmail/alias") or die;
while($_ = readdir(D)) {
next unless /^\.qmail-(.*)/;
$alias = $1;
$aliases{$alias} = "$_" while $alias =~ s/-.*?//;
}
closedir(D);
# the biggest file is /etc/passwd, so we deal with it one line at a time rather
# than reading the whole thing into memory.
while(($name, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell) = getpwent) {
($homemode, $homeuid) = (stat($dir))[2,4];
# print all the users who cannot receive email. We use a heuristic that skips reporting
# any users until we've found one who can. This skips the system accounts.
if ($uid == 0) {
# ignore root.
} elsif ($dir eq $qmail) {
# ignore qmail users.
} elsif ($homeuid != $uid) {
if ($foundhomedir) {
print "Warning: cannot receive mail (does not own home directory): $name\n";
&verbose(NOOWNHOME);
}
} else {
$foundhomedir = 1;
}
# Warn about home directories that don't match conf-patrn.
if ($homemode & $auto_patrn) {
print "Warning: cannot receive mail (home directory writable by others): $name\n";
&verbose(BADHOMEPERMS);
}
# Warn about .qmail files that don't match conf-patrn.
if (opendir(D, "$dir")) {
while($_ = readdir(D)) {
next unless /^\.qmail/;
if (m/[A-Z]/) {
print "Warning: qmail-local ignores .qmail files with upper-case characters: ~$name/$_\n";
&verbose(BADQMAILCASE);
}
if (m/^\..*\./) {
print "Warning: qmail-local replaces dot with a colon in .qmail filenames: ~$name/$_\n";
&verbose(BADQMAILDOT);
}
$mode = (stat($_))[2];
if ($mode & $auto_patrn) {
print "Warning: cannot receive mail (.qmail writable by others): ~$name/$_\n";
&verbose(BADQMAILPERMS);
}
}
closedir(D);
}
# Remember if this user is the target of a virtualdomains match
foreach $domain (keys %virtualdomains) {
$virtualdomains_userok{$domain} = 1 if $virtualdomains{$domain} =~ m/^$name($|-)/;
}
# Make sure that there are no addresses in ~alias which appear to match valid usernames.
if (defined($aliases{$name})) {
print "Warning: ~alias/$aliases{$name} matches an actual username and will be ignored.\n";
&verbose(ALIASVSUSER);
}
}
# report on the virtualdomains whose targets are not users
foreach $domain (keys %virtualdomains) {
next if $virtualdomains_userok{$domain};
print "Warning: delivery of the virtual domain $domain is implicitly controlled by alias\n";
&verbose(IMPLICITVDOM);
}
# make sure trigger isn't broken.
$mode = (stat("$qmail/queue/lock/trigger"))[2];
if ($mode != 010622) {
print "Error: $qmail/queue/lock/trigger does not have prw--w--w- permissions\n";
&verbose(TRIGGER);
}
__END__
" # GNU Emacs is sooooooo dumb about perl syntax.
NHL
If you don't list any hosts in control/locals, no mail will ever be
considered local. qmail-local will never be run, and no mail will be
stored in mailboxes
NOCOLON
qmail-send ignores lines in control/virtualdomains if they have no colon.
If you really must have such a line, consider putting a hash mark (#) in
front of the line to make it clear that the line is just a comment.
TWOVDOMS
No damage is done by having multiple identical entries for the same domain.
It can cause confusion if the entry needs to be changed, and only one of
them is changed.
TWODOMSDIFF
No damage is done by having multiple identical entries for the same domain.
It can cause confusion if the entry needs to be changed, and only one of
them is changed.
NORCPTHOSTS
Qmail-smtpd uses control/rcpthosts to determine which hosts it will receive
mail for. Typically, this includes any host mentioned in locals or
virtualdomains. It also includes any hosts for which you are a secondary
MX. See http://qmail-docs.surfdirect.com.au/docs/qmail-antirelay.html
LOCALVDOM
Qmail-send examines control/locals before it examines control/virtualdomains.
Therefore, if a host is in locals, it will never be considered virtual.
If you're trying to create a virtual domain, remove the host from
control/locals.
LOCALNORCPT
If a host is not in rcpthosts, and rcpthosts exists, then qmail-smtpd will
not be able to receive mail from the Internet at large. It will only
receive mail from hosts which set RELAYCLIENTS, or mail generated on
this host.
LOCALUSERHOSTMAP
There is no reason or need to use the virtualdomains USER@HOST:MAP
capability when the host is already local. Use a .qmail file in the user's
home directory or in alias's home directory, as needed.
VDOMUSERHOSTMAP
There is no reason or need to use the virtualdomains USER@HOST:MAP
capability when the host is already virtual. Use a .qmail file in the
controlling user's home directory.
USERHOSTMAP
The virtualdomains USER@HOST:MAP capability is designed to allow you to
short-circuit mail delivery. If you're sending email to an address on a
host which is not this host, but which will eventually be delivered to this
host, then you should use the USER@HOST:MAP capability.
NOOWNHOME
qmail-getpw makes several tests to see if it can deliver mail to a
particular address. First, it searches through /etc/passwd to see if the
full local part exists. If not, it strips off dash-separated subparts and
searches. When it finds a match, it checks to see if that user owns their
home directory. If not, it keeps looking. This prevents mail deliveries to
system userids. This particular user doesn't own their home directory,
so qmail-getpw will skip over their name when attempting to deliver mail.
BADHOMEPERMS
If other users on the system can write to a user's home directory, then the
user has no security at all. Anyone can run any commands as that user
simply by creating an appropriate .qmail file and sending mail to it. To
prevent such a security give-away, qmail checks the mode bits stored in
conf-patrn, and defers delivery as long as the home directory is writable.
BADQMAILPERMS
If other users on the system can write to any of a user's .qmail files, then
the user has no security at all. Anyone can run any commands as that user
simply by modifying the appropriate .qmail file and sending mail to it. To
prevent such a security give-away, qmail checks the mode bits stored in
conf-patrn, and defers delivery as long as the .qmail file is writable.
BADQMAILCASE
qmail-local maps upper-case letters to lower-case before searching for a matching
.qmail file. It will never find a .qmail file containing upper-case characters.
BADQMAILDOT
qmail-local converts a dot into a colon before searching for a matching
.qmail file. It will never find a .qmail file with a dot in it (other than
the first dot in .qmail, of course). It does this to avoid the "../" security
hole. Rather than search for ../ and replace it by something more innocuous,
it uses the simpler (and more reliable) replacement of the single character
dot with a colon. A colon can never be in the local part of an email
address, since it's parsed as an email group indicator.
ALIASVSUSER
Users always control their own mail delivery when you are not using
users/assign. So, if an .qmail file in ~alias resembles a username, it will
never be used. If you have a user named joe, then ~alias/.qmail-joe will
never be consulted. Instead, ~joe/.qmail will be used. Similarly,
~alias/.qmail-joe-mike will also not be used. Instead, ~joe/.qmail-mike
will be used.
IMPLICITVDOM
The right-hand-side of a control/virtualdomains entry is used to decide
which user controls delivery of the virtualdomain. If that right-hand-side
does not correspond to an actual user, then alias will be implicitly used.
This carries the risk that a user matching the alias will later be created.
It's better to explicitly say "alias-vdom", rather than let the fact that no
user matches vdom cause alias to control the mail.
TRIGGER
queue/lock/trigger must be a fifo with these permissions: prw--w--w-