Sun Java logo     Previous      Contents      Index      Next     

Sun logo
Sun Java(TM) System Directory Server 5 2004Q2 Plug-In Developer's Guide 

Chapter 6
Handling Authentication

This chapter explains how to write a plug-in that adds to, bypasses, or replaces the authentication mechanisms supported for Directory Server 5.2. It demonstrates use of existing mechanisms for authentication. You must adapt the code examples to meet your particular authentication requirements.


Caution

The examples alone do not provide secure authentication methods.


This chapter contains the following sections:


How Authentication Works

This section identifies authentication methods available and describes how Directory Server handles authentication to identify clients. Consider the Directory Server model described here when writing plug-ins to modify the mechanism.

Support for Standard Methods

Directory Server supports the two authentication methods described in RFC 2251: simple authentication, rendered more secure through the use of Secure Socket Layer (SSL) for transport; and Simple Authentication and Security Layer (SASL). RFC 2222, Simple Authentication and Security Layer (SASL), describes the technology. Through SASL, Directory Server supports Kerberos authentication to the LDAP server and the Directory System Agent (DSA, an X.500 term) as described in RFC 1777, Lightweight Directory Access Protocol.

Identifying Clients During the Bind

For LDAP clients, Directory Server keeps track of client identity through the DN the client used to connect, and through the authentication method and external credentials the client uses to connect. The parameter block holds the relevant client connection information. The DN can be accessed through the SLAPI_CONN_DN and SLAPI_CONN_AUTHTYPE parameters to slapi_pblock_set() and slapi_pblock_get().

For DSML clients connecting over HTTP, Directory Server performs identity mapping for the bind as described in the Directory Server Administration Guide. As a result, plug-ins have the same view of the client bind, regardless of the front end protocol.

Bind Processing in Directory Server

Much happens before Directory Server calls a pre-operation bind plug-in. For example, Directory Server completes authentication for anonymous binds and binds by the Directory Manager and replication users before calling pre-operation bind functions, thus completing the bind without calling the plug-in.


Note

For SASL authentication mechanisms, pre-operation and post-operation bind functions may be called several times during processing of a single authentication request.

This happens because multiple LDAP bind operations may be used to implement the authentication mechanism, as is the case for DIGEST-MD5, for example.


To process the bind, Directory Server:

  1. Parses the bind request
  2. Determines the authentication method
  3. Determines whether the bind DN is handled locally
  4. Adds request information to the parameter block
  5. Determines whether to handle the bind in the front end or to call pre-operation bind plug-in functions
  6. Performs the bind or not, using information about the bind DN entry from the server back end

The rest of this section provides more detail for each step.

Parsing the Bind Request

When a bind request arrives, Directory Server retrieves the DN, the authentication method used, and any credentials sent such as a password. This may involve a mapping if the client accesses the server using DSML over HTTP. If the request calls for SASL authentication (LDAP_AUTH_SASL), Directory Server retrieves the SASL mechanism. Next, Directory Server normalizes the DN retrieved from the request. It also retrieves any LDAP v3 controls included in the request.

Determining the Authentication Method

If the method is simple authentication, and the DN or credentials are empty or missing, Directory Server assumes the client is binding anonymously, sets SLAPI_CONN_DN to NULL and SLAPI_CONN_AUTHTYPE to SLAPD_AUTH_NONE, and sends an LDAP_SUCCESS result to the client. If the method is SASL authentication, Directory Server first determines whether the mechanism is supported. If not, it sends an LDAP_AUTH_METHOD_NOT_SUPPORTED result to the client.

Determining Whether the DN Is Handled Locally

If the DN retrieved is not stored in the local instance, and Directory Server is configured for default referral, it sends an LDAP_REFERRAL result to the client. Otherwise, it sends an LDAP_NO_SUCH_OBJECT result to the client.

Adding Request Information to the Parameter Block

Directory Server puts bind request information into the parameter block:

Determining Whether to Handle the Bind or Call a Plug-In

Directory Server authenticates bind requests from the Directory Manager (root user whose DN is specified as the value of nsslapd-rootdn) or the replication manager (DN used for binds related to replication), sending an LDAP_SUCCESS result on success or an LDAP_INVALID_CREDENTIALS result on failure.

After completing all this work, the server calls pre-operation bind functions.

Directory Server does not call pre-operation bind functions for bind requests from the directory manager, the replication manager, nor for anonymous binds.

Performing the Backend Bind

Assuming any pre-operation bind functions called return 0, Directory Server completes the bind operation, checking the bind against the information in the directory database, setting SLAPI_CONN_DN and SLAPI_CONN_AUTHTYPE appropriately. If necessary, Directory Server sends a “password expiring” control (OID 2.16.840.1.113730.3.4.5) result to the client. It always sends an LDAP_SUCCESS result on success or some other (non-zero) result on failure.


How a Plug-In Can Modify Authentication

A pre-operation bind function can modify Directory Server authentication in one of two ways. The plug-in either:

Bypassing Authentication

If the plug-in bypasses the comparison of authentication information in the client request to authentication information stored in the directory, then the plug-in returns a non-zero value, such as 1, to prevent the server from completing the bind after the pre-operation bind function returns. You may opt for this solution if you store all authentication information outside the directory, and for some reason cannot adequately map to information available in the directory through LDAP or the plug-in API. In addition to the other testing and validation performed on the plug-in, make sure you verify that it works well with server access control mechanisms.

Refer to Developing a Simple Authentication Plug-In for an example.

Using Custom SASL Mechanisms

If the plug-in implements a custom SASL mechanism then clients using the mechanism must support it as well. Refer to the documentation for the Directory SDK for C for example for details on developing client applications to support specific bind requirements. An SDK for the Java language is also available.

Refer to Developing a SASL Authentication Plug-In for an example.


Developing a Simple Authentication Plug-In

This section shows how a pre-operation bind plug-in can use the plug-in API to authenticate a user. The example discussed here obtains the appropriate bind information from the parameter block, then handles the authentication if the method requested is LDAP_AUTH_SIMPLE, but allows the server to continue the bind if the authentication succeeds.

Notice some binds are performed by the front end before pre-operation bind functions are called. Refer to Determining the Authentication Method and Determining Whether to Handle the Bind or Call a Plug-In for details.

Finding the Example

Code Example 6-1 shows the code excerpt from the source file ServerRoot/plugins/slapd/slapi/examples/testbind.c.

Code Example 6-1  Pre-Operation Bind Function (testbind.c

#include "slapi-plugin.h"

int

test_bind(Slapi_PBlock * pb)

{

    char          *  dn;               /* Target DN                 */

    int              method;           /* Authentication method     */

    struct berval *  credentials;      /* Client SASL credentials   */

    Slapi_DN      *  sdn      = NULL;  /* DN used in internal srch  */

    char          *  attrs[2] = {      /* Look at userPassword only */

                                SLAPI_USERPWD_ATTR,

                                NULL

                     };

    Slapi_Entry   *  entry    = NULL;  /* Entry returned by srch    */

    Slapi_Attr    *  attr     = NULL;  /* Pwd attr in entry found   */

    int              is_repl;          /* Is this replication?      */

    int              is_intl;          /* Is this an internal op?   */

    int              connId, opId, rc = 0;

    long             msgId;

        

    /* Obtain the bind information from the parameter block.        */

    rc |= slapi_pblock_get(pb, SLAPI_BIND_TARGET,             &dn);

    rc |= slapi_pblock_get(pb, SLAPI_BIND_METHOD,             &method);

    rc |= slapi_pblock_get(pb, SLAPI_BIND_CREDENTIALS,        &credentials);

    rc |= slapi_pblock_get(pb, SLAPI_OPERATION_MSGID,         &msgId);

    rc |= slapi_pblock_get(pb, SLAPI_CONN_ID,                 &connId);

    rc |= slapi_pblock_get(pb, SLAPI_OPERATION_ID,            &opId);

    rc |= slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_repl);

    rc |= slapi_pblock_get(pb, SLAPI_IS_INTERNAL_OPERATION,   &is_intl);

    if (rc != 0) {

        slapi_log_info_ex(

            SLAPI_LOG_INFO_AREA_PLUGIN,

            SLAPI_LOG_INFO_LEVEL_DEFAULT,

            SLAPI_LOG_NO_MSGID,

            SLAPI_LOG_NO_CONNID,

            SLAPI_LOG_NO_OPID,

            "test_bind in test-bind plug-in",

            "Could not get parameters for bind operation (error %d).\n", rc

        );

        slapi_send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL);

        return (LDAP_OPERATIONS_ERROR);/* Server aborts bind here.  */

    }

    /* The following code handles simple authentication, where a

     * user offers a bind DN and a password for authentication.

     *

     * Handling simple authentication is a matter of finding the

     * entry corresponding to the bind DN sent in the request,

     * then if the entry is found, checking whether the password

     * sent in the request matches a value found on the

     * userPassword attribute of the entry.                         */

    /* Avoid interfering with replication or internal operations.   */

    if (!is_repl && !is_intl) switch (method) {

    case LDAP_AUTH_SIMPLE:

        /* Find the entry specified by the bind DN...               */

        sdn = slapi_sdn_new_dn_byref(dn);

        rc |= slapi_search_internal_get_entry(

            sdn,

            attrs,

            &entry,

            plugin_id

            );

        slapi_sdn_free(&sdn);

        if (rc != 0 || entry == NULL) {

            slapi_log_info_ex(

                SLAPI_LOG_INFO_AREA_PLUGIN,

                SLAPI_LOG_INFO_LEVEL_DEFAULT,

                msgId,

                connId,

                opId,

                "test_bind in test-bind plug-in",

                "Could not find entry: %s\n", dn

            );

            rc = LDAP_NO_SUCH_OBJECT;

            slapi_send_ldap_result(pb, rc, NULL, NULL, 0, NULL);

            return (rc);

        } else {

        /* ...check credentials against the userpassword...         */

            Slapi_Value    *  credval; /* Value of credentials      */

            Slapi_ValueSet * pwvalues; /* Password attribute values */

                        

            rc |= slapi_entry_attr_find(

                entry,

                SLAPI_USERPWD_ATTR,

                &attr

            );

            if (attr == NULL) {

                slapi_log_info_ex(

                    SLAPI_LOG_INFO_AREA_PLUGIN,

                    SLAPI_LOG_INFO_LEVEL_DEFAULT,

                    msgId,

                    connId,

                    opId,

                    "test_bind in test-bind plug-in",

                    "Entry %s has no userpassword.\n",

                    dn

                );

                rc = LDAP_INAPPROPRIATE_AUTH;

                slapi_send_ldap_result(pb, rc, NULL, NULL, 0, NULL);

                return (rc);

            }

            rc |= slapi_attr_get_valueset(

                attr,

                &pwvalues

            );

            if (rc != 0 || slapi_valueset_count(pwvalues) == 0) {

                slapi_log_info_ex(

                    SLAPI_LOG_INFO_AREA_PLUGIN,

                    SLAPI_LOG_INFO_LEVEL_DEFAULT,

                    msgId,

                    connId,

                    opId,

                    "test_bind in test-bind plug-in",

                    "Entry %s has no %s attribute values.\n",

                    dn, SLAPI_USERPWD_ATTR

                );

                rc = LDAP_INAPPROPRIATE_AUTH;

                slapi_send_ldap_result(pb, rc, NULL, NULL, 0, NULL);

                                slapi_valueset_free(pwvalues);

                                return (rc);

            }

            credval = slapi_value_new_berval(credentials);

            rc = slapi_pw_find_valueset(pwvalues, credval);

            slapi_value_free(&credval);

                        slapi_valueset_free(pwvalues);

            if (rc != 0) {

                slapi_log_info_ex(

                    SLAPI_LOG_INFO_AREA_PLUGIN,

                    SLAPI_LOG_INFO_LEVEL_DEFAULT,

                    msgId,

                    connId,

                    opId,

                    "test_bind in test-bind plug-in",

                    "Credentials are not correct.\n"

                );

                rc = LDAP_INVALID_CREDENTIALS;

                slapi_send_ldap_result(pb, rc, NULL, NULL, 0, NULL);

                return (rc);

            }

        }

        /* ...if successful, set authentication for the connection. */

        if (rc != 0) return (rc);

        rc |= slapi_pblock_set(pb, SLAPI_CONN_DN, slapi_ch_strdup(dn));

        rc |= slapi_pblock_set(pb, SLAPI_CONN_AUTHMETHOD, SLAPD_AUTH_SIMPLE);

        if (rc != 0) {

            slapi_log_info_ex(

                SLAPI_LOG_INFO_AREA_PLUGIN,

                SLAPI_LOG_INFO_LEVEL_DEFAULT,

                msgId,

                connId,

                opId,

                "test_bind in test-bind plug-in",

                "Failed to set connection info.\n"

            );

            rc = LDAP_OPERATIONS_ERROR;

            slapi_send_ldap_result(pb, rc, NULL, NULL, 0, NULL);

            return (rc);

        } else {

            slapi_log_info_ex(

                SLAPI_LOG_INFO_AREA_PLUGIN,

                SLAPI_LOG_INFO_LEVEL_DEFAULT,

                msgId,

                connId,

                opId,

                "test_bind in test-bind plug-in",

                "Authenticated: %s\n", dn

            );

            /* Now that authentication succeeded, the plug-in

             * returns a value greater than 0, even though the

             * authentication has been successful. A return

             * code > 0 tells the server not to continue

             * processing the bind. A return code of 0, such

             * as LDAP_SUCCESS tells the server to continue

             * processing the operation.                            */

            slapi_send_ldap_result(pb, LDAP_SUCCESS, NULL, NULL, 0, NULL);

            rc = 1;

        }

        break;

    /* This plug-in supports only simple authentication.            */

    case LDAP_AUTH_NONE:

        /* Anonymous binds are handled by the front-end before

         * pre-bind plug-in functions are called, so we should

         * never reach this part of the code.                       */

    case LDAP_AUTH_SASL:

    default:

        slapi_log_info_ex(

            SLAPI_LOG_INFO_AREA_PLUGIN,

            SLAPI_LOG_INFO_LEVEL_DEFAULT,

            msgId,

            connId,

            opId,

            "test_bind in test-bind plug-in",

            "Plug-in does not handle auth. method: %d\n", method

        );

        rc = 0;                        /* Let server handle bind.   */

        break;

    }

    return (rc);                       /* Server stops processing

                                        * the bind if rc > 0.       */

}

The bulk of the code processes the LDAP_AUTH_SIMPLE case, where the plug-in uses the DN and the password to authenticate the user requesting the bind through plug-in API calls.

Seeing It Work

We demonstrate the plug-in by turning on informational logging for plug-ins and reading the log messages written by the plug-in at different stages in its operation. Before using the plug-in, we load some example users and data, because we cannot demonstrate the functionality while binding as the Directory Manager whose DN is specified as the value of nsslapd-rootdn. As described in Determining Whether to Handle the Bind or Call a Plug-In, Directory Server processes the directory manager bind without calling the pre-operation bind functions.

Setting Up an Example Suffix

If you have not done so already, prepare some data by creating a directory suffix, dc=example,dc=com, whose users we load from an LDIF file, ServerRoot/slapd-serverID/ldif/Example-Plugin.ldif. Refer to Setting Up an Example Suffix for instructions.

Building and Registering the Plug-In

  1. If you have not already done so, build the plug-in.
  2. Hint Use ServerRoot/plugins/slapd/slapi/examples/Makefile.

  3. Edit the value of nsslapd-pluginPath in the configuration entry taken from the introductory comment in testbind.c.
  4. Code Example 6-2  Configuration Entry (testbind.c

    dn: cn=Test Bind,cn=plugins,cn=config

    objectClass: top

    objectClass: nsSlapdPlugin

    objectClass: extensibleObject

    cn: Test Bind

    nsslapd-pluginPath: <ServerRoot>/plugins/slapd/slapi/examples/<LibName>

    nsslapd-pluginInitfunc: testbind_init

    nsslapd-pluginType: preoperation

    nsslapd-pluginEnabled: on

    nsslapd-plugin-depends-on-type: database

    nsslapd-pluginId: test-bind

    nsslapd-pluginVersion: 5.2

    nsslapd-pluginVendor: Sun Microsystems, Inc.

    nsslapd-pluginDescription: Sample bind pre-operation plug-in

    For 64-bit plug-ins, refer to 64-Bit Plug-In Locations.

  5. Save the configuration entry as testbind.ldif.
  6. Load the configuration entry into the directory.
  7. $ ldapmodify -a -p port -h host -D "cn=Directory Manager" -w password -f testbind.ldif

  8. Turn on informational logging for plug-ins as described in Code Example 3-3.
  9. Hint Configuration tab page. Select Logs node. Set Log Level.

  10. Restart Directory Server.

When Directory Server Bypasses the Plug-In

The example suffix contains a number of people. If we look up the entry for one of those people, Arabella Godiva, as either the directory manager (nsslapd-rootdn) user or as an anonymous client, the test_bind() plug-in function is never called. The plug-in therefore never logs informational messages to the errors log.

Code Example 6-3  Bypassing the Plug-In 

$ ldapsearch -L -p port -h host -D "cn=Directory Manager" -w password \

-b "dc=example,dc=com" "(uid=agodiva)" uid

dn: uid=agodiva,ou=People,dc=example,dc=com

uid: agodiva

$ ldapsearch -L -p port -h host -b "dc=example,dc=com" "(uid=agodiva)" uid

dn: uid=agodiva,ou=People,dc=example,dc=com

uid: agodiva

$ grep test_bind ServerRoot/slapd-serverID/logs/errors

$

Here we see the server bypasses pre-operation bind plug-ins when special users request a bind.

Binding as an Example.com User

First, check what happens in the errors log when we bind as Arabella Godiva using the correct password, agodiva, as shown in Code Example 6-4.

Code Example 6-4  Bind Involving the Plug-In 

$ ldapsearch -L -p port -h host -D "uid=agodiva,ou=People,dc=example,dc=com" \

-w agodiva -b "dc=example,dc=com" "(uid=agodiva)" uid

dn: uid=agodiva,ou=People,dc=example,dc=com

uid: agodiva

$ grep test_bind ServerRoot/slapd-serverID/logs/errors

[07/May/2112:16:50:16 +0200] - INFORMATION - test_bind in test-bind plug-in - conn=7 op=0 msgId=1 -  Authenticated: uid=agodiva,ou=people,dc=example,dc=com

What happens when we bind as Arabella Godiva, but get the password wrong?

Code Example 6-5  Wrong Password 

$ ldapsearch -L -p port -h host -D "uid=agodiva,ou=People,dc=example,dc=com" \

-w AAARGH -b "dc=example,dc=com" "(uid=agodiva)" uid

ldap_simple_bind_s: Invalid credentials

$ grep test_bind ServerRoot/slapd-serverID/logs/errors

...

[07/May/2112:16:51:18 +0200] - INFORMATION - test_bind in test-bind plug-in - conn=8 op=0 msgId=1 -  Credentials are not correct.

We see here the LDAP result interpreted correctly by the command-line client, and the plug-in message to the same effect written to the errors log.

Delete Arabella’s password and try again.

Code Example 6-6  No Password 

$ ldapmodify -p port -h host -D "cn=Directory Manager" -w password

dn: uid=agodiva,ou=people,dc=example,dc=com

changetype: modify

delete: userpassword

^D

modifying entry uid=agodiva,ou=people,dc=example,dc=com

$ ldapsearch -L -p port -h host -D "uid=agodiva,ou=People,dc=example,dc=com" \

-b "dc=example,dc=com" "(uid=agodiva)" uid

Bind Password:

ldap_simple_bind_s: Inappropriate authentication

$ grep test_bind ServerRoot/slapd-serverID/logs/errors

[07/May/2112:16:54:25 +0200] - INFORMATION - test_bind in test-bind plug-in - conn=12 op=0 msgId=1 -  Entry uid=agodiva,ou=people,dc=example,dc=com has no userpassword attribute values

We see the LDAP result displayed correctly by the command-line client, and the plug-in message giving more information about what went wrong during Arabella’s attempt to bind (no userpassword attribute values).


Developing a SASL Authentication Plug-In

This section shows how a pre-bind operation plug-in can implement a custom Simple Authentication and Security Layer (SASL) mechanism that enables mutual authentication without affecting existing authentication mechanisms.

The example plug-in responds to SASL bind requests with a mechanism called my_sasl_mechanism. Binds with this mechanism always succeed. The example is intended only to illustrate how a SASL authentication plug-in works, not to provide a secure mechanism for use in production.

Finding Examples

This section covers the examples ServerRoot/plugins/slapd/slapi/examples/testsaslbind.c and ServerRoot/plugins/slapd/slapi/examples/clients/saslclient.c.

Before using the plug-in function as described here, set up the example suffix and register the plug-in as described under Developing a Simple Authentication Plug-In, loading the configuration entry from the introductory comment of testsaslbind.c. Code Example 6-7 shows the entry.

Code Example 6-7  Configuration Entry (testsaslbind.c

dn: cn=Test SASL bind,cn=plugins,cn=config

objectclass: top

objectclass: nsSlapdPlugin

objectclass: extensibleObject

cn: SASL bind test

nsslapd-pluginPath: <ServerRoot>/plugins/slapd/slapi/examples/<LibName>

nsslapd-plugininitfunc: testsasl_init

nsslapd-plugintype: preoperation

nsslapd-pluginenabled: on

nsslapd-pluginid: test-saslbind

nsslapd-pluginversion: 5.2

nsslapd-pluginvendor: Sun Microsystems, Inc.

nsslapd-plugindescription: Sample SASL pre-bind plug-in

Adjust nsslapd-pluginPath to fit how the product is installed on your system. For 64-bit plug-ins, refer to 64-Bit Plug-In Locations.

Registering the SASL Mechanism

Register the SASL mechanism in the same function that registers the plug-in, using slapi_register_supported_saslmechanism(). Code Example 6-8 shows the function that registers both the plug-in and the SASL mechanism.

Code Example 6-8  Registering a Custom SASL Plug-In (testsaslbind.c

#include "slapi-plugin.h"

Slapi_PluginDesc saslpdesc = {

    "test-saslbind",                   /* plug-in identifier          */

    "Sun Microsystems, Inc.",          /* vendor name                 */

    "5.2",                             /* plug-in revision number     */

    "Sample SASL pre-bind plug-in"     /* plug-in description         */

};

#define TEST_MECHANISM  "my_sasl_mechanism"

#define TEST_AUTHMETHOD SLAPD_AUTH_SASL TEST_MECHANISM

/* Register the plug-in with the server.                              */

#ifdef _WIN32

__declspec(dllexport)

#endif

int

testsasl_init(Slapi_PBlock * pb)

{

    int rc = 0;                        /* 0 means success             */

    rc |= slapi_pblock_set(

        pb,

        SLAPI_PLUGIN_VERSION,

        SLAPI_PLUGIN_CURRENT_VERSION

    );

    rc |= slapi_pblock_set(            /* Plug-in description         */

        pb,

        SLAPI_PLUGIN_DESCRIPTION,

        (void *) &saslpdesc

    );

    rc |= slapi_pblock_set(            /* Pre-op bind SASL function   */

        pb,

        SLAPI_PLUGIN_PRE_BIND_FN,

        (void *) testsasl_bind

    );

            

    /* Register the SASL mechanism.                                   */

    slapi_register_supported_saslmechanism(TEST_MECHANISM);

    return (rc);

}

Refer to the Directory Server Plug-In Developer’s Reference for details on using slapi_register_supported_saslmechanism().

The plug-in function testsasl_bind() first obtains the client DN, method, SASL mechanism, and credentials. If the client does not ask to use the TEST_MECHANISM, the function returns 0 so the server can process the bind. Code Example 6-9 shows how this is done.

Code Example 6-9  Handling the my_sasl_mechanism Bind (testsaslbind.c

#include "slapi-plugin.h"

#define TEST_MECHANISM  "my_sasl_mechanism"

#define TEST_AUTHMETHOD SLAPD_AUTH_SASL TEST_MECHANISM

int

testsasl_bind(Slapi_PBlock * pb)

{

    char          * dn;                /* Target DN                   */

    int             method;            /* Authentication method       */

    char          * mechanism;         /* SASL mechanism              */

    struct berval * credentials;       /* SASL client credentials     */

    struct berval   svrcreds;          /* SASL server credentials     */

    int             connId, opId, rc = 0;

    long            msgId;

        

    rc |= slapi_pblock_get(pb, SLAPI_BIND_TARGET,        &dn);

    rc |= slapi_pblock_get(pb, SLAPI_BIND_METHOD,        &method);

    rc |= slapi_pblock_get(pb, SLAPI_BIND_CREDENTIALS,   &credentials);

    rc |= slapi_pblock_get(pb, SLAPI_BIND_SASLMECHANISM, &mechanism);

    rc |= slapi_pblock_get(pb, SLAPI_OPERATION_MSGID,    &msgId);

    rc |= slapi_pblock_get(pb, SLAPI_CONN_ID,            &connId);

    rc |= slapi_pblock_get(pb, SLAPI_OPERATION_ID,       &opId);

    if (rc == 0) {

        if (mechanism == NULL || strcmp(mechanism, TEST_MECHANISM) != 0)

            return(rc);                /* Client binding another way. */

    } else {                           /* slapi_pblock_get() failed!  */

        return(rc);

    }

    /* Using our SASL mechanism that always succeeds, set conn. info. */

    rc |= slapi_pblock_set(pb, SLAPI_CONN_DN,         slapi_ch_strdup(dn));

    rc |= slapi_pblock_set(pb, SLAPI_CONN_AUTHMETHOD, TEST_AUTHMETHOD);

    if (rc != 0) {                     /* Failed to set conn. info!   */

        slapi_log_info_ex(

            SLAPI_LOG_INFO_AREA_PLUGIN,

        SLAPI_LOG_INFO_LEVEL_DEFAULT,

            msgId,

            connId,

            opId,

            "testsasl_bind in test-saslbind plug-in",

            "slapi_pblock_set() for connection information failed.\n"

        );

        slapi_send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL);

        return(LDAP_OPERATIONS_ERROR); /* Server tries other mechs.   */

    }

    /* Set server credentials.                                        */

    svrcreds.bv_val = "my credentials";

    svrcreds.bv_len = sizeof("my credentials") - 1;

    rc |= slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, &svrcreds);

    if (rc != 0) {                     /* Failed to set credentials!  */

        slapi_log_info_ex(

            SLAPI_LOG_INFO_AREA_PLUGIN,

        SLAPI_LOG_INFO_LEVEL_DEFAULT,

            msgId,

            connId,

            opId,

            "testsasl_bind in test-saslbind plug-in",

            "slapi_pblock_set() for server credentials failed.\n"

        );

        rc |= slapi_pblock_set(pb, SLAPI_CONN_DN,         NULL);

        rc |= slapi_pblock_set(pb, SLAPI_CONN_AUTHMETHOD, SLAPD_AUTH_NONE);

        return(LDAP_OPERATIONS_ERROR); /* Server tries other mechs.   */

    }

    

    /* Send credentials to client.                                    */

    slapi_log_info_ex(

        SLAPI_LOG_INFO_AREA_PLUGIN,

        SLAPI_LOG_INFO_LEVEL_DEFAULT,

        msgId,

        connId,

        opId,

        "testsasl_bind in test-saslbind plug-in",

        "Authenticated: %s\n", dn

    );

    slapi_send_ldap_result(pb, LDAP_SUCCESS, NULL, NULL, 0, NULL);

    return 1;                          /* Server stops processing the

                                        * bind if the plug-in returns

                                        * a value greater than 0.     */

}

Code Example 6-9 sets the DN for the client connection and the authentication method, as Directory Server does for a bind. Setting SLAPI_CONN_DN and SLAPI_CONN_AUTHMETHOD is the key step of the bind. Without these values in the parameter block, Directory Server handles the client request as if the bind were anonymous, leaving the client with access rights corresponding to an anonymous bind. Next, according to the SASL model, Directory Server sends credentials to the client as shown.

Developing the Client

To test our plug-in, we need a client that uses my_sasl_mechanism to bind. The example discussed here uses the mechanism, and the client code, saslclient.c, is delivered with the product. The Sun Java System Directory SDK for C, available separately as described in Resources and Tools on the Web, is used to build the client.

The client authenticates using the example SASL mechanism for a synchronous bind to Directory Server. Code Example 6-10 shows how authentication proceeds on the client side for Soapy Cooper.

Code Example 6-10  Client Using my_sasl_mechanism (clients/saslclient.c

#include "ldap.h"

/* Use our fake SASL mechanism.                                      */

#define MECH "my_sasl_mechanism"

/* Global variables for client connection information.

* You may set these here or on the command line.                    */

static char * host = "localhost";      /* Server hostname            */

static int    port = 389;              /* Server port                */

/* Load <ServerRoot>/slapd-<ServerID>/ldif/Example-Plugin.ldif

* before trying the plug-in with this default user.                 */

static char * user = "uid=scooper,ou=people,dc=example,dc=com";

/* New value for userPassword                                        */

static char * npwd = "23skidoo";

/* Check for host, port, user and new password as arguments.         */

int get_user_args(int argc, char ** argv);

int

main(int argc, char ** argv)

{

    LDAP          * ld;                /* Handle to LDAP connection  */

    LDAPMod         modPW, * mods[2];  /* For modifying the password */

    char          * vals[2];           /* Value of modified password */

    struct berval   cred;              /* Client bind credentials    */

    struct berval * srvCred;           /* Server bind credentials    */

    int             ldapVersion;

    /* Use default hostname, server port, user, and new password

     * unless they are provided as arguments on the command line.    */

    if (get_user_args(argc, argv) != 0) return 1; /* Usage error     */

    

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

    printf("Getting the handle to the LDAP connection...\n");

    if ((ld = ldap_init(host, port)) == NULL) {

        perror("ldap_init");

        return 1;

    }

    /* By default, the LDAP version is set to 2.                     */

    printf("Setting the version to LDAP v3...\n");

    ldapVersion = LDAP_VERSION3;

    ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldapVersion);

    /* Authenticate using the example SASL mechanism.                */

    printf("Bind DN is %s...\n", user);

    printf("Binding to the server using %s...\n", MECH);

    cred.bv_val = "magic";

    cred.bv_len = sizeof("magic") - 1;

    if (ldap_sasl_bind_s(ld, user, MECH, &cred, NULL, NULL, &srvCred)) {

        ldap_perror(ld, "ldap_sasl_bind_s");

        return 1;

    }

    /* Display the credentials returned by the server.               */

    printf("Server credentials: %s\n", srvCred->bv_val);

    /* Modify the user's password.                                   */

    printf("Modifying the password...\n");

    modPW.mod_op     = LDAP_MOD_REPLACE;

    modPW.mod_type   = "userpassword";

    vals[0]          = npwd;

    vals[1]          = NULL;

    modPW.mod_values = vals;

    mods[0] = &modPW; mods[1] = NULL;

    if (ldap_modify_ext_s(ld, user, mods, NULL, NULL)) {

        ldap_perror(ld, "ldap_modify_ext_s");

        return 1;

    }

    /* Finish up.                                                    */

    ldap_unbind(ld);

    printf("Modification was successful.\n");

    return 0;

}

Trying It Out

The client changes Soapy’s password by binding to Directory Server, then requesting a modification to the userPassword attribute value. To be able to read the new password as clear text, set the password storage scheme for user passwords to No encryption (CLEAR) using the console. (Hint Configuration tab page. Data node. Use the Password encryption drop-down list.)

As the directory manager, change Soapy Cooper’s password to newpassword. Verify the change, and that the password appears in clear text.

Code Example 6-11  Modifying a Password 

$ ldapmodify -p port -h host -D "cn=Directory Manager" -w password

dn: uid=scooper,ou=People,dc=example,dc=com

changetype: modify

replace: userpassword

userpassword: newpassword

^D

modifying entry uid=scooper,ou=People,dc=example,dc=com

$ ldapsearch -L -p port -h host -D "cn=Directory Manager" -w password \

-b "dc=example,dc=com" "(uid=scooper)"

dn: uid=scooper,ou=People,dc=example,dc=com

mail: scooper@example.com

uid: scooper

secretary: uid=bcubbins,ou=People,dc=example,dc=com

givenName: Soapy

objectClass: top

objectClass: person

objectClass: organizationalPerson

objectClass: inetOrgPerson

sn: Cooper

cn: Soapy Cooper

userPassword: newpassword

The client can then change Soapy’s password to soapy after using the example SASL mechanism to bind.

With the plug-in active in the server, compile the client, saslclient.c. One way of doing this with the Sun Java System Directory SDK for C involves adding a saslclient target to lib/ldapcsdk/examples/Makefile.

Code Example 6-12  Makefile Additions to Build the SASL Client 

saslclient:     saslclient.o

    $(CC) -o saslclient saslclient.o $(LIBS)

Next, copy saslclient.c to the lib/ldapcsdk/examples/ directory and build the client. The Makefile works with GNU Make.

Code Example 6-13  Building the SASL Client 

$ cd sdk_install_dir/lib/ldapcsdk/examples

$ cp ServerRoot/plugins/slapd/slapi/examples/clients/saslclient.c \

sdk_install_dir/lib/ldapcsdk/examples

$ gmake saslclient

Next, run the client to perform the bind and the password modification.

Code Example 6-14  Using the SASL Client 

$ ./saslclient -p port

Using the following connection info:

        host:    localhost

        port:    port

        bind DN: uid=scooper,ou=people,dc=example,dc=com

        new pwd: 23skidoo

Getting the handle to the LDAP connection...

Setting the version to LDAP v3...

Bind DN is uid=scooper,ou=people,dc=example,dc=com...

Binding to the server using my_sasl_mechanism...

Server credentials: my credentials

Modifying the password...

Modification was successful.

On the Directory Server side, we see a message in the errors log showing that the plug-in authenticated the client:

[10/May/2112:14:50:48 +0200] - INFORMATION - testsasl_bind in test-saslbind plug-in - conn=13 op=0 msgId=1 -  Authenticated: uid=scooper,ou=people,dc=example,dc=com

As directory manager, we can see Soapy’s password has changed from newpassword to soapy.

Code Example 6-15  Results of Password Modification after SASL Bind 

$ ldapsearch -L -p port -h host -D "cn=Directory Manager" -w password \

-b "dc=example,dc=com" "(uid=scooper)"

dn: uid=scooper,ou=People,dc=example,dc=com

mail: scooper@example.com

uid: scooper

secretary: uid=bcubbins,ou=People,dc=example,dc=com

givenName: Soapy

objectClass: top

objectClass: person

objectClass: organizationalPerson

objectClass: inetOrgPerson

sn: Cooper

cn: Soapy Cooper

userPassword: soapy

We have thus demonstrated that our example SASL mechanism provides an alternative way of binding to the server.



Previous      Contents      Index      Next     


Copyright 2004 Sun Microsystems, Inc. All rights reserved.