#!/usr/bin/perl -w use strict; use constant TIMEOUT => 20*60; use IPC::Open2; use FileHandle; use MIME::Base64; use POSIX; use IO::Handle '_IONBF'; $| = 1; if (!defined($ENV{GREETING}) || !defined($ENV{EHLORESP})) { die "You must set the environment variables GREETING and EHLORESP.\n"; } STDIN->setvbuf(undef, _IONBF, 0) or die "Couldn't setvbuf: $!\n"; #$GREETING="220 mailweb.flinthomes.net ESMTP\r #"; #$HELORESP="250-mailweb.flinthomes.net\r #250-PIPELINING\r #250-8BITMIME\r #"; $ENV{GREETING} =~ s/\n/\r\n/g; $ENV{EHLORESP} =~ s/\n/\r\n/g; print $ENV{GREETING}; $ENV{SENTGREETING}=1; our $checkpassword = shift @ARGV; $SIG{ALRM} = sub { die "smtp_auth: Connection timed out.\n"; }; $ENV{SMTP_IN_PROGRESS}=""; while (alarm(TIMEOUT),my $l = ) { if ($l =~ /^EHLO/i) { print $ENV{EHLORESP}; print "250-AUTH PLAIN LOGIN\r\n"; print "250 AUTH=PLAIN LOGIN\r\n"; $ENV{EHLO}=$l; } elsif ($l =~ /^AUTH(.*)$/i) { my $authtype = $1; my($username,$password); if ($authtype =~ /^\s+PLAIN\s+(\S*)\s*$/i) { (undef,$username,$password)=split(/\0/,decode_base64($1)); if (!defined($username) || !defined($password)) { print "501 Unable to decode password.\r\n"; next; } } elsif ($authtype =~ /^\s+login/i) { print "334 VXNlcm5hbWU6\r\n"; $username = ; $username =~ s/[\x0a\x0d\s]*$//; $username = decode_base64($username); print "334 UGFzc3dvcmQ6\r\n"; $password = ; $password =~ s/[\x0a\x0d\s]*$//; $password = decode_base64($password); } else { print "504 Unrecognized authentication type.\r\n"; next; } my $a = authenticate($checkpassword,$username,$password); warn "checkpassword exited $a\n"; if ($a == 0) { $ENV{RELAYCLIENT}=""; $ENV{AUTHENTICATED}=$username; print "235 2.0.0 OK Authenticated. Hello $username.\r\n"; last; } elsif ($a == 111) { print "454 Temporary authentication error\r\n"; next; } else { print "535 Authentication credentials rejected\r\n"; next; } } else { $ENV{CMD}=$l; last; } } &runproxy; sub runproxy { if ($ENV{CMD}) { my($fdR,$fdW) = POSIX::pipe() or die "Couldn't create pipe: $!\n"; warn "Starting child data pusher\n"; my $pid = fork; defined $pid or die "fork failed\n"; if ($pid) { # Parent POSIX::close($fdR) or die "Couldn't close read pipe: $!\n"; open(PRESMTP,">&=$fdW") or die "Couldn't open fd #$fdW as AUTHOUT: $!\n"; print PRESMTP $ENV{CMD}; close PRESMTP or die "Couldn't close write pipe: $!\n"; wait; exit($?); } # Child POSIX::dup2($fdR,3) unless ($fdR == 3); POSIX::close($fdW) unless ($fdW == 3); } warn "Running SMTP server: @ARGV\n"; exec(@ARGV); exit(1); # Should never happen. } sub authenticate { my($checkpassword,$user,$pass)=@_; $ENV{RELAYCLIENT}=""; $ENV{AUTHENTICATED}=$user; my($fdR,$fdW) = POSIX::pipe() or die "Couldn't create pipe: $!\n"; warn "Starting checkpassword\n"; my $pid = fork; defined $pid or die "fork failed\n"; if (!$pid) { # Child POSIX::dup2($fdR,3) unless ($fdR == 3); POSIX::close($fdW) unless ($fdW == 3); warn "exec('$checkpassword','/bin/true')\n"; exec($checkpassword,"/bin/true"); die "exec error: $!\n"; } # Parent POSIX::close($fdR); open(AUTHOUT,">&=$fdW") or die "Couldn't open fd #$fdW as AUTHOUT: $!\n"; print AUTHOUT "$user\0$pass\0"; close(AUTHOUT); wait == $pid or die "Bad pid returned by wait (?!?)\n"; warn "checkpassword really exited $?\n"; if ($? & 255) { # Died of signal or some such nonsense return 111; } my $ec = $? >> 8; warn "ec=$ec\n"; if ($ec == 0) { return 0; } elsif ($ec == 1) { return -1; # "535 Authentication credentials rejected\r\n"; } elsif ( ($ec == 2) || ($ec == 111) ) { # Temporary error return 111; } else { # Unknown error warn "Child process exited with $?\n"; return 111; } exit(0); }