Chapter 6. LDAP Schemas

The focus of this chapter will be LDAP Schemas. Schemas are the standard way of describing the structure of objects that may be stored inside the directory. The first few sections are designed to provide foundational knowledge of what schemas do and how they work—a foundation necessary for our work, later in this chapter, using and implementing schemas. But we will continue on from there to a number of more practical topics, including adding pre-defined schemas and defining our own custom schemas.

We will begin with a general examination of schemas. From there, we will look at schema hierarchies. Like the directory information tree itself, schemas are organized into hierarchies. Next, we will examine some of the basic schemas that are included with OpenLDAP. We will also look at two overlays that require their own schemas. Finally, we will create a custom schema consisting of a pair of new object classes, each with new attributes. The main topics we will discuss in this chapter are:

  • The basics of schema definitions
  • The three types of object classes
  • Using different schemas in OpenLDAP
  • Configuring the Accesslog and Password Policy Overlays
  • Obtaining and using an Object Identifier (OID)
  • Creating new schemas by hand

We have already looked at a variety of attributes and object classes used in OpenLDAP. For example, we created entries for our users using the person, organizationalPerson, and inetOrgPerson object classes and, in so doing, we used attributes like cn, sn, uid, mail, and userPassword. We also created groups using the groupOfNames and groupOfUniqueNames object classes, paying special attention to the member and uniqueMember attributes. We even looked briefly (in Chapter 3) at object classes and attributes for describing documents and collections of documents (document and documentCollection respectively).

Each of these object classes and attributes has a strict definition. The definitions of attributes and object classes are bundled together into larger collections called schemas. OpenLDAP applications use these schemas to determine how records should be structured and where (in the hierarchical structure) each entry can be located.

LDAP schemas have a bad reputation. They are viewed as complex, arcane, hyper-technical, and difficult to implement. The goal of this chapter is to overcome this perception.

It is understandable why this reputation persists though. I think there are a few aspects of LDAP schemas that are daunting to the neophyte.

First, LDAP schemas are based on generations of technical specifications coming out of the complex X.500 system. Because of this heritage, LDAP schemas make frequent use of equipment that is not particularly human-friendly, such as object identifier numbers that look like this: 1.3.6.1.4.1.1466.115.121.1.25. However, a little bit of background knowledge can overcome this hurdle.

Second, the LDAP schema definition language is notably different from the sorts of definition languages (DDL) familiar to SQL developers. This is largely due to the different nature of the backend database. LDAP is not inherently tabular as relational databases are, while it does make frequent use of concepts like inheritance (a rarity in SQL DDL languages, though some do support the idea). Finally, while SQL DDL takes the form of a SQL command, LDAP schema definitions are purely descriptive.

But the LDAP schema language is actually quite compact and typically only two directives (attributetype and objectclasstype), each with a handful of arguments, are needed in order to create custom schemas. For this reason, the learning curve is short, and by the end of this chapter you should be able to comfortably create your own schemas.

Typically schemas are written in plain-text files and stored in a subdirectory of the OpenLDAP configuration folder. In Ubuntu these files are located at /etc/ldap/schema. If you built from source the schema files are located by default at /usr/local/etc/openldap/schema.

SLAPD does not automatically use all of the schemas in the schema directory. When SLAPD starts up, it loads only the schemas specified in the slapd.conf file.

Usually schemas are included using the include directive. In Chapter 2 we included three schema files in our slapd.conf file. The include section, near the top of the file, looks like this:

The first line imports the core schema, which contains the schemas of attributes and object classes necessary for standard LDAP use. The second imports a number of commonly used object classes and attributes, including those used for storing document information and DNS records. The inetorgperson.schema file includes the inetOrgPerson object class definition and its associated attribute definitions.

In the coming sections we will look at the format of these files, implementing some existing schemas, and finally creating our own schema.

LDAP schemas are used to formally define attributes, object classes, and various rules for structuring the directory information tree. The term schema refers to a collection of (conceptually related) schema definitions. The inetOrgPerson schema, for example, contains the definition of the inetOrgPerson object class, as well as all of the extra (non-core) attributes that are allowed or required by the inetOrgPerson object class.

A schema definition is a special type of directive that provides information about how a particular entity in SLAPD is to be structured. There are four different types of schema definition that can be included in slapd.conf (or an included schema definition):

In addition to these four, there are other schema definitions that are not typically placed in a schema. Instead, most of these are generated by OpenLDAP code. Here is a brief description of what each does (for more information see RFC 4512, which defines the LDAP schema language):

SLAPD builds this part of the schema in code. For example, matching rule uses are generated based on what matching rules exist and what attributes implement those matching rules. Like the rest of the schema, matching rules, LDAP syntaxes, structure rules, and name forms can all be accessed over the LDAP protocol. See the Retrieving Schemas from SLAPD section for more information.

For the time being though, we will focus primarily on the four schema definitions that can be included in the slapd.conf file. In particular, we will focus on creating new object classes and attributes.

There are two different types of schema definition that we need in order to extend the types of information that our directory server will store:

We will look at each of these in turn. To start, let's take another look at one of the schemas introduced in Chapter 3. Here is a graphical representation of the person object class:

Object Classes and Attributes

The person object class has two required attributes (cn and sn) and four more attributes that are allowed, but not required: userPassword, telephoneNumber, seeAlso, and description.

A new record that is of object class person (and has no other object classes) might look like this:

This record contains all, and only, the attributes in the person object class. Attempting to add a different attribute type not mentioned in the schema would lead to an error. Similarly, trying to remove all values for the cn or sn attributes would also lead to an error since their presence is required.

But how does OpenLDAP know which attributes are required and which are allowed? This information is stored in the schema definition for the person object class.

The schema definition is stored in the core.schema (and also in core.ldif) file at /etc/ldap/schema (or /usr/local/etc/openldap/schema if you compiled from source). Have a look at this:

This is a simple object class definition. It begins with a descriptor, objectclass, which tells the schema interpreter what type of definition is being made. The rest of the definition is enclosed in parentheses. Extra whitespace characters, including line breaks, are generally ignored (unless enclosed in a quoted string), but remember that

since objectclass is a directive in the slapd.conf file format, every line other than the first must start with a whitespace character.

The first field within the definition is the numeric identifier for the object class: 2.5.6.6. This unique identifier is called an Object IDentifier (OID). Every schema definition has a unique OID that distinguishes that definition from any other definition in the world. Because this OID is supposed to be globally unique there is an official procedure for giving a definition a unique identifier. This will be described later in the chapter. For now it is sufficient to note that these OIDs must be universally unique.

Any LDAP application may refer to a definition by its OID. Object classes, attributes, matching rules, and many other LDAP entities have OIDs.

The second field in the definition is the NAME field. While an OID is easily used by a computer, it is not so easily used by humans. So, in addition to an OID, server-unique names (in character strings) may be specified. The above object class only has one name: person. Multiple names can be given to a single object class, but usually a single one will suffice.

In a schema definition, the string names should always be enclosed in single quotation marks. In a list of string values, each value must be enclosed in single quotes and the entire list must be enclosed in parentheses. For example, if the person definition specified two names, person and humanBeing, the NAME field would look like this:

NAME ( 'person' 'humanBeing' )

Note also that spaces are not allowed in the values of the NAME field, so 'human being' would be an illegal name.

Most of the time object classes and attributes are referred to by the values in the NAME field rather than by OID.

The DESC field is a brief description of what this schema definition is to be used for. In this case the description field refers to an RFC (RFC 4519) that gives a detailed explanation of the object class. Of course it is not necessary to create an RFC to formally define your schemas, though if you plan on distributing the schema widely writing an RFC is a good idea.

The next field, SUP, which is short for 'superior,' indicates what the parent object class of this object class is. The parent of the person object class is the object class called top. Object classes, like directory information trees, are organized in hierarchies. The top object class is at the top of the object class hierarchy. The STRUCTURAL keyword also pertains to how this schema definition fits into the schema hierarchy. We will discuss schema hierarchies in the next part.

The last two fields are less mysterious. They define which attributes a person object must (MUST) contain, and which attributes an object may (MAY) contain.

The syntax for the MUST and MAY fields is straightforward. Each description takes a list of attributes:

The list of attribute values (designated either by OID or by an attribute name) is enclosed in parentheses. Values are separated with the dollar sign ($). The example above indicates that the four values, userPassword, telephoneNumber, seeAlso, and description, are all attributes that a person object is allowed to have.

An attribute should be specified in only one of the two lists. There is no need to put an attribute in both a MAY and a MUST list.

Of course, the names can be replaced with OIDs instead. Thus, the following two lines are equivalent:

and

The OID for the cn attribute is 2.5.4.3, and either identifier will work.

There are a few fields that may be present in an object class definition but which are not present in the previous code. The first is the OBSOLETE keyword, which appears after the DESC field. This is used to designate an object class as obsolete but still (temporarily) supported.

The second is the extensions section, which is used for providing implementation-specific extensions to a schema. At the end of the schema one or more extensions may be specified. An extension is a keyword followed by a list enclosed in parentheses. By default, none of the schemas included in OpenLDAP's schema/ directory have any extensions.

In summary then, an object class definition begins with the objectclasstype directive, and can contain the following fields:

Object class definitions are an important part of schemas and we will look back at these concepts several times in this chapter. After covering other definition types, we will take a detailed look at the object class hierarchy. As we do that the role of the SUP line will become clearer.

Further on we will look at some specific object classes and we will also write our own custom object class. But before we move on to those things we will look at the other schema definitions. Next, we will look at attribute definitions.

The person object class that we examined now can have six different attributes—the two necessary sn and cn attributes, and the optional userPassword, telephoneNumber, seeAlso, and description attributes. Just as the object class was defined in the schema, so each attribute is also defined. The syntax for attribute definitions is similar though the fields allowed in the definition are different and more numerous.

The schema definition for the telephoneNumber attribute is a good example of a basic attribute definition:

The attribute definition begins with an attributetype directive. The rest of the definition is enclosed in parentheses.

The first field in the definition is the unique OID for this attribute. As with all OIDs, this identifier must be globally unique. The OID 2.5.4.20 should only be used to refer to a telephoneNumber attribute. Later in this chapter, in the section Getting an OID, we will discuss getting and using a base OID.

After the OID comes the NAME field that associates one or more names with the attribute.

It is not uncommon for attributes to have two names—a long name (such as commonName or surname) and an abbreviated name (cn or sn respectively). When an attribute has multiple names, the list of names should be enclosed in parentheses. As an example, consider the NAME field for the fax attribute:

Note the syntax of the highlighted line. Each name in the list of names is enclosed in single quotes (') and the entire list is enclosed in parentheses.

The DESC field provides a brief description of the purpose of the attribute. In the telephoneNumber attribute definition, the value of this field is 'RFC2256: Telephone Number', indicating that the attribute is defined in RFC 2256.

One important aspect of defining an attribute is specifying how an application should test two attribute values to see if they match. Do TEST and test match? In some cases we might want them to, while in others we might not. Does t*st match test? Again, in some cases, this is desirable while in others it is not.

We can determine, in the attribute definition, which matching rules should be used to test whether one value matches another. When we discussed the search operation in Chapter 3 we saw four different comparison operators that could be used in search filters:

In addition to these we looked at using regular expression characters, such as the asterisk (*), to match portions, or substrings, of an attribute value. The behavior of each of these is determined, to a large degree, by the matching rules in the attribute definition.

When the LDAP server processes a comparison (during operations like binding, comparing, and searching) it uses the schema to determine how to handle these comparisons. The schema specifies which matching rules should be used. There are three different sorts of matching rules that can be assigned in a schema:

An attribute schema may specify rules for one, two, or all three of these. The value of each can be either the OID or the name of a matching rule. In the telephoneNumber schema, EQUALITY and SUBSTRING are used:

When an equality test for a telephone number is requested, such as the evaluation of the filter (telephoneNumber=+1 234 567 8901), the telephoneNumberMatch rule is used. Note that the plus sign (+) is part of the telephone number, not part of the operator. If the filter includes a wild-card match, such as (telephoneNumber=+1 234 567*), then the telephoneNumberSubstringsMatch rule is used instead.

How do these two matching rules perform? Let's look at an example. When we defined the user with UID matt, we assigned that user a telephone number. Here, we will search for that entry, requesting only the telephoneNumber attribute:

And the search result is as follows:

The telephoneNumber attribute has the value +1 555 555 4321. Now let's perform a search using the telephone number:

And the search result is as follows:

As expected a search using the exact phone number returned a result. This looks no different from what we would expect a string matching rule to do. Using the special telephoneNumberMatch rule in the schema has some advantages though. When using this matching rule, SLAPD will ignore certain telephone number formatting characters. Here's an example using a substring search:

Here is the result:

The filter in this example uses dashes (-) where the previous filter used spaces. Using the telphoneNumberSubstringMatch rule, SLAPD ignored the dashes. With the telephoneNumberMatch and telephoneNumberSubstringMatch rules, the numbers +15555554321, +1 555 555 4321, 1-5-5-55554-3-2-1, and +1 555-555-4321 are all treated as identical matches.

This illustrates the virtue of being able to specify matching rules in the schema. For attributes such as cn, sn, or mail (email address), we certainly wouldn't want dashes to be treated the same as white space characters. We wouldn't want Dan Forth to match Danforth. But it is certainly a desirable feature when matching phone numbers. The LDAP answer to this problem is to assign matching rules fitting to the type of information stored in the attribute.

The last field in the telephoneNumber matching scheme is the SYNTAX field. This relates to the type and structure of the data stored in values for telephoneNumber attributes.

The value of the SYNTAX parameter has two parts. The first is the OID (or the name) of the LDAP syntax, and the second, set off in curly braces ({ and }) is the maximum length (usually the number of characters) for the field. The length specifier is optional, and the server is not obligated to enforce the maximum length.

The OID mentioned earlier, 1.3.6.1.4.1.1466.115.121.1.50, is the telephone number syntax. This indicates that attribute values for instances of the telephoneNumber attribute are to contain the characters (integers, dashes, spaces, and so on) that a phone number would require. SLAPD will reject attempts to add phone numbers that contain letters and other special characters. Later in this chapter, in the Creating a Schema part, we will look at the list of common LDAP syntaxes that OpenLDAP supports.

As far as complexity goes the telephoneNumber attribute is about average. However, many attribute definitions are much shorter, taking advantage of the fields set in similar attributes. Thus, there are many attributes that, because they inherit most of their features from their superior (parent) attribute, have only an OID, a NAME field, and a DESC field. The schema definition for the ever popular cn attribute looks like this:

attributetype 
  ( 
   2.5.4.3
   NAME ( 'cn' 'commonName' )
   DESC 'RFC2256: common name(s) for which the entity is known by'
   SUP name 
  )

In this case, the SUP name field indicates that the name attribute is the parent of the cn attribute. Attributes, like object classes, can be organized hierarchically. A superior attribute is the parent or prototype for this attribute and certain properties, if left unspecified in the schema definition, are inherited from the superior. Syntax and matching rules, for example, can be inherited from a parent.

In the previous example no matching rules and no LDAP syntax were specified. Therefore, the cn attribute type inherits these values from its superior. The name attribute uses the caseIgnoreMatch EQUALITY matching rule and the caseIgnoreSubstringMatch SUBSTR rule, and uses the Directory String LDAP syntax (1.3.6.1.4.1.1466.115.121.1.15). A directory string is a UTF-8 encoded string intended to store text.

There are a handful of other fields that the previous examples do not make use of. These are OBSOLETE, SINGLE-VALUE, COLLECTIVE, NO-USER-MODIFICATION, USAGE, and the extension area. Let's briefly look at those.

The OBSOLETE flag, which usually appears after the DESC field, plays the same role in attribute definitions as it does in object class definitions. It labels an attribute obsolete. While obsolete attributes are still supported and can be used for records in the directory information tree, they are to be treated as deprecated, subject to removal in future versions of the schema or software. OBSOLETE takes no parameters.

The SINGLE-VALUE flag indicates that the defined attribute can only have one attribute value. Typically, an attribute can have an arbitrary number of values. But any attribute whose schema includes the SINGLE-VALUE flag can have no more than one. The domain component (dc) attribute that we looked at in Chapters 3 and 4 is an example of this. An object that has a dc attribute can only assign one value to that attribute. SINGLE-VALUE takes no parameters.

The COLLECTIVE flag indicates that this attribute is a collective attribute. Entries can be grouped, with collective attributes, into an entry collection.

Collectives are implemented in OpenLDAP via the collect overlay, which is not compiled or installed by default, though it can be found in the servers/slapd/overlays directory of the source code. The schemas necessary for collective support are also not included by default in the OpenLDAP distribution, and must be copied from another source (such as RFC 3671).

Here's a rough idea of how entry collections work:

  1. One record is the collection record, and must use the collectiveAttributeSubentry object class. This becomes the authority for that collective attribute. All other subordinate records then inherit the attribute (and its value) and the attribute is visible (though read-only) as an attribute of each of these records. For more information on collectives see RFC 3671 (http://www.ietf.org/rfc/rfc3671.txt).
  2. The NO-USER-MODIFICATION flag is used to indicate that the attribute is an operational attribute (used by SLAPD or an overlay), and cannot be modified by an LDAP client. This is not usually used in user-defined schemas. Use it only when writing a custom overlay that will make use of its own operational attributes.
  3. The USAGE field provides SLAPD with information about what will use the attribute. There are four possible values. The first three, directoryOperation, distributedOperation, and dSAOperation, all indicate that SLAPD itself uses the attribute. The last, userApplication, is the default and it indicates that the attribute is primarily intended to be used by client applications. Since most schemas are intended for client application use, the default is usually what is desired and the USAGE field is rarely used.
  4. Finally, attributetype definitions can also use extensions, though there are no extensions used in the main schemas included in OpenLDAP. The syntax for extensions is the same for attribute types as it is for object class definitions.

In summary, an attribute schema definition begins with the attributetype directive which is followed by a schema definition enclosed in parentheses. The following fields are allowed in attribute definitions:

At this point we have looked at both object class definitions and attribute definitions. When creating your own custom schemas it is most likely that these are the only two types of schema definitions that you will need to use.

We have discussed the basics of schemas and seen a few examples in the text. Later in this chapter we will look at some other specific examples. But if you want to take a look at more examples of attribute and object class schemas peruse the files in the schema directory for OpenLDAP (/etc/ldap/schema or /usr/local/etc/openldap/schema). The best place to start is with the core.schema schema, which defines the standard LDAPv3 schemas.

The cosine.schema file contains many other commonly used schemas and is also a good place to look. The inetOrgPerson.schema schema is a good example of what a user-defined schema file ought to look like. Or, for a shorter example of a user-defined schema, see openldap.schema.

While attributetype and objectclass are the two primary directives used in schema creation there are a few others which we will cover, albeit more briefly, in the next two sections.

The object identifier directive (objectidentifier) is an extension to the standard definition language. While it doesn't provide additional functionality to the schema language, it serves as a time-saving (and human-friendly) utility.

The objectidentifier directive is used to assign a string alias to an OID. When SLAPD processes OID fields for attributetype, objectclasstype, and ditcontentrule directives, if it encounters a string instead of an OID, it will check to see if this string is an alias to an OID and, if so, it will use the value of the OID. The telephoneNumber schema we examined in the last section provides a good example:

attributetype 
  ( 
   2.5.4.20
   NAME 'telephoneNumber'
   DESC 'RFC2256: Telephone Number'
   EQUALITY telephoneNumberMatch
   SUBSTR telephoneNumberSubstringsMatch
   SYNTAX 1.3.6.1.4.1.1466.115.121.1.50{32} 
  )

Instead of using the OID for the telephone number equality and substring matching rules (1.3.6.1.4.1.1466.115.121.1.50 and 1.3.6.1.4.1.1466.115.121.1.58, respectively), the schema refers to the names of the matching rules: telephoneNumberMatch and telephoneNumberSubstringMatch. This later form is much easier for humans to read.

The objectidentifier directive makes it easy to define such aliases for OID numbers, in whole or in part. Here is a simple example of assigning a name to an OID:

objectidentifier exampleComDemo 1.3.6.1.4.1.8254.1021.3.1

Using a directive like this at the top of a schema makes it possible to refer to the OID using the name exampleComDemo later.

For example, we could create a schema like this:

Note that instead of using the OID number for the object, we used the exampleComDemo alias. But, generally, we would not assign one alias per object class. It would be more convenient to alias a common root OID and then append just the last part of the OID number. For example:

In this example we used the objectidentifier directive to create an alias for the OID base that will be used for all of my object class definitions. Thus, when SLAPD encounters the name exampleComOC, it will expand it to 1.3.6.1.4.1.8254.1021.1. The object class definition for myPersonObjectClass should have the OID 1.3.6.1.4.1.8254.1021.1.1 (note the extra .1 at the end). Rather than writing out the entire number we use the exampleComOC alias and append a colon (:) and then the numeric suffix for the object class.

When SLAPD encounters exampleComOC:1 it will expand it to 1.3.6.1.4.1.8254.1021.1.1. Likewise, if I were to create a second object class with the desired OID 1.3.6.1.4.1.8254.1021.1.2, I could use exampleComOC:2 instead of typing out the entire long OID.

For more examples of the objectidentifier directive, see openldap.schema in the schema directory for OpenLDAP.

The last schema directive we will look at is the ditcontentrule directive which is used for creating DIT Content Rules.

A DIT content rule identifies a particular structural object class, and indicates which auxiliary object classes are allowed (or not allowed) to be included in entries that use that object class.

For an example, let's use a few of the object classes introduced in Chapter 3. In the Anatomy of an LDIF File section we created an entry representing a document. It implemented the document object class, whose schema (located in cosine.schema) looks like this:

This is a structural object class. Also in Chapter 3, in the Adding System Records section we added the entry for uid=authenticate,ou=System,dc=example,dc=com. This entry implemented the simpleSecurityObject object class. Here is the schema for simpleSecurityObject:

objectclass 
  ( 
   0.9.2342.19200300.100.4.19 
   NAME 'simpleSecurityObject'
   DESC 'RFC1274: simple security object'
   SUP top 
   AUXILIARY
   MUST userPassword 
  )

This object class is an auxiliary object class, meaning that it can be added to entries that already have a structural object class, the result being that the attributes of the auxiliary object class are now available for that entry.

According to default OpenLDAP settings, if we had an entry with the document structural object class, we could give this document a password (for binding to the directory) by adding objectclass: simpleSecurityObject to the record, and then adding a userPassword attribute. This would give us a record looking something like this:

This entry is essentially a document that has the ability to log in! A client that used this record's DN and the correct password could log in as this document.

Perhaps there are cases where this is desirable, but for the sake of this example, let us suppose that this is a configuration that we do not want to allow.

Normally, decisions about which entries have which object classes are left to external applications. But what if we wanted to make sure that no application could give document a userPassword attribute?

The best method for solving this problem is to create a DIT content rule that disallows adding the userPassword attribute to any entry that has the document object class. This is done with the ditcontentrule directive:

The form of the ditcontentrule directive should be familiar by now. Like the objectclass and attributetype directives, this directive encloses the DIT content rule definition inside of parentheses.

The first field is an OID. But unlike the other schema definitions, this OID is not the OID for this definition. Instead, it is the OID of the structural object class that we are targeting.

In this case, the OID 0.9.2342.19200300.100.4.6 is the OID for the document object class. You can verify this with a glance at the document schema listed a few pages back or by browsing the cosine schema.

The NAME field should contain a unique name used for referencing this rule. For the most part, the value of this field is used in reporting references to this rule in the log file, and in responses to the client.

The DESC field contains a short-text description of what the rule does.

The NOT field contains a list of OIDs or names of attributes that should be disallowed. The name userPassword comes from the NAME field in the userPassword attribute definition.

With this content rule in place, what will happen if we try to add a userPassword attribute to a document? Here is an example using ldapmodify:

The highlighted portion in this example is the attempted modification. We attempted to add the simpleSecurityObject object class and the userPassword attribute to the record. But the server responded with an Object class violation error, giving the following reason:

Our custom DIT content rule did its job—it prevented the addition of the userPassword attribute to the document entry.

This DIT content rule we created above is a negative rule—it defines what attributes an entry cannot have. But ditcontentrule can also be used to create positive rules: rules that specify which attributes (or auxiliary object classes) are allowed.

For example, we could write a rule that says that every entry that is an inetOrgPerson must have a userPassword:

The OID used in this rule is the OID for the inetOrgPerson object class. The MUST field indicates that any entry with the structural object class inetOrgPerson must also have the userPassword attribute set.

Because of this rule, an attempt to add a new inetOrgPerson entry without a userPassword would result in an error similar to the one we looked at earlier:

The record being added (highlighted) is a valid inetOrgPerson entry, according to the inetOrgPerson object class definition. But, because of the DIT content rule, adding the record failed because there is no userPassword attribute value specified.

Now let's expand this rule to take advantage of the AUX field. The AUX field may be used to explicitly state which auxiliary classes can be combined with this structural object class.

In our newly revised DIT content rule we will make it so that only the pkiUser and the labeledURIObject auxiliary object classes can be added to an inetOrgUser record.

The labeledURIObject object class allows an additional attribute, labeledURI, which takes a URI (such as a URL) and a plain text description:

labeledURI: http://aleph-null.tv Home Page

The URI is separated from the label by a white space. So the URI is http://aleph-null.tv and the label is Home Page. The labeledURIObject is defined in RFC 2079 (http://www.ietf.org/rfc/rfc2079.txt).

Also, we will change the NAME and DESC elements to reflect the fact that our rule now does more than just require a userPassword. The DIT content rule now looks like this:

ditcontentrule 
  (
   2.16.840.1.113730.3.2.2
   NAME 'inetOrgPersonRules'
   DESC 'Restrictions for entries with inetOrgPerson object class'
   MUST userPassword
   AUX ( labeledURIObject $ pkiUser )
  )

Note the syntax of the AUX field. To list multiple values in a field it is necessary to enclose the list of values, separated by a dollar sign ($), inside of parentheses.

Using this DIT content rule, we can successfully add a URL (using the labeledURIObject auxiliary object class) to my record:

$ ldapmodify -U matt
SASL/DIGEST-MD5 authentication started
Please enter your password: 
SASL username: matt
SASL SSF: 128
SASL installing layers

dn: uid=matt,ou=users,dc=example,dc=com
changetype: modify
add: objectclass
objectclass: labeledURIObject
-
add: labeledURI
labeledURI: http://aleph-null.tv Home Page

modifying entry "uid=matt,ou=users,dc=example,dc=com"

The entry, highlighted above, was added successfully because the labeledURIObject (which allows the labeledURI attribute) is allowed by the content rule. But if I try to add a different auxiliary object class—one not explicitly allowed in the DIT content rule – the change request will be denied:

$ ldapmodify -U matt
SASL/DIGEST-MD5 authentication started
Please enter your password: 
SASL username: matt
SASL SSF: 128
SASL installing layers

dn: uid=matt,ou=users,dc=example,dc=com
changetype: modify
add: objectclass
objectclass: userSecurityInformation

modifying entry "uid=matt,ou=users,dc=example,dc=com"
ldap_modify: Object class violation (65)
        additional info: content rule 'inetOrgPersonRules' does not 
        allow class 'userSecurityInformation'

The DIT content rule prevented the addition of an auxiliary object class because this class is not specified in the AUX field of the rule.

Like the other definitions, the ditcontentrule directive also allows the OBSOLETE flag.

In summary, the ditcontentrule directive takes a definition of a DIT content rule enclosed in parentheses. The following fields are supported:

Now we have looked at the four different schema definition directives allowed in the slapd.conf file (or included files). With this information you should be able to read through and understand any of the schemas defined in OpenLDAP.

Next we will take a quick look at how to get schema information out of a SLAPD server using the LDAP protocol.

When SLAPD loads the schemas, it stores them in a special part of the directory information tree, along with the Root DSE record; a special entry holds schema information. Having this information is useful for debugging, but more importantly it provides a way for client applications to find out about what types of objects and attributes may be stored in this directory server.

Obtaining the information from the directory is as easy as issuing an ldapsearch command.

The schema information is stored in a special record called the subschema subentry. You can access the subschema subentry using ldapsearch:

  $ ldapsearch -U matt -b 'cn=subschema' -s base +

This will retrieve the entire schema specification from the server including not only the attribute and object class definitions, but also definitions of matching rules, matching rule uses, structure rules, name forms, and LDAP syntaxes.

But, as with any other record in an LDAP server, we can use search filters to get just the values of specific attributes. For example, we can find out what all of the existing DIT content rules are:

This search returns all of the DIT content rules currently included in the schema definitions for this server. Of course, the only two there are the ones we created in the last section.

The following schema-related attributes are included in the cn=Subschema record:

Other standard attributes, such as cn, objectclass, and the basic operational attributes, are also part of the record.

Examining schemas this way is an alternative to simply reading the schema files. While it has less documentation (since there are no comments), using filters can be helpful. Also, information not in the standard schemas (such as schema definitions for operational attributes) is also available in this record.

Later in the chapter we will begin implementing schemas in SLAPD, first by including some already written schemas, then by writing our own. But next we will take a quick look at one more theoretical component of schemas: the schema hierarchy.