initial commit
This commit is contained in:
commit
f6021cecbb
6 changed files with 259 additions and 0 deletions
24
LICENSE
Normal file
24
LICENSE
Normal file
|
|
@ -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.
|
||||||
65
README.md
Normal file
65
README.md
Normal file
|
|
@ -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 <secrets>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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 <class.passwdmodule.php.patch
|
||||||
|
|
||||||
|
rm class.passwdmodule.php.orig class.passwdmodule.php.patch
|
||||||
|
```
|
||||||
19
class.passwdmodule.php.patch
Normal file
19
class.passwdmodule.php.patch
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
--- class.passwdmodule.php 2019-09-08 16:43:34.699514994 +0200
|
||||||
|
+++ class.passwdmodule.php 2019-09-08 16:44:07.749514350 +0200
|
||||||
|
@@ -212,6 +212,16 @@
|
||||||
|
if (mapi_zarafa_setuser($store, $userinfo['userid'], $userName, $userinfo['fullname'], $userinfo['emailaddress'], $newPassword, 0, $userinfo['admin'])) {
|
||||||
|
// password changed successfully
|
||||||
|
|
||||||
|
+ // send password to smtp_auth_sync
|
||||||
|
+ $sock = socket_create(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
+ if(socket_connect($sock, "/run/smtp_auth_sync.sock")) {
|
||||||
|
+ $send = $userName."\n";
|
||||||
|
+ socket_send($sock, $send, strlen($send), 0);
|
||||||
|
+ $send = $newPassword."\n";
|
||||||
|
+ socket_send($sock, $send, strlen($send), 0);
|
||||||
|
+ socket_close($sock);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
// send feedback to client
|
||||||
|
$this->sendFeedback(true, array(
|
||||||
|
'info' => array(
|
||||||
17
install_linux.sh
Executable file
17
install_linux.sh
Executable file
|
|
@ -0,0 +1,17 @@
|
||||||
|
#!/bin/sh
|
||||||
|
cat <<EOF> /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
|
||||||
17
install_openbsd.sh
Executable file
17
install_openbsd.sh
Executable file
|
|
@ -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
|
||||||
117
smtp_auth_sync.pl
Executable file
117
smtp_auth_sync.pl
Executable file
|
|
@ -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 = <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");
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue