<!--

Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved.
Portions copyright 1999 Netscape Communications Corporation. All
Rights Reserved.

The contents of this document are subject to the terms of the
Creative Commons Attribution-ShareAlike 2.5 license or any later
version (the "License"). You may not use this document except in
compliance with the License.

See the License for the specific language governing
permissions and limitations under the License. You can obtain
a copy of the License at
http://creativecommons.org/licenses/by-sa/2.5/legalcode.

-->
<chapter id="csdk-extop"><title>Extended Operations With &DirectorySDKForC;</title>
<indexterm>
<primary>C SDK</primary>
<secondary>extended operations</secondary>
</indexterm><highlights>
<para>This chapter explains LDAP v3 extended operations. It also explains
how to use the extended operations supported by your LDAP server.</para>
<itemizedlist>
<para>This chapter covers the following topics:</para>
<listitem><para><olink targetptr="bdajl">How Extended Operations Work With
Directory SDK for C</olink></para></listitem>
<listitem><para><olink targetptr="bdajm">Determining the Extended Operations
Supported With Directory SDK for C</olink></para></listitem>
<listitem><para><olink targetptr="bdajn">Performing an Extended Operation
With Directory SDK for C</olink></para></listitem>
<listitem><para><olink targetptr="extendop-pwdmod">Performing an LDAP Password
Modify Extended Operation With Directory SDK for C</olink></para></listitem>
<listitem><para><olink targetptr="extendedop-starttls">Using Start TLS With
Directory SDK for C</olink></para></listitem>
<listitem><para><olink targetptr="extendop-whoami">Performing a Who Am I?
Extended Operation With Directory SDK for C</olink></para></listitem>
</itemizedlist>
</highlights>
<sect1 id="bdajl"><title>How Extended Operations Work With Directory SDK for
C</title>
<para>Extended operations are part of LDAP v3. Each extended operation is
identified by an object identifier (OID). LDAP clients can request the operation
by sending an extended operation request.</para>
<itemizedlist>
<para>Within the request, the client specifies the following:</para>
<listitem><para>The OID of the extended operation that should be performed</para>
</listitem>
<listitem><para>Data specific to the extended operation</para></listitem>
</itemizedlist>
<para>The server receives the request, then performs the extended operation.
The server sends back a response to the client that contains an OID, and any
additional data.</para>
<para>To use extended operations, both the server and the client must understand
the specific extended operation to be performed. From the LDAP server perspective, &cnDirectoryServer; supports
a server plug-in interface that you can use to add support for extended operations.
</para></sect1>
<sect1 id="bdajm"><title>Determining the Extended Operations Supported With &DirectorySDKForC;</title>
<para>To determine the extended operations supported by the server, get the
root DSE of the server, and check the <literal>supportedExtension</literal> attribute.
The values of this attribute are the OIDs of the extended operations supported
by this server. If the root DSE does not have a <literal>supportedExtension</literal> attribute,
the server does not support any extended operations. See <olink targetptr="bdahv">
To Get the Root DSE</olink> for instructions on reading that entry.</para>
</sect1>
<sect1 id="bdajn"><title>Performing an Extended Operation With &DirectorySDKForC;</title>
<itemizedlist>
<para>To perform an extended operation, for which no specialized API exists,
call one of the following functions:</para>
<listitem><para>The synchronous <function>ldap_extended_operation_s</function> function
</para></listitem>
<listitem><para>The asynchronous <function>ldap_extended_operation</function> function
</para></listitem>
</itemizedlist>
<para>Both of these functions allow you to specify the OID of the extended
operation and the data that you want applied to the operation.</para>
<para>Before calling the function to perform an LDAP extended operation, make
sure to specify that your client is using version 3 of LDAP. If you do not,
an <errorcode>LDAP_NOT_SUPPORTED</errorcode> result code is returned. For
details, see <olink targetptr="bdaci">Specifying the LDAP Version of Your
Client</olink>.</para>
<sect2 id="bdajo"><title>Synchronous Extended Operation</title>
<para>If you want to wait for the results of an LDAP extended operation to
complete before continuing, call the synchronous <function>ldap_extended_operation_s
</function> function. This function sends a SASL bind request to the server.
The server blocks other work until the server sends the results of the operation
back to your client.</para>
<para><function>ldap_extended_operation_s</function> returns <errorcode>LDAP_SUCCESS
</errorcode> if the operation completed successfully, or an error code if
a problem occurred. See the documentation for the <olink targetptr="bdarv">ldap_extended_operation_s
</olink> function for a list of the possible result codes.</para></sect2>
<sect2 id="bdajp"><title>Asynchronous Extended Operation</title>
<para>If you want to perform other work in parallel while waiting for an LDAP
extended operation to complete, perform the following procedure.</para>
<task><title>To Perform an Asynchronous Extended Operation</title>
<procedure>
<step><para>Call the asynchronous <function>ldap_extended_operation</function> function
to send an LDAP extended operation request.</para><para>This function returns
an <errorcode>LDAP_SUCCESS</errorcode> result code if the request was successfully
sent, or an LDAP result code if an error occurred while sending the request.
The function also sets the <literal>msgidp</literal> argument to point to
a message ID identifying the extended operation. To determine whether the
server sent a response to your client for this operation, call the <function>ldap_result
</function> function and pass in this message ID. The function passes back
the response in an <structname>LDAPMessage</structname> structure.</para></step>
<step><para>Call the <function>ldap_parse_extended_result</function> function
to parse the <structname>LDAPMessage</structname> structure and retrieve information
from the server&rsquo;s response.</para><para>If the server sent an OID of
an extended operation to your client, the OID is passed back as the <literal>retoidp
</literal> argument. If the server sent data to your client, the data is specified
in the <structname>berval</structname> structure passed back as the <literal>retdatap
</literal> argument.</para></step>
<step><para>Call the <function>ldap_get_lderrno</function> function to get
the LDAP result code for the operation.</para><para>The function returns an <errorcode>
LDAP_SUCCESS</errorcode> result code if the extended operation was performed
successfully, or an LDAP error code if a problem occurred. See the documentation
for the <olink targetptr="bdaru">ldap_extended_operation</olink> function
for a list of result codes that the server can return for this operation.</para>
</step>
</procedure>
<example id="csdk-extop-example">
<title>Perform an Asynchronous Extended Operation</title>
<para>This example client requests an asynchronous extended operation from
the server with OID <literal>1.2.3.4</literal>.</para>
<programlisting>#include &lt;stdio.h>
#include &lt;stdlib.h>
#include &lt;string.h>
#include &lt;time.h>
#include "ldap.h"

/* Name and port of the LDAP server you want to connect to. */
#define MY_HOST "localhost"
#define MY_PORT  389
/* DN of user (and password of user) who you want to authenticate as */
#define MGR_DN  "cn=Directory Manager"
#define MGR_PW  "23skidoo"
int
main( int argc, char **argv )
{
    /* OID of the extended operation that you are requesting */
    const char     *oidrequest = "1.2.3.4";
    char           *oidresult;
    struct berval  valrequest;
    struct berval  *valresult;
    LDAP           *ld;
    int rc, version;
    /* Set up the value that you want to pass to the server */
    printf( "Setting up value to pass to server...\n" );
    valrequest.bv_val = "My Value";
    valrequest.bv_len = strlen( "My Value" );
    /* Get a handle to an LDAP connection. Use prldap_init() for IPv6. */
    printf( "Getting the handle to the LDAP connection...\n" );
    if ( (ld = ldap_init( MY_HOST, MY_PORT )) == NULL ) {
  perror( "ldap_init" );
  ldap_unbind( ld );
  return( 1 );
    }
    /* Set the LDAP protocol version supported by the client
       to 3. (By default, this is set to 2. Extended operations
       are part of version 3 of the LDAP protocol.) */
    ldap_get_option( ld, LDAP_OPT_PROTOCOL_VERSION, &amp;version );
    printf( "Resetting version %d to 3.0...\n", version );
    version = LDAP_VERSION3;
    ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &amp;version );
    /* Authenticate to the directory as the Directory Manager */
    printf( "Binding to the directory...\n" );
    if ( ldap_simple_bind_s( ld, MGR_DN, MGR_PW ) != LDAP_SUCCESS ) {
  ldap_perror( ld, "ldap_simple_bind_s" );
  ldap_unbind( ld );
  return( 1 );
    }
    /* Initiate the extended operation */
    printf( "Initiating the extended operation...\n" );
    if ( ( rc = ldap_extended_operation_s( ld, oidrequest, &amp;valrequest,
                  NULL, NULL, &amp;oidresult, &amp;valresult ) ) !=
         LDAP_SUCCESS ) {
  ldap_perror( ld, "ldap_extended_operation failed: " );
  ldap_unbind( ld );
  return( 1 );
    }
    /* Get the OID and the value from the result returned by the server. */
    printf( "Operation successful.\n" );
    printf( "\tReturned OID: %s\n", oidresult );
    printf( "\tReturned value: %s\n", valresult->bv_val );
    /* Disconnect from the server. */
    ldap_unbind( ld );
    return 0;
}</programlisting>
</example>
</task>
</sect2>
</sect1>
<sect1 id="extendop-pwdmod"><title>Performing an LDAP Password Modify Extended
Operation With &DirectorySDKForC;</title>
<indexterm>
<primary>C SDK</primary>
<secondary>extended operations</secondary>
<tertiary>password modify</tertiary>
</indexterm>
<para><ulink url="http://www.ietf.org/rfc/rfc3062.txt" type="text_url">RFC
3062</ulink>, <citetitle>LDAP Password Modify Extended Operation</citetitle>,
describes the extended operation, which is particularly useful for changing
expired passwords.</para>
<task><title>To Perform an LDAP Password Modify Extended Operation</title>
<procedure>
<step><para>Get a connection to the directory that uses LDAP version 3.</para>
</step>
<step><para>Authenticate to the directory.</para>
<stepalternatives>
<step><para>As an administrator, to be able to reset an expired user password
if you do not have the old password</para></step>
<step><para>Anonymously to reset an expired password if you have the old password
</para></step>
<step><para>As the user herself to change the password that has not yet expired</para>
</step></stepalternatives>
</step>
<step><para>Modify the password with the synchronous function <function>ldap_passwd_s
</function> or the asynchronous function <function>ldap_passwd</function> and
use <function>ldap_parse_passwd</function> or <function>ldap_parse_passwd_result</function> to
examine the results.</para></step>
</procedure>
<example>
<title>Performing an LDAP Password Modify Extended Operation</title>
<para>This example changes a password using <function>ldap_passwd_s</function>.</para>
<programlisting>/*
 * Use the password modify extended operation to change a password.
 */

#include "examples.h"

int
main( int argc, char **argv )
{
    int             version;
    LDAP            *ld;
    char            *target;
    int             rc;
    struct berval   userid;
    struct berval   oldpasswd;
    struct berval   newpasswd;
    struct berval   genpasswd;

    /* Use LDAPv3. */
    version = LDAP_VERSION3;
    if ( ldap_set_option( NULL, LDAP_OPT_PROTOCOL_VERSION, &amp;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. */
    if ( ldap_simple_bind_s( ld, ENTRYDN, ENTRYPW ) != LDAP_SUCCESS ) {
        ldap_perror( ld, "ldap_simple_bind_s" );
        return( 1 );
    }

    /* Change the password using the extended operation. */
    userid.bv_val = ENTRYDN;
    userid.bv_len = strlen(userid.bv_val);

    oldpasswd.bv_val = ENTRYPW;
    oldpasswd.bv_len = strlen(oldpasswd.bv_val);

    newpasswd.bv_val = "ChangeMe!";
    newpasswd.bv_len = strlen(newpasswd.bv_val);

    rc = ldap_passwd_s(
        ld, &amp;userid, &amp;oldpasswd, &amp;newpasswd, &amp;genpasswd, NULL, NULL );
    if ( rc != LDAP_SUCCESS ) {
        fprintf( stderr, "ldap_passwd_s: %s\n", ldap_err2string( rc ) );
        ldap_unbind( ld );
        return( 1 );
    } else {
        printf( "Successfully changed password for %s\n", userid.bv_val );
    }

    ldap_unbind( ld );
    return( 0 );</programlisting>
<para>When you compile and run this sample program against &cnDirectoryServer; with
a suffix that contains data from <filename>Example.ldif</filename>, the server
produces output similar to this:</para>
<screen>Successfully changed password for uid=bjensen, ou=People, dc=example,dc=com
</screen>
</example>
</task>
</sect1>
<sect1 id="extendedop-starttls"><title>Using Start TLS With &DirectorySDKForC;</title>
<indexterm>
<primary>C SDK</primary>
<secondary>extended operations</secondary>
<tertiary>start TLS</tertiary>
</indexterm>
<para><ulink url="http://www.ietf.org/rfc/rfc4513.txt" type="text_url">RFC
4513</ulink>, <citetitle>Lightweight Directory Access Protocol (LDAP): Authentication
Methods and Security Mechanisms</citetitle>, describes the extended operation.
Start TLS allows you to connect on a non secure port, and then request transport
layer security.</para>
<para>See <olink targetptr="ssl-start-tls">Starting Transport Layer Security
With Directory SDK for C</olink> for an example of how to use Start TLS.</para>
</sect1>
<sect1 id="extendop-whoami"><title>Performing a Who Am I? Extended Operation
With &DirectorySDKForC;</title>
<indexterm>
<primary>C SDK</primary>
<secondary>extended operations</secondary>
<tertiary>Who Am I?</tertiary>
</indexterm>
<para>The &ldquo;Who am I?&rdquo; extended operation allows you to retrieve
the authorization identity that is associated with a connection.</para>
<para>This method can involve less code than the standard authorization identity
controls that are described in <olink targetptr="controls-authzid">Using the
Authorization Identity Bind Request Control With Directory SDK for C</olink>.</para>
<task><title>To Perform a Who Am I? Extended Operation</title>
<procedure>
<step><para>Get a connection to the directory that uses LDAP version 3.</para>
</step>
<step><para>Use <function>ldap_whoami</function> and <function>ldap_parse_whoami_result
</function>, or use <function>ldap_whoami_s</function>, to retrieve the authorization
identity.</para></step>
</procedure>
<example>
<title>Getting Authorization Identity</title>
<para>This example retrieves authorization identity by using <function>ldap_whoami_s
</function>.</para>
<programlisting>/*
 * Use the Who Am I? extended operation.
 */

#include "examples.h"

int
main( int argc, char **argv )
{
    int             version;
    LDAP            *ld;
    int             rc;
    char            *authzid;

    /* Use LDAPv3. */
    version = LDAP_VERSION3;
    if ( ldap_set_option( NULL, LDAP_OPT_PROTOCOL_VERSION, &amp;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. */
    if ( ldap_simple_bind_s( ld, ENTRYDN, ENTRYPW ) != LDAP_SUCCESS ) {
        ldap_perror( ld, "ldap_simple_bind_s" );
        return( 1 );
    }

    /* Examine my authorization ID. */
    rc = ldap_whoami_s( ld, NULL, NULL, &amp;authzid );
    if ( rc != LDAP_SUCCESS ) {
        fprintf( stderr, "ldap_whoami_s: %s\n", ldap_err2string( rc ) );
        ldap_unbind( ld );
        return( 1 );
    }
    printf( "Authorization ID: %s\n", authzid );

    ldap_unbind( ld );
    return( 0 );
}</programlisting>
<para>When you compile and run this sample program against &cnDirectoryServer;<?Pub
Caret> with a suffix that contains data from <filename>Example.ldif</filename>,
the server produces output similar to this:</para>
<screen>Authorization ID: dn:uid=bjensen,ou=people,dc=example,dc=com</screen>
</example>
</task>
</sect1>
</chapter>
