/* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * * Copyright 2002-2022 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 "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 *, ConfigArgs *c ); /* 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, ConfigArgs *c ) { Slapi_PBlock *pPlugin = NULL; Slapi_PluginDesc *pPluginDesc = NULL; lt_dlhandle hdLoadHandle; int rc; char **av2 = NULL, **ppPluginArgv; char *path = c->argv[2]; char *initfunc = c->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( c->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, c ); 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, ConfigArgs *c ) { 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 ) { snprintf( c->cr_msg, sizeof( c->cr_msg ), "failed to load plugin %s: %s", path, lt_dlerror() ); Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg ); return LDAP_LOCAL_ERROR; } fpInitFunc = (SLAPI_FUNC)lt_dlsym( *pLdHandle, initfunc ); if ( fpInitFunc == NULL ) { snprintf( c->cr_msg, sizeof( c->cr_msg ), "failed to find symbol %s in plugin %s: %s", initfunc, path, lt_dlerror() ); Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg ); 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( struct config_args_s *c ) { int iType = -1; int numPluginArgc = 0; if ( c->argc < 4 ) { snprintf( c->cr_msg, sizeof( c->cr_msg ), "missing arguments " "in \"plugin " " []\" line" ); Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg ); return 1; } /* automatically instantiate overlay if necessary */ if ( !slapi_over_is_inst( c->be ) ) { if ( slapi_over_config( c->be, &c->reply ) != 0 ) { Debug( LDAP_DEBUG_ANY, "%s: " "Failed to instantiate SLAPI overlay: " "err=%d msg=\"%s\"\n", c->log, c->reply.err, c->reply.msg ); return -1; } } if ( strcasecmp( c->argv[1], "preoperation" ) == 0 ) { iType = SLAPI_PLUGIN_PREOPERATION; } else if ( strcasecmp( c->argv[1], "postoperation" ) == 0 ) { iType = SLAPI_PLUGIN_POSTOPERATION; } else if ( strcasecmp( c->argv[1], "extendedop" ) == 0 ) { iType = SLAPI_PLUGIN_EXTENDEDOP; } else if ( strcasecmp( c->argv[1], "object" ) == 0 ) { iType = SLAPI_PLUGIN_OBJECT; } else { snprintf( c->cr_msg, sizeof( c->cr_msg ), "invalid plugin type \"%s\"", c->argv[1] ); Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg ); return 1; } numPluginArgc = c->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, c->argv ); if (pPlugin == NULL) { return 1; } if (iType == SLAPI_PLUGIN_EXTENDEDOP) { rc = slapi_int_register_extop(c->be, &pGExtendedOps, pPlugin); if ( rc != LDAP_SUCCESS ) { slapi_pblock_destroy( pPlugin ); return 1; } } rc = slapi_int_register_plugin_index( c->be, pPlugin, c->valx ); if ( rc != LDAP_SUCCESS ) { if ( iType == SLAPI_PLUGIN_EXTENDEDOP ) { slapi_int_unregister_extop( c->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 */