Complete Contents
Getting Started
Chapter 1 Understanding Server Plug-Ins
Chapter 2 Writing and Compiling Plug-Ins
Chapter 3 Calling the Front-End API Functions
Chapter 4 Quick Start
Chapter 5 Writing Database Plug-Ins
Chapter 6 Writing Pre/Post-Operation Plug-Ins
Chapter 7 Defining Functions for LDAP Operations
Chapter 8 Defining Functions for Database Operations
Chapter 9 Defining Functions for Authentication
Chapter 10 Writing Entry Store/Fetch Plug-Ins
Chapter 11 Writing Extended Operation Plug-Ins
Chapter 12 Writing Matching Rule Plug-Ins
Chapter 13 Data Type and Structure Reference
Chapter 14 Function Reference
Chapter 15 Parameter Reference
Glossary
Previous Next Contents Bookshelf


Chapter 9 Defining Functions for Authentication

This chapter explains how to write a plug-in function to bypass or replace the standard function for authentication with your own function.

Information on authentication with the Netscape Directory Server is organized in the following sections:


Understanding Authentication Methods
Two methods that you can use to authenticate clients are simple authentication and SASL authentication:


How the Directory Server Identifies Clients
The server keeps track of the identity of the LDAP client through the SLAPI_CONN_DN and SLAPI_CONN_AUTHTYPE parameters.

During an LDAP bind operation, the server authenticates the user and puts the DN and authenticated method in the SLAPI_CONN_DN and SLAPI_CONN_AUTHTYPE parameters.

When an authenticated client requests the server to perform an LDAP operation, the server checks the DN in the SLAPI_CONN_DN parameter to determine if the client has the appropriate access rights.


How the Authentication Process Works
When the Netscape Directory Server receives an LDAP bind request from a client, it processes the request in the following steps:

  1. The server parses the LDAP bind request and retrieves the following information:
  2. If the method of authentication is LDAP_AUTH_SASL (SASL authentication), the server also retrieves the name of the SASL mechanism used from the LDAP bind request.

  3. The server normalizes the DN retrieved from the request. (See the slapi_dn_normalize() function for more information on normalized DNs.)
  4. The server retrieves any LDAP v3 controls included with the LDAP bind request.
  5. If the method of authentication is LDAP_AUTH_SASL (SASL authentication), the server determines whether or not the SASL mechanism (specified in the request) is supported.
  6. If the SASL mechanism is not supported by the server, the server sends an LDAP_AUTH_METHOD_NOT_SUPPORTED result code back to the client and ends the processing of the bind request.

  7. If the method of authentication is LDAP_AUTH_SIMPLE (simple authentication), the server checks if the DN is an empty string or if there are no credentials.
  8. If the DN is an empty string, if the DN is not specified, or if no credentials are specified, the server assumes that the client is binding anonymously and sends an LDAP_SUCCESS result code back to the client.

    The DN and authentication method for the connection (which are used to determine access rights for all operations performed through the connection) are left as NULL and SLAPD_AUTH_NONE, respectively.

  9. If the DN specified in the request is not served by this directory server (for example, if the DN is "uid=moxcross, o=kccorp.com" and the directory root of the server is "o=Airius.com"), the server sends one of the following two results back to the client and ends the processing of the bind request:
  10. The server puts the information from the bind request into the parameter block:
  11. If the DN is the root DN or the update DN (the DN of the master entity responsible for replicating the directory), the server authenticates the client.
  12. The server calls any pre-operation bind plug-in functions. If the function returns a non-zero value, the server ends the processing of the bind request.
  13. If you are writing your own plug-in function to handle authentication, you should return a non-zero value so that the server does not attempt to continue processing the bind request.

  14. The server calls the database bind function. If the function returns a non-zero value, the server ends the processing of the bind request and sends the appropriate result code back to the client.
  15. If the database bind function succeeds, the server sets the SLAPI_CONN_DN parameter to the DN and the SLAPI_CONN_AUTHTYPE parameter to the authentication method.
  16. The server sends an LDAP_SUCCESS result code back to the client and ends the processing of the bind request.
  17. If the client's password has expired or is expiring, the server includes a "password expired" control (with the OID 2.16.840.1.113730.3.4.4) or a "password expiring" control (with the OID 2.16.840.1.113730.3.4.5) with the result sent to the client.


Writing Your Own Authentication Plug-in
There are two types of situations in which you may want to write and implement your own function for authentication:


Writing a Pre-Operation Bind Plug-in
You can define your own pre-operation bind plug-in function to authenticate LDAP clients. The server will call your function during the authentication process (see Step  9). Your function should return a non-zero value to bypass the default database bind function and the post-operation bind functions.

Note that this means that Step  10 through Step  12 are skipped. Your pre-operation plug-in function is responsible for sending the result code to the client and for setting the DN and authentication method for the connection.

Figure 9.1 summarizes the process of using a pre-operation bind plug-in function to authenticate LDAP clients to the Directory Server.

Figure 9.1 Using a pre-operation bind plug-in function to handle authentication.

Figure 9.2 illustrates the steps that your pre-operation bind plug-in function must take to authenticate LDAP clients to the Directory Server.

Figure 9.2 How your pre-operation bind plug-in function can authenticate LDAP clients.

Defining Your Authentication Function

The following sections cover guidelines that you can follow when defining your authentication function:

Getting and Checking the Bind Parameters

Call the slapi_pblock_get() function to get the values of the following parameters:

If you plan to support authentication through SASL mechanisms, you should also get the value of the SLAPI_BIND_SASLMECHANISM parameter (a string value specifying the name of the SASL mechanism to use for authentication).

Make sure to check the following:

In both cases, return a non-zero value to prevent the server from calling the default database function for authentication.

Getting the Entry and Checking the Credentials

Get the entry for the DN specified by the SLAPI_BIND_TARGET parameter and compare the credentials in the SLAPI_BIND_CREDENTIALS parameter against the known credentials for that entry.

By default, the Netscape Directory Server uses the userpassword attribute to store the credentials for an entry. The server encrypts the password using the scheme specified in the passwdhash directive of the slapd.conf configuration file. The scheme can be crypt or sha or "" (for cleartext).

If you need to compare the client's credentials against the value of the userpassword attribute, you can call the slapi_pw_find() function. This function determines which password scheme was used to store the password and uses the appropriate comparison function to compare a given value against the encrypted value of the userpassword attribute.

What to Do If Authentication Fails

If authentication fails, send one of the following result codes back to the client:

You do not need to set any values for the SLAPI_CONN_DN parameter and the SLAPI_CONN_AUTHTYPE parameter. By default, these parameters are set to NULL and LDAP_AUTH_NONE, which indicate that the client has bound anonymously.

What to Do If Authentication Succeeds

If the authentication is successful, your authentication function should do the following:

Make sure that your function returns a non-zero value to bypass the default database bind function and any post-operation plug-in functions.

Registering the SASL Mechanism

If you are using SASL as the authentication method, you need to register the SASL mechanisms that you plan to use.

In your initialization function (see "Writing an Initialization Function" on page  34), call the slapi_register_supported_saslmechanism() function and specify the name of the SASL mechanism. For example:

slapi_register_supported_saslmechanism( "babsmechanism" );

Note that if you do not register your SASL mechanism, the Directory Server will send an LDAP_AUTH_METHOD_NOT_SUPPORTED result code back to the client and will not call your pre-operation bind function.

Example of a Pre-Operation Bind Plug-In

The following sections document an example of a pre-operation bind plug-in that handles authentication:

Example of a Pre-Operation Bind Function

The following is an example of a pre-operation bind function that authenticates clients and bypasses the default database bind function. In this example, the function just compares the client's credentials against the value of the userpassword attribute for the entry.

#include <stdio.h>

#include <string.h>

#include "slapi-plugin.h"

/* Pre-operation plug-in function */

int

test_bind( Slapi_PBlock *pb )

{

char *dn;

int method, rc = LDAP_SUCCESS;

struct berval *credentials;

struct berval **pwvals;

Slapi_PBlock *searchpb = NULL;

Slapi_Entry *e = NULL;

Slapi_Entry **entries = NULL;

Slapi_Attr *attr = NULL;

/* Log a message to the server error log. */

slapi_log_error( SLAPI_LOG_PLUGIN, "test_bind",

"Pre-operation bind function called.\n" );

/* Gets parameters available when processing an LDAP bind

operation. */

if ( slapi_pblock_get( pb, SLAPI_BIND_TARGET, &dn ) != 0 ||

slapi_pblock_get( pb, SLAPI_BIND_METHOD, &method ) != 0 ||

slapi_pblock_get( pb, SLAPI_BIND_CREDENTIALS, &credentials ) != 0 ) {

slapi_log_error( SLAPI_LOG_PLUGIN, "test_bind",

"Could not get parameters for bind operation\n" );

slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR,

NULL, NULL, 0, NULL );

return( 1 );

}

/* Check the authentication method */

switch( method ) {

case LDAP_AUTH_SIMPLE:

/* First, get the entry specified by the DN. */

searchpb = slapi_search_internal( dn, LDAP_SCOPE_BASE,

"(objectclass=*)", NULL, NULL, 0 );

if ( searchpb != NULL ) {

slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_RESULT, &rc );

if ( rc == LDAP_SUCCESS ) {

slapi_pblock_get( searchpb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES,

&entries );

if ( entries != NULL && entries[0] != NULL ) {

e = entries[0];

} else {

slapi_log_error( SLAPI_LOG_PLUGIN, "test_bind",

"Could not find entry for %s\n", dn );

rc = LDAP_NO_SUCH_OBJECT;

break;

}

} else {

slapi_log_error( SLAPI_LOG_PLUGIN, "test_bind",

"Could not find entry for %s (error %d)\n", dn, rc );

break;

}

} else {

slapi_log_error( SLAPI_LOG_PLUGIN, "test_bind",

"Could not search for entry\n" );

rc = LDAP_OPERATIONS_ERROR;

break;;

}

/* Next, check the credentials against the userpassword attribute

of that entry. */

if ( e != NULL ) {

if ( slapi_entry_attr_find( e, "userpassword", &attr ) != 0 ) {

slapi_log_error( SLAPI_LOG_PLUGIN, "test_bind",

"Entry has no userpassword attribute\n" );

rc = LDAP_INAPPROPRIATE_AUTH;

break;

}

slapi_attr_get_values( attr, &pwvals );

if ( slapi_pw_find( pwvals, credentials ) != 0 ) {

slapi_log_error( SLAPI_LOG_PLUGIN, "test_bind",

"Credentials are not correct for the entry\n" );

rc = LDAP_INVALID_CREDENTIALS;

break;

}

} else {

/* This should not happen. The previous section of code

already checks for this case. */

slapi_log_error( SLAPI_LOG_PLUGIN, "test_bind",

"Could find entry for %s\n", dn );

rc = LDAP_NO_SUCH_OBJECT;

break;

}

/* Set the DN and the authentication method for the connection. */

if ( slapi_pblock_set( pb, SLAPI_CONN_DN, slapi_ch_strdup( dn ) ) != 0 ||

slapi_pblock_set( pb, SLAPI_CONN_AUTHTYPE, SLAPD_AUTH_SIMPLE ) != 0 ) {

slapi_log_error( SLAPI_LOG_PLUGIN, "testbind_init",

"Failed to set DN and auth method for connection\n" );

rc = LDAP_OPERATIONS_ERROR;

break;

}

/* Send a "success" result code back to the client. */

slapi_log_error( SLAPI_LOG_PLUGIN, "test_bind", "Authenticated: %s\n", dn );

rc = LDAP_SUCCESS;

break;

/* If NONE is specified, the client is requesting to bind anonymously.

Normally, this case should be handled by the server's front-end

before it calls this plug-in function. Just in case this does

get through to the plug-in function, you can handle this by

sending a successful result code back to the client and returning

1.

*/

case LDAP_AUTH_NONE:

slapi_log_error( SLAPI_LOG_PLUGIN, "test_bind", "Authenticating anonymously\n" );

rc = LDAP_SUCCESS;

break;

/* This plug-in does not support any other method of authentication */

case LDAP_AUTH_SASL:

default:

slapi_log_error( SLAPI_LOG_PLUGIN, "test_bind",

"Unsupported authentication method requested: %d\n",

method );

rc = LDAP_AUTH_METHOD_NOT_SUPPORTED;

break;

}

if ( searchpb ) {

slapi_free_search_results_internal( searchpb );

slapi_ch_free( ( void ** )&searchpb );

}

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

return( 1 );

}

Example of an Initialization Function

To initialize your plug-in, write an initialization function to do the following:

The following is an example of an initialization function that registers the pre-operation bind function.

#include <stdio.h>

#include <string.h>

#include "slapi-plugin.h"

Slapi_PluginDesc bindpdesc = { "test-bind", "Netscape", "0.5",

"sample bind pre-operation plugin" };

/* Initialization function */

#ifdef _WIN32

__declspec(dllexport)

#endif

int

testbind_init( Slapi_PBlock *pb )

{

/* Register the pre-operation bind function and specify

the server plug-in version. */

if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
SLAPI_PLUGIN_VERSION_01 ) != 0 ||
slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
(void *)&bindpdesc ) != 0 ||
slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_BIND_FN,
(void *) test_bind ) != 0 ) {

slapi_log_error( SLAPI_LOG_PLUGIN, "testbind_init",

"Failed to set version and function\n" );

return( -1 );

}

return( 0 );

}

Editing the slapd.conf File

To register the plug-in, add the plugin preoperation directive to your slapd.conf file (which is located under the server root directory in slapd-<server_id>/config).

For example:

plugin preoperation /usr/netscape/suitespot/plugins/slapd/slapi/ examples/libtest-plugin.so testauth_init

Note. Each pre-operation plug-in is associated with a back-end. Make sure that the plugin directive that registers the plug-in is within the database section for that back-end in the slapd.conf file. (The plugin directive should be after the database directive and before the next database directive, if any.)


Writing a Database Bind Plug-in
As part of the process of defining a custom back-end to the Netscape Directory Server, you need to define a database bind function to authenticate LDAP clients. The server will call your function during the authentication process (see Step  10).

Unlike the pre-operation bind plug-in function, your function should return 0 if successful. Returning 0 will allow the server to complete Step  11 and Step  12. If authentication is not successful, your function should return the appropriate result code back to the client.

Figure 9.3 summarizes the process of using your database bind plug-in function to authenticate LDAP clients to the Directory Server.

Figure 9.3 Using a database bind plug-in function to handle authentication.

Figure 9.4 illustrates the steps that your server plug-in function must take to authenticate LDAP clients to the Directory Server.

Figure 9.4 How your database bind plug-in function can authenticate LDAP clients.

Defining Your Authentication Function

The following sections cover guidelines that you can follow when defining your authentication function:

Getting and Checking the Bind Parameters

Call the slapi_pblock_get() function to get the values of the following parameters:

If you plan to support authentication through SASL mechanisms, you should also get the value of the SLAPI_BIND_SASLMECHANISM parameter (a string value specifying the name of the SASL mechanism to use for authentication).

Make sure to check the following:

In both cases, return a non-zero value to indicate to the server that you have already sent a result code to the client. (If you return 0, the server will send an LDAP_SUCCESS result code back to the client.)

Getting the Entry and Checking the Credentials

Get the entry for the DN specified by the SLAPI_BIND_TARGET parameter. If the SLAPI_BIND_METHOD parameter is LDAP_AUTH_SIMPLE, check the credentials in the SLAPI_BIND_CREDENTIALS parameter.

The SLAPI_BIND_CREDENTIALS parameter holds a berval structure that contains the credentials sent by the client.

What to Do If Authentication Fails

If authentication fails, send one of the following result codes back to the client:

You do not need to set any values for the SLAPI_CONN_DN parameter and the SLAPI_CONN_AUTHTYPE parameter. By default, these parameters are set to NULL and LDAP_AUTH_NONE, which indicate that the client has bound anonymously.

What to Do If Authentication Succeeds

If the authentication is successful, your authentication function should return 0.

You do not need to set the value of the SLAPI_CONN_DN parameter and the SLAPI_CONN_AUTHTYPE parameter; the server does this for you. The server also sends an LDAP_SUCCESS result code to the client.

If the value of the SLAPI_BIND_METHOD parameter is LDAP_AUTH_SASL and you want to return a set of credentials to the client, call slapi_pblock_set() to set the SLAPI_BIND_RET_SASLCREDS parameter to the credentials.

Registering the SASL Mechanism

If you are using SASL as the authentication method, you need to register the SASL mechanisms that you plan to use.

In your initialization function (see "Writing an Initialization Function" on page  34), call the slapi_register_supported_saslmechanism() function and specify the name of the SASL mechanism. For example:

slapi_register_supported_saslmechanism( "babsmechanism" );

Note that if you do not register your SASL mechanism, the Directory Server will send an LDAP_AUTH_METHOD_NOT_SUPPORTED result code back to the client and will not call your pre-operation bind function.


Using SASL with an LDAP Client
If you intend to use SASL as the method for authenticating clients, you need to enable your LDAP clients to use SASL.

In your client, call the ldap_sasl_bind() or ldap_sasl_bind_s() function to request authentication using SASL. To parse credentials from an asynchronous SASL bind operation, call ldap_parse_sasl_bind_result(). These functions are part of the Netscape LDAP C SDK 3.0.

The syntax for these functions are listed below:

LDAP_API(int) LDAP_CALL ldap_sasl_bind( LDAP *ld, const char *dn,
const char *mechanism, struct berval *cred,
LDAPControl **serverctrls, LDAPControl **clientctrls, int *msgidp );

LDAP_API(int) LDAP_CALL ldap_sasl_bind_s( LDAP *ld, const char *dn,
const char *mechanism, struct berval *cred,
LDAPControl **serverctrls, LDAPControl **clientctrls,
struct berval **servercredp );

The parameters are described below:

If you are call the ldap_sasl_bind() function, you need to call the ldap_result() function to get the result of the authentication attempt:

LDAP_API(int) LDAP_CALL ldap_result( LDAP *ld, int msgid, int all,
struct timeval *timeout, LDAPMessage **result );

The parameters are described below:

After calling ldap_result(), you need to call the ldap_parse_sasl_bind_result() function to parse the results and retrieve the LDAP result code and any credentials returned by the server:

LDAP_API(int) LDAP_CALL ldap_parse_sasl_bind_result( LDAP *ld,
LDAPMessage *res, struct berval **servercredp, int freeit );

The parameters are described below:

The following example is an LDAP client that authenticates using the SASL mechanism named babsmechanism.

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <time.h>

#include <ldap.h>

int

main( int argc, char **argv )

{

LDAP *ld;

LDAPMod mod0;

LDAPMod mod1;

LDAPMod *mods[ 3 ];

char *vals0[ 2 ];

char *vals1[ 2 ];

time_t now;

char buf[ 128 ];

struct berval cred;

struct berval *servcred;

int version;

/* get a handle to an LDAP connection */

if ( (ld = ldap_init( "localhost", 389 )) == NULL ) {

perror( "ldap_init" );

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.) */

version = LDAP_VERSION3;

ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );

/* authenticate */

cred.bv_val = "magic";

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

if ( ldap_sasl_bind_s( ld, "uid=bjensen,ou=people,o=airius.com", "babsmechanism", &cred, NULL, NULL, &servcred ) != LDAP_SUCCESS ) {

ldap_perror( ld, "ldap_sasl_bind_s" );

return( 1 );

}

/* get and print the credentials returned by the server */

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

/* construct the list of modifications to make */

mod0.mod_op = LDAP_MOD_REPLACE;

mod0.mod_type = "mail";

vals0[0] = "babs@airius.com";

vals0[1] = NULL;

mod0.mod_values = vals0;

mod1.mod_op = LDAP_MOD_ADD;

mod1.mod_type = "description";

time( &now );

sprintf( buf, "This entry was modified with the modattrs program on %s",

ctime( &now ));

/* Get rid of \n which ctime put on the end of the time string */

if ( buf[ strlen( buf ) - 1 ] == '\n' ) {

buf[ strlen( buf ) - 1 ] = '\0';

}

vals1[ 0 ] = buf;

vals1[ 1 ] = NULL;

mod1.mod_values = vals1;

mods[ 0 ] = &mod0;

mods[ 1 ] = &mod1;

mods[ 2 ] = NULL;

/* make the change */

if ( ldap_modify_s( ld, "uid=bjensen,ou=people,o=airius.com", mods )

!= LDAP_SUCCESS ) {

ldap_perror( ld, "ldap_modify_s" );

return( 1 );

}

ldap_unbind( ld );

printf( "modification was successful\n" );

return( 0 );

}

 

© Copyright 1998 Netscape Communications Corporation