LDAP: Using ldapmodify


ldapmodify is a command-line tool that can be used to modify directory server entries via the LDAP protocol. ldapmodify is distributed with most directory server software. Examples are given first in the legacy OpenLDAP syntax and second in the more modern syntax. Users should use the correct syntax for their local installation. The examples use the root DN, which in the case of this server is "cn=directory manager". Your friendly neighborhood LDAP administrator most likely will not allow the use of the root DN; contact the administrator to obtain access rights that can be used to perform the examples here, which include:

  1. add an entry
  2. modify an existing entry
  3. delete an existing entry

Naming Context

The following discussion refers to a directory server running on the localhost and listening to port 1389. Directory Servers use naming contexts to list the prefixes of the data that the server masters or shadows. If the directory server permits, the naming contexts can be discovered by querying the root DSE for the namingContexts attribute (to query the root DSE it is necessary to specify the base DN as the zero-length string and the search scope as base):


#
# The legacy OpenLDAP syntax
#
/usr/bin/ldapsearch -LLL -x -h localhost -p 1389 \
    -b '' -s base '(objectClass=*)' namingContexts
dn:
namingContexts: dc=example,dc=com


#
# The more modern syntax:
#
ldapsearch --hostname localhost --port 1389 \
    --baseDn '' --searchScope base \
    '(objectClass=*)' namingContexts
dn: 
namingContexts: dc=example,dc=com

As shown in the example, the prefix (naming context) of this directory server is 'dc=example,dc=com': this naming context will be used through the examples. Note that most modern directory servers have the ability to support multiple naming contexts.

Adding a New Entry

Create an LDIF file containing the data to use for the new entry. This example adds an entry for a user named Wolfie Mozart. The LDIF:


dn: uid=wam,ou=people,dc=example,dc=com
changetype: add
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
cn: Johannes Chrysostomus Wolfgangus Theophilus Mozart
cn: Wolfgang Amadeus Mozart
cn: Wolfie Mozart
displayName: Mozart
givenName: Wolfgang 
givenName: Wolfie
initials: wam
sn: Mozart
uid: wam
userPassword: the-password-to-use-for-mozart

Add the entry:


#
# The legacy ldapmodify syntax
#
/usr/bin/ldapmodify -x -h localhost -p 1389 \
   -D 'cn=directory manager' -w password -c -f /tmp/mozart.LDIF 
adding new entry "uid=wam,ou=people,dc=example,dc=com"

Warning icon There is an insidious error that could go un-noticed when using the legacy OpenLDAP ldapmodify tool. In the example LDIF, the line givenName: Wolfgang has a trailing space, which is not allowed. Leading or trailing spaces must be escaped. Note that the older ldapmodify tool fails to note this fact and adds the entry anyway. The result is that the directory server base-64 encodes the givenName which ended in a space. Look for the '::' (double colon) in the LDIF output of the ldapsearch tool:


/usr/bin/ldapsearch -LLL -x -h localhost -p 1389 \
  -b uid=wam,ou=people,dc=example,dc=com -s base '(objectClass=*)'
dn: uid=wam,ou=people,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
initials: wam
givenName:: V29sZmdhbmcg
givenName: Wolfie
uid: wam
cn: Johannes Chrysostomus Wolfgangus Theophilus Mozart
cn: Wolfgang Amadeus Mozart
cn: Wolfie Mozart
sn: Mozart
displayName: Mozart
#
# The more modern syntax (in this case also using
# the Start TLS extended operation request to promote
# the initial non-secure connection to TLS):
#
ldapsearch --hostname localhost --port 1389 \
   --useStartTls --trustAll \
   --baseDn uid=wam,ou=people,dc=example,dc=com \
   --searchScope base '(objectClass=*)'
dn: uid=wam,ou=people,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
initials: wam
givenName:: V29sZmdhbmcg
givenName: Wolfie
uid: wam
cn: Johannes Chrysostomus Wolfgangus Theophilus Mozart
cn: Wolfgang Amadeus Mozart
cn: Wolfie Mozart
sn: Mozart
displayName: Mozart

The use of the modern ldapmodify tool correctly notes the error – which is most likely a typographical error – giving the operator an opportunity to consider whether the entry is as intended before it is added:


ldapmodify --hostname localhost --port 1389 \
  --useStartTls --trustAll \
  --bindDn 'cn=directory manager' --bindPassword password \
  -c -f /tmp/mozart.LDIF 
# Error at or near line 1 in LDIF file Console: \
 com.unboundid.directory.server.util.LDIFException: The LDIF record for entry
# 'uid=wam,ou=people,dc=example,dc=com' contains line 'givenName: Wolfgang ' \
 which ends with an illegal trailing space.  The '--stripTrailingSpaces' argument
# may be used to indicate that illegal trailing spaces should be stripped out \
 of records instead of causing them to be rejected

Modifying Existing Entries

Modifying an Existing Attribute

Change Mozart’s displayName. Create the following LDIF using the keyword replace and the changetype designator modify:


dn: uid=wam,ou=people,dc=example,dc=com
changetype: modify
replace: displayName
displayName: Wolfgang Mozart

Modify the entry:


/usr/bin/ldapmodify -x -h localhost -p 1389 \
    -D 'cn=directory manager' -w password \
    -c -f /tmp/modify-mozart-displayname.LDIF 
modifying entry "uid=wam,ou=people,dc=example,dc=com"

Verify the change:


ldapsearch --hostname localhost --port 1389 \
     --useStartTls --trustAll \
    --baseDN uid=wam,ou=people,dc=example,dc=com \
    --searchScope base '(objectClass=*)' displayName
dn: uid=wam,ou=people,dc=example,dc=com
displayName: Wolfgang Mozart

Note that modern directory servers should support the pre-read control, which will result in the attributes being read and presented before they are modified.


ldapmodify --preReadAttributes displayName \
    --hostname localhost --port 1389\
    --useStartTls --trustAll \
    --bindDn 'cn=directory manager' --bindPassword password \
    -c -f /tmp/modify-mozart-displayname.LDIF 
# Processing MODIFY request for uid=wam,ou=people,dc=example,dc=com
# MODIFY operation successful for DN uid=wam,ou=people,dc=example,dc=com
# Target entry before the operation:
# dn: uid=wam,ou=people,dc=example,dc=com
# displayName: Wolfgang Mozart

Changing Multiple Attributes

In this example, change multiple attributes in one connection:


ldapmodify --hostname localhost --port 1389 \
 --useStartTls --trustAll \
 --bindDn uid=user.0,ou=people,dc=example,dc=com
Password for user 'uid=user.0,ou=people,dc=example,dc=com':
dn: uid=user.0,ou=people,dc=example,dc=com
changetype: modify
replace: street
street: 91327-A Madison Avenue
-
replace: homePhone
homePhone: +1 457 878 9948
# Processing MODIFY request for uid=user.0,ou=people,dc=example,dc=com
# MODIFY operation successful for DN uid=user.0,ou=people,dc=example,dc=com

Adding a new Attribute

Add a description attribute to Mozart’s user entry. First create the necessary LDIF, this time using the keyword add and the changetype designator modify. The add keyword is used for attributes, the modify designator is used to indicate that the operator desires to change an attribute in the entry. Simply put, indicate which attribute with add: attributeName, then specify the attribute itself with the value:


dn: uid=wam,ou=people,dc=example,dc=com
changetype: modify
add: description
description: This is W. Mozart's user entry.

Modify the entry:


#
# The legacy ldapmodify syntax
#
/usr/bin/ldapmodify -x -h localhost -p 1389 \
    -D 'cn=directory manager' -w password -c -f /tmp/modify-mozart.LDIF
modifying entry "uid=wam,ou=people,dc=example,dc=com"


#
# The more modern syntax  - in this case promoting the non-secure 
# connection to a secure connection with StartTLS
#
ldapmodify --hostname localhost --port 1389 \
    --useStartTls --trustAll \
    --bindDn 'cn=directory manager' --bindPassword password \
    -c -f /tmp/modify-mozart.LDIF 
# Processing MODIFY request for uid=wam,ou=people,dc=example,dc=com
# MODIFY operation successful for DN uid=wam,ou=people,dc=example,dc=com

Once an multi-valued attribute (in this case. description) has a value, that same value cannot be added to the entry, in other words, multi-valued attributes may not have duplicated values. Here is the result of trying to add a description attribute with the same value as one that already exists:


/usr/bin/ldapmodify -x -h localhost -p 1389 \
     -D 'cn=directory manager' -w password \
    -c -f /tmp/modify-mozart.LDIF
modifying entry "uid=wam,ou=people,dc=example,dc=com"
ldap_modify: Type or value exists (20)
	additional info: Entry uid=wam,ou=people,dc=example,dc=com cannot be modified \
 because it would have resulted in one or more duplicate values for attribute \
 description:  This is W. Mozart's user entry.

Not all attributes are multi-valued, but description is. Interestingly, the userPassword is multi-valued, though modern professional-quality directory servers can force users to have only one userPassword attribute value. PAM can also use the first password.

Deleting an Existing Attribute

To delete an attribute from an entry, use the keyword delete and the changetype designator modify. If the attribute is multi-valued, the client must specify the value of the attribute to delete. Create the following LDIF:


dn: uid=wam,ou=people,dc=example,dc=com
changetype: modify
delete: description
description: This is W. Mozart's user entry.

Remove the value:


#
# The older ldapmodify syntax
#
/usr/bin/ldapmodify -x -h localhost -p 1389 \
    -D 'cn=directory manager' -w password \
    -c -f /tmp/remove-description-mozart.LDIF 
modifying entry "uid=wam,ou=people,dc=example,dc=com"
#
# The more modern syntax (in this case also using the Start TLS extended operation 
# for added security):
#
ldapmodify --hostname localhost --port 1389 \
    --useStartTls --trustAll \
    --bindDn 'cn=directory manager' --bindPassword password \
    -c -f /tmp/remove-description-mozart.LDIF 
# Processing MODIFY request for uid=wam,ou=people,dc=example,dc=com
# MODIFY operation successful for DN uid=wam,ou=people,dc=example,dc=com

r

Verify that the description attribute with the designated value has been deleted:


#
# The legacy ldapsearch syntax
#
/usr/bin/ldapsearch -LLL -x -h localhost -p 1389 \
     -b uid=wam,ou=people,dc=example,dc=com \
     -s base '(objectClass=*)'
dn: uid=wam,ou=people,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
initials: wam
givenName: Wolfgang
givenName: Wolfie
uid: wam
cn: Johannes Chrysostomus Wolfgangus Theophilus Mozart
cn: Wolfgang Amadeus Mozart
cn: Wolfie Mozart
sn: Mozart
displayName: Mozart


#
# The more modern syntax
#
ldapsearch --hostname localhost --port 1389 \
    --useStartTls --trustAll \
    --baseDN uid=wam,ou=people,dc=example,dc=com \
    --searchScope base '(objectClass=*)'
dn: uid=wam,ou=people,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
initials: wam
givenName: Wolfgang
givenName: Wolfie
uid: wam
cn: Johannes Chrysostomus Wolfgangus Theophilus Mozart
cn: Wolfgang Amadeus Mozart
cn: Wolfie Mozart
sn: Mozart
displayName: Mozart

Deleting an Existing Entry

An entry is deleted by specifying the keyword delete for the changetype operation designator in the LDIF:


dn: uid=wam,ou=people,dc=example,dc=com
changetype: delete

Delete the entry:


#
# The legacy ldapmodify syntax
#
/usr/bin/ldapmodify -x -h localhost -p 1389 \
     -D 'cn=directory manager' -w password -c -f /tmp/delete-mozart.LDIF 
deleting entry "uid=wam,ou=people,dc=example,dc=com"
#
# The more modern syntax (in this case also using the Start TLS extended operation 
# for added security and the pre-read control to show the value of 
# the entry's attributes before deletion):
#
ldapmodify --preReadAttributes '*' \
    --hostname localhost --port 1389 \
    --useStartTls --trustAll \
    --bindDn 'cn=directory manager' --bindPassword password \
    -c -f /tmp/delete-mozart.LDIF 
# Processing DELETE request for uid=wam,ou=people,dc=example,dc=com
# DELETE operation successful for DN uid=wam,ou=people,dc=example,dc=com
# Target entry before the operation:
# dn: uid=wam,ou=people,dc=example,dc=com
# objectClass: top
# objectClass: person
# objectClass: organizationalPerson
# objectClass: inetOrgPerson
# initials: wam
# givenName: Wolfgang
# givenName: Wolfie
# uid: wam
# cn: Johannes Chrysostomus Wolfgangus Theophilus Mozart
# cn: Wolfgang Amadeus Mozart
# cn: Wolfie Mozart
# sn: Mozart
# userPassword: {SSHA}bJdy+0w4YTkWD9wNLnZVqhved5r4QPa33l3X/Q==
# displayName: Mozart

The use of the --preReadAttributes in the more modern syntax examples should not be construed to mean that the pre-read control is not supported by the older ldapmodify client: the command-line option to include the pre-read control with the older tool is -e preread=attribute-list. For example, the following sequence replaces a password and includes the pre-read control:


/usr/bin/ldapmodify -x -h localhost -p 1389 \
 -D 'cn=directory manager' -W \
 -e preread=userPassword
Enter LDAP Password: 
dn: uid=user.1,ou=people,dc=example,dc=com
changetype: modify
replace: userPassword
userPassword: a-new-password 
modifying entry "uid=user.1,ou=people,dc=example,dc=com"
control: 1.3.6.1.1.13.1 false ZGwEJnVpZD11c2VyLjEsb3U9UGVvcGxlLGRjPWV4YW1wbGUs
 ZGM9Y29tMEIwQAQMdXNlclBhc3N3b3JkMTAELntTU0hBfS9Vd1ZlSzh0cDlVaXhRVUxwTjZnR2Qrd
 HJ3dHNkRTVhb2FXTHZBPT0=
# ==> preread
dn: uid=user.1,ou=People,dc=example,dc=com
userPassword:: e1NTSEF9L1V3VmVLOHRwOVVpeFFVTHBONmdHZCt0cnd0c2RFNWFvYVdMdkE9PQ=
 =
# <== preread

References

  1. RFC 4511 LDAP: the protocol
  2. RFC 4512 LDAP: directory information models
  3. The root DSE
  4. Base 64
  5. LDAP: Programming Practices

Notes

About Terry Gardner

Terry Gardner was a leading directory services architect with experience with many large scale directory services installations and messaging server installations, and was a Subject Matter Expert in the field of Directory Services and Solaris (operating system) performance. Mr. Gardner also participated in the open-source software community. Mr. Gardner passed away in December, 2013.
This entry was posted in computing, LDAP and tagged , , , , , , . Bookmark the permalink.

2 Responses to LDAP: Using ldapmodify

  1. Pingback: LDAP: The Subtree Delete Control « Diaries, Triumphs, Failures, and Rants

  2. Pingback: LDAP: Adminstrative users | Diaries, Triumphs, Failures, and Rants

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s