Paranoid Penguin - Authenticate with LDAP, Part III
Just as schema browsing can be done either manually or with a GUI, so can adding LDAP records. We used the manual method last month to create our root organization entry, and we'll do so again to add our first user record. This method has two steps: first, create a special text file in LDIF format, then use the ldapadd command to import it into the LDAP database. Consider the LDIF file in Listing 1.
Listing 1. LDIF File for a User Record
dn: cn=Wong Fei Hung,dc=wiremonkeys,dc=org cn: Wong Fei Hung sn: Wong givenname: Fei Hung objectclass: person objectclass: top objectclass: inetOrgPerson mail: email@example.com telephonenumber: 651-344-1043 o: Wiremonkeys uid: wongfh
Because they determine everything else, we'll begin by examining Listing 1's objectclass statements: this user has been associated with the object classes top (mandatory for all records), person and inetorgperson. I chose person because it supports the attributes userPassword (which is not set in Listing 1; we'll set Mr. Wong's password shortly) and telephonenumber, which I don't need now but may in the future. The object class inetOrgPerson supports the uid attribute, plus a whole slew of others that also may come in handy later.
One way around having to know and comply with the MUST and MAY restrictions in schema is to add the statement schemacheck off to /etc/openldap/slapd.conf. This allows you to use any attribute defined in any schema file included in slapd.conf without needing to pay any attention to object classes. However, it also adversely affects your LDAP server's interoperability with other LDAP servers and even with other applications (besides flouting LDAP RFCs), so many LDAP experts consider it poor form to disable schema checking in this manner.
It isn't necessary to discuss each and every line in Listing 1; many of the attributes are self-explanatory. In short, know that you don't need to set every attribute you intend to use, but some are mandatory; they are contained in MUST() statements in their respective object class definitions. Each attribute you do define must be specified in the MUST() or MAY() statement of at least one of the object classes defined in the record, and some attributes, such as cn, may be defined multiple times in the same record.
To add the record specified in Listing 1, use the ldapadd command:
$ ldapadd -x \ -D "cn=ldapguy,dc=wiremonkeys,dc=org" \ -W -f ./wong.ldif
This is similar to how we used ldapadd in last month's column. For a complete explanation of this command's syntax, see the ldapadd(1) man page.
If you specified the attributes required by all object classes set in the LDIF file, if all attributes you specified are supported by those object classes and if you provide the correct LDAP bind password when prompted, the record is added to the database. If any of those conditions is false, however, the action fails and ldapadd tells you what went wrong. Thus, you can use trial and error to craft a workable record format. After you've figured this out the first time, you can use the same format for subsequent records, without going through all this schema-induced zaniness.
I offer one caveat: say your LDIF file contains multiple records, which is permitted, if your LDAP server detects an error, it quits parsing the file and does not attempt to add any records below the one that failed. Therefore, you should stick to single-record LDIF files for the first couple of user adds, until you've finalized your record format.
The manual record creation method is a little clunky, but it accommodates a certain amount of tinkering. This is especially useful in the early stages of LDAP database construction.
Once you have a user record or two in place, you can use a GUI tool such as LDAP Browser/Editor (www.iit.edu/~gawojar/ldap) or gq (included in most Linux distributions) to create additional records. In gq, for example, left-clicking on a record pops up a menu containing the option New→Use current entry, which copies the selected record into a new record. This is much faster and simpler than typing everything into an LDIF file manually.
I mentioned in the description of Listing 1 that we generally don't specify user passwords in LDIF files. A separate mechanism is used for that, in the form of the command ldappasswd. By design, its syntax is similar to that of ldapadd:
bind-$ ldappasswd -S -x -D "cn=hostmaster,dc=upstreamsolutions,dc=com" \ -W "cn=Phil Lesh,dc=upstreamsolutions,dc=com"
You don't need to be logged in to a shell session on the LDAP server to use the ldappasswd command. You instead can use the -H option to specify the URL of a remote LDAP server, like this:
$ ldappasswd -S -x \ -H ldaps://ldap.upstreamsolutions.com \ -D "cn=hostmaster,dc=upstreamsolutions,dc=com" \ -W "cn=Phil Lesh,dc=upstreamsolutions,dc=com"