I have been working with Sun, Oracle and ForgeRock products for some time now and am always looking for new and interesting topics that pertain to theirs and other open source identity products. When Google alerted me to the following blog posting, I just couldn’t resist:
Radovan Semancik | February 25, 2015
There were two things in the alert that caught my attention. The first was the title and the obvious implications that it contained and the second is the author of the blog and the fact that he’s associated with Evolveum, a ForgeRock OpenIDM competitor.
The identity community is relatively small and I have read many of Radovan’s postings in the past. We share a few of the same mailing lists and I have seen his questions/comments come up in those forums from time to time. I have never met Radovan in person, but I believe we are probably more alike than different. We share a common lineage; both being successful Sun identity integrators. We both agree that open source identity is preferable to closed source solutions. And it seems that we both share many of the same concerns over Internet privacy. So when I saw this posting, I had to find out what Radovan had discovered that I must have missed over the past 15 years in working with these products. After reading his blog posting, however, I do not share his same concerns nor do I come to the same conclusions. In addition, there are several inaccuracies in the blog that could easily be misinterpreted and are being used to spread fear, uncertainty, and doubt around OpenAM.
What follows are my responses to each of Radovan’s concerns regarding OpenAM. These are based on my experiences of working with the product for over 15 years and as Radovan aptly said, “your mileage may vary.”
In the blog Radovan comments “OpenAM is formally Java 6. Which is a problem in itself. Java 6 does not have any public updates for almost two years.”
ForgeRock is not stuck with Java 6. In fact, OpenAM 12 supports Java 7 and Java 8. I have personally worked for governmental agencies that simply cannot upgrade their Java version for one reason or another. ForgeRock must make their products both forward looking as well as backward compatible in order to support their vast customer base.
In the blog Radovan comments “OpenAM also does not have any documents describing the system architecture from a developers point of view.”
I agree with Radovan that early versions of the documentation were limited. As with any startup, documentation is one of the things that suffers during the initial phases, but over the past couple of years, this has flipped. Due to the efforts of the ForgeRock documentation team I now find most of my questions answered in the ForgeRock documentation. In addition, ForgeRock is a commercial open source company, so they do not make all high value documents publicly available. This is part of the ForgeRock value proposition for subscription customers.
In the blog Radovan comments “OpenAM is huge. It consists of approx. 2 million lines of source code. It is also quite complicated. There is some component structure. But it does not make much sense on the first sight.”
I believe that Radovan is confusing the open source trunk with commercial open source product. Simply put, ForgeRock does not include all code from the trunk in the OpenAM commercial offering. As an example the extensions directory, which is not part of the product, has almost 1000 Java files in it.
More importantly, you need to be careful in attempting to judge functionality, quality, and security based solely on the number of lines of code in any product. When I worked at AT&T, I was part of a development team responsible for way more than 2M lines of code. My personal area of responsibility was directly related to approximately 250K lines of code that I knew inside and out. A sales rep could ask me a question regarding a particular feature or issue and I could envision the file, module, and even where in the code the question pertained (other developers can relate to this). Oh, and this code was rock solid.
In the blog Radovan comments that the “bulk of the OpenAM code is still efficiently Java 1.4 or even older.”
Is this really a concern? During the initial stages of my career as a software developer, my mentor beat into my head the following mantra:
If it ain’t broke, don’t fix it!
I didn’t always agree with my mentor, but I was reminded of this lesson each time I introduced bugs into code that I was simply trying to make better. Almost 25 years later this motto has stuck with me but over time I have modified it to be:
If it ain’t broke, don’t fix it, unless there is a damn good reason to do so!
It has been my experience that ForgeRock follows a mantra similar to my modified version. When they decide to refactor the code, they do so based on customer or market demand not just because there are newer ways to do it. If the old way works, performance is not limited, and security is not endangered, then why change it. Based on my experience with closed-source vendors, this is exactly what they do; their source code, however, is hidden so you don’t know how old it really is.
A final thought on refactoring. ForgeRock has refactored the Entitlements Engine and the Secure Token Service (both pretty mammoth projects) all while fixing bugs, responding to RFEs, and implementing new market-driven features such as:
- Adaptive Authentication
- New XUI Interface
- REST STS
- WS-TRUST STS
- OpenID Connect
- OAuth 2.0
- Core Token Service
In my opinion, ForgeRock product development is focused on the right areas.
In the blog Radovan comments “OpenAM is in fact (at least) two somehow separate products. There is “AM” part and “FM” part.”
From what I understand, ForgeRock intentionally keeps the federation code independent. This was done so that administrators could easily create and export a “Fedlet” which is essentially a small web application that provides a customer with the code they need to implement SAML in a non-SAML application. In short, keeping it separate allows for sharing between the OpenAM core services and providing session independent federation capability. Keeping federation independent has also made it possible to leverage the functionality in other products such as OpenIG.
In the blog Radovan comments “OpenAM debugging is a pain. It is almost uncontrollable, it floods log files with useless data and the little pieces of useful information are lost in it.“
There are several places that you can look in order to debug OpenAM issues and where you look depends mostly on how you have implemented the product.
- Debug Files – OpenAM debug files contain messages output by the developer in order to debug code. This includes a timestamp, the ID of the thread that called the Debug API, the message recorded by the code (error, info, warning), and optionally, a Java stack trace. As a default, the verbosity level is low, but you can increase the verbosity to see additional messages.
- OpenAM Log Files – OpenAM log files contain operational information for the OpenAM components. They are not designed for debugging purposes, but they may shed additional light in the debugging process. As a default, the verbosity level is low, but you can increase the verbosity to see additional messages.
- Java Container Log Files – The Java container hosting the OpenAM application will also contain log files that may assist in the debugging process. These log files contain general connection request/response for all connectivity to/from OpenAM.
- Policy Agent Log Files – Policy Agents also generate log messages when used in an OpenAM implementation. These log files may be stored on the server hosting the Policy Agent or on OpenAM, itself (or both).
I will agree with Radovan’s comments that this can be intimidating at first, but as with most enterprise products, knowing where to look and how to interpret the results is as much of an art as it is a science. For someone new to OpenAM, debugging can be complex. For skilled OpenAM customers, integrators, and ForgeRock staff, the debug logs yield a goldmine of valuable information that often assists in the rapid diagnosis of a problem.
Note: Debugging the source code is the realm of experienced developers and ForgeRock does not expect their customers to diagnose product issues.
For those who stick strictly to the open source version, the learning curve can be steep and they have to rely on the open source community for answers (but hey, what do you want for free). ForgeRock customers, however, will most likely have taken some training on the product to know where to look and what to look for. In the event that they need to work with ForgeRock’s 24×7 global support desk, then they will most likely be asked to capture these files (as well as configuration information) in order to submit a ticket to ForgeRock.
In the blog Radovan comments that the “OpenAM is still using obsolete technologies such as JAX-RPC. JAX-RPC is a really bad API.” He then goes on to recommend Apache CXF and states “it takes only a handful of lines of code to do. But not in OpenAM.”
Ironically, OpenAM 12 has a modern REST STS along with a WS-TRUST Apache CXF based implementation (exactly what Radovan recommends). ForgeRock began migrating away from JAX-RPC towards REST-based web services as early as version 11.0. Now with OpenAM 12, ForgeRock has a modern (fully documented) REST STS along with a WS-TRUST Apache CXF based implementation (exactly what Radovan recommends).
ForgeRock’s commitment to REST is so strong, in fact, that they have invested heavily in the ForgeRock Common REST (CREST) Framework and API – which is used across all of their products. They are the only vendor that I am aware of that provides REST interfaces across all products in their IAM stack. This doesn’t mean, however, that ForgeRock can simply eliminate JAX-RPC functionality from the product. They must continue to support JAX-RPC to maintain backwards compatibility for existing customers that are utilizing this functionality.
In the blog Radovan comments “OpenAM originated between 1998 and 2002. And the better part of the code is stuck in that time as well.”
In general, Radovan focuses on very specific things he does not like in OpenAM, but ignores all the innovations and enhancements that have been implemented since Sun Microsystems. As mentioned earlier, ForgeRock has continuously refactored, rewritten, and added several major new features to OpenAM.
“ForgeRock also has a mandatory code review process for every code modification. I have experienced that process first-hand when we were cooperating on OpenICF. This process heavily impacts efficiency and that was one of the reasons why we have separated from OpenICF project.”
I understand how in today’s Agile focused world there is the tendency to shy away from old school concepts such as design reviews and code reviews. I understand the concerns about how they “take forever” and “cost a lot of money”, but consider the actual cost of a bug getting out the door and into a customer’s environment. The cost is born by both the vendor and the customer but ultimately it is the vendor who incurs a loss of trust, reputation, and ultimately customers. Call me old school, but I will opt for code reviews every time – especially when my customer’s security is on the line.
Note: there is an interesting debate on the effectiveness of code reviews on Slashdot.
So, while I respect Radovan’s opinions, I don’t share them and apparently neither do many of the rather large companies and DOD entities that have implemented OpenAM in their own environments. The DOD is pretty extensive when it comes to product reviews and I have worked with several Fortune 500 companies that have had their hands all up in the code – and still choose to use it. I have worked with companies that elect to have a minimal IAM implementation team (and rely on ForgeRock for total support) to those that have a team of developers building in and around their IAM solution. I have seen some pretty impressive integrations between OpenAM log files, debug files, and the actual source code using tools such as Splunk. And while you don’t need to go to the extent that I have seen some companies go in getting into the code, knowing that you could if you wanted to is a nice thing to have in your back pocket. That is the benefit of open source code and one of the benefits of working with ForgeRock in general.
I can remember working on an implementation for one rather large IAM vendor where we spent more than three months waiting for a patch. Every status meeting with the customer became more and more uncomfortable as we waited for the vendor to respond. With ForgeRock software, I have the opportunity to look into the code and put in my own temporary patch if necessary. I can even submit the patch to ForgeRock and if they agree with the change (once it has gone through the code review), my patch can then be shared with others and become supported by ForgeRock.
It is the best of both worlds, it is commercial open source!
An OpenDJ implementation will contain certain data that you would like to explicitly grant or deny access to. Personally identifiable information (PII) such as a user’s home telephone number, their address, birth date, or simply their email address might be required by certain team members or applications, but it might be a good idea to keep this type of information private from others. On the other hand, you may want their office phone number published for everyone within the company to see but limit access to this data outside of the company.
Controlling users’ access to different types of information forms the basis of access control in OpenDJ and consists of the following two stages:
- Authentication (AuthN) – the process of positively identifying a subject
- Authorization (AuthZ) – the process of determining the rights that a subject has on a particular resource
Before you are allowed to perform any action within OpenDJ, it must first know who you are. Once your identity has been established, OpenDJ can then ascertain the rights you have to perform actions either on the data contained in its database(s) or within the OpenDJ process, itself.
Access Control = Authentication + Authorization
Note: Access control is not defined in any of the LDAP RFCs so the manner in which directory servers implement access control varies from vendor to vendor. Many directory services (including OpenDJ) follow the LDAP v3 syntax introduced by Netscape.
Access control is implemented with an operational attribute called aci (which stands for access control instruction). Access control instructions can be configured globally (the entire OpenDJ instance) or added to specific directory entries.
1. Global ACIs:
Global ACIs are not associated with directory entries and therefore are not available when searching against a typical OpenDJ suffix (such as dc=example,dc=com). Instead, Global ACIs are considered configuration objects and may be found in the configuration suffix (cn=config). You can find the currently configured Global ACIs by opening the config.ldif file and locating the entry for the “Access Control Handler”. Or, you can search for “cn=Access Control Handler” in the configuration suffix (cn=config) as follows:
./ldapsearch –h hostname –p portnumber –D “cn=directory manager” –w “password” -b “cn=config” -s sub “cn=Access Control Handler” ds-cfg-global-aci
This returns the following results on a freshly installed (unchanged) OpenDJ server.
dn: cn=Access Control Handler,cn=config
ds-cfg-global-aci: (extop=”22.214.171.124.4.1.26027.1.6.1 || 126.96.36.199.4.1.26027.1.6.3 || 188.8.131.52.4.1.4184.108.40.206 || 220.127.116.11.4.1.1466.20037 || 18.104.22.168.4.1.422.214.171.124″) (version 3.0; acl “Anonymous extended operation access”; allow(read) userdn=”ldap:///anyone”;)
ds-cfg-global-aci: (target=”ldap:///”)(targetscope=”base”)(targetattr=”objectClass||namingContexts||supportedAuthPasswordSchemes||supportedControl||supportedExtension||supportedFeatures||supportedLDAPVersion||supportedSASLMechanisms||supportedTLSCiphers||supportedTLSProtocols||vendorName||vendorVersion”)(version 3.0; acl “User-Visible Root DSE Operational Attributes”; allow (read,search,compare) userdn=”ldap:///anyone”;)
ds-cfg-global-aci: target=”ldap:///cn=schema”)(targetattr=”attributeTypes||objectClasses”)(version 3.0;acl “Modify schema”; allow (write)(userdn = “ldap:///uid=openam,ou=Service Accounts,dc=example,dc=com”);)
ds-cfg-global-aci: target=”ldap:///cn=schema”)(targetscope=”base”)(targetattr=” objectClass||attributeTypes||dITContentRules||dITStructureRules||ldapSyntaxes||matchingRules||matchingRuleUse||nameForms||objectClasses”)(version 3.0; acl “User-Visible Schema Operational Attributes”; allow (read,search,compare) userdn=”ldap:///anyone”;)
ds-cfg-global-aci: (target=”ldap:///dc=replicationchanges”)(targetattr=”*”)(version 3.0; acl “Replication backend access”; deny (all) userdn=”ldap:///anyone”;)
ds-cfg-global-aci: (targetattr!=”userPassword||authPassword||changes||changeNumber||changeType||changeTime||targetDN||newRDN||newSuperior||deleteOldRDN”)(version 3.0; acl “Anonymous read access”; allow (read,search,compare) userdn=”ldap:///anyone”;)
ds-cfg-global-aci: (targetattr=”audio||authPassword||description||displayName||givenName||homePhone||homePostalAddress||initials||jpegPhoto||labeledURI||mobile||pager||postalAddress||postalCode||preferredLanguage||telephoneNumber||userPassword”)(version 3.0; acl “Self entry modification”; allow (write) userdn=”ldap:///self”;)
ds-cfg-global-aci: (targetattr=”createTimestamp||creatorsName||modifiersName||modifyTimestamp||entryDN||entryUUID||subschemaSubentry||etag||governingStructureRule||structuralObjectClass||hasSubordinates||numSubordinates”)(version 3.0; acl “User-Visible Operational Attributes”; allow (read,search,compare) userdn=”ldap:///anyone”;)
ds-cfg-global-aci: (targetattr=”userPassword||authPassword”)(version 3.0; acl “Self entry read”; allow (read,search,compare) userdn=”ldap:///self”;)
ds-cfg-global-aci: (targetcontrol=”126.96.36.199.1.12 || 188.8.131.52.1.13.1 || 184.108.40.206.1.13.2 || 1.2.840.1135220.127.116.119 || 1.2.818.104.22.16844810.2.3 || 2.16.840.1.113722.214.171.124 || 2.16.840.1.1137126.96.36.199 || 1.2.840.1135188.8.131.523 || 184.108.40.206.220.127.116.11.18.104.22.168″) (version 3.0; acl “Authenticated users control access”; allow(read) userdn=”ldap:///all”;)
ds-cfg-global-aci: (targetcontrol=”2.16.840.1.113722.214.171.124 || 2.16.840.1.1137126.96.36.199 || 2.16.840.1.1137188.8.131.52 || 184.108.40.206.4.1.4220.127.116.11 || 18.104.22.168.22.214.171.124.126.96.36.199 || 2.16.840.1.1137188.8.131.52 || 1.2.840.1135184.108.40.2063″) (version 3.0; acl “Anonymous control access”; allow(read) userdn=”ldap:///anyone”;)
2. Entry-Based ACIs:
Access control instructions may also be applied to any entry in the directory server. This allows fine grained access control to be applied anywhere in the directory information tree and therefore affects the scope of the ACI.
Note: Placement has a direct effect on the entry where the ACI is applied as well as any children of that entry.
You can obtain a list of all ACIs configured in your server (sans the Global ACIs) by performing the following search:
./ldapsearch –h hostname –p portnumber –D “cn=directory manager” –w “password” –b “dc=example,dc=com” –s sub aci=* aci
By default, there are no ACIs configured at the entry level. The following is an example of ACIs that might be returned if you did have ACIs configured, however.
aci: (targetattr=”*”)(version 3.0;acl “Allow entry search”; allow (search,read)(userdn = “ldap:///uid=openam,ou=Service Accounts,dc=example,dc=com”);)
aci: (targetattr=”*”)(version 3.0;acl “Modify config entry”; allow (write)(userdn = “ldap:///uid=openam,ou=Service Accounts,dc=example,dc=com”);)
aci: (targetcontrol=”2.16.840.1.1137220.127.116.11″)(version 3.0;acl “Allow persistent search”; allow (search, read)(userdn = “ldap:///uid=openam,ou=Service Accounts,dc=example,dc=com”);)
aci: (version 3.0;acl “Add config entry”; allow (add)(userdn = “ldap:///uid=openam,ou=Service Accounts,dc=example,dc=com”);)
aci: (version 3.0;acl “Delete config entry”; allow (delete)(userdn = “ldap:///uid=openam,ou=Service Accounts,dc=example,dc=com”); )
aci: (target =”ldap:///ou=Applications,dc=example,dc=com”)(targetattr=”*”)(version 3.0;acl “Allow Application Config Access to Web UI Admin”; allow (all)(userdn = “ldap:///uid=webui,ou=Applications,dc=example,dc=com”); )
The syntax for access control instructions is not specific to OpenDJ, in fact, for the most part, it shares the same syntax with the Oracle Directory Server Enterprise Edition (“ODSEE”). This is mainly due the common lineage with Sun Microsystems, but other directory servers do not use the same syntax and this makes migration more difficult (even the schema in both servers contains an attribute called aci). If you export OpenDJ directory entries to LDIF and attempt to import them into another vendor’s server, the aci statements would either be ignored, or worse, might have unpredictable results, altogether.
The following syntax is used by the OpenDJ server.
Access control instructions require three inputs: target, permission, and subject. The target specifies the entries to which the aci applies. The subject applies to the client that is performing the operation and the permissions specify what the subject is allowed to do. You can create some very powerful access control based on these three inputs.
The syntax also includes the version of the aci syntax, version 3.0. This is the aci syntax version, not the LDAP version. Finally, the syntax allows you to enter a human readable name. This allows you to easily search for and identify access control statements in the directory server.
Note: Refer to the OpenDJ Administration Guide for a more detailed description of the aci A components.
The following is an example of an ACI that permits a user to write to their own password and mobile phone attributes.
You cannot read the ACI from left to right, or even right to left, you simply have to dive right in and look for the information required to understand the intent of the ACI. If you have been working with ACIs for some time, you probably already have your own process, but I read/interpret the preceding ACI as follows:
This ACI “allows” a user to “write” to their own (“ldap:///self”) userPassword and mobile attributes “(targetattr=”userPassword||mobile”)”
If you place this ACI on a particular user’s object (i.e. uid=bnelson, ou=people,dc=example,dc=com), then this ACI would only apply to this object. If you place this ACI on a container of multiple user objects (i.e. ou=people,dc=example,dc=com), then this ACI would apply to all user objects included in this container.
Access Control Processing:
Access control instructions provide fine-grained control over what a given user or group member is authorized to do within the directory server.
When a directory-enabled client tries to perform an operation on any entry in the server, an access control list (ACL) is created for that particular entry. The ACL for any given entry consists of the entry being accessed as well as any parent entries all the way up to the root entry.
The ACL is essentially the summation of all acis defined for the target(s) being accessed plus the acis for all parent entries all the way to the top of the tree. Included in this list are any Global ACIs that may have been configured in the cn=config as well. While not entirely mathematically accurate, the following formula provides an insight into how the ACL is generated.
Using the previous formula, the access control lists for each entry in the directory information tree would be as follows:
Once the ACL is created, the list is then processed to determine if the client is allowed to perform the operation or not. ACLs are processed as follows:
- If there exists at least one explicit DENY rule that prevents a user from performing the requested action (i.e. deny(write)), then the user is denied.
- If there exists at least one explicit ALLOW rule that allows a user to perform the requested action (i.e. allow(write)), then the user is allowed (as long as there are no other DENY rules preventing this).
- If there are neither DENY nor ALLOW rules defined for the requested action, then the user is denied. This is referred to as the implicit deny.
Something to Think About…
OpenDJ’s Super User:
The RootDN user (“cn=Directory Manager” by default) is a special administrative user that can pretty much perform any action in OpenDJ. This user account is permitted full access to directory server data and can perform almost any action in the directory service, itself. Essentially, this account is similar to the root or Administrator accounts on UNIX and Windows systems, respectively.
If you look in the directory server you will find that there are no access control instruction granting the RootDN this unrestricted access; but there are however privileges that do so.
While access control instructions restrict access to directory data through LDAP operations, privileges define administrative tasks that may be performed by users within OpenDJ. Assignment of privileges to users (either directly or through groups) effectively allows those users the ability to perform the administrative tasks defined by those privileges.
The following table provides a list of common privileges and their relationship to the RootDN user.
The RootDN user is assigned these privileges by default and similar to Global ACIs, these privileges are defined and maintained in the OpenDJ configuration object. The following is the default list of privileges associated with Root DN users (of which the Directory Manager account is a member).
dn: cn=Root DNs,cn=config
cn: Root DNs
This list can retrieved using the OpenDJ dsconfig command:
./dsconfig –h localhost –p 4444 –D “cn=directory manager” –w password get-root-dn-prop
with the ldapsearch command:
./ldapsearch –h hostname –p portnumber –D “cn=directory manager” –w “password” -b “cn=config” -s sub “cn=Root DNs” ds-cfg-default-root-privilege-name
or simply by opening the config.ldif file and locating the entry for the “cn=Root DNs” entry.
Most operations involving sensitive or administrative data require that a user has both the appropriate privilege(s) as well as certain access control instructions. This allows you to configure authorization at a fine grained level – such as managing access control or resetting passwords.
Privileges are assigned to users and apply globally to the directory service. Any user can be granted or denied any privilege and by default only the RootDN users are assigned a default set of privileges.
Note: Consider creating different types of administrative groups in OpenDJ and assign the privileges and ACIs to those groups to define what a group member is allowed to do. Adding users to that group then automatically grants those users the rights defined in the group and conversely, removing them from the group drops those privileges (unless they are granted through another group).
Once you set up a number of ACIs, you may find it difficult to understand how the resulting access control list is processed and ultimately the rights that a particular user may have. Fortunately OpenDJ provides a method of evaluating the effective rights that a subject has on a given target.
You can use the ldapsearch command to determine the effective rights that a user has on one or more attributes on one or more entries.
$ ldapsearch –h localhost –p 1389 -D “cn=Directory Manager” -w password
-g “dn:uid=helpdeskadmin,ou=administrators, dc=example,dc=com” -b “uid=scarter,ou=people, dc=example,dc=com” -s base ‘(objectclass=*)’ ‘*’ aclrights
The preceding search is being performed by the Root DN user (“cn=Directory Manager”). It is passing the –g option requesting the get effective rights control (to which the Directory Manager has the appropriate access configured). The command wants to determine what rights the Help Desk Administrator (uid=helpdeskadmin,…) has on Sam Carter’s entry (uid=scarter,…). The scope of the search has been limited only to Sam Carter’s entry using the base parameter. Finally, the search operation is returning not only the attributes, but the effective rights (aclrights) as well.
Possible results from a search operation such as this are as follows:
cn: Sam Carter
The search results contain not only the attributes/attribute values associated with Sam Carter’s object, but the effective rights that the Help Desk Admins have on those attributes. For instance,
The aclRights;attributeLevel;givenname notation indicate that this line includes the effective rights for the givenname attribute. Individual permissions are listed that demonstrate the rights that the Help Desk Administrator has on this attribute for Sam Carter’s entry (1 = allowed and 0 = denied).
An OpenDJ installation includes a set of default (Global) access control instructions which by some standards may be considered insecure. For instance, there are five ACIs that allow an anonymous user the ability to read certain controls, extended operations, operational attributes, schema attributes, and user attributes. The basic premise behind this is that ForgeRock wanted to provide an easy out-of-the-box evaluation of the product while at the same time providing a path forward for securing the product. It is intended that OpenDJ should be hardened in order to meet a company’s security policies and in fact, one task that is typically performed before placing OpenDJ in production is to limit anonymous access. There are two ways you can perform this:
- Enable the reject-unauthenticated-request property using the dsconfig command.
- Update the Global ACIs
Mark Craig provides a nice blog posting on how to turn off anonymous access using the dsconfig command. You can find that blog here. The other option is to simply change the reference in the Global ACIs from ldap:///anyone to ldap:///all. This prevents anonymous users from gaining access to this information.
Note: Use of ldap:///anyone in an ACI includes both authenticated and anonymous users – essentially, anyone. Changing this to ldap:///all restricts the subject to all authenticated users.
The following comments from Ludo Poitou (ForgeRock’s OpenDJ Product Manager) should be considered before simply removing anonymous access.
You don’t want to remove the ACI rules for Anonymous access, you want to change it from granting access to anyone (ldap:///anyone) to granting access to all authenticated users (ldap:///all).
This said, there are some differences between fully rejecting unauthenticated requests and using ACI to control access. The former will block all access including the attempts to discover the server’s capabilities by reading the RootDSE. The later allows you to control which parts can be accessed anonymously, and which shouldn’t.
There’s been a lot of fuss around allowing anonymous access to a directory service. Some people are saying that features and naming context discovery is a threat to security, allowing malicious users to understand what the server contains and what security mechanisms are available and therefore not available. At the same time, it is important for generic purpose applications to understand how they can or must use the directory service before they actually authenticate to it.
Fortunately, OpenDJ has mechanisms that allow administrators to configure the directory services according to their security constraints, using either a simple flag to reject all unauthenticated requests, or by using ACIs.
A few other things to consider when configuring access control in OpenDJ include the following:
- Limit the number of Root DN user accounts
You should have one Root DN account and it should not be shared with multiple administrators. Doing so makes it nearly impossible to determine the identity of the person who performed a configuration change or operation in OpenDJ. Instead, make the password complex and store it in a password vault.
- Create a delegated administration environment
Now that you have limited the number of Root DN accounts, you need to create groups to allow users administrative rights in OpenDJ. Users would then log in as themselves and perform operations against the directory server using their own account. The tasks associated with this are as follows:
- Create administrative groups
- Use the ds-privilege-name attribute to assign privilege(s) to the group entry
- Create ACIs based on the group name, not an individual user. For instance, “groupdn:///cn=Help Desk Admin, ou=Administrators,dc=example,dc=com”
- Add users to the groups
- Associate privileges and ACIs to users for fine grained access control
Now that you have create administrative groups, you are ultimately going to need to provide certain users with more rights than others. You can create additional administrative groups, but what if you only need one user to have these rights. Creating a group of one may or may not be advisable and may actually lead to group explosion (where you end up with more groups than you actually have users). Instead, consider associating privileges to a particular user and then create ACIs based on that user.
The face of identity is changing. Historically, it was the duty of an identity management solution to manage and control an individual’s access to corporate resources. Such solutions worked well as long as the identity was safe behind the corporate firewall – and the resources were owned by the organization.
But in today’s world of social identities (BYOI), mobile devices (BYOD), dynamic alliances (federation), and everything from tractors to refrigerators being connected to the Internet (IoT), companies are finding that legacy identity management solutions are no longer able to keep up with the demand. Rather than working with thousands to hundreds of thousands of identities, today’s solutions are tasked with managing hundreds of thousands to millions of identities and include not only carbon-based life forms (people) but also those that are silicon-based (devices).
In order to meet this demand, today’s identity solutions must shift from the corporation-centric view of a user’s identity to one that is more user-centric. Corporations typically view the identity relationship as one between the user and the organization’s resources. This is essentially a one-to-many relationship and is relatively easy to manage using legacy identity management solutions.
What is becoming evident, however, is the growing need to manage many-to-many relationships as these same users actually have multiple identities (personas) that must be shared with others that, in turn, have multiple identities, themselves.
The corporation is no longer the authoritative source of a user’s identity, it has been diminished to the role of a persona as users begin to take control of their own identities in other aspects of their lives.
Identity : the state or fact of being the same one as described.
Persona : the mask or façade presented to satisfy the demands of the situation or the environment.
In developing the next generation of identity management solutions, the focus needs to move away from the node (a reference to an entry in a directory server) and more towards the links (or relationships) between the nodes (a reference to social graphs).
In order to achieve this, today’s solutions must take a holistic view of the user’s identity and allow the user to aggregate, manage, and decide with whom to share their identity data.
Benefits to Corporations
While corporations may perceive this as a loss of control, in actuality it is the corporation that stands to benefit the most from a user-centric identity management solution. Large corporations spend hundreds of thousands of dollars each year in an attempt to manage a user’s identity only to find that much of what they have on file is incorrect. There are indeed many characteristics that must be managed by the organization, but many of a user’s attributes go well-beyond a corporation’s reach. In such cases, its ability to maintain accurate data within these attributes is relatively impossible without the user’s involvement.
Take for instance a user’s mobile telephone number; in the past, corporations issued, sponsored, and managed these devices. But today’s employees typically purchase their own mobile phones and change carriers (or even phone numbers) on a periodic basis. As such, corporate white pages are filled with inaccurate data; this trend will only increase as users continue to bring more and more of themselves into the workplace.
Legacy identity solutions attempt to address this issue by introducing “end-user self-service” – a series of Web pages that allow a user to maintain their corporate profile. Users are expected to update their profile whenever a change occurs. The problem with this approach is that users selectively update their profiles and in some cases purposely supply incorrect data (in order to avoid after hours calls). The other problem with this approach is that it still adheres to a corporate-centric/corporate-owned identity mindset. The truth is that users’ identities are not centralized, they are distributed across many different systems both in front of and behind the corporate firewall and while companies may “own” certain data, it is the information that the user brings from other sources that is elusive to the company.
Identity Relationship Management
A user has relationships that extend well beyond those maintained within a company and as such has core identity data strewn across hundreds, if not thousands of databases. The common component in all of these relationships is the user. It is the user who is in charge of that data and it is the user who elects to share their information within the context of those relationships. The company is just one of those relationships, but it is the one for which legacy identity management solutions have been written.
Note: Relationships are not new, but the number of relationships that a user has and types of relationships they have with other users and other things is rapidly growing.
Today’s identity management solutions must evolve to accept (or at a minimum acknowledge) multiple authoritative sources beyond their own. They must evolve to understand the vast number of relationships that a user has both with other users, but also with the things the user owns (or uses) and they must be able to provide (or deny) services based on those relationships and even the context of those relationships. These are lofty goals for today’s identity management solutions as they require vendors to think in a whole new way, implement a whole new set of controls, and come up with new and inventive interfaces to scale to the order of millions. To borrow a phrase from Ian Glazer, we need to kill our current identity management solutions in order to save them, but such an evolution is necessary for identity to stay relevant in today’s relationship-driven world.
I am not alone in recognizing the need for a change. Others have come to similar conclusions and this has given rise to the term, Identity Relationship Management (or IRM). The desire for change is so great in fact that Kantara has sponsored the Identity Relationship Management Working Group of which I am privileged to be a member. This has given rise to a LinkedIn Group on IRM, a Twitter feed (@irmwg), various conferences either focused on or discussing IRM, and multiple blogs of which this is only one.
LinkedIn IRM Working Group Description:
In today’s internet-connected world, employees, partners, and customers all need anytime access to secure data from millions of laptops, phones, tablets, cars, and any devices with internet connections.
Identity relationship management platforms are built for IoT, scale, and contextual intelligence. No matter the device, the volume, or the circumstance, an IRM platform will adapt to understand who you are and what you can access.
Call to Action
Do you share similar thoughts and/or concerns? Are you looking to help craft the future of identity management? If so, then consider being part of the IRM Working Group or simply joining the conversation on LinkedIn or Twitter.
Having implemented Sun, Novell, and Oracle provisioning solutions in the past, the one thing that I found to be lacking in ForgeRock’s OpenIDM solution was an easy to use administrative interface for connecting to and configuring target resources.
Sure, you could configure JSON objects at the file level, but who wants to do that when “point and click” and “drag and drop” is the way to go.
With OpenIDM 3.1 my main objection has been eliminated as the the new resource configuration interfaces have arrived – and boy have they arrived!
See the OpenIDM Integrator’s Guide for more information.
The latest release now places OpenIDM directly in line as a viable alternative to the big boys and will make our deployments much quicker and less prone to error. Way to go ForgeRock, thanks for listening (and responding).
With Halloween in the rear view mirror and Christmas right around the corner, it is easy for Thanksgiving to get lost in the shuffle. Bordered by two holidays where much of our society is focused on gifts of candy and presents, Thanksgiving is sort of an “odd man out” and like many of the other holidays, much of its meaning is oftentimes overlooked.
While not the official start of the Thanksgiving holiday that we celebrate today, it was George Washington who in 1789 declared Thursday, Nov. 26, a day of “thanksgiving.” This was a one time occurrence and its intent was to devote a day to “public thanksgiving and prayer” in gratitude to “the service of that great and glorious Being who is the beneficent Author of all the good that was, that is, or that will be.”
(Read the full proclamation here)
It wasn’t until 1863 that Abraham Lincoln set aside the fourth Thursday in November as our official Thanksgiving holiday, but it is the day that George Washington set aside that gives this holiday special meaning to me. The meaning of the word “thanks” is associated with an “expression of gratitude”; and to give thanks is to express that gratitude to others.
(Read the full proclamation here)
In both cases, George Washington and Abraham Lincoln were expressing gratitude to the Almighty God for the wonderful gifts He had bestowed on a fledgling nation. While we can join in these expressions, each of us has something unique to be grateful for. Maybe it’s your health, or your family or friends. Maybe its your finances or the fact that you have achieved long sought after goals in your life, or simply that you have a roof over your head – each of us has something to be thankful for on this Thanksgiving Day.
So, on one of the most important holidays of the year, one that focuses on giving thanks for the blessings that we have received in the past year, let’s stop and take the time to thank the God Almighty, respective spouses, family members, friends, or whomever deserves that expression of gratitude.
After all, isn’t giving thanks what Thanksgiving is all about?
In real life we tend to value those traits that make us unique from others; but in an identity management deployment uniqueness is essential to the authentication process and should not be taken for granted.
Case in point, attributes in OpenDJ may share values that you may or may not want (or need) to be unique. For instance the following two (different) entries are both configured with the same value for the email address:
dn: uid=bnelson,ou=people,dc=example,dc=com uid: bnelson mail: email@example.com [LDIF Stuff Snipped]
dn: uid=scarter,ou=people,dc=example,dc=com uid: scarter mail: firstname.lastname@example.org [LDIF Stuff Snipped]
In some cases this may be fine, but in others this may not be the desired effect as you may need to enforce uniqueness for attributes such as uid, guid, email address, or simply credit cards. To ensure that attribute values are unique across directory server entries you need to configure attribute uniqueness.
UID Uniqueness Plug-In
OpenDJ has an existing plug-in that can be used to configure unique values for the uid attribute, but this plug-in is disabled by default. You can find this entry in OpenDJ’s main configuration file (config.ldif) or by searching the cn=config tree in OpenDJ (assuming you have the correct permissions to do so).
dn: cn=UID Unique Attribute,cn=Plugins,cn=config objectClass: ds-cfg-unique-attribute-plugin objectClass: ds-cfg-plugin objectClass: top ds-cfg-enabled: false ds-cfg-java-class: org.opends.server.plugins.UniqueAttributePlugin ds-cfg-plugin-type: preOperationAdd ds-cfg-plugin-type: preOperationModify ds-cfg-plugin-type: preOperationModifyDN ds-cfg-plugin-type: postOperationAdd ds-cfg-plugin-type: postOperationModify ds-cfg-plugin-type: postOperationModifyDN ds-cfg-plugin-type: postSynchronizationAdd ds-cfg-plugin-type: postSynchronizationModify ds-cfg-plugin-type: postSynchronizationModifyDN ds-cfg-invoke-for-internal-operations: true ds-cfg-type: uid cn: UID Unique Attribute
Leaving this plug-in disabled can cause problems with OpenAM, however, if OpenAM has been configured to authenticate using the uid attribute (and you ‘accidentally’ create entries with the same uid value). In such cases you will see an authentication error during the login process as OpenAM cannot determine which account you are trying to use for authentication.
To fix this problem in OpenAM, you can use the OpenDJ dsconfig command to enable the UID Unique Attribute plug-in as follows:
./dsconfig set-plugin-prop --hostname localhost --port 4444 \ --bindDN "cn=Directory Manager" --bindPassword password \ --plugin-name "UID Unique Attribute" \ --set base-dn:ou=people,dc=example,dc=com --set enabled:true \ --trustAll --no-prompt
This will prevent entries from being added to OpenDJ where the value of any existing uids conflicts with the incoming entry’s uid. This will address the situation where you are using the uid attribute for authentication in OpenAM, but what if you want to use a different attribute (such as mail) to authenticate? In such cases, you need to create your own uniqueness plug-in as follows:
./dsconfig create-plugin --hostname localhost --port 4444 \ --bindDN "cn=Directory Manager" --bindPassword password \ --plugin-name "Unique Email Address Plugin" \ --type unique-attribute --set type:mail --set enabled:true \ --set base-dn:ou=people,dc=example,dc=com --trustAll \ --no-prompt
In both cases the base-dn parameter defines the scope where the the uniqueness applies. This is useful in multitenant environments where you may want to define uniqueness within a particular subtree but not necessarily across the entire server.
The uniqueness plug-in requires that you have an existing equality index configured for the attribute where you would like to enforce uniqueness. The index is necessary so that OpenDJ can search for other entries (within the scope of the base-dn) where the attribute may already have a particular value set.
The following dscconfig command can be used to create an equality index for the mail attribute:
./dsconfig create-local-db-index --hostname localhost --port 4444 \ --bindDN "cn=Directory Manager" --bindPassword password --backend-name userRoot \ --index-name mail --set index-type:equality --trustAll --no-prompt
OpenAM’s default settings (Data Store, LDAP authentication module, etc) uses the uid attribute to authenticate and uniquely identify a user. OpenDJ typically uses uid as the unique naming attribute in a user’s distinguished name. When combined together, it is almost assumed that you will be using the uid attribute in this manner, but that is not always the case. You can easily run into issues when you start coloring outside of the lines and begin using other attributes (i.e. mail) for this purpose. Armed with the information contained in this post, however, you should easily be able to configure OpenDJ to enforce uniqueness for any attribute.
The OpenAM Authentication Service can be configured to lock a user’s account after a defined number of log in attempts has failed. Account Lockout is disabled by default, but when configured properly, this feature can be useful in fending off brute force attacks against OpenAM login screens.
If your OpenAM environment includes an LDAP server (such as OpenDJ) as an authentication database, then you have options on how (and where) you can configure Account Lockout settings. This can be performed in either OpenAM (as mentioned above) or in the LDAP server, itself. But the behavior is different based on where this is configured. There are benefits and drawbacks towards configuring Account Lockout in either product and knowing the difference is essential.
Note: Configuring Account Lockout simultaneously in both products can lead to confusing results and should be avoided unless you have a firm understanding of how each product works. See the scenario at the end of this article for a deeper dive on Account Lockout from an attribute perspective.
The OpenAM Approach
You can configure Account Lockout in OpenAM either globally or for a particular realm. To access the Account Lockout settings for the global configuration,
- Log in to OpenAM Console
- Navigate to: Configuration > Authentication > Core
- Scroll down to Account Lockout section
To access Account Lockout settings for a particular realm,
- Log in to OpenAM Console
- Navigate to: Access Control > realm > Authentication > All Core Settings
- Scroll down to Account Lockout section
In either location you will see various parameters for controlling Account Lockout as follows:
Account Lockout is disabled by default; you need to select the “Login Failure Lockout Mode” checkbox to enable this feature. Once it is enabled, you configure the number of attempts before an account is locked and even if a warning message is displayed to the user before their account is locked. You can configure how long the account is locked and even the duration between successive lockouts (which can increase if you set the duration multiplier). You can configure the attributes to use to store the account lockout information in addition to the default attributes configured in the Data Store.
Enabling Account Lockout affects the following Data Store attributes: inetUserStatus and sunAMAuthInvalidAttemptsData. By default, the value of the inetUserStatus attribute is either Active or Inactive, but this can be configured to use another attribute and another attribute value. This can be configured in the User Configuration section of the Data Store configuration as follows:
These attributes are updated in the Data Store configuration for the realm. A benefit of implementing Account Lockout in OpenAM is that you can use any LDAPv3 directory, Active Directory, or even a relational database – but you do need to have a Data Store configured to provide OpenAM with somewhere to write these values. An additional benefit is that OpenAM is already configured with error messages that can be easily displayed when a user’s account is about to be locked or has become locked. Configuring Account Lockout within OpenAM, however, may not provide the level of granularity that you might need and as such, you may need to configure it in the authentication database (such as OpenDJ).
The OpenDJ Approach
OpenDJ can be configured to lock accounts as well. This is defined in a password policy and can be configured globally (the entire OpenDJ instance) or it may be applied to a subentry (a group of users or a specific user). Similar to OpenAM, a user’s account can be locked after a number of invalid authentication attempts have been made. And similar to OpenAM, you have several additional settings that can be configured to control the lockout period, whether warnings should be sent, and even who to notify when the account has been locked.
But while configuring Account Lockout in OpenAM may recognize invalid password attempts in your SSO environment, configuring it in OpenDJ will recognize invalid attempts for any application that is using OpenDJ as an authentication database. This is more of a centralized approach and can recognize attacks from several vectors.
Configuring Account Lockout in OpenDJ affects the following OpenDJ attributes: pwdFailureTime (a multivalued attribute consisting of the timestamp of each invalid password attempt) and pwdAccountLockedTime (a timestamp indicating when the account was locked).
Another benefit of implementing Account Lockout in OpenDJ is the ability to configure Account Lockout for different types of users. This is helpful when you want to have different password policies for users, administrators, or even service accounts. This is accomplished by assigning different password polices directly to those users or indirectly through groups or virtual attributes. A drawback to this approach, however, is that OpenAM doesn’t necessarily recognize the circumstances behind error messages returned from OpenDJ when a user is unable to log in. A scrambled password in OpenDJ, for instance, simply displays as an Authentication failed error message in the OpenAM login screen.
By default, all users in OpenDJ are automatically assigned a generic (rather lenient) password policy that is aptly named: Default Password Policy. The definition of this policy can be seen as follows:
dn: cn=Default Password Policy,cn=Password Policies,cn=config objectClass: ds-cfg-password-policy objectClass: top objectClass: ds-cfg-authentication-policy ds-cfg-skip-validation-for-administrators: false ds-cfg-force-change-on-add: false ds-cfg-state-update-failure-policy: reactive ds-cfg-password-history-count: 0 ds-cfg-password-history-duration: 0 seconds ds-cfg-allow-multiple-password-values: false ds-cfg-lockout-failure-expiration-interval: 0 seconds ds-cfg-lockout-failure-count: 0 ds-cfg-max-password-reset-age: 0 seconds ds-cfg-max-password-age: 0 seconds ds-cfg-idle-lockout-interval: 0 seconds ds-cfg-java-class: org.opends.server.core.PasswordPolicyFactory ds-cfg-lockout-duration: 0 seconds ds-cfg-grace-login-count: 0 ds-cfg-force-change-on-reset: false ds-cfg-default-password-storage-scheme: cn=Salted SHA-1,cn=Password Storage Schemes,cn=config ds-cfg-allow-user-password-changes: true ds-cfg-allow-pre-encoded-passwords: false ds-cfg-require-secure-password-changes: false cn: Default Password Policy ds-cfg-require-secure-authentication: false ds-cfg-expire-passwords-without-warning: false ds-cfg-password-change-requires-current-password: false ds-cfg-password-generator: cn=Random Password Generator,cn=Password Generators, cn=config ds-cfg-password-expiration-warning-interval: 5 days ds-cfg-allow-expired-password-changes: false ds-cfg-password-attribute: userPassword ds-cfg-min-password-age: 0 seconds
The value of the ds-cfg-lockout-failure-count attribute is 0; which means that user accounts are not locked by default – no matter how many incorrect attempts are made. This is one of the many security settings that you can configure in a password policy and while many of these mimic what is available in OpenAM, others go quite deeper.
You can use the OpenDJ dsconfig command to change the Default Password Policy as follows:
dsconfig set-password-policy-prop --policy-name "Default Password Policy" --set lockout-failure-count:3 --hostname localhost --port 4444 --trustAll --bindDN "cn=Directory Manager" --bindPassword ****** --no-prompt
Rather than modifying the Default Password Policy, a preferred method is to create a new password policy and apply your own specific settings to the new policy. This policy can then be applied to a specific set of users.
The syntax for using the OpenDJ dsconfig command to create a new password policy can be seen below.
dsconfig create-password-policy --set default-password-storage-scheme:"Salted SHA-1" --set password-attribute:userpassword --set lockout-failure-count:3 --type password-policy --policy-name "Example Corp User Password Policy" --hostname localhost --port 4444 --trustAll --bindDN cn="Directory Manager" --bindPassword ****** --no-prompt
Note: This example contains a minimum number of settings (default-password-storage-scheme, password-attribute, and lockout-failure-count). Consider adding additional settings to customize your password policy as desired.
You can now assign the password policy to an individual user by adding the following attribute as a subentry to the user’s object:
ds-pwp-password-policy-dn: cn=Example Corp User Password Policy,cn=Password Policies, cn=config
This can be performed using any LDAP client where you have write permissions to a user’s entry. The following example uses the ldapmodify command in an interactive mode to perform this operation:
$ ldapmodify -D "cn=Directory Manager" -w ****** <ENTER> dn: uid=bnelson,ou=People,dc=example,dc=com <ENTER> changetype: modify <ENTER> replace: ds-pwp-password-policy-dn <ENTER> ds-pwp-password-policy-dn: cn=Example Corp User Password Policy, cn=Password Policies,cn=config <ENTER> <ENTER>
Another method of setting this password policy is through the use of a dynamically created virtual attribute (i.e. one that is not persisted in the OpenDJ database backend). The following definition automatically assigns this new password policy to all users that exist beneath the ou=people container (the scope of the virtual attribute).
dn: cn=Example Corp User Password Policy Assignment,cn=Virtual Attributes,cn=config objectClass: ds-cfg-virtual-attribute objectClass: ds-cfg-user-defined-virtual-attribute objectClass: top ds-cfg-base-dn: ou=people,dc=example,dc=com cn: Example Corp User Password Policy Assignment ds-cfg-attribute-type: ds-pwp-password-policy-dn ds-cfg-enabled: true ds-cfg-java-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider ds-cfg-filter: (objectclass=sbacperson) ds-cfg-value: cn=Example Corp User Password Policy,cn=Password Policies,cn=config
Note: You can also use filters to create very granular results on how password polices are applied.
Configuring Account Lockout in OpenDJ has more flexibility and as such may be considered to be more powerful than OpenAM in this area. The potential confusion, however, comes when attempting to unlock a user’s account when they have been locked out of both OpenAM and OpenDJ. This is described in the following example.
A Deeper Dive into Account Lockout
Consider an environment where OpenAM is configured with the LDAP authentication module and that module has been configured to use an OpenDJ instance as the authentication database.
OpenAM and OpenDJ have both been configured to lock a user’s account after 3 invalid password attempts. What kind of behavior can you expect? Let’s walk through each step of an Account Lockout process and observe the behavior on Account Lockout specific attributes.
Step 1: Query Account Lockout Specific Attributes for the Test User
$ ldapsearch -D "cn=Directory Manager" -w ****** uid=testuser1 inetuserstatus \ sunAMAuthInvalidAttemptsData pwdFailureTime pwdAccountLockedTime dn: uid=testuser1,ou=test,dc=example,dc=com inetuserstatus: Active
The user is currently active and Account Lockout specific attributes are empty.
Step 2: Open the OpenAM Console and access the login screen for the realm where Account Lockout has been configured.
Step 3: Enter an invalid password for this user
Step 4: Query Account Lockout Specific Attributes for the Test User
$ ldapsearch -D "cn=Directory Manager" -w ****** uid=testuser1 inetuserstatus \ sunAMAuthInvalidAttemptsData pwdFailureTime pwdAccountLockedTime dn: uid=testuser1,ou=test,dc=example,dc=com sunAMAuthInvalidAttemptsData:: PEludmFsaWRQYXNzd29yZD48SW52YWxpZENvdW50PjE8L0 ludmFsaWRDb3VudD48TGFzdEludmFsaWRBdD4xMzk4MTcxNTAwMDE4PC9MYXN0SW52YWxpZEF0P jxMb2NrZWRvdXRBdD4wPC9Mb2NrZWRvdXRBdD48QWN0dWFsTG9ja291dER1cmF0aW9uPjA8L0Fj dHVhbExvY2tvdXREdXJhdGlvbj48L0ludmFsaWRQYXNzd29yZD4= inetuserstatus: Active pwdFailureTime: 20140422125819.918Z
You now see that there is a value for the pwdFailureTime. This is the timestamp of when the first password failure occurred. This attribute was populated by OpenDJ.
The sunAMAuthInvalidAttemptsData attribute is populated by OpenAM. This is a base64 encoded value that contains valuable information regarding the invalid password attempt. Run this through a base64 decoder and you will see that this attribute contains the following information:
<InvalidPassword><InvalidCount>1</InvalidCount><LastInvalidAt>1398171500018 </LastInvalidAt><LockedoutAt>0</LockedoutAt><ActualLockoutDuration>0 </ActualLockoutDuration></InvalidPassword>
Step 5: Repeat Steps 2 and 3. (This is the second password failure.)
Step 6: Query Account Lockout Specific Attributes for the Test User
$ ldapsearch -D "cn=Directory Manager" -w ****** uid=testuser1 inetuserstatus \ sunAMAuthInvalidAttemptsData pwdFailureTime pwdAccountLockedTime dn: uid=testuser1,ou=test,dc=example,dc=com sunAMAuthInvalidAttemptsData:: PEludmFsaWRQYXNzd29yZD48SW52YWxpZENvdW50PjI8L0 ludmFsaWRDb3VudD48TGFzdEludmFsaWRBdD4xMzk4MTcxNTUzMzUwPC9MYXN0SW52YWxpZEF0P jxMb2NrZWRvdXRBdD4wPC9Mb2NrZWRvdXRBdD48QWN0dWFsTG9ja291dER1cmF0aW9uPjA8L0Fj dHVhbExvY2tvdXREdXJhdGlvbj48L0ludmFsaWRQYXNzd29yZD4= inetuserstatus: Active pwdFailureTime: 20140422125819.918Z pwdFailureTime: 20140422125913.151Z
There are now two values for the pwdFailureTime attribute – one for each password failure. The sunAMAuthInvalidAttemptsData attribute has been updated as follows:
<InvalidPassword><InvalidCount>2</InvalidCount><LastInvalidAt>1398171553350 </LastInvalidAt><LockedoutAt>0</LockedoutAt><ActualLockoutDuration>0 </ActualLockoutDuration></InvalidPassword>
Step 7: Repeat Steps 2 and 3. (This is the third and final password failure.)
OpenAM displays an error message indicating that the user’s account is not active. This is OpenAM’s way of acknowledging that the user’s account has been locked.
Step 8: Query Account Lockout Specific Attributes for the Test User
$ ldapsearch -D "cn=Directory Manager" -w ****** uid=testuser1 inetuserstatus \ sunAMAuthInvalidAttemptsData pwdFailureTime pwdAccountLockedTime dn: uid=testuser1,ou=test,dc=example,dc=com sunAMAuthInvalidAttemptsData:: PEludmFsaWRQYXNzd29yZD48SW52YWxpZENvdW50PjA8L0 ludmFsaWRDb3VudD48TGFzdEludmFsaWRBdD4wPC9MYXN0SW52YWxpZEF0PjxMb2NrZWRvdXRBd D4wPC9Mb2NrZWRvdXRBdD48QWN0dWFsTG9ja291dER1cmF0aW9uPjA8L0FjdHVhbExvY2tvdXRE dXJhdGlvbj48L0ludmFsaWRQYXNzd29yZD4= inetuserstatus: Inactive pwdFailureTime: 20140422125819.918Z pwdFailureTime: 20140422125913.151Z pwdFailureTime: 20140422125944.771Z pwdAccountLockedTime: 20140422125944.771Z
There are now three values for the pwdFailureTime attribute – one for each password failure. The sunAMAuthInvalidAttemptsData attribute has been updated as follows:
<InvalidPassword><InvalidCount>0</InvalidCount><LastInvalidAt>0</LastInvalidAt> <LockedoutAt>0</LockedoutAt><ActualLockoutDuration>0</ActualLockoutDuration> </InvalidPassword>
You will note that the counters have all been reset to zero. That is because the user’s account has been inactivated by OpenAM by setting the value of the inetuserstatus attribute to Inactive. Additionally, the third invalid password caused OpenDJ to lock the account by setting the value of the pwdAccountLockedTime attribute to the value of the last password failure.
Now that the account is locked out, how do you unlock it? The natural thing for an OpenAM administrator to do is to reset the value of the inetuserstatus attribute and they would most likely use the OpenAM Console to do this as follows:
The problem with this approach is that while the user’s status in OpenAM is now made active, the status in OpenDJ remains locked.
$ ldapsearch -D "cn=Directory Manager" -w ****** uid=testuser1 inetuserstatus \ sunAMAuthInvalidAttemptsData pwdFailureTime pwdAccountLockedTime dn: uid=testuser1,ou=test,dc=example,dc=com sunAMAuthInvalidAttemptsData:: PEludmFsaWRQYXNzd29yZD48SW52YWxpZENvdW50PjA8L0 ludmFsaWRDb3VudD48TGFzdEludmFsaWRBdD4wPC9MYXN0SW52YWxpZEF0PjxMb2NrZWRvdXRBd D4wPC9Mb2NrZWRvdXRBdD48QWN0dWFsTG9ja291dER1cmF0aW9uPjA8L0FjdHVhbExvY2tvdXRE dXJhdGlvbj48L0ludmFsaWRQYXNzd29yZD4= inetuserstatus: Active pwdFailureTime: 20140422125819.918Z pwdFailureTime: 20140422125913.151Z pwdFailureTime: 20140422125944.771Z pwdAccountLockedTime: 20140422125944.771Z
Attempting to log in to OpenAM with this user’s account yields an authentication error that would make most OpenAM administrators scratch their head; especially after just resetting the user’s status.
The trick to fixing this is to clear the pwdAccountLockedTime and pwdFailureTime attributes and the way to do this is by modifying the user’s password. Once again, the ldapmodify command can be used as follows:
$ ldapmodify -D "cn=Directory Manager" -w ****** <ENTER> dn: uid=testuser1,ou=test,dc=example,dc=com <ENTER> changetype: modify <ENTER> replace: userPassword <ENTER> userPassword: newpassword <ENTER> <ENTER>
$ ldapsearch -D "cn=Directory Manager" -w ****** uid=testuser1 inetuserstatus \ sunAMAuthInvalidAttemptsData pwdFailureTime pwdAccountLockedTime dn: uid=testuser1,ou=test,dc=example,dc=com sunAMAuthInvalidAttemptsData:: PEludmFsaWRQYXNzd29yZD48SW52YWxpZENvdW50PjA8L0 ludmFsaWRDb3VudD48TGFzdEludmFsaWRBdD4wPC9MYXN0SW52YWxpZEF0PjxMb2NrZWRvdXRBd D4wPC9Mb2NrZWRvdXRBdD48QWN0dWFsTG9ja291dER1cmF0aW9uPjA8L0FjdHVhbExvY2tvdXRE dXJhdGlvbj48L0ludmFsaWRQYXNzd29yZD4= inetuserstatus: Active pwdChangedTime: 20140422172242.676Z
This, however, requires two different interfaces for managing the user’s account. An easier method is to combine the changes into one interface. You can modify the inetuserstatus attribute using ldapmodify or if you are using the OpenAM Console, simply change the password while you are updating the user’s status.
There are other ways to update one attribute by simply modifying the other. This can range in complexity from a simple virtual attribute to a more complex yet powerful custom OpenDJ plugin. But in the words of Voltaire, “With great power comes great responsibility.”
So go forth and wield your new found power; but do it in a responsible manner.