Elliot F wrote:
> Here is a plugin to allow authentication against an LDAP Directory.  Let me 
> know
> how it should be wrapped/formatted, or if there are any changes that should be
> made.  Specifically, if some other names for the configuration files are 
> preferred.

So, not that I'm replying to my own post, but (other than the input validation
issue) what do you guys think?  What changes do I need to make before someone
commits this?

> Elliot
> 
> 
> ------------------------------------------------------------------------
> 
> # bind to directory as user/pass given
> # 
> # $user (form of '[EMAIL PROTECTED]' or just 'user'), $passclear, $passHash 
> (md5?)
> #
> 
> sub register {
>   my ( $self, $qp ) = @_;
>   $self->register_hook( "auth-plain", "authldap" );
>   $self->register_hook( "auth-login", "authldap" );
> }
> 
> sub authldap {
>   ## TODO:  add unencrypted password retrieval in the future for CRAM
>   ## TODO:  add ability to specify custom filter in config
>   ##    Example:  
> (&(uid=$USER)(customattr=$DOMAIN)(!(accountstatus=disabled)))
> 
>   use Net::LDAP qw(:all);
>   use Qpsmtpd::Constants;
>   #use Digest::HMAC_MD5 qw(hmac_md5_hex);
> 
>   my ( $self, $transaction, $method, $user, $passClear, $passHash, $ticket ) =
>     @_;
>   my ($ldhost, $ldport, $ldwait, $ldbase, $ldmattr, $lduserdn, $ldh, $mesg);
> 
>   # do light validation of ldap_host and ldap_port to satisfy -T
>   $ldhost = $self->qp->config('ldap_host');
>   $ldport = $self->qp->config('ldap_port');
>   if (($ldhost) && ($ldhost =~ m/^(([a-z0-9]+\.?)+)$/)) {
>     $ldhost = $1;
>   } else {
>     $ldhost = "localhost";
>   }
>   if (($ldport) && ($ldport =~ m/^(\d+)$/)) {
>     $ldport = $1;
>   } else {
>     $ldport = "389";
>   }
>   # log error here and DENY because a custom baseDN is required:
>   $ldbase = $self->qp->config('ldap_base');
>   unless ($ldbase) {
>     $self->log(LOGERROR, "authldap/$method - please configure ldap_base" ) &&
>     return ( DENY, "authldap/$method - temporary auth error" );
>   }
>   $ldwait  = $self->qp->config('ldap_timeout') || "5";
>   $ldmattr  = $self->qp->config('ldap_auth_filter_attr') || "uid";
> 
>   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 ( DENY, "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 ( DENY, "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 ( DENY, "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 ( $lduserdn = $mesg->entry->dn ) {
>     $ldh = Net::LDAP->new($ldhost, port=>$ldport, timeout=>$ldwait ) or
>       $self->log(LOGALERT, "authldap/$method - err in user conn" ) &&
>         return ( DENY, "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" );
>     }
> 
>   # this is if the plugin couldn't find user's entry
>   } else {
>     $self->log(LOGALERT, "authldap/$method - err in search results" ) &&
>       return ( DENY, "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
> 
> The only configuration file which is required is the 'ldap_base' file.  This
> file tells the plugin what your base DN is.  The plugin will deny access (the
> sane default) until it has been configured.
> 
> The configuration files '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 file '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 file '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 <[EMAIL PROTECTED]>
> 
> =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
> 

Reply via email to