Newbie help : How to reset the Directory Manager’s password ?

We get this question quite often on IRC or mailling lists, from newbies who’ve installed OpenDJ (or OpenDS) for evaluation and forgot the Directory Manager’s password.

So here are the steps :

Make sure OpenDJ is stopped.

bin/stop-ds

Generate an encoded password for Directory Manager :

bin/encode-password -s SSHA512 -c AS3cur3PassW0rd
Encoded Password:  "{SSHA512}G/knE0xkyW2Af3+1MFy+yPYxchGgLuqog71R4njPJcs9t5NDAadqLxU7pxZjZkrDquQeb5aq7tum1ZFC3uE+r4Nmuil4S46A"

Copy the string within quotes (without the quotes), and edit the config/config.ldif file.

Go down to the following entry

dn: cn=Directory Manager,cn=Root DNs,cn=config

Replace the value of userPassword with the newly generated one.

dn: cn=Directory Manager,cn=Root DNs,cn=config
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: top
objectClass: ds-cfg-root-dn-user
userpassword: {SSHA512}G/knE0xkyW2Af3+1MFy+yPYxchGgLuqog71R4njPJcs9t5NDAadqLxU7pxZjZkrDquQeb5aq7tum1ZFC3uE+r4Nmuil4S46A
...

You can now restart the server and administer it.

OpenDJ: Troubleshooting LDAP SSL connections

Troubleshooting Secure Socket Layer (SSL, also now standardized as TLS) issues is not trivial and there is no secret sauce specific to OpenDJ.

Should an LDAP SSL connection fails due to the server, you should find a descriptive error message in the server’s errors log (in logs/errors). But sometime the connection is aborted by the client with some obscure message. Often we see a message ending with ” javax.net.ssl.SSLHandshakeException: no cipher suites in common“.

Java has some debugging capabilities embedded and they are pretty easy to use with the OpenDJ LDAP directory server, which just need to be restarted with some additional arguments: -Djavax.net.debug=all or -Djavax.net.debug=ssl.

There are two ways to add extra arguments to the OpenDJ server startup command, using an environment variable, or using the java.properties file.

Using env variable

– you define the OPENDS_JAVA_ARGS environment variable. And you restart the server. If you do so, make sure you include all previous arguments.

OPENDS_JAVA_ARGS='-server -Xms1G -Xmx1G -Djavax.net.debug=ssl,handshake,trustmanager' bin/start-ds

Using the java.properties file

Edit the java.properties file in the config directory.
Since you probably only want to track the OpenDS directory server SSL access, you should append the -Djavax.net.debug=ssl,handshake,trustmanager args to the start-ds line (rather than applying it to all commands).

start-ds.java-args=-Xms1G -Xmx1G -server -Djavax.net.debug=ssl,handshake,trustmanager

Save the file and run the dsjavaproperties command:

bin/dsjavaproperties

Now restart the server, using the start-ds command

Where is the output ?

All SSL related logs are output in the logs/server.out file.
To test, you can use ldapsearch :

bin/ldapsearch -Z -X -p 1636 -b "" -s base '(objectclass=*)'

And if you look into the logs/server.out file, you will see something similar to this:

Using SSLEngineImpl.
 Allow unsafe renegotiation: false
 Allow legacy hello messages: true
 Is initial handshake: true
 Is secure renegotiation: false
 LDAP Request Handler 0 for connection handler LDAP Connection Handler 0.0.0.0 port 1636, READ: SSL v2, contentType = Handshake, translated length = 81
 *** ClientHello, TLSv1
 RandomCookie: GMT: 1287771875 bytes = { 68, 231, 5, 253, 105, 26, 137, 36, 38, 238, 12, 141, 110, 12, 59, 10, 192, 135, 113, 119, 108, 153, 10, 31, 127, 120, 110, 61 }
 Session ID: {}
 Cipher Suites: [SSL_RSA_WITH_RC4_128_MD5, SSL_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_DES_CBC_SHA, SSL_DHE_RSA_WITH_DES_CBC_SHA, SSL_DHE_DSS_WITH_DES_CBC_SHA, SSL_RSA_EXPORT_WITH_RC4_40_MD5, SSL_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
 ...

This will help you to identify what part of the secure connection is failing and fix it.

Note that -Djavax.net.debug=ssl enables debug of the SSL connections, while -Djavax.net.debug=all enables full debugging including use of certificates, and more. You can also find more debug options by using -Djavax.net.debug=help.

 

OpenDJ Tip: Auto-completion of dsconfig command

With OpenDJ LDAP directory services, a single command-line tool, dsconfig, is used to manage every configuration parameter of the server.

The dsconfig command has several modes and useful options, some of them are not well known. So let me repeat them.

  • interactive mode: if started with no parameter or no command, the tool goes in interactive mode. This is a nice way to discover the various parts of OpenDJ configuration.
  • advanced mode: There are really a lot of configuration parameters in OpenDJ, so not all of them can be read or set by default. An advanced mode allows to view and edit the hidden ones.
  • scripted mode: dsconfig can be used with a file containing several commands, and will call them one after the other.
  • teaching mode: if the option –displayCommand is used in interactive mode, it will display the complete command to use for non interactive mode, or for scripted mode.
But regardless of the different modes, there are really too many commands and options to remember. So shell completion can come to the rescue.
Just insert the following lines in your .bashrc or .cshrc file.

Bash:

complete -W "`bin/dsconfig --help-all|grep '^[a-z].*'`" dsconfig

csh:

set DSC = `<OpenDJPath>/bin/dsconfig --help-all|grep '^[a-z].*'`
complete dsconfig "p/1/($DSC)/"

And now type bin/dsconfig set[TAB] and the list of commands appears magically…

$ dsconfig set-password-[TAB][TAB]
set-password-generator-prop       set-password-storage-scheme-prop
set-password-policy-prop          set-password-validator-prop
$ dsconfig set-password-

OpenDJ: Enabling the External Change Log on a single server

Yesterday, I described how easy it is to enable Multi-Master Replication between 2 instances of OpenDJ. One of the nice thing with OpenDJ replication, is that all changes are also publicly available (subject to access control) through LDAP under the cn=changelog suffix, also nick-named the External Change Log.

But the command to enable replication and thus the External Change Log requires 2 servers. So what if you want to enable the External Change Log on a single server ?

Note that this is not a fully supported procedure, but is handy for unit testing against the External Change Log. In production environment, you will have multiple servers for high availability and thus the External Change Log will be automatically configured.

Well you cannot use the dsreplication command, but you can configure the OpenDJ instance with the dsconfig utility.

$ bin/dsconfig create-replication-server -h ldap1.example.com -p 4444 \
  -D "cn=directory manager" -w secret12 -X -n \
  --provider-name "Multimaster Synchronization" --set replication-port:8989 \
  --set replication-server-id:2 --type generic
  
$ bin/dsconfig create-replication-domain -h ldap1.example.com -p 4444 \
  -D "cn=directory manager" -w secret12 -X -n \
  --provider-name "Multimaster Synchronization" --set base-dn:dc=example,dc=com \
  --set replication-server:ldap1.example.com:8989 --set server-id:3 \
  --type generic --domain-name example_com

If you want to be able to join this server in a replication topology, you should also create the global administrator’s entry. If you do so, then you will be able to use the dsreplication enable command as illustrated here.

$ bin/dsframework create-admin-user -X -h ldap1.example.com -p 4444 \
  -D "cn=Directory Manager" -w secret12 --userID admin --set password:password

Once enabled, you can read or search the changes with ldapsearch or other LDAP clients :

$ bin/ldapsearch -D cn=directory\ manager -w secret12 -h ldap1.example.com -p 1389 \
  -J "1.3.6.1.4.1.26027.1.5.4:false:;" -b "cn=changelog" '(objectclass=*)'
dn: cn=changelog
cn: changelog
objectClass: top
objectClass: container

# Public changelog exchange control(1.3.6.1.4.1.26027.1.5.4): dc=example,dc=com:0000012fd9bdf863000300000001;
dn: replicationCSN=0000012fd9bdf863000300000001,dc=example,dc=com,cn=changelog
targetDN: cn=a,ou=people,dc=example,dc=com
changeNumber: 0
changes:: b2JqZWN0Q2xhc3M6IHBlcnNvbgpvYmplY3RDbGFzczogdG9wCmNuOiBhCnNuOiBhCmVudH
 J5VVVJRDogNWQzMTNlY2UtYjY4Mi00MDFiLTg2NmYtM2NiZWNlMWNjNTJjCmNyZWF0ZVRpbWVzdGFtc
 DogMjAxMTA1MTAxMTQ5NTZaCmNyZWF0b3JzTmFtZTogY249RGlyZWN0b3J5IE1hbmFnZXIsY249Um9v
 dCBETnMsY249Y29uZmlnCg==
changeType: add
changeTime: 20110510114956Z
objectClass: top
objectClass: changeLogEntry

# Public changelog exchange control(1.3.6.1.4.1.26027.1.5.4): dc=example,dc=com:0000012fd9be46af000300000002;
dn: replicationCSN=0000012fd9be46af000300000002,dc=example,dc=com,cn=changelog
targetDN: cn=a,ou=people,dc=example,dc=com
changeNumber: 0
changes:: YWRkOiBkZXNjcmlwdGlvbgpkZXNjcmlwdGlvbjogTmV3IG9uZQotCnJlcGxhY2U6IG1vZG
 lmaWVyc05hbWUKbW9kaWZpZXJzTmFtZTogY249RGlyZWN0b3J5IE1hbmFnZXIsY249Um9vdCBETnMsY
 249Y29uZmlnCi0KcmVwbGFjZTogbW9kaWZ5VGltZXN0YW1wCm1vZGlmeVRpbWVzdGFtcDogMjAxMTA1
 MTAxMTUwMTZaCi0K
changeType: modify
changeTime: 20110510115016Z
objectClass: top
objectClass: changeLogEntry

Note: the search above uses the “Cookie Control” which is the optimized way to search the External Change Log. The value “;” means that the “cookie” is unknown, and therefore the search starts from the first change. If you want to continue from the last change received, provide the string value that is either in the ChangeLogCookie operational attribute (returned if asked for) or the comment before the change itself.

More OpenDJ Tips…

I’ve already mentioned that Mark Craig has joined ForgeRock and started to blog about OpenDJ.

Here’s a few tips he’s recently posted about OpenDJ :

I’m sure there are more to come.

If you’re not following Mark’s blog feed yet, you should !

Directory Administrative Accounts – cont.

In a previous post, I’ve explained how to create multiple administrative accounts in the OpenDJ directory service. Today we’re going to look at restricting what applications can do with these administrative accounts.

In the OpenDJ directory service, there are 2 types of authorization systems :

  • Privileges control who can perform which administrative tasks : backup, restore, stop and restart of the server, managing acl…
  • Access Controls Lists govern the access to the data through LDAP operations.

Most operations involving sensitive or administrative data require that the user has both the privilege and authorization. This allows finer-grain authorization for specific data related action such as managing acl or reseting passwords.

The Privilege SubSystem

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.

That set of privileges assigned to RootDN users is defined by the “default-root-privilege-name” property, which can be listed or modified using the dsconfig command.

To list the current default privileges assigned to all RootDN users :

bin/dsconfig -h localhost -p 4444 -X -D “cn=directory manager” -j /var/tmp/dmpassfile -n get-root-dn-prop

Property                    : Value(s)
----------------------------:--------------------------------------------------
default-root-privilege-name : backend-backup, backend-restore, bypass-acl,
                            : bypass-lockdown, cancel-request, config-read,
                            : config-write, disconnect-client, ldif-export,
                            : ldif-import, modify-acl, password-reset,
                            : privilege-change, server-lockdown,
                            : server-restart, server-shutdown, subentry-write,
                            : unindexed-search, update-schema

To make sure that all searches are done with proper indexes, you may want to remove the privilege to all Administrative Accounts to perform unindexed searches :

bin/dsconfig -h localhost -p 4444 -X -D “cn=directory manager” -j /var/tmp/dmpassfile -n set-root-dn-prop –remove default-root-privilege-name:unindexed-search

Note: Removing this privilege to all Administrative accounts including the default “cn=Directory Manager” may have side effects for certain internal operations such as group membership, referential integrity…

Whenever adding a new Administrative Account under the “cn=Root DNs,cn=config” container, it automatically inherits from those privileges. But each administrative account can then be denied or added specific privileges by adding values of the “ds-privilege-name” attribute directly in the user entry, in the form of the privilege name or a Minus sign followed by the privilege name.

For example, I can make sure that my newly added Administrative Account is subject to access controls and cannot modify access controls lists, I modify the entry as followed :

Create a temporary file modAdminPrivileges.ldif with the following LDIF modification statement:

dn: cn=Second Admin,cn=Root DNs,cn=config
changetype: modify
add: ds-privilege-name
ds-privilege-name: -bypass-acl
ds-privilege-name: -modify-acl

And then apply it to the directory service with the following command :

bin/ldapmodify -h localhost -p 4444 -X -Z -D “cn=directory manager” -j /var/tmp/dmpassfile -f modAdminPrivileges.ldif

Using similar commands and different privileges, one could completely separate the administrative tasks and restrict each Administrative Account to its specific tasks.

But the great thing about privileges is that they can also be granted to regular users part of the Directory Information Tree, allowing them to become administrators for very specific tasks.

The complete list of privileges supported in OpenDJ 2.4 is below:

  • backend-restore: Ability to perform backend restore operations.
  • bypass-acl: Ability to bypass access control evaluation.
  • bypass-lockdown:Ability to bypass server lockdown mode.
  • cancel-request: Ability to cancel arbitrary client requests.
  • config-read: Ability to read the server configuration.
  • config-write: Ability to update the server configuration.
  • data-sync: Ability to participate in a data synchronization environment.
  • disconnect-client: Ability to terminate arbitrary client connections.
  • jmx-notify: Ability to subscribe to JMX notifications.
  • jmx-read: Ability to perform read operations via JMX.
  • jmx-write: Ability to perform write operations via JMX.
  • ldif-export: Ability to perform LDIF export operations.
  • ldif-import: Ability to perform LDIF import operations.
  • modify-acl: Ability to modify access control rules.
  • password-reset: Ability to reset user passwords.
  • privilege-change: Ability to change the set of privileges for a user, or to change the set of privileges automatically assigned to a root user.
  • proxied-auth: Ability to perform proxied authorization or request an alternate authorization identity.
  • server-lockdown: Ability to lockdown a server.
  • server-restart: Ability to request a server restart.
  • server-shutdown: Ability to request a server shutdown.
  • subentry-write: Ability to perform write operations on LDAP subentries.
  • unindexed-search: Ability to perform an unindexed search
  • update-schema: Ability to update the server schema.

Multiple Directory Administrative Users

Most of LDAP directory servers configure a single well known directory administrative account (cn=Directory Manager [,dc=example,dc=com]) which has full access to everything. While there is a need to have one special user to bootstrap the server, we are too often seeing that special account being used by all applications that have specific administrative needs : the provisioning application, the email management application, …

OpenDJ has different mechanisms to define multiple administrative accounts, but today, I’m going to focus on the “Root DNs” i.e. defining multiple Directory Managers.

The default administrative account is “cn=Directory Manager”, and is stored in the configuration under the “cn=Root DNs,cn=config” container entry.

Adding another administrative account is as simple as adding another entry under that container, with one specific objectClass : ds-cfg-root-dn-user.

Create a file newAdmin.ldif

dn: cn=Second Admin,cn=Root DNs,cn=config
cn: Second Admin
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
objectclass: ds-cfg-root-dn-user
sn: Second Admin
ds-cfg-alternate-bind-dn: cn=Admin2,dc=example,dc=com
ds-pwp-password-policy-dn: cn=Root Password Policy,cn=Password Policies,cn=config
userPassword: password42

ldapmodify -a -D cn=Directory Manager -j /var/tmp/dmpassfile -f  newAdmin.ldif

Processing ADD request for cn=Second Admin,cn=Root DNs,cn=config
ADD operation successful for DN cn=Second Admin,cn=Root DNs,cn=config

If you prefer, you can choose not to set the password in the LDIF file, but set it in a secure way afterwards :

$ bin/ldappasswordmodify -p 1389 -D “cn=directory manager” -j /var/tmp/dmpassfile -a “”cn=Admin2,dc=example,dc=com” -N /var/tmp/newpw
The LDAP password modify operation was successful

Where /var/tmp/dmpassfile contains the password for “cn=directory manager” and /var/tmp/newpw the new password for Admin2.

Did you notice the “ds-cfg-alternate-bind-dn” attribute in the definition of the new administrative account ? This enables to authenticate to the directory server with the DN value specified in this attribute, while the entry still has a DN and is located under the “cn=config” suffix.

So now, don’t hesitate to create different administrative accounts for the various applications that need special access to the directory.

In a follow-up post, I will explain how to restrict what those administrative accounts can do in the OpenDJ directory service.

OpenDS Silent install

Opends Logo TagOne of the things we’re the most proud of in the OpenDS project is the simplicity of installation and initial configuration, thanks to the Java Web Start QuickSetup installer. We say that you can download, install and configure OpenDS to run on your machine in less than 3 minutes and 6 clicks.

But OpenDS can also be downloaded as a Zip and installed with the setup program, which can be either graphical or in command line and even used in silent mode.

The OpenDS community is often full of resources and Lucas Rockwell pointed out to his script for downloading and installing OpenDS automatically. I’ve taken the liberty to improve his idea and show it here :

#!/bin/sh

# This is the OpenDS version number to install

if [ -z “$1” ]
then
VER=2.2.0
else
VER=$1
fi

# Download with curl or wget, uncomment the preferred download method
curl -O http://www.opends.org/promoted-builds/${VER}/OpenDS-${VER}.zip
# wget -nd http://www.opends.org/promoted-builds/${VER}/OpenDS-${VER}.zip

unzip OpenDS-${VER}.zip

cd OpenDS-${VER}/

# Some possible option change :
# Replace -d 20 (generate sample data with 20 entries) with -a (create
# top entry) or -l <ldifFile> (load data from the LDIF file)
# Change -w “secret12” with -j /tmp/me/passwordfile to avoid hardcoded
# cleartext password
# Add -O to avoid starting the server after install
# Add -Q for a quiet install
# ./setup –help for more information on options

./setup –cli -n -b “dc=example,dc=com” -d 20 -p 1389 \
–adminConnectorPort 4444 -D “cn=Directory Manager” \
-w “secret12” -q -Z 1636 –generateSelfSignedCertificate

As you can see, it’s really trivial and it does the work from a few seconds to a few minutes depending on the speed of your internet connection.

The script can be downloaded here.

Have fun !

Technorati Tags: , , , ,

Enumeration based attributes in LDAP

Yesterday I’ve explained how to restrict LDAP attribute values using Regular Expression based syntaxes, with the OpenDS directory server. There is another use case for restricting attribute values: when there is an enumerated list of possible values. It’s possible to define finite list of values as a regular expression, but as we wanted to be able to provide additional values, we added in OpenDS the ability to define Enumeration based syntaxes, and we implemented it as a syntax definition extension as well.

Here’s an example of use of an Enumeration syntax for the day of the week. Let’s first define and load the syntax in the OpenDS directory server’s schema :

$ bin/ldapmodify -D cn=directory\ manager -p 1389

Password for user ‘cn=directory manager’:

dn: cn=schema

changetype: modify

add: ldapsyntaxes

ldapSyntaxes: ( 1.3.6.1.4.1.32473.4 DESC ‘Day Of The Week’

X-ENUM ( ‘monday’ ‘tuesday’ ‘wednesday’ ‘thursday’

‘friday’ ‘saturday’ ‘sunday’ ) )

Processing MODIFY request for cn=schema

MODIFY operation successful for DN cn=schema

^D

Let’s use the syntax in an attribute, itself used in an object classes:

$ bin/ldapmodify -D cn=directory\ manager -p 1389

Password for user ‘cn=directory manager’:

dn: cn=schema

changetype: modify

add: attributetypes

attributetypes: ( 1.3.6.1.4.1.32473.5 NAME ‘test-attr-enum’

SYNTAX 1.3.6.1.4.1.32473.4 )



add: objectclasses

objectclasses: ( 1.3.6.1.4.1.32473.6 NAME ‘testOCenum’ SUP top

AUXILIARY MUST test-attr-enum)

Processing MODIFY request for cn=schema

MODIFY operation successful for DN cn=schema

^D

Let’s create a test entry :

$ bin/ldapmodify -D cn=directory\ manager -p 1389

Password for user ‘cn=directory manager’:

dn: cn=TestEntry,dc=example,dc=com

changetype: add

sn: TestEntry

cn: TestEntry

objectclass: Person

Processing ADD request for cn=TestEntry,dc=example,dc=com

ADD operation successful for DN cn=TestEntry,dc=example,dc=com

^D

And now, let’s make use of the newly created schema objects with that test entry :

$ bin/ldapmodify -D cn=directory\ manager -p 1389

Password for user ‘cn=directory manager’:

dn: cn=TestEntry,dc=example,dc=com

changetype: modify

add: objectclass

objectclass: testOCenum



add: test-attr-enum

test-attr-enum: monday

Processing MODIFY request for cn=TestEntry,dc=example,dc=com

MODIFY operation successful for DN cn=TestEntry,dc=example,dc=com

^D

But if the value isn’t part of the enumeration, it gets rejected :

$ bin/ldapmodify -D cn=directory\ manager -p 1389

Password for user ‘cn=directory manager’:

dn: cn=TestEntry,dc=example,dc=com

changetype: modify

replace: test-attr-enum

test-attr-enum: Lundi

Processing MODIFY request for cn=TestEntry,dc=example,dc=com

MODIFY operation failed

Result Code: 21 (Invalid Attribute Syntax)

Additional Information: When attempting to modify entry cn=TestEntry,dc=example,dc=com to replace the set of values for attribute test-attr-enum, value "Lundi" was found to be invalid according to the associated syntax: The provided value "Lundi" cannot be parsed because it is not allowed by enumeration syntax with OID "1.3.6.1.4.1.32473.4"

$

The enumeration syntaxes, like the regular expression one, matches like a DirectoryString, that is matches using CaseIgnoreMatch equality rule.

$ bin/ldapsearch -p 1389 -D cn=directory\ manager -w secret12 \

-b "dc=example,dc=com" ‘(test-attr-enum=Monday)’

dn: cn=TestEntry,dc=example,dc=com

objectClass: Person

objectClass: top

objectClass: testOCenum

test-attr-enum: monday

cn: TestEntry

sn: TestEntry

But the biggest advantage of the Enumeration syntax is the ability to use Ordering match, which is not based on strings, but on the order of the enumerated values in the syntax definition. So "Monday" is lower than "Tuesday" which is lower than "Wednesday"…

$ bin/ldapsearch -p 1389 -D cn=directory\ manager -w secret12 \

-b "dc=example,dc=com" ‘(test-attr-enum<=Thursday)’

dn: cn=TestEntry,dc=example,dc=com

objectClass: Person

objectClass: top

objectClass: testOCenum

test-attr-enum: monday

cn: TestEntry

sn: TestEntry

I hope you will find this useful and make use of these syntaxes. To do so, you need to download and install OpenDS 2.2 Release Candidate 1 (or higher).

And if you have additional requirements with syntaxes, I’d be happy to hear about them.

Technorati Tags: , , , ,

Regular Expression based attributes in LDAP

One of the question that I get frequently asked when discussing with customers or coworkers about defining custom schema and attributes, is how to restrict the values that can be set to an attribute. From a pure LDAP standard point of view, you would need to define a new syntax and describe the valid values. Then you would need to check with the directory server’s vendor or discuss with the open source developers to get the syntax implemented in the server, either in the core product, or as a plug-in extension. In the end, the easy choice goes to use a standard syntax (like DirectoryString) and let all client applications validate the values.

In OpenDS, we’ve choose another option. We have added support for regular expression based syntaxes, and implemented this as a syntax definition extension.

So in order to define, for example, an attribute whose values must be in the form of host:port, you simply need to define a new syntax for it with the regular expression pattern and load it in the server’s schema:

$ bin/ldapmodify -D cn=directory\ manager -p 1389

Password for user ‘cn=directory manager’:

dn: cn=schema

changetype: modify

add: ldapsyntaxes

ldapSyntaxes: ( 1.3.6.1.4.1.32473.1

DESC ‘Host and Port in the format of HOST:PORT’

X-PATTERN ‘^[a-zA-Z][.a-zA-Z0-9-]+:[0-9]+$’ )

Processing MODIFY request for cn=schema

MODIFY operation successful for DN cn=schema

^D

And then you can make use of the newly defined syntax in attributes.

$ bin/ldapmodify -D cn=directory\ manager -p 1389

Password for user ‘cn=directory manager’:

dn: cn=schema

changetype: modify

add: attributetypes

attributetypes: ( 1.3.6.1.4.1.32473.2 NAME ‘test-attr-regex’ SYNTAX 1.3.6.1.4.1.32473.1 )



add: objectclasses

objectclasses: ( 1.3.6.1.4.1.32473.3 NAME ‘testOCregex’ SUP top AUXILIARY MUST test-attr-regex)

Processing MODIFY request for cn=schema

MODIFY operation successful for DN cn=schema

^D

Let’s create a test entry

$ bin/ldapmodify -D cn=directory\ manager -p 1389

Password for user ‘cn=directory manager’:

dn: cn=TestEntry,dc=example,dc=com

changetype: add

sn: TestEntry

cn: TestEntry

objectclass: Person

Processing ADD request for cn=TestEntry,dc=example,dc=com

ADD operation successful for DN cn=TestEntry,dc=example,dc=com

^D

And now make use of this new attribute and objectclass:

$ bin/ldapmodify -D cn=directory\ manager -p 1389

Password for user ‘cn=directory manager’:

dn: cn=TestEntry,dc=example,dc=com

changetype: modify

add: objectclass

objectclass: testOCregex



add: test-attr-regex

test-attr-regex: localhost:1389

Processing MODIFY request for cn=TestEntry,dc=example,dc=com

MODIFY operation successful for DN cn=TestEntry,dc=example,dc=com

^D

$ bin/ldapmodify -D cn=directory\ manager -p 1389

Password for user ‘cn=directory manager’:

dn: cn=testentry,dc=example,dc=com

changetype: modify

replace: test-attr-regex

test-attr-regex: foobar.com

Processing MODIFY request for cn=testentry,dc=example,dc=com

MODIFY operation failed

Result Code: 21 (Invalid Attribute Syntax)

Additional Information: When attempting to modify entry cn=testentry,dc=example,dc=com to replace the set of values for attribute test-attr-regex, value "foobar.com" was found to be invalid according to the associated syntax: The provided value "foobar.com" cannot be parsed as a valid regex syntax because it does not match the pattern "^[a-zA-Z][.a-zA-Z0-9-]+:[0-9]+$"

It’s simple and efficient. But wait there’s more to come, tomorrow.

Technorati Tags: , , , ,

Tip on OpenDS localization and error messages…

The OpenDS LDAP directory server is localized by default in many different languages, thanks to our community.

All (well we try) messages from the client tools, command lines or graphical, are translated in Chinese (Simplified and Traditional), German, French, Japanese, Korean and Spanish (and soon Polish). But the server error messages are also localized, and the OpenDS directory server picks up the current locale of the process owner to choose in which language to print them.

Not everyone wants to have the server error messages in their own language, especially in distributed or international teams. There is a way to make sure the server always uses English as the language for the message, regardless of who starts it, and it’s very simple (thanks to Josu for reminding me how to do it 😉 ):

Edit the java.properties file (from the config/ directory) and append the following to the start-ds.java-args line:

-Duser.language=en -Duser.country=US

Example:

start-ds.java-args=-server -Xms128m -Xmx256m -Duser.language=en -Duser.country=US

Now run the dsjavaproperties command and restart the server.

Et voila ! All in English.

Technorati Tags: , , , , , ,

Updated schema-convert.py script for OpenDS.

Opends Logo TagBack in March, in the series of OpenDS tips, I had written an article and a script to help converting schema files for OpenLDAP to a format suitable for OpenDS.

I received multiple suggestions for the scripts as well as sample schema files that would not load in OpenDS after being converted. Most of the time, it was due to the lack of respect of the RFC syntaxes.

Recently, I found the time to update the script and produce better conversion. You can get the python script here. The March article has been refreshed with the new script as well.

Technorati Tags: , , , , ,

LDAP Tip : Counting the number of entries in a branch…

This is a general LDAP tip and it applies to OpenDS the open source LDAP directory service in Java, as well as Sun Directory Server (all versions) and other LDAP servers:

How can I know the number of entries under a specific node of the Directory Information Tree ?
Well it’s simple. Every entry contains an operational attribute that specify the number of immediate subordinates entry : numSubordinates.

So to retrieve the number of entries under a specific node of the DIT, for example in the ou=people,dc=example,dc=com, a simple read is required.

$ bin/ldapsearch -p 3389 -D "cn=directory manager" -w – -b "ou=people,dc=example,dc=com" -s base ‘(objectclass=*)’ numsubordinates

Password for user ‘cn=directory manager’:

dn: ou=People,dc=example,dc=com

numsubordinates: 21

This attribute is defined in an expired Internet-Draft but has been well implemented in many servers. There are often some limitations, like the value only counts entries on the same server, but overall it’s a very useful attribute especially when browsing through the DIT.

OpenDS and Sun Directory Server also implements another attribute : hasSubordinates, defined in X.501. hasSubordinates is a boolean and returns "true" or "false" depending on whether the entry is a branch or a leaf in the Directory Information Tree.

Technorati Tags: , , , ,

OpenDS Tips: Adding schema from OpenLDAP

Opends Logo TagThe OpenDS schema is slightly different from the OpenLDAP one, but it’s quite simple to convert schema files from one format to another.

OpenDS, like Sun Directory Server Enterprise Edition and Fedora DS, uses a strict RFC 4512 and LDIF format.

In OpenLDAP, the actual text of the schema definition is similar and described using the RFC 4512 notation but uses the printer friendly notation, similar to the textual description in RFC documents.

So when converting schema files from OpenLDAP, for use in OpenDS, there are mainly 4 differences to take care of:

  • In OpenLDAP, an attribute definition begins with “attributetype” while in OpenDS it begins with “attributetypes: “
  • Similarly, in OpenLDAP, an object class definition has an “objectclass” prefix while it is “objectclasses: “
  • OpenDS follows the LDIF conventions that the continuation line begins with a single space character, and that an empty line is an entry separator
  • Finally, OpenDS schema files have a .ldif extension and only this extension is considered when loading schema from the config/schema directory.

The following python script can be used to convert an OpenLDAP schema file to a format usable by OpenDS (as well as Sun Directory Enterprise Edition). The script also recursively expands the OID macro format used in OpenLDAP schema files.

For now, syntax definitions are currently ignored as they cannot be loaded in OpenDS as they require associated code.

Usage is quite simple: schema-convert.py -o result.ldif openldap-schema-file

Enjoy and don’t hesitate to send feedback, suggestions for improvements.

Update on March 15: I’ve added support for name prefixed OIDs substitution as suggested by Martin Gwerder.

Update on April 9: OpenDS schema files uses the .ldif extension, and only files with this extension are loaded by the server from the config/schema directory.

Update on July 31: Now checking and removing quotes around Sup or Syntaxes values.

 


#!/usr/bin/env python
# encoding: utf-8
"""
schema-convert.py
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License, Version 1.0 only
# (the "License").  You may not use this file except in compliance
# with the License.
#
# You can obtain a copy of the license at
# trunk/opends/resource/legal-notices/OpenDS.LICENSE
# or https://OpenDS.dev.java.net/OpenDS.LICENSE.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at
# trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
# add the following below this CDDL HEADER, with the fields enclosed
# by brackets "[]" replaced with your own identifying information:
#      Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
#      Copyright 2009 Sun Microsystems, Inc.
Created by Ludovic Poitou on 2009-01-28.
This program converts an OpenLDAP schema file to the OpenDS schema file format.
"""
import sys
import getopt
import re
import string
help_message = '''
Usage: schema-convert.py [options] <openldap-schema-file>
options:
\t -o output : specifies the output file, otherwise stdout is used
\t -v : verbose mode
'''
class Usage(Exception):
def __init__(self, msg):
self.msg = msg
def main(argv=None):
output = ""
seclineoid = 0
IDs = {}
if argv is None:
argv = sys.argv
try:
try:
opts, args = getopt.getopt(argv[1:], "ho:v", ["help", "output="])
except getopt.error, msg:
raise Usage(msg)
# option processing
for option, value in opts:
if option == "-v":
verbose = True
if option in ("-h", "--help"):
raise Usage(help_message)
if option in ("-o", "--output"):
output = value
except Usage, err:
print >> sys.stderr, sys.argv[0].split("/")[-1] + ": " + str(err.msg)
print >> sys.stderr, "\t for help use --help"
return 2
try:
infile = open(args[0], "r")
except Usage, err:
print >> sys.stderr, "Can't open file: " + str(err.msg)
if output != "":
try:
outfile = open(output, "w")
except Usage, err:
print >> sys.stderr, "Can't open output file: " + str(err.msg)
else:
outfile = sys.stdout
outfile.write("dn: cn=schema\n")
outfile.write("objectclass: top\n")
outfile.write("")
for i in infile:
newline = ""
if not i.strip():
continue
#if i.startswith("#"):
#	continue
if re.match("objectidentifier", i, re.IGNORECASE):
# Need to fill in an array of identifiers
oid = i.split()
if not re.match ("[0-9.]+", oid[2]):
suboid = oid[2].split(':')
IDs[oid[1]] = IDs[suboid[0]] + "." + suboid[1]
else:
IDs[oid[1]] = oid[2]
continue
if seclineoid == 1:
subattr = i.split()
if not re.match("[0-9.]+", subattr[0]):
if re.match (".*:", subattr[0]):
# The OID is an name prefixed OID. Replace string with the OID
suboid = subattr[0].split(":")
repl = IDs[suboid[0]] + "." + suboid[1]
else:
# The OID is a name. Replace string with the OID
repl = IDs[subattr[0]]
newline = string.replace(i, subattr[0], repl, 1)
seclineoid = 0
if re.match("attributetype ", i, re.IGNORECASE):
newline = re.sub("attribute[tT]ype", "attributeTypes:", i)
# replace OID string with real OID if necessary
subattr = newline.split()
if len(subattr) < 3:
seclineoid = 1
else:
if not re.match("[0-9.]+", subattr[2]):
if re.match (".*:", subattr[2]):
# The OID is an name prefixed OID. Replace string with the OID
suboid = subattr[2].split(":")
repl = IDs[suboid[0]] + "." + suboid[1]
else:
# The OID is a name. Replace string with the OID
repl = IDs[subattr[2]]
newline = string.replace(newline, subattr[2], repl, 1)
if re.match("objectclass ", i, re.IGNORECASE):
newline = re.sub("object[cC]lass", "objectClasses:", i)
# replace OID String with real OID
subattr = newline.split()
if len(subattr) < 3:
seclineoid = 1
else:
if not re.match("[0-9.]+", subattr[2]):
if re.match (".*:", subattr[2]):
# The OID is an name prefixed OID. Replace string with the OID
suboid = subattr[2].split(":")
repl = IDs[suboid[0]] + "." + suboid[1]
else:
# The OID is a name. Replace string with the OID
repl = IDs[subattr[2]]
newline = string.replace(newline, subattr[2], repl, 1)
# Remove quoted syntax.
if re.search("SYNTAX\s'[\d.]+'", newline):
# Found a quoted syntax in an already updated line
newline = re.sub("SYNTAX '([\d.]+)'", "SYNTAX \g<1>", newline)
else:
if re.search("SYNTAX\s'[\d.]+'", i):
# Found a quoted syntax in the original line
newline = re.sub("SYNTAX '([\d.]+)'", "SYNTAX \g<1>", i)
# Remove quoted SUP
if re.search("SUP\s'[\w\-]+'", newline):
# Found a quoted sup in an already updated line
newline = re.sub("SUP '([\w\-]+)'", "SUP \g<1>", newline)
else:
if re.search("SUP\s'[\w\-]+'", i):
# Found a quoted sup in the original line
newline = re.sub("SUP '([\w\-]+)'", "SUP \g<1>", i)
# transform continuation lines with only 2 spaces
if re.match("  +|\t", i):
if newline != "":
newline = "  " + newline.strip() + "\n"
else:
newline = "  " + i.strip() + "\n"
if newline != "":
outfile.write(newline)
else:
outfile.write(i)
outfile.close()
if __name__ == "__main__":
sys.exit(main())

Technorati Tags: , , , , , , ,

OpenDS Tips: Importing LDIF with encrypted passwords.

Opends Logo TagBy default, the OpenDS LDAP directory server password policy is set to reject encrypted passwords, as it cannot check that they match the quality requirements.

So when adding or importing data with encrypted passwords, the server returns some error like this:

LDAP: error code 53 – Pre-encoded passwords are not allowed for the password attribute userPassword

To allow pre-encoded passwords, the default password policy settings must be changed. This can be done using the dsconfig command line tool in advanced mode:

$ dsconfig --advanced -p 4444 -h localhost -D "cn=directory manager" -X
>>>> Specify OpenDS LDAP connection parameters
Password for user 'cn=directory manager':
>>>> OpenDS configuration console main menu
What do you want to configure?
1)   Access Control Handler          24)  Monitor Provider
2)   Account Status Notification     25)  Network Group
Handler
3)   Administration Connector        26)  Network Group Criteria
4)   Alert Handler                   27)  Network Group Request Filtering
Policy
5)   Attribute Syntax                28)  Network Group Resource Limits
6)   Backend                         29)  Password Generator
7)   Certificate Mapper              30)  Password Policy
8)   Connection Handler              31)  Password Storage Scheme
9)   Crypto Manager                  32)  Password Validator
10)  Debug Target                    33)  Plugin
11)  Entry Cache                     34)  Plugin Root
12)  Extended Operation Handler      35)  Replication Domain
13)  Extension                       36)  Replication Server
14)  Global Configuration            37)  Root DN
15)  Group Implementation            38)  Root DSE Backend
16)  Identity Mapper                 39)  SASL Mechanism Handler
17)  Key Manager Provider            40)  Synchronization Provider
18)  Local DB Index                  41)  Trust Manager Provider
19)  Local DB VLV Index              42)  Virtual Attribute
20)  Log Publisher                   43)  Work Queue
21)  Log Retention Policy            44)  Workflow
22)  Log Rotation Policy             45)  Workflow Element
23)  Matching Rule
q)   quit
Enter choice: 30
>>>> Password Policy management menu
What would you like to do?
1)  List existing Password Policies
2)  Create a new Password Policy
3)  View and edit an existing Password Policy
4)  Delete an existing Password Policy
b)  back
q)  quit
Enter choice [b]: 3
>>>> Select the Password Policy from the following list:
1)  Default Password Policy
2)  Root Password Policy
c)  cancel
q)  quit
Enter choice [c]: 1
>>>> Configure the properties of the Password Policy
Property                                   Value(s)
--------------------------------------------------------------------
1)   account-status-notification-handler        -
2)   allow-expired-password-changes             false
3)   allow-multiple-password-values             false
4)   allow-pre-encoded-passwords                false
5)   allow-user-password-changes                true
6)   default-password-storage-scheme            Salted SHA-1
7)   deprecated-password-storage-scheme         -
8)   expire-passwords-without-warning           false
9)   force-change-on-add                        false
10)  force-change-on-reset                      false
11)  grace-login-count                          0
12)  idle-lockout-interval                      0 s
13)  last-login-time-attribute                  -
14)  last-login-time-format                     -
15)  lockout-duration                           0 s
16)  lockout-failure-count                      0
17)  lockout-failure-expiration-interval        0 s
18)  max-password-age                           0 s
19)  max-password-reset-age                     0 s
20)  min-password-age                           0 s
21)  password-attribute                         userpassword
22)  password-change-requires-current-password  false
23)  password-expiration-warning-interval       5 d
24)  password-generator                         Random Password Generator
25)  password-history-count                     0
26)  password-history-duration                  0 s
27)  password-validator                         -
28)  previous-last-login-time-format            -
29)  require-change-by-time                     -
30)  require-secure-authentication              false
31)  require-secure-password-changes            false
32)  skip-validation-for-administrators         false
33)  state-update-failure-policy                reactive
?)   help
f)   finish - apply any changes to the Password Policy
c)   cancel
q)   quit
Enter choice [f]: 4
>>>> Configuring the "allow-pre-encoded-passwords" property
Indicates whether users can change their passwords by providing a
pre-encoded value.
This can cause a security risk because the clear-text version of the
password is not known and therefore validation checks cannot be applied to
it.
Do you want to modify the "allow-pre-encoded-passwords" property?
1)  Keep the default value: false
2)  Change it to the value: true
?)  help
q)  quit
Enter choice [1]: 2
Press RETURN to continue
>>>> Configure the properties of the Password Policy
Property                                   Value(s)
--------------------------------------------------------------------
1)   account-status-notification-handler        -
2)   allow-expired-password-changes             false
3)   allow-multiple-password-values             false
4)   allow-pre-encoded-passwords                true
5)   allow-user-password-changes                true
6)   default-password-storage-scheme            Salted SHA-1
7)   deprecated-password-storage-scheme         -
8)   expire-passwords-without-warning           false
9)   force-change-on-add                        false
10)  force-change-on-reset                      false
11)  grace-login-count                          0
12)  idle-lockout-interval                      0 s
13)  last-login-time-attribute                  -
14)  last-login-time-format                     -
15)  lockout-duration                           0 s
16)  lockout-failure-count                      0
17)  lockout-failure-expiration-interval        0 s
18)  max-password-age                           0 s
19)  max-password-reset-age                     0 s
20)  min-password-age                           0 s
21)  password-attribute                         userpassword
22)  password-change-requires-current-password  false
23)  password-expiration-warning-interval       5 d
24)  password-generator                         Random Password Generator
25)  password-history-count                     0
26)  password-history-duration                  0 s
27)  password-validator                         -
28)  previous-last-login-time-format            -
29)  require-change-by-time                     -
30)  require-secure-authentication              false
31)  require-secure-password-changes            false
32)  skip-validation-for-administrators         false
33)  state-update-failure-policy                reactive
?)   help
f)   finish - apply any changes to the Password Policy
c)   cancel
q)   quit
Enter choice [f]:
The Password Policy was modified successfully
Press RETURN to continue

The equivalent non interactive command is:

$ dsconfig set-password-policy-prop \
--policy-name "Default Password Policy" \
--set allow-pre-encoded-passwords:true \
--hostname localhost \
--trustAll \
--port 4444 \
--bindDN "cn=directory manager" \
--bindPassword ****** \
--no-prompt

Alternately, this can be done over LDAP (although it’s not officially supported):

$ bin/ldapmodify -Z -X -p 4444 -h localhost -D "cn=directory manager"
Password for user 'cn=directory manager':
dn: cn=Default Password Policy,cn=Password Policies,cn=config
changetype: modify
replace: ds-cfg-allow-pre-encoded-passwords
ds-cfg-allow-pre-encoded-passwords: true
Processing MODIFY request for cn=Default Password Policy,cn=Password Policies,cn=config
MODIFY operation successful for DN cn=Default Password Policy,cn=Password Policies,cn=config

Technorati Tags: , , ,