Sendmail Tips
This is a quick document on how to get yourself out of some binds I've run into with sendmail. These aren't going to be the most organized, but I figure someone else may find these useful.
Mail Spool
Sendmail generally stores its queued mail in the /var/spool/mqueue folder. Queued messages are messages that have not reached their final destination yet. There are two files associated with each e-mail message, one beginning with qf, the other beginning df. qf files contain the message header, df files contain the body of the mail message. You can tell which files are tied together because they have the same ending; for example the header file qfPAA22846 has an associated "body" file named dfPAA22846.
Stopping/Starting Sendmail
Depending on which type of OS you are running on, here's some of the ways you may start/stop sendmail:
Linux
/etc/init.d/sendmail stop | startFreeBSD
cd /etc/mail make stop | startSolaris
/etc/rc2.d/S88sendmail stopYou'll generally want to let sendmail slowly come to a halt -- it tries to stop its connections gracefully. When you're being mail bombed you might want to be a little more insistant on killing off sendmail. Note that people local to the machine (e.g. pine users and certain cgi-bin programs) may still be able to put their new messages into mqueue for delivery even while sendmail is stopped.
Large Mail Spool
If sendmail is having problems, after stopping it you might want to move the current queue directory to someplace else, to be processed specially. This is simple enough --
mv /var/spool/mqueue /var/spool/mqueue-fixme mkdir /var/spool/mqueue chmod 755 /var/spool/mqueue chown root:daemon /var/spool/mqueueCleaning the Mail Spool
There could be a lot of "trash" qf or df files left behind following a bout of misbehaving sendmails. If the size of either file is 0, you should be able to trash them safely.
cd /var/spool/mqueue-fixmeto get into your queue directory, and
find . -size 0 -exec rm {} \;to find everything with a size of 0 and execute the remove command on the found files. This is unwise to run in your default queue directory, even when sendmail is stopped.
Perhaps a sleazy spammer, cretinous chain-mailer, naughty Novell server, or administrator accidentally sending out e-mail to everyone is the cause of your mail woes. You can pull these messages from the mail queue thusly:
cd /var/spool/mqueue-fixme mkdir /var/tmp/EVIDENCE-OF-ILLDOING grep idiot@wherever qf* | cut -d":" -f1 | uniq | cut -d"f" -f2- | \ xargs -i echo "mv *{} /var/tmp/EVIDENCE-OF-ILLDOING" > RUNME chmod 700 RUNME ./RUNMEA sendmail problem could result in lots of "qf" files with no corresponding "df" file, and vice versa. You can get rid of these unmatched files with:
cd /var/spool/mqueue-fixme ls -1 | cut -c 3-16 | sort | uniq -c | sort -n | grep " 1 " | awk '{print "*"$2}' > ~/rmfileThen, execute this perl script OR bash script:
#!/usr/local/bin/perl use strict; open (RMFILE, "rmfile"); while (my $line=) { chomp $line; print $line."\n"; system ("rm /var/spool/mqueue/$line"); } close (RMFILE); OR
for i in $(cat rmfile) do rm -rf "/var/spool/mqueue/$i" doneI realize that this is not pretty, but it was the best that I could come up with since echo and xargs were not cooperating with me whatsoever.
Parsing an Alternate Mail Spool
To process mail lurking in an alternate queue, you can run sendmail manually. For example, to see exactly what happens:
sendmail -oQ/var/spool/mqueue-fixme -q -vwill process the mail queue (-q) located in /var/spool/mqueue-fixme (-oQ/var/spool/mqueue-fixme) verbosely one message at a time (-v). This should produce something like the following:
Running /home/rnejdl/var/spool/mqueue/i05HgAGV098325 (sequence 3352 of 4476) <7868@7218.j8j87hyrf.com>... Connecting to 7218.j8j87hyrf.com. via esmtp...Filtering Bad HELO's
by Neil W Rickert <rickert+nn@cs.niu.edu>
There has been recent discussion on bad HELO parameters. I have been experimenting with the rules below. Use at your own risk. They may require some local changes. You can put at the top of "Local_check_mail". In my case, I am using `delay_checks' so I put them in a "check_mail" ruleset. They should not be in "Local_check_relay" because you would want to first know if the client has authenticated. Basic strategy: Allow any HELO parameter if: The client authenticated, or the client is one of ours (its IP address begins with a component in class $=R). Else reject if the HELO hostname is our own name or our own IP address (tested against $=w), or if the HELO hostname is unqualified (no "." in the middle). This is a conservative check. I don't require that the HELO parameter match the sending hostname -- only that it not match our name and be syntactically valid at least to the extent of containing a ".". I have been testing this afternoon. So far it has only rejected one message that didn't look obviousl spammish. But if the client says HELO LIILXQMAIL01 I am not going to shed any tears over blocking that message. I suspect (from recent history) that this is to a "User unknown" and the sender never cleans up its mailing lists. Limitations: This might not stop a lot of spam. It is blocking about 1 message every 4 minutes. But most of those would have been blocked anyway. It is blocking andviruses, and some other viruses. It is blocking a lot of asian mail that I was probably blocked before. So far I haven't seen any 200.* addresses blocked. The spammers will probably retune their spamware, so within a few months this might stop having much effect. I check for SASL authentication, but not for SSL/client certificate authentication. If you use that, you will need to make changes. I check for the connecting client having an IP that starts in $=R. I do not check for the client hostname ending in $=R. If that is important, you may need to add a line or two. There is no provision to whitelist particular clients. If you need that, you will need to add rules that do an access lookup. Also read the comments in the m4 rules source below (they begin "dnl`'"). Note that what this will mainly do for me (until spammers retool) is reduce the amount of spam sent to postmaster. With `delay_checks' I was exempting postmaster from blocking. But I have placed these rules where they will reject mail prior to that exempting. # helo/ehlo checks of $s dnl`'Rationale: dnl`'Client software is often broken. We don't want to reject dnl`'our own users client connections. Therefore we attempt dnl`'to allow our users to pass the checks. Otherwise, block dnl`'sites with a HELO/EHLO hostname that is unqualified, or dnl`'is one of our own names dnl`' dnl`'Note that I had to at "127.0.0.1" to class $=R, so that dnl`'local client software would bypass these tests. I also dnl`'added "[127.0.0.1]" to class $=w, so that the localhost dnl`'IP would count as one of our IPs. dnl`' R$* $:$1 $| <$&{auth_authen}> Check if authenticated dnl`'Bypass the test for users who have authenticated. R$* $| <$+> $:$1 skip if auth R$* $| <$*> $:$1$|<$&{client_addr}>[$&s] Get connection info dnl`'Bypass for local clients -- IP address starts with $=R R$* $| <$=R $*>[$*] $:$1 skip if local client dnl`'Bypass a "sendmail -bs" session, which use 0 for client ip address R$* $| <0>[$*] $:$1 skip if sendmail -bs dnl`'Reject our IP - assumes "[ip]" is in class $=w R$* $| <$*> $=w $#error $@5.7.1 $:"550 Access denied - bogus HELO " $&s dnl`'Reject our hostname R$* $| <$*> [$=w] $#error $@5.7.1 $:"550 Access denied - bogus HELO " $&s dnl`'Pass anything else with a "." in the domain parameter R$* $| <$*> [$+.$+] $:$1 qualified domain ok dnl`'Reject if there was no "." or only an initial or final "." R$* $| <$*> [$*] $#error $@5.7.1 $:"550 Access denied - bogus HELO " $&s Unresolvable client address
Some people want to accept only mail from systems which have a valid PTR entry in DNS. In many cases this will reject also legitimate mail hence it is not a feature provided by sendmail. However, if you really want to do this, here is how:
LOCAL_RULESETS SLocal_check_relay R$* $: $&{client_resolve} RTEMP $#error $@ 4.7.1 $: "450 Access denied. Cannot resolve PTR record for " $&{client_addr} RFORGED $#error $@ 4.7.1 $: "450 Access denied. IP name possibly forged " $&{client_name} RFAIL $#error $@ 4.7.1 $: "450 Access denied. IP name lookup failed " $&{client_name}You can put these rules in any of the Local_check_* rulesets depending on your needs.