commit f6021cecbb146f4f33ddf36f4646c313beccd5cb Author: ston1th Date: Sun Sep 8 17:26:52 2019 +0200 initial commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..45785b1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +Copyright (C) 2019 Marius Schellenberger +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The names of the authors and/or contributors may not be used to + endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL ston1th BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..7331e3c --- /dev/null +++ b/README.md @@ -0,0 +1,65 @@ +# SMTP Authentication Sync + +This is a small daemon to sync Zarafa/Kopano users and passwords to an authentication file which can be used by OpenSMTPD. + +Any successful login or password change will be synced to this file. +The passwords are stored as Bcrypt hashes generated by `smtpctl encrypt`. + +## Install + +Copy `smtp_auth_sync.pl` to `/usr/local/sbin/smtp_auth_sync.pl` and set execute permissions: + +`chmod 755 /usr/local/sbin/smtp_auth_sync.pl` + +Use the `install_openbsd.sh` or `install_linux.sh` scripts to create the `rc` or `systemd` configs. + +## Config Options + +At the top of `smtp_auth_sync.pl` are some config options you may need to edit for your environment. + +Defaults are for OpenBSD: + +``` +# 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"; +``` + +## smtpd.conf + +The table name must be `secrets`. + +The file path can be changed. + +``` +table secrets file:/etc/mail/secrets + +listen on egress port 587 smtps pki mx.example.com auth +``` + +## Zarafa/Kopano Password Change Plugin + +Plugin (version 1.3): https://github.com/silentsakky/zarafa-webapp-passwd + +Apply this patch to send changed passwords to `smtp_auth_sync`: + +Depending on your setup you may need to change `"/run/smtp_auth_sync.sock"` to the appropriate socket path inside your chroot. + +``` +# Default path on OpenBSD +cd /var/www/kopano-webapp/plugins/passwd/php + +ftp -o class.passwdmodule.php.patch https://git.giftfish.de/ston1th/smtp_auth_sync/raw/branch/master/class.passwdmodule.php.patch + +patch -p0 sendFeedback(true, array( + 'info' => array( diff --git a/install_linux.sh b/install_linux.sh new file mode 100755 index 0000000..1a63c4e --- /dev/null +++ b/install_linux.sh @@ -0,0 +1,17 @@ +#!/bin/sh +cat < /etc/systemd/system/smtp_auth_sync.service +[Unit] +Description=SMTP Authentication Sync +After=syslog.target +After=network.target + +[Service] +Type=simple +ExecStart=/usr/local/sbin/smtp_auth_sync.pl +Restart=always + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable smtp_auth_sync +systemctl start smtp_auth_sync diff --git a/install_openbsd.sh b/install_openbsd.sh new file mode 100755 index 0000000..e6fed3e --- /dev/null +++ b/install_openbsd.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +cat <<'EOF'> /etc/rc.d/smtp_auth_sync +#!/bin/ksh + +daemon="/usr/bin/perl" +daemon_flags="/usr/local/sbin/smtp_auth_sync.pl" + +. /etc/rc.d/rc.subr +rc_bg=YES +rc_reload=NO + +rc_cmd $1 +EOF +chmod 555 /etc/rc.d/smtp_auth_sync +rcctl enable smtp_auth_sync +rcctl start smtp_auth_sync diff --git a/smtp_auth_sync.pl b/smtp_auth_sync.pl new file mode 100755 index 0000000..bcd778a --- /dev/null +++ b/smtp_auth_sync.pl @@ -0,0 +1,117 @@ +#!/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 = ); + 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"); +}