UseridPasswordAuthenticateState
Introduction and overview
The JNDI-based UseridPasswordAuthenticateState AuthState permits performing simple authentication (e.g., password login) to any LDAP directory via LDAP or LDAPS.
The AuthState supports the following features:
- Simple login (login ID, password).
- Prospect login (the directory is queried for the user's existence, but no credential is checked).
- Password change (the user's userCredential attribute can be updated with a new password).
- Password expiration detection (SunONE, Novell eDirectory, AD and OpenLDAP).
- Direct directory bind, in case of a flat directory structure (see the
userDN
property).
During a login, the AuthState processes the following steps:
- The AuthState checks that all input fields configured in the GUI are provided by the user. Otherwise, the GUI is displayed (If the AuthState is final the GUI is always displayed).
- If no
userDN
is configured, the AuthState opens a connection to the LDAP and searches for the user with either the configureduserBaseDN
anduserFilter
or with abaseDN
and filter derived from the configured LDAP dialect (via the propertydirStyle
). If auserDN
is configured, this connection will use the DN and password specified in the connection string. - If no user was found, the result
usernotfound
is returned. If multiple users were found anduserSelectAttr
is configured, the AuthState will display a selection of users based on the values of the attribute. If multiple users were found butuserSelectAttr
is not defined, the transitionusernotunique
is returned. - The AuthState uses the
userDN
and the user password to bind to the LDAP. If this bind succeeds, the user is authenticated. Depending on the dialect of the server (set in propertydirStyle
), a required password change can be signaled by a special error code. IfuserCheckOnly
is enabled, this step is omitted. If the bind failed, the AuthState will stop processing with the resultdefault
. - If new passwords are provided (properties
newPw1Source
andnewPw2Source
), a password change is attempted using the connection bound to the user in step 3. The details of the password change depend on the dialect configured. This step is ommitted ifuserCheckOnly
is enabled. - The user attributes are fetched. This query will be performed according to the `delegate`` properties configured, but some additional, dialect-specific attributes that are used for password expiration detection are also fetched.
- The user roles are fetched and transformed according to the `role`` properties configured.
- If a password expiration has been detected in step 3 or 5, the result
pwchangerequired
(if a matching transition is configured) orpwchange
is selected. Otherwise, the resultok
is returned.
Structured user sub-tree in LDAP
Indirect directory bind by first locating the user anonymously or with a functional LDAP account (see userBaseDN
property).
Authorization support
Coarse-grained authorization support on nevisProxy by retrieving role or group information from the directory and propagate them as Nevis security roles (see roleBaseDN
property).
User-profile support
User profile support by retrieving further attributes from the directory and delegateBaseDN
property.
Fail-safety
The LDAP connector is failsafe, performing a failover when the client library (JNDI) throws a communication exception.Note: Old releases may perform a failover on any exception, i.e., a user providing an invalid passphrase is authenticated against every directory. If the directory uses a failure counter, bookkeeping of user login failures may therefore be wrong.
Description
The following table and chapters describe the characteristics of the AuthState.
Topic | Description |
---|---|
Class | ch.nevis.esauth.auth.states.jndi.UseridPasswordAuthenticateState |
Logging | JNDIState, JNDI |
Auditing | none |
Marker | LDAP:username/password |
Methods | authenticate, setup |
Properties
Network
connection1, ..., connection9
(url, -)This property configures the network connection and simple (functional) authentication when the authentication needs to query the LDAP to have access to directory subtrees (login information is required if required data is not visible, i.e., authorized to an anonymous client). If two-way SSL is used, the LDAP directory may perform authentication based on the client certificate. As JNDI is used as a client library, connection parameters, keystores and truststores must be configured as system properties.
Connections are used in the indexed order for fail-safety, i.e., load balancing and stateful target discarding are not supported at the time. The general syntax is:
(ldap|ldaps)://<host-or-ip>:<port>
[<functional-user-DN> (neviscred://<neviscred-alias>|secret://<obfuscated-password>|<plain-password)]This AuthState uses the LDAP protocol when establishing a connection with an LDAP server. The AuthState is thus susceptible to SOCKS proxies, as described in the chapter Configuring proxies.
Examples:
ldap://ldap.siven.ch:389
ldaps://ldap.siven.ch:636 CN=admin,O=siven,C=ch secret://Ll41Zsw54rmeNi2ZeoZDThe following (backward compatible) properties are available too:
- url (URL): URL part of the connection
- user (DN): User part of the connection above
- password (string): Password part of the connection
The default connection time-out is five seconds (com.sun.jndi.ldap.connect.timeout=5000). The JNDI client sets the following system properties by default (see Sun documentation for details). To configure different behavior, set the appropriate values in
JAVA_OPS
.com.sun.jndi.ldap.connect.pool.maxsize=8
com.sun.jndi.ldap.connect.pool.timeout=300000 // cache connections for up to 5 minutes
com.sun.jndi.ldap.connect.pool.protocol="plain ssl"
com.sun.jndi.ldap.connect.pool.authentication=none // only cache unauthentic connectionsThe following per-pool defaults will be set and may be overridden by defining equivalent properties for the AuthState:
com.sun.jndi.ldap.connect.pool=true // turn on connection pooling
com.sun.jndi.ldap.connect.timeout=5000 // 5sec connect time-out
com.sun.jndi.ldap.read.timeout=10000 // 10sec read time-outsearchSizeLimit
(int, 256)This property limits the number of results returned by an LDAP search. This way, you can control resources such as memory and network bandwidth.
Policies
dirStyle
(enum { SunONE, AD, AD-basic, OpenLDAP, eDirectory }. SunONE)The directory style is used to control directory dependent behavior of the AuthState:
- SunONE: Uses "uid" as the person's login-ID attribute. Checks well-known attributes
passwordexpwarned
for initial and periodic password change. - AD: Uses "cn" as the person's login-ID attribute. Checks the well-known attributes whenCreated and pwdLastSet for initial and periodic password change. Support for a password change with a technical user before binding with the personal user. Password change is detected through the error codes 532 (password expired) and 773 (user must reset password) and verified by checking
msDS-User-Account-Control-Computed
. - AD-basic: Only binds against AD. Reading data from AD is not possible due to forest referral problems.
- OpenLDAP: Same as SunONE but without support of policy overlay for password change detection.
- eDirectory: Uses "cn" as the person's login-ID attribute. Checks attribute
passwordexpirationtime
for periodic password change.
- SunONE: Uses "uid" as the person's login-ID attribute. Checks well-known attributes
propagateErrors
(boolean, "true")When set to "true", error messages from the JNDI subsystem will be propagated in the OutArg
jndi.error
.userCheckOnly
(boolean, false)This attribute is used when the AuthState is only configured with a
isiwebuserid
input (to only check for the user's existence, i.e., prospect authentication). It needs to be set to "true" to signal that no credential is required to proceed.
User
userDN
(DN, -)This property is used to configure a parameterized distinguished name addressing an inetOrgPerson node in the LDAP directory. In this case, all the users to authenticate must be in the same (flat) directory node. To parameterize search, use the variable
${notes:userid}
instead of${inargs:isiwebuserid}
because the user input needs to be checked for JNDI injection."Exampleuid=${notes:requestId},ou=people,o=company,c=ch
With this approach, the AuthState uses the user's account to authenticate against the LDAP directory. A functional user is not required. Privileges to read role and delegation data (see below) need to be set accordingly in the directory.
For hierarchical directories and a precedent user search, use
userBaseDN
andloginidField
(oruserFilter
) instead.userBaseDN
(DN, -)This property specifies the directory subtree where all users are located. The following restrictions need to be considered:
- The subtree should not contain other object classes that have an attribute as configured with
loginidField
and matching the filteruserFilter
- The subtree must be readable for an anonymous client, or a functional user authentication needs to be used (see
connection1
property above).
Exampleou=people,o=company,c=ch
- The subtree should not contain other object classes that have an attribute as configured with
loginidField
(string, "cn" or "uid")This field must be set when
userBaseDN
is used anduserFilter
is not configured. It specifies the attribute in the LDAP directory that should match the users login-ID input.The default depends on the directory used:
- SunONE: "uid"
- AD and others: "cn"
userFilter
(JNDI filter, "(&(objectClass=person)(cn=${notes:userid}))")This attribute allows to customize the user search filter according to specific needs. It is usually not explicitly set.
userSelectAttr
(string, -)If this attribute is set to a (comma-separated) list of LDAP attribute name(s) (e.g., "email"), it is used to resolve ambiguous login-IDs by querying the attribute(s) and presenting the resulting list to the user. The user may select the account to proceed with. Otherwise, the login is aborted because it is not clear which account to use.
useridSource
(string, "${inargs.isiwebuserid}")This attribute allows to change the input argument holding the userID sent by the user.
passwordSource
(string, ${inargs:isiwebpasswd})The password of the user with an LDAP bind operation is attempted.
newPw1Source, newPw2Source
(string, ${inargs:isiwebnewpw1} / ${inargs:isiwebnewpw1})Source variables for the new password. If these values resolve to non-empty strings, a password change will be attempted.
useridField
(string, "dn")Declares to which
inetOrgPerson
object field the user ID should be set after a successful authentication took place. I.e., setting the property value to "dn" results in the user ID being set to the distinguished name of theinetOrgPerson
object after a successful authentication. No variable substitution takes place for this property.
Role
roleBaseDN
(DN, -)This property specifies the directory subtree where role or group information is stored. If not set, no authorization data is fetched from the directory.
Directories usually support some basic authorization storage by the following means:
- objectClass "organizationalRole": References to the DN of the user can be set using roleOccupant.
- objectClass "posixGroup" (unix groups): References to the user's user ID ("uid") can be set using memberUid.
- objectClass "top" (AD only): References to some other entity can be set using memberOf. The AD uses this to store group memberships.
roleNameField
(string, "dn")This property specifies which attribute of a role object in the directory should be used as a Nevis security role name. Note that using the default attribute "dn" may result in huge security role lists. See
roleBaseDN
androleFilter
for further details.roleFilter
(JNDI filter, "(&(roleOccupant=${notes:userdn})(objectClass=organizationalRole))")Specifying this property allows to customize the role query to apply to the tree specified by roleBaseDN.
Example<property name="roleFilter" value="(&(roleOccupant=${notes:userdn})(objectClass=organizationalRole))"/>
<property name="roleNameField" value="dn"/>
<property name="roleFilter" value="(&(memberUid=${notes:userid})(objectClass=posixGroup))"/>
<property name="roleNameField" value="cn"/>
<property name="roleFilter" value="(&(cn=${notes:userid})(objectClass=person))"/>
<property name="roleNameField" value="memberOf"/>roleTransformation
(regular expression, -)This property specifies a regular expression that transforms the role names. If defined, roles not matching the regular expression will be discarded, and if a grouping is defined in the expression, the result of the grouping will form the role name.
Examples:
^cn=([^_,]+).*$ (use only the "cn" part of the DN and only the prefix until the first underscore)
Profile
delegateBaseDN
(DN, authenticated user's DN)To propagate further attributes to proxy and application, the LDAP authentication plug-in per default delegates all readable attributes of the user's LDAP entry "as is". When customizing this behavior, these attributes may be filtered or prefixed with a scope identifier to prevent collisions with other delegation attribute sources. The user profiles may even be fetched from another directory context.
This property specifies the directory subtree where user profile data needs to be queried. If not specified, the authenticated user's DN is used. If this property is set, the delegateFilter property must also be set.
delegateFilter
(JNDI filter, -)Specifying this property allows to customize the attribute filter used to query the user's profile attributes.
Example<property name="delegateBaseDN" value="ou=profiles,o=siven,c=ch"/>
<property name="delegateFilter" value="(&(uid=${request:userId})(objectClass=person))"/>delegateMap
(string, -)The property defines a whitespace-separated list of mappings from LDAP attributes to delegate names. The specified LDAP attributes are queried and set as output arguments with the specified output argument name. If delegatePrefix is set, the map must be adapted accordingly because it is applied after prefixing the directory attributes.
Delegation of all attributes is possible using the wildcard character (*). The output argument name will automatically be set to the attribute name in the directory. I.e., cn will be mapped cn.
Record syntax<attribute-name-in-directory>:<output-argument-name>
<attribute-name-in-directory>:<output-argument-name>:<transformation-regex>
*Examplecn:cn sn:sn givenName:givenName mail:email dn:baseDN:^.*?(ou=.*)$
cn *delegatePrefix
(string, -)The string configured is used to prefix all attributes that are fetched from the directory. E.g., using
dir.
as a prefix results in LDAP attributes being nameddir.cn
,dir.uid
, etc. This may collide with the definitions in the delegateMap configuration.delegateMode
(enum {single, multiple, list}, single)Defines if just the first delegate object should be used or if all results that match the delegateFilter are propagated:
single
Consider only the first attribute of the first result.multiple
Concatenate all attributes with the same name using delegateSeparator and add a counter postfix for each result found.list
Same as "multiple", but only the first result is considered.
delegateEmptyFields
(boolean, "false")Defines whether to delegate attributes that are not set in the LDAP.
delegateSeparator
(string, ",")If
delegateMode=multiple
is set, this string allows to override the default attribute list separator (which is a comma).propagationScope
(enum {outargs, notes, session, inctx, inargs, roles}, outargs)Defines the scope of delegated variables.
escapeSpecialChars
(boolean, false)If enabled, special characters in the LDAP queries, e.g., umlauts, are escaped using corresponding hex values of the extended ASCII table.
Input
isiwebuserid
(user, required)Login ID, known by the user
isiwebpasswd
(user, optional)Password matching the userPassword attribute in the directory. Optional means that the GUI descriptor in the configuration defines if the credential is required. See the property
userCheckOnly
in this table for related information.isiwebnewpw1, isiwebnewpw2
(user, optional)This input is required for password change only. The AuthEngine enforces user input if these fields are defined in the configured GUI descriptor.
Transitions
ok
User was authenticated
usernotfound
User not found in the directory. This is usually handled as an input failure (user mistyped login-ID), but the result can be used to invoke some alternative authentication method.
usernotunique
Multiple users found in the directory. This result can be used to make a transition to another LDAP AuthState instance with another
loginid
Field and GUI descriptor configuration, e.g., locating the user based on the e-mail address.pwchange
The directory signals a required password change. The password change can be ignored (as the user was already authenticated) or may be used to address another instance of the plug-in with a GUI descriptor that requires entry of
isiwebnewpw1
andisiwebnewpw2
(see above).The password change detection works as follows:
- SunONE: Password policy must be enabled on the directory server. The directory signals the required password change by setting the attribute
passwordexpwarned
. - AD: We try to bind the user against the AD. If the error codes 532 (password expired) or 773 (user must reset password) are returned by the AD, we assume that a password change was requested. This is verified by checking a bit flag in the user property
msDS-User-Account-Control-Computed
.
- SunONE: Password policy must be enabled on the directory server. The directory signals the required password change by setting the attribute
pwchangerequired
Like
pwchange
, but it indicates that the user is not actually authenticated (only prospect). This has been introduced for AD, where the password change check must be done with a technical user without the possibility to authenticate. Never treat this password change as optional!For backward compatibility, this transition is only returned if it is configured. If an AD account requires a password change and
pwchangerequired
is not configured, a personal bind will be tried.pwnotset
The directory signals that the
userPassword
is not set yet (SunONE only). This result may be used to trigger some migration or initialization mechanism.noroles
The user was authenticated, but the role query found no assigned roles. This transition is only chosen if role fetching has been configured. If the transition is not defined, the fallback transition ok is used.
infoNote that the system decides whether to choose this transition before applying
roleTransformation
on the fetched roles. That is, if roles are fetched but dropped afterwards due toroleTranformation
, this will not result innoroles
being chosen.
Output
Depending on the dirStyle configured, some login-related attributes are delegated automatically:
SunONE
passwordexpirationtime
passwordallowchangetime
passwordexpwarned
passwordretrycount
AD
pwdLastSet
whenCreated
lastLogon
eDirectory
logingraceremaining
passwordexpirationtime
OpenLDAP
none
AD-basic
none
Errors
lasterror=1
lasterrorinfo=authentication failed (no password)
lasterror=1
lasterrorinfo=authentication failed, invalid input
lasterror=1
lasterrorinfo=differing password input
lasterror=1
lasterrorinfo=previous step timed out
lasterror=1
lasterrorinfo=user not found in root directory
lasterror=3
lasterrorinfo=authentication failed (account locked)
lasterror=3
lasterrorinfo=password policy violated
lasterror=5
lasterrorinfo=password confirmation failed
lasterror=6
lasterrorinfo=password change required
lasterror=8
lasterrorinfo=account locked
lasterror=10
lasterrorinfo=login is not unique
lasterror=50
lasterrorinfo=password too short
lasterror=53
lasterrorinfo=password in history
lasterror=55
lasterrorinfo=password in dictionary
lasterror=98
lasterrorinfo=account disabled
lasterror=99
lasterrorinfo=authentication failed (directory problem)
lasterror=99
lasterrorinfo=authentication failed (internal)
lasterror=99
lasterrorinfo=authentication failed (system problem)
lasterror=99
lasterrorinfo=missing loginid in session
lasterror=99
lasterrorinfo=password change failed (directory problem)
lasterror=99
lasterrorinfo=password change failed (system problem)
lasterror=99
lasterrorinfo=retrieving user profile failed (directory problem)
lasterror=99
lasterrorinfo=retrieving user profile failed (system problem)
lasterror=99
lasterrorinfo=User <DN> does not have a <useridField> attribute, check LDAP entry
Notes
loginid
Set to the user's login ID as soon as it is known (entered by the user or retrieved from the existing authentication session). The entry was checked for JNDI injection and can be referenced in the configuration.
userid
This note is set to the same value as
loginid
and the value is switched to the authenticated user ID when the user profile is processed after successful authentication (and password change).userdn
The user's DN in the directory. The note is set as soon as the LDAP query is constructed from the configured userDN property. When
userBaseDN
is used, the value is set to the DN of the user found in the directory.
Example
<Domain name="SSO1" default="true"
<Entry method="authenticate" state="LdapLogin"/>
<Entry method="authenticate" state="LdapPwChange" selector="/login/admin/pw"/>
<Entry method="stepup" state="LdapPwChange"/>
</Domain>
<AuthState name="LdapLogin"
class="ch.nevis.esauth.auth.states.jndi.UseridPasswordAuthenticateState">
<ResultCond name="ok" next="AuthDone"/>
<ResultCond name="pwchange" next="LdapPwChange"/>
<ResultCond name="pwnotset" next="LdapPwChange"/>
<ResultCond name="usernotunique" next="LdapAlternate"/>
<ResultCond name="usernotfound" next="LdapLogin"/>
<Response value="AUTH_CONTINUE">
<Gui name="AuthUidPwDialog" label="login.uidpw.label">
<GuiElem name="lasterror" type="error" label="${notes:lasterrorinfo}" value="${notes:lasterror}"/>
<GuiElem name="info" type="info" label="login.uidpw.text"/>
<GuiElem name="isiwebuserid" type="text" label="userid.label" value="${request:loginId}"/>
<GuiElem name="isiwebpasswd" type="pw-text" label="password.label"/>
<GuiElem name="submit" type="button" label="submit.button.label" value="Login"/>
</Gui>
</Response>
<!-- list the ldap hosts -->
<property name="connection1" value="ldap://ldap.company.ch:389"/>
<property name="connection2" value="ldaps://192.168.4.216:636"/>
<!-- authentication: DN to bind as -->
<property name="userDN" value="uid=${request:loginId},ou=people,o=company,c=ch"/>
<!-- authenticated user identity -->
<property name="useridField" value="uid"/>
</AuthState>
<AuthState name="LdapPwChange"
class="ch.nevis.esauth.auth.states.jndi.UseridPasswordAuthenticateState">
<ResultCond name="ok" next="AuthDone"/>
<Response value="AUTH_CONTINUE">
<Gui name="AuthPwChangeDialog" label="login.pwchange.label">
<GuiElem name="lasterror" type="error" label="${notes:lasterrorinfo}" value="${notes:lasterror}"/>
<GuiElem name="info" type="info" label="newpassword.text"/>
<GuiElem name="isiwebuserid" type="text" label="userid.label" value="${request:loginId}"/>
<GuiElem name="isiwebpasswd" type="pw-text" label="password.label"/>
<GuiElem name="isiwebnewpw1" type="pw-text" label="newpassword.label"/>
<GuiElem name="isiwebnewpw2" type="pw-text" label="newpassword.label"/>
<GuiElem name="submit" type="button" label="submit.button.label" value="Change Password"/>
</Gui>
</Response>
<!-- use config from other auth state -->
<propertyRef name="LdapLogin"/>
</AuthState>
Common configuration parameters are specified only once and referenced by the other states via the propertyRef element.
If this state is used as a stepup (existing session) or addressed via a pwchange
transition, the isiwebuserid
field can be omitted (for convenience).
::::
If the directory contains enterprise roles for the user, these roles may be delegated as security roles to the proxy and applications. This is enabled via the following configuration:
<AuthState name="LdapLogin" ...>
....
<property name="roleBaseDN" value="ou=roles,o=company,c=ch"/>
<property name="roleFilter"
value="(&(roleOccupant=${notes:userdn})(objectClass=organizationalRole))"/>
<property name="roleNameField" value="dn"/>
</AuthState>
Customized output arguments as follows:
<AuthState name="LdapLogin" ...>
....
<property name="delegateBaseDN" value="ou=profiles,o=company,c=ch"/>
<property name="delegateFilter" value="(&(uid=${request:userId})(objectClass=person))"/>
<property name="delegateMap" value="cn:CommonName sn:SurName givenname:MyName"/>-->
</AuthState>
Testing
The above configuration examples can be tested by performing the following LDAP query:
# Linux/OpenLDAP
ldapsearch -H ldap://ldap.company.ch:389 \
-x -D uid=someuser,ou=people,o=company,c=ch -W \
-b o=company,c=ch '(&(uid=someuser)(objectClass=person))'
# Solaris
ldapsearch -h ldap.company.ch -p 389 \
-D uid=someuser,ou=people,o=company,c=ch \
-b o=company,c=ch '(&(uid=someuser)(objectClass=person))'
LDAPS
If LDAPS is used (default port 636), the client may at least need a truststore to verify the server's certificate. This is done by setting the JSSE system properties as follows:
nevisauth config env
# 1-way SSL, verify server cert
-Djavax.net.ssl.trustStore=/var/opt/neviskeybox/default/default/
truststore.jks
Debugging using the ldapsearch client requires a similar setup:
echo "TLS_CACERT=/var/opt/neviskeybox/default/default/truststore.pem" >$HOME/.ldaprc
ldapserach -H ldaps://ldap.company.com:636 ....
Notes on Active Directory integration
To integrate an Active Directory, the normal setup is as follows:
- Connect with LDAPS
- Bind with a technical user and locate the user distinguished name (DN) with a tree search, searching on sAMAccountName from the configured userBaseDN.
Note especially the following points:
- The domain\user notation will work for the bind but not for searches or password changes because it is not a valid user DN.
- A user cannot bind if his password needs to be changed and can therefore not change his password with "his own permissions"; a technical account is needed. You will get error code 19 - 00000056: "AtrErr: DSID-03190F00" or similar if you try.
- You need to be connected with LDAPS so that user password can be changed when connected (bound) with a technical account. You will get something like error code 53 - 00002077: "SvcErr: DSID-03190DC9" if you try without SSL.
- After a successful password change, the old password can be used for a while to bind but not for another password change.
Proxy support
The UseridPasswordAuthenticateState AuthState uses the LDAP protocol when establishing a connection with an LDAP server. The AuthState is thus susceptible to SOCKS proxies, as described in the chapter Configuring proxies.