Developing C-based applications
Many C-based applications will want to make use of directory based information. The IBM Directory Server C-Client SDK includes various sample LDAP client programs, and an LDAP client library used to provide application access to the LDAP servers. In this chapter, sample code is provided to connect and search a directory and get the results. In addition sample code is provided to modify a directory entry. More information about the C-Client SDK can be found at:
19.1 Overview
Whether writing new C-based applications or modernizing existing applications, there are many benefits from directory-enabling them. The IBM Directory Server C-Client SDK provides a rich set of application programming interfaces (APIs) that allow developers to search and update entries in an LDAP directory. This chapter gives examples of how to use some of these APIs for searching and updating the directory.
The set of LDAP APIs are designed to provide a suite of functions that can be used to develop directory-enabled applications. Directory enabled applications will typically connect to one or more directories and perform various directory-related operations, such as:
Performing binds
Adding entries
Searching the directory and obtaining the resulting list of entries
Deleting entries
Modifying entries
Renaming entries
Setting LDAP controls
The type of information that is managed in the directory depends on the nature of the application. Directories are often used to provide public access to information about people, including:
Name information
Phone numbers
E-mail addresses
Fax numbers
Mailing addresses
Increasingly, directories are being used to manage and publish other types of information, including:
Configuration information
Public key certificates (managed by Certification Authorities)
Access control information
Locating information (how to find a service)
The LDAP APIs provide for both synchronous and asynchronous access to a directory. Asynchronous access makes it easy for the application to do other work while waiting for the results of a potentially lengthy directory operation to be returned by the server.
Source code, example makefile, and executable programs are provided with the IBM Directory Server Client SDK for performing the following operations:
ldapchangepwd - changes a user's password
ldapsearch - searches the directory
ldapmodify - modifies information in the directory
ldapdelete - deletes information from the directory
ldapmodrdn - modifies the Relative Distinguished Name (RDN) of an entry in the directory
19.2 Typical API usage
The basic interaction is as follows:
1. A connection is made to an LDAP server by calling either ldap_init or ldap_ssl_init, which is used to establish a secure connection over Secure Sockets Layer (SSL).
2. An LDAP bind operation is performed by calling ldap_simple_bind. The bind operation is used to authenticate to the directory server. Note that the LDAP V3 API and protocol permits the bind to be skipped, in which case the access rights associated with anonymous access are obtained.
3. Other operations are performed by calling one of the synchronous or asynchronous routines (for example, ldap_search_s or ldap_search followed by ldap_result).
4. Results returned from these routines are interpreted by calling the LDAP parsing routines, which include operations such as:
 – ldap_first_entry, ldap_next_entry
 – ldap_get_dn
 – ldap_first_attribute, ldap_next_attribute
 – ldap_get_values
 – ldap_parse_result (new for LDAP V3)
5. The LDAP connection is terminated by calling ldap_unbind.
When handling a client referral to another server, the ldap_set_rebind_proc routine defines the entry point of a routine called when an LDAP bind operation is needed.
For more detailed information on the API calls mentioned above, please refer to the IBM Tivoli Directory Server 5.2 C-Client SDK Programming Reference Guide. This guide is available at:
19.3 API flow when searching a directory
This section provides an overview of what APIs can be used to perform a search operation on a LDAP directory. This example, based on LDAP Version 3 APIs, shows one way of searching the directory using a non-secure session in synchronous mode. There are also APIs available to initiate an SSL session to the LDAP server. The APIs are documented in the order they have to be used. Figure 19-1 shows an overview of the APIs and the order in which they are used.
Figure 19-1 Overview of APIs used for searching a directory
In the example given in Figure 19-1 the APIs are processed in a certain order. The following information explains the purpose of each API used in the example.
19.3.1 ldap_init()
The ldap_init() API initializes a session with an LDAP server. The server is not actually contacted until an operation is performed that requires it, allowing various options to be set after initialization, but before actually contacting the host. It allocates an LDAP structure that is used to identify the connection and maintain per-connection information. The input parameters required are the host and port number of the LDAP server. The ldap_init() function returns a pointer to an LDAP structure, which should be passed to subsequent calls to other LDAP functions such as ldap_simple_bind_s() and ldap_search_s().
19.3.2 ldap_simple_bind_s()
The ldap_simple_bind_s() function is used to authenticate a distinguished name (DN) to a directory server. There are other APIs available to authenticate users with a different authentication method. With LDAP Version 3 the bind API can be skipped allowing an anonymous connection to the directory server. However, anonymous access will have limited access on the majority of LDAP servers. The ldap_simple_bind_s() API requires as input parameters: the LDAP structure ld as returned by the ldap_init() API, the distinguished name (DN) of the entry performing the bind, and the password. The DN used has to have the authorities to perform the intended changes. The return code of this API indicates a successful bind or another error code.
19.3.3 ldap_search_s()
The ldap_search_s() API is used to perform an LDAP search operation. ldap_search_s() is a synchronous request. This API requires the LDAP structure that was returned by the ldap_init() API as an input parameter. The remaining input parameters define the search base, scope of search, search filter, attributes to be returned, and whether to return only attribute names or names as well as values. Entries returned from the search (if any) are contained in the res parameter. When an LDAP operation completes and the result is obtained as described, a list of LDAPMessage structures is returned. This is referred to as the search result chain. A pointer to the first of these structures is returned by ldap_search_s() API. However, the results cannot be used in the form returned. They have to be parsed by the corresponding APIs to process the returned entries, their attributes, and the attribute values as depicted in Figure 19-1 on page 606.
19.3.4 ldap_first_entry()
In the search example, this API is used to parse results for the first entry received from the synchronous LDAP search function ldap_search_s(). Used an input parameters are the LDAP structure that was returned by the ldap_init() API and the result LDAPMessage structure returned by the ldap_search_s() API. The latter value is the pointer to the first entry returned by the search function. The return value is the pointer to the first entry of the search results and is required as an input parameter for the ldap_first_attribute() API.
19.3.5 ldap_first_attribute()
The ldap_first_attribute() API returns the first attribute in an entry. ldap_first_attribute() takes the LDAP structure returned by the ldap_init() API and an entry returned by ldap_first_entry() or ldap_next_entry(). In addition it has an output parameter that contains a pointer to an opaque data structure for data encoded with Basic Encoding Rules (BER). This pointer is used in subsequent calls to the ldap_next_attribute() API to keep track of the current position. It returns a pointer to a buffer containing the first attribute type in the entry.
19.3.6 ldap_get_values()
The ldap_get_values() API is used to retrieve attribute values from an LDAP entry as returned by ldap_first_entry() or ldap_next_entry(). The input parameters for the ldap_get_values() API are the pointer to the entry as returned by the ldap_first_entry() or ldap_next_entry() APIs and the pointer to the buffer containing the attribute type as returned by the ldap_first_attribute() or ldap_next_attribute() APIs. The ldap_get_values() returns a NULL-terminated array of the attribute's values. Remember that an attribute value can contain more than one value.
19.3.7 ldap_next_attribute()
Once the values of the first attribute have been processed, a loop can be used to process the remaining attributes of the current entry. The ldap_next_attribute() API takes the LDAP structure returned by the ldap_init() API and the entry returned by ldap_first_entry() or ldap_next_entry(). In addition it has an input/output parameter that contains the pointer that is used to keep track of the current position. For the first time the ldap_next_atttribute() API is called, the pointer is the one returned by the ldap_first_attribute() API. It returns a pointer to a buffer containing the next attribute type in the entry. Processing continues with the ldap_get_values() API until a NULL value is received indicating that no more attributes are available in the current entry.
19.3.8 ldap_get_values()
The ldap_get_values() API is now used to retrieve attribute values from the subsequent attributes returned by the ldap_next_attribute() API.
19.3.9 ldap_next_entry()
After the attributes and values of the first entry have been processed, the next entry from the search results can be processed using the ldap_next_entry() API. The input parameters needed are the LDAP structure that was returned by the ldap_init() API. The second parameter is the pointer to the entry as returned by the ldap_first_entry() or for subsequent calls to the ldap_next_entry() API by the ldap_next_entry() API. The return value is the pointer to the next entry of the search results and is required as an input parameter for the ldap_first_attribute() and ldap_next_attribute() APIs. The next entries' attributes and their values can now be processed using the next entry as described in ldap_first_attribute(). A return value of NULL indicates that no more entries are in the search results to be processed.
19.3.10 ldap_unbind_s()
After all entries have been processed the application must unbind from the LDAP server using, as in this example, the ldap_unbind_s() API. The API is used to end the connection to the LDAP server and free the resources contained in the LDAP structure that was created by the ldap_init() API.
 
Note: Several of the APIs mentioned in this section allocate memory and resources. It is strongly recommended to use APIs, such as ldap_memfree(), ldap_msgfree(), and ldap_control_free(), to free up the allocated resources.
19.4 Sample code to search a directory
The sample application shown in Example 19-1 was written to help developers understand the various tasks involved to use the API to search an LDAP-based directory. It was written to provide a proof of concept.
 
Important: The sample application shown in Example 19-1 does not cover and act on all possible exceptions, nor is it fully tested under all possible circumstances. It is a working application that can be used as an example and be extended to build a complete application.
Example 19-1 Code to search a directory using the C API
/* c_search.c - generic program to display ldap search results to STDOUT */
 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ldap.h>
 
/* global variables */
static char *binddn = "cn=root";
static char *bindpwd = "password";
static char *ldaphost = "serverA.ibm.com";
static int ldapport = LDAP_PORT;
static char *ldapbase = "o=ibm,c=us";
static int referrals = LDAP_OPT_ON;
static int deref = LDAP_DEREF_NEVER;
static int ldapversion = LDAP_VERSION3;
 
main( int argc, char **argv )
{
int rc; // return code
int i = 0; // counter
const char *errormsg = NULL; // error msg
LDAP *ld; // Ldap Object
LDAPMessage *searchResult; // LDAPMessage used to get searchResult
LDAPMessage *ldapEntry; // LDAPMessage used to retrieve entries
BerElement *ber; // BER element
char *attr = NULL; // attribute pointer
char **values = NULL; // values pointer
 
/* open connection to server */
if ((ld = ldap_init(ldaphost, ldapport)) == NULL)
{
perror("ldap_init");
exit(1);
}
 
// BIND to server using userid and password
rc = ldap_simple_bind_s( ld, binddn, bindpwd );
 
// Check to make sure BIND was successful otherwise exit
if ( rc != LDAP_SUCCESS )
{
errormsg = ldap_err2string(ldap_get_errno(ld));
fprintf(stderr, "ldap_bind_s: %s ", errormsg);
exit(rc);
}
 
// Perform search for all user objects in directory
rc = ldap_search_s(ld, ldapbase, LDAP_SCOPE_SUBTREE, "(objectclass=inetorgperson)", NULL, 0, &searchResult);
 
// Check to ensure search was successful
if ( rc != LDAP_SUCCESS )
{
errormsg = ldap_err2string(ldap_get_errno(ld));
fprintf(stderr, "ldap_search_s: %s ", errormsg);
exit(rc);
}
 
// Get first entry from searchResult object
ldapEntry = ldap_first_entry(ld, searchResult);
 
// Continue to loop until we have no more entries
while (ldapEntry != NULL)
{
// output dn to STDOUT
printf("%s ", ldap_get_dn(ld, ldapEntry));
 
// Get first attribute
attr = ldap_first_attribute(ld, ldapEntry, &ber);
 
// Continue to loop as long as we still have attributes
while ( attr != NULL)
{
// Get the array of values for the current attribute
values = ldap_get_values(ld, ldapEntry, attr);
 
i=0;
// Enumerate thru the array until we have printed all values to screen
while (values[i] != NULL)
{
printf("%s=%s ", attr, values[i]);
i++;
}
 
// Get the next attribute
attr = ldap_next_attribute(ld, ldapEntry, ber);
}
 
// Get the next entry
ldapEntry = ldap_next_entry(ld, ldapEntry);
printf(" ");
}
 
// Clean up allocated memory
ldap_msgfree(searchResult);
ldap_ber_free(ber);
ldap_memfree(attr);
ldap_value_free(values);
 
/* unbind and exit */
ldap_unbind_s(ld);
exit(rc);
}
19.5 API flow when updating a directory entry
This section provides an overview of what APIs can be used to perform an update of attributes for an existing entry in the LDAP directory. This example, based on LDAP Version 3 APIs, shows how to perform the update using a non-secure session in synchronous mode. There are also APIs available to initiate an SSL session to the LDAP server. The APIs are documented in the order they have to be used. Figure 19-2 shows an overview of the APIs and the order in which they are used.
Figure 19-2 Overview of API used for updating a directory entry
In the example shown in Figure 19-2, the application will perform an update of the existing entry with the DN of uid=mjordan,ou=People,o=ibm,c=us. Example 19-2 shows the current attributes and Example 19-3 on page 613 shows the new attributes after the update has been performed.
Example 19-2 Current attributes before being updated
Entry: uid=mjordan,ou=People,o=ibm,c=us
Attribute: cn Value: Michael Jordan
Attribute: uid Value: mjordan
Attribute: sn Value: Jordan
Attribute: givenName Value: Michael
Attribute: telephoneNumber Value: 202-555-1234
Value: 919-555-9876
Attribute: mail Value: [email protected]
Attribute: objectclass Value: top
Value: person
Value: organizationalperson
Value: inetorgperson
Example 19-3 Attribute values after being updated
Entry: uid=mjordan,ou=People,o=ibm,c=us
Attribute: cn Value: Michael Jordan
Attribute: uid Value: mjordan
Attribute: sn Value: Jordan
Attribute: givenName Value: Michael
Value: Mike
Attribute: employeeNumber Value: 23
Attribute: mail Value: [email protected]
Attribute: objectclass Value: top
Value: person
Value: organizationalperson
Value: inetorgperson
As shown in Example 19-3, the common name (cn), last name (sn), userid (uid), and e-mail address (mail) attributes remain unchanged. The first name (givenName) was changed, the telephone numbers have been deleted, and the employee number (employeeNumber) was added. The following descriptions contain an overview of each API that is involved in the update process.
19.5.1 ldap_init()
The ldap_init() API initializes a session with an LDAP server. The server is not actually contacted until an operation is performed that requires it, allowing various options to be set after initialization, but before actually contacting the host. It allocates an LDAP structure that is used to identify the connection and maintain per-connection information. The input parameters required are the host and port number of the LDAP server. The ldap_init() function returns a pointer to an LDAP structure, which should be passed to subsequent calls to other LDAP functions such as ldap_simple_bind_s() and ldap_search_s().
19.5.2 ldap_simple_bind_s()
The ldap_simple_bind_s() function is used to authenticate a distinguished name (DN) to a directory server. There are other APIs available to authenticate users with a different authentication method. With LDAP Version 3 the bind API can be skipped allowing an anonymous connection to the directory server. However, anonymous access will have limited access on the majority of LDAP servers. The ldap_simple_bind_s() API requires as input parameters: The LDAP structure ld as returned by the ldap_init() API, the distinguished name (DN) of the entry performing the bind, and the password. The DN used has to have the authorities to perform the intended changes. The return code of this API indicates a successful bind or another error code.
19.5.3 ldap_modify_s()
The ldap_modify_s() API is a synchronous API that can be used to add, replace, and delete attributes from an existing entry. It takes several input parameters. The first one is the LDAP structure as returned by the ldap_init() API. The second parameter is the DN of the entry to be changed. It is not the DN used for the ldap_simple_bind_s() API unless the authenticated DN is the one that needs to be changed. The third parameter, named mods, is more complex. It is a NULL-terminated array of modifications to be performed to the entry. Each element of the mods array is a pointer to an LDAPMod structure. In regards to the changes described in Example 19-3 on page 613, three LDAPMod structure elements are required.
Element 1 (changes the first name):
mod_op: Set to 0x02 (LDAP_MOD_REPLACE).
mod_type: Specifies the name of the attribute. In this case it is givenName.
mod_vals: The mod_vals field specifies a pointer to a NULL-terminated array of values to add, modify, or delete. In this case the pointer points to an array with three elements. The first two elements contain the first name values. The third element is a null pointer.
Element 2 (adds the employee number):
mod_op: Set to 0x00 (LDAP_MOD_ADD).
mod_type: Specifies the name of the attribute. In this case it is employeeNumber.
mod_vals: The mod_vals field specifies a pointer to a NULL-terminated array of values to add, modify, or delete. In this case the pointer points to an array with two elements. The first element contains the employee number and the second a null pointer.
Element 3 (removes the telephone number):
mod_op: Set to 0x01 (LDAP_MOD_DELETE).
mod_type: Specifies the name of the attribute. In this case telephoneNumber.
mod_vals: The mod_vals field specifies a pointer to a NULL-terminated array of values to add, modify, or delete. Since this element is supposed to delete the mail attribute, mod_vals is set to NULL. The pointer can also point to a specific value to be removed.
An LDAPMod element is not necessary for last name (sn) and first name (givenName) attributes, as they remain unchanged. All modifications are performed in the order in which they are listed.
The return value of the ldap_modify_s() API indicates whether the modification was successful or not.
19.5.4 ldap_unbind_s()
After all entries have been processed the application must unbind from the LDAP server using, as in this example, the ldap_unbind_s() API. The API is used to end the connection to the LDAP server and free the resources contained in the LDAP structure that was created by the ldap_init() API.
19.6 Sample code to update a directory entry
Important: The sample application shown in Example 19-4 was written to help developers understand the various tasks involved to use the API to search an LDAP-based directory. It was written to provide a proof of concept.
 
Important: The sample application shown in Example 19-4 does not cover and act on all possible exceptions, nor is it fully tested under all possible circumstances. It is a working application that can be used as an example and be extended to build a complete application.
Example 19-4 Code to update a directory using the C API
/* c_modify.c - simple program to modify an ldap user */
 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ldap.h>
 
/* global variables */
static char *binddn = "cn=root";
static char *bindpwd = "password";
static char *ldaphost = "serverA.ibm.com";
static int ldapport = LDAP_PORT;
static char *ldapbase = "o=ibm,c=us";
static int referrals = LDAP_OPT_ON;
static int deref = LDAP_DEREF_NEVER;
static int ldapversion = LDAP_VERSION3;
 
main( int argc, char **argv )
{
int rc; // return code
int i = 0; // counter
const char *errormsg = NULL; // error msg
LDAP *ld; // Ldap Object
LDAPMod **mod; // Ldap Modification Object
char *givenName[] = {"Michael", "Mike", NULL}; // array of givenName values, NULL terminated
char *employeeNum[] = {"23", NULL}; // array of employeeNumber values, NULL terminated
 
/* open connection to server */
if ((ld = ldap_init(ldaphost, ldapport)) == NULL)
{
perror("ldap_init");
exit(1);
}
 
// perform simple bind to server
rc = ldap_simple_bind_s( ld, binddn, bindpwd );
 
if ( rc != LDAP_SUCCESS )
{
errormsg = ldap_err2string(ldap_get_errno(ld));
fprintf(stderr, "ldap_bind_s: %s ", errormsg);
exit(rc);
}
 
printf("Connection complete ");
 
// Construct the array of LDAPMod structures representing the attributes
mod = (LDAPMod **)malloc((4) * sizeof(LDAPMod *));
 
// Allocate the memory for each of the Mod objects
for (i = 0; i < 3; i++)
{
if ((mod[i] = (LDAPMod *)malloc(sizeof(LDAPMod))) == NULL)
{
fprintf(stderr, "Cannot allocate memory");
}
}
 
// set up mod object with attributes we want to modify
// replace the current value of givenName with Mike
mod[0]->mod_op = LDAP_MOD_REPLACE;
mod[0]->mod_type = "givenName";
mod[0]->mod_values = givenName;
 
// Add the employeenumber of 23
mod[1]->mod_op = LDAP_MOD_ADD;
mod[1]->mod_type = "employeeNumber";
mod[1]->mod_values = employeeNum;
 
// Delete the attribute telephoneNumber
mod[2]->mod_op = LDAP_MOD_DELETE;
mod[2]->mod_type = "telephoneNumber";
mod[2]->mod_values = NULL;
 
// NULL terminate the array
mod[3] = NULL;
 
// Perform the modify operation.
rc = ldap_modify_s(ld, "uid=mjordan,ou=People,o=ibm,c=us", mod);
 
if ( rc != LDAP_SUCCESS )
{
errormsg = ldap_err2string(ldap_get_errno(ld));
fprintf(stderr, "ldap_modify_s: %s ", errormsg);
exit(rc);
}
 
printf("Modification complete ");
 
/* unbind and exit */
ldap_unbind_s(ld);
printf("Connection complete ");
exit(rc);
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset