Mozilla LDAP C SDK Programmer's Guide
Chapter 14 - Working with LDAP Controls
This chapter explains how LDAP controls work and how to use the LDAP controls that are supported by the Mozilla LDAP C SDK.
The chapter includes the following sections:
- How LDAP Controls Work
- Using Controls in the LDAP API
- Determining the Controls Supported by the Server
- Using the Server-Side Sorting Control
- Using the Persistent Search Control
- Using the Entry Change Notification Control
- Using the Virtual List View Control
- Using the Manage DSA IT Control
- Using Password Policy Controls
- Using the Proxied Authorization Control
How LDAP Controls Work
The LDAPv3 protocol (documented in RFC 2251, "Lightweight Directory Access Protocol (v3)") allows clients and servers to use controls as a mechanism for extending an LDAP operation. A control is a way to specify additional information as part of a request and a response.
For example, a client can send a control to a server as part of a search request to indicate that the server should sort the search results before sending the results back to the client.
Servers can also send controls back to clients. For example, the Netscape Directory Server sends a control back to a client during the authentication process if the client's password has expired or is going to expire.
A control specifies the following information:
- A unique object identifier (OID), as defined by the creator of this control.
- An indication of whether or not the control is critical to the operation.
- Optional data related to the control (for example, for the server-side sorting control, the attributes used for sorting search results).
The OID identifies the control. If you plan to use a control, you need to make sure that the server supports the control. (See "Determining the Controls Supported by the Server" for details.)
When your client includes a control in a request for an LDAP operation, the server may respond in one of the following ways:
- If the server supports this control and if the control is appropriate to the operation, the server should make use of the control when performing the operation.
-
If the server does not support the control type or if the control
is not appropriate, the server should do one of the following:
-
If the control is marked as critical to the operation, the
server should not perform the operation and should instead
return the result code
LDAP_UNAVAILABLE_CRITICAL_EXTENSION
. - If the control is marked as not critical to the operation, the server should ignore the control and should perform the operation.
-
If the control is marked as critical to the operation, the
server should not perform the operation and should instead
return the result code
Note that servers can also send controls back to clients.
The LDAP API supports two types of controls:
- Server controls can be included in requests sent by clients and in responses sent by servers.
- Client controls affect the behavior of the LDAP API only and are never sent to the server.
The next section describes how controls are implemented in the LDAP API and which functions you can call to create, send, and parse data from LDAP controls.
Using Controls in the LDAP API
In the LDAP API, a control is represented by an
LDAPControl
structure:
typedef struct ldapcontrol { char *ldctl_oid; struct berval ldctl_value; char ldctl_iscritical; } LDAPControl;
The fields in this structure represent the data in a control:
ldctl_oid
specifies the OID of the control.-
ldctl_value
contains aberval
structure containing data associated with the control. -
ldctl_iscritical
specifies whether or not the control is critical to the operation (LDAP_OPT_ON
indicates that the control is critical, andLDAP_OPT_OFF
indicates that the control is not critical).
For more information on the
LDAPControl
structure,
see Chapter 17 - Data Types and
Structures.
You can either allocate and create the control yourself, or you
can call an LDAP API function to create the control. For example,
you can call the
ldap_create_sort_control()
function to create a server-sorting control.
To include a control in a request, call one of the LDAPv3 API
functions (functions with names ending with _ext
and
_ext_s
). These functions allow you to pass in an array
of server controls and an array of client controls.
(You can also include controls in a request by specifying the
array of controls in the LDAP_OPT_SERVER_CONTROLS
option. Note, however, that these controls will be sent to the
server with every request. If the control is specific to a certain
type of operation, you should use the _ext
and
_ext_s
functions instead.)
To retrieve any controls included in a server's response, call
the ldap_parse_result()
function. You can then retrieve data from the returned controls
yourself (by checking the fields of the
LDAPControl
structure)
or by calling additional API functions (such as the
ldap_parse_sort_control()
function).
When you are done working with a control or with an array of
controls, you should free them from memory by calling the
ldap_control_free()
function or the
ldap_controls_free()
function.
The rest of this chapter explains how to determine which controls are supported by an LDAPv3 server and how to use LDAP API functions to send and retrieve specific types of controls.
Determining the Controls Supported By the Server
According to the LDAPv3 protocol, servers should list any
controls that they support in the supportedControl
attribute in the root DSE. (See
"Understanding DSEs" and
"Getting the Root DSE" for more
information.)
The following table lists some of the OIDs for server controls.
Table 14-1 - LDAPv3 Server Controls
OID of Control | Defined Name (in ldap.h) | Description of Control |
---|---|---|
2.16.840.1.113730.3.4.2 | LDAP_CONTROL_MANAGEDSAIT |
"Manage DSA IT" control (see "Using the Manage DSA IT Control" for details). |
2.16.840.1.113730.3.4.3 | LDAP_CONTROL_PERSISTENTSEARCH |
"Persistent search" control (see "Using the Persistent Search Control" for details). |
2.16.840.1.113730.3.4.4 | LDAP_CONTROL_PWEXPIRED |
"Password expired" control (see "Using Password Policy Controls" for details). |
2.16.840.1.113730.3.4.5 | LDAP_CONTROL_PWEXPIRING |
"Password expiration warning" control (see "Using Password Policy Controls" for details). |
2.16.840.1.113730.3.4.9 | LDAP_CONTROL_VLVREQUEST |
"Virtual list view" control (see "Using the Virtual List View Control" for details). |
1.2.840.113556.1.4.473 | LDAP_CONTROL_SORTREQUEST |
"Server-side sorting" control (see "Using the Server-Side Sorting Control" for details). |
2.16.840.1.113730.3.4.12 | LDAP_CONTROL_PROXYAUTH |
"Proxy authorization" control (see "Using the Proxied Authorization Control" for details). |
The following example is a simple command-line program that searches for the root DSE and prints the values of the supported Control attribute.
Code Example 14-1 - Searching the root DSE and outputting the Control attribute
#include "ldap.h" static char *usage = "Usage: listctrl -h <hostname> -p <portnumber>\n"; /* Associate OIDs of known controls with descriptions. */ struct oid2desc { char *oid; char *desc; }; static struct oid2desc oidmap[] = { {LDAP_CONTROL_MANAGEDSAIT, "Manage DSA IT control"}, {LDAP_CONTROL_SORTREQUEST, "Server-side sorting control"}, {LDAP_CONTROL_PERSISTENTSEARCH, "Persistent search control"}, {LDAP_CONTROL_VLVREQUEST, "Virtual list view control"}, {LDAP_CONTROL_PWEXPIRED, "Password expired control"}, {LDAP_CONTROL_PWEXPIRING, "Password expiration warning control"}, { NULL, NULL } }; int main( int argc, char **argv ) { LDAP *ld; LDAPMessage *result, *e; char *hostname = NULL; char **vals; char *attrs[2]; int i, j, c, portnumber = LDAP_PORT, rc; LDAPControl **serverctrls = NULL, **clntctrls = NULL; /* Parse the command line arguments. */ while ( ( c = getopt( argc, argv, "h:p:" ) ) != -1 ) { switch ( c ) { case 'h': hostname = strdup( optarg ); break; case 'p': portnumber = atoi( optarg ); break; default: printf( "Unsupported option: %c\n", c ); printf( usage ); exit( 1 ); } } /* By default, connect to localhost at port 389. */ if ( hostname == NULL || hostname[0] == NULL ) { hostname = "localhost"; } /* Initialize the connection. */ if ( (ld = ldap_init( hostname, portnumber )) == NULL ) { perror( "ldap_init" ); return( 1 ); } /* Set automatic referral processing off. */ if ( ldap_set_option( ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF ) != LDAP_SUCCESS ) { ldap_perror( ld, "ldap_set_option" ); return( 1 ); } /* Search for the root DSE and retrieve only the supportedControl attribute. */ attrs[ 0 ] = "supportedControl"; attrs[ 1 ] = NULL; rc = ldap_search_ext_s( ld, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, 0, serverctrls, clntctrls, NULL, NULL, &result ); /* Check the search results. */ switch( rc ) { /* If successful, the root DSE was found. */ case LDAP_SUCCESS: break; /* If the root DSE was not found, the server does not comply with the LDAPv3 protocol. */ case LDAP_PARTIAL_RESULTS: case LDAP_NO_SUCH_OBJECT: case LDAP_OPERATIONS_ERROR: case LDAP_PROTOCOL_ERROR: printf( "LDAP server %s:%d returned result code %d (%s).\n" "This server does not support the LDAPv3 protocol.\n", hostname, portnumber, rc, ldap_err2string( rc ) ); return( 1 ); break; /* If any other value is returned, an error must have occurred. */ default: ldap_perror( ld, "ldap_search_ext_s" ); return( 1 ); break; } /* Get the root DSE from the results. Since there is only one root DSE, there should be only one entry in the results. */ e = ldap_first_entry( ld, result ); /* Get and print the values of the supportedControl attribute. */ if (e != NULL && (vals = ldap_get_values(ld, e, "supportedControl")) != NULL ) { printf( "\nControls Supported by %s:%d\n", hostname, portnumber ); printf( "==================================================\n" ); for ( i = 0; vals[i] != NULL; i++ ) { printf( "%s\n", vals[i] ); for ( j = 0; oidmap[j].oid != NULL; j++ ) { if ( !strcmp( vals[i], oidmap[j].oid )) { printf( "\t%s\n", oidmap[j].desc ); } } } /* Free the values allocated by ldap_get_values(). */ ldap_value_free( vals ); printf( "\n" ); } /* Free memory allocated by ldap_search_ext_s(). */ ldap_msgfree( result ); ldap_unbind( ld ); return( 0 ); }
Using the Server-Side Sorting Control
The control with the OID 1.2.840.113556.1.4.473 (or
LDAP_CONTROL_SORTREQUEST
, as defined in the
ldap.h
header file) is a server-side sorting control.
When you send a search request with this control to the server, the
server should sort the results before sending them back to you.
The server-side sorting control is described in RFC 2891: www.ietf.org/rfc/rfc2891.txt
The following sections explain how to use the server-side sorting control:
- Specifying the Sort Order
- Creating the Control
- Performing the Search
- Interpreting the Results
- Example of Using the Server-Sorting Control
Specifying the Sort Order
To specify the sort order of the results, you can call the
ldap_create_sort_keylist()
function. This function creates a sort key list from a string in the
following format:
[-]< attrname >[:< matching_rule_oid >] ...
attrname
is the name of the attribute that you want
to sort by. You can specify a space-delimited list of attribute
names. matchingruleoid
is the optional OID of a
matching rule that you want to use for sorting. The minus sign
indicates that the results should be sorted in reverse order for
that attribute.
For example, the following string specifies that results should
be sorted by last name ("sn
") first in ascending order.
If multiple entries have the same last name, these entries are
sorted by first name ("givenname
") in descending order:
"sn -givenname"
Passing this string to the
ldap_create_sort_keylist()
function creates a sort key list, which is an array of
LDAPsortkey
structures. You can use this to create the server-side sorting
control.
Creating the Control
Next, to create the server-side sorting control, you pass the
sort key list (the array of
LDAPsortkey
structures)
to the ldap_create_sort_control()
function.
You can also specify whether or not the control is critical to the search operation. If the control is marked as critical and the server cannot sort the results, the server should not send back any entries. See "Interpreting the Results" for more information on the ramifications of marking the control as critical.
The function passes back a newly created sort control, an
LDAPControl
structure,
which you can include in a search request.
After you call the ldap_create_sort_control()
function and create the control, you should free the array of
LDAPsortkey
structures
by calling ldap_free_sort_keylist()
.
When you are done receiving sorted results from the server, you
should free the
LDAPControl
structure
by calling ldap_control_free()
.
Performing the Search
To specify that you want the server to sort the results, add the
newly created server-sorting control to a
NULL
-terminated array of
LDAPControl
structures
and pass this array to the
ldap_search_ext()
function or the
ldap_search_ext_s()
function.
The server returns a result for the search operation and a response control. The response control indicates the success or failure of the sorting. To determine if sorting was successful, do the following:
-
Call
ldap_parse_result()
to parse the result of the search operation and retrieve any response controls sent back from the server. Response controls are passed back in aNULL
-terminated array ofLDAPControl
structures. -
Pass this array of structures as an argument to
ldap_parse_sort_control()
to retrieve the LDAP result code for the sorting operation. If the sorting operation fails, the server may also return the name of the attribute that caused the failure. Theldap_parse_sort_control()
function also retrieves this name, if available.
When you are done parsing the array of response controls, you
should free the array by calling the
ldap_controls_free()
function.
The server can return the following result codes that apply to the sorting operation.
Table 14-2 - LDAP result codes for sorting search results
Result Code | Description |
---|---|
LDAP_SUCCESS |
The results were sorted successfully. |
LDAP_OPERATIONS_ERROR |
An internal server error occurred. |
LDAP_TIMELIMIT_EXCEEDED |
The maximum time allowed for a search was exceeded before the server finished sorting the results. |
LDAP_STRONG_AUTH_REQUIRED |
The server refused to send back the sorted search results because it requires you to use a stronger authentication method. |
LDAP_ADMINLIMIT_EXCEEDED |
There are too many entries for the server to sort. |
LDAP_NO_SUCH_ATTRIBUTE |
The sort key list specifies an attribute that does not exist. |
LDAP_INAPPROPRIATE_MATCHING |
The sort key list specifies a matching rule that is not recognized or appropriate. |
LDAP_INSUFFICIENT_ACCESS |
The server did not send the sorted results because the client has insufficient access rights. |
LDAP_BUSY |
The server is too busy to sort the results. |
LDAP_UNWILLING_TO_PERFORM |
The server is unable to sort the results. |
LDAP_OTHER |
This general result code indicates that the server failed to sort the results for a reason other than the ones listed above. |
Interpreting the Results
The following table lists the kinds of results to expect from the LDAP server under different situations.
Table 14-3 - Server responses to sorting controls under different circumstances
Does the Server Support the Sort Control? | Is the Sort Control Marked As Critical? | Other Conditions | Results from LDAP Server |
---|---|---|---|
No | Yes | N/A | The server does not send back any entries. |
No | The server ignores the sorting control and returns the entries unsorted. | ||
Yes | Yes | The server cannot sort the results using the specified sort key list. | The server does not send back any entries. The server sends back the sorting response control, which specifies the result code of the sort attempt and (optionally) the attribute type that caused the error. |
No | The server returns the entries unsorted. The server sends back the sorting response control, which specifies the result code of the sort attempt and (optionally) the attribute type that caused the error. | ||
N/A (may or may not be marked as critical) | The server successfully sorted the entries. |
The server sends back the sorted entries.
The server sends back the sorting response control, which
specifies the result code of the sort attempt
(LDAP_SUCCESS ).
|
|
The search itself failed (for any reason). | The server sends back a result code for the search operation. The server does not send back the sorting response control. |
Example of Using the Server-Sorting Control
The following program uses the server-sorting control to get a list of all users in the directory, sorted in ascending order by last name, then in descending order by first name.
Code Example 14-2 - Applying the server-sorting control
#include <stdio.h> #include "ldap.h" /* Change these as needed. */ #define HOSTNAME "localhost" #define PORTNUMBER 3890 int main( int argc, char **argv ) { LDAP *ld; LDAPMessage *result, *e; char *attrfail, *matched = NULL, *errmsg = NULL; char **vals, **referrals; int rc, parse_rc, version; unsigned long rcode; LDAPControl *sortctrl = NULL; LDAPControl *requestctrls[ 2 ]; LDAPControl **resultctrls = NULL; LDAPsortkey **sortkeylist; /* Get a handle to an LDAP connection */ if ( (ld = ldap_init( HOSTNAME, PORTNUMBER ) ) == NULL ) { perror( "ldap_init" ); return( 1 ); } version = LDAP_VERSION3; ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version ); /* Create a sort key list that specifies the sort order of the results. Sort the results by last name first, then by first name. */ ldap_create_sort_keylist( &sortkeylist, "sn -givenname" ); /* Create the sort control. */ rc = ldap_create_sort_control( ld, sortkeylist, 1, &sortctrl ); if ( rc != LDAP_SUCCESS ) { fprintf( stderr, "ldap_create_sort_control: %s\n", ldap_err2string( rc ) ); ldap_unbind( ld ); return( 1 ); } requestctrls[ 0 ] = sortctrl; requestctrls[ 1 ] = NULL; /* Search for all entries in Sunnyvale */ rc = ldap_search_ext_s( ld, "dc=example,dc=com", LDAP_SCOPE_SUBTREE, "(mail=*example.com*)", NULL, 0, requestctrls, NULL, NULL, 0, &result ); if ( rc != LDAP_SUCCESS ) { fprintf( stderr, "ldap_search_ext_s: %s\n", ldap_err2string( rc ) ); ldap_unbind( ld ); return( 1 ); } parse_rc = ldap_parse_result( ld, result, &rc, &matched, &errmsg, &referrals, &resultctrls, 0 ); if ( parse_rc != LDAP_SUCCESS ) { fprintf( stderr, "ldap_parse_result: %s\n", ldap_err2string( parse_rc ) ); ldap_unbind( ld ); return( 1 ); } if ( rc != LDAP_SUCCESS ) { fprintf( stderr, "ldap_search_ext_s: %s\n", ldap_err2string( rc ) ); if ( errmsg != NULL && *errmsg != '\0' ) { fprintf( stderr, "%s\n", errmsg ); } ldap_unbind( ld ); return( 1 ); } parse_rc = ldap_parse_sort_control( ld, resultctrls, &rcode, &attrfail ); if ( parse_rc != LDAP_SUCCESS ) { fprintf( stderr, "ldap_parse_sort_control: %s\n", ldap_err2string( parse_rc ) ); ldap_unbind( ld ); return( 1 ); } if ( rcode != LDAP_SUCCESS ) { fprintf( stderr, "Sort error: %s\n", ldap_err2string( rcode ) ); if ( attrfail != NULL && *attrfail != '\0' ) { fprintf( stderr, "Bad attribute: %s\n", attrfail ); } ldap_unbind( ld ); return( 1 ); } /* for each entry print out name + all attrs and values */ for ( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) ) { if ((vals = ldap_get_values( ld, e, "sn")) != NULL ) { if ( vals[0] != NULL ) { printf( "%s", vals[0] ); } ldap_value_free( vals ); } if ((vals = ldap_get_values( ld, e, "givenname")) != NULL ) { if ( vals[0] != NULL ) { printf( "\t%s", vals[0] ); } ldap_value_free( vals ); } printf( "\n" ); } ldap_msgfree( result ); ldap_free_sort_keylist( sortkeylist ); ldap_control_free( sortctrl ); ldap_controls_free( resultctrls ); ldap_unbind( ld ); return( 0 ); }
Using the Persistent Search Control
The control with the OID 2.16.840.1.113730.3.4.3 (or
LDAP_CONTROL_PERSISTENTSEARCH
, as defined in the
ldap.h
header file) is the persistent search control. A
persistent search (an ongoing search operation), which allows your
LDAP client to get notification of changes to the directory.
The persistent search control is described in the Internet-Drafts Persistent Search: A Simple LDAP Change Notification Mechanism and LDAP C API Extensions for Persistent Search. For details, see Where to Find Additional Information.
To use persistent searching for change notification, you create a "persistent search" control that specifies the types of changes that you want to track. You include the control in a search request. If an entry in the directory is changed, the server determines if the entry matches the search criteria in your request and if the change is the type of change that you are tracking. If both of these are true, the server sends the entry to your client.
You can use this control in conjunction with an "entry change notification" control. See "Using the Entry Change Notification Control."
To create a persistent search control, call
ldap_create_persistentsearch_control()
.
int ldap_create_persistentsearch_control( LDAP *ld, int changetypes, int changesonly, int return_echg_ctls, char ctl_iscritical, LDAPControl **ctrlp );
You can specify the following information:
-
changetypes
specifies the type of change you want to track. You can specify any of the following (or any combination of the following using a bitwiseOR
operator):-
LDAP_CHANGETYPE_ADD
indicates that you want to track added entries. -
LDAP_CHANGETYPE_DELETE
indicates that you want to track deleted entries. -
LDAP_CHANGETYPE_MODIFY
indicates that you want to track modified entries. -
LDAP_CHANGETYPE_MODDN
indicates that you want to track renamed entries. -
LDAP_CHANGETYPE_ANY
indicates that you want to track all changes to entries.
-
-
changesonly
indicates whether or not you want the server to return all entries that initially matched the search criteria (0 to return all entries, or non-zero to return only the entries that change). -
return_echg_ctls
indicates whether or not you want entry change notification controls included with every modified entry returned by the server (non-zero to return entry change notification controls).
The function passes back an LDAPControl
structure
representing the control in the ctrlp
parameter. You
can add the newly created control to a NULL
-terminated
array of LDAPControl
structures and pass this array to
the ldap_search_ext()
function.
To end the persistent search, you can either call the
ldap_abandon_ext()
function to abandon the search operation, or you can call the
ldap_unbind()
function
to disconnect from the server.
For an example showing how to perform a persistent search, refer
to the example provided with the LDAP C SDK, psearch.c
.
Using the Entry Change Notification Control
The control with the OID 2.16.840.1.113730.3.4.7 (or
LDAP_CONTROL_ENTRYCHANGE
, as defined in the
ldap.h
header file) is the "entry change notification"
control. This control contains additional information about the
change made to the entry, including the type of change made, the
change number (which corresponds to an item in the server's change
log, if the server supports a change log), and, if the entry was
renamed, the old DN of the entry.
The entry change notification control is described in the Internet-Drafts Persistent Search: A Simple LDAP Change Notification Mechanism and LDAP C API Extensions for Persistent Search. For more information on these documents, see Where to Find Additional Information.
You use this control in conjunction with a persistent search control. (See "Using the Persistent Search Control.") If you have specified the preference for returning entry change notification controls, the server includes an entry change notification control with each entry found by the search.
To retrieve and parse an entry change notification control included with an entry, do the following:
-
Pass the
LDAPMessage
structure that represents an entry to theldap_get_entry_controls()
function. -
Pass the entry change notification control to the
ldap_parse_entrychange_control()
function.
Using the Virtual List View Control
The control with the OID 2.16.840.1.113730.3.4.9 (or
LDAP_CONTROL_VLVREQUEST
, as defined in the
ldap.h
header file) is a virtual list view control.
When you send a search request with this control and with a
server-side sorting control to the server, the server should sort
the results and return the specified subset of entries back to your
client. This version of the LDAP C SDK supports this control.
The virtual list view control is described in the Internet-Draft LDAP Extensions for Scrolling View Browsing of Search Results. For details, see Where to Find Additional Information.
Using the Manage DSA IT Control
The control with the OID 2.16.840.1.113730.3.4.2 (or
LDAP_CONTROL_MANAGEDSAIT
, as defined in the
ldap.h
header file) is the manage DSA IT control. You
can use this control to manage search
references in the directory.
The manage DSA IT control is described in the Internet-Draft LDAP Control Extension for Server Side Sorting of Search Results. For details, see Where to Find Additional Information.
To create this control, create an
LDAPControl
structure
and set the ldctl_oid
field to 2.16.840.1.113730.3.4.2.
When you add this control to the array of LDAPControl
structures that you pass to a function (for example,
ldap_search_ext()
or
ldap_modify_ext()
, the
server treats search references as ordinary entries. Rather than
returning a reference to you, the server returns the entry
containing the reference. This allows your client application to
manage search references in the directory.
Using Password Policy Controls
Netscape Directory Server use two server response controls to send information back to a client after an LDAP bind operation:
-
The control with the OID 2.16.840.1.113730.3.4.4 (or
LDAP_CONTROL_PWEXPIRED
, as defined in theldap.h
header file) is the expired password control. This control is used if the server is configured to require users to change their passwords when first logging in and whenever the passwords are reset. If the user is logging in for the first time or if the user's password has been reset, the server sends this control to indicate that the client needs to change the password immediately. At this point, the only operation that the client can perform is to change the user's password. If the client requests any other LDAP operation, the server sends back anLDAP_UNWILLING_TO_PERFORM
result code with an expired password control. -
The control with the OID 2.16.840.1.113730.3.4.5 (or
LDAP_CONTROL_PWEXPIRING
, as defined in theldap.h
header file) is the password expiration warning control. This control is used if the server is configured to expire user passwords after a certain amount of time. The server sends this control back to the client if the client binds using a password that will soon expire. Theldctl_value
field of theLDAPControl
structure specifies the number of seconds before the password will expire.
To get these server response controls when binding, you can do the following:
-
Call
ldap_simple_bind()
to send a request for an asynchronous bind operation. -
Call
ldap_result()
to get the results of the operation. -
Call the
ldap_parse_result()
function to parse the result and retrieve the server response controls from the result as an array ofLDAPControl
structures.
You can then check the ldctl_oid
field to determine
the OID of the control and the ldctl_value
field for
any data included in the control.
Using the Proxied Authorization Control
Proxied Authorization is an extension to the LDAPv3 protocol that allows a bound client to assume the identity of another directory entity without rebinding. This allows the client to perform operations as if it were bound as the proxied directory entity. All directory access (read, write, search, compare, delete, and add) is supported by proxied authorization.
This control is supported by Netscape Directory Server 4.1 or later. Proxied authorization support is based on the IETF Internet Draft. For more information, see Where to Find Additional Information.
As an example of proxied authorization, suppose a client is
bound as uid=bjensen, ou=Engineering, dc=example,dc=com
.
Further suppose the bjensen
does not have the right to
search the ou=Marketing, dc=example,dc=com
tree.
However, uid=lboyd, ou=Marketing, dc=example,dc=com
does have rights to search the Marketing
tree.
Further, lboyd
grants proxy right to
bjensen
. In this case, bjensen
may bind as
herself, assume the identity of lboyd
, and then search
the Marketing
tree.
This feature is intended as a performance and administrative benefit for certain types of directory usage. Specifically, applications that want to allow many clients to access or modify specific directory data without rebinding as another directory entity may want to use this feature.
Access Control: The Proxy Right
Proxied authorization adds an additional access right: proxy. If an entry grants the proxy right, then the entity to which that right is granted may assume the identity of the granting entity.
For example, if you wanted to allow uid=bjensen
the
right to proxy as uid=lboyd
, add the following aci to
your directory:
aci: (target = "ldap:///uid=lboyd, ou=Marketing, dc=example,dc=com")(targetattr=*)(version 3.0; aci "grant bjensen the right to proxy as lboyd"; allow(proxy) userdn="ldap:///uid=bjensen, ou=Engineering, dc=example,dc=com";)
This aci allows bjensen
to assume the identity of
lboyd
for all directory operations. Essentially,
bjensen
can do to the directory whatever
lboyd
has permission to do.
While Netscape Directory Server supports this feature, be aware that other directory servers might not support this feature as described here. Netscape Directory Server access control (including the proxy right) is fully described in the Netscape Directory Server Administrator's Guide.
The Proxied Authorization Control
To support proxied authorization (an extension to the LDAPv3
protocol), the proxy authorization control has been added to the
LDAP C SDK in the form of the
ldap_create_proxyauth_control()
function. You use this function to create the control that allows a
bound entity to assume the identity of another directory entry.
Proxy authorization is an optional LDAP server feature; it may
not be supported on all LDAP servers. You should call the proxy
authorization control function only when interacting with LDAP
servers that support this LDAPv3 extension. You can check on the
support of this control by looking at the rootDSE
supportedControl
attrubute. For example, the following
command uses the ldapsearch utility to display the rootDSE:
ldapsearch -v -h hostname -p 389 -b "" -s base ""
In order for the control to work, the LDAP server that you are
connecting to must support the server control for proxied
authorization (OID 2.16.840.1.113730.3.4.12, or
LDAP_CONTROL_PROXYAUTH
as defined in the
ldap.h
header file).
Example
The following code fragment creates an LDAP connection, sets the proxied authorization control, binds to the directory, and then performs a search operation using the proxied authorization control. A more complete example is also available with the SDK example files.
Code Example 14-3 - Proxied authorization control example
#include <ldap.h> int version; LDAP *ld; LDAPControl *requestctrls[ 2 ]; LDAPControl *pactrl = NULL; /* Customize the following host and bind information for your site. */ int port=389; char *host="directory.example.com"; char *baseDN="dc=example,dc=com"; /* Proxied auth specific information. proxyDN is the entity that will be proxied. bindDN and bindpw is for the bind entity that will use the proxyDN. */ char *proxyDN = "uid=lboyd, ou=marketing, dc=example,dc=com"; char *bindDN = "uid=bjensen, ou=engineering, dc=example,dc=com"; char *bindpw = "password"; /* Do general LDAP init stuff */ /* Get a handle to an LDAP connection */ if ( (ld = ldap_init( host, port ) ) == NULL ) { printf("ldap_init did not return a conn handle.\n"); return; } /* set version to ldap version 3 */ version = LDAP_VERSION3; ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version ); /* authenticate to the directory */ if ( ldap_simple_bind_s( ld, bindDN, bindpw ) != LDAP_SUCCESS ) { printf("ldap_simple_bind_s failed"); return (-1); } /* create the proxied authorization control */ if ( ldap_create_proxyauth_control( ld, proxyDN, 1, &pactrl ) ) { printf("ldap_create_proxyauth_control failed.\n"); if ( ldap_unbind( ld ) != LDAP_SUCCESS ) { printf("ldap_unbind failed\n"); } return(-1); } requestctrls[ 0 ] = pactrl; requestctrls[ 1 ] = NULL; /* Perform the search using the control */ printf("Searching for %s with the proxy auth control.\n", proxyDN); if ( ldap_search_ext_s( ld, proxyDN, LDAP_SCOPE_SUBTREE, "(objectclass=*)", NULL, 0, requestctrls, NULL, NULL, LDAP_NO_LIMIT, &results ) != LDAP_SUCCESS ) { printf("ldap_search_ext failed.\n"); printf("Something is wrong with proxied auth.\n"); } else { print_search_results(ld, results); }