8.5. Writing a Pre-Operation Bind Plug-in
bind plug-in function to authenticate LDAP clients. The server will call your function during the authentication process. See Procedure 8.1, “How an Authentication Request is Processed” for more information on the authentication process. Your function should return a non-zero value to bypass the default backend bind function and the post-operation bind functions.
bind Plug-in Function to Handle Authentication” summarizes the process of using a pre-operation bind plug-in function to authenticate LDAP clients to the Directory Server.

Figure 8.1. Using a Pre-Operation bind Plug-in Function to Handle Authentication
bind plug-in function must take to authenticate LDAP clients to the Directory Server.

Figure 8.2. How Your Pre-Operation Bind Plug-in Function Can Authenticate LDAP Clients
8.5.1. Defining the Authentication Function
Note
testbind.c source file as an example of a pre-operation plug-in function that handles authentication. This file is in the /usr/lib64/dirsrv/plugins/test-plugins/ directory.
8.5.1.1. Getting and Checking the Bind Parameters
SLAPI_BIND_TARGET- A string value specifying the DN as which the client is attempting to authenticate.SLAPI_BIND_METHOD- An integer value specifying the authentication method, such asLDAP_AUTH_SIMPLEorLDAP_AUTH_SASL.SLAPI_BIND_CREDENTIALS- A berval structure containing the credentials sent by the client.
SLAPI_BIND_SASLMECHANISM parameter (a string value specifying the name of the SASL mechanism to use for authentication).
- Determine if the client is requesting to
bindas an anonymous user.If theSLAPI_BIND_METHODparameter isLDAP_AUTH_SIMPLEand theSLAPI_BIND_CREDENTIALSparameter is empty or NULL, the client is attempting tobindanonymously. Alternatively, disallow an anonymous bind and return the LDAP result codeLDAP_UNWILLING_TO_PERFORM.Call slapi_send_ldap_result() to send the LDAP result code LDAP_SUCCESS back to the client. - If the
SLAPI_BIND_METHODparameter specifies a method that you do not recognize or support, call slapi_send_ldap_result() to send an LDAP_STRONG_AUTH_NOT_SUPPORTED result code back to the client.
8.5.1.2. Getting the Entry and Checking the Credentials
SLAPI_BIND_TARGET parameter, and compare the credentials in the SLAPI_BIND_CREDENTIALS parameter against the known credentials for that entry. In order to get the entry, you must perform an internal search. There are several functions that can be used, listed in order of increasing power and complexity:
- slapi_search_internal_get_entry() is useful to retrieve a single entry given a DN and a list of attributes.
- slapi_search_internal_pb() returns an array of matching entries.
- slapi_search_internal_callback_pb() returns each matching entry in a user-supplied callback.
userPassword attribute to store the credentials for an entry. The server encodes the password using the scheme specified in the nsslapd-rootpwstoragescheme attribute for the Directory Manager or passwordStorageScheme attribute for other users. These attributes are defined in the cn=config entry contained in the dse.ldif file. The scheme can be any of the following:
CLEAR— No encryption is used, and can be defined using theclear-password-storage-schemeplug-in.CRYPT— Uses the Unix crypt algorithm, and can be defined using thecrypt-password-storage-schemeplug-in.SHA,SHA256,SHA384,SHA512— Uses the Secure Hashing Algorithm, and can be defined using thesha-password-storage-schemeplug-in.SHAisSHA-1, which is 140 bits. For the others, the number indicates the number of bits used by the hash.SSHA,SSHA256,SSHA384,SSHA512— Uses the Salted Secure Hashing Algorithm, and can be defined using thessha-password-storage-schemeplug-in.SSHAisSSHA-1, which is 140 bits, including the salt. For the others, the number indicates the number of bits used by the hash, including the salt.
userPassword attribute, you can call the slapi_pw_find_sv() 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.
8.5.1.3. What to Do If Authentication Fails
- If no entry matches the DN specified by the client, send an LDAP_NO_SUCH_OBJECT result code back to the client.When calling the slapi_send_ldap_result() function to send the result code back to the client, specify the closest matching DN as the
matchedargument. - If the client fails to provide the necessary credentials, or if credentials cannot be found in the entry, send an LDAP_INAPPROPRIATE_AUTH result code back to the client.
- If the credentials specified by the client do not match the credentials found in the entry, send an LDAP_INVALID_CREDENTIALS result code back to the client.
- If a general error occurs, send an LDAP_OPERATIONS_ERROR result code back to the client.
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.
8.5.1.4. What to Do If Authentication Succeeds
- Call slapi_pblock_set() to set the values of the
SLAPI_CONN_DNparameter and theSLAPI_CONN_AUTHTYPEparameter to the DN and authentication method.This sets the DN and authentication method for the connection to the client. The server uses this DN and method in subsequent operations when checking access rights.You can setSLAPI_CONN_AUTHTYPEto one of the following values:SLAPD_AUTH_NONErepresents no authentication. (The client is binding anonymously.)SLAPD_AUTH_SIMPLErepresents the simple authentication method.SLAPD_AUTH_SSLrepresents authentication through TLS.SLAPD_AUTH_SASLrepresents SASL authentication.
These values differ from the values in theSLAPI_BIND_METHODparameter. The values listed above are string values defined in theslapi-plugin.hheader file, whereas the values of theSLAPI_BIND_METHODparameter (such asLDAP_AUTH_SIMPLEandLDAP_AUTH_SASL) are integer values defined in theldap.hheader file. - If required, specify the credentials that you want sent back to the client.If the value of the
SLAPI_BIND_METHODparameter isLDAP_AUTH_SASLand you want to return a set of credentials to the client, call slapi_pblock_set() to set theSLAPI_BIND_RET_SASLCREDSparameter to the credentials. - Send the result of the authentication process back to the client.Call slapi_send_ldap_result() to send an LDAP_SUCCESS return code to the client.
bind function and any post-operation plug-in functions.
8.5.2. Registering the SASL Mechanism
slapi_register_supported_saslmechanism( "babsmechanism" );
bind function.
Note
testsaslbind.c source file as an example of a pre-operation plug-in function for SASL authentication. This file is in the /usr/lib64/dirsrv/plugins/test-plugins/ directory.
8.5.3. Example of a Pre-Operation Bind Plug-in
bind plug-in that handles authentication.
Note
testbind.c source file as an example of a pre-operation plug-in function that handles authentication. This file is in the /usr/lib64/dirsrv/plugins/test-plugins/ directory.
8.5.3.1. Example of a Pre-Operation Bind Function
bind function that authenticates clients and bypasses the default backend bind function. In this example, the function compares the client's credentials against the value of the userpassword attribute for the entry.
#include <stdio.h>
#include <string.h>
#include "dirsrv/slapi-plugin.h"
/* Pre-operation plug-in function */
int
test_bind(Slapi_PBlock *pb )
{
int method, rc = LDAP_SUCCESS;
struct berval *credentials;
Slapi_Entry *e = NULL;
Slapi_Attr *attr = NULL;
Slapi_ValueSet *vs = NULL;
Slapi_Value *sv_creds = NULL;
Slapi_Value **sva = NULL;
Slapi_DN *sdn = NULL;
const char *dn = NULL;
/* we only care about these attributes */
char *attrlist[] = { "userPassword", NULL };
void *my_plugin_id = 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_SDN, &sdn ) != 0 ||
slapi_pblock_get( pb, SLAPI_BIND_METHOD, &method ) != 0 ||
slapi_pblock_get( pb, SLAPI_BIND_CREDENTIALS, &credentials ) != 0 ||
slapi_pblock_get( pb, SLAPI_PLUGIN_IDENTITY, &my_plugin_id) != 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 );
}
sv_creds = slapi_value_new_berval(credentials); /* wrap in Slapi_Value* */
dn = slapi_sdn_get_dn(sdn);
/* Check the authentication method */
switch( method ) {
case LDAP_AUTH_SIMPLE:
/* First, get the entry specified by the DN. */
rc = slapi_search_internal_get_entry(sdn, attrlist, &e, my_plugin_id);
if ((LDAP_SUCCESS == rc) && (NULL != e)) {
Slapi_Value *val = NULL;
int num_vals = 0;
int hint = 0;
int i = 0;
/* see if the entry has the userpassword attribute */
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_valueset( attr, &vs ); /* must free vs */
slapi_attr_get_numvalues(attr, &num_vals);
sva = (Slapi_Value **) slapi_ch_calloc( (num_vals + 1), sizeof(Slapi_Value *));
/* Loop through all of our values s and create a value array */
hint = slapi_valueset_first_value(vs, &val);
while (val)
{
sva[i] = val;
i++;
hint = slapi_valueset_next_value(vs, hint, &val);
}
/* Next, check the credentials against the userpassword attribute of that entry. */
if ( slapi_pw_find_sv( sva, sv_creds ) != 0 ) {
slapi_log_error( SLAPI_LOG_PLUGIN, "test_bind" ,
"Credentials are not correct for the entry\n" );
rc = LDAP_INVALID_CREDENTIALS;
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_AUTHMETHOD, 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;
} else { /* error code or no entry */
slapi_log_error( SLAPI_LOG_PLUGIN, "test_bind",
"Could not find entry for %s: Error: %s\n",
dn, (rc == LDAP_SUCCESS) ? "unknown" : ldap_err2string(rc) );
/* if the entry was null, there was probably an internal error */
if (LDAP_SUCCESS == rc) {
rc = LDAP_OPERATIONS_ERROR;
}
}
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,
* or if you do not want to support anon, return LDAP_UNWILLING_TO_PERFORM
*/
case LDAP_AUTH_NONE:
slapi_log_error( SLAPI_LOG_PLUGIN, "test_bind" , "Authenticating anonymously\n" );
rc = LDAP_SUCCESS; /* or return LDAP_UNWILLING_TO_PERFORM if anon not supported */
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;
}
/* clean up - ok to pass NULL to these */
slapi_entry_free(e);
slapi_valueset_free(vs);
slapi_ch_free((void **)&sva);
slapi_value_free(&sv_creds);
slapi_sdn_free(&sdn);
/* actually return the result to the client */
slapi_send_ldap_result( pb, rc, NULL, NULL, 0, NULL );
return( rc );
}
8.5.3.2. Example of an Initialization Function
- Call slapi_pblock_set() to set the
SLAPI_PLUGIN_PRE_BIND_FNparameter to the name of your pre-operationbindfunction. (For details, see Section 2.2.3, “Registering Your Plug-in Functions”.) - If you are using SASL as the authentication method, call the Chapter 22, Functions for Syntax Plug-ins function to register your SASL mechanism with the Directory Server.
bind function.
#include <stdio.h>
#include <string.h>
#include "dirsrv/slapi-plugin.h"
Slapi_PluginDesc bindpdesc = { "test-bind" , "Red Hat" , "0.5" ,"sample bind pre-operation plugin" };
/* our plug-in identity . set in init function */
static Slapi_ComponentId *my_plugin_identity;
/* Initialization function */
#ifdef _WIN32
__declspec(dllexport)
#endif
int
testbind_init( Slapi_PBlock *pb )
{
/*
* Get our plug-in identity . we will need this to perform
* any internal operations (search, modify, etc.)
*/
slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &my_plugin_identity);
/*
* Register the pre-operation bind function and specify
* the server plug-in version.
*/
if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,SLAPI_PLUGIN_VERSION_03 ) != 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 );
}
8.5.3.3. Registering the Plug-in
/etc/dirsrv/slapd-instance/dse.ldif file:
dn: cn=Test Bind,cn=plugins,cn=config objectClass: top objectClass: nsSlapdPlugin objectClass: extensibleObject cn: Test Bind nsslapd-pluginPath:/path/to/test-plugin.so nsslapd-pluginInitfunc: testbind_init nsslapd-pluginType: preoperation nsslapd-pluginEnabled: on nsslapd-plugin-depends-on-type: database nsslapd-pluginId: test-bind
testbind.c source file as an example of a pre-operation plug-in function that handles authentication. This file is in the /usr/lib64/dirsrv/plugins/test-plugins/ directory.
Note
/usr/lib64/dirsrv/plugins directory for plug-ins that implement SLAPI_PLUGIN_PRE_BIND_FN.
- Log the authentication attempt to the access log for auditing.
- Check for password expiration and use slapi_add_pwd_control() to send that information back to the client.
- See if the client has requested additional password policy information in a couple of different ways:
slapi_pblock_get (pb, SLAPI_REQCONTROLS, ...) slapi_pblock_get (pb, SLAPI_PWPOLICY, ...)
Then send the requested information back to the client using slapi_pwpolicy_make_response_control(). - Manage other aspects of password policy.
bind.c to see what sort of processing it does.

Where did the comment section go?
Red Hat's documentation publication system recently went through an upgrade to enable speedier, more mobile-friendly content. We decided to re-evaluate our commenting platform to ensure that it meets your expectations and serves as an optimal feedback mechanism. During this redesign, we invite your input on providing feedback on Red Hat documentation via the discussion platform.