There are two major ways to programmatically communicate with Active Directory—Lightweight Directory Access Protocol (LDAP) and Active Directory Service Interfaces (ADSI.). In the following sections, I'll provide an overview of each and describe reasons why you might choose one or the other when writing programs for Active Directory.
As we discussed in the previous chapters, Active Directory is built on an LDAP foundation. The Active Directory services use the LDAP model for defining, formatting, and storing information within the directory. LDAP also defines how client applications on remote computers communicate with the directory server to read and write information in the directory.
LDAP defines and Active Directory supports nine basic protocol operations that are sent over a Transport Control Protocol (TCP) connection between the directory server and the client application. These operations are listed in Table 3-1. Most LDAP commands have a separate a request and response operation. The client makes requests of the server, and the server responds. Figure 3-2 shows how a typical LDAP session between a client and the Active Directory server works. Some operations, such as an asynchronous search, have multiple response parts, and in the case of Unbind, no response is necessary. This interaction is similar to what occurs between a Web browser and Web server using the Hypertext Transfer Protocol (HTTP).
Operation | Description |
---|---|
Bind | Initiates communication session between a client and server. |
Unbind | Discontinues the session. |
Abandon | Cancels any requests in progress. |
Search | Searches the directory for data and returns results. |
Compare | Checks whether a directory entry contains a specified attribute. |
Modify | Updates a directory entry. |
Add | Adds a new entry to the directory. |
Delete | Removes an entry from the directory. |
Modify DN (LDAPv3 only) | Renames an entry or moves an entry to a new location. |
Extended (LDAPv3 only) | Used for operations not yet defined or for implementation-specific options. For example, Windows XP will use an extended operation to support dynamic directory objects. |
Table 3-1 LDAP protocol operations.
Figure 3-2 Client and server LDAP session.
The formal definition for LDAP is contained in documents known as Requests for Comments, or RFCs for short. RFCs have developed into a de-facto means of standardizing a technology to be used on the Internet. Many technologies and specifications, from DNS to how e-mail is passed around, are defined by RFC documents. The following is a partial listing of the important LDAP and directory related RFCs.
The Internet Engineering Task Force (http://www.ietf.org) reviews and approves all RFC documents.
How a client generates the LDAP request operation is entirely up to the developer of the application. A crude way to do this is to use low-level networking functions to form LDAP messages and send them over the TCP connection. More commonly, a developer will use a higher-level programming interface provided by the operating system or a third-party. To increase acceptance of their protocol, the creators of LDAP also defined a client interface for the C programming language. Eventually this definition became RFC 1823, "The LDAP Application Program Interface."
Microsoft provides an implementation of the LDAP API as part of Windows. This API conforms to RFC 1823 and is contained in the Wldap32.dll module. It contains a number of functions that can be used to communicate with LDAP servers. Listing 3-2 shows a C language LDAP application that searches for top level objects in Active Directory.
#include <windows.h>
#include <stdio.h>
#include <winldap.h>
void main( )
{
PLDAP pldapSession; // LDAP session data
PLDAPMessage plmsgSearchResponse; // Server allocated response to
// search request
PLDAPMessage plmsgEntry; // Server allocated response to entry request
PCHAR pszDN; // LDAP distinguished name string
PCHAR* ppszDomainDN = NULL; // Domain DN (string allocated by LDAP
// library)
// Start an LDAP session to nearest LDAP server
pldapSession = ldap_init( NULL, LDAP_PORT );
// Authenticate using user's current credentials
ldap_bind_s( pldapSession, NULL, NULL, LDAP_AUTH_NEGOTIATE );
// Search the root of the LDAP server
ldap_search_s ( pldapSession, // Session handle
NULL, // Location to start search, NULL specifies top
// level
LDAP_SCOPE_BASE, // Search only the root entry (rootDSE)
NULL, // Search for all objects (only one for the
// RootDSE)
NULL, // No attributes specified, return all attributes
FALSE, // Return attributes types and values
Listing 3-2 The LDAPEnumTop.c sample connects to Active Directory and searches for top-level objects.
&plmsgSearchResponse ); // Server allocates and fills
// with search results
// Using the defaultNamingContext attribute, get the distinguished
// name of the domain
ppszDomainDN = ldap_get_values( pldapSession, plmsgSearchResponse,
"defaultNamingContext");
// Display info
printf("Listing objects at %s.\nPress CTRL+C to interrupt.\n",
*ppszDomainDN);
// Search first level of root container
ldap_search_s ( pldapSession, // Session handle
*ppszDomainDN, // Location in directory to start search
LDAP_SCOPE_ONELEVEL, // Search first level below the
// base entry
NULL, // Search for all objects
NULL, // No attributes specified, return all attributes
FALSE, // Return attributes types and values
&plmsgSearchResponse ); // Server allocates and fills
// with search results
// Get the first entry from the search results
plmsgEntry = ldap_first_entry( pldapSession, plmsgSearchResponse );
while ( plmsgEntry ) {
// Get the distinguished name of the entry
pszDN = ldap_get_dn ( pldapSession, plmsgEntry );
// Print the DN of the entry
printf("%s\n", pszDN);
// Get next entry
plmsgEntry = ldap_next_entry( pldapSession, plmsgEntry );
}
// Instruct the library to free the search results
ldap_msgfree( plmsgSearchResponse );
// Free string allocated by the LDAP API
ldap_value_free ( ppszDomainDN );
// Close the session
ldap_unbind( pldapSession );
}
If you are running Windows 2000, I recommend installing at least Service Pack 1. SP1 fixed several problems with Wldap32.dll. For other versions of Windows, be sure that the Active Directory Client located on this book's companion CD and on the Windows 2000 CD-ROM is installed to provide current LDAP support.
Figure 3-3 shows an example of the information that will be displayed by LDAPEnumTop.c.
Figure 3-3 LDAPEnumTop.c sample output.
By far the greatest benefit of the LDAP API is that it provides low-level access to directory information. This access is extremely fast and requires little overhead. Compared to actually coding network messages to talk to an LDAP server, the LDAP API is a godsend.
The LDAP API also provides portability between platforms. You can take LDAP code to nearly any platform, and as long as there is an implementation of RFC 1823, the LDAP code should compile and work correctly.
While the LDAP API provides very low-level support, it is not object oriented, and it uses the same set of operations for all types of directory entries. Since the API is designed for C (not C++) programming, it's extremely difficult to use the API in other programming environments. There are some C++ LDAP classes available that give the LDAP API more of an object-oriented feel, but these classes are provided by third parties and aren't part of the API itself. Microsoft solves these disadvantages, not with a C++ class library, but with a COM-based solution called Active Directory Service Interfaces.
Since the introduction of OLE version 2 and the Component Object Model (COM) in 1993, Microsoft has created many software technologies based on COM. A sampling of these technologies includes ActiveX Data Objects, Collaborative Data Objects, and Microsoft Active Accessibility. All provide COM objects that encapsulate functionality rather than C-language APIs that are monolithic and can be difficult to use in other programming languages.
To that end, Microsoft provides the Active Directory Service Interfaces (ADSI), which is the COM-based, object-oriented means to work with a variety of directory services, including Active Directory. Through a flexible architecture, ADSI can work with open directories based on LDAP or closed directories such as the Novell Netware bindery or the Windows NT Security Accounts Manager (SAM). The architecture can also be customized to allow ADSI-aware applications to access specialized data. This frees up the application developer from having to worry about network protocols or a specific programming interface.
Behind the scenes, the magic of working with multiple directories is made possible with ADSI's provider architecture. Similar to hardware device drivers, ADSI providers support standard interfaces between directory services and the applications that wish to work with them. Figure 3-4 shows the relationship between the various directory pieces. At the top are the client applications that request directory information from ADSI. The providers plug into ADSI and communicate with the respective servers. How these providers communicate with their directories is completely hidden from the client application developer. Authentication is accomplished the same way between all the providers, resolving a major headache as new encryption technologies (and the hackers to defeat them) appear.
The following sections describe the features of ADSI.
ADSI can work with a variety of directory services, including LDAP, Windows NT SAM, and Novell Directory Services (NDS). While this book concentrates on Active Directory, the process of migrating applications to Active Directory might involve programming with existing and legacy directories already in use. ADSI supports the following providers:
In addition to the providers mentioned above, developers can create their own providers to allow applications using ADSI to access proprietary information stored in a directory or directory-like database. Since this book is about Active Directory, and there is already a provider available, I won't be talking about customized ADSI providers. If you'd like to read more about them, refer to the ADSI Software Developers Kit (SDK) on the companion CD.
Figure 3-4 ADSI component architecture.
In keeping with the provider spirit, ADSI also includes a provider for OLE DB to allow access to supported directories from any application that understands OLE DB. The programmatic name of the ADSI OLE DB provider is ADsDSOObject. If you have used ADO, you are familiar with OLE DB. Whereas ADSI provides a common set of interfaces to work with a variety of directories, OLE DB provides a common set of interfaces to work with data from multiple sources. This means you can use the same database technology, such as OLE DB and ADO, to access information in both a SQL database and in Active Directory. This functionality is extremely helpful when linking data stored in a SQL database and within Active Directory or when moving data from separate databases into Active Directory. It's important to note that the ADSI OLE DB provider is read-only; that is you can only use it to get information from Active Directory, not to update Active Directory. For that you must use the regular ADSI interfaces. I'll cover the ADSI OLE DB provider in more detail in Chapter 5.
ADSI can be used with various languages. Most ADSI interfaces support Automation (formally known as OLE Automation), which allows languages compatible with COM to access directory objects. These languages include Microsoft Visual Basic, Microsoft Visual Basic Scripting Edition (VBScript), and Microsoft JScript. Developers writing Web-based applications using either VBScript or JScript with Active Server Pages (ASP), for example, can access directory information without having to download binary code onto the client computer. C and C++ developers can choose between using the Automation interfaces or calling the object properties and methods directly. In this book, the sample applications are primarily C++, Visual Basic, and VBScript. See the upcoming "What This Book Uses" section for additional information.
ADSI supports technology independence as well. For example, ActiveX Data Objects (ADO) can be used to query and filter, but not to write directory information.
Starting with version 2.5, ADSI includes extensibility interfaces that developers can use to further integrate their applications with directory services supported by ADSI. For example, when a new user is added to a directory, a human resources application can be invoked and can provide prompts for specific information. Active Directory has its own extension mechanism that is different from the ADSI method. In Chapter 9, I'll cover extending the Active Directory schema.