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