/* $NetBSD: plugin.c,v 1.3 2021/08/14 16:15:02 christos Exp $ */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software .
*
* Copyright 2002-2021 The OpenLDAP Foundation.
* Portions Copyright 1997,2002-2003 IBM Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in the file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* .
*/
/* ACKNOWLEDGEMENTS:
* This work was initially developed by IBM Corporation for use in
* IBM products and subsequently ported to OpenLDAP Software by
* Steve Omrani. Additional significant contributors include:
* Luke Howard
*/
#include
__RCSID("$NetBSD: plugin.c,v 1.3 2021/08/14 16:15:02 christos Exp $");
#include "portable.h"
/*
* Note: if ltdl.h is not available, slapi should not be compiled
*/
#ifdef HAVE_LTDL_H
#include "ldap_pvt_thread.h"
#include "slap.h"
#include "slap-config.h"
#include "slapi.h"
#include "lutil.h"
#include
static int slapi_int_load_plugin( Slapi_PBlock *, const char *, const char *, int,
SLAPI_FUNC *, lt_dlhandle * );
/* pointer to link list of extended objects */
static ExtendedOp *pGExtendedOps = NULL;
/*********************************************************************
* Function Name: plugin_pblock_new
*
* Description: This routine creates a new Slapi_PBlock structure,
* loads in the plugin module and executes the init
* function provided by the module.
*
* Input: type - type of the plugin, such as SASL, database, etc.
* path - the loadpath to load the module in
* initfunc - name of the plugin function to execute first
* argc - number of arguments
* argv[] - an array of char pointers point to
* the arguments passed in via
* the configuration file.
*
* Output:
*
* Return Values: a pointer to a newly created Slapi_PBlock structure or
* NULL - function failed
*
* Messages: None
*********************************************************************/
static Slapi_PBlock *
plugin_pblock_new(
int type,
int argc,
char *argv[] )
{
Slapi_PBlock *pPlugin = NULL;
Slapi_PluginDesc *pPluginDesc = NULL;
lt_dlhandle hdLoadHandle;
int rc;
char **av2 = NULL, **ppPluginArgv;
char *path = argv[2];
char *initfunc = argv[3];
pPlugin = slapi_pblock_new();
if ( pPlugin == NULL ) {
rc = LDAP_NO_MEMORY;
goto done;
}
slapi_pblock_set( pPlugin, SLAPI_PLUGIN_TYPE, (void *)&type );
slapi_pblock_set( pPlugin, SLAPI_PLUGIN_ARGC, (void *)&argc );
av2 = ldap_charray_dup( argv );
if ( av2 == NULL ) {
rc = LDAP_NO_MEMORY;
goto done;
}
if ( argc > 0 ) {
ppPluginArgv = &av2[4];
} else {
ppPluginArgv = NULL;
}
slapi_pblock_set( pPlugin, SLAPI_PLUGIN_ARGV, (void *)ppPluginArgv );
slapi_pblock_set( pPlugin, SLAPI_X_CONFIG_ARGV, (void *)av2 );
rc = slapi_int_load_plugin( pPlugin, path, initfunc, 1, NULL, &hdLoadHandle );
if ( rc != 0 ) {
goto done;
}
if ( slapi_pblock_get( pPlugin, SLAPI_PLUGIN_DESCRIPTION, (void **)&pPluginDesc ) == 0 &&
pPluginDesc != NULL ) {
slapi_log_error(SLAPI_LOG_TRACE, "plugin_pblock_new",
"Registered plugin %s %s [%s] (%s)\n",
pPluginDesc->spd_id,
pPluginDesc->spd_version,
pPluginDesc->spd_vendor,
pPluginDesc->spd_description);
}
done:
if ( rc != 0 && pPlugin != NULL ) {
slapi_pblock_destroy( pPlugin );
pPlugin = NULL;
if ( av2 != NULL ) {
ldap_charray_free( av2 );
}
}
return pPlugin;
}
/*********************************************************************
* Function Name: slapi_int_register_plugin
*
* Description: insert the slapi_pblock structure to a given position the end of the plugin
* list
*
* Input: a pointer to a plugin slapi_pblock structure to be added to
* the list
*
* Output: none
*
* Return Values: LDAP_SUCCESS - successfully inserted.
* LDAP_LOCAL_ERROR.
*
* Messages: None
*********************************************************************/
int
slapi_int_register_plugin_index(
Backend *be,
Slapi_PBlock *pPB,
int index )
{
Slapi_PBlock *pTmpPB;
Slapi_PBlock *pSavePB;
int pos = 0, rc = LDAP_SUCCESS;
assert( be != NULL );
pTmpPB = SLAPI_BACKEND_PBLOCK( be );
if ( pTmpPB == NULL || index == 0 ) {
SLAPI_BACKEND_PBLOCK( be ) = pPB;
} else {
while ( pTmpPB != NULL && rc == LDAP_SUCCESS &&
( index < 0 || pos++ < index ) ) {
pSavePB = pTmpPB;
rc = slapi_pblock_get( pTmpPB, SLAPI_IBM_PBLOCK, &pTmpPB );
}
if ( rc == LDAP_SUCCESS ) {
rc = slapi_pblock_set( pSavePB, SLAPI_IBM_PBLOCK, (void *)pPB );
}
}
if ( index >= 0 && rc == LDAP_SUCCESS ) {
rc = slapi_pblock_set( pPB, SLAPI_IBM_PBLOCK, (void *)pTmpPB );
}
return ( rc != LDAP_SUCCESS ) ? LDAP_OTHER : LDAP_SUCCESS;
}
int
slapi_int_register_plugin(
Backend *be,
Slapi_PBlock *pPB )
{
return slapi_int_register_plugin_index( be, pPB, -1 );
}
/*********************************************************************
* Function Name: slapi_int_get_plugins
*
* Description: get the desired type of function pointers defined
* in all the plugins
*
* Input: the type of the functions to get, such as pre-operation,etc.
*
* Output: none
*
* Return Values: this routine returns a pointer to an array of function
* pointers containing backend-specific plugin functions
* followed by global plugin functions
*
* Messages: None
*********************************************************************/
int
slapi_int_get_plugins(
Backend *be,
int functype,
SLAPI_FUNC **ppFuncPtrs )
{
Slapi_PBlock *pCurrentPB;
SLAPI_FUNC FuncPtr;
SLAPI_FUNC *pTmpFuncPtr;
int numPB = 0;
int rc = LDAP_SUCCESS;
assert( ppFuncPtrs != NULL );
*ppFuncPtrs = NULL;
if ( be == NULL ) {
goto done;
}
pCurrentPB = SLAPI_BACKEND_PBLOCK( be );
while ( pCurrentPB != NULL && rc == LDAP_SUCCESS ) {
rc = slapi_pblock_get( pCurrentPB, functype, &FuncPtr );
if ( rc == LDAP_SUCCESS ) {
if ( FuncPtr != NULL ) {
numPB++;
}
rc = slapi_pblock_get( pCurrentPB,
SLAPI_IBM_PBLOCK, &pCurrentPB );
}
}
if ( numPB == 0 ) {
rc = LDAP_SUCCESS;
goto done;
}
/*
* Now, build the function pointer array of backend-specific
* plugins followed by global plugins.
*/
*ppFuncPtrs = pTmpFuncPtr =
(SLAPI_FUNC *)ch_malloc( ( numPB + 1 ) * sizeof(SLAPI_FUNC) );
if ( ppFuncPtrs == NULL ) {
rc = LDAP_NO_MEMORY;
goto done;
}
pCurrentPB = SLAPI_BACKEND_PBLOCK( be );
while ( pCurrentPB != NULL && rc == LDAP_SUCCESS ) {
rc = slapi_pblock_get( pCurrentPB, functype, &FuncPtr );
if ( rc == LDAP_SUCCESS ) {
if ( FuncPtr != NULL ) {
*pTmpFuncPtr = FuncPtr;
pTmpFuncPtr++;
}
rc = slapi_pblock_get( pCurrentPB,
SLAPI_IBM_PBLOCK, &pCurrentPB );
}
}
*pTmpFuncPtr = NULL;
done:
if ( rc != LDAP_SUCCESS && *ppFuncPtrs != NULL ) {
ch_free( *ppFuncPtrs );
*ppFuncPtrs = NULL;
}
return rc;
}
/*********************************************************************
* Function Name: createExtendedOp
*
* Description: Creates an extended operation structure and
* initializes the fields
*
* Return value: A newly allocated structure or NULL
********************************************************************/
ExtendedOp *
createExtendedOp()
{
ExtendedOp *ret;
ret = (ExtendedOp *)slapi_ch_malloc(sizeof(ExtendedOp));
ret->ext_oid.bv_val = NULL;
ret->ext_oid.bv_len = 0;
ret->ext_func = NULL;
ret->ext_be = NULL;
ret->ext_next = NULL;
return ret;
}
/*********************************************************************
* Function Name: slapi_int_unregister_extop
*
* Description: This routine removes the ExtendedOp structures
* asscoiated with a particular extended operation
* plugin.
*
* Input: pBE - pointer to a backend structure
* opList - pointer to a linked list of extended
* operation structures
* pPB - pointer to a slapi parameter block
*
* Output:
*
* Return Value: none
*
* Messages: None
*********************************************************************/
void
slapi_int_unregister_extop(
Backend *pBE,
ExtendedOp **opList,
Slapi_PBlock *pPB )
{
ExtendedOp *pTmpExtOp, *backExtOp;
char **pTmpOIDs;
int i;
#if 0
assert( pBE != NULL); /* unused */
#endif /* 0 */
assert( opList != NULL );
assert( pPB != NULL );
if ( *opList == NULL ) {
return;
}
slapi_pblock_get( pPB, SLAPI_PLUGIN_EXT_OP_OIDLIST, &pTmpOIDs );
if ( pTmpOIDs == NULL ) {
return;
}
for ( i = 0; pTmpOIDs[i] != NULL; i++ ) {
backExtOp = NULL;
pTmpExtOp = *opList;
for ( ; pTmpExtOp != NULL; pTmpExtOp = pTmpExtOp->ext_next) {
int rc;
rc = strcasecmp( pTmpExtOp->ext_oid.bv_val,
pTmpOIDs[ i ] );
if ( rc == 0 ) {
if ( backExtOp == NULL ) {
*opList = pTmpExtOp->ext_next;
} else {
backExtOp->ext_next
= pTmpExtOp->ext_next;
}
ch_free( pTmpExtOp );
break;
}
backExtOp = pTmpExtOp;
}
}
}
/*********************************************************************
* Function Name: slapi_int_register_extop
*
* Description: This routine creates a new ExtendedOp structure, loads
* in the extended op module and put the extended op function address
* in the structure. The function will not be executed in
* this routine.
*
* Input: pBE - pointer to a backend structure
* opList - pointer to a linked list of extended
* operation structures
* pPB - pointer to a slapi parameter block
*
* Output:
*
* Return Value: an LDAP return code
*
* Messages: None
*********************************************************************/
int
slapi_int_register_extop(
Backend *pBE,
ExtendedOp **opList,
Slapi_PBlock *pPB )
{
ExtendedOp *pTmpExtOp = NULL;
SLAPI_FUNC tmpFunc;
char **pTmpOIDs;
int rc = LDAP_OTHER;
int i;
if ( (*opList) == NULL ) {
*opList = createExtendedOp();
if ( (*opList) == NULL ) {
rc = LDAP_NO_MEMORY;
goto error_return;
}
pTmpExtOp = *opList;
} else { /* Find the end of the list */
for ( pTmpExtOp = *opList; pTmpExtOp->ext_next != NULL;
pTmpExtOp = pTmpExtOp->ext_next )
; /* EMPTY */
pTmpExtOp->ext_next = createExtendedOp();
if ( pTmpExtOp->ext_next == NULL ) {
rc = LDAP_NO_MEMORY;
goto error_return;
}
pTmpExtOp = pTmpExtOp->ext_next;
}
rc = slapi_pblock_get( pPB,SLAPI_PLUGIN_EXT_OP_OIDLIST, &pTmpOIDs );
if ( rc != 0 ) {
rc = LDAP_OTHER;
goto error_return;
}
rc = slapi_pblock_get(pPB,SLAPI_PLUGIN_EXT_OP_FN, &tmpFunc);
if ( rc != 0 ) {
rc = LDAP_OTHER;
goto error_return;
}
if ( (pTmpOIDs == NULL) || (tmpFunc == NULL) ) {
rc = LDAP_OTHER;
goto error_return;
}
for ( i = 0; pTmpOIDs[i] != NULL; i++ ) {
pTmpExtOp->ext_oid.bv_val = pTmpOIDs[i];
pTmpExtOp->ext_oid.bv_len = strlen( pTmpOIDs[i] );
pTmpExtOp->ext_func = tmpFunc;
pTmpExtOp->ext_be = pBE;
if ( pTmpOIDs[i + 1] != NULL ) {
pTmpExtOp->ext_next = createExtendedOp();
if ( pTmpExtOp->ext_next == NULL ) {
rc = LDAP_NO_MEMORY;
break;
}
pTmpExtOp = pTmpExtOp->ext_next;
}
}
error_return:
return rc;
}
/*********************************************************************
* Function Name: slapi_int_get_extop_plugin
*
* Description: This routine gets the function address for a given function
* name.
*
* Input:
* funcName - name of the extended op function, ie. an OID.
*
* Output: pFuncAddr - the function address of the requested function name.
*
* Return Values: a pointer to a newly created ExtendOp structure or
* NULL - function failed
*
* Messages: None
*********************************************************************/
int
slapi_int_get_extop_plugin(
struct berval *reqoid,
SLAPI_FUNC *pFuncAddr )
{
ExtendedOp *pTmpExtOp;
assert( reqoid != NULL );
assert( pFuncAddr != NULL );
*pFuncAddr = NULL;
if ( pGExtendedOps == NULL ) {
return LDAP_OTHER;
}
pTmpExtOp = pGExtendedOps;
while ( pTmpExtOp != NULL ) {
int rc;
rc = strcasecmp( reqoid->bv_val, pTmpExtOp->ext_oid.bv_val );
if ( rc == 0 ) {
*pFuncAddr = pTmpExtOp->ext_func;
break;
}
pTmpExtOp = pTmpExtOp->ext_next;
}
return ( *pFuncAddr == NULL ? 1 : 0 );
}
/***************************************************************************
* This function is similar to slapi_int_get_extop_plugin above. except it returns one OID
* per call. It is called from root_dse_info (root_dse.c).
* The function is a modified version of get_supported_extop (file extended.c).
***************************************************************************/
struct berval *
slapi_int_get_supported_extop( int index )
{
ExtendedOp *ext;
for ( ext = pGExtendedOps ; ext != NULL && --index >= 0;
ext = ext->ext_next) {
; /* empty */
}
if ( ext == NULL ) {
return NULL;
}
return &ext->ext_oid ;
}
/*********************************************************************
* Function Name: slapi_int_load_plugin
*
* Description: This routine loads the specified DLL, gets and executes the init function
* if requested.
*
* Input:
* pPlugin - a pointer to a Slapi_PBlock struct which will be passed to
* the DLL init function.
* path - path name of the DLL to be load.
* initfunc - either the DLL initialization function or an OID of the
* loaded extended operation.
* doInit - if it is TRUE, execute the init function, otherwise, save the
* function address but not execute it.
*
* Output: pInitFunc - the function address of the loaded function. This param
* should be not be null if doInit is FALSE.
* pLdHandle - handle returned by lt_dlopen()
*
* Return Values: LDAP_SUCCESS, LDAP_LOCAL_ERROR
*
* Messages: None
*********************************************************************/
static int
slapi_int_load_plugin(
Slapi_PBlock *pPlugin,
const char *path,
const char *initfunc,
int doInit,
SLAPI_FUNC *pInitFunc,
lt_dlhandle *pLdHandle )
{
int rc = LDAP_SUCCESS;
SLAPI_FUNC fpInitFunc = NULL;
assert( pLdHandle != NULL );
if ( lt_dlinit() ) {
return LDAP_LOCAL_ERROR;
}
/* load in the module */
*pLdHandle = lt_dlopen( path );
if ( *pLdHandle == NULL ) {
fprintf( stderr, "failed to load plugin %s: %s\n",
path, lt_dlerror() );
return LDAP_LOCAL_ERROR;
}
fpInitFunc = (SLAPI_FUNC)lt_dlsym( *pLdHandle, initfunc );
if ( fpInitFunc == NULL ) {
fprintf( stderr, "failed to find symbol %s in plugin %s: %s\n",
initfunc, path, lt_dlerror() );
lt_dlclose( *pLdHandle );
return LDAP_LOCAL_ERROR;
}
if ( doInit ) {
rc = ( *fpInitFunc )( pPlugin );
if ( rc != LDAP_SUCCESS ) {
lt_dlclose( *pLdHandle );
}
} else {
*pInitFunc = fpInitFunc;
}
return rc;
}
/*
* Special support for computed attribute plugins
*/
int
slapi_int_call_plugins(
Backend *be,
int funcType,
Slapi_PBlock *pPB )
{
int rc = 0;
SLAPI_FUNC *pGetPlugin = NULL, *tmpPlugin = NULL;
if ( pPB == NULL ) {
return 1;
}
rc = slapi_int_get_plugins( be, funcType, &tmpPlugin );
if ( rc != LDAP_SUCCESS || tmpPlugin == NULL ) {
/* Nothing to do, front-end should ignore. */
return rc;
}
for ( pGetPlugin = tmpPlugin ; *pGetPlugin != NULL; pGetPlugin++ ) {
rc = (*pGetPlugin)(pPB);
/*
* Only non-postoperation plugins abort processing on
* failure (confirmed with SLAPI specification).
*/
if ( !SLAPI_PLUGIN_IS_POST_FN( funcType ) && rc != 0 ) {
/*
* Plugins generally return negative error codes
* to indicate failure, although in the case of
* bind plugins they may return SLAPI_BIND_xxx
*/
break;
}
}
slapi_ch_free( (void **)&tmpPlugin );
return rc;
}
int
slapi_int_read_config(
Backend *be,
const char *fname,
int lineno,
int argc,
char **argv,
int index )
{
int iType = -1;
int numPluginArgc = 0;
if ( argc < 4 ) {
fprintf( stderr,
"%s: line %d: missing arguments "
"in \"plugin "
" []\" line\n",
fname, lineno );
return 1;
}
/* automatically instantiate overlay if necessary */
if ( !slapi_over_is_inst( be ) ) {
ConfigReply cr = { 0 };
if ( slapi_over_config( be, &cr ) != 0 ) {
fprintf( stderr, "Failed to instantiate SLAPI overlay: "
"err=%d msg=\"%s\"\n", cr.err, cr.msg );
return -1;
}
}
if ( strcasecmp( argv[1], "preoperation" ) == 0 ) {
iType = SLAPI_PLUGIN_PREOPERATION;
} else if ( strcasecmp( argv[1], "postoperation" ) == 0 ) {
iType = SLAPI_PLUGIN_POSTOPERATION;
} else if ( strcasecmp( argv[1], "extendedop" ) == 0 ) {
iType = SLAPI_PLUGIN_EXTENDEDOP;
} else if ( strcasecmp( argv[1], "object" ) == 0 ) {
iType = SLAPI_PLUGIN_OBJECT;
} else {
fprintf( stderr, "%s: line %d: invalid plugin type \"%s\".\n",
fname, lineno, argv[1] );
return 1;
}
numPluginArgc = argc - 4;
if ( iType == SLAPI_PLUGIN_PREOPERATION ||
iType == SLAPI_PLUGIN_EXTENDEDOP ||
iType == SLAPI_PLUGIN_POSTOPERATION ||
iType == SLAPI_PLUGIN_OBJECT ) {
int rc;
Slapi_PBlock *pPlugin;
pPlugin = plugin_pblock_new( iType, numPluginArgc, argv );
if (pPlugin == NULL) {
return 1;
}
if (iType == SLAPI_PLUGIN_EXTENDEDOP) {
rc = slapi_int_register_extop(be, &pGExtendedOps, pPlugin);
if ( rc != LDAP_SUCCESS ) {
slapi_pblock_destroy( pPlugin );
return 1;
}
}
rc = slapi_int_register_plugin_index( be, pPlugin, index );
if ( rc != LDAP_SUCCESS ) {
if ( iType == SLAPI_PLUGIN_EXTENDEDOP ) {
slapi_int_unregister_extop( be, &pGExtendedOps, pPlugin );
}
slapi_pblock_destroy( pPlugin );
return 1;
}
}
return 0;
}
int
slapi_int_unregister_plugin(
Backend *be,
Slapi_PBlock *pPlugin,
Slapi_PBlock *pPrev
)
{
int type;
assert( pPlugin != NULL );
slapi_pblock_get( pPlugin, SLAPI_PLUGIN_TYPE, (void *)&type );
if ( type == SLAPI_PLUGIN_EXTENDEDOP ) {
slapi_int_unregister_extop( be, &pGExtendedOps, pPlugin );
}
if ( pPrev != NULL ) {
Slapi_PBlock *pNext = NULL;
slapi_pblock_get( pPlugin, SLAPI_IBM_PBLOCK, &pNext );
slapi_pblock_set( pPrev, SLAPI_IBM_PBLOCK, &pNext );
}
slapi_pblock_destroy( pPlugin );
return LDAP_SUCCESS;
}
int
slapi_int_unregister_plugins(
Backend *be,
int index
)
{
Slapi_PBlock *pTmpPB = NULL;
Slapi_PBlock *pSavePB = NULL;
int rc = LDAP_SUCCESS;
pTmpPB = SLAPI_BACKEND_PBLOCK( be );
if ( pTmpPB == NULL ) {
return ( index < 0 ) ? LDAP_SUCCESS : LDAP_OTHER;
}
if ( index < 0 ) {
/* All plugins must go */
while ( pTmpPB != NULL && rc == LDAP_SUCCESS ) {
pSavePB = pTmpPB;
rc = slapi_pblock_get( pTmpPB, SLAPI_IBM_PBLOCK, &pTmpPB );
if ( pSavePB != NULL ) {
slapi_int_unregister_plugin( be, pSavePB, NULL );
}
}
} else if ( index == 0 ) {
slapi_pblock_get( pTmpPB, SLAPI_IBM_PBLOCK, &pSavePB );
SLAPI_BACKEND_PBLOCK( be ) = pSavePB;
slapi_int_unregister_plugin( be, pTmpPB, NULL );
} else {
int pos = -1;
while ( pTmpPB != NULL && rc == LDAP_SUCCESS && ++pos < index ) {
pSavePB = pTmpPB;
rc = slapi_pblock_get( pTmpPB, SLAPI_IBM_PBLOCK, &pTmpPB );
}
if ( pos == index ) {
slapi_int_unregister_plugin( be, pTmpPB, pSavePB );
}
}
return rc;
}
void
slapi_int_plugin_unparse(
Backend *be,
BerVarray *out
)
{
Slapi_PBlock *pp;
int i, j;
char **argv, ibuf[32], *ptr;
struct berval idx, bv;
*out = NULL;
idx.bv_val = ibuf;
i = 0;
for ( pp = SLAPI_BACKEND_PBLOCK( be );
pp != NULL;
slapi_pblock_get( pp, SLAPI_IBM_PBLOCK, &pp ) )
{
slapi_pblock_get( pp, SLAPI_X_CONFIG_ARGV, &argv );
if ( argv == NULL ) /* could be dynamic plugin */
continue;
idx.bv_len = snprintf( idx.bv_val, sizeof( ibuf ), "{%d}", i );
if ( idx.bv_len >= sizeof( ibuf ) ) {
/* FIXME: just truncating by now */
idx.bv_len = sizeof( ibuf ) - 1;
}
bv.bv_len = idx.bv_len;
for (j=1; argv[j]; j++) {
bv.bv_len += strlen(argv[j]);
if ( j ) bv.bv_len++;
}
bv.bv_val = ch_malloc( bv.bv_len + 1 );
ptr = lutil_strcopy( bv.bv_val, ibuf );
for (j=1; argv[j]; j++) {
if ( j ) *ptr++ = ' ';
ptr = lutil_strcopy( ptr, argv[j] );
}
ber_bvarray_add( out, &bv );
}
}
#endif /* HAVE_LTDL_H */