1
0
Fork 0
smtp_auth_sync/smtp_auth_sync.pl
2019-09-08 17:26:52 +02:00

117 lines
3 KiB
Perl
Executable file

#!/usr/bin/perl
use strict;
use warnings;
use Config;
use IO::Socket;
use IO::Select;
use IPC::Open3;
# Configs
# File to store the user:password hash mappings
my $authfile = "/etc/mail/secrets";
# UNIX Domain Socket inside the web chroot
my $sfile = "/var/www/run/smtp_auth_sync.sock";
# Absolute path of smtpctl
my $smtpctl = "/usr/sbin/smtpctl";
# Group OpenSMTPD runs with
my $smtp_group = "_smtpd";
# Group the webserver runs with
my $www_group = "www";
sub read_timeout {
my ($s, $conn) = @_;
$s->can_read(2) or return 0;
return readline($conn);
}
my ($r_uid,$r_gid) = (getpwnam("root"))[2,3];
my $s_gid = (getpwnam($smtp_group))[3] or die "group '$smtp_group' not found: $!";
my $w_gid = (getpwnam($www_group))[3] or die "group '$www_group' not found: $!";
my $authfiletmp = "${authfile}.tmp";
if ($Config{osname} eq "openbsd") {
eval "use OpenBSD::Pledge";
if ($Config{osvers} >= "6.6") {
eval "use OpenBSD::Unveil";
unveil($authfile, "rwc") or die "Unable to unveil: $!";
unveil($authfiletmp, "rwc") or die "Unable to unveil: $!";
unveil($sfile, "rwc") or die "Unable to unveil: $!";
unveil($smtpctl, "rx") or die "Unable to unveil: $!";
}
pledge("rpath wpath cpath fattr chown unix proc exec prot_exec") or die "Unable to pledge: $!";
}
if (-e $sfile) {
unlink $sfile or die "Could not delete socket file '$sfile': $!";
}
my $sock = IO::Socket::UNIX->new(
Type => SOCK_STREAM(),
Local => $sfile,
Listen => 1,
) or die "Could not create socket: '$!'";
chown($r_uid, $w_gid, $sfile);
chmod(0620, $sfile);
sub cleanup {
$sock->close();
unlink $sfile;
exit 0;
}
$SIG{INT} = \&cleanup;
$SIG{TERM} = \&cleanup;
my $enc = "$smtpctl encrypt";
my $update = "$smtpctl update table secrets";
while (my $conn = $sock->accept()) {
my $user;
my $pass;
my $s = IO::Select->new($conn);
$user = read_timeout($s, $conn) or do { $conn->close(); next; };
chomp($user);
$pass = read_timeout($s, $conn) or do { $conn->close(); next; };
chomp($pass);
$conn->close();
if ($user eq "" or $pass eq "") {
next;
}
open3(\*WRITE, \*READ, 0, $enc);
print WRITE "$pass";
close(WRITE);
chomp(my $hash = <READ>);
close(READ);
if ($hash eq "") {
next;
}
if (! -f $authfile) {
open(my $auth, ">", $authfile) or next;
chown($r_uid, $s_gid, $authfile);
chmod(0640, $authfile);
print $auth "$user $hash\n";
close($auth);
next;
}
my $found = 0;
open(my $auth, "<", $authfile) or do { print "Could not open file '$authfile': $!"; next; };
chown($r_uid, $s_gid, $authfile);
chmod(0640, $authfile);
open(my $authtmp, ">", $authfiletmp) or do { print "Could not open file '$authfiletmp': $!"; next; };
chown($r_uid, $s_gid, $authfiletmp);
chmod(0640, $authfiletmp);
while (<$auth>) {
my $line = $_;
if ($line =~ /$user/) {
print $authtmp "$user $hash\n";
$found = 1;
} else {
print $authtmp "$line";
}
}
if ($found == 0) {
print $authtmp "$user $hash\n";
}
close($auth);
close($authtmp);
rename($authfiletmp, $authfile);
system("$update >/dev/null");
}