175 lines
5.1 KiB
Perl
175 lines
5.1 KiB
Perl
|
#!/usr/bin/perl -w -T
|
||
|
|
||
|
package esmith::SMTPAuthProxy;
|
||
|
|
||
|
use strict;
|
||
|
use vars qw(@ISA);
|
||
|
use Net::Server::Fork;
|
||
|
use Net::SMTP;
|
||
|
use Net::SMTP::SSL;
|
||
|
use esmith::ConfigDB;
|
||
|
|
||
|
@ISA = qw(Net::Server::Fork);
|
||
|
|
||
|
|
||
|
sub options {
|
||
|
my $self = shift;
|
||
|
my $prop = $self->{server};
|
||
|
my $ref = shift;
|
||
|
|
||
|
$self->SUPER::options($ref);
|
||
|
|
||
|
my $config = esmith::ConfigDB->open_ro || die "Could not open config db";
|
||
|
my $smtp_proxy_rec = $config->get('smtp-auth-proxy');
|
||
|
|
||
|
$prop->{SMTPSmartHost} = $config->get_value('SMTPSmartHost');
|
||
|
$prop->{Blacklist} = $smtp_proxy_rec->prop('Blacklist') || " ";
|
||
|
$prop->{Userid} = $smtp_proxy_rec->prop('Userid');
|
||
|
$prop->{Passwd} = $smtp_proxy_rec->prop('Passwd');
|
||
|
$prop->{Debug} = $smtp_proxy_rec->prop('Debug');
|
||
|
$prop->{SystemName} = $config->get_value('SystemName');
|
||
|
$prop->{DomainName} = $config->get_value('DomainName');
|
||
|
$prop->{PeerPort} = $smtp_proxy_rec->prop('PeerPort') || 25;
|
||
|
$prop->{Helo} = $config->get('qpsmtpd')->prop('HeloHost') ||
|
||
|
"$prop->{SystemName}.$prop->{DomainName}";
|
||
|
}
|
||
|
|
||
|
esmith::SMTPAuthProxy->run(
|
||
|
max_servers => 4,
|
||
|
proto => 'tcp',
|
||
|
user => 'nobody',
|
||
|
group => 'nobody',
|
||
|
host => 'localhost',
|
||
|
port => 26);
|
||
|
exit;
|
||
|
|
||
|
### over-ridden subs below
|
||
|
|
||
|
sub process_request
|
||
|
{
|
||
|
my $self = shift;
|
||
|
my $kidpid;
|
||
|
|
||
|
my $smarthost = $self->get_property('SMTPSmartHost');
|
||
|
my $port = $self->get_property('PeerPort');
|
||
|
my $user = $self->get_property('user');
|
||
|
my $domain_name = $self->get_property('DomainName');
|
||
|
my $system_name = $self->get_property('SystemName');
|
||
|
my $helo = $self->get_property('Helo');
|
||
|
my $name = $self->get_property('Userid');
|
||
|
my $pass = $self->get_property('Passwd');
|
||
|
my $debug = (($self->get_property('Debug') || 'disabled') eq 'enabled')
|
||
|
? 1 : 0;
|
||
|
unless ($smarthost && $system_name && $domain_name && $name && $pass)
|
||
|
{
|
||
|
print "421 Internal error\n";
|
||
|
warn "Insufficient configuration for smtp-auth-proxy (SystemName)!\n"
|
||
|
unless $system_name;
|
||
|
warn "Insufficient configuration for smtp-auth-proxy (DomainName)!\n"
|
||
|
unless $domain_name;
|
||
|
warn "Insufficient configuration for smtp-auth-proxy (SMTPSmartHost)!\n"
|
||
|
unless $smarthost;
|
||
|
warn "Insufficient configuration for smtp-auth-proxy (Userid)!\n"
|
||
|
unless $name;
|
||
|
warn "Insufficient configuration for smtp-auth-proxy (Passwd)!\n"
|
||
|
unless $pass;
|
||
|
exit;
|
||
|
}
|
||
|
|
||
|
my $class = ($port == 465) ? 'Net::SMTP::SSL' : 'Net::SMTP';
|
||
|
my $smtp = $class->new($smarthost,
|
||
|
Hello => $helo,
|
||
|
Debug => $debug,
|
||
|
Port => $port,
|
||
|
);
|
||
|
unless ($smtp)
|
||
|
{
|
||
|
print "451 Upstream SMTP server not available\n";
|
||
|
warn "No SMTP connection to server $smarthost on port $port\n";
|
||
|
exit;
|
||
|
}
|
||
|
|
||
|
my @ehlo = $smtp->message;
|
||
|
|
||
|
if (grep { /STARTTLS$/ } @ehlo)
|
||
|
{
|
||
|
$smtp->command('STARTTLS');
|
||
|
my $response = $smtp->response;
|
||
|
my $status = $smtp->message;
|
||
|
use IO::Socket::SSL;
|
||
|
my $sslret = IO::Socket::SSL->start_SSL(
|
||
|
$smtp,
|
||
|
Timeout => 30,
|
||
|
SSL_startHandshake => 1
|
||
|
);
|
||
|
unless ($sslret) {
|
||
|
my $error = IO::Socket::SSL::errstr();
|
||
|
print("454 TLS not available due to temporary reason [$error]");
|
||
|
}
|
||
|
bless $smtp, 'Net::SMTP::SSL';
|
||
|
$smtp->reset;
|
||
|
$smtp->hello("${system_name}.${domain_name}");
|
||
|
@ehlo = $smtp->message;
|
||
|
}
|
||
|
|
||
|
if ($smtp->supports("AUTH"))
|
||
|
{
|
||
|
my $authenticated = 0;
|
||
|
my $blacklist = $self->get_property('Blacklist') || "";;
|
||
|
foreach my $method (split (/ /, ${*$smtp}{'net_smtp_esmtp'}->{"AUTH"}))
|
||
|
{
|
||
|
# Skip $method if in the blacklist
|
||
|
if ($blacklist =~ /$method/)
|
||
|
{
|
||
|
warn "Skipping blacklisted method $method\n" if $debug;
|
||
|
next;
|
||
|
}
|
||
|
warn "Trying authentication method $method\n" if $debug;
|
||
|
# Only present one auth method at a time to NET::SMTP
|
||
|
{${*$smtp}{'net_smtp_esmtp'}->{"AUTH"} = $method;}
|
||
|
|
||
|
eval { $authenticated = $smtp->auth($name, $pass); };
|
||
|
|
||
|
if ($@)
|
||
|
{
|
||
|
warn "exception during authentication (with $method): $@\n";
|
||
|
next;
|
||
|
}
|
||
|
last if $authenticated;
|
||
|
warn "SMTP authentication (with $method) with ISP server failed\n";
|
||
|
}
|
||
|
unless ($authenticated)
|
||
|
{
|
||
|
print "451 Could not auth to mail server\n";
|
||
|
warn "all SMTP authentication methods failed\n";
|
||
|
$smtp->quit;
|
||
|
exit;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
warn "Upstream SMTP server does not support authentication\n";
|
||
|
}
|
||
|
print "220 ", $smtp->banner;
|
||
|
|
||
|
my $discard = <STDIN>; # Swallow EHLO
|
||
|
while (my $msg = shift @ehlo) {
|
||
|
my $sep = (@ehlo ? "-" : " ");
|
||
|
print "250$sep$msg";
|
||
|
}
|
||
|
|
||
|
die "can't fork: $!" unless defined ($kidpid = fork());
|
||
|
if ($kidpid)
|
||
|
{
|
||
|
print $smtp $_ while <STDIN>;
|
||
|
kill ("TERM" => $kidpid);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
print while <$smtp>;
|
||
|
kill ("TERM" => getppid());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
1;
|