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 in the.
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 (this is a regular input validation flow).
- If no userDN is configured, the AuthState opens a connection to the LDAP and searches for the user with either the configured userBaseDN and userFilter or with a baseDN and filter derived from the configured LDAP dialect (via the property dirStyle). If a userDN 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 and userSelectAttr is configured, the AuthState will display a selection of users based on the values of the attribute. If multiple users were found but userSelectAttr is not defined, the transition usernotunique 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 property dirStyle), a required password change can be signaled by a special error code. If userCheckOnly is enabled, this step is omitted. If the bind failed, the AuthState will stop processing with the result default.
- If new passwords are provided (properties newPw1Source and newPw2Source), 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 if userCheckOnly 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) or pwchange is selected. Otherwise, the result ok 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 section in the.
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 section in the.
User-profile support
User profile support by retrieving further attributes from the directory and delegateBaseDN property section in the.
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 describes the characteristics of the AuthState.
Topic | Description |
---|---|
Class | ch.nevis.esauth.auth.states.jndi.UseridPasswordAuthenticateState |
Logging | JNDIState, JNDI |
Auditing | none |
Marker | LDAP:username/password |
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>
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://Ll41Zsw54rmeNi2ZeoZD
The obfuscated password is generated using the command nevisauthencSecret.The 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 vmargs.conf.
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 connections
The 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-out
Topic | Description |
---|---|
searchSizeLimit (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. | |
Properties(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 attributespasswordexpwarnedfor 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 attributepasswordexpirationtime* for periodic password change. |
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. | |
Properties(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.Example: uid=${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 and loginidField (or userFilter) 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 withloginidFieldand 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). Example: ou=people,o=company,c=ch | |
loginidField (string, "cn" or "uid")This field must be set when userBaseDN is used and userFilter 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 andothe rs: "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 the inetOrgPerson object after a successful authentication. No variable substitution takes place for this property. | |
Properties(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 usingroleOccupant. * 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 and roleFilter 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. |
Examples:
<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"/>
Topic | Description |
---|---|
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) | |
Properties(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, thedelegateFilter 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.,cnwill be mappedcn*. |
Record syntax:
<attribute-name-in-directory>:<output-argument-name>
<attribute-name-in-directory>:<output-argument-name>:<transformation-regex>
Example:
cn:cn sn:sn givenName:givenName mail:email dn:baseDN:^.*?(ou=.*)$
cn *
Topic | Description |
---|---|
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 named dir.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. | |
Methods | authenticatestepup |
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 loginidField 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 and isiwebnewpw2 (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 attributepasswordexpwarned. 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. | |
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.Since nevisAuth/4.5.6.12. | |
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.Note 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 to roleTranformation, this will not result in noroles 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) |
*: See in this table. | |
Errors | 1: authentication failed (no password) 1: authentication failed, invalid input 1: differing password input*1: previous step timed out 1: user not found in root directory* 3: authentication failed (account locked) 4: password policy violated*5: password confirmation failed 6: password change required* 8: account locked 10: login is not unique*50: password too short 53: password in history* 55: password in dictionary 98: account disabled*99: authentication failed (directory problem) 99: authentication failed (internal)* 99: authentication failed (system problem) 99: missing loginid in session*99: password change failed (directory problem) 99: password change failed (system problem) 99: retrieving user profile failed (directory problem) 99: retrieving user profile failed (system problem) 99: 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(Dispatcher)
<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>
Example(Login)
<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>
Example(Password change)
<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).
Example(Authorization)
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>
Example(Profile delegation)
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 vmargs
# 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.