File: badmailfrom

package info (click to toggle)
qpsmtpd 0.94-8
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 2,340 kB
  • sloc: perl: 17,176; sh: 543; makefile: 186; sql: 100
file content (139 lines) | stat: -rw-r--r-- 3,740 bytes parent folder | download | duplicates (4)
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
#!perl -w

=head1 NAME

check_badmailfrom - checks the badmailfrom config, with per-line reasons

=head1 DESCRIPTION

Reads the "badmailfrom" configuration like qmail-smtpd does.  From the
qmail-smtpd docs:

"Unacceptable envelope sender addresses. qmail-smtpd will reject every
recipient address for a message if the envelope sender address is
listed in badmailfrom. A line in badmailfrom may be of the form
@host, meaning every address at host."

You may include an optional message after the sender address (leave a space),
to be used when rejecting the sender.

=head1 CONFIGURATION

=head2 reject

  badmailfrom reject [ 0 | 1 | naughty ]

I<0> will not reject any connections.

I<1> will reject naughty senders.

I<connect> is the most efficient setting. It's also the default.

To reject at any other connection hook, use the I<naughty> setting and the
B<naughty> plugin.

=head1 PATTERNS

This plugin also supports regular expression matches. This allows
special patterns to be denied (e.g. FQDN-VERP, percent hack, bangs,
double ats).

Patterns are stored in the format pattern(\s+)response, where pattern
is a Perl pattern expression. Don't forget to anchor the pattern
(front ^ and back $) if you want to restrict it from matching
anywhere in the string.

 ^streamsendbouncer@.*\.mailengine1\.com$    Your right-hand side VERP doesn't fool me
 ^return.*@.*\.pidplate\.biz$                I don't want it regardless of subdomain
 ^admin.*\.ppoonn400\.com$


=head1 AUTHORS

2002 - Jim Winstead - initial author of badmailfrom

2010 - Johan Almqvist <johan-qpsmtpd@almqvist.net> - pattern matching plugin

2012 - Matt Simerson - merging of the two and plugin tests

=cut

sub register {
    my ($self, $qp) = (shift, shift);
    $self->{_args} = {@_};

    $self->{_args}{reject} = 1 if !defined $self->{_args}{reject};
}

sub hook_mail {
    my ($self, $transaction, $sender, %param) = @_;

    return DECLINED if $self->is_immune();

    my @badmailfrom = $self->qp->config('badmailfrom');
    if (defined $self->{_badmailfrom_config}) {    # testing
        @badmailfrom = @{$self->{_badmailfrom_config}};
    }
    return DECLINED if $self->is_immune_sender($sender, \@badmailfrom);

    my $host = lc $sender->host;
    my $from = lc($sender->user) . '@' . $host;

    for my $config (@badmailfrom) {
        $config =~ s/^\s+//g;                      # trim leading whitespace
        my ($bad, $reason) = split /\s+/, $config, 2;
        next unless $bad;
        next unless $self->is_match($from, $bad, $host);
        $reason ||= "Your envelope sender is in my badmailfrom list";
        $self->adjust_karma(-1);
        return $self->get_reject($reason);
    }

    $self->log(LOGINFO, "pass");
    return DECLINED;
}

sub is_match {
    my ($self, $from, $bad, $host) = @_;

    if ($bad =~ /[\/\^\$\*\+\!\%\?\\]/) {    # it's a regexp
        if ($from =~ /$bad/) {
            $self->log(LOGDEBUG, "badmailfrom pattern ($bad) match for $from");
            return 1;
        }
        return;
    }

    $bad = lc $bad;
    if ($bad !~ m/\@/) {
        $self->log(LOGWARN, "badmailfrom: bad config: no \@ sign in $bad");
        return;
    }
    if (substr($bad, 0, 1) eq '@') {
        return 1 if $bad eq "\@$host";
        return;
    }
    return if $bad ne $from;
    return 1;
}

sub is_immune_sender {
    my ($self, $sender, $badmf) = @_;

    if (!scalar @$badmf) {
        $self->log(LOGDEBUG, 'skip, empty list');
        return 1;
    }

    if (!$sender || $sender->format eq '<>') {
        $self->log(LOGDEBUG, 'skip, null sender');
        return 1;
    }

    if (!$sender->host || !$sender->user) {
        $self->log(LOGDEBUG, 'skip, missing user or host');
        return 1;
    }

    return;
}