* lib/Qpsmtpd/Auth.pm

Fix some totally egregious spelling errors
 
 *  plugins/auth/auth_ldap_bind
    New plugin to authenticate against an LDAP database
    Thanks to Elliot Foster <elliotf@gratuitous.net>


git-svn-id: https://svn.perl.org/qpsmtpd/trunk@404 958fd67b-6ff1-0310-b445-bb7760255be9
This commit is contained in:
John Peacock 2005-04-12 19:59:52 +00:00
parent 1be0263025
commit 58ded6369d
2 changed files with 204 additions and 12 deletions

View File

@ -8,8 +8,8 @@ Qpsmtpd::Auth - Authentication framework for qpsmtpd
Provides support for SMTP AUTH within qpsmtpd transactions, see Provides support for SMTP AUTH within qpsmtpd transactions, see
L<http://www.faqs.org/rfcs/rfc2222.html> L<http://www.faqs.org/rfcs/rfc2222.html>
L<http://www.faqs.org/rfcs/rfc2554.html> L<http://www.faqs.org/rfcs/rfc2554.html>
for more details. for more details.
@ -106,23 +106,25 @@ of the following values (taken from Qpsmtpd::Constants):
If the authentication has succeeded, the plugin can return this value and If the authentication has succeeded, the plugin can return this value and
all subsequently registered hooks will be skipped. all subsequently registered hooks will be skipped.
=item DECLINE =item DECLINED
If the authentication has failed, but any additional plugins should be run, If the authentication has failed, but any additional plugins should be run,
this value will be returned. If none of the registered plugins succeed, the this value will be returned. If none of the registered plugins succeed, the
overall authentication will fail. overall authentication will fail. Normally an auth plugin should return
this value for all cases which do not succeed (so that another auth plugin
can have a chance to authenticate the user).
=item DENY =item DENY
If the authentication has failed, and the plugin wishes this to short circuit If the authentication has failed, and the plugin wishes this to short circuit
any further testing, it should return this value. For example, a plugin could any further testing, it should return this value. For example, a plugin could
register the L<auth-plain> hook and immediately fail any connection which is register the L<auth-plain> hook and immediately fail any connection which is
not trusted (i.e. not in the same network). not trusted (e.g. not in the same network).
Another reason to return DENY over DECLINE would be if the user name matched Another reason to return DENY over DECLINED would be if the user name matched
an existing account but the password failed to match. This would make a an existing account but the password failed to match. This would make a
dictionary-based attack much harder to accomplish. See the example authsql dictionary-based attack much harder to accomplish. See the included
plugin for how this might be accomplished auth_vpopmail_sql plugin for how this might be accomplished.
By returning DENY, no further authentication attempts will be made using the By returning DENY, no further authentication attempts will be made using the
current method and data. A remote SMTP client is free to attempt a second current method and data. A remote SMTP client is free to attempt a second
@ -138,9 +140,7 @@ and this will be appended to whatever response is sent to the remote SMTP
client. There is no guarantee that the end user will see this information, client. There is no guarantee that the end user will see this information,
though, since some prominent MTA's (produced by M$oft) I<helpfully> though, since some prominent MTA's (produced by M$oft) I<helpfully>
hide this information under the default configuration. This message will hide this information under the default configuration. This message will
be logged locally, if appropriate based on the configured log level. If be logged locally, if appropriate, based on the configured log level.
you are running multiple auth plugins, it is helpful to include at least
the plugin name in the returned message (for debugging purposes).
=head1 Auth Hooks =head1 Auth Hooks
@ -153,7 +153,7 @@ The currently defined authentication methods are:
Any plugin which registers an auth-plain hook will engage in a plaintext Any plugin which registers an auth-plain hook will engage in a plaintext
prompted negotiation. This is the least secure authentication method since prompted negotiation. This is the least secure authentication method since
both the user name and password are visible in plaintext. Most SMTP clients both the user name and password are visible in plaintext. Most SMTP clients
will preferentially chose a more secure method if it is advertised by the will preferentially choose a more secure method if it is advertised by the
server. server.
=item * auth-login =item * auth-login

192
plugins/auth/auth_ldap_bind Normal file
View File

@ -0,0 +1,192 @@
#!/usr/bin/perl -Tw
sub register {
my ( $self, $qp, @args ) = @_;
$self->register_hook( "auth-plain", "authldap" );
$self->register_hook( "auth-login", "authldap" );
# pull config defaults in from file
%{ $self->{"ldconf"} } = map { (split /\s+/, $_, 2)[0,1] } $self->qp->config('ldap');
# override ldap config defaults with plugin args
for my $ldap_arg (@args) {
%{ $self->{"ldconf"} } = map { (split /\s+/, $_, 2)[0,1] } $ldap_arg;
}
# do light validation of ldap_host and ldap_port to satisfy -T
my $ldhost = $self->{"ldconf"}->{'ldap_host'};
my $ldport = $self->{"ldconf"}->{'ldap_port'};
if (($ldhost) && ($ldhost =~ m/^(([a-z0-9]+\.?)+)$/)) {
$self->{"ldconf"}->{'ldap_host'} = $1
} else {
undef $self->{"ldconf"}->{'ldap_host'};
}
if (($ldport) && ($ldport =~ m/^(\d+)$/)) {
$self->{"ldconf"}->{'ldap_port'} = $1
} else {
undef $self->{"ldconf"}->{'ldap_port'};
}
# set any values that are not already
$self->{"ldconf"}->{"ldap_host"} ||= "127.0.0.1";
$self->{"ldconf"}->{"ldap_port"} ||= 389;
$self->{"ldconf"}->{"ldap_timeout"} ||= 5;
$self->{"ldconf"}->{"ldap_auth_filter_attr"} ||= "uid";
}
sub authldap {
use Net::LDAP qw(:all);
use Qpsmtpd::Constants;
my ( $self, $transaction, $method, $user, $passClear, $passHash, $ticket ) =
@_;
my ($ldhost, $ldport, $ldwait, $ldbase, $ldmattr, $lduserdn, $ldh, $mesg);
# pull values in from config
$ldhost = $self->{"ldconf"}->{"ldap_host"};
$ldport = $self->{"ldconf"}->{"ldap_port"};
$ldbase = $self->{"ldconf"}->{"ldap_base"};
# log error here and DECLINE if no baseDN, because a custom baseDN is required:
unless ($ldbase) {
$self->log(LOGERROR, "authldap/$method - please configure ldap_base" ) &&
return ( DECLINED, "authldap/$method - temporary auth error" );
}
$ldwait = $self->{"ldconf"}->{'ldap_timeout'};
$ldmattr = $self->{"ldconf"}->{'ldap_auth_filter_attr'};
my ( $pw_name, $pw_domain ) = split "@", lc($user);
# find dn of user matching supplied username
$ldh = Net::LDAP->new($ldhost, port=>$ldport, timeout=>$ldwait ) or
$self->log(LOGALERT, "authldap/$method - error in initial conn" ) &&
return ( DECLINE, "authldap/$method - temporary auth error" );
# find the user's DN
$mesg = $ldh->search(
base=>$ldbase,
scope=>'sub',
filter=>"$ldmattr=$pw_name",
attrs=>['uid'],
timeout=>$ldwait,
sizelimit=>'1') or
$self->log(LOGALERT, "authldap/$method - err in search for user" ) &&
return ( DECLINE, "authldap/$method - temporary auth error" );
# deal with errors if they exist
if ( $mesg->code ) {
$self->log(LOGALERT, "authldap/$method - err " . $mesg->code . " in search for user" );
return ( DECLINE, "authldap/$method - temporary auth error" );
}
# unbind, so as to allow a rebind below
$ldh->unbind if ($ldh);
# bind against directory as user with password supplied
if (($mesg->count) && ($lduserdn = $mesg->entry->dn)) {
$ldh = Net::LDAP->new($ldhost, port=>$ldport, timeout=>$ldwait ) or
$self->log(LOGALERT, "authldap/$method - err in user conn" ) &&
return ( DECLINE, "authldap/$method - temporary auth error" );
# here's the whole reason for the script
$mesg = $ldh->bind($lduserdn, password=>$passClear, timeout=>$ldwait);
$ldh->unbind if ($ldh);
# deal with errors if they exist, or allow success
if ( $mesg->code ) {
$self->log(LOGALERT, "authldap/$method - error in user bind" );
return ( DENY, "authldap/$method - wrong username or password" );
} else {
$self->log( LOGINFO, "authldap/$method - $user auth success" );
$self->log( LOGDEBUG, "authldap/$method - user: $user, pass: $passClear" );
return ( OK, "authldap/$method" );
}
# if the plugin couldn't find user's entry
} else {
$self->log(LOGALERT, "authldap/$method - user not found" ) &&
return ( DECLINE, "authldap/$method - wrong username or password" );
}
$ldh->disconnect;
}
=head1 NAME
auth_ldap_bind - Authenticate user via an LDAP bind
=head1 DESCRIPTION
This plugin authenticates users against an LDAP Directory. The plugin
first performs a lookup for an entry matching the connecting user. This
lookup uses the 'ldap_auth_filter_attr' attribute to match the connecting
user to their LDAP DN. Once the plugin has found the user's DN, the plugin
will attempt to bind to the Directory as that DN with the password that has
been supplied.
=head1 CONFIGURATION
Configuration items can be held in either the 'ldap' configuration file, or as
arguments to the plugin.
Configuration items in the 'ldap' configuration file
are set one per line, starting the line with the configuration item key,
followed by a space, then the values associated with the configuration item.
Configuration items given as arguments to the plugin are keys and values
separated by spaces. Be sure to quote any values that have spaces in them.
The only configuration item which is required is 'ldap_base'. This tells the
plugin what your base DN is. The plugin will not work until it has been
configured.
The configuration items 'ldap_host' and 'ldap_port' specify the host and port
at which your Directory server may be contacted. If these are not specified,
the plugin will use port '389' on 'localhost'.
The configuration item 'ldap_timeout' specifies how long the plugin should
wait for a response from your Directory server. By default, the value is 5
seconds.
The configuration item 'ldap_auth_filter_attr' specifies how the plugin should
find the user in your Directory. By default, the plugin will look up the user
based on the 'uid' attribute.
=head1 NOTES
Each auth requires an initial lookup to find the user's DN. Ideally, the
plugin would simply bind as the user without the need for this lookup(see
FUTURE DIRECTION below).
This plugin requires that the Directory allow anonymous bind (see FUTURE
DIRECTION below).
=head1 FUTURE DIRECTION
A configurable LDAP filter should be made available, to account for users
who are over quota, have had their accounts disabled, or whatever other
arbitrary requirements.
A configurable DN template (uid=$USER,ou=$DOMAIN,$BASE). This would prevent
the need of the initial user lookup, as the DN is created from the template.
A configurable bind DN, for Directories that do not allow anonymous bind.
Another plugin ('ldap_auth_cleartext'?), to allow retrieval of plain-text
passwords from the Directory, permitting CRAM-MD5 or other hash algorithm
authentication.
=head1 AUTHOR
Elliot Foster <elliotf@gratuitous.net>
=head1 COPYRIGHT AND LICENSE
Copyright (c) 2005 Elliot Foster
This plugin is licensed under the same terms as the qpsmtpd package itself.
Please see the LICENSE file included with qpsmtpd for details.
=cut