Novell is now a part of Micro Focus

How to Make an Existing LDAP Application Work with NDS

Articles and Tips: article

Michael L. Buck
Software Engineer Consultant
Strategic Partner Engineering
mbuck@novell.com

01 Jun 2000


This article discusses how to make applications written for other LDAP directories work with NDS. It describes differences in schema files and unique characteristics of NDS and includes sample LDIF files showing how to perform directory schema and data modifications.

Many companies have written applications for LDAP directories. As developers migrate these applications to work with NDS, they encounter differences in how to set up their applications to achieve this. Directory vendors have used different file formats for schema definitions, and there are also differences in how these directory vendors have implemented their directories. This article is not intended to be a tutorial of LDAP. Instead, it outlines some of the characteristics of NDS that are different from other directories. It also shows how to work with these differences. Some of these differences utilize features available in NDS that are not available in other directories. Many of the extended capabilities of NDS are provided as extensions to LDAP.

For additional information, see:

Understanding NDS Characteristics

Referential Integrity

The first thing that developers usually run into when they start migrating their LDAP applications to NDS is the capability of NDS to ensure referential integrity. This means that NDS guarantees that any data or schema item that is referenced by another item will exist. For example, if you define a schema class that references certain attributes, NDS will verify that all the referenced attributes exist before it allows the class to be added. So, when defining schema, the attributes need to be defined and added to the schema before the classes. If a class inherits from another class in a schema definition, the class from which the new class is inherited must exist. Classes or attributes cannot be deleted if they are referenced by other schema definitions. Also, the schema item cannot be deleted if data objects of that class exist. These rules apply to data too. Any attribute that is a distinguished name (DN) must reference an existing object. If a referenced object is moved from one location in the directory tree to another, NDS will automatically fix up the DN to reference the object in its new location.

Because other directories do not support referential integrity checking, many schema definitions have been created which break these rules. Schema classes can be created that use non-existent attributes. More commonly, schema definition files are created that define classes that use attributes that are defined later in the file. These schema definitions fail when applied to NDS because of the integrity checking done by NDS. If care is taken to follow these rules as you create schema definitions and add data to the directory, the integrity checking done by NDS can be used to full advantage.

Naming

Naming defines how an object is named within the directory. All objects stored in the directory must have a name. This name can be made up of one or more printable string attributes. Naming attributes are used to reference objects by name rather than requiring domain context or a search to find objects in the directory. All objects stored within NDS must have at least one attribute that is designated as a naming attribute. As schema classes are defined, naming can be specified with an extension to LDIF. All NDS specific extensions to LDIF are defined in a draft submitted to the IETF. See the references below. The following class definition shows how to specify naming attributes:

  • dn: cn=schemachangetype: modifyadd: objectclassesobjectclasses: ( 2.16.840.1.113719.1.131.6.1.23 NAME 'myNewClass' SUP top MUST ( cn $ NewAttrOne )MAY ( NewAttrThree $ NewAttrFour $ NewAttrFive ) X-NDS_NAMING ( `cn' `NewAttrTwo' ) )

If you do not specify naming in a class definition, NDS will parse your class definition and define naming for each string attribute. This will allow your application to behave more like it was using another directory server.

The following shows an example of an auxiliary class definition. Naming can be specified as part of the auxiliary class, but it is not necessary. Naming can be provided by the object to which the auxiliary class is associated when the data object is created. Also note that a superior can be specified for an auxiliary class, but it is not necessary.

  • dn: cn=schemachangetype: modifyadd: objectclassesobjectclasses: ( 2.16.840.1.113719.1.131.6.1.22 NAME 'myObject' AUXILIARY MAY ( NewAttrOne $ NewAttrTwo $ NewAttrThree $ NewAttrFour $ NewAttrFive ) )

Containment

Containment defines where an object can reside within the directory. Objects which contain other objects are called containers. Leaf objects in the directory tree can be, but may not be, container objects. The most commonly used container objects are organization and organationalUnit. Simply put, containment defines the types of containers where an object can be placed. The following shows how to specify containment in a class definition:

  • dn: cn=schemachangetype: modifyadd: objectclassesobjectclasses: ( 2.16.840.1.113719.1.131.6.1.23 NAME 'myNewClass' SUP top MUST ( cn $ NewAttrOne )MAY ( NewAttrThree $ NewAttrFour $ NewAttrFive ) X-NDS_CONTAINMENT ( `organization' `organizationalUnit' `myContainer' ) )

This definition specifies three container types in which objects of class myNewClass can be placed. Note that auxiliary classes cannot specify containment. Objects created using auxiliary classes will inherit containment from the base class. If you do not specify containment for a class, NDS will automatically assign containment to the following container types:

organization organizationalUnit country locality domain

If you are creating a container class in which to place your objects, you must also specify your container class to be a container when you define your object's class. For example, if you define a class called myContainer in which to place objects of the class myObject, you must specify myContainer in a containment statement when you define the myObject class. The following example shows how to define these two classes:

  • dn: cn=schemachangetype: modifyadd: objectclassesobjectclasses: ( 2.16.840.1.113719.1.131.6.1.21 NAME 'myContainer' SUP top MUST ( cn ) MAY ( description ) )

  • dn: cn=schemachangetype: modifyadd: objectclassesobjectclasses: ( 2.16.840.1.113719.1.131.6.1.22 NAME 'myObject' SUP top MUST ( cn $ NewAttrOne )MAY ( NewAttrThree $ NewAttrFour $ NewAttrFive ) X-NDS_CONTAINMENT ( `organization' `organizationalUnit' `myContainer' ) )

In this example, since no containment was specified for the myContainer class, the default containment will be in force. Objects of the class myObject can only be placed in organization containers, organizationalUnit containers, or myContainer containers. Note that the referential integrity rules dictate that you must define the myContainer class first since it is referenced by the myObject class.

By default, any new class defined in NDS is a container. You can designate a class to NOT be a container by specifying an additional extension on the definition. The following example shows the myObject, from the example above, defined in order that it will not be a container.

  • dn: cn=schemachangetype: modifyadd: objectclassesobjectclasses: ( 2.16.840.1.113719.1.131.6.1.22 NAME 'myObject' SUP top MUST ( cn $ NewAttrOne ) MAY ( NewAttrThree $ NewAttrFour $ NewAttrFive ) X-NDS_CONTAINMENT ( `organization' `organizationalUnit' `myContainer' ) X-NDS_NOT_CONTAINER `1' )

Converting Schema Files to LDIF

While directory vendors have closely followed the LDAP standards as they have implemented their directories, most all of these vendors have created their own proprietary format for defining schema extensions. Even Novell, since its directory has been around since before LDAP, has its own file format for schema extensions. These require a separate utility to read the file and extend the schema. Netscape has two files used to extend the schema. One is for defining attributes, and the other is for defining classes. These files are usually called slapd.user_at.conf for attribute definitions and slapd.user_oc.conf for class definitions.

Attributes are defined in the following format:


attribute myNewAttrOneattribute myNewAttrTwoattribute myNewAttrThreeattribute myNewAttrFourattribute myNewAttrFiveattribute myNewAttrSix

myNewAttrOne-oidmyNewAttrTwo-oidmyNewAttrThree-oidmyNewAttrFour-oidmyNewAttrFive-oidmyNewAttrSix-oid

dnciscesinttelbin

A class is defined as follows:

  • objectclass myNewClass oid myNewClass-oid superior top requires myNewAttrOne, myNewAttrTwo allows myNewAttrThree, myNewAttrFour, myNewAttrFive, myNewAttrSix

These files can be converted to the standard LDIF format (see RFC 2252) using the Schema Migration Utility (SCHMIG) available on Novell's NDK. This utility takes the files, as specified above, as input and converts them into a standard LDIF format. To employ the utility, use the following command line:

schmig slapd_at.con slapd_oc.con outputfile

The program is DOS based and does not support long file names, so the files need to be renamed to the old 8.3 DOS format file names. When the program runs, two input files are converted into the output file, and the following message is displayed:

6 Attributes Processed. 1 Classes Processed.

More information about how to use this utility is provided in a user's guide that can be downloaded with the utility. The resulting output file looks like this:

  • dn: cn=schemachangetype: modifyadd: attributetypesattributetypes: ( 0.0.0 NAME 'myNewAttrOne' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )attributetypes: ( 0.0.0 NAME 'myNewAttrTwo' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )attributetypes: ( 0.0.0 NAME 'myNewAttrThree' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )attributetypes: ( 0.0.0 NAME 'myNewAttrFour' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )attributetypes: ( 0.0.0 NAME 'myNewAttrFive' SYNTAX 1.3.6.1.4.1.1466.115.121.1.50 )attributetypes: ( 0.0.0 NAME 'myNewAttrSix' SYNTAX 1.3.6.1.4.1.1466.115.121.1.5 )

  • dn: cn=schemachangetype: modifyadd: objectclassesobjectclasses: ( 0.0.0 NAME 'myNewClass' SUP top MUST ( myNewAttrOne $ myNewAttrTwo ) MAY ( myNewAttrThree $ myNewAttrFour $ myNewAttrFive $ myNewAttrSix ) )

A couple of things should be noted about this output file. First, the OID values of 0.0.0 should not be used permanently for a shipping product. They will work for testing. The registering of proper OID values can be done through Novell's developer services (seehttp://developer.novell.com/engsup/schreg2c.htm) or other appropriate standards bodies. Next, since these were originally Netscape files, there is no information about naming or containment or other NDS features. The resulting file can be used to extend the NDS schema, but default values for naming and containment will be used. Before extending the NDS schema, you can add any of the NDS extensions or make any desired changes to the LDIF file.

Microsoft Active Directory uses an LDIF file to extend its schema, but it also uses a number of non-standard extensions. Because of this, it has its own utility LDIFDE that is needed to extend the schema. The Schema Migration Utility can also convert these Active Directory schema files into the standard LDIF format to extend the NDS schema.

Modifying the NDS Schema Using LDAPModify

Once you have a standard LDIF file, it is a simple process to modify the NDS schema. An LDIF file can be applied to the NDS schema using a standard LDAPModify utility, or statements similar to those contained in an LDIF file can be used to extend the NDS schema using an LDAP API. The LDAPModify utility can be used with a command line similar to the following:

LDAPModify -h myHost - myPort -D cn=admin,o=novell -w myPassword -f myLDIFFile

This will extend the NDS schema adding the attributes and classes. In addition to adding and deleting classes, classes and attributes can be deleted from the schema. However, the referential integrity rules will not allow schema entries to be deleted if data has been instantiated using any part of the schema entries to be deleted. The following is an example of an LDIF file entry that will delete a class from the schema.

  • dn: cn=schemachangetype: modifydelete: objectclassesobjectclasses: ( 2.16.840.1.113719.1.131.6.1.23 NAME 'myNewClass' SUP top MUST ( myNewAttrOne $ myNewAttrTwo ) MAY ( myNewAttrThree $ myNewAttrFour $ myNewAttrFive $ myNewAttrSix ) )

With the version of NDS that will be released in the summer of 2000, existing schema entries can also be modified. Here is an example of an LDAP entry that will modify an existing schema attribute. This modification is written as a delete and an add. However, the NDS LDAP server will recognize this modification of a single DN as a modify since the delete and the add have the same attribute OID and name. Therefore, this modification will work even if there is data instantiated using this attribute. The following example modifies the upper bound of the attribute from 8 to 40.

  • dn: cn=schemachangetype: modifydelete: attributetypesattributetypes: ( 2.16.840.1.113719.1.131.4.1.7 NAME 'myNewAttr' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{8} )-add: attributetypesattributetypes: ( 2.16.840.1.113719.1.131.4.1.7 NAME 'myNewAttr' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{40} ).

Here is an example of a modification to a class that adds an attribute to the class:

  • dn: cn=schemachangetype: modifydelete: objectclassesobjectclasses: ( 2.16.840.1.113719.1.131.6.1.23 NAME 'myNewClass' SUP top MUST ( myNewAttrOne $ myNewAttrTwo ) MAY (myANewAttrThree $ myNewAttrFour $ myNewAttrFive ) )-add: objectclassesobjectclasses: ( 2.16.840.1.113719.1.131.6.1.23 NAME 'myNewClass' SUP top MUST ( myNewAttrOne $ myNewAttrTwo ) MAY ( myNewAttrThree $ myNewAttrFour $ myNewAttrFive $ myNewAttrSix ) )

There are some limitations with making modifications to existing schema entries. These are based on referential integrity and follow common sense. Any schema modification that could possibly invalidate any data is not allowed. For example, in the first example above, the upper bound was changed from 8 to 40, thus increasing the range of possible values. The bound could not be modified to a value below 8, or it could invalidate data values. Similarly, an attribute could be changed from a single-valued attribute to a multivalued attribute, but not the reverse as there may be data with multiple values. You could change a class and make an object a container, but you cannot change a container object into a leaf object as the container may contain other objects.

Accessing NDS Schema Using LDAPSearch

Accessing the NDS schema using LDAP is the same as accessing other LDAP data. Schema access requires binding to the directory using LDAP v3. Older versions of LDAPSearch may do a v2 bind by default and may require an additional command-line switch to do a v3 bind. There are a few aspects of the schema data that differ among directory vendors. To access the schema, you need to access the RootDSE to get information about the directory. Using LDAPSearch, the RootDSE can be accessed by using the following command:

LDAPSearch -h myHost -b "" -s base objectclass=*

One of the values contained in the RootDSE is the subschemasubentry. This gives the name of the object used to access the schema. For NDS this value is "cn=schema." With this value, you can search the schema with the following command:

LDAPSearch -h myHost -b "cn=schema" -s base objectclass=*

This command will dump the entire schema. The schema is stored in the directory with the schema object containing two values. These are objectclasses and attributetypes. Each of these is a multi-valued attribute. If you want to access just the attributetypes attribute, you can issue the command:

LDAPSearch -b "cn=schema" -s base objectclass=* attributetypes

This command searches for all the schema but returns only the attribute values. Because they are multi-valued attributes you cannot retrieve a single attribute or class. However, if you are using an LDAP API to programmatically access the schema, you can access just the attributetypes attribute or the objectclasses attribute and then loop through the values looking for a particular value. This way you could retrieve just the value for the inetOrgPerson class, for example.

The new version of NDS due in the summer of 2000 will also return a third attribute, ldapSyntaxes. This lists a mapping between the LDAP syntax, OID values, and their associated NDS syntax value.

Accessing and Modifying NDS Data Using LDAPModify

Generally, reading data from NDS and writing data to NDS works with LDAP as you would expect. However, there are a few things of which you should be aware. First, passwords are not treated as any other attribute. Whenever you define userPassword as part of a class in a schema definition, NDS will automatically associate your class with the ndsLoginProperties class and will provide the userPassword as part of the NDS login properties. This allows the object to either log in to NDS or to bind to NDS using LDAP. Because of Novell's security model with NDS, the userPassword attribute cannot be read from the directory. However, you can set the userPassword attribute. In effect, this changes the NDS password for that object.

Next, the use of access control lists (ACLs) via LDAP is available in the summer 2000 version of NDS. This allows you to read and write ACLs using LDAP. As of this writing, a standard for the implementation of ACLs or Access Control Information (ACI ) has not yet been accepted by the IETF. Because of customer demand, Novell has exposed access to the NDS ACLs to LDAP. The following is an example of setting an ACL on an object. This ACL grants rights to cn=MyUser, o=Novell to read and write all of the attributes of cn=myObject,o=Novell.

  • dn: cn=myObject,o=Novellchangetype: modifyadd: aclacl: 6#subtree#cn=myUser,o=Novell#[All Attributes Rights]

Details of the ACLs are explained in a document submitted to the IETF as a draft. It is located at:

http://www.ietf.org/internet-drafts/draft-sermersheim-nds-ldap-schema-00.txt

In addition to support for ACLs, the new version of NDS will also provide support for all the standard operational attributes. These include createTimeStamp, modifyTimeStamp, modifiersName, creatorsName, and subschemaSubEntry. In addition to these standard operational attributes, NDS also provides a few NDS specific operational attributes that provide additional information about objects within NDS. These include structuralObjectClass, subordinateCount, and entryFlags. The structuralObjectClass attributes will tell you the base class of an object. For example, a user object might be a subclass of person, organizationalPerson, and inetOrgPerson. The structuralObjectClass will tell you the base class is inetOrgPerson. The subordinateCount attribute gives you the number of objects in a container object. The entryFlags attribute gives some additional information about an object in the form of a bit mask. Valid values include:

0x01 - the referenced object is an alias 0x02 - the referenced object is at the root of an NDS partition 0x04 - the referenced object is a container object 0x08 - the referenced object is an alias of a container

Operational attributes are not normally returned when you read an object from the directory. If you want to retrieve operational attributes, you have to explicitly request the values. Read Only attributes are handled in the same manner. For example, the following search returns all standard attributes for a user object:

LDAPSearch -h myHost -b o=myOrganization -s sub objectclass=inetOrgPerson

The following example will do the same search, but will retrieve the GUID and modifyTimeStamp attributes. GUID is a read only attribute, and modifyTimeStamp is an operational attribute. These attributes are not returned as part of the basic search done above.

LDAPSearch -h myHost -b o=myOrganization -s sub objectclass=inetOrgPerson GUID modifyTimeStamp

Configuring the NDS LDAP Server

When NDS is installed, LDAP services are installed automatically as part of NDS. During this install process, NDS creates two LDAP objects in the directory. These are used to configure the LDAP services. Using the NDS management console, ConsoleOne, you can make modifications to the LDAP configuration. The LDAP objects created in the directory are usually created in the same container as the server object. If the NDS name for your server is "MyServer," the two LDAP objects would be named, "LDAP Server - MyServer," and "LDAP Group - MyServer." The screen in Figure 1 shows these objects.

Figure 1: LDAP objects created in the directory.

Most of the default settings for LDAP services will work without additional settings. However, there are a few of the settings worth discussing here. First is the general information in the LDAP Group object. One of these settings is a check box allowing clear text passwords. This option is not checked by default. Setting this option is not recommended by Novell. It is added here for convenience and compatibility with other directories. If you try to access NDS without an SSL connection and this box is not checked, the LDAP bind will fail. Checking this box is convenient for testing applications without SSL, but remember that setting this option means that the passwords used to access NDS will not be encrypted.

The next items to discuss are the LDAP class and attribute maps. Since NDS has been available since before LDAP was invented, NDS names can be made up of characters that are not valid LDAP names. The mapping tables allow valid LDAP names to map to NDS names (see Figure 2).

Figure 2: LDAP class and attribute maps.

NDS also has a limit of 32 characters for the length of a name. LDAP does not have this limitation. The mapping tables provide a means for LDAP names to be longer than NDS names. If your application uses LDAP names longer than 32 characters, you can create a mapping that will allow an LDAP name longer than 32 characters to be able to access an NDS name that is shorter than 32 characters. A mapping entry can be added using ConsoleOne or by using another NDS LDIF extension. The following is a LDIF specification that will add an entry to the class mapping table:

  • dn: cn=schemachangetype: modifyadd: objectclassesobjectclasses: ( 2.16.840.1.113719.1.131.6.1.25 NAME 'ldapName' X-NDS_NAME 'NDS Name' )

This mapping maps the LDAP class name ldapName to the NDS class name NDS Name. Mappings can also be done for attribute names in the same manner.

  • dn: cn=schemachangetype: modifyadd: attributetypesattributetypes: ( 2.16.840.1.113719.1.131.4.1.8 NAME 'ldapName' X-NDS_NAME 'NDS Name' )

The attribute and class maps are also contained within the LDAP group object. Within the LDAP Server object you can configure general server information. This includes port number, screen options for tracing, and SSL configuration information.

The new version of NDS due in the summer of 2000 takes one more step in minimizing the need for the class and attribute maps. It will provide an on-the-fly mapping of LDAP names to NDS names so that you can use valid LDAP names to access NDS attributes that do not have valid LDAP names. This will be done without the need for an entry in the attribute or class map tables. The automatic mapping simply allows the access of an NDS name using a name created by using the NDS name with all invalid characters removed. For example an NDS attribute name might exist with the name:

Audit:File Link

This name contains both a colon and a space. The LDAP server would allow access to this attribute using the name:

auditFileLink

In this case, the colon and space were removed, the first character of the name was set to be lower case, and all words that were separated by spaces were set to upper case. So, if you know the name of an NDS attribute or class, you can follow this simple algorithm to access the attribute or class using a valid LDAP name.

Also, we should take a look at the NDS Schema Manager. It can be accessed from the Tools menu of ConsoleOne. A tree must be selected in ConsoleOne, or the Schema Manager menu option will be greyed out and not accessible. When the Schema Manager is displayed, a list of classes and attributes is available. Schema classes and attributes can be added or deleted using ConsoleOne, but you can only make these changes one at a time. If you want to make a number of schema changes, it is easier to define the changes in an LDIF file and use LDAPModifiy to make all the changes at once.

Note that the Schema Manager shows the names of all classes and attributes using their NDS names rather than LDAP names (see Figure 3). The Schema Manager always shows the current state of the schema and is a good way to make sure any schema definitions added with LDAP have been done correctly and are defined as you expect.

Figure 3: The Schema Manager.

Summary

Generally, LDAP servers all tend to follow the specification for directory access. However, defining schema and directory implementations tend to be different among directory vendors. By taking care as you set up and configure NDS as your LDAP server, it will behave much like any other LDAP server and will also provide the features to help you get the most out of NDS.

* Originally published in Novell AppNotes


Disclaimer

The origin of this information may be internal or external to Novell. While Novell makes all reasonable efforts to verify this information, Novell does not make explicit or implied claims to its validity.

© Copyright Micro Focus or one of its affiliates