An example of SASL bind plug-in
The
following sample C code creates a simple SASL bind plug-in that uses
the mechanism SAMPLE_BIND. It compares the password
that is sent across the wire to the password stored in the directory
for the bind DN. It is important to realize that this example is meant
only to illustrate the basic operation of servicing a simple bind
request, and how the operations are implemented by way of a user developed
plug-in. Actual processing of a simple bind request as part of the
fundamental operation of the LDAP server involves more processing.
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <slapi-plugin.h>
#define FALSE 0
/* Let the next plug-in try the operation */
#define NEXTPLUGIN 0
/* We handled the operation, so don't run any other plug-ins */
#define STOP_PLUGIN_SEARCH 1
/* SASL mechanism type */
#define SAMPLE_MECH "SAMPLE_BIND"
/* Subsystem to use for slapi_log_error calls */
#define SAMPLE_SUBSYSTEM "SAMPLE"
/* Filter used when searching for the entry DN */
#define FILTER "objectclass=*"
/* Password attribute name */
#define PWATTR "userpassword"
/* Forward declaration of our bind plug-in function */
int sampleBind(Slapi_PBlock *pb);
/* Initialization function */
int sampleInit(Slapi_PBlock *pb)
{
int argc = 0;
char ** argv = NULL;
/* to register the Sample_Bind function as the pre-operation
* bind funtion */
if (slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_BIND_FN, (void*) sampleBind ) != 0)
{
slapi_log_error( LDAP_MSG_LOW, SAMPLE_SUBSYSTEM,
"sampleInit couldn't set plug-in function\n");
return (-1);
}
/* Get the plug-in argument count. These arguments are defined
* in the plug-in directive in the configuration file. */
if (slapi_pblock_get( pb, SLAPI_PLUGIN_ARGC, &argc ) != 0)
{
slapi_log_error( LDAP_MSG_LOW, SAMPLE_SUBSYSTEM,
"sampleInit couldn't get argc\n");
return (-1);
}
/* Get the plug-in argument array */
if(slapi_pblock_get( pb, SLAPI_PLUGIN_ARGV, &argv ) != 0)
{
slapi_log_error( LDAP_MSG_LOW, SAMPLE_SUBSYSTEM,
"sampleInit couldn't get argv\n");
return (-1);
}
/* Low "severity" means high importance. */
slapi_log_error( LDAP_MSG_LOW, SAMPLE_SUBSYSTEM,
"Hello from sample\n" );
/** Register SAMPLE_BIND as one of the supported SASL mechanisms
* so that it shows up when the RootDSE is queried. */
slapi_register_supported_saslmechanism(SAMPLE_MECH);
return LDAP_SUCCESS;
}
/* * Function to get the password for the specified dn.*/
int getEntryPassword(char *dn, char ** passwd)
{
Slapi_PBlock *pb = NULL;
int rc = LDAP_SUCCESS;
int numEntries = 0;
Slapi_Entry **entries = NULL;
Slapi_Attr *a = NULL;
struct berval **attr_vals = NULL;
/** Do an internal search to get the entry for the given dn*/
pb = slapi_search_internal(dn, /* Entry to retrieve */
LDAP_SCOPE_BASE,
/* Only get the entry asked for */
FILTER, /* Search filter */
NULL, /* No controls */
NULL, /* Get all attributes */
FALSE);
/* Get attribute values (names only is false) */
if (pb == NULL)
{
slapi_log_error( LDAP_MSG_LOW, SAMPLE_SUBSYSTEM,
"Search failed for dn = %s\n", dn);
return (LDAP_OPERATIONS_ERROR);
}
/* Get the return code from the search */
slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
if (rc != LDAP_SUCCESS)
{
/* Search failed */
slapi_pblock_destroy( pb );
return (rc);
}
/* Get the number of entries returned from the search */
slapi_pblock_get( pb, SLAPI_NENTRIES, &numEntries );
if (numEntries == 0)
{
/* Couldn't find entry */
slapi_free_search_results_internal( pb );
slapi_pblock_destroy( pb );
return (LDAP_NO_SUCH_OBJECT);
}
/* Get the entries */
slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries );
/** Since we did a base level search, there can only be one entry returned.
* Get the value of the "userpassword" attribute from the entry. */
if (slapi_entry_attr_find( entries[0], PWATTR, &a ) == 0)
{
/* Copy the password into the out parameter */
slapi_attr_get_values( a, &attr_vals );
(*passwd) = slapi_ch_strdup( attr_vals[0]->bv_val );
}
else
{
/* No userpassword attribute */
slapi_free_search_results_internal( pb );
slapi_pblock_destroy( pb );
return (LDAP_INAPPROPRIATE_AUTH);
}
slapi_free_search_results_internal( pb );
slapi_pblock_destroy( pb );
return (LDAP_SUCCESS);
}
/* Function to handle a bind request */
int sampleBind(Slapi_PBlock *pb)
{
char * mechanism = NULL;
char * dn = NULL;
char * passwd = NULL;
char * connDn = NULL;
char * aString = NULL;
struct berval * credentials = NULL;
int rc = LDAP_SUCCESS;
/* Get the target DN */
if (slapi_pblock_get( pb, SLAPI_BIND_TARGET, &dn ) != 0
{
slapi_log_error( LDAP_MSG_LOW, SAMPLE_SUBSYSTEM,
"sampleBind couldn't get bind target\n");
return (NEXTPLUGIN);
}
/* Get the password */
if (slapi_pblock_get( pb, SLAPI_BIND_CREDENTIALS, &credentials ) != 0)
{
lapi_log_error( LDAP_MSG_LOW, SAMPLE_SUBSYSTEMs,
"sampleBind couldn't get bind target\n");
return (NEXTPLUGIN);
}
/* Get the bind mechanism */
if (slapi_pblock_get( pb, SLAPI_BIND_SASLMECHANISM, &mechanism ) != 0)
{
slapi_log_error( LDAP_MSG_LOW, SAMPLE_SUBSYSTEM,
"sampleBind couldn't get bind target\n");
return (NEXTPLUGIN);
}
/** If the requested mechanism isn't SAMPLE, then we're not going to
* handle it.
*/
if ((mechanism == NULL)|| (strcmp(mechanism, SAMPLE_MECH) != 0))
{
return (NEXTPLUGIN);
}
rc = getEntryPassword( dn, &passwd);
if (rc != LDAP_SUCCESS)
{
slapi_send_ldap_result( pb, rc, NULL, NULL, 0, NULL );
return (STOP_PLUGIN_SEARCH);
}
/*Check if they gave the correct password */
if ((credentials->bv_val == NULL) || (passwd == NULL) ||
(strcmp(credentials->bv_val,passwd) != 0))
{
slapi_log_error( LDAP_MSG_LOW, SAMPLE_SUBSYSTEM,
"Bind as %s failed\n", dn);
rc = LDAP_INVALID_CREDENTIALS;
}
else
{
/*
* Make a copy of the DN and authentication method and set them
* in the pblock. The server will use them for the connection.
*/
connDn = slapi_ch_strdup(dn);
if (connDn == NULL)
{
slapi_log_error( LDAP_MSG_LOW, SAMPLE_SUBSYSTEM,
"Could not duplicate connection DN\n");
slapi_send_ldap_result( pb, LDAP_NO_MEMORY, NULL, NULL,0, NULL );
slapi_ch_free(passwd);
return (STOP_PLUGIN_SEARCH);
}
/** The authentication method string will look something like
* "SASL SAMPLE_BIND" */
aString = slapi_ch_malloc(strlen(SLAPD_AUTH_SASL) +
strlen(SAMPLE_MECH) + 2);
if (aString == NULL)
{
slapi_log_error( LDAP_MSG_LOW, SAMPLE_SUBSYSTEM,
"Could not duplicate authString\n");
slapi_ch_free(passwd);
slapi_ch_free(connDn);
slapi_send_ldap_result( pb, LDAP_NO_MEMORY, NULL, NULL,0, NULL );
return (STOP_PLUGIN_SEARCH);
}
sprintf(aString, "%s%s", SLAPD_AUTH_SASL, SAMPLE_MECH);
/* Set the connection DN */
if (slapi_pblock_set( pb, SLAPI_CONN_DN,(void *) connDn) != 0)
{
slapi_log_error( LDAP_MSG_LOW,SAMPLE_SUBSYSTEM,
"Could not set SLAPI_CONN_DN\n");
slapi_ch_free(passwd);
slapi_ch_free(connDn);
slapi_ch_free(aString);
slapi_send_ldap_result(pb, LDAP_OPERATIONS_ERROR,
NULL, NULL, 0, NULL );
return (STOP_PLUGIN_SEARCH);
}
/* Set the authentication type */
if (slapi_pblock_set( pb, SLAPI_CONN_AUTHTYPE, (void *) aString) != 0)
{
slapi_log_error( LDAP_MSG_LOW,SAMPLE_SUBSYSTEM,
"Could not set SLAPI_CONN_AUTHTYPE\n");
slapi_ch_free(passwd);
slapi_ch_free(connDn);
slapi_ch_free(aString);
slapi_send_ldap_result(pb, LDAP_OPERATIONS_ERROR,
NULL, NULL, 0, NULL );
return (STOP_PLUGIN_SEARCH);
}
rc = LDAP_SUCCESS;
}
/* Send the result back to the client */
slapi_send_ldap_result(pb, rc, NULL, NULL, 0, NULL );
/*Free the memory allocated by the plug-in */
slapi_ch_free(passwd);
return (STOP_PLUGIN_SEARCH);
}
To use the plug-in you must:
- Compile it. Use the following makefile to compile the plug-in:
CC = gcc LINK = gcc -shared WARNINGS = -Wall -Werror LDAP_HOME = /usr/ldap INCDIRS = -I${LDAP_HOME}/include LIBDIRS = -L${LDAP_HOME}/lib CFLAGS = -g ${WARNINGS} ${INCDIRS} LINK_FLAGS = ${LIBDIRS} ${LIBS} PLUGIN = libsample.so OBJECTS = sample.o .PHONY: clean all: ${PLUGIN} .c.o: $(CC) ${CFLAGS} -c -o $@ $< ${PLUGIN}: ${OBJECTS} ${LINK} -o $@ $< ${LINK_FLAGS} clean: ${RM} ${PLUGIN} ${RM} ${OBJECTS} - Add the following information to the
ibmslapd.conffile by using the ldapmodify command:
where <filename> contains:ldapmodify -D <adminDN> -w<adminPW> -i<filename>dn: cn=SchemaDB, cn=LDCF Backends, cn=IBM Directory, cn=Schemas, cn=Configuration changetype: modify add: ibm-slapdPlugin ibm-slapdPlugin: preoperation <path to plug-in>/libsample.so sampleInit - Restart the server. If the plug-in was loaded, its initialization
function writes a message to the
ibmslapd.logfile similar to the following messages:08/25/2003 01:28:50 PM SAMPLE: Hello from sample - Perform an LDAP operation:
The search succeeds if the entry cn=bob,o=sampleexists and has a user password attribute with the value hello. If the entry does not exist, an authentication denied error is returned.ldapsearch -D cn=bob,o=sample -w hello -p 1234 -b o=sample objectclass=*