qpsmtpd/plugins/auth/auth_checkpassword

194 lines
5.9 KiB
Plaintext
Raw Normal View History

#!perl -w
=head1 NAME
auth_checkpassword - Authenticate against a DJB style checkpassword program
=head1 DESCRIPTION
This plugin authenticates users against a DJB style checkpassword
program. Unlike previous checkpassword implementations, this plugin
expects qpsmtpd to be running as the qpsmtpd user. Privilege
escalation can be attained by running the checkpassword binary setuid
or with sudo.
=head1 CONFIGURATION
Configure the path to your checkpassword binary. You can configure this in
config/plugins by defining the checkpw and true arguments as follows:
auth/auth_checkpassword checkpw /usr/local/vpopmail/bin/vchkpw true /bin/true
or by editing the config file config/smtpauth-checkpassword:
echo "/usr/local/vpopmail/bin/vchkpw /bin/true" > ~qpsmtpd/config/smtpauth-checkpassword
vchkpw is the checkpassword program provided by vpopmail. Substitute
your own checkpassword app as appropriate.
If you are using vchkpw and this plugin is being executed by a user ID
other than 89 or 0 (as is the default), and the vchkpw binary is not
setuid (as is the default), this plugin will automatically prepend the
vchkpw command with sudo. If that is the case, you must configure sudo
by adding these two lines to your sudoers file:
Defaults:qpsmtpd closefrom_override
qpsmtpd ALL = (ALL) NOPASSWD: /usr/local/vpopmail/bin/vchkpw
The closefrom_override option is necessary because, by default, sudo
appropriates the first 3 file descriptors. Those descriptors are
necessary to communicate with the checkpassword program. If you run
qpsmtpd as some other user, adjust the sudo lines approriately.
Using sudo is preferable to enabling setuid on the vchkpw binary. If
you reinstall vpopmail and the setuid bit is lost, this plugin will be
broken.
=head1 SEE ALSO
If you are using this plugin with vpopmail, please read the VPOPMAIL
section in docs/authentication.pod
=head1 DIAGNOSTICS
Is the path in the config/smtpauth-checkpassword correct?
Is the path to true in config/smtpauth-checkpassword correct?
Is qpsmtpd running as the qpsmtpd user? If not, did you adjust the
sudo configuration appropriately?
If you are not using sudo, did you remember to make the vchkpw binary
setuid (chmod 4711 ~vpopmail/bin/vchkpw)?
While writing this plugin, I first wrote myself a little test script,
which helped me identify the sudo closefrom_override issue. Here is
that script:
POD corrections, additional tests, plugin consistency on files in plugins dir: fixed a number of POD errors formatted some # comments into POD removed bare 1; (these are plugins, not perl modules) most instances of this were copy/pasted from a previous plugin that had it removed instances of # vim ts=N ... they weren't consistent, many didn't match .perltidyrc on modules that failed perl -c tests, added 'use Qpsmtpd::Constants;' Conflicts: plugins/async/check_earlytalker plugins/async/dns_whitelist_soft plugins/async/dnsbl plugins/async/queue/smtp-forward plugins/async/require_resolvable_fromhost plugins/async/rhsbl plugins/async/uribl plugins/auth/auth_checkpassword plugins/auth/auth_cvm_unix_local plugins/auth/auth_flat_file plugins/auth/auth_ldap_bind plugins/auth/auth_vpopmail plugins/auth/auth_vpopmail_sql plugins/auth/authdeny plugins/check_badmailfromto plugins/check_badrcptto_patterns plugins/check_bogus_bounce plugins/check_earlytalker plugins/check_norelay plugins/check_spamhelo plugins/connection_time plugins/dns_whitelist_soft plugins/dnsbl plugins/domainkeys plugins/greylisting plugins/hosts_allow plugins/http_config plugins/logging/adaptive plugins/logging/apache plugins/logging/connection_id plugins/logging/transaction_id plugins/logging/warn plugins/milter plugins/queue/exim-bsmtp plugins/queue/maildir plugins/queue/postfix-queue plugins/queue/smtp-forward plugins/quit_fortune plugins/random_error plugins/rcpt_map plugins/rcpt_regexp plugins/relay_only plugins/require_resolvable_fromhost plugins/rhsbl plugins/sender_permitted_from plugins/spamassassin plugins/tls plugins/tls_cert plugins/uribl plugins/virus/aveclient plugins/virus/bitdefender plugins/virus/clamav plugins/virus/clamdscan plugins/virus/hbedv plugins/virus/kavscanner plugins/virus/klez_filter plugins/virus/sophie plugins/virus/uvscan
2012-04-08 02:11:16 +02:00
#!/usr/bin/perl
use strict;
my $sudo = "/usr/local/bin/sudo";
$sudo .= " -C4 -u vpopmail";
my $vchkpw = "/usr/local/vpopmail/bin/vchkpw";
my $true = "/bin/true";
POD corrections, additional tests, plugin consistency on files in plugins dir: fixed a number of POD errors formatted some # comments into POD removed bare 1; (these are plugins, not perl modules) most instances of this were copy/pasted from a previous plugin that had it removed instances of # vim ts=N ... they weren't consistent, many didn't match .perltidyrc on modules that failed perl -c tests, added 'use Qpsmtpd::Constants;' Conflicts: plugins/async/check_earlytalker plugins/async/dns_whitelist_soft plugins/async/dnsbl plugins/async/queue/smtp-forward plugins/async/require_resolvable_fromhost plugins/async/rhsbl plugins/async/uribl plugins/auth/auth_checkpassword plugins/auth/auth_cvm_unix_local plugins/auth/auth_flat_file plugins/auth/auth_ldap_bind plugins/auth/auth_vpopmail plugins/auth/auth_vpopmail_sql plugins/auth/authdeny plugins/check_badmailfromto plugins/check_badrcptto_patterns plugins/check_bogus_bounce plugins/check_earlytalker plugins/check_norelay plugins/check_spamhelo plugins/connection_time plugins/dns_whitelist_soft plugins/dnsbl plugins/domainkeys plugins/greylisting plugins/hosts_allow plugins/http_config plugins/logging/adaptive plugins/logging/apache plugins/logging/connection_id plugins/logging/transaction_id plugins/logging/warn plugins/milter plugins/queue/exim-bsmtp plugins/queue/maildir plugins/queue/postfix-queue plugins/queue/smtp-forward plugins/quit_fortune plugins/random_error plugins/rcpt_map plugins/rcpt_regexp plugins/relay_only plugins/require_resolvable_fromhost plugins/rhsbl plugins/sender_permitted_from plugins/spamassassin plugins/tls plugins/tls_cert plugins/uribl plugins/virus/aveclient plugins/virus/bitdefender plugins/virus/clamav plugins/virus/clamdscan plugins/virus/hbedv plugins/virus/kavscanner plugins/virus/klez_filter plugins/virus/sophie plugins/virus/uvscan
2012-04-08 02:11:16 +02:00
open(CPW,"|$sudo $vchkpw $true 3<&0");
printf(CPW "%s\0%s\0Y123456\0",'user@example.com','pa55word');
close(CPW);
my $status = $?;
print "FAIL\n" and exit if ( $status != 0 );
print "OK\n";
Save that script to vchkpw.pl and then run it as the same user that
qpsmtpd runs as:
setuidgid qpsmtpd perl vchkpw.pl
If you aren't using sudo, then remove $sudo from the open line.
=head1 ACKNOWLEDGEMENTS
based upon authcheckpassword by Michael Holzt
and adapted by Johan Almqvist 2006-01-18
=head1 AUTHOR
Matt Simerson <msimerson@cpan.org>
=head1 COPYRIGHT AND LICENSE
Copyright (c) 2010 Matt Simerson
This plugin is licensed under the same terms as the qpsmtpd package itself.
Please see the LICENSE file included with qpsmtpd for details.
=cut
sub register {
my ($self, $qp, %args) = @_;
my ($checkpw, $true) = $self->get_checkpw(\%args);
return DECLINED if !$checkpw || !$true;
$self->connection->notes('auth_checkpassword_bin', $checkpw);
$self->connection->notes('auth_checkpassword_true', $true);
$self->register_hook('auth-plain', 'auth_checkpassword');
$self->register_hook('auth-login', 'auth_checkpassword');
}
sub auth_checkpassword {
my ($self, $transaction, $method, $user, $passClear, $passHash, $ticket) =
@_;
my $binary = $self->connection->notes('auth_checkpassword_bin');
my $true = $self->connection->notes('auth_checkpassword_true');
chomp($binary, $true);
my $sudo = get_sudo($binary);
$self->log(LOGDEBUG, "auth_checkpassword: $sudo $binary $true 3<&0");
open(CPW, "|$sudo $binary $true 3<&0");
printf(CPW "%s\0%s\0Y123456\0", $user, $passClear);
close(CPW);
my $status = $?;
if ($status != 0) {
$self->log(LOGNOTICE, "fail, auth failed: $status");
return DECLINED;
}
$self->connection->notes('authuser', $user);
$self->log(LOGINFO, "pass, auth success with $method");
return OK, "auth_checkpassword";
}
sub get_checkpw {
my ($self, $args) = @_;
my ($checkpw) = $args->{checkpw} =~ /^(.*)$/ if $args->{checkpw}; # untaint
my ($true) = $args->{true} =~ /^(.*)$/ if $args->{true}; # untaint
return $checkpw, $true
if $checkpw && $true && -x $checkpw && -x $true;
my $missing_config =
"disabled due to invalid configuration. See 'perldoc plugins/auth/auth_checkpassword' for how to configure.";
if (!$self->qp->config('smtpauth-checkpassword')) {
$self->log(LOGERROR, $missing_config);
return;
}
$self->log(LOGNOTICE, "reading config from smtpauth-checkpassword");
my $config = $self->qp->config("smtpauth-checkpassword");
($checkpw, $true) = $config =~ /^(\S+)\s+(\S+)\s*$/;
if (!$checkpw || !$true || !-x $checkpw || !-x $true) {
$self->log(LOGERROR, $missing_config);
return;
}
return $checkpw, $true;
}
sub get_sudo {
my $binary = shift;
return '' if $> == 0; # running as root
return '' if $> == 89 && $binary =~ /vchkpw$/; # running as vpopmail
my $mode = (stat($binary))[2];
$mode = sprintf "%lo", $mode & 07777;
return '' if $mode eq '4711'; # $binary is setuid
my $sudo = `which sudo` || '/usr/local/bin/sudo';
return '' if !-x $sudo;
$sudo .= ' -C4'; # prevent sudo from clobbering file descriptor 3
return $sudo if $binary !~ /vchkpw$/;
return "$sudo -u vpopmail";
}