Sun logo      Previous      Contents      Index      Next     

Sun Java System LDAP SDK for C Programming Guide

Chapter 3
Writing an LDAP Client

With the Sun™ Java System LDAP SDK for C, you can write a new application, or enable an existing application, to interact with an Lightweight Directory Access Protocol (LDAP) server. This chapter covers the procedures for connecting to an LDAP server, authenticating, requesting operations, and disconnecting from the server. It includes the following sections:


Designing an LDAP Client

The following procedure outlines a typical process for communicating with an LDAP server.

  1. Initialize an LDAP session.
  2. See "Initializing an LDAP Session" for details.

  3. Bind to the LDAP server, if necessary.
  4. See "Binding and Authenticating to an LDAP Server" for details.

  5. Perform LDAP operations, such as searching the directory or modifying entries in the directory.
  6. See "Performing LDAP Operations" for details.

  7. Close the connection to the LDAP server when finished.
  8. See "Closing the Connection to the Server" for details.

Code Example 3-1 is source code for a client that requests a LDAP search operation from a server. The client connects to the LDAP server (running on the local machine at port 389), searches the directory for entries with the last name Jensen (sn=Jensen), and prints out the distinguished name (DN) of any matching entry.

Code Example 3-1  Source Code for LDAP search operation 

#include <stdio.h>

#include "ldap.h"

/* Specify the search criteria here. */

#define HOSTNAME "localhost"

#define PORTNUMBER 389

#define BASEDN "dc=example,dc=com"

#define SCOPE LDAP_SCOPE_SUBTREE

#define FILTER "(sn=Jensen)"

int

main( int argc, char **argv )

{

LDAP *ld;

LDAPMessage *result, *e;

char *dn;

int version, rc;

/* Print out an informational message. */

printf( "Connecting to host %s at port %d...\n\n", HOSTNAME,

PORTNUMBER );

/* STEP 1: Get a handle to an LDAP connection and

set any session preferences. */

if ( (ld = ldap_init( HOSTNAME, PORTNUMBER )) == NULL ) {

perror( "ldap_init" );

return( 1 );

}

/* Use the LDAP_OPT_PROTOCOL_VERSION session preference to specify

that the client is an LDAPv3 client. */

version = LDAP_VERSION3;

ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );

/* STEP 2: Bind to the server.

In this example, the client binds anonymously to the server

(no DN or credentials are specified). */

rc = ldap_simple_bind_s( ld, NULL, NULL );

if ( rc != LDAP_SUCCESS ) {

fprintf(stderr, "ldap_simple_bind_s: %s\n", ldap_err2string(rc));

return( 1 );

}

/* Print out an informational message. */

printf( "Searching the directory for entries\n"

" starting from the base DN %s\n"

" within the scope %d\n"

" matching the search filter %s...\n\n",

BASEDN, SCOPE, FILTER );

/* STEP 3: Perform the LDAP operations.

In this example, a simple search operation is performed.

The client iterates through each of the entries returned and

prints out the DN of each entry. */

rc = ldap_search_ext_s( ld, BASEDN, SCOPE, FILTER, NULL, 0,

NULL, NULL, NULL, 0, &result );

if ( rc != LDAP_SUCCESS ) {

fprintf(stderr, "ldap_search_ext_s: %s\n", ldap_err2string(rc));

return( 1 );

}

for ( e = ldap_first_entry( ld, result ); e != NULL;

e = ldap_next_entry( ld, e ) ) {

if ( (dn = ldap_get_dn( ld, e )) != NULL ) {

printf( "dn: %s\n", dn );

ldap_memfree( dn );

}

}

ldap_msgfree( result );

/* STEP 4: Disconnect from the server. */

ldap_unbind( ld );

return( 0 );

}

...


Initializing an LDAP Session

Before connecting to an LDAP server, you need to initialize a session. As part of this process, you create an LDAP structure that contains information about the LDAP server and session. You then pass this LDAP structure (usually as a pointer) to all subsequent functions in order to identify the LDAP server with which you are working. Sample code for initializing an LDAP session is provided in the section, "Session Initialization Sample Code".


Note

If you plan to connect to the LDAP server over a Secure Sockets Layer (SSL), the procedure for initializing an LDAP session is different. For details, see Chapter 11, "Connecting Over SSL."


Specifying a Single LDAP Server

To initialize a single LDAP session, call ldap_init() and pass the host name and port number of your LDAP server as arguments to the function. If the server is using the default port 389 for the LDAP, you can pass LDAP_PORT as the value for the defport parameter as illustrated in Code Example 3-2.

Code Example 3-2  Passing an LDAP Server with the Default LDAP Port

...

LDAP *ld

...

ld = ldap_init( "directory.iplanet.com", LDAP_PORT );

If successful, ldap_init() returns a connection handle (a pointer to the LDAP structure that contains information about the connection) to the LDAP server. You need to pass this pointer to the API for connecting, authenticating, and performing LDAP operations on a server. For example, when you call a function to search the directory, you need to pass the connection handle as a parameter to provide a context for the connection.


Caution

ldap_init() does not open a connection to the LDAP server. The actual connection opening will occur when the first operation is attempted.


Specifying a List of LDAP Servers

When initializing the LDAP session, you can also specify a list of LDAP servers for which you want to attempt connections. If the first LDAP server in the list does not respond, the client will attempt to connect to the next server in the list. To specify a list of LDAP servers, pass a space-delimited list of the host names as the first argument to the ldap_init() function. In Code Example 3-3, the LDAP client will attempt to connect to the LDAP server on ld1.example.com, port 389. If that server does not respond, the client will attempt to connect to the LDAP server on ld2.example2.com, port 389. If that server does not respond, the client will use the server on ld3.example.com, port 389.

Code Example 3-3  Passing Multiple LDAP Servers with Default LDAP Port 

...

LDAP *ld

...

ld = ldap_init( "ld1.example.com ld2.example2.com

ld3.example.com", LDAP_PORT );

If any of the servers do not use the default LDAP port, use the host:port format to specify the server name and port number as the argument to ldap_init(). In Code Example 3-4, the client will attempt to connect to the LDAP server on ld1.example.com, port 389. If that server does not respond, the client will attempt to connect to the LDAP server on ld2.example.com, port 38900.

Code Example 3-4  Passing Non-Default LDAP Ports

...

LDAP *ld

...

ld = ldap_init( "ld1.example.com ld2.example.com:38900",

LDAP_PORT );

Session Initialization Sample Code

Code Example 3-5 initializes a session with an LDAP server, specifying a list of LDAP servers to try: ldap.example.com:389 and directory.example.com:3890. It also sets a session preference that identifies the client as an LDAPv3 client. (After you initialize a session with an LDAP server, you can set session preferences. More information can be found in the section, "Setting Session Preferences".)


Note

Note that ldap_init() does not connect to the LDAP server right away.


Code Example 3-5  Initializing an LDAP Session 

#include <stdio.h>

#include <ldap.h>

...

LDAP *ld;

int ldap_default_port, version;

/* Specify list of LDAP servers that you want to try connecting to. */

char *ldap_host = "ldap.example.com directory.example.com:3890";

/* If the LDAP server is running on the standard LDAP port (port 389),

* you can use LDAP_PORT to identify the port number. */

ldap_default_port = LDAP_PORT;

...

/* Initialize the session with the LDAP servers. */

if ( ( ld = ldap_init( ldap_host, ldap_default_port ) ) == NULL ) {

perror( "ldap_init" );

return( 1 );

}

/* Specify the LDAP version supported by the client. */

version = LDAP_VERSION3;

ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );

...

/* Subsequent API calls pass ld as argument to identify the LDAP server. */

...


Setting Session Preferences

With the LDAP SDK for C, you can set preferences for your client that you want applied to all LDAP sessions. To get or set the value of a preference, call either the ldap_get_option() or ldap_set_option() functions, respectively. Both functions pass two parameters (in addition to the ld parameter which represents the connection to the server):

The following sections illustrate how to set two specific preferences. For a complete listing of the options and values you can get and set, see the documentation for the two functions in Chapter 17, "Function Reference."

Setting the Restart Preference

If communication with the LDAP server is interrupted, the result code LDAP_SERVER_DOWN is returned by your client. If you want your client to continue to attempt communication with the server, you can set the LDAP_OPT_RECONNECT preference for the session. Once set, if your connection is lost, the client will attempt another bind (using the same authentication) to reestablish the connection.

To set the restart preference, call the ldap_set_option() function and pass LDAP_OPT_RECONNECT as the value of the option parameter. If you want the client to resume LDAP I/O operations automatically, set the optdata parameter to LDAP_OPT_ON to specify that the same connection handle can be used to reconnect to the server. If you do not want the client to resume I/O operations, set the optdata parameter to LDAP_OPT_OFF to specify that you want to create a new connection handle to connect to the server. By default, the optdata parameter is set to LDAP_OPT_OFF. Both LDAP_OPT_OFF and LDAP_OPT_ON are cast to (void *). Code Example 3-6 illustrates how you can pass these parameters directly to the function.

Code Example 3-6  Passing Restart Preferences to Function

...

ldap_set_option( ld, LDAP_OPT_RECONNECT, LDAP_OPT_ON );

...

Specifying the LDAP Version of Your Client

If you plan to call functions that make use of LDAPv3 features (such as controls or extended operations), you should set the protocol version of your client to LDAPv3. By default, clients built with the Sun Java System LDAP SDK for C identify themselves to LDAP servers as LDAPv2 clients.

To specify the LDAP version supported by your client, call the ldap_set_option() function and set the LDAP_OPT_PROTOCOL_VERSION option to the value 3. Code Example 3-7 illustrates this.

Code Example 3-7  Passing the LDAP Protocol Version Number

...

version = LDAP_VERSION3;

ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );

...

After setting this option, as part of the authentication process, your client sends the supported LDAP version number to the server allowing it to determine whether or not to enable LDAPv3 features.


Tip

The LDAPv3 allows you to perform LDAP operations without first binding to the server. An LDAPv3 server will assume that the client is LDAPv3 compliant if the client issues non-bind operations before it issues a bind.



Binding and Authenticating to an LDAP Server

When connecting to the LDAP server, your client may need to send a bind request. A bind request should be sent if:

The bind request should contain the following information:

Methods of Authentication

When binding to an LDAP server, you can use a number of different methods to authenticate your client. They include:

Simple Authentication

With simple authentication, your client provides the DN of the user and the user’s password to the LDAP server. For more information, see "Using Simple Authentication".


Note

You can also use this method to bind anonymously by providing NULL values as the user’s DN and password as described in "Binding Anonymously".


Certificate-based Client Authentication

With certificate-based client authentication, your client sends its certificate to the LDAP server. For more information, see Chapter 11, "Connecting Over SSL."

Simple Authentication and Security Layer (SASL)

SASL is detailed in the Simple Authentication and Security Layer (SASL) - RFC 2222 (http://www.faqs.org/rfcs/rfc2222.html). Some LDAPv3 servers (including Sun Java System Directory Server) support authentication through SASL. For more information, see Chapter 12, "Using SASL Authentication."


Note

If you are binding to Sun Java System Directory Server, the server may send back special controls to indicate that your password has expired or will expire in the near future. For more information on these controls, see "Using Password Policy Controls" in Chapter 13, "Working with LDAP Controls."


Using Simple Authentication

If you plan to use simple authentication, call one of the following functions:

Performing a Synchronous Authentication Operation

If you want to wait for the bind operation to complete before continuing, call ldap_simple_bind_s(). This function returns LDAP_SUCCESS if the operation completed successfully or an LDAP result code if a problem occurred. (See ldap_simple_bind_s() in Chapter 17, "Function Reference" for a list of result codes returned.)


Note

If you specify a DN but no password, your client will bind to the server anonymously. If you want a NULL password to be rejected as incorrect, you need to write code to perform the check before you call ldap_simple_bind_s().


Code Example 3-8 uses the synchronous ldap_simple_bind_s() function to authenticate user Barbara Jensen to the LDAP server.

Code Example 3-8  Synchronous Authentication 

#include <stdio.h>

#include "ldap.h"

/* Change these as needed. */

#define HOSTNAME "localhost"

#define PORTNUMBER LDAP_PORT

#define BIND_DN "uid=bjensen,ou=People,dc=example,dc=com"

#define BIND_PW "hifalutin"

LDAP *ld;

int rc;

/* Get a handle to an LDAP connection. */

if ( (ld = ldap_init( HOSTNAME, PORTNUMBER )) == NULL ) {

perror( "ldap_init" );

return( 1 );

}

/* Print out an informational message. */

printf( "Binding to server %s:%d\n", HOSTNAME, PORTNUMBER );

printf( "as the DN %s ...\n", BIND_DN );

/* Bind to the LDAP server. */

rc = ldap_simple_bind_s( ld, BIND_DN, BIND_PW );

if ( rc != LDAP_SUCCESS ) {

fprintf(stderr, "ldap_simple_bind_s: %s\n\n", ldap_err2string(rc));

return( 1 );

} else {

printf( "Bind operation successful.\n" );

}

...

/* If you want, you can perform LDAP operations here. */

...

/* Disconnect from the server when done. */

ldap_unbind( ld );

return( 0 );

...

Performing an Asynchronous Authentication Operation

If you want to perform other work (in parallel) while waiting for the bind operation to complete, call ldap_simple_bind(). This function sends a LDAP bind request to the server and returns a message ID identifying the bind operation. You can check to see if your client has received the results of the bind operation from the server by calling the ldap_result() function and passing it this message ID. If your client has received the results, ldap_result() passes back the information in an LDAPMessage structure. To retrieve error information from LDAPMessage, you can pass it to the ldap_parse_result() function. ldap_parse_result() gets the LDAP result code of the operation and any error messages sent back from the server. This function also retrieves any controls sent back.


Note

If you specify a DN but no password, your client will bind to the server anonymously. If you want a NULL password to be rejected as incorrect, you need to write code to perform the check before you call ldap_simple_bind().


Code Example 3-9 uses the asynchronous ldap_simple_bind() function to authenticate user Barbara Jensen to the LDAP server.

Code Example 3-9  Asynchronous Authentication 

#include <stdio.h>

#include "ldap.h"

void do_other_work();

int global_counter = 0;

...

#define HOSTNAME "localhost"

#define PORTNUMBER LDAP_PORT

#define BIND_DN "uid=bjensen,ou=People,dc=example,dc=com"

#define BIND_PW "hifalutin"

...

LDAP *ld;

LDAPMessage *res;

int msgid = 0, rc = 0, parse_rc = 0, finished = 0;

char *matched_msg = NULL, *error_msg = NULL;

char **referrals;

LDAPControl **serverctrls;

struct timeval zerotime;

/* Specify the timeout period for ldap_result(),

which specifies how long the function should block when waiting

for results from the server. */

zerotime.tv_sec = zerotime.tv_usec = 0L;

/* Get a handle to an LDAP connection. */

if ( (ld = ldap_init( HOSTNAME, PORTNUMBER )) == NULL ) {

perror( "ldap_init" );

return( 1 );

}

/* Print out an informational message. */

printf( "Binding to server %s:%d\n", HOSTNAME, PORTNUMBER );

printf( "as the DN %s ...\n", BIND_DN );

/* Send an LDAP bind request to the server. */

msgid = ldap_simple_bind( ld, BIND_DN, BIND_PW );

/* If the returned message ID is less than zero, an error occurred. */

if ( msgid < 0 ) {

rc = ldap_get_lderrno( ld, NULL, NULL );

fprintf(stderr, "ldap_simple_bind : %s\n", ldap_err2string(rc));

ldap_unbind( ld );

return( 1 );

}

/* Check to see if the bind operation completed. */

while ( !finished ) {

rc = ldap_result( ld, msgid, 0, &zerotime, &res );

switch ( rc ) {

/* If ldap_result() returns -1, error occurred. */

case -1:

rc = ldap_get_lderrno( ld, NULL, NULL );

fprintf( stderr, "ldap_result: %s\n", ldap_err2string( rc ) );

ldap_unbind( ld );

return ( 1 );

/* If ldap_result() returns 0, the timeout (specified by the

timeout argument) has been exceeded before the client received

the results from the server. Continue calling ldap_result()

to poll for results from the server. */

case 0:

break;

default:

/* The client has received the result of the bind operation. */

finished = 1;

/* Parse this result to determine if the operation was successful.

Note that a non-zero value is passed as the last parameter,

which indicates that the LDAPMessage structure res should be

freed when done. (No need to call ldap_msgfree().) */

parse_rc = ldap_parse_result( ld, res, &rc, &matched_msg,

&error_msg, &referrals, &serverctrls, 1 );

if ( parse_rc != LDAP_SUCCESS ) {

fprintf( stderr, "ldap_parse_result: %s\n",

ldap_err2string( parse_rc ) );

ldap_unbind( ld );

return( 1 );

}

/* Check the results of the operation. */

if ( rc != LDAP_SUCCESS ) {

fprintf( stderr, "ldap_simple_bind: %s\n",

ldap_err2string( rc ) );

/* If the server sent an additional error message,

print it out. */

if ( error_msg != NULL && *error_msg != '\0' ) {

fprintf( stderr, "%s\n", error_msg );

}

/* If an entry specified by a DN could not be found,

the server may also return the portion of the DN

that identifies an existing entry.

(See “Receiving the Portion of the DN Matching an Entry”

for an explanation.) */

if ( matched_msg != NULL && *matched_msg != '\0' ) {

fprintf( stderr,

"Part of the DN that matches an existing entry: %s\n",

matched_msg );

}

ldap_unbind( ld );

return( 1 );

} else {

printf( "Bind operation successful.\n" );

printf( "Counted to %d while waiting for bind op.\n",

global_counter );

}

break;

}

/* Do other work here while waiting for results from the server. */

if ( !finished ) {

do_other_work();

}

}

...

/* If you want, you can perform LDAP operations here. */

...

/* Disconnect from the server when done. */

ldap_unbind( ld );

return( 0 );

...

/* Function that does work while waiting for results from the server. */

void do_other_work() {

global_counter++;

}

...

Binding Anonymously

In some cases, you may not need to authenticate to the LDAP server. For example, if you are writing a client to search the directory and users don’t need special access permissions for the operation, you don’t need to authenticate before performing the search. To bind as an anonymous user, call ldap_simple_bind() or ldap_simple_bind_s(), and pass NULL values for the who and passwd parameters as illustrated in Code Example 3-10.

Code Example 3-10  Binding Anonymously

...

rc = ldap_simple_bind_s( ld, NULL, NULL );

...

With LDAPv2, the client is required to send a bind request, even when binding anonymously (without specifying a name or password). With LDAPv3, the client is no longer required to bind to the server if the client does not need to authenticate.


Performing LDAP Operations

Once you initialize a session with an LDAP server and complete the authentication process, you can perform LDAP operations, such as searching the directory, adding new entries, updating existing entries, and removing entries (provided the server’s access control allows these operations). Table 3-1 lists some of these LDAP operations and where you can get more information about them.

Table 3-1  Performing LDAP Operations

To Perform This Operation

Call This API Function

For More Information

Search for entries

ldap_search_ext()
ldap_search_ext_s()

Chapter 5, "Searching a LDAP Directory"

Determine an attribute’s value

ldap_compare_ext()
ldap_compare_ext_s()

Chapter 8, "Comparing Entries"

Add entries

ldap_add_ext()
ldap_add_ext_s()

Chapter 7, "Adding, Modifying and Deleting Entries"

Modify entries

ldap_modify_ext()
ldap_modify_ext_s()

Chapter 7, "Adding, Modifying and Deleting Entries"

Delete entries

ldap_delete_ext()
ldap_delete_ext_s()

Chapter 7, "Adding, Modifying and Deleting Entries"

Change DN of entries

ldap_rename()
ldap_rename_s()

Chapter 7, "Adding, Modifying and Deleting Entries"

Most LDAP operations can be performed synchronously or asynchronously. The functions with names ending in _s are synchronous, and the remaining ones are asynchronous. For more information on the distinction between calling synchronous and asynchronous functions, see "Synchronous and Asynchronous Operations" of Chapter 1, "Introduction" as well as "Synchronous and Asynchronous Functions" of Chapter 4, "Using the LDAP API.")


Closing the Connection to the Server

When you have finished performing all necessary LDAP operations, you need to close the connection to the LDAP server. After you close the connection, you can no longer use the LDAP structure as it is freed from memory. To close a connection to an LDAP server, call one of the following functions:

Both ldap_unbind() and ldap_unbind_s() are identical synchronous functions. They use different names so that each has a corresponding authentication function (ldap_simple_bind() and ldap_simple_bind_s()) to close the server connection. The ldap_unbind_ext() function allows you to explicitly include both server and client controls in your unbind request. However, since there is no server response to an unbind request, there is no way to receive a response from a server control that is included with your unbind request. Code Example 3-11 closes the current connection with the LDAP server.

Code Example 3-11  Closing an LDAP Server Connection

#include <stdio.h>

#include "ldap.h"

...

LDAP *ld;

int rc;

...

/* After completing your LDAP operations with the server, close

the connection. */

rc = ldap_unbind( ld );

if ( rc != LDAP_SUCCESS ) {

fprintf( stderr, "ldap_unbind: %s\n", ldap_err2string( rc ) );

}

...



Previous      Contents      Index      Next     


Copyright 2004 Sun Microsystems, Inc. All rights reserved.