Mozilla LDAP SDK Programmer's Guide/LDAP Controls With LDAP C SDK LDAP

From MozillaWiki
Jump to: navigation, search

This section explains how LDAP controls work and how to use them.

How LDAP Controls Work With LDAP C SDK

LDAP v3, as documented in RFC 4511, 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. The control indicates 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, Directory Server can send a control back to a client to indicate that the client password has expired, or that the password is going to expire.

  • A unique object identifier (OID) as defined by the creator of the control
    The OID identifies the control.
  • An indication of whether or not the control is critical to the operation
  • Optional data related to the control
    For example, the server-side sort control needs the attributes that would be used for sorting search results.
  • If the server supports the control, and if the control is appropriate, the server should make use of the control when performing the operation.
    • If the control is marked as critical to the operation, the server should not perform the operation. Instead, the server should return the result code LDAP_UNAVAILABLE_CRITICAL_EXTENSION.
    • If the control is not marked as critical to the operation, the server should ignore the control and perform the operation.

If you plan to use a control, make sure that the server supports the control.

Using Controls in the LDAP API

Server controls can be included in requests sent by clients and in responses sent by servers.

Client controls affect the behavior of the SDK only and are never sent to the server.

In the SDK, a control is represented by an LDAPControl structure.

  • ldctl_oid specifies the OID of the control.
  • ldctl_value contains a berval structure that contains data that is 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. LDAP_OPT_OFF indicates that the control is not critical.

The following shows the LDAPControl structure definition.

typedef struct ldapcontrol {
  char                *ldctl_oid;
  struct berval       ldctl_value;
  char                ldctl_iscritical;
} LDAPControl;

You can allocate space for the control, then create the control yourself. You can also call a function to create the control. For example, you can call the ldap_create_sort_control() function to create a server-side sorting control. To include a control in a request, call one of the LDAP v3 API functions, which are functions with names that end 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. However, these controls are sent to the server with every request. If the control is specific to a certain type of operation, you should use functions with names that end with _ext() and _ext_s() instead.

To retrieve any controls included in a servers 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 functions such as ldap_parse_sort_control().

After working with a control, or with an array of controls, free the controls from memory. Call ldap_control_free() or ldap_controls_free() function.

Determining the Controls Supported by the Server With LDAP C SDK

According to LDAP v3, servers should list any controls that the server supports in the supportedControl attribute in the root DSE.

The following list shows OIDs for server controls that might be referenced in the supportedControl attribute.

  • 1.2.840.113556.1.4.473, 1.2.840.113556.1.4.474: Server-Side Sort (LDAP_CONTROL_SORTREQUEST, LDAP_CONTROL_SORTRESPONSE)
  • 1.3.6.1.4.1.42.2.27.8.5.1: Password Policy (LDAP_CONTROL_PASSWD_POLICY)
  • 1.3.6.1.4.1.42.2.27.9.5.2: Get Effective Rights Request (LDAP_CONTROL_GETEFFECTIVERIGHTS_REQUEST)
  • 1.3.6.1.4.1.42.2.27.9.5.8: Account Availability (LDAP_CONTROL_ACCOUNT_USABLE)
  • 2.16.840.1.113730.3.4.2: Manage DSA IT (LDAP_CONTROL_MANAGEDSAIT)
  • 2.16.840.1.113730.3.4.3: Persistent Search (LDAP_CONTROL_PERSISTENTSEARCH)
  • 2.16.840.1.113730.3.4.7: Entry Change Notification (LDAP_CONTROL_ENTRYCHANGE)
  • 2.16.840.1.113730.3.4.4: Password Expired (LDAP_CONTROL_PWEXPIRED)
  • 2.16.840.1.113730.3.4.5: Password Expiring (LDAP_CONTROL_PWEXPIRING)
  • 2.16.840.1.113730.3.4.9, 2.16.840.1.113730.3.4.10: Virtual List View (LDAP_CONTROL_VLVREQUEST, LDAP_CONTROL_VLVRESPONSE)
  • 2.16.840.1.113730.3.4.12, 2.16.840.1.113730.3.4.18: Proxied Authorization (LDAP_CONTROL_PROXYAUTH, LDAP_CONTROL_PROXIEDAUTH)
  • 2.16.840.1.113730.3.4.15, 2.16.840.1.113730.3.4.16: Authorization Identity Bind Request (LDAP_CONTROL_AUTHZID_RES, LDAP_CONTROL_AUTHZID_REQ)
    Also known as LDAP_CONTROL_AUTH_RESPONSE, LDAP_CONTROL_AUTH_REQUEST.
  • 2.16.840.1.113730.3.4.17: Real Attributes Only Request (LDAP_CONTROL_REAL_ATTRS_ONLY)
  • 2.16.840.1.113730.3.4.19: Virtual Attributes Only Request (LDAP_CONTROL_VIRTUAL_ATTRS_ONLY)

The following sample command-line program searches for the root DSE. The program then prints the values of the supportedControl 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_ACCOUNT_USABLE, "Account availability control"}
  {LDAP_CONTROL_AUTH_REQUEST, "Authorization bind identity request"}
  {LDAP_CONTROL_AUTH_RESPONSE, "Authorization bind identity response"}
  {LDAP_CONTROL_ENTRYCHANGE, "Entry change notification control"}
  {LDAP_CONTROL_GETEFFECTIVERIGHTS_REQUEST, "Get effective rights control"}
  {LDAP_CONTROL_MANAGEDSAIT, "Manage DSA IT control"}
  {LDAP_CONTROL_PASSWD_POLICY, "Password policy control"}
  {LDAP_CONTROL_PERSISTENTSEARCH, "Persistent search control"}
  {LDAP_CONTROL_PROXIEDAUTH, "Proxied authorization (version 2) control"}
  {LDAP_CONTROL_PROXYAUTH, "Proxied authorization (version 1) control"}
  {LDAP_CONTROL_PWEXPIRED, "Password expired control"}
  {LDAP_CONTROL_PWEXPIRING, "Password expiring control"}
  {LDAP_CONTROL_REAL_ATTRS_ONLY, "Real attributes only control"}
  {LDAP_CONTROL_SORTREQUEST, "Server-side sort request control"}
  {LDAP_CONTROL_SORTRESPONSE, "Server-side sort response control"}
  {LDAP_CONTROL_VIRTUAL_ATTRS_ONLY, "Virtual attributes only control"}
  {LDAP_CONTROL_VLVREQUEST, "Virtual list view request control"}
  {LDAP_CONTROL_VLVRESPONSE, "Virtual list view response 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. Use prldap_init() for IPv6. */
  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 With LDAP C SDK

The control with 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 the results back to you.

The server-side sorting control is described in RFC 2891.


Specifying the Sort Order With LDAP C SDK


To specify the sort order of the results, call the ldap_create_sort_keylist function to create a sort key list from a string in the following format:

[-]''attr-name''[:''matching-rule-oid'']
  • attr-name is the name of the attribute to sort by.
    You can specify a space-delimited list of attribute names.
  • matching-rule-oid is the optional OID of a matching rule that you want to use for sorting.
sn -givenname
  • 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.

Pass this string to ldap_create_sort_keylist() to create a sort key list, which is an array of LDAPsortkey structures. You can use this technique to create the server-side sorting control.


Creating the Server-Side Sorting Control With LDAP C SDK


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. The function passes back a newly created sort control, an LDAPControl structure that you can include in a search request.

You can specify whether or not the control is critical to the search operation. If the control is marked as critical, but the server cannot sort the results, the server should not send back any entries. See Interpreting the Results of Sorting With LDAP C SDK for more information about the ramifications of marking the control as critical.

After you call the ldap_create_sort_control() function and create the control, free the array of LDAPsortkey structures by calling ldap_free_sort_keylist(). When you are done receiving sorted results from the server, free the LDAPControl structure by calling ldap_control_free().


Performing a Search With LDAP C SDK


For the server to sort the results, add the newly created server-side sorting control to a NULL terminated array of LDAPControl structures. 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. The server also returns a response control. The response control indicates the success or failure of the sort. To determine if sort was successful, use the following procedure.

To Search With a Sort Request for LDAP C SDK

  1. Call ldap_parse_result() to parse the result of the search operation.
    The function retrieves any response controls sent back from the server.
    Response controls are passed back in a NULL terminated array of LDAPControl structures.
  2. 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 can also return the name of the attribute that caused the failure. The ldap_parse_sort_control() function also retrieves this name, if available.
  3. Free the array by calling the ldap_controls_free() function when you are done parsing the array of response controls.
  4. The server can return the following result codes.
    • LDAP_SUCCESS: The results were sorted successfully.
    • LDAP_OPERATION_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 the server requires you to use a stronger authentication method.
    • LDAP_ADMINLIMIT_EXCEEDED: The server retrieved too many entries 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 previously.


Interpreting the Results of Sorting With LDAP C SDK


The following table shows the kinds of results to expect from the LDAP server under different situations.

Server Responses to Sorting Controls
Does the server support the sort control? Is the sort control marked as critical? Other Conditions Results From LDAP Server
Server does not support the sort control. Control is marked as critical. Not applicable The server does not send back any entries.
  Control is not marked as critical.   The server ignores the sorting control. Instead, the server returns the entries unsorted.
Server does support the sort control. Control is marked as critical. The server cannot sort the results by using the specified sort key list. The server does not send back any entries. The server sends back the sorting response control. The response control specifies the result code of the sort attempt and optionally the attribute type that caused the error.
  Control is not marked as critical.   The server returns the entries unsorted. The server sends back the sorting response control. The response control specifies the result code of the sort attempt and optionally the attribute type that caused the error.
  Not applicable, might or might 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. The response control specifies the result code of the sort attempt, LDAP_SUCCESS.
    The search 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.


Server-Side Sorting Control Sample Program for LDAP C SDK


The following sample program uses the server-side sorting control to get a list of all users in the directory. The list is sorted in ascending order by last name, then in descending order by first name.

#include <stdio.h>
#include "ldap.h"
/* Change these as needed. */
#define HOSTNAME "localhost"
#define PORTNUMBER 389
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. Use prldap_init() for IPv6. */
  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 With LDAP C SDK

The control OID 2.16.840.1.113730.3.4.3, LDAP_CONTROL_PERSISTENTSEARCH as defined in the ldap.h header file, is the persistent search control. A persistent search is an ongoing search operation that allows your LDAP client to get notification of changes to the directory.

The persistent search control is described in the Internet Draft Persistent Search: A Simple LDAP Change Notification Mechanism.

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. When an entry in the directory changes, the server determines if the entry matches the search criteria in your request. The server also determines if the change is the type of change that you are tracking. If both of these conditions are true, the server sends the entry to your client.

To create a persistent search control, call ldap_create_persistentsearch_control() as shown here.

int ldap_create_persistentsearch_control( LDAP *ld,
   int changetypes, int changesonly, int return_echg_ctls,
   char ctl_iscritical, LDAPControl **ctrlp );
  • changetypes specifies the type of change you want to track.
    • 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. Use 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. Use a non zero value to return entry change notification controls.

You can use this control in conjunction with an entry change notification control.

The ldap_create_persistentsearch_control() function passes back an LDAPControl structure that represents the control in the ctrlp parameter. You can add the newly created control to a NULL terminated array of LDAPControl structures. Pass this array to the ldap_search_ext() function.

To end the persistent search, call the ldap_abandon_ext() function. Alternatively, call the ldap_unbind() function to disconnect from the server.

The example provided in examples/psearch.c shows how to perform a persistent search.

Using the Entry Change Notification Control With LDAP C SDK

The control with OID 2.16.840.1.113730.3.4.7, 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 to the entry. The information includes the type of change, and the change number, which corresponds to an item in the servers change log. If the entry was renamed, the control also contains the old DN of the entry in the change log.

You use this control in conjunction with a persistent search control. You can specify the preference for returning entry change notification controls. The server then 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, follow this procedure.

To Use Entry Change Notification With LDAP C SDK

  1. Pass the LDAPMessage structure that represents an entry to the ldap_get_entry_controls() function.
  2. Pass the entry change notification control to the ldap_parse_entrychange_control() function.

Using the Virtual List View Control With LDAP C SDK

The control with OID 2.16.840.1.113730.3.4.9, 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 a server-side sorting control, the server should sort the results. The server should then return the specified subset of entries back to your client.

The virtual list view control is described in the Internet Draft, LDAP Extensions for Scrolling View Browsing of Search Results.

Using the Manage DSA IT Control With LDAP C SDK

The control with OID 2.16.840.1.113730.3.4.2, 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. 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 for 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 that contains the reference. This mechanism allows your client application to manage search references in the directory.

The manage DSA IT control is described in RFC 2891.

Using Password Policy Controls With LDAP C SDK

Directory Server offers three password policy response controls sent back to a client that performs a bind operation. The server also offers an account availability control that does not require a bind to return status about a client account.


Using Password Policy Expiration Controls With LDAP C SDK


  • The control with OID 2.16.840.1.113730.3.4.4, LDAP_CONTROL_PWEXPIRED, is the expired password control.
    This control serves when the server requires users to change passwords when first logging in, and after password reset. After the first login, and after password 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 users password. If the client requests any other operation, the server sends back an LDAP_UNWILLING_TO_PERFORM result code with an expired password control.
  • The control with OID 2.16.840.1.113730.3.4.5, LDAP_CONTROL_PWEXPIRING, 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 with a password that is to expire soon . The ldctl_value field of the LDAPControl structure specifies the number of seconds before the password expires.

To Use Password Policy Expiration Controls With LDAP C SDK

  1. Call ldap_simple_bind() to send a request for an asynchronous bind operation.
  2. Call ldap_result() to get the results of the operation.
  3. Call ldap_parse_result() to parse the result.
    The function retrieves the server response controls from the result as an array of LDAPControl structures.
  4. Check the ldctl_oid field to determine the OID of the control and the ldctl_value field for any data that is included in the control.

Using the Account Availability Control With LDAP C SDK

Directory Server offers an account availability control that does not require a bind to return status about a client account. The account availability control is assigned OID 1.3.6.1.4.1.42.2.27.9.5.8, LDAP_CONTROL_ACCOUNT_USABLE. This control allows the client to read information about an account without having to bind as the user having that account.

To Use the Account Availability Control With LDAP C SDK

  1. Allocate an LDAPuserstatus structure to hold the values for the account status.
  2. Create an account status control with ldap_create_userstatus_control().
  3. Read the entry for which you want account status, passing in the control as part of the search.
  4. Get the controls on the entry that the search returns.
  5. Pass the LDAPuserstatus structure to ldap_parse_userstatus_control() to fill the structure.
  6. Read account status information from the LDAPuserstatus structure.

This example displays status for Barbara Jensen's account.

/*
 * Get account status using the account status control.
 */

#include "examples.h"

int
main( int argc, char **argv )
{
    LDAPuserstatus  *status;
    int             version;
    LDAP            *ld;
    int             rc;
    LDAPControl     *status_ctrl = NULL;
    LDAPControl     *requestctrls[ 2 ];
    LDAPMessage     *result;
    char            *matched = NULL;
    char            *errmsg = NULL;
    char            **referrals;
    LDAPControl     **resultctrls = NULL;
    LDAPMessage     *msg;
    LDAPControl     **ectrls = NULL;

    /* Allocate the LDAPuserstatus structure. */
    if ( !( status = (LDAPuserstatus*)malloc(sizeof(LDAPuserstatus)) ) ) {
        perror("malloc");
        return ( 1 );
    }

    /* Use LDAPv3. */
    version = LDAP_VERSION3;
    if ( ldap_set_option( NULL, LDAP_OPT_PROTOCOL_VERSION, version )
         != 0 ) {
            fprintf( stderr,
                "ldap_set_option protocol version to %d failed\n",
                version );
            return ( 1 );
    }

    /* Get a handle to an LDAP connection. Use prldap_init() for IPv6. */
    if ( (ld = ldap_init( MY_HOST, MY_PORT )) == NULL ) {
        perror( "ldap_init" );
        return( 1 );
    }

    /* Create an account status control. */
    rc = ldap_create_userstatus_control( ld, 1, status_ctrl );
    if ( rc != LDAP_SUCCESS ) {
        fprintf( stderr, "ldap_create_userstatus_control: %s\n",
                 ldap_err2string( rc ) );
        ldap_unbind( ld );
        return( 1 );
    }
    requestctrls[ 0 ] = status_ctrl;
    requestctrls[ 1 ] = NULL;

    /* Authenticate to the directory as a user. */
    if ( ldap_simple_bind_s( ld, USER_DN, USER_PW ) != LDAP_SUCCESS ) {
        ldap_perror( ld, "ldap_simple_bind_s" );
        return( 1 );
    }

    /* Read the account entry using the control. */
    rc = ldap_search_ext_s( ld, ENTRYDN, LDAP_SCOPE_BASE,
        "(objectclass=*)", 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 );
    }

    /* Show the account status. */
    rc = ldap_parse_result( ld, result, rc, matched, errmsg,
        referrals, resultctrls, 0 );
    if ( rc != LDAP_SUCCESS ) {
        fprintf( stderr, "ldap_parse_result: %s\n", ldap_err2string( rc ) );
        ldap_unbind( ld );
        return( 1 );
    }

    for ( msg = ldap_first_message( ld, result );
          msg != NULL;
          msg = ldap_next_message ( ld, msg) ) {
        if ( ldap_msgtype( msg ) != LDAP_RES_SEARCH_ENTRY ) continue;
        if ( ldap_get_entry_controls( ld, msg, ectrls ) != LDAP_SUCCESS ) {
            ldap_perror ( ld, "ldap_get_entry_controls" );
        } else {
            rc = ldap_parse_userstatus_control( ld, ectrls, status );
            if ( rc != LDAP_SUCCESS ) {
                fprintf( stderr,
                    "ldap_parse_userstatus_control: %s\n",
                    ldap_err2string( rc ) );
            } else {
                printf( "DN: %s\n", ENTRYDN );
                if ( LDAP_US_ACCOUNT_USABLE == status->us_available ) {
                    printf( " Account is usable:\tY\n" );
                } else {
                    printf( " Account is usable:\tN\n" );
                }
                printf( " Password expires in:\t%ld s\n",
                    status->us_expire );
                if ( LDAP_US_ACCOUNT_INACTIVE == status->us_inactive ) {
                    printf( " Account is locked:\tY\n" );
                } else {
                    printf( " Account is locked:\tN\n" );
                }
                if ( LDAP_US_ACCOUNT_RESET == status->us_reset ) {
                    printf( " Password was reset:\tY\n" );
                } else {
                    printf( " Password was reset:\tN\n" );
                }
                if ( LDAP_US_ACCOUNT_EXPIRED == status->us_expired ) {
                    printf( " Password has expired:\tY\n" );
                } else {
                    printf( " Password has expired:\tN\n" );
                }
                printf( " Grace logins left:\t%d\n",
                    status->us_remaining );
                printf( " Account unlocks in:\t%d s\n",
                    status->us_seconds );
            }
        }
    }
    
    ldap_msgfree( result );
    ldap_control_free( status_ctrl );
    ldap_controls_free( resultctrls );
    ldap_unbind( ld );
    return( 0 );
}

Using the Password Policy Control With LDAP C SDK

Directory Server offers a password policy control to retrieve information about the password policy that applies to the account used to bind to the server. The password policy control is assigned OID 1.3.6.1.4.1.42.2.27.8.5.1, LDAP_CONTROL_PASSWD_POLICY.

To Use the Password Policy Control With LDAP C SDK

  1. Declare LDAPPasswordPolicyError, expiration time, and remaining grace login variables to hold the account status values.
  2. Create a password policy control with ldap_create_passwordpolicy_control().
  3. Bind sending the password policy control.
  4. Perform a bind, a modify, an add, a compare, or an extended operation, getting the result controls.
  5. Pass the LDAPPasswordPolicyError, expiration time, and remaining grace login variables to ldap_parse_passwordpolicy_control().
  6. Read password policy information from the variables, using ldap_passwordpolicy_err2txt() to extract textual information from the LDAPPasswordPolicyError.

This example displays the password policy that governs Barbara Jensen's account retrieved during the bind operation.

/*
 * Get password policy information using the password policy control.
 */

#include "examples.h"

int
main( int argc, char **argv )
{
    LDAPPasswordPolicyError   policy;
    ber_int_t                 expire;
    ber_int_t                 grace;
    int                       version;
    LDAP                      *ld;
    int                       rc;
    LDAPControl               *pwpctrl = NULL;
    LDAPControl               *requestctrls[ 2 ];
    int                       msgid;
    LDAPMessage               *result;
    int                       parse_rc;
    char                      *matched = NULL;
    char                      *errmsg = NULL;
    char                      **referrals;
    LDAPControl               **resultctrls = NULL;

    /* Use LDAPv3. */
    version = LDAP_VERSION3;
    if ( ldap_set_option( NULL, LDAP_OPT_PROTOCOL_VERSION, &version )
         != 0 ) {
            fprintf( stderr,
                "ldap_set_option protocol version to %d failed\n",
                version );
            return ( 1 );
    }

    /* Get a handle to an LDAP connection. */
    if ( (ld = ldap_init( MY_HOST, MY_PORT )) == NULL ) {
        perror( "ldap_init" );
        return( 1 );
    }

    /* Create a password policy control. */
    rc = ldap_create_passwordpolicy_control( ld, &pwpctrl);
    if ( rc != LDAP_SUCCESS ) {
        fprintf( stderr, "ldap_create_passwordpolicy_control: %s\n",
                 ldap_err2string( rc ) );
        ldap_unbind( ld );
        return( 1 );
    }
    requestctrls[ 0 ] = pwpctrl;
    requestctrls[ 1 ] = NULL;

    /* Use the password policy control for the bind. */
    rc = ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, &pwpctrl );
    if ( rc != LDAP_SUCCESS ) {
        ldap_perror( ld, "ldap_set_option" );
        return ( 1 );
    }

    /* Authenticate to the directory, checking for result controls. */
    msgid = ldap_simple_bind( ld, ENTRYDN, ENTRYPW );
    if ( msgid < 0 ) {
        fprintf( stderr, "ldap_simple_bind: %s\n", ldap_err2string( rc ) );
        if ( errmsg != NULL && errmsg != '\0' ) {
            fprintf( stderr, "%s\n", errmsg );
        }
        ldap_unbind( ld );
        return ( 1 );
    }

    rc = ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &result );
    if ( rc < 0 ) {
        rc = ldap_get_lderrno( ld, NULL, NULL );
        fprintf( stderr, "ldap_result: %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( rc ) );
        ldap_unbind( ld );
        return ( 1 );
    }
    if ( rc != LDAP_SUCCESS ) {
        fprintf( stderr, "ldap_simple_bind: %s\n", ldap_err2string( rc ) );
        if ( errmsg != NULL && errmsg != '\0' ) {
            fprintf( stderr, "%s\n", errmsg );
        }
    }
    if ( resultctrls == NULL ) {
        fprintf( stderr, "No pwp result control from server.\n" );
        ldap_unbind( ld );
        return ( 1 );
    }

    /* Show the password policy information. */
    parse_rc = ldap_parse_passwordpolicy_control( ld, *resultctrls,
        &expire, &grace, &policy );
    if ( parse_rc != LDAP_SUCCESS ) {
        fprintf( stderr, "ldap_parse_passwordpolicy_control: %s\n",
            ldap_err2string( rc ) );
        ldap_unbind( ld );
        return ( 1 );
    }

    printf( "DN: %s\n", ENTRYDN );
    printf( " Password expires in: %d s\n", (int)expire );
    printf( " Grace logins left: %d\n", (int)grace );
    printf(" %s\n", ldap_passwordpolicy_err2txt(policy));

    ldap_msgfree( result );
    ldap_control_free( pwpctrl );
    ldap_controls_free( resultctrls );
    ldap_unbind( ld );
    return( 0 );
}

Using the Proxied Authorization Control With LDAP C SDK

Proxied authorization is an extension to LDAP v3 that allows a bound client to assume the identity of another directory entity without rebinding. The rebind allows the client to perform operations as if it were bound as the proxied directory entity. All directory access, including read, write, search, compare, delete, and add operations, is supported by proxied authorization. For example, suppose a client is bound as uid=bjensen,ou=Engineering,dc=example,dc=com. The user 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, and lboyd grants proxy rights to bjensen. In this case, bjensen can 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 allow many clients to access directory data without rebinding as another directory entity might use this feature.


Proxy Right for LDAP C SDK


Proxied authorization adds an additional access right: proxy. If an entry grants the proxy right, then the entity to which that right is granted can assume the identity of the granting entity. For example, to allow uid=bjensen the right to proxy as uid=lboyd, add the Proxy Right access control instruction (ACI) as shown in the following example. This ACI allows bjensen to assume the identity of lboyd for all directory operations. The ACI gives bjensen permission to do to the directory whatever lboyd has permission to do.

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";)

Proxy Authorization Control With LDAP C SDK


To support proxy authorization, an extension to LDAP v3, the proxy authorization control has been added to 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. Proxy authorization might not be supported on all LDAP servers. You should call the proxy authorization control function only when interacting with LDAP servers that support this LDAP v3 extension. You can check on the support of this control by looking at the root DSE supportedControl attribute. For example, the following command uses the ldapsearch utility to display the root DSE:

$ ldapsearch -h localhost -p 389 -b "" -s base "(objectclass=*)"

For the control to work, the server to connect to must support the server control for Proxy Authorization, OID 2.16.840.1.113730.3.4.12. This control is LDAP_CONTROL_PROXYAUTH as defined in the ldap.h header file.


Proxy Authorization Sample Program for LDAP C SDK


The following sample program creates an LDAP connection, sets the Proxy Authorization control, binds to the directory, and then performs a search operation using the Proxy Authorization control.

#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. Use prldap_init() for IPv6. */
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 Proxy 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);
}

Using the Authorization Identity Bind Request Control With LDAP C SDK

The control with OID 2.16.840.1.113730.3.4.16, LDAP_CONTROL_AUTHZID_REQ, is the authorization identity bind request control. This control lets you request the authorization ID when binding to the server.

To Retrieve the Authorization ID

Directory Server supports the authorization identity bind request and response controls defined in RFC 3829. The server also allows you to retrieve the authorization identity value as a string.

  1. Create an authorization identity request control using the ldap_create_authzid_control() function.
  2. Bind sending the authorization identity request control.
  3. Read the authorization identity from the response control using the ldap_parse_authzid_control() function.

This example gets the authorization ID for Barbara Jensen.

/*
 * Get the authorization ID for an operation.
 */

#include "examples.h"

int
main( int argc, char **argv )
{
    int             version;
    LDAP            *ld;
    int             rc;
    LDAPControl     *authzidctrl = NULL;
    LDAPControl     *requestctrls[ 2 ];
    int             msgid;
    LDAPMessage     *result;
    int             parse_rc;
    char            *matched = NULL;
    char            *errmsg = NULL;
    char            **referrals;
    LDAPControl     **resultctrls = NULL;
    char            *authzid;

    /* Use LDAPv3. */
    version = LDAP_VERSION3;
    if ( ldap_set_option( NULL, LDAP_OPT_PROTOCOL_VERSION, version )
         != 0 ) {
            fprintf( stderr,
                "ldap_set_option protocol version to %d failed\n",
                version );
            return ( 1 );
    }

    /* Get a handle to an LDAP connection. Use prldap_init() for IPv6. */
    if ( (ld = ldap_init( MY_HOST, MY_PORT )) == NULL ) {
        perror( "ldap_init" );
        return( 1 );
    }

    /* Create a authorization ID control. */
    rc = ldap_create_authzid_control( ld, 1, authzidctrl );
    if ( rc != LDAP_SUCCESS ) {
        fprintf( stderr, "ldap_create_authzid_control: %s\n",
                 ldap_err2string( rc ) );
        ldap_unbind( ld );
        return( 1 );
    }
    requestctrls[ 0 ] = authzidctrl;
    requestctrls[ 1 ] = NULL;

    /* Use the authorization ID control for the bind. */
    rc = ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, authzidctrl );
    if ( rc != LDAP_SUCCESS ) {
        ldap_perror( ld, "ldap_set_option" );
        return ( 1 );
    }

    /* Authenticate to the directory, checking for result controls. */
    msgid = ldap_simple_bind( ld, ENTRYDN, ENTRYPW );
    if ( msgid  0 ) {
        fprintf( stderr, "ldap_simple_bind: %s\n", ldap_err2string( rc ) );
        if ( errmsg != NULL  errmsg != '\0' ) {
            fprintf( stderr, "%s\n", errmsg );
        }
        ldap_unbind( ld );
        return ( 1 );
    }

    rc = ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, result );
    if ( rc  0 ) {
        rc = ldap_get_lderrno( ld, NULL, NULL );
        fprintf( stderr, "ldap_result: %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( rc ) );
        ldap_unbind( ld );
        return ( 1 );
    }
    if ( rc != LDAP_SUCCESS ) {
        fprintf( stderr, "ldap_simple_bind: %s\n", ldap_err2string( rc ) );
        if ( errmsg != NULL  errmsg != '\0' ) {
            fprintf( stderr, "%s\n", errmsg );
        }
    }
    if ( resultctrls == NULL ) {
        fprintf( stderr, "No result control from server.\n" );
        ldap_unbind( ld );
        return ( 1 );
    }

    /* Show the authorization ID. */
    parse_rc = ldap_parse_authzid_control( ld, resultctrls, authzid );
    if ( parse_rc != LDAP_SUCCESS ) {
        fprintf( stderr, "ldap_parse_authzid_control: %s\n",
            ldap_err2string( rc ) );
        ldap_unbind( ld );
        return ( 1 );
    }

    printf( "DN:       %s\n", ENTRYDN );
    printf( "Authz ID: %s\n", authzid );

    ldap_msgfree( result );
    ldap_control_free( authzidctrl );
    ldap_controls_free( resultctrls );
    ldap_unbind( ld );
    return( 0 );
}

Using the Get Effective Rights Request Control With LDAP C SDK

The control with OID 1.3.6.1.4.1.42.2.27.9.5.2, LDAP_CONTROL_GETEFFECTIVERIGHTS_REQUEST, is the get effective rights request control. This control lets you request information about the effective access rights a user has, by performing a search for the aclRights and aclRightsInfo attributes.

To Get Effective Rights

  1. Create a get effective rights request control using the ldap_create_geteffectiveRights_control function.
  2. Perform a search with the control, requesting the aclRights and aclRightsInfo attributes.
  3. Read the values of the attributes for the effective rights information.

This example gets effective rights for Kirsten Vaughan.

/*
 * Get effective rights for another user.
 */

#include "examples.h"

int
main( int argc, char **argv )
{
    int             version;
    LDAP            *ld;
    int             rc;
    LDAPControl     *gerctrl = NULL;
    LDAPControl     *requestctrls[ 2 ];
    char            *authzid;
    char            **attrlist;
    LDAPMessage     *result;
    LDAPMessage     *entry;
    char            *attr;
    BerElement      *ber;
    char            **vals;
    int             i;

    /* Use LDAPv3. */
    version = LDAP_VERSION3;
    if ( ldap_set_option( NULL, LDAP_OPT_PROTOCOL_VERSION, version )
         != 0 ) {
            fprintf( stderr,
                "ldap_set_option protocol version to %d failed\n",
                version );
            return ( 1 );
    }

    /* Get a handle to an LDAP connection. Use prldap_init() for IPv6. */
    if ( (ld = ldap_init( MY_HOST, MY_PORT )) == NULL ) {
        perror( "ldap_init" );
        return( 1 );
    }

    /* Authenticate to the directory as a user. */
    if ( ldap_simple_bind_s( ld, USER_DN, USER_PW ) != LDAP_SUCCESS ) {
        ldap_perror( ld, "ldap_simple_bind_s" );
        return( 1 );
    }

    /* Create a get effective rights control. */
    authzid = "dn: uid=kvaughan,ou=people,dc=example,dc=com";
    if ( !( attrlist = (char**)malloc(sizeof(char * [ 2 ]) ) ) ) {
        perror( "malloc" );
        ldap_unbind( ld );
        return ( 1 );
    }
    attrlist[ 0 ] = "aclRights";
    attrlist[ 1 ] = NULL;
    rc = ldap_create_geteffectiveRights_control( ld, authzid,
        (const char **)attrlist, 1, gerctrl );
    if ( rc != LDAP_SUCCESS ) {
        fprintf( stderr, "ldap_create_geteffectiveRights_control: %s\n",
                 ldap_err2string( rc ) );
        ldap_unbind( ld );
        return( 1 );
    }
    requestctrls[ 0 ] = gerctrl;
    requestctrls[ 1 ] = NULL;

    /* Read an entry using the control. */
    rc = ldap_search_ext_s( ld, ENTRYDN, LDAP_SCOPE_BASE,
        "(objectclass=*)", attrlist, 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 );
    }

    /* Examine the entry for effective rights. */
    printf( "Bind DN:  %s\n", ENTRYDN );
    printf( "Authz ID: %s\n", authzid );
    printf( "***Rights***\n" );
    for ( entry = ldap_first_entry( ld, result );
          entry != NULL;
          entry = ldap_next_entry( ld, entry ) ) {
        for ( attr = ldap_first_attribute( ld, entry, ber );
              attr != NULL;
              attr = ldap_next_attribute ( ld, entry, ber) ) {
            if ( (vals = ldap_get_values( ld, entry, attr ) ) != NULL) {
                for ( i = 0; vals[i] != NULL; ++i ) {
                    printf( "%s: %s\n", attr, vals[i] );
                }
                ldap_value_free( vals );
            }
            ldap_memfree( attr );
        }
        if ( ber != NULL ) {
            ber_free( ber, 0 );
        }
    }
    printf( "\n" );

    ldap_msgfree( result );
    ldap_control_free( gerctrl );
    ldap_unbind( ld );
    return( 0 );
}

When you compile and run this sample program against Directory Server with a suffix that contains data from Example.ldif, the server produces output similar to the following. Lines are wrapped for readability.

Bind DN:  uid=bjensen, ou=People, dc=example,dc=com
Authz ID: dn: uid=kvaughan,ou=people,dc=example,dc=com
***Rights***
aclRights;entryLevel: add:1,delete:1,read:1,write:1,proxy:0
aclRights;attributeLevel;: search:1,read:1,compare:1,write:1,
 selfwrite_add:1,selfwrite_delete:1,proxy:0
aclRights;attributeLevel;dn: uid=kvaughan,ou=people,dc=example,dc=com:
 search:1,read:1,compare:1,write:1,selfwrite_add:1,selfwrite_delete:1,
 proxy:0
aclRights;attributeLevel;@c : search:1,read:1,compare:1,write:1,
 selfwrite_add:1,selfwrite_delete:1,proxy:0

See your server documentation for information about aclRights and aclRightsInfo values.

Using the Real Attributes Only Request Control With LDAP C SDK

The control with OID 2.16.840.1.113730.3.4.17, LDAP_CONTROL_REAL_ATTRS_ONLY, is the real attributes only request control. This control lets you convey to the server to return only real attributes, attributes that are stored by the directory, during a search.

To Retrieve Only Real Attributes

  1. Create an LDAPControl structure with the OID defined using LDAP_CONTROL_REAL_ATTRS_ONLY.
  2. Pass the control in to the server with the search request.
  3. Free the control when finished.

This example relies on sample data from Example-roles.ldif.

/*
 * Use the control to get only real attributes.
 * First load suffix data from Example-roles.ldif.
 */

#include "examples.h"

int
main( int argc, char **argv )
{
    LDAPControl     *ctrl = NULL;
    LDAPControl     *requestctrls[ 2 ];
    char            **attrlist;
    int             version;
    LDAP            *ld;
    char            *target;
    int             rc;
    LDAPMessage     *result;
    LDAPMessage     *entry;
    char            *dn;
    char            *attr;
    BerElement      *ber;
    char            **vals;
    int             i;

    /* Prepare a real attributes only request control. */
    if ( !(ctrl = (LDAPControl *)malloc(sizeof(LDAPControl))) ) {
        perror( "malloc" );
        return( 1 );
    }
    ctrl->ldctl_oid = strdup( LDAP_CONTROL_REAL_ATTRS_ONLY );
    ctrl->ldctl_iscritical = 1;
    requestctrls[ 0 ] = ctrl;
    requestctrls[ 1 ] = NULL;

    /* Create a list of attributes to retrieve. */
    if ( !( attrlist = (char**)malloc(sizeof(char * [ 3 ]) ) ) ) {
        perror( "malloc" );
        return ( 1 );
    }
    attrlist[ 0 ] = "cn";               /* Real attribute */
    attrlist[ 1 ] = "nsrole";           /* Virtual attribute */
    attrlist[ 2 ] = NULL;

    /* Use LDAPv3. */
    version = LDAP_VERSION3;
    if ( ldap_set_option( NULL, LDAP_OPT_PROTOCOL_VERSION, version )
         != 0 ) {
        fprintf( stderr,
                 "ldap_set_option protocol version to %d failed\n",
                 version );
        return ( 1 );
    }

    /* Get a handle to an LDAP connection. Use prldap_init() for IPv6. */
    if ( (ld = ldap_init( MY_HOST, MY_PORT )) == NULL ) {
        perror( "ldap_init" );
        return( 1 );
    }

    /* Authenticate to the directory to read an entry. */
    if ( ldap_simple_bind_s( ld, USER_DN, USER_PW ) != LDAP_SUCCESS ) {
        ldap_perror( ld, "ldap_simple_bind_s" );
        return( 1 );
    }

    /* Read an entry using the control. */
    target = "uid=kvaughan,ou=people,dc=example,dc=com";
    rc = ldap_search_ext_s( ld, target, LDAP_SCOPE_BASE, "(objectclass=*)",
        attrlist, 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 );
    }

    /* Examine the results. */
    for ( entry = ldap_first_entry( ld, result );
          entry != NULL;
          entry = ldap_next_entry( ld, entry ) ) {
        if ( (dn = ldap_get_dn( ld, entry )) != NULL ) {
            printf( "dn: %s\n", dn );
            ldap_memfree( dn );
        }
        for ( attr = ldap_first_attribute( ld, entry, ber );
              attr != NULL;
              attr = ldap_next_attribute ( ld, entry, ber) ) {
            if ( (vals = ldap_get_values( ld, entry, attr ) ) != NULL) {
                for ( i = 0; vals[i] != NULL; ++i ) {
                    printf( "%s: %s\n", attr, vals[i] );
                }
                ldap_value_free( vals );
            }
            ldap_memfree( attr );
        }
        if ( ber != NULL ) {
            ber_free( ber, 0 );
        }
    }
    printf( "\n" );

    ldap_msgfree( result );
    ldap_control_free( ctrl );
    ldap_unbind( ld );
    return( 0 );
}

When you compile and run this sample program against Directory Server with a suffix that contains data from Example-roles.ldif, the server produces output similar to this:

dn: uid=kvaughan, ou=People, dc=example,dc=com
cn: Kirsten Vaughan

Using the Virtual Attributes Only Request Control With LDAP C SDK

The control with OID 2.16.840.1.113730.3.4.19, LDAP_CONTROL_VIRTUAL_ATTRS_ONLY, is the virtual attributes only request control. This control lets you convey to the server to return only virtual attributes during a search. Virtual attribute values are not stored by the directory, but instead are generated on request.

To Retrieve Only Virtual Attributes

  1. Create an LDAPControl structure with the OID defined using LDAP_CONTROL_VIRTUAL_ATTRS_ONLY.
  2. Pass the control in to the server with the search request.
  3. Free the control when finished.

This example relies on sample data from Example-roles.ldif.

/*
 * Use the control to get only virtual attributes.
 * First load suffix data from Example-roles.ldif.
 */

#include "examples.h"

int
main( int argc, char **argv )
{
    LDAPControl     *ctrl = NULL;
    LDAPControl     *requestctrls[ 2 ];
    char            **attrlist;
    int             version;
    LDAP            *ld;
    char            *target;
    int             rc;
    LDAPMessage     *result;
    LDAPMessage     *entry;
    char            *dn;
    char            *attr;
    BerElement      *ber;
    char            **vals;
    int             i;

    /* Prepare a virtual attributes only request control. */
    if ( !(ctrl = (LDAPControl *)malloc(sizeof(LDAPControl))) ) {
        perror( "malloc" );
        return( 1 );
    }
    ctrl->ldctl_oid = strdup( LDAP_CONTROL_VIRTUAL_ATTRS_ONLY );
    ctrl->ldctl_iscritical = 1;
    requestctrls[ 0 ] = ctrl;
    requestctrls[ 1 ] = NULL;

    /* Create a list of attributes to retrieve. */
    if ( !( attrlist = (char**)malloc(sizeof(char * [ 3 ]) ) ) ) {
        perror( "malloc" );
        return ( 1 );
    }
    attrlist[ 0 ] = "cn";               /* Real attribute */
    attrlist[ 1 ] = "nsrole";           /* Virtual attribute */
    attrlist[ 2 ] = NULL;

    /* Use LDAPv3. */
    version = LDAP_VERSION3;
    if ( ldap_set_option( NULL, LDAP_OPT_PROTOCOL_VERSION, version )
         != 0 ) {
        fprintf( stderr,
                 "ldap_set_option protocol version to %d failed\n",
                 version );
        return ( 1 );
    }

    /* Get a handle to an LDAP connection. Use prldap_init() for IPv6. */
    if ( (ld = ldap_init( MY_HOST, MY_PORT )) == NULL ) {
        perror( "ldap_init" );
        return( 1 );
    }

    /* Authenticate to the directory to read an entry. */
    if ( ldap_simple_bind_s( ld, USER_DN, USER_PW ) != LDAP_SUCCESS ) {
        ldap_perror( ld, "ldap_simple_bind_s" );
        return( 1 );
    }

    /* Read an entry using the control. */
    target = "uid=kvaughan,ou=people,dc=example,dc=com";
    rc = ldap_search_ext_s( ld, target, LDAP_SCOPE_BASE, "(objectclass=*)",
        attrlist, 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 );
    }

    /* Examine the results. */
    for ( entry = ldap_first_entry( ld, result );
          entry != NULL;
          entry = ldap_next_entry( ld, entry ) ) {
        if ( (dn = ldap_get_dn( ld, entry )) != NULL ) {
            printf( "dn: %s\n", dn );
            ldap_memfree( dn );
        }
        for ( attr = ldap_first_attribute( ld, entry, ber );
              attr != NULL;
              attr = ldap_next_attribute ( ld, entry, ber) ) {
            if ( (vals = ldap_get_values( ld, entry, attr ) ) != NULL) {
                for ( i = 0; vals[i] != NULL; ++i ) {
                    printf( "%s: %s\n", attr, vals[i] );
                }
                ldap_value_free( vals );
            }
            ldap_memfree( attr );
        }
        if ( ber != NULL ) {
            ber_free( ber, 0 );
        }
    }
    printf( "\n" );

    ldap_msgfree( result );
    ldap_control_free( ctrl );
    ldap_unbind( ld );
    return( 0 );
}

When you compile and run this sample program against Directory Server with a suffix that contains data from Example-roles.ldif, the server produces output similar to this:

dn: uid=kvaughan, ou=People, dc=example,dc=com
nsrole: cn=directory administrators,dc=example,dc=com
nsrole: cn=hr managers,dc=example,dc=com