diff options
Diffstat (limited to 'ldap/servers/slapd/plugin.c')
-rw-r--r-- | ldap/servers/slapd/plugin.c | 2833 |
1 files changed, 2833 insertions, 0 deletions
diff --git a/ldap/servers/slapd/plugin.c b/ldap/servers/slapd/plugin.c new file mode 100644 index 00000000..5477f3e2 --- /dev/null +++ b/ldap/servers/slapd/plugin.c @@ -0,0 +1,2833 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* plugin.c - routines for setting up and calling plugins */ + +#include <stddef.h> +#include <stdio.h> +#include <plhash.h> +#include "slap.h" + +/* this defines are used for plugin configuration */ +#define LOCAL_DATA "local data" +#define REMOTE_DATA "remote data" +#define ALL_DATA "*" +#define ROOT_BIND "directory manager" +#define ANONYMOUS_BIND "anonymous" + +/* Forward Declarations */ +static int plugin_call_list (struct slapdplugin *list, int operation, Slapi_PBlock *pb); +static int plugin_call_one (struct slapdplugin *list, int operation, Slapi_PBlock *pb); +static int plugin_call_func (struct slapdplugin *list, int operation, Slapi_PBlock *pb, int call_one); + +static PRBool plugin_invoke_plugin_pb (struct slapdplugin *plugin, int operation, Slapi_PBlock *pb); +static PRBool plugin_matches_operation (Slapi_DN *target_spec, PluginTargetData *ptd, + PRBool bindop, PRBool isroot, PRBool islocal, int method); +static int isApprovedPlugin( struct slapdplugin *plugin ); + +static void plugin_config_init (struct pluginconfig *config); +static void plugin_config_cleanup (struct pluginconfig *config); +static int plugin_config_set_action (int *action, char *value); +static struct pluginconfig* plugin_get_config (struct slapdplugin *plugin); +static void default_plugin_init(); +static void ptd_init (PluginTargetData *ptd); +static void ptd_cleanup (PluginTargetData *ptd); +static void ptd_add_subtree (PluginTargetData *ptd, Slapi_DN *subtree); +static void ptd_set_special_data (PluginTargetData *ptd, int type); +static Slapi_DN *ptd_get_first_subtree (const PluginTargetData *ptd, int *cookie); +static Slapi_DN *ptd_get_next_subtree (const PluginTargetData *ptd, int *cookie); +static PRBool ptd_is_special_data_set (const PluginTargetData *ptd, int type); +int ptd_get_subtree_count (const PluginTargetData *ptd); +static void plugin_set_global (PluginTargetData *ptd); +static PRBool plugin_is_global (const PluginTargetData *ptd); +static void plugin_set_default_access (struct pluginconfig *config); + +static PLHashTable *global_plugin_dns = NULL; + +/* The global plugin list is indexed by the PLUGIN_LIST_* constants defined in slap.h */ +static struct slapdplugin *global_plugin_list[PLUGIN_LIST_GLOBAL_MAX]; + +/* plugin structure used to configure internal operation issued by the core server */ +static int global_server_plg_initialised= 0; +struct slapdplugin global_server_plg; + +/* plugin structure used to configure internal operation issued by the core server */ +static int global_server_plg_id_initialised= 0; +struct slapi_componentid global_server_id_plg; + +/* plugin structure used to configure operations issued by the old plugins that + do not pass their identity in the operation */ +static struct slapdplugin global_default_plg; + +/* Enable/disable plugin callbacks for clean startup */ +static int global_plugin_callbacks_enabled = 0; + + +static void +add_plugin_to_list(struct slapdplugin **list, struct slapdplugin *plugin) +{ + struct slapdplugin **tmp; + for ( tmp = list; *tmp; tmp = &(*tmp)->plg_next ) + { + ; /* NULL */ + } + *tmp = plugin; +} + +struct slapdplugin * +get_plugin_list(int plugin_list_index) +{ + return global_plugin_list[plugin_list_index]; +} + +/* + * As the plugin configuration information is read an array of + * entries is built which reflect the plugins. The entries + * are added after the syntax plugins are started so that the + * nodes in the attribute tree are initialised correctly. + */ +typedef struct entry_and_plugin { + Slapi_Entry *e; + struct slapdplugin *plugin; + struct entry_and_plugin *next; +} entry_and_plugin_t; + +static entry_and_plugin_t *plugin_entries = NULL; +static entry_and_plugin_t *dep_plugin_entries = NULL; /* for dependencies */ + +#if 0 +static void +add_plugin_entries() +{ + entry_and_plugin_t *ep = plugin_entries; + entry_and_plugin_t *deleteep = 0; + while (ep) + { + int plugin_actions = 0; + Slapi_PBlock newpb; + pblock_init(&newpb); + slapi_add_entry_internal_set_pb(&newpb, ep->e, NULL, + ep->plugin, plugin_actions); + slapi_pblock_set(&newpb, SLAPI_TARGET_DN, (void*)slapi_entry_get_dn_const(ep->e)); + slapi_add_internal_pb(&newpb); + deleteep = ep; + ep = ep->next; + slapi_ch_free((void**)&deleteep); + pblock_done(&newpb); + } + + plugin_entries = NULL; +} +#endif + +static void +new_plugin_entry(entry_and_plugin_t **ep, Slapi_Entry *e, struct slapdplugin *plugin) +{ + entry_and_plugin_t *oldep = 0; + entry_and_plugin_t *iterep = *ep; + + entry_and_plugin_t *newep = + (entry_and_plugin_t*)slapi_ch_calloc(1,sizeof(entry_and_plugin_t)); + newep->e = e; + newep->plugin = plugin; + + while(iterep) + { + oldep = iterep; + iterep = iterep->next; + } + + newep->next = 0; + + if(oldep) + oldep->next = newep; + else + *ep = newep; +} + + +static void +add_plugin_entry_dn(const Slapi_DN *plugin_dn) +{ + if (!global_plugin_dns) + { + global_plugin_dns = PL_NewHashTable(20, PL_HashString, + PL_CompareStrings, + PL_CompareValues, 0, 0); + } + + PL_HashTableAdd(global_plugin_dns, + slapi_sdn_get_ndn(plugin_dn), + (void*)plugin_dn); +} + +#define SLAPI_PLUGIN_NONE_IF_NULL( s ) ((s) == NULL ? "none" : (s)) + +/* + * Allows a plugin to register a plugin. + * This was added so that 'object' plugins could register all + * the plugin interfaces that it supports. + */ +int +slapi_register_plugin( + const char *plugintype, + int enabled, + const char *initsymbol, + slapi_plugin_init_fnptr initfunc, + const char *name, + char **argv, + void *group_identity +) +{ + int ii = 0; + int rc = 0; + Slapi_Entry *e = slapi_entry_alloc(); + char *dn = slapi_ch_calloc(1, strlen(name) + strlen(PLUGIN_BASE_DN) + 10); + + sprintf(dn, "cn=%s, %s", name, PLUGIN_BASE_DN); + /* this function consumes dn */ + slapi_entry_init(e, dn, NULL); + + slapi_entry_attr_set_charptr(e, "cn", name); + slapi_entry_attr_set_charptr(e, ATTR_PLUGIN_TYPE, plugintype); + if (!enabled) + slapi_entry_attr_set_charptr(e, ATTR_PLUGIN_ENABLED, "off"); + + slapi_entry_attr_set_charptr(e, ATTR_PLUGIN_INITFN, initsymbol); + + for (ii = 0; argv && argv[ii]; ++ii) { + char argname[64]; + sprintf(argname, "%s%d", ATTR_PLUGIN_ARG, ii); + slapi_entry_attr_set_charptr(e, argname, argv[ii]); + } + + /* plugin_setup copies the given entry */ + plugin_setup(e, group_identity, initfunc, 0); + slapi_entry_free(e); + + return rc; +} + +int +plugin_call_plugins( Slapi_PBlock *pb, int whichfunction ) +{ + int plugin_list_number= -1; + int rc= 0; + int do_op = global_plugin_callbacks_enabled; + + if ( pb == NULL ) + { + return( 0 ); + } + + switch ( whichfunction ) { + case SLAPI_PLUGIN_PRE_BIND_FN: + case SLAPI_PLUGIN_PRE_UNBIND_FN: + case SLAPI_PLUGIN_PRE_SEARCH_FN: + case SLAPI_PLUGIN_PRE_COMPARE_FN: + case SLAPI_PLUGIN_PRE_MODIFY_FN: + case SLAPI_PLUGIN_PRE_MODRDN_FN: + case SLAPI_PLUGIN_PRE_ADD_FN: + case SLAPI_PLUGIN_PRE_DELETE_FN: + case SLAPI_PLUGIN_PRE_ABANDON_FN: + case SLAPI_PLUGIN_PRE_ENTRY_FN: + case SLAPI_PLUGIN_PRE_REFERRAL_FN: + case SLAPI_PLUGIN_PRE_RESULT_FN: + plugin_list_number= PLUGIN_LIST_PREOPERATION; + break; + case SLAPI_PLUGIN_POST_BIND_FN: + case SLAPI_PLUGIN_POST_UNBIND_FN: + case SLAPI_PLUGIN_POST_SEARCH_FN: + case SLAPI_PLUGIN_POST_SEARCH_FAIL_FN: + case SLAPI_PLUGIN_POST_COMPARE_FN: + case SLAPI_PLUGIN_POST_MODIFY_FN: + case SLAPI_PLUGIN_POST_MODRDN_FN: + case SLAPI_PLUGIN_POST_ADD_FN: + case SLAPI_PLUGIN_POST_DELETE_FN: + case SLAPI_PLUGIN_POST_ABANDON_FN: + case SLAPI_PLUGIN_POST_ENTRY_FN: + case SLAPI_PLUGIN_POST_REFERRAL_FN: + case SLAPI_PLUGIN_POST_RESULT_FN: + plugin_list_number= PLUGIN_LIST_POSTOPERATION; + break; + case SLAPI_PLUGIN_BE_PRE_MODIFY_FN: + case SLAPI_PLUGIN_BE_PRE_MODRDN_FN: + case SLAPI_PLUGIN_BE_PRE_ADD_FN: + case SLAPI_PLUGIN_BE_PRE_DELETE_FN: + plugin_list_number= PLUGIN_LIST_BEPREOPERATION; + do_op = 1; /* always allow backend callbacks (even during startup) */ + break; + case SLAPI_PLUGIN_BE_POST_MODIFY_FN: + case SLAPI_PLUGIN_BE_POST_MODRDN_FN: + case SLAPI_PLUGIN_BE_POST_ADD_FN: + case SLAPI_PLUGIN_BE_POST_DELETE_FN: + plugin_list_number= PLUGIN_LIST_BEPOSTOPERATION; + do_op = 1; /* always allow backend callbacks (even during startup) */ + break; + case SLAPI_PLUGIN_INTERNAL_PRE_MODIFY_FN: + case SLAPI_PLUGIN_INTERNAL_PRE_MODRDN_FN: + case SLAPI_PLUGIN_INTERNAL_PRE_ADD_FN: + case SLAPI_PLUGIN_INTERNAL_PRE_DELETE_FN: + plugin_list_number= PLUGIN_LIST_INTERNAL_PREOPERATION; + break; + case SLAPI_PLUGIN_INTERNAL_POST_MODIFY_FN: + case SLAPI_PLUGIN_INTERNAL_POST_MODRDN_FN: + case SLAPI_PLUGIN_INTERNAL_POST_ADD_FN: + case SLAPI_PLUGIN_INTERNAL_POST_DELETE_FN: + plugin_list_number= PLUGIN_LIST_INTERNAL_POSTOPERATION; + break; + } + if(plugin_list_number!=-1 && do_op) + { + /* We stash the pblock plugin pointer to preserve the callers context */ + struct slapdplugin *p; + slapi_pblock_get(pb, SLAPI_PLUGIN, &p); + /* Call the operation on the Global Plugins */ + rc= plugin_call_list(global_plugin_list[plugin_list_number], whichfunction, pb); + + slapi_pblock_set(pb, SLAPI_PLUGIN, p); + } + else + { + /* Programmer error! or the callback is denied during startup */ + } + return rc; +} + + +void +plugin_call_entrystore_plugins(char **entrystr, uint *size) +{ + struct slapdplugin *p; + for (p = global_plugin_list[PLUGIN_LIST_LDBM_ENTRY_FETCH_STORE]; + p != NULL; p = p->plg_next ) + { + if (p->plg_entrystorefunc) + (*p->plg_entrystorefunc)(entrystr, size); + } +} + +void +plugin_call_entryfetch_plugins(char **entrystr, uint *size) +{ + struct slapdplugin *p; + for (p = global_plugin_list[PLUGIN_LIST_LDBM_ENTRY_FETCH_STORE]; + p != NULL; p = p->plg_next ) + { + if (p->plg_entryfetchfunc) + (*p->plg_entryfetchfunc)(entrystr, size); + } +} + +/* + * call extended operation plugins + * + * return SLAPI_PLUGIN_EXTENDED_SENT_RESULT if one of the extended operation + * plugins sent a result. + * return SLAPI_PLUGIN_EXTENDED_NOT_HANDLED if no extended operation plugins + * handled the operation. + * otherwise, return an LDAP error code (possibly a merge of the errors + * returned by the plugins we called). + */ +int +plugin_call_exop_plugins( Slapi_PBlock *pb, char *oid ) +{ + struct slapdplugin *p; + int i, rc; + int lderr = SLAPI_PLUGIN_EXTENDED_NOT_HANDLED; + + for ( p = global_plugin_list[PLUGIN_LIST_EXTENDED_OPERATION]; p != NULL; p = p->plg_next ) { + if ( p->plg_exhandler != NULL ) { + if ( p->plg_exoids != NULL ) { + for ( i = 0; p->plg_exoids[i] != NULL; i++ ) { + if ( strcasecmp( oid, p->plg_exoids[i] ) + == 0 ) { + break; + } + } + if ( p->plg_exoids[i] == NULL ) { + continue; + } + } + + slapi_pblock_set( pb, SLAPI_PLUGIN, p ); + set_db_default_result_handlers( pb ); + if ( (rc = (*p->plg_exhandler)( pb )) + == SLAPI_PLUGIN_EXTENDED_SENT_RESULT ) { + return( rc ); /* result sent */ + } else if ( rc != SLAPI_PLUGIN_EXTENDED_NOT_HANDLED ) { + /* + * simple merge: report last real error + */ + if ( lderr == SLAPI_PLUGIN_EXTENDED_NOT_HANDLED + || rc != LDAP_SUCCESS ) { + lderr = rc; + } + } + } + } + + return( lderr ); +} + + +/* + * Attempt to convert the extended operation 'oid' to a string by + * examining the registered plugins. Returns NULL if no plugin is + * registered for this OID. + * + * Our first choice is to use an OID-specific name that has been + * registered by a plugin via the SLAPI_PLUGIN_EXT_OP_NAMELIST pblock setting. + * Our second choice is to use the plugin's ID (short name). + * Our third choice is to use the plugin's RDN (under cn=config). + */ +const char * +plugin_extended_op_oid2string( const char *oid ) +{ + struct slapdplugin *p; + int i, j; + const char *rval = NULL; + + for ( p = global_plugin_list[PLUGIN_LIST_EXTENDED_OPERATION]; p != NULL; + p = p->plg_next ) { + if ( p->plg_exhandler != NULL && p->plg_exoids != NULL ) { + for ( i = 0; p->plg_exoids[i] != NULL; i++ ) { + if ( strcasecmp( oid, p->plg_exoids[i] ) == 0 ) { + if ( NULL != p->plg_exnames ) { + for ( j = 0; j < i && p->plg_exnames[j] != NULL; ++j ) { + ; + } + rval = p->plg_exnames[j]; /* OID-related name */ + } + + if ( NULL == rval ) { + if ( NULL != p->plg_desc.spd_id ) { + rval = p->plg_desc.spd_id; /* short name */ + } else { + rval = p->plg_name; /* RDN */ + } + } + break; + } + if ( p->plg_exoids[i] != NULL ) { + break; + } + } + } + } + + return( rval ); +} + + +/* + * kexcoff: return the slapdplugin structure + */ +struct slapdplugin * +plugin_get_pwd_storage_scheme(char *name, int len, int index) +{ + /* index could be PLUGIN_LIST_PWD_STORAGE_SCHEME or PLUGIN_LIST_REVER_PWD_STORAGE_SCHEME */ + struct slapdplugin *p; + + for ( p = global_plugin_list[index]; p != NULL; p = p->plg_next ) { + if (strncasecmp(p->plg_pwdstorageschemename, name, len) == 0) + return( p ); + } + return( NULL ); +} + +char * +plugin_get_pwd_storage_scheme_list(int index) +{ + /* index could be PLUGIN_LIST_PWD_STORAGE_SCHEME or PLUGIN_LIST_REVER_PWD_STORAGE_SCHEME */ + + struct slapdplugin *p = NULL; + char *names_list = NULL; + int len = 0; + + /* first pass - calculate space needed for comma delimited list */ + for ( p = global_plugin_list[index]; p != NULL; p = p->plg_next ) { + if ( p->plg_pwdstorageschemeenc != NULL ) + { + /* + 1 for comma, 1 for space, 1 for null */ + len += strlen(p->plg_pwdstorageschemename) + 3; + } + } + + /* no plugins? */ + if (!len) + return NULL; + + /* next, allocate the space */ + names_list = (char *)slapi_ch_malloc(len+1); + *names_list = 0; + + /* second pass - write the string */ + for ( p = global_plugin_list[index]; p != NULL; p = p->plg_next ) { + if ( p->plg_pwdstorageschemeenc != NULL ) + { + strcat(names_list, p->plg_pwdstorageschemename); + if (p->plg_next != NULL) + strcat(names_list, ", "); + } + } + return( names_list ); +} + +int +slapi_send_ldap_search_entry( Slapi_PBlock *pb, Slapi_Entry *e, LDAPControl **ectrls, + char **attrs, int attrsonly ) +{ + IFP fn = NULL; + slapi_pblock_get(pb,SLAPI_PLUGIN_DB_ENTRY_FN,(void*)&fn); + if (NULL == fn) + { + return -1; + } + return (*fn)(pb,e,ectrls,attrs,attrsonly); +} + +void +slapi_set_ldap_result( Slapi_PBlock *pb, int err, char *matched, char *text, + int nentries, struct berval **urls ) +{ + char * old_matched = NULL; + char * old_text = NULL; + char * matched_copy = slapi_ch_strdup(matched); + char * text_copy = slapi_ch_strdup(text); + + /* free the old matched and text, if any */ + slapi_pblock_get(pb, SLAPI_RESULT_MATCHED, &old_matched); + slapi_ch_free_string(&old_matched); + + slapi_pblock_get(pb, SLAPI_RESULT_TEXT, &old_text); + slapi_ch_free_string(&old_text); + + /* set the new stuff */ + slapi_pblock_set(pb, SLAPI_RESULT_CODE, &err); + slapi_pblock_set(pb, SLAPI_RESULT_MATCHED, matched_copy); + slapi_pblock_set(pb, SLAPI_RESULT_TEXT, text_copy); +} + +void +slapi_send_ldap_result_from_pb( Slapi_PBlock *pb) +{ + int err; + char *matched; + char *text; + IFP fn = NULL; + + slapi_pblock_get(pb, SLAPI_RESULT_CODE, &err); + slapi_pblock_get(pb, SLAPI_RESULT_TEXT, &text); + slapi_pblock_get(pb, SLAPI_RESULT_MATCHED, &matched); + + slapi_pblock_get(pb,SLAPI_PLUGIN_DB_RESULT_FN,(void*)&fn); + if (NULL != fn) + { + (*fn)(pb,err,matched,text,0,NULL); + } + + slapi_pblock_set(pb, SLAPI_RESULT_TEXT, NULL); + slapi_pblock_set(pb, SLAPI_RESULT_MATCHED, NULL); + slapi_ch_free((void **)&matched); + slapi_ch_free((void **)&text); +} + +void +slapi_send_ldap_result( Slapi_PBlock *pb, int err, char *matched, char *text, + int nentries, struct berval **urls ) +{ + IFP fn = NULL; + Slapi_Operation *operation; + long op_type; + + /* GB : for spanning requests over multiple backends */ + if (err == LDAP_NO_SUCH_OBJECT) + { + slapi_pblock_get (pb, SLAPI_OPERATION, &operation); + + op_type = operation_get_type(operation); + if (op_type == SLAPI_OPERATION_SEARCH) + { + if (urls || nentries) + { + LDAPDebug( LDAP_DEBUG_ANY, "ERROR : urls or nentries set" + "in sendldap_result while NO_SUCH_OBJECT returned\n",0,0,0); + } + + slapi_set_ldap_result(pb, err, matched, text, 0, NULL); + return; + } + } + + slapi_pblock_set(pb, SLAPI_RESULT_CODE, &err); + + slapi_pblock_get(pb,SLAPI_PLUGIN_DB_RESULT_FN,(void*)&fn); + if (NULL == fn) + { + return ; + } + + /* + * Call the result function. It should set pb->pb_op->o_status to + * SLAPI_OP_STATUS_RESULT_SENT right after sending the result to + * the client or otherwise consuming it. + */ + (*fn)(pb,err,matched,text,nentries,urls); +} + +int +slapi_send_ldap_referral( Slapi_PBlock *pb, Slapi_Entry *e, struct berval **refs, + struct berval ***urls ) +{ + IFP fn = NULL; + slapi_pblock_get(pb,SLAPI_PLUGIN_DB_REFERRAL_FN,(void*)&fn); + if (NULL == fn) + { + return -1; + } + return (*fn)(pb,e,refs,urls); +} + + +/*********************************************************** + start of plugin dependency code +************************************************************/ + +/* struct _plugin_dep_type + * we shall not presume to know all plugin types + * so as to allow new types to be added without + * requiring changes to this code (hopefully) + * so we need to dynamically keep track of them + */ + +typedef struct _plugin_dep_type{ + char *type; /* the string descriptor */ + int num_not_started; /* the count of plugins which have yet to be started for this type */ + struct _plugin_dep_type *next; +} *plugin_dep_type; + +/* _plugin_dep_config + * we need somewhere to collect the plugin configurations + * prior to attempting to resolve dependencies + */ + +typedef struct _plugin_dep_config { + char *name; + char *type; + Slapi_PBlock pb; + struct slapdplugin *plugin; + Slapi_Entry *e; + int entry_created; + int op_done; + char **depends_type_list; + int total_type; + char **depends_named_list; + int total_named; +} plugin_dep_config; + +/* list of plugins which should be shutdown in reverse order */ +static plugin_dep_config *global_plugin_shutdown_order = 0; +static int global_plugins_started = 0; + +/* + * find_plugin_type + * + * searches the list for the plugin type + * and returns the plugin_dep_type if found + */ + +static plugin_dep_type +find_plugin_type(plugin_dep_type head, char *type) +{ + plugin_dep_type ret = 0; + plugin_dep_type iter = head; + + while(iter) + { + if(!slapi_UTF8CASECMP(iter->type, type)) + { + ret = iter; + break; + } + + iter = iter->next; + } + + return ret; +} + + +/* + * increment_plugin_type + * + * searches the list for the plugin type + * and increments its not started value + * returns the current type count on success -1 on failure + * to find the type + */ + +static int +increment_plugin_type(plugin_dep_type head, char *type) +{ + int ret = -1; + plugin_dep_type the_type; + + if ((the_type = find_plugin_type(head, type)) != NULL) + ret = ++the_type->num_not_started; + + return ret; +} + + +/* + * decrement_plugin_type + * + * searches the list for the plugin type + * and decrements its not started value + * returns the current type count on success -1 on failure + * to find the type + */ + +static int +decrement_plugin_type(plugin_dep_type head, char *type) +{ + int ret = -1; + plugin_dep_type the_type; + + if ((the_type = find_plugin_type(head, type)) != NULL) + ret = --the_type->num_not_started; + + return ret; +} + +/* + * add_plugin_type + * + * Either increments the count of the plugin type + * or when it does not exist, adds it to the list + */ + +static int +add_plugin_type(plugin_dep_type *head, char *type) +{ + int ret = -1; + + if(*head) + { + if(0 < increment_plugin_type(*head, type)) + { + ret = 0; + } + } + + if(ret) + { + /* create new head */ + plugin_dep_type tmp_head; + + tmp_head = (plugin_dep_type)slapi_ch_malloc(sizeof(struct _plugin_dep_type)); + tmp_head->num_not_started = 1; + tmp_head->type = slapi_ch_strdup(type); + ret = 0; + tmp_head->next = *head; + (*head) = tmp_head; + } + + return ret; +} + + +/* + * plugin_create_stringlist + * + * Creates a string list from values of the entries + * attribute passed in as args - used to track dependencies + * + */ + +int +plugin_create_stringlist( Slapi_Entry *plugin_entry, char *attr_name, + int *total_strings, char ***list) +{ + Slapi_Attr *attr = 0; + int hint =0; + int num_vals = 0; + int val_index = 0; + Slapi_Value *val; + + if(0 == slapi_entry_attr_find( plugin_entry, attr_name, &attr )) + { + /* allocate memory for the string array */ + slapi_attr_get_numvalues( attr, &num_vals); + + if(num_vals) + { + *total_strings = num_vals; + *list = (char **)slapi_ch_malloc(sizeof(char*) * num_vals); + } + else + goto bail; /* if this ever happens, then they are running on a TSR-80 */ + + val_index = 0; + + hint = slapi_attr_first_value( attr, &val ); + while(val_index < num_vals) + { + /* add the value to the array */ + (*list)[val_index] = (char*)slapi_ch_strdup(slapi_value_get_string(val)); + + hint = slapi_attr_next_value( attr, hint, &val ); + val_index++; + } + } + else + *total_strings = num_vals; + +bail: + return num_vals; +} + + + +/* + * plugin_dependency_startall() + * + * Starts all plugins (apart from syntax and matching rule) in order + * of dependency. + * + * Dependencies will be determined by these multi-valued attributes: + * + * nsslapd-plugin-depends-on-type : all plugins whose type value matches one of these values must + * be started prior to this plugin + * + * nsslapd-plugin-depends-on-named : the plugin whose cn value matches one of these values must + * be started prior to this plugin + */ + +static int +plugin_dependency_startall(int argc, char** argv, char *errmsg, int operation) +{ + int ret = 0; + Slapi_PBlock pb; + int total_plugins = 0; + plugin_dep_config *config = 0; + plugin_dep_type plugin_head = 0; + int plugin_index = 0; + Slapi_Entry *plugin_entry; + int i = 0; /* general index iterator */ + plugin_dep_type the_plugin_type; + int index = 0; + char * value; + int plugins_started; + int num_plg_started; + struct slapdplugin *plugin; + entry_and_plugin_t *ep = dep_plugin_entries; + int shutdown_index = 0; + + /* + * Disable registered plugin functions so preops/postops/etc + * dont get called prior to the plugin being started (due to + * plugins performing ops on the DIT) + */ + + global_plugin_callbacks_enabled = 0; + + /* Count the plugins so we can allocate memory for the config array */ + while(ep) + { + total_plugins++; + + ep = ep->next; + } + + /* allocate the config array */ + config = (plugin_dep_config*)slapi_ch_malloc(sizeof(plugin_dep_config) * total_plugins); + + if(config) + memset(config, 0, sizeof(plugin_dep_config) * total_plugins); + else + { + ret = -1; + goto bail; + } + + ep = dep_plugin_entries; + + /* Collect relevant config */ + while(ep) + { + plugin = ep->plugin; + + if(plugin == 0) + continue; + + pblock_init(&pb); + slapi_pblock_set( &pb, SLAPI_ARGC, &argc); + slapi_pblock_set( &pb, SLAPI_ARGV, &argv); + + config[plugin_index].pb = pb; + config[plugin_index].e = ep->e; + + /* add type */ + plugin_entry = ep->e; + ep->e = NULL; /* consumed by the operation above, and eventually by the + slapi_internal_add operation below */ + + if(plugin_entry) + { + /* + * Pass the plugin DN in SLAPI_TARGET_DN and the plugin entry + * in SLAPI_ADD_ENTRY. For this to actually work, we need to + * create an operation and include that in the pblock as well, + * because these two items are stored in the operation parameters. + */ + Operation *op = internal_operation_new(SLAPI_OPERATION_ADD, 0); + slapi_pblock_set(&(config[plugin_index].pb), SLAPI_OPERATION, op); + slapi_pblock_set(&(config[plugin_index].pb), SLAPI_TARGET_DN, + (void*)(slapi_entry_get_dn_const(plugin_entry))); + slapi_pblock_set(&(config[plugin_index].pb), SLAPI_ADD_ENTRY, + plugin_entry ); + + value = slapi_entry_attr_get_charptr(plugin_entry, "nsslapd-plugintype"); + if(value) + { + add_plugin_type( &plugin_head, value); + config[plugin_index].type = value; + value = NULL; + } + + /* now the name */ + value = slapi_entry_attr_get_charptr(plugin_entry, "cn"); + if(value) + { + config[plugin_index].name = value; + value = NULL; + } + + + config[plugin_index].plugin = plugin; + + /* now add dependencies */ + plugin_create_stringlist( plugin_entry, "nsslapd-plugin-depends-on-named", + &(config[plugin_index].total_named), &(config[plugin_index].depends_named_list)); + + plugin_create_stringlist( plugin_entry, "nsslapd-plugin-depends-on-type", + &(config[plugin_index].total_type), &(config[plugin_index].depends_type_list)); + } + + plugin_index++; + ep = ep->next; + } + + /* prepare list of shutdown order (we need nothing fancier right now + * than the reverse startup order) The list may include NULL entries, + * these will be plugins which were never started + */ + shutdown_index = total_plugins - 1; + + global_plugin_shutdown_order = (plugin_dep_config*)slapi_ch_malloc(sizeof(plugin_dep_config) * total_plugins); + if(global_plugin_shutdown_order) + memset(global_plugin_shutdown_order, 0, sizeof(plugin_dep_config) * total_plugins); + else + { + ret = -1; + goto bail; + } + + /* now resolve dependencies + * cycle through list, if a plugin has no dependencies then start it + * then remove it from the dependency lists of all other plugins + * and decrement the corresponding element of the plugin types array + * for depends_type we will need to check the array of plugin types + * to see if all type dependencies are at zero prior to start + * if one cycle fails to load any plugins we have failed, however + * we shall continue loading plugins in case a configuration error + * can correct itself + */ + + plugins_started = 1; + num_plg_started = 0; + + while(plugins_started && num_plg_started < total_plugins) + { + plugins_started = 0; + + for(plugin_index=0; plugin_index < total_plugins; plugin_index++) + { + /* perform op on plugins only once */ + if(config[plugin_index].op_done == 0) + { + int enabled = 0; + int satisfied = 0; + int break_out = 0; + + /* + * determine if plugin is enabled + * some processing is necessary even + * if it is not + */ + if ( NULL != config[plugin_index].e && (value = slapi_entry_attr_get_charptr(config[plugin_index].e, + ATTR_PLUGIN_ENABLED)) && + !strcasecmp(value, "on")) + { + enabled = 1; + } + else + enabled = 0; + + slapi_ch_free((void**)&value); + + /* + * make sure named dependencies have been satisfied + * that means that the list of names should contain all + * null entries + */ + + if(enabled && config[plugin_index].total_named) + { + i = 0; + + while(break_out == 0 && i < config[plugin_index].total_named) + { + satisfied = 1; + + if((config[plugin_index].depends_named_list)[i] != 0) + { + satisfied = 0; + break_out = 1; + } + + i++; + } + + if(!satisfied) + continue; + } + + /* + * make sure the type dependencies have been satisfied + * that means for each type in the list, it's number of + * plugins left not started is zero + * + */ + satisfied = 0; + break_out = 0; + + if(enabled && config[plugin_index].total_type) + { + i = 0; + + while(break_out == 0 && i < config[plugin_index].total_type) + { + satisfied = 1; + + the_plugin_type = find_plugin_type(plugin_head, (config[plugin_index].depends_type_list)[i]); + + if(the_plugin_type && the_plugin_type->num_not_started != 0) + { + satisfied = 0; + break_out = 1; + } + + i++; + } + + if(!satisfied) + continue; + } + + /**** This plugins dependencies have now been satisfied ****/ + + satisfied = 1; /* symbolic only */ + + /* + * Add the plugins entry to the DSE so the plugin can get + * its config (both enabled and disabled have entries + */ + + if(!config[plugin_index].entry_created) + { + int plugin_actions = 0; + Slapi_PBlock newpb; + Slapi_Entry *newe; + + pblock_init(&newpb); + /* + * config[plugin_index].e is freed up by + * below function calls, but we may need + * it later, so create a copy + */ + newe = slapi_entry_dup( config[plugin_index].e ); + slapi_add_entry_internal_set_pb(&newpb, newe, NULL, + plugin_get_default_component_id(), plugin_actions); + slapi_pblock_set(&newpb, SLAPI_TARGET_DN, (void*)slapi_entry_get_dn_const(newe)); + slapi_add_internal_pb(&newpb); + pblock_done(&newpb); + config[plugin_index].entry_created = 1; + } + + /* + * only actually start plugin and remove from lists if its enabled + * we do remove from plugin type list however - rule is dependency on + * zero or more for type + */ + + if (enabled) + { + /* finally, perform the op on the plugin */ + + LDAPDebug( LDAP_DEBUG_PLUGIN, "Starting %s plugin %s\n" , config[plugin_index].type, config[plugin_index].name, 0 ); + + ret = plugin_call_one( config[plugin_index].plugin, operation, &(config[plugin_index].pb)); + + pblock_done(&(config[plugin_index].pb)); + + if(ret) + { + /* + * We will not exit here. If we allow plugins to load normally it is + * possible that a configuration error (dependedncies which were not + * configured properly) can be recovered from. If there really is a + * problem then the plugin will never start and eventually it will + * trigger an exit anyway. + */ + LDAPDebug( LDAP_DEBUG_ANY, "Failed to start %s plugin %s\n" , config[plugin_index].type, config[plugin_index].name, 0 ); + continue; + } + + /* Add this plugin to the shutdown list */ + + global_plugin_shutdown_order[shutdown_index] = config[plugin_index]; + shutdown_index--; + global_plugins_started++; + + /* remove this named plugin from other plugins lists */ + + for(i=0; i<total_plugins; i++) + { + index = 0; + + while(index < config[i].total_named) + { + if((config[i].depends_named_list)[index] != 0 && !slapi_UTF8CASECMP((config[i].depends_named_list)[index], config[plugin_index].name)) + { + slapi_ch_free((void**)&((config[i].depends_named_list)[index])); + (config[i].depends_named_list)[index] = 0; + } + + index++; + } + } + } + + /* decrement the type counter for this plugin type */ + + decrement_plugin_type(plugin_head, config[plugin_index].type); + config[plugin_index].op_done = 1; + num_plg_started++; + plugins_started = 1; + } + + } + } + + if(plugins_started == 0) + { + /* a dependency was not resolved - error */ + LDAPDebug( LDAP_DEBUG_ANY, "Error: Failed to resolve plugin dependencies\n" , 0, 0, 0 ); + + /* list the plugins yet to perform op */ + index = 0; + + while(i < total_plugins) + { + if(config[i].op_done == 0) + { + LDAPDebug( LDAP_DEBUG_ANY, "Error: %s plugin %s is not started\n" , config[i].type, config[i].name, 0 ); + } + + i++; + } + + exit(1); + } + +bail: + + /* + * need the details in config to hang around for shutdown + * config itself may be deleted since its contents have been + * copied by value to the shutdown list + */ + + if(config) + { + /* + index = 0; + + while(index < total_plugins) + { + if(config[index].depends_named_list) + { + slapi_ch_free((void**)&(config[index].depends_named_list)); + } + + if(config[index].depends_type_list) + { + i = 0; + + while(i < config[index].total_type) + { + slapi_ch_free((void**)&(config[index].depends_type_list)[i]); + + i++; + } + + slapi_ch_free((void**)&(config[index].depends_type_list)); + } + + slapi_ch_free((void**)&(config[index].name)); + slapi_ch_free((void**)&(config[index].type)); + + index++; + } + */ + + slapi_ch_free((void**)&config); + } + + /* Finally enable registered plugin functions */ + + global_plugin_callbacks_enabled = 1; + + return ret; +} + +/* + * plugin_dependency_closeall + * + * uses the shutdown list created at startup to close + * plugins in the correct order + * + * For now this leaks the list and contents, but since + * it hangs around until shutdown anyway, we don't care + * + */ +void +plugin_dependency_closeall() +{ + Slapi_PBlock pb; + int plugins_closed = 0; + int index = 0; + + while(plugins_closed<global_plugins_started) + { + /* + * the first few entries may not be valid + * since the list was created in the reverse + * order and some plugins may have been counted + * for the purpose of list allocation but are + * disabled and so were never started + * + * we check that here + */ + if(global_plugin_shutdown_order[index].name) + { + pblock_init(&pb); + plugin_call_one( global_plugin_shutdown_order[index].plugin, SLAPI_PLUGIN_CLOSE_FN, &pb ); + plugins_closed++; + } + + index++; + } +} + +/*********************************************************** + end of plugin dependency code +************************************************************/ + + +/* + * Function: plugin_startall + * + * Returns: squat + * + * Description: Some plugins may need to do some stuff after all the config + * stuff is done with. So this function goes through and starts all plugins + */ +void +plugin_startall(int argc, char** argv, int start_backends, int start_global) +{ + /* initialize special plugin structures */ + default_plugin_init (); + + plugin_dependency_startall(argc, argv, "plugin startup failed\n", SLAPI_PLUGIN_START_FN); +} + +/* + * Function: plugin_close_all + * + * Returns: squat + * + * Description: cleanup routine, allows plugins to kill threads, free memory started in start fn + * + */ +void +plugin_closeall(int close_backends, int close_globals) +{ + plugin_dependency_closeall(); +} + + +static int +plugin_call_list (struct slapdplugin *list, int operation, Slapi_PBlock *pb) +{ + return plugin_call_func(list, operation, pb, 0); +} + +static int +plugin_call_one (struct slapdplugin *list, int operation, Slapi_PBlock *pb) +{ + return plugin_call_func(list, operation, pb, 1); +} + + +/* + * Return codes: + * - For preoperation plugins, returns the return code passed back from the first + * plugin that fails, or zero if all plugins succeed. + * - For bepreop and bepostop plugins, returns a bitwise OR of the return codes + * returned by all the plugins called (there's only one bepreop and one bepostop + * in DS 5.0 anyway). + * - For postoperation plugins, returns 0. + */ +static int +plugin_call_func (struct slapdplugin *list, int operation, Slapi_PBlock *pb, int call_one) +{ + /* Invoke the operation on the plugins that are registered for the subtree effected by the operation. */ + int rc; + int return_value = 0; + int count= 0; + for (; list != NULL; list = list->plg_next) + { + IFP func = NULL; + + slapi_pblock_set (pb, SLAPI_PLUGIN, list); + set_db_default_result_handlers (pb); /* JCM: What's this do? Is it needed here? */ + if (slapi_pblock_get (pb, operation, &func) == 0 && func != NULL && + plugin_invoke_plugin_pb (list, operation, pb)) + { + char *n= list->plg_name; + LDAPDebug( LDAP_DEBUG_TRACE, "Calling plugin '%s' #%d type %d\n", (n==NULL?"noname":n), count, operation ); + /* counters_to_errors_log("before plugin call"); */ + if (( rc = func (pb)) != 0 ) + { + if (SLAPI_PLUGIN_PREOPERATION == list->plg_type || + SLAPI_PLUGIN_INTERNAL_PREOPERATION == list->plg_type || + SLAPI_PLUGIN_START_FN == operation ) + { + /* + * We bail out of plugin processing for preop plugins + * that return a non-zero return code. This allows preop + * plugins to cause further preop processing to terminate, and + * causes the operation to be vetoed. + */ + return_value = rc; + break; + } else if (SLAPI_PLUGIN_BEPREOPERATION == list->plg_type || + SLAPI_PLUGIN_BEPOSTOPERATION == list->plg_type) + { + /* OR the result into the return value for be pre/postops */ + return_value |= rc; + } + } + /* counters_to_errors_log("after plugin call"); */ + } + + count++; + + if(call_one) + break; + } + return( return_value ); +} + +int +slapi_berval_cmp (const struct berval* L, const struct berval* R) /* JCM - This does not belong here. But, where should it go? */ +{ + int result = 0; + if (L->bv_len < R->bv_len) { + result = memcmp (L->bv_val, R->bv_val, L->bv_len); + if (result == 0) + result = -1; + } else { + result = memcmp (L->bv_val, R->bv_val, R->bv_len); + if (result == 0 && (L->bv_len > R->bv_len)) + result = 1; + } + return result; +} + + +static char **supported_saslmechanisms = NULL; +static PRRWLock *supported_saslmechanisms_lock = NULL; + +/* + * register a supported SASL mechanism so it will be returned as part of the + * root DSE. + */ +void +slapi_register_supported_saslmechanism( char *mechanism ) +{ + if ( mechanism != NULL ) { + if (NULL == supported_saslmechanisms_lock) { + /* This is thread safe, as it gets executed by + * a single thread at init time (main->init_saslmechanisms) */ + supported_saslmechanisms_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, + "supported saslmechanisms rwlock"); + if (NULL == supported_saslmechanisms_lock) { + /* Out of resources */ + slapi_log_error(SLAPI_LOG_FATAL, "startup", + "slapi_register_supported_saslmechanism: failed to create lock.\n"); + exit (1); + } + } + PR_RWLock_Wlock(supported_saslmechanisms_lock); + charray_add( &supported_saslmechanisms, slapi_ch_strdup( mechanism )); + PR_RWLock_Unlock(supported_saslmechanisms_lock); + } +} + + +/* + * return pointer to NULL-terminated array of supported SASL mechanisms. + * This function is not MTSafe and should be deprecated. + * slapi_get_supported_saslmechanisms_copy should be used instead. + */ +char ** +slapi_get_supported_saslmechanisms( void ) +{ + return( supported_saslmechanisms ); +} + + +/* + * return pointer to NULL-terminated array of supported SASL mechanisms. + */ +char ** +slapi_get_supported_saslmechanisms_copy( void ) +{ + char ** ret = NULL; + PR_RWLock_Rlock(supported_saslmechanisms_lock); + ret = charray_dup(supported_saslmechanisms); + PR_RWLock_Unlock(supported_saslmechanisms_lock); + return( ret ); +} + + +static char **supported_extended_ops = NULL; +static PRRWLock *extended_ops_lock = NULL; + +/* + * register all of the LDAPv3 extended operations we know about. + */ +void +ldapi_init_extended_ops( void ) +{ + extended_ops_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, + "supported extended ops rwlock"); + if (NULL == extended_ops_lock) { + /* Out of resources */ + slapi_log_error(SLAPI_LOG_FATAL, "startup", + "ldapi_init_extended_ops: failed to create lock.\n"); + exit (1); + } + + PR_RWLock_Wlock(extended_ops_lock); + charray_add(&supported_extended_ops, + slapi_ch_strdup(EXTOP_BULK_IMPORT_START_OID)); + charray_add(&supported_extended_ops, + slapi_ch_strdup(EXTOP_BULK_IMPORT_DONE_OID)); + /* add future supported extops here... */ + PR_RWLock_Unlock(extended_ops_lock); +} + + +/* + * register an extended op. so it can be returned as part of the root DSE. + */ +void +ldapi_register_extended_op( char **opoids ) +{ + int i; + + PR_RWLock_Wlock(extended_ops_lock); + for ( i = 0; opoids != NULL && opoids[i] != NULL; ++i ) { + if ( !charray_inlist( supported_extended_ops, opoids[i] )) { + charray_add( &supported_extended_ops, slapi_ch_strdup( opoids[i] )); + } + } + PR_RWLock_Unlock(extended_ops_lock); +} + + +/* + * retrieve supported extended operation OIDs + * return 0 if successful and -1 if not. + * This function is not MTSafe and should be deprecated. + * slapi_get_supported_extended_ops_copy should be used instead. + */ +char ** +slapi_get_supported_extended_ops( void ) +{ + return( supported_extended_ops ); +} + + +/* + * retrieve supported extended operation OIDs + * return 0 if successful and -1 if not. + */ +char ** +slapi_get_supported_extended_ops_copy( void ) +{ + char ** ret = NULL; + PR_RWLock_Rlock(extended_ops_lock); + ret = charray_dup(supported_extended_ops); + PR_RWLock_Unlock(extended_ops_lock); + return( ret ); +} + + +/* isApprovedPlugin: + * returns 1 if the plugin is approved to be loaded, 0 otherwise. + * + * If the server is running as the Full version, all plugins are approved, + * otherwise, if the server is running as DirectoryLite, only plugins from + * Netscape are approved. + * + * We have a special case for the NT Synch plugin, which is disabled for DLite. + */ +static int +isApprovedPlugin( struct slapdplugin *plugin ) +{ + if ( config_is_slapd_lite() == 0 ) { + /* All the plugins are approved for Directory Full */ + return 1; + } + + if ( plugin == NULL ) { + LDAPDebug( LDAP_DEBUG_ANY, "isApprovedPlugin: plugin is NULL\n", 0,0,0 ); + return 0; + } + if (plugin->plg_desc.spd_vendor == NULL ) { + LDAPDebug( LDAP_DEBUG_ANY, "isApprovedPlugin: plugin vendor is NULL\n",0,0,0 ); + return 0; + } + + LDAPDebug ( LDAP_DEBUG_TRACE, "isApprovedPlugin() looking at plugin \"%s\" from vendor %s\n", + plugin->plg_name, plugin->plg_desc.spd_vendor, 0 ); + + /* + * approved plugins must have their vendor string set to PLUGIN_MAGIC VENDOR_STR. External + * plugins are not allowed for Lite. + */ + if ( strcmp( plugin->plg_desc.spd_vendor, PLUGIN_MAGIC_VENDOR_STR ) == 0) + return 1; + + LDAPDebug ( LDAP_DEBUG_ANY, "isApprovedPlugin() plugin \"%s\" is not approved for Directory Lite\n", + plugin->plg_name, 0,0 ); + return 0; +} + + +/* + looks up the given string type to convert to the internal integral type; also + returns the plugin list associated with the plugin type + returns 0 upon success and non-zero upon failure +*/ +static int +plugin_get_type_and_list( + const char *plugintype, + int *type, + struct slapdplugin ***plugin_list +) +{ + int plugin_list_index = -1; + if ( strcasecmp( plugintype, "database" ) == 0 ) { + *type = SLAPI_PLUGIN_DATABASE; + plugin_list_index= PLUGIN_LIST_DATABASE; + } else if ( strcasecmp( plugintype, "extendedop" ) == 0 ) { + *type = SLAPI_PLUGIN_EXTENDEDOP; + plugin_list_index= PLUGIN_LIST_EXTENDED_OPERATION; + } else if ( strcasecmp( plugintype, "preoperation" ) == 0 ) { + *type = SLAPI_PLUGIN_PREOPERATION; + plugin_list_index= PLUGIN_LIST_PREOPERATION; + } else if ( strcasecmp( plugintype, "postoperation" ) == 0 ) { + *type = SLAPI_PLUGIN_POSTOPERATION; + plugin_list_index= PLUGIN_LIST_POSTOPERATION; + } else if ( strcasecmp( plugintype, "matchingrule" ) == 0 ) { + *type = SLAPI_PLUGIN_MATCHINGRULE; + plugin_list_index= PLUGIN_LIST_MATCHINGRULE; + } else if ( strcasecmp( plugintype, "syntax" ) == 0 ) { + *type = SLAPI_PLUGIN_SYNTAX; + plugin_list_index= PLUGIN_LIST_SYNTAX; + } else if ( strcasecmp( plugintype, "accesscontrol" ) == 0 ) { + *type = SLAPI_PLUGIN_ACL; + plugin_list_index= PLUGIN_LIST_ACL; + } else if ( strcasecmp( plugintype, "bepreoperation" ) == 0 ) { + *type = SLAPI_PLUGIN_BEPREOPERATION; + plugin_list_index= PLUGIN_LIST_BEPREOPERATION; + } else if ( strcasecmp( plugintype, "bepostoperation" ) == 0 ) { + *type = SLAPI_PLUGIN_BEPOSTOPERATION; + plugin_list_index= PLUGIN_LIST_BEPOSTOPERATION; + } else if ( strcasecmp( plugintype, "internalpreoperation" ) == 0 ) { + *type = SLAPI_PLUGIN_INTERNAL_PREOPERATION; + plugin_list_index= PLUGIN_LIST_INTERNAL_PREOPERATION; + } else if ( strcasecmp( plugintype, "internalpostoperation" ) == 0 ) { + *type = SLAPI_PLUGIN_INTERNAL_POSTOPERATION; + plugin_list_index= PLUGIN_LIST_INTERNAL_POSTOPERATION; + } else if ( strcasecmp( plugintype, "entry" ) == 0 ) { + *type = SLAPI_PLUGIN_ENTRY; + plugin_list_index= PLUGIN_LIST_ENTRY; + } else if ( strcasecmp( plugintype, "object" ) == 0 ) { + *type = SLAPI_PLUGIN_TYPE_OBJECT; + plugin_list_index= PLUGIN_LIST_OBJECT; + } else if ( strcasecmp( plugintype, "pwdstoragescheme" ) == 0 ) { + *type = SLAPI_PLUGIN_PWD_STORAGE_SCHEME; + plugin_list_index= PLUGIN_LIST_PWD_STORAGE_SCHEME; + } else if ( strcasecmp( plugintype, "reverpwdstoragescheme" ) == 0 ) { + *type = SLAPI_PLUGIN_REVER_PWD_STORAGE_SCHEME; + plugin_list_index= PLUGIN_LIST_REVER_PWD_STORAGE_SCHEME; + } else if ( strcasecmp( plugintype, "vattrsp" ) == 0 ) { + *type = SLAPI_PLUGIN_VATTR_SP; + plugin_list_index= PLUGIN_LIST_VATTR_SP; + } else if ( strcasecmp( plugintype, "ldbmentryfetchstore" ) == 0 ) { + *type = SLAPI_PLUGIN_LDBM_ENTRY_FETCH_STORE; + plugin_list_index= PLUGIN_LIST_LDBM_ENTRY_FETCH_STORE; + } else if ( strcasecmp( plugintype, "index" ) == 0 ) { + *type = SLAPI_PLUGIN_INDEX; + plugin_list_index= PLUGIN_LIST_INDEX; + } else { + return( 1 ); /* unknown plugin type - pass to backend */ + } + + if (plugin_list_index >= 0) + *plugin_list = &global_plugin_list[plugin_list_index]; + + return 0; +} + +static const char * +plugin_exists(const Slapi_DN *plugin_dn) +{ + /* check to see if the plugin name is unique */ + const char *retval = 0; + if (global_plugin_dns && PL_HashTableLookup(global_plugin_dns, + slapi_sdn_get_ndn(plugin_dn))) + { + retval = slapi_sdn_get_dn(plugin_dn); + } + + return retval; +} + +static int +plugin_set_subtree_config(PluginTargetData *subtree_config, const char *val) +{ + int status = 0; + + if (strcasecmp (val, ALL_DATA) == 0) /* allow access to both local and remote data */ + { + plugin_set_global (subtree_config); + } + else if (strcasecmp (val, LOCAL_DATA) == 0) /* allow access to all locally hosted data */ + { + ptd_set_special_data (subtree_config, PLGC_DATA_LOCAL); + } + else if (strcasecmp (val, REMOTE_DATA) == 0)/* allow access to requests for remote data */ + { + ptd_set_special_data (subtree_config, PLGC_DATA_REMOTE); + } + else /* dn */ + { + ptd_add_subtree (subtree_config, slapi_sdn_new_dn_byval(val)); + } + /* I suppose we could check the val at this point to make sure + its a valid DN . . . */ + + return status; +} + +static int +set_plugin_config_from_entry( + const Slapi_Entry *plugin_entry, + struct slapdplugin *plugin +) +{ + struct pluginconfig *config = &plugin->plg_conf; + char *value = 0; + int status = 0; + PRBool target_seen = PR_FALSE; + PRBool bind_seen = PR_FALSE; + + if ((value = slapi_entry_attr_get_charptr(plugin_entry, + ATTR_PLUGIN_SCHEMA_CHECK)) != NULL) + { + if (plugin_config_set_action(&config->plgc_schema_check, value)) + { + LDAPDebug(LDAP_DEBUG_PLUGIN, "Error: invalid value %s for attribute %s " + "from entry %s\n", value, ATTR_PLUGIN_SCHEMA_CHECK, + slapi_entry_get_dn_const(plugin_entry)); + status = 1; + } + slapi_ch_free((void**)&value); + } + + if ((value = slapi_entry_attr_get_charptr(plugin_entry, + ATTR_PLUGIN_LOG_ACCESS)) != NULL) + { + if (plugin_config_set_action(&config->plgc_log_access, value)) + { + LDAPDebug(LDAP_DEBUG_PLUGIN, "Error: invalid value %s for attribute %s " + "from entry %s\n", value, ATTR_PLUGIN_LOG_ACCESS, + slapi_entry_get_dn_const(plugin_entry)); + status = 1; + } + slapi_ch_free((void**)&value); + } + + if ((value = slapi_entry_attr_get_charptr(plugin_entry, + ATTR_PLUGIN_LOG_AUDIT)) != NULL) + { + if (plugin_config_set_action(&config->plgc_log_audit, value)) + { + LDAPDebug(LDAP_DEBUG_PLUGIN, "Error: invalid value %s for attribute %s " + "from entry %s\n", value, ATTR_PLUGIN_LOG_AUDIT, + slapi_entry_get_dn_const(plugin_entry)); + status = 1; + } + slapi_ch_free((void**)&value); + } + + if ((value = slapi_entry_attr_get_charptr(plugin_entry, + ATTR_PLUGIN_INVOKE_FOR_REPLOP)) != NULL) + { + if (plugin_config_set_action(&config->plgc_invoke_for_replop, value)) + { + LDAPDebug(LDAP_DEBUG_PLUGIN, "Error: invalid value %s for attribute %s " + "from entry %s\n", value, ATTR_PLUGIN_INVOKE_FOR_REPLOP, + slapi_entry_get_dn_const(plugin_entry)); + status = 1; + } + slapi_ch_free((void**)&value); + } + + if ((value = slapi_entry_attr_get_charptr(plugin_entry, + ATTR_PLUGIN_TARGET_SUBTREE)) != NULL) + { + if (plugin_set_subtree_config(&(config->plgc_target_subtrees), value)) + { + LDAPDebug(LDAP_DEBUG_PLUGIN, "Error: invalid value %s for attribute %s " + "from entry %s\n", value, ATTR_PLUGIN_TARGET_SUBTREE, + slapi_entry_get_dn_const(plugin_entry)); + status = 1; + } + else + { + target_seen = PR_TRUE; + } + slapi_ch_free((void**)&value); + } + + if ((value = slapi_entry_attr_get_charptr(plugin_entry, + ATTR_PLUGIN_BIND_SUBTREE)) != NULL) + { + if (plugin_set_subtree_config(&(config->plgc_bind_subtrees), value)) + { + LDAPDebug(LDAP_DEBUG_PLUGIN, "Error: invalid value %s for attribute %s " + "from entry %s\n", value, ATTR_PLUGIN_BIND_SUBTREE, + slapi_entry_get_dn_const(plugin_entry)); + status = 1; + } + else + { + bind_seen = PR_TRUE; + } + slapi_ch_free((void**)&value); + } + + /* set target subtree default - allow access to all data */ + if (!target_seen) + { + plugin_set_global(&(config->plgc_target_subtrees)); + } + + /* set bind subtree default - allow access to local data only */ + if (!bind_seen) + { + ptd_set_special_data(&(config->plgc_bind_subtrees), PLGC_DATA_LOCAL); + ptd_set_special_data(&(config->plgc_bind_subtrees), PLGC_DATA_REMOTE); + } + + return status; +} + +#if 0 +static PRBool +plugin_matches_key (char *arg, char *key) +{ + PRBool haveVal = strlen (arg) > strlen (key); + return (haveVal && strncasecmp (arg, key, strlen (key)) == 0); +} + +static char* +plugin_get_str_val (char *arg, char *key) +{ + return &(arg[strlen (key)]); +} + +static PRBool +plugin_get_bool_val (char*arg, char *key, char *true_val) +{ + return (strcasecmp (&(arg[strlen (key)]), true_val) == 0); +} +#endif + +/* This function is called after the plugin init function has been called + which fills in the desc part of the plugin +*/ +static int +add_plugin_description(Slapi_Entry *e, const char *attrname, char *val) +{ + struct berval desc; + struct berval *newval[2] = {0, 0}; + int status = 0; + + desc.bv_val = SLAPI_PLUGIN_NONE_IF_NULL( val ); + desc.bv_len = strlen(desc.bv_val); + newval[0] = &desc; + if ((status = entry_replace_values(e, attrname, newval)) != 0) + { + LDAPDebug(LDAP_DEBUG_PLUGIN, "Error: failed to add value %s to " + "attribute %s of entry %s\n", val, attrname, + slapi_entry_get_dn_const(e)); + status = 1; + } + + return status; +} + + +/* + * The plugin initfunc sets some vendor and version information in the plugin. + * This function extracts that and adds it as attributes to `e'. If + * `plugin' is NULL, the plugin is located based on the DN in `e'. + * + * Returns 0 if all goes well and 1 if not. + */ +int +plugin_add_descriptive_attributes( Slapi_Entry *e, struct slapdplugin *plugin ) +{ + int status = 0; + + if ( NULL == plugin ) { + int i; + const Slapi_DN *ednp = slapi_entry_get_sdn_const( e ); + Slapi_DN pdn; + struct slapdplugin *plugtmp; + + for( i = 0; NULL == plugin && i < PLUGIN_LIST_GLOBAL_MAX; ++i ) + { + for ( plugtmp = global_plugin_list[i]; NULL == plugin && plugtmp; + plugtmp = plugtmp->plg_next) + { + slapi_sdn_init_dn_byref( &pdn, plugtmp->plg_dn ); + if ( 0 == slapi_sdn_compare( &pdn, ednp )) + { + plugin = plugtmp; + } + slapi_sdn_done( &pdn ); + } + } + + if ( NULL == plugin ) + { + LDAPDebug(LDAP_DEBUG_PLUGIN, + "Error: failed to add descriptive values for plugin %s" + " (could not find plugin entry)\n", + slapi_entry_get_dn_const(e), 0, 0 ); + return 1; /* failure */ + } + } + + + if (add_plugin_description(e, ATTR_PLUGIN_PLUGINID, + plugin->plg_desc.spd_id)) + { + status = 1; + } + + if (add_plugin_description(e, ATTR_PLUGIN_VERSION, + plugin->plg_desc.spd_version)) + { + status = 1; + } + + if (add_plugin_description(e, ATTR_PLUGIN_VENDOR, + plugin->plg_desc.spd_vendor)) + { + status = 1; + } + + if (add_plugin_description(e, ATTR_PLUGIN_DESC, + plugin->plg_desc.spd_description)) + { + status = 1; + } + + return status; +} + + +/* + clean up the memory associated with the plugin +*/ +static void +plugin_free(struct slapdplugin *plugin) +{ + charray_free(plugin->plg_argv); + slapi_ch_free((void**)&plugin->plg_libpath); + slapi_ch_free((void**)&plugin->plg_initfunc); + slapi_ch_free((void**)&plugin->plg_name); + slapi_ch_free((void**)&plugin->plg_dn); + if (!plugin->plg_group) + plugin_config_cleanup(&plugin->plg_conf); + slapi_ch_free((void**)&plugin); +} + +/*********************************** +This is the main entry point for plugin configuration. The plugin_entry argument +should already contain the necessary fields required to initialize the plugin and +to give it a proper name in the plugin configuration DIT. + +Argument: +Slapi_Entry *plugin_entry - the required attributes are + dn: the dn of the plugin entry + cn: the unique name of the plugin + nsslapd-pluginType: one of the several recognized plugin types e.g. "postoperation" + +if p_initfunc is given, pluginPath and pluginInitFunc are optional + nsslapd-pluginPath: full path and file name of the dll implementing the plugin + nsslapd-pluginInitFunc: the name of the plugin initialization function + +the optional attributes are: + nsslapd-pluginArg0 + ... + nsslapd-pluginArg[N-1] - the (old style) arguments to the plugin, where N varies + from 0 to the number of arguments. The numbers must be consecutive i.e. no + skipping + + Instead of using nsslapd-pluginArgN, it is encouraged for you to use named + parameters e.g. + nsslapd-tweakThis: 1 + nsslapd-tweakThat: 2 + etc. + + nsslapd-pluginEnabled: "on"|"off" - by default, the plugin will be enabled unless + this attribute is present and has the value "off" + + for other known attributes, see set_plugin_config_from_entry() above + + all other attributes will be ignored + + The reason this parameter is not const is because it may be modified. This + function will modify it if the plugin init function is called successfully + to add the description attributes, and the plugin init function may modify + it as well. + +Argument: +group - the group to which this plugin will belong - each member of a plugin group + shares the pluginconfig of the group leader; refer to the function plugin_get_config + for more information + +Argument: +add_entry - if true, the entry will be added to the DIT using the given + DN in the plugin_entry - this is the default behavior; if false, the + plugin entry will not show up in the DIT +************************************/ +int +plugin_setup(Slapi_Entry *plugin_entry, struct slapi_componentid *group, + slapi_plugin_init_fnptr p_initfunc, int add_entry) +{ + int ii = 0; + char attrname[BUFSIZ]; + char *value = 0; + struct slapdplugin *plugin = NULL; + struct slapdplugin **plugin_list = NULL; + struct slapi_componentid *cid=NULL; + const char *existname = 0; + slapi_plugin_init_fnptr initfunc = p_initfunc; + Slapi_PBlock pb; + int status = 0; + int approved = 1; + int enabled = 1; + char *configdir = 0; + + attrname[0] = '\0'; + + if (!slapi_entry_get_sdn_const(plugin_entry)) + { + LDAPDebug(LDAP_DEBUG_ANY, "Error: DN is missing from the plugin.\n", + 0, 0, 0); + return -1; + } + + if ((existname = plugin_exists(slapi_entry_get_sdn_const(plugin_entry))) != NULL) + { + LDAPDebug(LDAP_DEBUG_ANY, "Error: the plugin named %s " + "already exists.\n", existname, 0, 0); + return -1; + } + + /* + * create a new plugin structure, fill it in, and prepare to + * call the plugin's init function. the init function will + * set the plugin function pointers. + */ + plugin = (struct slapdplugin *)slapi_ch_calloc(1, sizeof(struct slapdplugin)); + + plugin->plg_dn = slapi_ch_strdup(slapi_entry_get_dn_const(plugin_entry)); + + if (!(value = slapi_entry_attr_get_charptr(plugin_entry, + ATTR_PLUGIN_TYPE))) + { + /* error: required attribute %s missing */ + LDAPDebug(LDAP_DEBUG_ANY, "Error: required attribute %s is missing " + "from entry \"%s\"\n", ATTR_PLUGIN_TYPE, + slapi_entry_get_dn_const(plugin_entry), 0); + status = -1; + goto PLUGIN_CLEANUP; + } + else + { + status = plugin_get_type_and_list(value, &plugin->plg_type, + &plugin_list); + + if ( status != 0 ) { + /* error: unknown plugin type */ + LDAPDebug(LDAP_DEBUG_ANY, "Error: unknown plugin type \"%s\" " + "in entry \"%s\"\n", + value, slapi_entry_get_dn_const(plugin_entry), 0); + slapi_ch_free((void**)&value); + status = -1; + goto PLUGIN_CLEANUP; + } + slapi_ch_free((void**)&value); + } + + if (!status && + !(value = slapi_entry_attr_get_charptr(plugin_entry, "cn"))) + { + /* error: required attribute %s missing */ + LDAPDebug(LDAP_DEBUG_ANY, "Error: required attribute %s is missing " + "from entry \"%s\"\n", "cn", + slapi_entry_get_dn_const(plugin_entry), 0); + status = -1; + goto PLUGIN_CLEANUP; + } + else + { + plugin->plg_name = value; /* plugin owns value's memory now, don't free */ + } + + if (!(value = slapi_entry_attr_get_charptr(plugin_entry, + ATTR_PLUGIN_INITFN))) + { + if (!initfunc) + { + /* error: required attribute %s missing */ + LDAPDebug(LDAP_DEBUG_ANY, "Error: required attribute %s is missing " + "from entry \"%s\"\n", ATTR_PLUGIN_INITFN, + slapi_entry_get_dn_const(plugin_entry), 0); + status = -1; + goto PLUGIN_CLEANUP; + } + } + else + { + plugin->plg_initfunc = value; /* plugin owns value's memory now, don't free */ + } + + if (!initfunc) + { + if (!(value = slapi_entry_attr_get_charptr(plugin_entry, + ATTR_PLUGIN_PATH))) + { + /* error: required attribute %s missing */ + LDAPDebug(LDAP_DEBUG_ANY, "Error: required attribute %s is missing " + "from entry \"%s\"\n", ATTR_PLUGIN_PATH, + slapi_entry_get_dn_const(plugin_entry), 0); + status = -1; + goto PLUGIN_CLEANUP; + } + else + { + plugin->plg_libpath = value; /* plugin owns value's memory now, don't free */ + } + + /* + * load the plugin's init function + */ + if ((initfunc = (slapi_plugin_init_fnptr)sym_load(plugin->plg_libpath, + plugin->plg_initfunc, plugin->plg_name, 1 /* report errors */ + )) == NULL) + { + status = -1; + goto PLUGIN_CLEANUP; + } +#ifdef _WIN32 + { + set_debug_level_fn_t fn; + /* for Win32 only, attempt to get its debug level init function */ + if ((fn = (set_debug_level_fn_t)sym_load(plugin->plg_libpath, + "plugin_init_debug_level", plugin->plg_name, + 0 /* do not report errors */ )) != NULL) { + /* we hooked the function, so call it */ + (*fn)(module_ldap_debug); + } + } +#endif + } + + if (!status && group) /* uses group's config; see plugin_get_config */ + { + struct slapi_componentid * cid = (struct slapi_componentid *) group; + plugin->plg_group = (struct slapdplugin *) cid->sci_plugin; + } + else if (!status) /* using own config */ + { + plugin_config_init(&(plugin->plg_conf)); + set_plugin_config_from_entry(plugin_entry, plugin); + } + + /* add the plugin arguments */ + value = 0; + ii = 0; + sprintf(attrname, "%s%d", ATTR_PLUGIN_ARG, ii); + while ((value = slapi_entry_attr_get_charptr(plugin_entry, attrname)) != NULL) + { + charray_add(&plugin->plg_argv, value); + plugin->plg_argc++; + ++ii; + sprintf(attrname, "%s%d", ATTR_PLUGIN_ARG, ii); + } + + memset((char *)&pb, '\0', sizeof(pb)); + slapi_pblock_set(&pb, SLAPI_PLUGIN, plugin); + slapi_pblock_set(&pb, SLAPI_PLUGIN_VERSION, (void *)SLAPI_PLUGIN_CURRENT_VERSION); + + cid = generate_componentid (plugin,NULL); + slapi_pblock_set(&pb, SLAPI_PLUGIN_IDENTITY, (void*)cid); + + configdir = config_get_configdir(); + slapi_pblock_set(&pb, SLAPI_CONFIG_DIRECTORY, configdir); + + if ((*initfunc)(&pb) != 0) + { + LDAPDebug(LDAP_DEBUG_ANY, "Init function \"%s\" for \"%s\" plugin" + " in library \"%s\" failed\n", + plugin->plg_initfunc, plugin->plg_name, + plugin->plg_libpath); + status = -1; + goto PLUGIN_CLEANUP; + } + + if ( !status ) { + status = plugin_add_descriptive_attributes( plugin_entry, plugin ); + } + + /* see if the plugin is approved or not */ + if ((approved = isApprovedPlugin(plugin)) != 0) + { + if ((!plugin->plg_version) || + (!SLAPI_PLUGIN_IS_COMPAT(plugin->plg_version))) { + LDAPDebug( LDAP_DEBUG_ANY, "Plugin \"%s\" from library \"%s\"" + " has wrong version (supported versions: %s)\n", + plugin->plg_name, plugin->plg_libpath, + SLAPI_PLUGIN_SUPPORTED_VERSIONS); + approved = 0; + } + } + + /* see if the plugin is enabled or not */ + if ((value = slapi_entry_attr_get_charptr(plugin_entry, + ATTR_PLUGIN_ENABLED)) && + !strcasecmp(value, "off")) + { + enabled = 0; + } + else + { + enabled = 1; + } + + if (value) + slapi_ch_free((void**)&value); + + if (!approved) { + enabled = 0; + LDAPDebug(LDAP_DEBUG_ANY, "Plugin \"%s\" is disabled.\n", + plugin->plg_name,0,0); + } + + if (approved) + { + if(enabled) + { + /* don't use raw pointer from plugin_entry because it + will be freed later by the caller */ + Slapi_DN *dn_copy = slapi_sdn_dup(slapi_entry_get_sdn_const(plugin_entry)); + add_plugin_to_list(plugin_list, plugin); + add_plugin_entry_dn(dn_copy); + } + + if (add_entry) + { + /* make a copy of the plugin entry for our own use because it will + be freed later by the caller */ + Slapi_Entry *e_copy = slapi_entry_dup(plugin_entry); + /* new_plugin_entry(&plugin_entries, plugin_entry, plugin); */ + new_plugin_entry(&dep_plugin_entries, e_copy, plugin); + } + } + + +PLUGIN_CLEANUP: + if (status) + plugin_free(plugin); + slapi_ch_free((void **)&configdir); + + return status; +} + +/* set default configuration parameters */ +static void +plugin_config_init (struct pluginconfig *config) +{ + PR_ASSERT (config); + + ptd_init (&config->plgc_target_subtrees); + ptd_init (&config->plgc_bind_subtrees); + config->plgc_schema_check = PLGC_ON; + config->plgc_invoke_for_replop = PLGC_ON; + /* currently, we leave it up to plugin, but don't actually tell plugins that they can choose. + We want changes to always be logged by regular plugins to avoid data inconsistency, but we + want to allow internal plugins like replication to make the decision.*/ + config->plgc_log_change = PLGC_UPTOPLUGIN; + config->plgc_log_access = PLGC_OFF; + config->plgc_log_audit = PLGC_OFF; +} + +static int +plugin_config_set_action (int *action, char *value) +{ + PR_ASSERT (action); + PR_ASSERT (value); + + if (strcasecmp (value, "on") == 0) + { + *action = PLGC_ON; + } + else if (strcasecmp (value, "off") == 0) + { + *action = PLGC_OFF; + } + else if (strcasecmp (value, "uptoplugin") == 0) + { + *action = PLGC_UPTOPLUGIN; + } + else + { + slapi_log_error(SLAPI_LOG_FATAL, NULL, + "plugin_config_set_action: invalid action %s\n", value); + return -1; + } + + return 0; +} + +static void +plugin_config_cleanup (struct pluginconfig *config) +{ + PR_ASSERT (config); + + ptd_cleanup (&config->plgc_target_subtrees); + ptd_cleanup (&config->plgc_bind_subtrees); +} + +#if 0 +static char* +plugin_config_action_to_string (int action) +{ + switch (action) + { + case PLGC_ON: return "on"; + case PLGC_OFF: return "off"; + case PLGC_UPTOPLUGIN: return "uptoplugin"; + default: return NULL; + } +} +#endif + +static struct pluginconfig* +plugin_get_config (struct slapdplugin *plugin) +{ + struct slapdplugin *temp = plugin; + + PR_ASSERT (plugin); + + while (temp->plg_group) + { + temp = temp->plg_group; + } + + return &(temp->plg_conf); +} + +static PRBool +plugin_invoke_plugin_pb (struct slapdplugin *plugin, int operation, Slapi_PBlock *pb) +{ + Slapi_DN *target_spec; + PRBool rc; + + PR_ASSERT (plugin); + PR_ASSERT (pb); + + /* we always allow initialization and cleanup operations */ + if (operation == SLAPI_PLUGIN_START_FN || operation == SLAPI_PLUGIN_POSTSTART_FN || + operation == SLAPI_PLUGIN_CLOSE_FN || operation == SLAPI_PLUGIN_CLEANUP_FN) + return PR_TRUE; + + PR_ASSERT (pb->pb_op); + + target_spec = operation_get_target_spec (pb->pb_op); + + PR_ASSERT (target_spec); + + rc = plugin_invoke_plugin_sdn (plugin, operation, pb, target_spec); + + return rc; +} + +PRBool +plugin_invoke_plugin_sdn (struct slapdplugin *plugin, int operation, Slapi_PBlock *pb, Slapi_DN *target_spec) +{ + PluginTargetData *ptd; + struct pluginconfig *config; + Slapi_Backend *be; + int isroot; + PRBool islocal; + PRBool bindop; + unsigned long op; + PRBool rc; + int method = -1; + + PR_ASSERT (plugin); + + /* get configuration from the group plugin if necessary */ + config = plugin_get_config (plugin); + slapi_pblock_get(pb, SLAPI_BIND_METHOD, &method); + /* check if plugin is configured to service replicated operations */ + if (!config->plgc_invoke_for_replop) + { + int repl_op; + + /* if pb is NULL we assume it is not a replicated operation */ + if (pb) + { + slapi_pblock_get (pb, SLAPI_IS_REPLICATED_OPERATION, &repl_op); + if (repl_op) + return PR_FALSE; + } + } + + if (pb) + { + if (pb->pb_op) + { + op = operation_get_type(pb->pb_op); + + if (op == SLAPI_OPERATION_BIND || op == SLAPI_OPERATION_UNBIND) + { + bindop = PR_TRUE; + } + else + { + bindop = PR_FALSE; + } + + slapi_pblock_get (pb, SLAPI_REQUESTOR_ISROOT, &isroot); + } + else + { + bindop = PR_FALSE; + isroot = 1; + } + + slapi_pblock_get (pb, SLAPI_BACKEND, &be); + + /* determine whether data are local or remote */ + /* remote if chaining backend or default backend */ + + if ( be!=NULL ) { + islocal=!(slapi_be_is_flag_set(be,SLAPI_BE_FLAG_REMOTE_DATA)); + } else { + islocal = be != defbackend_get_backend(); + } + + } + else + { + bindop = PR_FALSE; + islocal = PR_TRUE; + isroot = 1; + } + + if (bindop) + { + ptd = &(config->plgc_bind_subtrees); + } + else + { + ptd = &(config->plgc_target_subtrees); + } + + rc = plugin_matches_operation (target_spec, ptd, bindop, isroot, islocal, method); + + return rc; +} + +/* this interface is exposed to be used by internal operations. + */ +char* plugin_get_dn (const struct slapdplugin *plugin) +{ + char *plugindn; + char *pattern = "cn=%s," PLUGIN_BASE_DN; + + if (plugin == NULL) /* old plugin that does not pass identity - use default */ + plugin = &global_default_plg; + + if (plugin->plg_name == NULL) + return NULL; + + plugindn = (char*)slapi_ch_malloc (strlen (pattern) + strlen (plugin->plg_name)); + if (plugindn) + sprintf (plugindn, pattern, plugin->plg_name); + + return plugindn; +} + +static PRBool plugin_is_global (const PluginTargetData *ptd) +{ + /* plugin is considered to be global if it is invoked for + global data, local data and anonymous bind (bind target + data only). We don't include directory manager here + as it is considered to be part of local data */ + return (ptd_is_special_data_set (ptd, PLGC_DATA_LOCAL) && + ptd_is_special_data_set (ptd, PLGC_DATA_REMOTE) && + ptd_is_special_data_set (ptd, PLGC_DATA_BIND_ANONYMOUS) && + ptd_is_special_data_set (ptd, PLGC_DATA_BIND_ROOT)); +} + +static void plugin_set_global (PluginTargetData *ptd) +{ + PR_ASSERT (ptd); + + /* plugin is global if it is allowed access to all data */ + ptd_set_special_data (ptd, PLGC_DATA_LOCAL); + ptd_set_special_data (ptd, PLGC_DATA_REMOTE); + ptd_set_special_data (ptd, PLGC_DATA_BIND_ANONYMOUS); + ptd_set_special_data (ptd, PLGC_DATA_BIND_ROOT); +} + +static void plugin_set_default_access (struct pluginconfig *config) +{ + /* by default, plugins are invoked if dn is local for bind operations, + and for all requests for all other operations */ + PR_ASSERT (config); + + plugin_set_global (&config->plgc_target_subtrees); + ptd_set_special_data (&config->plgc_bind_subtrees, PLGC_DATA_LOCAL); + ptd_set_special_data (&config->plgc_bind_subtrees, PLGC_DATA_REMOTE); +} + +/* determine whether operation should be allowed based on plugin configuration */ +PRBool plugin_allow_internal_op (Slapi_DN *target_spec, struct slapdplugin *plugin) +{ + struct pluginconfig *config = plugin_get_config (plugin); + Slapi_Backend *be; + int islocal; + + if (plugin_is_global (&config->plgc_target_subtrees)) + return PR_TRUE; + + /* ONREPL - we do be_select to decide whether the request is for local + or remote data. We might need to reconsider how to do this + for performance reasons since be_select will be done again + once the operation goes through */ + be = slapi_be_select(target_spec); + + /* determine whether data are local or remote */ + /* remote if chaining backend or default backend */ + + if ( be!=NULL ) { + islocal=!(slapi_be_is_flag_set(be,SLAPI_BE_FLAG_REMOTE_DATA)); + } else { + islocal = be != defbackend_get_backend(); + } + /* SIMPLE auth method sends us through original code path in plugin_mathches_operation */ + return plugin_matches_operation (target_spec, &config->plgc_target_subtrees, + PR_FALSE, PR_FALSE, islocal, LDAP_AUTH_SIMPLE); +} + +static PRBool plugin_matches_operation (Slapi_DN *target_spec, PluginTargetData *ptd, + PRBool bindop, PRBool isroot, PRBool islocal, int method) +{ + int cookie; + Slapi_DN *subtree; + + /* check for special cases */ + + if (plugin_is_global (ptd)) + return PR_TRUE; + + /* if method is SASL we can have a null DN so bypass this check*/ + if(method != LDAP_AUTH_SASL) { + if (bindop && target_spec && (slapi_sdn_get_dn (target_spec) == NULL || + slapi_sdn_get_dn (target_spec)[0] == '\0')) + { + return (ptd_is_special_data_set (ptd, PLGC_DATA_BIND_ANONYMOUS)); + } + } + + /* check for root bind */ + if (bindop && isroot) + { + return (ptd_is_special_data_set (ptd, PLGC_DATA_BIND_ROOT)); + } + + /* check for local data */ + if (ptd_is_special_data_set (ptd, PLGC_DATA_LOCAL) && islocal) + { + return PR_TRUE; + } + + /* check for remote data */ + if (ptd_is_special_data_set (ptd, PLGC_DATA_REMOTE) && !islocal) + { + return (PR_TRUE); + } + + subtree = ptd_get_first_subtree (ptd, &cookie); + while (subtree) + { + if (slapi_sdn_issuffix (target_spec, subtree)) + return (PR_TRUE); + + subtree = ptd_get_next_subtree (ptd, &cookie); + } + + return PR_FALSE; + +} + +/* build operation action bitmap based on plugin configuration and actions specified for the operation */ +int plugin_build_operation_action_bitmap (int input_actions, const struct slapdplugin *plugin) +{ + int result_actions = 0; + + /* old plugin that does not pass its identity to the operation */ + if (plugin == NULL) + plugin = &global_default_plg; + + if (plugin->plg_conf.plgc_log_access) + result_actions |= OP_FLAG_ACTION_LOG_ACCESS; + + if (plugin->plg_conf.plgc_log_audit) + result_actions |= OP_FLAG_ACTION_LOG_AUDIT; + + /* + * OP_FLAG_ACTION_INVOKE_FOR_REPLOP is now used only by URP code. + * If someday this code needs to reclaim the flag, it has to use + * another flag to avoid the conflict with URP code. + * + * if (plugin->plg_conf.plgc_invoke_for_replop) + * result_actions |= OP_FLAG_ACTION_INVOKE_FOR_REPLOP; + */ + + switch (plugin->plg_conf.plgc_schema_check) + { + case PLGC_OFF: result_actions &= ~OP_FLAG_ACTION_SCHEMA_CHECK; + break; + + case PLGC_ON: result_actions |= OP_FLAG_ACTION_SCHEMA_CHECK; + break; + + case PLGC_UPTOPLUGIN: break; + + default: PR_ASSERT (PR_FALSE); + } + + switch (plugin->plg_conf.plgc_log_change) + { + case PLGC_OFF: result_actions &= ~OP_FLAG_ACTION_LOG_CHANGES; + break; + + case PLGC_ON: result_actions |= OP_FLAG_ACTION_LOG_CHANGES; + break; + + case PLGC_UPTOPLUGIN: break; + + default: PR_ASSERT (PR_FALSE); + } + + return result_actions; +} + +const struct slapdplugin* +plugin_get_server_plg() +{ + if(!global_server_plg_initialised) + { + global_server_plg.plg_name = "server"; + plugin_set_global (&global_server_plg.plg_conf.plgc_target_subtrees); + global_server_plg.plg_conf.plgc_log_access = 1; + global_server_plg.plg_conf.plgc_log_audit = 1; + global_server_plg.plg_conf.plgc_schema_check = 1; + global_server_plg.plg_conf.plgc_log_change = 1; + global_server_plg_initialised= 1; + global_server_plg_initialised= 1; + } + return &global_server_plg; +} + +struct slapi_componentid * plugin_get_default_component_id() { + + if(!global_server_plg_id_initialised) { + global_server_id_plg.sci_plugin=plugin_get_server_plg(); + global_server_id_plg.sci_component_name= + plugin_get_dn(global_server_id_plg.sci_plugin); + global_server_plg_id_initialised=1; + } + return &global_server_id_plg; +} + +static void +default_plugin_init() +{ + global_default_plg.plg_name = "old plugin"; + plugin_config_init (&global_default_plg.plg_conf); + plugin_set_default_access (&global_default_plg.plg_conf); +} + +#if 0 +static void trace_plugin_invocation (Slapi_DN *target_spec, PluginTargetData *ptd, + PRBool bindop, PRBool isroot, PRBool islocal, int invoked) +{ + int cookie, i = 0; + Slapi_DN *sdn; + + + slapi_log_error (SLAPI_LOG_FATAL, NULL, + "Invocation parameters: target_spec = %s, bindop = %d, isroot=%d, islocal=%d\n" + "Plugin configuration: local_data=%d, remote_data=%d, anonymous_bind=%d, root_bind=%d\n", + slapi_sdn_get_ndn (target_spec), bindop, isroot, islocal, ptd->special_data[0], + ptd->special_data[1], ptd->special_data[2], ptd->special_data[3]); + + sdn = ptd_get_first_subtree (ptd, &cookie); + while (sdn) + { + slapi_log_error (SLAPI_LOG_FATAL, NULL, "target_subtree%d: %s\n", i, slapi_sdn_get_ndn (sdn)); + sdn = ptd_get_next_subtree (ptd, &cookie); + } + + slapi_log_error (SLAPI_LOG_FATAL, NULL, invoked ? "Plugin is invoked\n" : "Plugin is not invoked\n"); +} +#endif + +/* functions to manipulate PluginTargetData type */ +static void ptd_init (PluginTargetData *ptd) +{ + PR_ASSERT (ptd); + + dl_init (&ptd->subtrees, 0 /* initial count */); + memset (&ptd->special_data, 0, sizeof (ptd->special_data)); +} + +static void ptd_cleanup (PluginTargetData *ptd) +{ + PR_ASSERT (ptd); + + dl_cleanup (&ptd->subtrees, (FREEFN)slapi_sdn_free); + memset (&ptd->special_data, 0, sizeof (ptd->special_data)); +} + +static void ptd_add_subtree (PluginTargetData *ptd, Slapi_DN *subtree) +{ + PR_ASSERT (ptd); + PR_ASSERT (subtree); + + dl_add (&ptd->subtrees, subtree); +} + +static void ptd_set_special_data (PluginTargetData *ptd, int type) +{ + PR_ASSERT (ptd); + PR_ASSERT (type >= 0 && type < PLGC_DATA_MAX); + + ptd->special_data [type] = PR_TRUE; +} + +#if 0 +static void ptd_clear_special_data (PluginTargetData *ptd, int type) +{ + PR_ASSERT (ptd); + PR_ASSERT (type >= 0 && type < PLGC_DATA_MAX); + + ptd->special_data [type] = PR_FALSE; +} +#endif + +static Slapi_DN *ptd_get_first_subtree (const PluginTargetData *ptd, int *cookie) +{ + PR_ASSERT (ptd); + + return dl_get_first (&ptd->subtrees, cookie); +} + +static Slapi_DN *ptd_get_next_subtree (const PluginTargetData *ptd, int *cookie) +{ + PR_ASSERT (ptd); + + return dl_get_next (&ptd->subtrees, cookie); +} + +static PRBool ptd_is_special_data_set (const PluginTargetData *ptd, int type) +{ + PR_ASSERT (ptd); + PR_ASSERT (type >= 0 && type < PLGC_DATA_MAX); + + return ptd->special_data [type]; +} + +#if 0 +static Slapi_DN* ptd_delete_subtree (PluginTargetData *ptd, Slapi_DN *subtree) +{ + PR_ASSERT (ptd); + PR_ASSERT (subtree); + + return (Slapi_DN*)dl_delete (&ptd->subtrees, subtree, (CMPFN)slapi_sdn_compare, NULL); +} +#endif + +int ptd_get_subtree_count (const PluginTargetData *ptd) +{ + PR_ASSERT (ptd); + + return dl_get_count (&ptd->subtrees); +} + +/* needed by command-line tasks to find an instance's plugin */ +struct slapdplugin *plugin_get_by_name(char *name) +{ + int x; + struct slapdplugin *plugin; + + for(x = 0; x < PLUGIN_LIST_GLOBAL_MAX; x++) { + for(plugin = global_plugin_list[x]; plugin; plugin = plugin->plg_next) { + if (!strcmp(name, plugin->plg_name)) { + return plugin; + } + } + } + + return NULL; +} + +struct slapi_componentid * +generate_componentid ( struct slapdplugin * pp , char * name ) +{ + struct slapi_componentid * idp; + + idp = (struct slapi_componentid *) slapi_ch_calloc(1, sizeof( *idp )); + if ( pp ) + idp->sci_plugin=pp; + else + idp->sci_plugin=(struct slapdplugin *) plugin_get_server_plg(); + + if ( name ) + idp->sci_component_name = slapi_ch_strdup(name); + else + /* Use plugin dn */ + idp->sci_component_name = plugin_get_dn( idp->sci_plugin ); + + if (idp->sci_component_name) + slapi_dn_normalize(idp->sci_component_name); + return idp; +} + +void release_componentid ( struct slapi_componentid * id ) +{ + if ( id ) { + if ( id->sci_component_name ) { + slapi_ch_free((void **)&id->sci_component_name); + id->sci_component_name=NULL; + } + slapi_ch_free((void **)&id); + } +} + +/* used in main.c if -V flag is given */ + +static void slapd_print_plugin_version ( + struct slapdplugin *plg, + struct slapdplugin *prev +) +{ + if (plg == NULL || plg->plg_libpath == NULL) return; + + /* same library as previous - don't print twice */ + if (prev != NULL && prev->plg_libpath != NULL) { + if (strcmp(prev->plg_libpath,plg->plg_libpath) == 0) { + return; + } + } + + printf("%s: %s\n", + plg->plg_libpath, + plg->plg_desc.spd_version ? plg->plg_desc.spd_version : ""); +} + +static void slapd_print_pluginlist_versions(struct slapdplugin *plg) +{ + struct slapdplugin *p,*prev = NULL; + + for (p = plg; p != NULL; p = p->plg_next) { + slapd_print_plugin_version(p,prev); + prev = p; + } +} + +void plugin_print_versions(void) +{ + int i; + + for (i = 0; i < PLUGIN_LIST_GLOBAL_MAX; i++) { + slapd_print_pluginlist_versions(get_plugin_list(i)); + } + +} |