diff options
author | Nathan Kinder <nkinder@redhat.com> | 2008-06-19 15:18:20 +0000 |
---|---|---|
committer | Nathan Kinder <nkinder@redhat.com> | 2008-06-19 15:18:20 +0000 |
commit | 0895c26fcf5eedda8064bed59df46b8f60017d6d (patch) | |
tree | 9c732fba0ffcc3b7e3db0885658e67d12a7b233d /ldap/servers | |
parent | 7d484eae318edd044de5d271abb7ed003f0878cb (diff) | |
download | ds-0895c26fcf5eedda8064bed59df46b8f60017d6d.tar.gz ds-0895c26fcf5eedda8064bed59df46b8f60017d6d.tar.xz ds-0895c26fcf5eedda8064bed59df46b8f60017d6d.zip |
Resolves: 450989
Summary: Make memberOf plug-in attributes configurable.
Diffstat (limited to 'ldap/servers')
-rw-r--r-- | ldap/servers/plugins/memberof/memberof.c | 534 | ||||
-rw-r--r-- | ldap/servers/plugins/memberof/memberof.h | 95 | ||||
-rw-r--r-- | ldap/servers/plugins/memberof/memberof_config.c | 341 |
3 files changed, 759 insertions, 211 deletions
diff --git a/ldap/servers/plugins/memberof/memberof.c b/ldap/servers/plugins/memberof/memberof.c index d435aec8..aa222049 100644 --- a/ldap/servers/plugins/memberof/memberof.c +++ b/ldap/servers/plugins/memberof/memberof.c @@ -72,18 +72,14 @@ #include "string.h" #include "nspr.h" -#define MEMBEROF_GROUP_ATTR "member" -#define MEMBEROF_ATTR "memberof" -#define MEMBEROF_GROUP_ATTR_IS_DN 1 -#define MEMBEROF_GROUP_FILTER "(" MEMBEROF_GROUP_ATTR "=*)" +#include "memberof.h" -#define MEMBEROF_PLUGIN_SUBSYSTEM "memberof-plugin" /* used for logging */ static Slapi_PluginDesc pdesc = { "memberof", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "memberof plugin" }; static void* _PluginID = NULL; -static Slapi_Filter *memberof_group_filter = NULL; static Slapi_Mutex *memberof_operation_lock = 0; +MemberOfConfig *qsortConfig = 0; typedef struct _memberofstringll { @@ -107,47 +103,60 @@ static int memberof_postop_close(Slapi_PBlock *pb); /* supporting cast */ static int memberof_oktodo(Slapi_PBlock *pb); static char *memberof_getdn(Slapi_PBlock *pb); -static int memberof_modop_one(Slapi_PBlock *pb, int mod_op, char *op_this, char *op_to); -static int memberof_modop_one_r(Slapi_PBlock *pb, int mod_op, char *group_dn, - char *op_this, char *op_to, memberofstringll *stack); -static int memberof_add_one(Slapi_PBlock *pb, char *addthis, char *addto); -static int memberof_del_one(Slapi_PBlock *pb, char *delthis, char *delfrom); -static int memberof_mod_smod_list(Slapi_PBlock *pb, int mod, char *groupdn, - Slapi_Mod *smod); -static int memberof_add_smod_list(Slapi_PBlock *pb, char *groupdn, Slapi_Mod *smod); -static int memberof_del_smod_list(Slapi_PBlock *pb, char *groupdn, Slapi_Mod *smod); -static int memberof_mod_attr_list(Slapi_PBlock *pb, int mod, char *groupdn, - Slapi_Attr *attr); -static int memberof_mod_attr_list_r(Slapi_PBlock *pb, int mod, char *group_dn, - char *op_this, Slapi_Attr *attr, memberofstringll *stack); -static int memberof_add_attr_list(Slapi_PBlock *pb, char *groupdn, Slapi_Attr *attr); -static int memberof_del_attr_list(Slapi_PBlock *pb, char *groupdn, Slapi_Attr *attr); -static int memberof_moddn_attr_list(Slapi_PBlock *pb, char *pre_dn, char *post_dn, - Slapi_Attr *attr); -static int memberof_replace_list(Slapi_PBlock *pb, char *group_dn); +static int memberof_modop_one(Slapi_PBlock *pb, MemberOfConfig *config, int mod_op, + char *op_this, char *op_to); +static int memberof_modop_one_r(Slapi_PBlock *pb, MemberOfConfig *config, int mod_op, + char *group_dn, char *op_this, char *op_to, memberofstringll *stack); +static int memberof_add_one(Slapi_PBlock *pb, MemberOfConfig *config, char *addthis, + char *addto); +static int memberof_del_one(Slapi_PBlock *pb, MemberOfConfig *config, char *delthis, + char *delfrom); +static int memberof_mod_smod_list(Slapi_PBlock *pb, MemberOfConfig *config, int mod, + char *groupdn, Slapi_Mod *smod); +static int memberof_add_smod_list(Slapi_PBlock *pb, MemberOfConfig *config, + char *groupdn, Slapi_Mod *smod); +static int memberof_del_smod_list(Slapi_PBlock *pb, MemberOfConfig *config, + char *groupdn, Slapi_Mod *smod); +static int memberof_mod_attr_list(Slapi_PBlock *pb, MemberOfConfig *config, int mod, + char *groupdn, Slapi_Attr *attr); +static int memberof_mod_attr_list_r(Slapi_PBlock *pb, MemberOfConfig *config, + int mod, char *group_dn, char *op_this, Slapi_Attr *attr, memberofstringll *stack); +static int memberof_add_attr_list(Slapi_PBlock *pb, MemberOfConfig *config, + char *groupdn, Slapi_Attr *attr); +static int memberof_del_attr_list(Slapi_PBlock *pb, MemberOfConfig *config, + char *groupdn, Slapi_Attr *attr); +static int memberof_moddn_attr_list(Slapi_PBlock *pb, MemberOfConfig *config, + char *pre_dn, char *post_dn, Slapi_Attr *attr); +static int memberof_replace_list(Slapi_PBlock *pb, MemberOfConfig *config, char *group_dn); static void memberof_set_plugin_id(void * plugin_id); static void *memberof_get_plugin_id(); -static int memberof_compare(const void *a, const void *b); +static int memberof_compare(MemberOfConfig *config, const void *a, const void *b); +static int memberof_qsort_compare(const void *a, const void *b); static void memberof_load_array(Slapi_Value **array, Slapi_Attr *attr); -static Slapi_Filter *memberof_string2filter(char *strfilter); -static int memberof_is_legit_member(Slapi_PBlock *pb, char *group_dn, - char *op_this, char *op_to, memberofstringll *stack); -static int memberof_del_dn_from_groups(Slapi_PBlock *pb, char *dn); +static int memberof_is_legit_member(Slapi_PBlock *pb, MemberOfConfig *config, + char *group_dn, char *op_this, char *op_to, memberofstringll *stack); +static int memberof_del_dn_from_groups(Slapi_PBlock *pb, MemberOfConfig *config, char *dn); static int memberof_call_foreach_dn(Slapi_PBlock *pb, char *dn, char *type, plugin_search_entry_callback callback, void *callback_data); -static int memberof_is_direct_member(Slapi_Value *groupdn, Slapi_Value *memberdn); -static int memberof_is_member(Slapi_Value *groupdn, Slapi_Value *memberdn); -static int memberof_test_membership(Slapi_PBlock *pb, char *group_dn); +static int memberof_is_direct_member(MemberOfConfig *config, Slapi_Value *groupdn, + Slapi_Value *memberdn); +static int memberof_is_member(MemberOfConfig *config, Slapi_Value *groupdn, + Slapi_Value *memberdn); +static int memberof_is_member_r(MemberOfConfig *config, Slapi_Value *groupdn, + Slapi_Value *memberdn, memberofstringll *stack); +static int memberof_test_membership(Slapi_PBlock *pb, MemberOfConfig *config, + char *group_dn); static int memberof_test_membership_callback(Slapi_Entry *e, void *callback_data); static int memberof_del_dn_type_callback(Slapi_Entry *e, void *callback_data); static int memberof_replace_dn_type_callback(Slapi_Entry *e, void *callback_data); -static int memberof_replace_dn_from_groups(Slapi_PBlock *pb, char *pre_dn, char *post_dn); -static int memberof_modop_one_replace_r(Slapi_PBlock *pb, int mod_op, char *group_dn, - char *op_this, char *replace_with, char *op_to, memberofstringll *stack); -static void memberof_lock(); -static void memberof_unlock(); +static int memberof_replace_dn_from_groups(Slapi_PBlock *pb, MemberOfConfig *config, + char *pre_dn, char *post_dn); +static int memberof_modop_one_replace_r(Slapi_PBlock *pb, MemberOfConfig *config, + int mod_op, char *group_dn, char *op_this, char *replace_with, char *op_to, + memberofstringll *stack); static int memberof_add_groups_search_callback(Slapi_Entry *e, void *callback_data); -static int memberof_add_membership(Slapi_PBlock *pb, char *op_this, char *op_to); +static int memberof_add_membership(Slapi_PBlock *pb, MemberOfConfig *config, + char *op_this, char *op_to); static int memberof_task_add(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg); @@ -155,7 +164,7 @@ static void memberof_task_destructor(Slapi_Task *task); static const char *fetch_attr(Slapi_Entry *e, const char *attrname, const char *default_val); static void memberof_fixup_task_thread(void *arg); -static int memberof_fix_memberof(char *dn, char *filter_str); +static int memberof_fix_memberof(MemberOfConfig *config, char *dn, char *filter_str); static int memberof_fix_memberof_callback(Slapi_Entry *e, void *callback_data); @@ -223,20 +232,31 @@ memberof_postop_init(Slapi_PBlock *pb) int memberof_postop_start(Slapi_PBlock *pb) { int rc = 0; + Slapi_Entry *config_e = NULL; /* entry containing plugin config */ slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, "--> memberof_postop_start\n" ); - memberof_group_filter = memberof_string2filter(MEMBEROF_GROUP_FILTER); - memberof_operation_lock = slapi_new_mutex(); - - if(0 == memberof_group_filter || 0 == memberof_operation_lock) + if(0 == memberof_operation_lock) { rc = -1; goto bail; } + if ( slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &config_e ) != 0 ) { + slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, + "missing config entry\n" ); + rc = -1; + goto bail; + } + + if (( rc = memberof_config( config_e )) != LDAP_SUCCESS ) { + slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, + "configuration failed (%s)\n", ldap_err2string( rc )); + return( -1 ); + } + rc = slapi_task_register_handler("memberof task", memberof_task_add); if(rc) { @@ -292,6 +312,7 @@ int memberof_postop_close(Slapi_PBlock *pb) int memberof_postop_del(Slapi_PBlock *pb) { int ret = 0; + MemberOfConfig configCopy = {0, 0, 0, 0}; char *dn; slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, @@ -302,22 +323,31 @@ int memberof_postop_del(Slapi_PBlock *pb) struct slapi_entry *e = NULL; slapi_pblock_get( pb, SLAPI_ENTRY_PRE_OP, &e ); - - memberof_lock(); + /* We need to get the config lock first. Trying to get the + * config lock after we already hold the op lock can cause + * a deadlock. */ + memberof_rlock_config(); + /* copy config so it doesn't change out from under us */ + memberof_copy_config(&configCopy, memberof_get_config()); + memberof_unlock_config(); + + /* get the memberOf operation lock */ + memberof_lock(); + /* remove this group DN from the * membership lists of groups */ - memberof_del_dn_from_groups(pb, dn); + memberof_del_dn_from_groups(pb, &configCopy, dn); /* is the entry of interest as a group? */ - if(e && !slapi_filter_test_simple(e, memberof_group_filter)) + if(e && !slapi_filter_test_simple(e, configCopy.group_filter)) { Slapi_Attr *attr = 0; - if(0 == slapi_entry_attr_find(e, MEMBEROF_GROUP_ATTR, &attr)) + if(0 == slapi_entry_attr_find(e, configCopy.groupattr, &attr)) { - memberof_del_attr_list(pb, dn, attr); + memberof_del_attr_list(pb, &configCopy, dn, attr); } } @@ -335,12 +365,12 @@ typedef struct _del_dn_data char *type; } del_dn_data; -int memberof_del_dn_from_groups(Slapi_PBlock *pb, char *dn) +int memberof_del_dn_from_groups(Slapi_PBlock *pb, MemberOfConfig *config, char *dn) { - del_dn_data data = {dn, MEMBEROF_GROUP_ATTR}; + del_dn_data data = {dn, config->groupattr}; return memberof_call_foreach_dn(pb, dn, - MEMBEROF_GROUP_ATTR, memberof_del_dn_type_callback, &data); + config->groupattr, memberof_del_dn_type_callback, &data); } int memberof_del_dn_type_callback(Slapi_Entry *e, void *callback_data) @@ -408,12 +438,7 @@ int memberof_call_foreach_dn(Slapi_PBlock *pb, char *dn, if(base_sdn) { - int filter_size = - (strlen(type) + - strlen(dn) + 4); /* 4 for (=) + null */ - filter_str = (char*)slapi_ch_malloc(filter_size); - - sprintf(filter_str, "(%s=%s)", type, dn); + filter_str = slapi_ch_smprintf("(%s=%s)", type, dn); } if(filter_str) @@ -451,10 +476,13 @@ int memberof_postop_modrdn(Slapi_PBlock *pb) if(memberof_oktodo(pb)) { + MemberOfConfig *mainConfig = 0; + MemberOfConfig configCopy = {0, 0, 0, 0}; struct slapi_entry *pre_e = NULL; struct slapi_entry *post_e = NULL; char *pre_dn = 0; char *post_dn = 0; + int interested = 0; slapi_pblock_get( pb, SLAPI_ENTRY_PRE_OP, &pre_e ); slapi_pblock_get( pb, SLAPI_ENTRY_POST_OP, &post_e ); @@ -466,8 +494,18 @@ int memberof_postop_modrdn(Slapi_PBlock *pb) } /* is the entry of interest? */ - if(pre_dn && post_dn && - !slapi_filter_test_simple(post_e, memberof_group_filter)) + memberof_rlock_config(); + mainConfig = memberof_get_config(); + if(pre_dn && post_dn && + !slapi_filter_test_simple(post_e, mainConfig->group_filter)) + { + interested = 1; + /* copy config so it doesn't change out from under us */ + memberof_copy_config(&configCopy, mainConfig); + } + memberof_unlock_config(); + + if(interested) { Slapi_Attr *attr = 0; @@ -475,15 +513,15 @@ int memberof_postop_modrdn(Slapi_PBlock *pb) /* get a list of member attributes present in the group * entry that is being renamed. */ - if(0 == slapi_entry_attr_find(post_e, MEMBEROF_GROUP_ATTR, &attr)) + if(0 == slapi_entry_attr_find(post_e, configCopy.groupattr, &attr)) { - memberof_moddn_attr_list(pb, pre_dn, post_dn, attr); + memberof_moddn_attr_list(pb, &configCopy, pre_dn, post_dn, attr); } /* modrdn must change the dns in groups that have * this group as a member. */ - memberof_replace_dn_from_groups(pb, pre_dn, post_dn); + memberof_replace_dn_from_groups(pb, &configCopy, pre_dn, post_dn); memberof_unlock(); } @@ -492,8 +530,7 @@ int memberof_postop_modrdn(Slapi_PBlock *pb) slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, "<-- memberof_postop_modrdn\n" ); - return ret;slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, - "--> memberof_postop_modify\n" ); + return ret; } typedef struct _replace_dn_data @@ -503,11 +540,12 @@ typedef struct _replace_dn_data char *type; } replace_dn_data; -int memberof_replace_dn_from_groups(Slapi_PBlock *pb, char *pre_dn, char *post_dn) +int memberof_replace_dn_from_groups(Slapi_PBlock *pb, MemberOfConfig *config, + char *pre_dn, char *post_dn) { - replace_dn_data data = {pre_dn, post_dn, MEMBEROF_GROUP_ATTR}; + replace_dn_data data = {pre_dn, post_dn, config->groupattr}; - return memberof_call_foreach_dn(pb, pre_dn, MEMBEROF_GROUP_ATTR, + return memberof_call_foreach_dn(pb, pre_dn, config->groupattr, memberof_replace_dn_type_callback, &data); } @@ -588,6 +626,10 @@ int memberof_postop_modify(Slapi_PBlock *pb) if(memberof_oktodo(pb) && (dn = memberof_getdn(pb))) { + int config_copied = 0; + MemberOfConfig *mainConfig = 0; + MemberOfConfig configCopy = {0, 0, 0, 0}; + /* get the mod set */ slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods); smods = slapi_mods_new(); @@ -597,10 +639,36 @@ int memberof_postop_modify(Slapi_PBlock *pb) smod = slapi_mods_get_first_smod(smods, next_mod); while(smod) { + int interested = 0; char *type = (char *)slapi_mod_get_type(smod); - /* we only care about the group attribute */ - if(slapi_attr_types_equivalent(type,MEMBEROF_GROUP_ATTR)) + /* We only want to copy the config if we encounter an + * operation that we need to act on. We also want to + * only copy the config the first time it's needed so + * it remains the same for all mods in the operation, + * despite any config changes that may be made. */ + if (!config_copied) + { + memberof_rlock_config(); + mainConfig = memberof_get_config(); + + if(slapi_attr_types_equivalent(type, mainConfig->groupattr)) + { + interested = 1; + /* copy config so it doesn't change out from under us */ + memberof_copy_config(&configCopy, mainConfig); + config_copied = 1; + } + + memberof_unlock_config(); + } else { + if(slapi_attr_types_equivalent(type, configCopy.groupattr)) + { + interested = 1; + } + } + + if(interested) { int op = slapi_mod_get_operation(smod); @@ -612,7 +680,7 @@ int memberof_postop_modify(Slapi_PBlock *pb) case LDAP_MOD_ADD: { /* add group DN to targets */ - memberof_add_smod_list(pb, dn, smod); + memberof_add_smod_list(pb, &configCopy, dn, smod); break; } @@ -624,12 +692,12 @@ int memberof_postop_modify(Slapi_PBlock *pb) * entry, which the replace code deals with. */ if (slapi_mod_get_num_values(smod) == 0) { - memberof_replace_list(pb, dn); + memberof_replace_list(pb, &configCopy, dn); } else { /* remove group DN from target values in smod*/ - memberof_del_smod_list(pb, dn, smod); + memberof_del_smod_list(pb, &configCopy, dn, smod); } break; } @@ -637,7 +705,7 @@ int memberof_postop_modify(Slapi_PBlock *pb) case LDAP_MOD_REPLACE: { /* replace current values */ - memberof_replace_list(pb, dn); + memberof_replace_list(pb, &configCopy, dn); break; } @@ -677,6 +745,7 @@ int memberof_postop_modify(Slapi_PBlock *pb) int memberof_postop_add(Slapi_PBlock *pb) { int ret = 0; + int interested = 0; char *dn = 0; slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, @@ -684,20 +753,33 @@ int memberof_postop_add(Slapi_PBlock *pb) if(memberof_oktodo(pb) && (dn = memberof_getdn(pb))) { + MemberOfConfig *mainConfig = 0; + MemberOfConfig configCopy = {0, 0, 0, 0}; struct slapi_entry *e = NULL; slapi_pblock_get( pb, SLAPI_ENTRY_POST_OP, &e ); + /* is the entry of interest? */ - if(e && !slapi_filter_test_simple(e, memberof_group_filter)) + memberof_rlock_config(); + mainConfig = memberof_get_config(); + if(e && !slapi_filter_test_simple(e, mainConfig->group_filter)) + { + interested = 1; + /* copy config so it doesn't change out from under us */ + memberof_copy_config(&configCopy, mainConfig); + } + memberof_unlock_config(); + + if(interested) { Slapi_Attr *attr = 0; memberof_lock(); - if(0 == slapi_entry_attr_find(e, MEMBEROF_GROUP_ATTR, &attr)) + if(0 == slapi_entry_attr_find(e, configCopy.groupattr, &attr)) { - memberof_add_attr_list(pb, dn, attr); + memberof_add_attr_list(pb, &configCopy, dn, attr); } memberof_unlock(); @@ -774,9 +856,10 @@ char *memberof_getdn(Slapi_PBlock *pb) * * Also, we must not delete entries that are a member of the group */ -int memberof_modop_one(Slapi_PBlock *pb, int mod_op, char *op_this, char *op_to) +int memberof_modop_one(Slapi_PBlock *pb, MemberOfConfig *config, int mod_op, + char *op_this, char *op_to) { - return memberof_modop_one_r(pb, mod_op, op_this, op_this, op_to, 0); + return memberof_modop_one_r(pb, config, mod_op, op_this, op_this, op_to, 0); } /* memberof_modop_one_r() @@ -784,19 +867,20 @@ int memberof_modop_one(Slapi_PBlock *pb, int mod_op, char *op_this, char *op_to) * recursive function to perform above (most things don't need the replace arg) */ -int memberof_modop_one_r(Slapi_PBlock *pb, int mod_op, char *group_dn, - char *op_this, char *op_to, memberofstringll *stack) +int memberof_modop_one_r(Slapi_PBlock *pb, MemberOfConfig *config, int mod_op, + char *group_dn, char *op_this, char *op_to, memberofstringll *stack) { return memberof_modop_one_replace_r( - pb, mod_op, group_dn, op_this, 0, op_to, stack); + pb, config, mod_op, group_dn, op_this, 0, op_to, stack); } /* memberof_modop_one_replace_r() * * recursive function to perform above (with added replace arg) */ -int memberof_modop_one_replace_r(Slapi_PBlock *pb, int mod_op, char *group_dn, - char *op_this, char *replace_with, char *op_to, memberofstringll *stack) +int memberof_modop_one_replace_r(Slapi_PBlock *pb, MemberOfConfig *config, + int mod_op, char *group_dn, char *op_this, char *replace_with, + char *op_to, memberofstringll *stack) { int rc = 0; LDAPMod mod; @@ -805,7 +889,7 @@ int memberof_modop_one_replace_r(Slapi_PBlock *pb, int mod_op, char *group_dn, char *val[2]; char *replace_val[2]; Slapi_PBlock *mod_pb = 0; - char *attrlist[2] = {MEMBEROF_GROUP_ATTR,0}; + char *attrlist[2] = {config->groupattr,0}; Slapi_DN *op_to_sdn = 0; Slapi_Entry *e = 0; memberofstringll *ll = 0; @@ -813,7 +897,6 @@ int memberof_modop_one_replace_r(Slapi_PBlock *pb, int mod_op, char *group_dn, Slapi_Value *to_dn_val = slapi_value_new_string(op_to); Slapi_Value *this_dn_val = slapi_value_new_string(op_this); - /* determine if this is a group op or single entry */ op_to_sdn = slapi_sdn_new_dn_byref(op_to); slapi_search_internal_get_entry( op_to_sdn, attrlist, @@ -831,7 +914,7 @@ int memberof_modop_one_replace_r(Slapi_PBlock *pb, int mod_op, char *group_dn, * membership given the delete operation that * triggered this operation */ - memberof_test_membership(pb, group_dn); + memberof_test_membership(pb, config, group_dn); } goto bail; @@ -855,10 +938,10 @@ int memberof_modop_one_replace_r(Slapi_PBlock *pb, int mod_op, char *group_dn, } slapi_log_error( SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, - "memberof_modop_one_r: %s %s in %s\n" + "memberof_modop_one_replace_r: %s %s in %s\n" ,op_str, op_this, op_to); - if(!slapi_filter_test_simple(e, memberof_group_filter)) + if(!slapi_filter_test_simple(e, config->group_filter)) { /* group */ Slapi_Value *ll_dn_val = 0; @@ -871,7 +954,7 @@ int memberof_modop_one_replace_r(Slapi_PBlock *pb, int mod_op, char *group_dn, { ll_dn_val = slapi_value_new_string(ll->dn); - if(0 == memberof_compare(&ll_dn_val, &to_dn_val)) + if(0 == memberof_compare(config, &ll_dn_val, &to_dn_val)) { slapi_value_free(&ll_dn_val); @@ -879,7 +962,7 @@ int memberof_modop_one_replace_r(Slapi_PBlock *pb, int mod_op, char *group_dn, recursive groups - bail out */ slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, - "memberof_modop_one_r: group recursion" + "memberof_modop_one_replace_r: group recursion" " detected in %s\n" ,op_to); goto bail; @@ -892,17 +975,17 @@ int memberof_modop_one_replace_r(Slapi_PBlock *pb, int mod_op, char *group_dn, /* do op on group */ slapi_log_error( SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, - "memberof_modop_one_r: descending into group %s\n", + "memberof_modop_one_replace_r: descending into group %s\n", op_to); /* Add the nested group's DN to the stack so we can detect loops later. */ ll = (memberofstringll*)slapi_ch_malloc(sizeof(memberofstringll)); ll->dn = op_to; ll->next = stack; - slapi_entry_attr_find( e, MEMBEROF_GROUP_ATTR, &members ); + slapi_entry_attr_find( e, config->groupattr, &members ); if(members) { - memberof_mod_attr_list_r(pb, mod_op, group_dn, op_this, members, ll); + memberof_mod_attr_list_r(pb, config, mod_op, group_dn, op_this, members, ll); } { @@ -921,11 +1004,11 @@ int memberof_modop_one_replace_r(Slapi_PBlock *pb, int mod_op, char *group_dn, /* We want to avoid listing a group as a memberOf itself * in case someone set up a circular grouping. */ - if (0 == memberof_compare(&this_dn_val, &to_dn_val)) + if (0 == memberof_compare(config, &this_dn_val, &to_dn_val)) { slapi_log_error( SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, - "memberof_modop_one_r: not processing memberOf " + "memberof_modop_one_replace_r: not processing memberOf " "operations on self entry: %s\n", this_dn_val); goto bail; } @@ -935,13 +1018,13 @@ int memberof_modop_one_replace_r(Slapi_PBlock *pb, int mod_op, char *group_dn, * it could still be a member in some other indirect manner. */ if(stack && LDAP_MOD_DELETE == mod_op) { - if(memberof_is_legit_member(pb, group_dn, + if(memberof_is_legit_member(pb, config, group_dn, op_this, op_to, stack)) { /* entry is member some other way too */ slapi_log_error( SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, - "memberof_modop_one_r: not deleting %s\n" + "memberof_modop_one_replace_r: not deleting %s\n" ,op_to); goto bail; } @@ -949,14 +1032,15 @@ int memberof_modop_one_replace_r(Slapi_PBlock *pb, int mod_op, char *group_dn, /* Check if the entry is still an indirect member. If it is, we * don't want to remove the memberOf value. */ - if((LDAP_MOD_DELETE != mod_op) || (0 == memberof_is_member(this_dn_val, to_dn_val))) { + if((LDAP_MOD_DELETE != mod_op) || + (0 == memberof_is_member(config, this_dn_val, to_dn_val))) { /* If we're about to add a memberOf value to an entry, we should first check * if the value already exists. */ if((LDAP_MOD_ADD == mod_op) && (slapi_entry_attr_has_syntax_value(e, - MEMBEROF_ATTR, this_dn_val))) + config->memberof_attr, this_dn_val))) { slapi_log_error( SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, - "memberof_modop_one_r: memberOf value %s already exists in " + "memberof_modop_one_replace_r: memberOf value %s already exists in " "entry %s\n", op_this, op_to); goto bail; } @@ -979,7 +1063,7 @@ int memberof_modop_one_replace_r(Slapi_PBlock *pb, int mod_op, char *group_dn, val[1] = 0; mod.mod_op = LDAP_MOD_REPLACE == mod_op?LDAP_MOD_DELETE:mod_op; - mod.mod_type = MEMBEROF_ATTR; + mod.mod_type = config->memberof_attr; mod.mod_values = val; if(LDAP_MOD_REPLACE == mod_op) @@ -988,7 +1072,7 @@ int memberof_modop_one_replace_r(Slapi_PBlock *pb, int mod_op, char *group_dn, replace_val[1] = 0; replace_mod.mod_op = LDAP_MOD_ADD; - replace_mod.mod_type = MEMBEROF_ATTR; + replace_mod.mod_type = config->memberof_attr; replace_mod.mod_values = replace_val; } @@ -1009,7 +1093,7 @@ int memberof_modop_one_replace_r(Slapi_PBlock *pb, int mod_op, char *group_dn, if(LDAP_MOD_DELETE == mod_op) { /* fix up membership for groups that have been orphaned */ - memberof_test_membership_callback(e, 0); + memberof_test_membership_callback(e, config); } if(LDAP_MOD_ADD == mod_op) @@ -1018,7 +1102,7 @@ int memberof_modop_one_replace_r(Slapi_PBlock *pb, int mod_op, char *group_dn, * try to fix up membership for parent groups. */ if (rc == 0) { /* fix up membership for groups that are now in scope */ - memberof_add_membership(pb, op_this, op_to); + memberof_add_membership(pb, config, op_this, op_to); } } } @@ -1037,9 +1121,9 @@ bail: * Add addthis DN to the memberof attribute of addto * */ -int memberof_add_one(Slapi_PBlock *pb, char *addthis, char *addto) +int memberof_add_one(Slapi_PBlock *pb, MemberOfConfig *config, char *addthis, char *addto) { - return memberof_modop_one(pb, LDAP_MOD_ADD, addthis, addto); + return memberof_modop_one(pb, config, LDAP_MOD_ADD, addthis, addto); } /* @@ -1048,9 +1132,9 @@ int memberof_add_one(Slapi_PBlock *pb, char *addthis, char *addto) * Delete delthis DN from the memberof attribute of delfrom * */ -int memberof_del_one(Slapi_PBlock *pb, char *delthis, char *delfrom) +int memberof_del_one(Slapi_PBlock *pb, MemberOfConfig *config, char *delthis, char *delfrom) { - return memberof_modop_one(pb, LDAP_MOD_DELETE, delthis, delfrom); + return memberof_modop_one(pb, config, LDAP_MOD_DELETE, delthis, delfrom); } /* @@ -1059,7 +1143,8 @@ int memberof_del_one(Slapi_PBlock *pb, char *delthis, char *delfrom) * Perform mod for group DN to the memberof attribute of the list of targets * */ -int memberof_mod_smod_list(Slapi_PBlock *pb, int mod, char *group_dn, Slapi_Mod *smod) +int memberof_mod_smod_list(Slapi_PBlock *pb, MemberOfConfig *config, int mod, + char *group_dn, Slapi_Mod *smod) { int rc = 0; struct berval *bv = slapi_mod_get_first_value(smod); @@ -1091,7 +1176,7 @@ int memberof_mod_smod_list(Slapi_PBlock *pb, int mod, char *group_dn, Slapi_Mod strncpy(dn_str, bv->bv_val, (size_t)bv->bv_len); - memberof_modop_one(pb, mod, group_dn, dn_str); + memberof_modop_one(pb, config, mod, group_dn, dn_str); bv = slapi_mod_get_next_value(smod); } @@ -1108,9 +1193,10 @@ int memberof_mod_smod_list(Slapi_PBlock *pb, int mod, char *group_dn, Slapi_Mod * Add group DN to the memberof attribute of the list of targets * */ -int memberof_add_smod_list(Slapi_PBlock *pb, char *groupdn, Slapi_Mod *smod) +int memberof_add_smod_list(Slapi_PBlock *pb, MemberOfConfig *config, + char *groupdn, Slapi_Mod *smod) { - return memberof_mod_smod_list(pb, LDAP_MOD_ADD, groupdn, smod); + return memberof_mod_smod_list(pb, config, LDAP_MOD_ADD, groupdn, smod); } @@ -1120,9 +1206,10 @@ int memberof_add_smod_list(Slapi_PBlock *pb, char *groupdn, Slapi_Mod *smod) * Remove group DN from the memberof attribute of the list of targets * */ -int memberof_del_smod_list(Slapi_PBlock *pb, char *groupdn, Slapi_Mod *smod) +int memberof_del_smod_list(Slapi_PBlock *pb, MemberOfConfig *config, + char *groupdn, Slapi_Mod *smod) { - return memberof_mod_smod_list(pb, LDAP_MOD_DELETE, groupdn, smod); + return memberof_mod_smod_list(pb, config, LDAP_MOD_DELETE, groupdn, smod); } /** @@ -1145,13 +1232,14 @@ void * memberof_get_plugin_id() * Perform mod for group DN to the memberof attribute of the list of targets * */ -int memberof_mod_attr_list(Slapi_PBlock *pb, int mod, char *group_dn, Slapi_Attr *attr) +int memberof_mod_attr_list(Slapi_PBlock *pb, MemberOfConfig *config, int mod, + char *group_dn, Slapi_Attr *attr) { - return memberof_mod_attr_list_r(pb, mod, group_dn, group_dn, attr, 0); + return memberof_mod_attr_list_r(pb, config, mod, group_dn, group_dn, attr, 0); } -int memberof_mod_attr_list_r(Slapi_PBlock *pb, int mod, char *group_dn, char *op_this, - Slapi_Attr *attr, memberofstringll *stack) +int memberof_mod_attr_list_r(Slapi_PBlock *pb, MemberOfConfig *config, int mod, + char *group_dn, char *op_this, Slapi_Attr *attr, memberofstringll *stack) { int rc = 0; Slapi_Value *val = 0; @@ -1189,12 +1277,12 @@ int memberof_mod_attr_list_r(Slapi_PBlock *pb, int mod, char *group_dn, char *op * to specify the new group DN value */ if(mod == LDAP_MOD_REPLACE) { - memberof_modop_one_replace_r(pb, mod, group_dn, op_this, group_dn, + memberof_modop_one_replace_r(pb, config, mod, group_dn, op_this, group_dn, dn_str, stack); } else { - memberof_modop_one_r(pb, mod, group_dn, op_this, dn_str, stack); + memberof_modop_one_r(pb, config, mod, group_dn, op_this, dn_str, stack); } hint = slapi_attr_next_value(attr, hint, &val); @@ -1212,9 +1300,10 @@ int memberof_mod_attr_list_r(Slapi_PBlock *pb, int mod, char *group_dn, char *op * Add group DN to the memberof attribute of the list of targets * */ -int memberof_add_attr_list(Slapi_PBlock *pb, char *groupdn, Slapi_Attr *attr) +int memberof_add_attr_list(Slapi_PBlock *pb, MemberOfConfig *config, char *groupdn, + Slapi_Attr *attr) { - return memberof_mod_attr_list(pb, LDAP_MOD_ADD, groupdn, attr); + return memberof_mod_attr_list(pb, config, LDAP_MOD_ADD, groupdn, attr); } /* @@ -1223,9 +1312,10 @@ int memberof_add_attr_list(Slapi_PBlock *pb, char *groupdn, Slapi_Attr *attr) * Remove group DN from the memberof attribute of the list of targets * */ -int memberof_del_attr_list(Slapi_PBlock *pb, char *groupdn, Slapi_Attr *attr) +int memberof_del_attr_list(Slapi_PBlock *pb, MemberOfConfig *config, char *groupdn, + Slapi_Attr *attr) { - return memberof_mod_attr_list(pb, LDAP_MOD_DELETE, groupdn, attr); + return memberof_mod_attr_list(pb, config, LDAP_MOD_DELETE, groupdn, attr); } /* @@ -1234,7 +1324,8 @@ int memberof_del_attr_list(Slapi_PBlock *pb, char *groupdn, Slapi_Attr *attr) * Perform mod for group DN to the memberof attribute of the list of targets * */ -int memberof_moddn_attr_list(Slapi_PBlock *pb, char *pre_dn, char *post_dn, Slapi_Attr *attr) +int memberof_moddn_attr_list(Slapi_PBlock *pb, MemberOfConfig *config, + char *pre_dn, char *post_dn, Slapi_Attr *attr) { int rc = 0; Slapi_Value *val = 0; @@ -1268,7 +1359,7 @@ int memberof_moddn_attr_list(Slapi_PBlock *pb, char *pre_dn, char *post_dn, Slap strncpy(dn_str, bv->bv_val, (size_t)bv->bv_len); - memberof_modop_one_replace_r(pb, LDAP_MOD_REPLACE, + memberof_modop_one_replace_r(pb, config, LDAP_MOD_REPLACE, post_dn, pre_dn, post_dn, dn_str, 0); hint = slapi_attr_next_value(attr, hint, &val); @@ -1282,21 +1373,23 @@ int memberof_moddn_attr_list(Slapi_PBlock *pb, char *pre_dn, char *post_dn, Slap typedef struct _memberof_add_groups { + MemberOfConfig *config; char *target_dn; char *group_dn; } memberof_add_groups; -int memberof_add_membership(Slapi_PBlock *pb, char *op_this, char *op_to) +int memberof_add_membership(Slapi_PBlock *pb, MemberOfConfig *config, + char *op_this, char *op_to) { - memberof_add_groups data = {op_to, op_this}; + memberof_add_groups data = {config, op_to, op_this}; - return memberof_call_foreach_dn(pb, op_this, MEMBEROF_GROUP_ATTR, + return memberof_call_foreach_dn(pb, op_this, config->groupattr, memberof_add_groups_search_callback, &data); } int memberof_add_groups_search_callback(Slapi_Entry *e, void *callback_data) { - return memberof_add_one(0, slapi_entry_get_dn(e), + return memberof_add_one(0, ((memberof_add_groups*)callback_data)->config, slapi_entry_get_dn(e), ((memberof_add_groups*)callback_data)->target_dn); } @@ -1305,11 +1398,12 @@ int memberof_add_groups_search_callback(Slapi_Entry *e, void *callback_data) * tests for direct membership of memberdn in group groupdn * returns non-zero when true, zero otherwise */ -int memberof_is_direct_member(Slapi_Value *groupdn, Slapi_Value *memberdn) +int memberof_is_direct_member(MemberOfConfig *config, Slapi_Value *groupdn, + Slapi_Value *memberdn) { int rc = 0; Slapi_DN *sdn = 0; - char *attrlist[2] = {MEMBEROF_GROUP_ATTR,0}; + char *attrlist[2] = {config->groupattr,0}; Slapi_Entry *group_e = 0; Slapi_Attr *attr = 0; @@ -1320,7 +1414,7 @@ int memberof_is_direct_member(Slapi_Value *groupdn, Slapi_Value *memberdn) if(group_e) { - slapi_entry_attr_find(group_e, MEMBEROF_GROUP_ATTR, &attr ); + slapi_entry_attr_find(group_e, config->groupattr, &attr ); if(attr) { rc = 0 == slapi_attr_value_find( @@ -1339,19 +1433,20 @@ int memberof_is_direct_member(Slapi_Value *groupdn, Slapi_Value *memberdn) * will check for both direct and indirect membership. * returns non-zero when true, zero otherwise */ -int memberof_is_member(Slapi_Value *groupdn, Slapi_Value *memberdn) +int memberof_is_member(MemberOfConfig *config, Slapi_Value *groupdn, + Slapi_Value *memberdn) { memberofstringll *stack = 0; /* Do a quick check to see if the entry is a direct * member before tracing through nested groups. */ - if(memberof_is_direct_member(groupdn, memberdn)) + if(memberof_is_direct_member(config, groupdn, memberdn)) { /* entry is a direct member */ return 1; } - return memberof_is_member_r(groupdn, memberdn, stack); + return memberof_is_member_r(config, groupdn, memberdn, stack); } /* memberof_is_member_r() @@ -1364,7 +1459,8 @@ int memberof_is_member(Slapi_Value *groupdn, Slapi_Value *memberdn) * * returns non-zero when true, zero otherwise */ -int memberof_is_member_r(Slapi_Value *groupdn, Slapi_Value *memberdn, memberofstringll *stack) +int memberof_is_member_r(MemberOfConfig *config, Slapi_Value *groupdn, + Slapi_Value *memberdn, memberofstringll *stack) { Slapi_DN *member_sdn = 0; Slapi_DN *base_sdn = 0; @@ -1382,7 +1478,7 @@ int memberof_is_member_r(Slapi_Value *groupdn, Slapi_Value *memberdn, memberofst { ll_dn_val = slapi_value_new_string(ll->dn); - if(0 == memberof_compare(&ll_dn_val, &memberdn)) + if(0 == memberof_compare(config, &ll_dn_val, &memberdn)) { slapi_value_free(&ll_dn_val); @@ -1393,6 +1489,11 @@ int memberof_is_member_r(Slapi_Value *groupdn, Slapi_Value *memberdn, memberofst "memberof_is_member_r: group recursion" " detected in %s\n" ,slapi_value_get_string(memberdn)); + /* We set this to null to avoid freeing it twice. + * If we don't do this, we'd free ll in the bail section + * and the caller (ourselves since we're using recursion) + * would free it as well. */ + ll = 0; goto bail; } @@ -1419,11 +1520,8 @@ int memberof_is_member_r(Slapi_Value *groupdn, Slapi_Value *memberdn, memberofst * and keep looping until we've exhausted it. */ if(base_sdn) { - int filter_size = - (strlen(MEMBEROF_GROUP_ATTR) + - strlen(slapi_value_get_string(memberdn)) + 4); /* 4 for (=) + null */ - filter_str = (char*)slapi_ch_malloc(filter_size); - sprintf(filter_str, "(%s=%s)", MEMBEROF_GROUP_ATTR, slapi_value_get_string(memberdn)); + filter_str = slapi_ch_smprintf("(%s=%s)", config->groupattr, + slapi_value_get_string(memberdn)); } if(filter_str) @@ -1465,7 +1563,7 @@ int memberof_is_member_r(Slapi_Value *groupdn, Slapi_Value *memberdn, memberofst * see if any of them are the group we are trying to find. * We do this by doing a recursive call on this function. */ Slapi_Value *entrydn = slapi_value_new_string(slapi_entry_get_ndn(entries[i])); - rc = memberof_is_member_r(groupdn, entrydn, ll); + rc = memberof_is_member_r(config, groupdn, entrydn, ll); slapi_value_free(&entrydn); } } @@ -1498,10 +1596,10 @@ int memberof_is_member_r(Slapi_Value *groupdn, Slapi_Value *memberdn, memberofst * iterate until a pass fails to move a group over to member groups * remaining groups should be deleted */ -int memberof_test_membership(Slapi_PBlock *pb, char *group_dn) +int memberof_test_membership(Slapi_PBlock *pb, MemberOfConfig *config, char *group_dn) { - return memberof_call_foreach_dn(pb, group_dn, MEMBEROF_ATTR, - memberof_test_membership_callback ,0); + return memberof_call_foreach_dn(pb, group_dn, config->memberof_attr, + memberof_test_membership_callback , config); } /* @@ -1519,6 +1617,7 @@ int memberof_test_membership_callback(Slapi_Entry *e, void *callback_data) Slapi_Value **member_array = 0; Slapi_Value **candidate_array = 0; Slapi_Value *entry_dn = 0; + MemberOfConfig *config = (MemberOfConfig *)callback_data; entry_dn = slapi_value_new_string(slapi_entry_get_dn(e)); @@ -1528,7 +1627,7 @@ int memberof_test_membership_callback(Slapi_Entry *e, void *callback_data) } /* divide groups into member and non-member lists */ - slapi_entry_attr_find(e, MEMBEROF_ATTR, &attr ); + slapi_entry_attr_find(e, config->memberof_attr, &attr ); if(attr) { slapi_attr_get_numvalues( attr, &total); @@ -1555,7 +1654,7 @@ int memberof_test_membership_callback(Slapi_Entry *e, void *callback_data) while(val) { /* test for direct membership */ - if(memberof_is_direct_member(val, entry_dn)) + if(memberof_is_direct_member(config, val, entry_dn)) { /* it is a member */ member_array[m_index] = val; @@ -1602,6 +1701,7 @@ int memberof_test_membership_callback(Slapi_Entry *e, void *callback_data) } if(memberof_is_direct_member( + config, candidate_array[inner_index], member_array[outer_index])) { @@ -1640,7 +1740,7 @@ int memberof_test_membership_callback(Slapi_Entry *e, void *callback_data) } memberof_del_one( - 0, + 0, config, (char*)slapi_value_get_string( candidate_array[outer_index]), (char*)slapi_value_get_string(entry_dn)); @@ -1675,7 +1775,7 @@ bail: * Perform replace the group DN list in the memberof attribute of the list of targets * */ -int memberof_replace_list(Slapi_PBlock *pb, char *group_dn) +int memberof_replace_list(Slapi_PBlock *pb, MemberOfConfig *config, char *group_dn) { struct slapi_entry *pre_e = NULL; struct slapi_entry *post_e = NULL; @@ -1687,8 +1787,8 @@ int memberof_replace_list(Slapi_PBlock *pb, char *group_dn) if(pre_e && post_e) { - slapi_entry_attr_find( pre_e, MEMBEROF_GROUP_ATTR, &pre_attr ); - slapi_entry_attr_find( post_e, MEMBEROF_GROUP_ATTR, &post_attr ); + slapi_entry_attr_find( pre_e, config->groupattr, &pre_attr ); + slapi_entry_attr_find( post_e, config->groupattr, &post_attr ); } if(pre_attr || post_attr) @@ -1711,6 +1811,14 @@ int memberof_replace_list(Slapi_PBlock *pb, char *group_dn) slapi_attr_get_numvalues( post_attr, &post_total); } + /* Stash a plugin global pointer here and have memberof_qsort_compare + * use it. We have to do this because we use memberof_qsort_compare + * as the comparator function for qsort, which requires the function + * to only take two void* args. This is thread-safe since we only + * store and use the pointer while holding the memberOf operation + * lock. */ + qsortConfig = config; + if(pre_total) { pre_array = @@ -1721,7 +1829,7 @@ int memberof_replace_list(Slapi_PBlock *pb, char *group_dn) pre_array, pre_total, sizeof(Slapi_Value*), - memberof_compare); + memberof_qsort_compare); } if(post_total) @@ -1734,9 +1842,11 @@ int memberof_replace_list(Slapi_PBlock *pb, char *group_dn) post_array, post_total, sizeof(Slapi_Value*), - memberof_compare); + memberof_qsort_compare); } + qsortConfig = 0; + /* work through arrays, following these rules: in pre, in post, do nothing @@ -1749,7 +1859,7 @@ int memberof_replace_list(Slapi_PBlock *pb, char *group_dn) { /* add the rest of post */ memberof_add_one( - pb, + pb, config, group_dn, (char*)slapi_value_get_string( post_array[post_index])); @@ -1760,7 +1870,7 @@ int memberof_replace_list(Slapi_PBlock *pb, char *group_dn) { /* delete the rest of pre */ memberof_del_one( - pb, + pb, config, group_dn, (char*)slapi_value_get_string( pre_array[pre_index])); @@ -1771,6 +1881,7 @@ int memberof_replace_list(Slapi_PBlock *pb, char *group_dn) { /* decide what to do */ int cmp = memberof_compare( + config, &(pre_array[pre_index]), &(post_array[post_index])); @@ -1778,7 +1889,7 @@ int memberof_replace_list(Slapi_PBlock *pb, char *group_dn) { /* delete pre array */ memberof_del_one( - pb, + pb, config, group_dn, (char*)slapi_value_get_string( pre_array[pre_index])); @@ -1789,7 +1900,7 @@ int memberof_replace_list(Slapi_PBlock *pb, char *group_dn) { /* add post array */ memberof_add_one( - pb, + pb, config, group_dn, (char*)slapi_value_get_string( post_array[post_index])); @@ -1832,42 +1943,35 @@ void memberof_load_array(Slapi_Value **array, Slapi_Attr *attr) * * compare two attr values */ -int memberof_compare(const void *a, const void *b) +int memberof_compare(MemberOfConfig *config, const void *a, const void *b) { - static Slapi_Attr *attr = 0; - static int first_time = 1; Slapi_Value *val1 = *((Slapi_Value **)a); Slapi_Value *val2 = *((Slapi_Value **)b); - if(first_time) - { - first_time = 0; - attr = slapi_attr_new(); - slapi_attr_init(attr, MEMBEROF_GROUP_ATTR); - } - return slapi_attr_value_cmp( - attr, - slapi_value_get_berval(val1), + config->group_slapiattr, + slapi_value_get_berval(val1), slapi_value_get_berval(val2)); } -/* memberof_string2filter() +/* memberof_qsort_compare() * - * For some reason slapi_str2filter writes to its input - * which means you cannot pass in a string constant - * so this is a fix up function for that + * This is a version of memberof_compare that uses a plugin + * global copy of the config. We'd prefer to pass in a copy + * of config that is local to the running thread, but we can't + * do this since qsort is using us as a comparator function. + * We should only use this function when using qsort, and only + * when the memberOf lock is acquired. */ -Slapi_Filter *memberof_string2filter(char *strfilter) +int memberof_qsort_compare(const void *a, const void *b) { - Slapi_Filter *ret = 0; - char *idontbelieveit = slapi_ch_strdup(strfilter); - - ret = slapi_str2filter( idontbelieveit ); - - slapi_ch_free_string(&idontbelieveit); + Slapi_Value *val1 = *((Slapi_Value **)a); + Slapi_Value *val2 = *((Slapi_Value **)b); - return ret; + return slapi_attr_value_cmp( + qsortConfig->group_slapiattr, + slapi_value_get_berval(val1), + slapi_value_get_berval(val2)); } /* memberof_is_legit_member() @@ -1880,8 +1984,8 @@ Slapi_Filter *memberof_string2filter(char *strfilter) * the second from bottom one of our stack do not appear * in the membership attribute of the group */ -int memberof_is_legit_member(Slapi_PBlock *pb, char *group_dn, - char *op_this, char *op_to, memberofstringll *stack) +int memberof_is_legit_member(Slapi_PBlock *pb, MemberOfConfig *config, + char *group_dn, char *op_this, char *op_to, memberofstringll *stack) { int rc = 0; Slapi_DN *group_sdn = 0; @@ -1890,10 +1994,9 @@ int memberof_is_legit_member(Slapi_PBlock *pb, char *group_dn, Slapi_Entry *opto_e = 0; char *filter_str = 0; Slapi_Filter *filter = 0; - int filter_size = 0; memberofstringll *ll = 0; - char *attrlist[2] = {MEMBEROF_GROUP_ATTR,0}; - char *optolist[2] = {MEMBEROF_ATTR,0}; + char *attrlist[2] = {config->groupattr,0}; + char *optolist[2] = {config->memberof_attr,0}; Slapi_Attr *memberof = 0; Slapi_Value *memberdn = 0; int hint = 0; @@ -1913,14 +2016,8 @@ int memberof_is_legit_member(Slapi_PBlock *pb, char *group_dn, goto bail; } - filter_size = 2 * - (strlen(MEMBEROF_GROUP_ATTR) + - strlen(op_to) + 4); /* 4 for (=) + null */ - filter_str = (char*)slapi_ch_malloc(filter_size); - - sprintf(filter_str, "(%s=%s)", MEMBEROF_GROUP_ATTR, op_to); - - filter = memberof_string2filter(filter_str); + filter_str = slapi_ch_smprintf("(%s=%s)", config->groupattr, op_to); + filter = slapi_str2filter(filter_str); if(!slapi_filter_test_simple(group_e, filter)) { @@ -1966,7 +2063,7 @@ int memberof_is_legit_member(Slapi_PBlock *pb, char *group_dn, if(opto_e) { - slapi_entry_attr_find(opto_e, MEMBEROF_ATTR, &memberof); + slapi_entry_attr_find(opto_e, config->memberof_attr, &memberof); } if(0 == memberof) @@ -1981,7 +2078,7 @@ int memberof_is_legit_member(Slapi_PBlock *pb, char *group_dn, { char *dn = (char*)slapi_value_get_string(memberdn); int current_size = - (strlen(MEMBEROF_GROUP_ATTR) + + (strlen(config->groupattr) + strlen(dn) + 4); /* 4 for (=) + null */ /* disregard the group being removed */ @@ -1991,15 +2088,14 @@ int memberof_is_legit_member(Slapi_PBlock *pb, char *group_dn, continue; } - if(current_size > filter_size) + if (current_size > strlen(filter_str)) { - filter_size = 2 * current_size; - filter_str = slapi_ch_realloc( - filter_str, filter_size); + int filter_size = 2 * current_size; + filter_str = slapi_ch_realloc(filter_str, filter_size); } - sprintf(filter_str, "(%s=%s)", MEMBEROF_GROUP_ATTR, dn); - filter = memberof_string2filter(filter_str); + sprintf(filter_str, "(%s=%s)", config->groupattr, dn); + filter = slapi_str2filter(filter_str); if(!slapi_filter_test_simple(group_e, filter)) { @@ -2046,6 +2142,7 @@ typedef struct _task_data void memberof_fixup_task_thread(void *arg) { + MemberOfConfig configCopy = {0, 0, 0, 0}; Slapi_Task *task = (Slapi_Task *)arg; task_data *td = NULL; int rc = 0; @@ -2057,8 +2154,22 @@ void memberof_fixup_task_thread(void *arg) slapi_task_log_notice(task, "Memberof task starts (arg: %s) ...\n", td->filter_str); + /* We need to get the config lock first. Trying to get the + * config lock after we already hold the op lock can cause + * a deadlock. */ + memberof_rlock_config(); + /* copy config so it doesn't change out from under us */ + memberof_copy_config(&configCopy, memberof_get_config()); + memberof_unlock_config(); + + /* get the memberOf operation lock */ + memberof_lock(); + /* do real work */ - rc = memberof_fix_memberof(td->dn, td->filter_str); + rc = memberof_fix_memberof(&configCopy, td->dn, td->filter_str); + + /* release the memberOf operation lock */ + memberof_unlock(); slapi_task_log_notice(task, "Memberof task finished."); slapi_task_log_status(task, "Memberof task finished."); @@ -2164,7 +2275,7 @@ memberof_task_destructor(Slapi_Task *task) } } -int memberof_fix_memberof(char *dn, char *filter_str) +int memberof_fix_memberof(MemberOfConfig *config, char *dn, char *filter_str) { int rc = 0; Slapi_PBlock *search_pb = slapi_pblock_new(); @@ -2176,7 +2287,7 @@ int memberof_fix_memberof(char *dn, char *filter_str) 0); rc = slapi_search_internal_callback_pb(search_pb, - 0, + config, 0, memberof_fix_memberof_callback, 0); @@ -2196,13 +2307,14 @@ int memberof_fix_memberof_callback(Slapi_Entry *e, void *callback_data) { int rc = 0; char *dn = slapi_entry_get_dn(e); - memberof_add_groups data = {dn, dn}; + MemberOfConfig *config = (MemberOfConfig *)callback_data; + memberof_add_groups data = {config, dn, dn}; /* step 1 */ - slapi_entry_attr_delete(e, MEMBEROF_ATTR); + slapi_entry_attr_delete(e, config->memberof_attr); /* step 2 and 3 */ - rc = memberof_call_foreach_dn(0, dn, MEMBEROF_GROUP_ATTR, + rc = memberof_call_foreach_dn(0, dn, config->groupattr, memberof_add_groups_search_callback, &data); return rc; diff --git a/ldap/servers/plugins/memberof/memberof.h b/ldap/servers/plugins/memberof/memberof.h new file mode 100644 index 00000000..139be784 --- /dev/null +++ b/ldap/servers/plugins/memberof/memberof.h @@ -0,0 +1,95 @@ +/** BEGIN COPYRIGHT BLOCK + * This Program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; version 2 of the License. + * + * This Program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA. + * + * In addition, as a special exception, Red Hat, Inc. gives You the additional + * right to link the code of this Program with code not covered under the GNU + * General Public License ("Non-GPL Code") and to distribute linked combinations + * including the two, subject to the limitations in this paragraph. Non-GPL Code + * permitted under this exception must only link to the code of this Program + * through those well defined interfaces identified in the file named EXCEPTION + * found in the source code files (the "Approved Interfaces"). The files of + * Non-GPL Code may instantiate templates or use macros or inline functions from + * the Approved Interfaces without causing the resulting work to be covered by + * the GNU General Public License. Only Red Hat, Inc. may make changes or + * additions to the list of Approved Interfaces. You must obey the GNU General + * Public License in all respects for all of the Program code and other code used + * in conjunction with the Program except the Non-GPL Code covered by this + * exception. If you modify this file, you may extend this exception to your + * version of the file, but you are not obligated to do so. If you do not wish to + * provide this exception without modification, you must delete this exception + * statement from your version and license this file solely under the GPL without + * exception. + * + * + * Copyright (C) 2008 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +/* + * memberof.h - memberOf shared definitions + * + */ + +#ifndef _MEMBEROF_H_ +#define _MEMBEROF_H_ + +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <sys/types.h> +#include "portable.h" +#include "slapi-plugin.h" +#include <dirlite_strings.h> /* PLUGIN_MAGIC_VENDOR_STR */ +#include "dirver.h" +#include <nspr.h> + +/* Private API: to get SLAPI_DSE_RETURNTEXT_SIZE, DSE_FLAG_PREOP, and DSE_FLAG_POSTOP */ +#include "slapi-private.h" + +/* + * macros + */ +#define MEMBEROF_PLUGIN_SUBSYSTEM "memberof-plugin" /* used for logging */ +#define MEMBEROF_GROUP_ATTR "memberOfGroupAttr" +#define MEMBEROF_ATTR "memberOfAttr" + + +/* + * structs + */ +typedef struct memberofconfig { + char *groupattr; + char *memberof_attr; + Slapi_Filter *group_filter; + Slapi_Attr *group_slapiattr; +} MemberOfConfig; + + +/* + * functions + */ +int memberof_config(Slapi_Entry *config_e); +void memberof_copy_config(MemberOfConfig *dest, MemberOfConfig *src); +MemberOfConfig *memberof_get_config(); +void memberof_lock(); +void memberof_unlock(); +void memberof_rlock_config(); +void memberof_wlock_config(); +void memberof_unlock_config(); + + +#endif /* _MEMBEROF_H_ */ diff --git a/ldap/servers/plugins/memberof/memberof_config.c b/ldap/servers/plugins/memberof/memberof_config.c new file mode 100644 index 00000000..63f3f7a8 --- /dev/null +++ b/ldap/servers/plugins/memberof/memberof_config.c @@ -0,0 +1,341 @@ +/** BEGIN COPYRIGHT BLOCK + * This Program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; version 2 of the License. + * + * This Program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA. + * + * In addition, as a special exception, Red Hat, Inc. gives You the additional + * right to link the code of this Program with code not covered under the GNU + * General Public License ("Non-GPL Code") and to distribute linked combinations + * including the two, subject to the limitations in this paragraph. Non-GPL Code + * permitted under this exception must only link to the code of this Program + * through those well defined interfaces identified in the file named EXCEPTION + * found in the source code files (the "Approved Interfaces"). The files of + * Non-GPL Code may instantiate templates or use macros or inline functions from + * the Approved Interfaces without causing the resulting work to be covered by + * the GNU General Public License. Only Red Hat, Inc. may make changes or + * additions to the list of Approved Interfaces. You must obey the GNU General + * Public License in all respects for all of the Program code and other code used + * in conjunction with the Program except the Non-GPL Code covered by this + * exception. If you modify this file, you may extend this exception to your + * version of the file, but you are not obligated to do so. If you do not wish to + * provide this exception without modification, you must delete this exception + * statement from your version and license this file solely under the GPL without + * exception. + * + * + * Copyright (C) 2008 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +/* + * memberof_config.c - configuration-related code for memberOf plug-in + * + */ + +#include <plstr.h> + +#include "memberof.h" + +#define MEMBEROF_CONFIG_FILTER "(objectclass=*)" + +/* + * The configuration attributes are contained in the plugin entry e.g. + * cn=MemberOf Plugin,cn=plugins,cn=config + * + * Configuration is a two step process. The first pass is a validation step which + * occurs pre-op - check inputs and error out if bad. The second pass actually + * applies the changes to the run time config. + */ + + +/* + * function prototypes + */ +static int memberof_validate_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, + int *returncode, char *returntext, void *arg); +static int memberof_apply_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, + int *returncode, char *returntext, void *arg); +static int memberof_search (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, + int *returncode, char *returntext, void *arg) +{ + return SLAPI_DSE_CALLBACK_OK; +} + +/* + * static variables + */ +/* This is the main configuration which is updated from dse.ldif. The + * config will be copied when it is used by the plug-in to prevent it + * being changed out from under a running memberOf operation. */ +static MemberOfConfig theConfig; +static PRRWLock *memberof_config_lock = 0; +static int inited = 0; + + +static int dont_allow_that(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, + int *returncode, char *returntext, void *arg) +{ + *returncode = LDAP_UNWILLING_TO_PERFORM; + return SLAPI_DSE_CALLBACK_ERROR; +} + +/* + * memberof_config() + * + * Read configuration and create a configuration data structure. + * This is called after the server has configured itself so we can + * perform checks with regards to suffixes if it ever becomes + * necessary. + * Returns an LDAP error code (LDAP_SUCCESS if all goes well). + */ +int +memberof_config(Slapi_Entry *config_e) +{ + int returncode = LDAP_SUCCESS; + char returntext[SLAPI_DSE_RETURNTEXT_SIZE]; + + if ( inited ) { + slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, + "only one memberOf plugin instance can be used\n" ); + return( LDAP_PARAM_ERROR ); + } + + /* initialize the RW lock to protect the main config */ + memberof_config_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "memberof_config_lock"); + + /* initialize fields */ + if (SLAPI_DSE_CALLBACK_OK == memberof_validate_config(NULL, NULL, config_e, + &returncode, returntext, NULL)) { + memberof_apply_config(NULL, NULL, config_e, + &returncode, returntext, NULL); + } + + /* config DSE must be initialized before we get here */ + if (returncode == LDAP_SUCCESS) { + const char *config_dn = slapi_entry_get_dn_const(config_e); + slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, + config_dn, LDAP_SCOPE_BASE, MEMBEROF_CONFIG_FILTER, + memberof_validate_config,NULL); + slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_POSTOP, + config_dn, LDAP_SCOPE_BASE, MEMBEROF_CONFIG_FILTER, + memberof_apply_config,NULL); + slapi_config_register_callback(SLAPI_OPERATION_MODRDN, DSE_FLAG_PREOP, + config_dn, LDAP_SCOPE_BASE, MEMBEROF_CONFIG_FILTER, + dont_allow_that, NULL); + slapi_config_register_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, + config_dn, LDAP_SCOPE_BASE, MEMBEROF_CONFIG_FILTER, + dont_allow_that, NULL); + slapi_config_register_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, + config_dn, LDAP_SCOPE_BASE, MEMBEROF_CONFIG_FILTER, + memberof_search,NULL); + } + + inited = 1; + + if (returncode != LDAP_SUCCESS) { + slapi_log_error(SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, + "Error %d: %s\n", returncode, returntext); + } + + return returncode; +} + + +/* + * memberof_validate_config() + * + * Validate the pending changes in the e entry. + */ +static int +memberof_validate_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, + int *returncode, char *returntext, void *arg) +{ + Slapi_Attr *attr = NULL; + + *returncode = LDAP_UNWILLING_TO_PERFORM; /* be pessimistic */ + + /* Make sure both the group attr and the memberOf attr + * config atributes are supplied. We don't care about &attr + * here, but slapi_entry_attr_find() requires us to pass it. */ + if (!slapi_entry_attr_find(e, MEMBEROF_GROUP_ATTR, &attr) && + !slapi_entry_attr_find(e, MEMBEROF_ATTR, &attr)) + { + *returncode = LDAP_SUCCESS; + } else { + PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, + "The %s and %s configuration attributes must be provided", + MEMBEROF_GROUP_ATTR, MEMBEROF_ATTR); + } + + if (*returncode != LDAP_SUCCESS) + { + return SLAPI_DSE_CALLBACK_ERROR; + } + else + { + return SLAPI_DSE_CALLBACK_OK; + } +} + + +/* + * memberof_apply_config() + * + * Apply the pending changes in the e entry to our config struct. + * memberof_validate_config() must have already been called. + */ +static int +memberof_apply_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, + int *returncode, char *returntext, void *arg) +{ + char *groupattr = NULL; + char *memberof_attr = NULL; + char *filter_str = NULL; + + *returncode = LDAP_SUCCESS; + + groupattr = slapi_entry_attr_get_charptr(e, MEMBEROF_GROUP_ATTR); + memberof_attr = slapi_entry_attr_get_charptr(e, MEMBEROF_ATTR); + + /* We want to be sure we don't change the config in the middle of + * a memberOf operation, so we obtain an exclusive lock here */ + memberof_wlock_config(); + + if (!theConfig.groupattr || + (groupattr && PL_strcmp(theConfig.groupattr, groupattr))) { + slapi_ch_free_string(&theConfig.groupattr); + theConfig.groupattr = groupattr; + groupattr = NULL; /* config now owns memory */ + + /* We allocate a Slapi_Attr using the groupattr for + * convenience in our memberOf comparison functions */ + slapi_attr_free(&theConfig.group_slapiattr); + theConfig.group_slapiattr = slapi_attr_new(); + slapi_attr_init(theConfig.group_slapiattr, theConfig.groupattr); + + /* The filter is based off of the groupattr, so we + * update it here too. */ + slapi_filter_free(theConfig.group_filter, 1); + filter_str = slapi_ch_smprintf("(%s=*)", theConfig.groupattr); + theConfig.group_filter = slapi_str2filter(filter_str); + slapi_ch_free_string(&filter_str); + } + + if (!theConfig.memberof_attr || + (memberof_attr && PL_strcmp(theConfig.memberof_attr, memberof_attr))) { + slapi_ch_free_string(&theConfig.memberof_attr); + theConfig.memberof_attr = memberof_attr; + memberof_attr = NULL; /* config now owns memory */ + } + + /* release the lock */ + memberof_unlock_config(); + + slapi_ch_free_string(&groupattr); + slapi_ch_free_string(&memberof_attr); + + if (*returncode != LDAP_SUCCESS) + { + return SLAPI_DSE_CALLBACK_ERROR; + } + else + { + return SLAPI_DSE_CALLBACK_OK; + } +} + +/* + * memberof_copy_config() + * + * Makes a copy of the config in src. This function will free the + * elements of dest if they already exist. This should only be called + * if you hold the memberof config lock if src was obtained with + * memberof_get_config(). + */ +void +memberof_copy_config(MemberOfConfig *dest, MemberOfConfig *src) +{ + if (dest && src) + { + /* Check if the copy is already up to date */ + if (!dest->groupattr || (src->groupattr + && PL_strcmp(dest->groupattr, src->groupattr))) + { + slapi_ch_free_string(&dest->groupattr); + dest->groupattr = slapi_ch_strdup(src->groupattr); + slapi_filter_free(dest->group_filter, 1); + dest->group_filter = slapi_filter_dup(src->group_filter); + slapi_attr_free(&dest->group_slapiattr); + dest->group_slapiattr = slapi_attr_dup(src->group_slapiattr); + } + + if (!dest->memberof_attr || (src->memberof_attr + && PL_strcmp(dest->memberof_attr, src->memberof_attr))) + { + slapi_ch_free_string(&dest->memberof_attr); + dest->memberof_attr = slapi_ch_strdup(src->memberof_attr); + } + } +} + +/* + * memberof_get_config() + * + * Returns a pointer to the main config. You should call + * memberof_rlock_config() first so the main config doesn't + * get modified out from under you. + */ +MemberOfConfig * +memberof_get_config() +{ + return &theConfig; +} + +/* + * memberof_rlock_config() + * + * Gets a non-exclusive lock on the main config. This will + * prevent the config from being changed out from under you + * while you read it, but it will still allow other threads + * to read the config at the same time. + */ +void +memberof_rlock_config() +{ + PR_RWLock_Rlock(memberof_config_lock); +} + +/* + * memberof_wlock_config() + * + * Gets an exclusive lock on the main config. This should + * be called if you need to write to the main config. + */ +void +memberof_wlock_config() +{ + PR_RWLock_Wlock(memberof_config_lock); +} + +/* + * memberof_unlock_config() + * + * Unlocks the main config. + */ +void +memberof_unlock_config() +{ + PR_RWLock_Unlock(memberof_config_lock); +} |