- Dovecot quota - check mailbox free space through Dovecot api before accepting mail
- Dovecot auth - authenticate through Dovecot api
- RCPT Database - *EXAMPLE* plugin to check if recipient is in database
- Remove headers - remove headers from outgoing emails due privacy or redundancy
Dovecot auth
Download
#!perl -w =head1 NAME auth_dovecot =head1 DESCRIPTION This plugin authenticate against dovecot socket (/var/run/dovecot/auth-smtp) =head1 CONFIGURATION You need enable socket at /etc/dovecot/conf.d/10-master.conf with something like this: service auth { unix_listener auth-smtp { mode = 0666 } } =head1 AUTHOR Copyright (c) 2015 Stanislav Humplik (stanislav@analogic.cz) http://poste.io This plugin is free software; you can distribute it and/or modify it under the terms of the General Public License v3. =cut use strict; use warnings; use Qpsmtpd::Auth; use Qpsmtpd::Constants; use IO::Socket::UNIX; use IO::Select; use MIME::Base64 qw(encode_base64); sub register { my ($self, $qp, %args) = @_; $self->register_hook("auth-plain", "dovecot_auth_plain"); $self->register_hook("auth-login", "dovecot_auth_plain"); $self->{_dovecot_auth_socket} = $args{socket} || '/var/run/dovecot/auth-smtp'; $self->{_dovecot_auth_timeout} = $args{timeout} || 5; } sub dovecot_auth_plain { my ($self, $transaction, $method, $user, $passClear, $passHash, $ticket) = @_; if ($user =~ /\x00/) { $self->log(LOGERROR, "deny: invalid username"); return (DENY, "dauth, invalid username"); }; utf8::encode($user); utf8::encode($passClear); my $login = $user; my $passwd = $passClear; my $service = 'smtp'; my $timeout = $self->{_dovecot_auth_timeout}; my $socket = $self->{_dovecot_auth_socket}; my $sock = new IO::Socket::UNIX(Type => SOCK_STREAM, Peer => $socket); if(!$sock) { $self->log(LOGERROR, "Can't open socket. Check dovecot is running and $socket is readable."); return (DENYSOFT, "dauth, internal problem"); } my $handshake = read_until($sock, '^DONE$', $timeout); if($handshake !~ /^VERSION\t1\t\d+$/m) { $self->log(LOGERROR, "Unsupported protocol version"); return (DENYSOFT, "dauth, internal problem"); } if($handshake !~ /^MECH\tPLAIN/m) { $self->log(LOGERROR, "PLAIN mechanism is not supported by the authentication daemon"); return (DENYSOFT, "dauth, internal problem"); } my $base64 = encode_base64("\0$login\0$passwd"); my $message = "VERSION\t1\t0\nCPID\t$$\nAUTH\t1\tPLAIN\tservice=$service\tresp=$base64"; if(!$sock->send($message)) { $self->log(LOGERROR, "Can't write to socket. Check dovecot is running and $socket is writable."); return (DENYSOFT, "dauth, internal problem"); } my $result = read_until($sock, '\n', $timeout); $sock->close; if ($result =~ /^OK/) { $self->log(LOGINFO, "pass: authentication for: $user"); $self->qp->connection->notes('logging-session-auth', $user); return (OK, "auth success for $user"); } else { $self->log(LOGINFO, "fail: authentication failure for: $user"); return (DENY, 'auth failure (100)'); } } sub read_until { my ($sock, $re, $timeout) = @_; my $sel = new IO::Select($sock); my $result = ''; while ($result !~ /$re/m) { $sel->can_read($timeout) or die "Timed out while waiting for response"; defined recv($sock, my $buf, 256, 0) or die 'Error while reading response'; $result .= $buf; } return $result; }