NAME

Connector::Builtin::Authentication::LDAP

DESCRIPTION

Connector (see perldoc Connector) to authenticate users against LDAP. Supports simple authentication (via LDAP bind), SASL authentication is not supported.

The module allows for direct bind or indirect bind (with preliminary user search). Direct bind is the most straightforward method, but it requires users to know their Distinguished Names (DNs) in LDAP. Indirect bind is more convenient for users, but it involves LDAP database search, which requires read access to larger parts of LDAP directory (so LDAP ACLs must be set properly to allow indirect bind).

The module implements group participation checking. With this option enabled, only users that belong to a predefined group may pass the authentication. The group is stored in LDAP directory (it may be for example an entry of type groupOfUniqueNames with the group participants listed in attribute uniqueMember).

When requesting indirect bind, the internal user search may return multiple DNs. By default this is treated as an error (because of ambiguity) and results with authentication failuer. This may be changed by setting a parameter named ambiguous, in which case the module will try to consecutively bind to each DN from the search result.

The indirect bind may be configured to use custom search filter, instead of the default one. This allows to incorporate additional restrictions on users based on their attributes stored in LDAP.

Usage

The username is the first component of the path, the password needs to be passed in the extended parameters using the key password.

Example:

$connector->get('username', {  password => 'mySecret' } );

To configure module for direct bind, the connector object should be created with parameter indirect => 0. This is the simplest authentication method and requires least parameters to be configured.

Example:

my $connector = Connector::Builtin::Authentication::LDAP->new({
    LOCATION => 'ldap://ldap.example.org',
    indirect => 0
})
my $result = $connector->get(
    'uid=jsmith,ou=people,dc=example,dc=org',
    { password => 'secret' }
);

Indirect bind, which is default, searches through the LDAP directory. This usually requires read access to database, and is performed by a separate user. We'll call that user binddn. For indirect-bind authentication, one usually has to provide DN and password of the existing binddn user.

Example:

my $connector = Connector::Builtin::Authentication::LDAP->new({
    LOCATION => 'ldap://ldap.example.org',
    binddn => 'cn=admin,dc=example,dc=org',
    password => 'binddnPassword'
})
my $result = $connector->get('jsmith', { password => 'secret' });

Two parameters are used to check group participation: groupdn and groupattr. The groupdn parameter specifies DN of a group entry and the groupattr specifies an attribute of the groupdn object where group participants are listed. If you specify groupdn, the group participation check is enabled.

Example:

# Assume, we have in LDAP:
#
# dn: cn=vip,dc=example,dc=org
# objectClass: groupOfNames
# member: uid=jsmith,ou=people,dc=example,dc=org
#
my $connector = Connector::Builtin::Authentication::LDAP->new({
    LOCATION => 'ldap://ldap.example.org',
    indirect => 0,
    binddn => 'cn=admin,dc=example,dc=org',
    password => 'binddnPassword',
    groupdn => 'cn=vip,dc=example,dc=org',
})
my $result = $connector->get(
    'uid=jsmith,ou=people,dc=example,dc=org',
    { password => 'secret' }
);

Note, that in this case we have provided binddn despite the direct-bind authentication was used. This is, because we needed read access to the cn=vip,dc=example,dc=org entry (the group object).

The indirect-bind method accepts custom filters for user search.

Example:

my $connector = Connector::Builtin::Authentication::LDAP->new({
    LOCATION => 'ldap://ldap.example.org',
    binddn => 'cn=admin,dc=example,dc=org',
    password => 'binddnPassword',
    filter => '(&(uid=[% LOGIN %])(accountStatus=active))'
})
my $result = $connector->get('jsmith', { password => 'secret' });

You may substitute user name by using [% LOGIN %] template parameter, as shown in the above example.

Configuration

Below is the full list of configuration options.

Connection options

keepalive => 1

If given, set the socket's SO_KEEPALIVE option depending on the Boolean value of the option. (Default: use system default).

timeout => N

Timeout passed to IO::Socket when connecting the remote server. (Default: 120)

multihomed => N

Will be passed to IO::Socket as the MultiHomed parameter when connecting to the remote server

localaddr => HOST

Will be passed to IO::Socket as the LocalAddr parameter, which sets the client's IP address (as opposed to the server's IP address.)

debug => N

Set the LDAP debug level.

SSL Connection options

verify => 'none' | 'optional' | 'require'

How to verify the server's certificate:

none
    The server may provide a certificate but it will not be checked - this
    may mean you are be connected to the wrong server
optional
    Verify only when the server offers a certificate
require
    The server must provide a certificate, and it must be valid.

If you set verify to optional or require, you must also set either cafile or capath. The most secure option is require.

sslversion => 'sslv2' | 'sslv3' | 'sslv23' | 'tlsv1'

This defines the version of the SSL/TLS protocol to use. Defaults to 'tlsv1'.

ciphers => CIPHERS

Specify which subset of cipher suites are permissible for this connection, using the standard OpenSSL string format. The default behavior is to keep the decision on the underlying cryptographic library.

capath => '/path/to/servercerts/'

See cafile.

cafile => '/path/to/servercert.pem'

When verifying the server's certificate, either set capath to the pathname of the directory containing CA certificates, or set cafile to the filename containing the certificate of the CA who signed the server's certificate. These certificates must all be in PEM format.

clientcert => '/path/to/cert.pem'

See clientkey.

clientkey => '/path/to/key.pem'

If you want to use the client to offer a certificate to the server for SSL authentication (which is not the same as for the LDAP Bind operation) then set clientcert to the user's certificate file, and clientkey to the user's private key file. These files must be in PEM format.

checkcrl => 1

BindDN

binddn => DN

Distinguished Name of the LDAP entry used to search LDAP database for users being authenticated (indirect bind) and check their group participation.

password => PASSWORD

Password for the binddn user.

Search options (indirect bind)

timelimit => N

A timelimit that restricts the maximum time (in seconds) allowed for a search. A value of 0 (the default), means that no timelimit will be requested.

sizelimit => N

A sizelimit that restricts the maximum number of entries to be returned as a result of the search. A value of 0, and the default, means that no restriction is requested. Servers may enforce a maximum number of entries to return.

base => DN

The DN that is the base object entry relative to which the search is to be performed.

filter => TEMPLATESTRING

A filter that defines the conditions an entry in the directory must meet in order for it to be returned by the search. This may be a (template) string or a Net::LDAP::Filter object.

scope => 'base' | 'one' | 'sub' | 'subtree' | 'children'

By default the search is performed on the whole tree below the specified base object. This maybe changed by specifying a scope parameter with one of the following values:

base
    Search only the base object.
one
    Search the entries immediately below the base object.
sub
subtree
    Search the whole tree below (and including) the base object. This is
    the default.
children
    Search the whole subtree below the base object, excluding the base object itself.

Note: children scope requires LDAPv3 subordinate feature extension.

Other options

userattr => ATTRNAME

If the search filter (for indirect bind) is not specified, it is constructed internally as "($userattr=[% LOGIN %])", where $userattr represents the value of userattr parameter.

groupattr => ATTRNAME

If groupdn is specified by caller, the groupattr defines an attribute within groupdn object which shall be compared against the DN of the user being authenticated in order to check its participation to the group. Defaults to 'member'.

groupdn => DN

DN of an LDAP entry which defines a group of users allowed to be authenticated. If not defined, the group participation is not checked.

indirect => 1 | 0

Use indirect bind (default). Set to 0 to disable indirect bind and use direct bind.

ambiguous => 0 | 1

Accept ambiguous search results when doing indirect-bind authentication. By default, this option is disabled.

Return values

1 if the password matches, 0 if the user is found but the password does not match and undef if the user is not found (or it's found but group check failed).

Limitations

User names are limited to so called valueencoding syntax defined by RFC4515. We allow non-ascii (utf-8) characters and non-printable characters. Invalid names are treated as not found.