diff options
author | Nathan Kinder <nkinder@redhat.com> | 2008-07-17 14:46:12 -0700 |
---|---|---|
committer | Rob Crittenden <rcrit@ipa.greyoak.com> | 2008-07-18 13:11:19 -0400 |
commit | 2301f606528e731c831d02a58a0d4914dbb662ba (patch) | |
tree | 22a51ca0687f0a9f133c02a56a26470e75de284a | |
parent | 67f62709f83997945b69919cd744ce936ddf5596 (diff) | |
download | freeipa-2301f606528e731c831d02a58a0d4914dbb662ba.tar.gz freeipa-2301f606528e731c831d02a58a0d4914dbb662ba.tar.xz freeipa-2301f606528e731c831d02a58a0d4914dbb662ba.zip |
Re-base memberOf plug-in off of current FDS memberOf plug-in. Resolves: 452537, 453011, 443241, 439628
4 files changed, 1189 insertions, 643 deletions
diff --git a/ipa-server/ipa-slapi-plugins/ipa-memberof/Makefile.am b/ipa-server/ipa-slapi-plugins/ipa-memberof/Makefile.am index cf084aae0..d0ac7f935 100644 --- a/ipa-server/ipa-slapi-plugins/ipa-memberof/Makefile.am +++ b/ipa-server/ipa-slapi-plugins/ipa-memberof/Makefile.am @@ -20,6 +20,7 @@ plugin_LTLIBRARIES = \ libipa_memberof_plugin_la_SOURCES = \ ipa-memberof.c \ + ipa-memberof_config.c \ $(NULL) libipa_memberof_plugin_la_LDFLAGS = -avoid-version diff --git a/ipa-server/ipa-slapi-plugins/ipa-memberof/ipa-memberof.c b/ipa-server/ipa-slapi-plugins/ipa-memberof/ipa-memberof.c index a304e093a..4898baa3b 100644 --- a/ipa-server/ipa-slapi-plugins/ipa-memberof/ipa-memberof.c +++ b/ipa-server/ipa-slapi-plugins/ipa-memberof/ipa-memberof.c @@ -49,10 +49,10 @@ * * To start the memberof task add an entry like: * - * dn: cn=memberof task 2, cn=memberof task, cn=tasks, cn=config + * dn: cn=mytask, cn=memberof task, cn=tasks, cn=config * objectClass: top * objectClass: extensibleObject - * cn: sample task + * cn: mytask * basedn: dc=example, dc=com * filter: (uid=test4) * @@ -70,48 +70,39 @@ #include "string.h" #include "nspr.h" -#define IPA_GROUP_ATTR "member" -#define IPA_MEMBEROF_ATTR "memberof" -#define IPA_GROUP_ATTR_IS_DN 1 -#define IPA_GROUP_ATTR_TYPE "uid" -#define IPA_GROUP_FILTER "(" IPA_GROUP_ATTR "=*)" +#include "ipa-memberof.h" -#define IPAMO_PLUGIN_SUBSYSTEM "ipa-memberof-plugin" /* used for logging */ static Slapi_PluginDesc pdesc = { "ipamo", "FreeIPA project", "FreeIPA/1.0", - "IPA memberof plugin" }; + "IPA memberof plugin" }; static void* _PluginID = NULL; -static Slapi_Filter *ipa_group_filter = NULL; -static Slapi_Mutex *ipamo_operation_lock = 0; +static Slapi_Mutex *memberof_operation_lock = 0; +MemberOfConfig *qsortConfig = 0; -typedef struct _ipamostringll +typedef struct _memberofstringll { - char *dn; + const char *dn; void *next; -} ipamostringll; - +} memberofstringll; +typedef struct _memberof_get_groups_data +{ + MemberOfConfig *config; + Slapi_Value *memberdn_val; + Slapi_ValueSet **groupvals; +} memberof_get_groups_data; /****** secrets *********/ #ifndef SLAPI_TASK_PUBLIC /*from FDS slap.h - * until we get a proper api for access - */ + * * until we get a proper api for access + * */ #define TASK_RUNNING_AS_TASK 0x0 -/*from FDS slapi-private.h - * until we get a proper api for access - */ - - -#define SLAPI_DSE_CALLBACK_OK (1) -#define SLAPI_DSE_CALLBACK_ERROR (-1) -#define SLAPI_DSE_CALLBACK_DO_NOT_APPLY (0) - /****************************************************************************** - * Online tasks interface (to support import, export, etc) - * After some cleanup, we could consider making these public. - */ + * * Online tasks interface (to support import, export, etc) + * * After some cleanup, we could consider making these public. + * */ struct _slapi_task { struct _slapi_task *next; char *task_dn; @@ -128,7 +119,7 @@ struct _slapi_task { void *task_private; /* for use by backends */ TaskCallbackFn cancel; /* task has been cancelled by user */ TaskCallbackFn destructor; /* task entry is being destroyed */ - int task_refcount; + int task_refcount; }; static void slapi_task_set_data(Slapi_Task *task, void *data) @@ -139,8 +130,8 @@ static void slapi_task_set_data(Slapi_Task *task, void *data) } /* - * Retrieve some opaque task specific data from the task. - */ + * * Retrieve some opaque task specific data from the task. + * */ static void * slapi_task_get_data(Slapi_Task *task) { if (task) { @@ -185,72 +176,80 @@ static void slapi_task_set_destructor_fn(Slapi_Task *task, TaskCallbackFn func) #endif /* !SLAPI_TASK_PUBLIC */ /****** secrets ********/ - /*** function prototypes ***/ /* exported functions */ int ipamo_postop_init(Slapi_PBlock *pb ); /* plugin callbacks */ -static int ipamo_postop_del(Slapi_PBlock *pb ); -static int ipamo_postop_modrdn(Slapi_PBlock *pb ); -static int ipamo_postop_modify(Slapi_PBlock *pb ); -static int ipamo_postop_add(Slapi_PBlock *pb ); -static int ipamo_postop_start(Slapi_PBlock *pb); -static int ipamo_postop_close(Slapi_PBlock *pb); +static int memberof_postop_del(Slapi_PBlock *pb ); +static int memberof_postop_modrdn(Slapi_PBlock *pb ); +static int memberof_postop_modify(Slapi_PBlock *pb ); +static int memberof_postop_add(Slapi_PBlock *pb ); +static int memberof_postop_start(Slapi_PBlock *pb); +static int memberof_postop_close(Slapi_PBlock *pb); /* supporting cast */ -static int ipamo_oktodo(Slapi_PBlock *pb); -static char *ipamo_getdn(Slapi_PBlock *pb); -static int ipamo_modop_one(Slapi_PBlock *pb, int mod_op, char *op_this, char *op_to); -static int ipamo_modop_one_r(Slapi_PBlock *pb, int mod_op, char *group_dn, - char *op_this, char *op_to, ipamostringll *stack); -static int ipamo_add_one(Slapi_PBlock *pb, char *addthis, char *addto); -static int ipamo_del_one(Slapi_PBlock *pb, char *delthis, char *delfrom); -static int ipamo_mod_smod_list(Slapi_PBlock *pb, int mod, char *groupdn, - Slapi_Mod *smod); -static int ipamo_add_smod_list(Slapi_PBlock *pb, char *groupdn, Slapi_Mod *smod); -static int ipamo_del_smod_list(Slapi_PBlock *pb, char *groupdn, Slapi_Mod *smod); -static int ipamo_mod_attr_list(Slapi_PBlock *pb, int mod, char *groupdn, - Slapi_Attr *attr); -static int ipamo_mod_attr_list_r(Slapi_PBlock *pb, int mod, char *group_dn, - char *op_this, Slapi_Attr *attr, ipamostringll *stack); -static int ipamo_add_attr_list(Slapi_PBlock *pb, char *groupdn, Slapi_Attr *attr); -static int ipamo_del_attr_list(Slapi_PBlock *pb, char *groupdn, Slapi_Attr *attr); -static int ipamo_moddn_attr_list(Slapi_PBlock *pb, char *pre_dn, char *post_dn, - Slapi_Attr *attr); -static int ipamo_replace_list(Slapi_PBlock *pb, char *group_dn); -static void ipamo_set_plugin_id(void * plugin_id); -static void *ipamo_get_plugin_id(); -static int ipamo_compare(const void *a, const void *b); -static void ipamo_load_array(Slapi_Value **array, Slapi_Attr *attr); -static Slapi_Filter *ipamo_string2filter(char *strfilter); -static int ipamo_is_legit_member(Slapi_PBlock *pb, char *group_dn, - char *op_this, char *op_to, ipamostringll *stack); -static int ipamo_del_dn_from_groups(Slapi_PBlock *pb, char *dn); -static int ipamo_call_foreach_dn(Slapi_PBlock *pb, char *dn, +static int memberof_oktodo(Slapi_PBlock *pb); +static char *memberof_getdn(Slapi_PBlock *pb); +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(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 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 ipamo_is_group_member(Slapi_Value *groupdn, Slapi_Value *memberdn); -static int ipamo_test_membership(Slapi_PBlock *pb, char *dn); -static int ipamo_test_membership_callback(Slapi_Entry *e, void *callback_data); -static int ipamo_del_dn_type_callback(Slapi_Entry *e, void *callback_data); -static int ipamo_replace_dn_type_callback(Slapi_Entry *e, void *callback_data); -static int ipamo_replace_dn_from_groups(Slapi_PBlock *pb, char *pre_dn, char *post_dn); -static int ipamo_modop_one_replace_r(Slapi_PBlock *pb, int mod_op, char *group_dn, - char *op_this, char *replace_with, char *op_to, ipamostringll *stack); -static void ipamo_lock(); -static void ipamo_unlock(); -static int ipamo_add_groups_search_callback(Slapi_Entry *e, void *callback_data); -static int ipamo_add_membership(Slapi_PBlock *pb, char *op_this, char *op_to); -static int ipamo_task_add(Slapi_PBlock *pb, Slapi_Entry *e, +static int memberof_is_direct_member(MemberOfConfig *config, Slapi_Value *groupdn, + Slapi_Value *memberdn); +static Slapi_ValueSet *memberof_get_groups(MemberOfConfig *config, char *memberdn); +static int memberof_get_groups_r(MemberOfConfig *config, char *memberdn, + memberof_get_groups_data *data); +static int memberof_get_groups_callback(Slapi_Entry *e, void *callback_data); +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, 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_task_add(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg); -static void ipamo_task_destructor(Slapi_Task *task); +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 ipamo_memberof_fixup_task_thread(void *arg); -static int ipamo_fix_memberof(char *dn, char *filter_str); -static int ipamo_fix_memberof_callback(Slapi_Entry *e, void *callback_data); +static void memberof_fixup_task_thread(void *arg); +static int memberof_fix_memberof(MemberOfConfig *config, char *dn, char *filter_str); +static int memberof_fix_memberof_callback(Slapi_Entry *e, void *callback_data); /*** implementation ***/ @@ -268,70 +267,81 @@ int ipamo_postop_init(Slapi_PBlock *pb) { int ret = 0; - char *ipamo_plugin_identity = 0; + char *memberof_plugin_identity = 0; - slapi_log_error( SLAPI_LOG_TRACE, IPAMO_PLUGIN_SUBSYSTEM, + slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, "--> ipamo_postop_init\n" ); /* * Get plugin identity and stored it for later use * Used for internal operations */ - slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &ipamo_plugin_identity); - PR_ASSERT (ipamo_plugin_identity); - ipamo_set_plugin_id(ipamo_plugin_identity); + slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &memberof_plugin_identity); + PR_ASSERT (memberof_plugin_identity); + memberof_set_plugin_id(memberof_plugin_identity); if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 || slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&pdesc ) != 0 || slapi_pblock_set( pb, SLAPI_PLUGIN_POST_DELETE_FN, - (void *) ipamo_postop_del ) != 0 || + (void *) memberof_postop_del ) != 0 || slapi_pblock_set( pb, SLAPI_PLUGIN_POST_MODRDN_FN, - (void *) ipamo_postop_modrdn ) != 0 || + (void *) memberof_postop_modrdn ) != 0 || slapi_pblock_set( pb, SLAPI_PLUGIN_POST_MODIFY_FN, - (void *) ipamo_postop_modify ) != 0 || + (void *) memberof_postop_modify ) != 0 || slapi_pblock_set( pb, SLAPI_PLUGIN_POST_ADD_FN, - (void *) ipamo_postop_add ) != 0 || + (void *) memberof_postop_add ) != 0 || slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN, - (void *) ipamo_postop_start ) != 0 || + (void *) memberof_postop_start ) != 0 || slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN, - (void *) ipamo_postop_close ) != 0) + (void *) memberof_postop_close ) != 0) { - slapi_log_error( SLAPI_LOG_FATAL, IPAMO_PLUGIN_SUBSYSTEM, + slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, "ipamo_postop_init failed\n" ); ret = -1; } - slapi_log_error( SLAPI_LOG_TRACE, IPAMO_PLUGIN_SUBSYSTEM, + slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, "<-- ipamo_postop_init\n" ); return ret; } /* - * ipamo_postop_start() + * memberof_postop_start() * * Do plugin start up stuff * */ -int ipamo_postop_start(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, IPAMO_PLUGIN_SUBSYSTEM, - "--> ipamo_postop_start\n" ); - - ipa_group_filter = ipamo_string2filter(IPA_GROUP_FILTER); + slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, + "--> memberof_postop_start\n" ); - ipamo_operation_lock = slapi_new_mutex(); - - if(0 == ipa_group_filter || 0 == ipamo_operation_lock) + memberof_operation_lock = slapi_new_mutex(); + if(0 == memberof_operation_lock) { rc = -1; goto bail; } - rc = slapi_task_register_handler("memberof task", ipamo_task_add); + 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) { goto bail; @@ -348,32 +358,32 @@ int ipamo_postop_start(Slapi_PBlock *pb) */ bail: - slapi_log_error( SLAPI_LOG_TRACE, IPAMO_PLUGIN_SUBSYSTEM, - "<-- ipamo_postop_start\n" ); + slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, + "<-- memberof_postop_start\n" ); return rc; } /* - * ipamo_postop_close() + * memberof_postop_close() * * Do plugin shut down stuff * */ -int ipamo_postop_close(Slapi_PBlock *pb) +int memberof_postop_close(Slapi_PBlock *pb) { - slapi_log_error( SLAPI_LOG_TRACE, IPAMO_PLUGIN_SUBSYSTEM, - "--> ipamo_postop_close\n" ); + slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, + "--> memberof_postop_close\n" ); - slapi_log_error( SLAPI_LOG_TRACE, IPAMO_PLUGIN_SUBSYSTEM, - "<-- ipamo_postop_close\n" ); + slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, + "<-- memberof_postop_close\n" ); return 0; } /* - * ipamo_postop_del() + * memberof_postop_del() * * All entries with a memberOf attribute that contains the group DN get retrieved * and have the their memberOf attribute regenerated (it is far too complex and @@ -383,61 +393,73 @@ int ipamo_postop_close(Slapi_PBlock *pb) * chains that must be created and traversed in order to decide if an entry should * really have those groups removed too) */ -int ipamo_postop_del(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, IPAMO_PLUGIN_SUBSYSTEM, - "--> ipamo_postop_del\n" ); + slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, + "--> memberof_postop_del\n" ); - if(ipamo_oktodo(pb) && (dn = ipamo_getdn(pb))) + if(memberof_oktodo(pb) && (dn = memberof_getdn(pb))) { struct slapi_entry *e = NULL; slapi_pblock_get( pb, SLAPI_ENTRY_PRE_OP, &e ); - - ipamo_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 */ - ipamo_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, ipa_group_filter)) + if(e && !slapi_filter_test_simple(e, configCopy.group_filter)) { Slapi_Attr *attr = 0; - if(0 == slapi_entry_attr_find(e, IPA_GROUP_ATTR, &attr)) + if(0 == slapi_entry_attr_find(e, configCopy.groupattr, &attr)) { - ipamo_del_attr_list(pb, dn, attr); + memberof_del_attr_list(pb, &configCopy, dn, attr); } } - ipamo_unlock(); + memberof_unlock(); + + memberof_free_config(&configCopy); } - slapi_log_error( SLAPI_LOG_TRACE, IPAMO_PLUGIN_SUBSYSTEM, - "<-- ipamo_postop_del\n" ); + slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, + "<-- memberof_postop_del\n" ); return ret; } -typedef struct _del_dn_data +typedef struct _memberof_del_dn_data { char *dn; char *type; -} del_dn_data; +} memberof_del_dn_data; -int ipamo_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, IPA_GROUP_ATTR}; + memberof_del_dn_data data = {dn, config->groupattr}; - return ipamo_call_foreach_dn(pb, dn, - IPA_GROUP_ATTR, ipamo_del_dn_type_callback, &data); + return memberof_call_foreach_dn(pb, dn, + config->groupattr, memberof_del_dn_type_callback, &data); } -int ipamo_del_dn_type_callback(Slapi_Entry *e, void *callback_data) +int memberof_del_dn_type_callback(Slapi_Entry *e, void *callback_data) { int rc = 0; LDAPMod mod; @@ -450,17 +472,17 @@ int ipamo_del_dn_type_callback(Slapi_Entry *e, void *callback_data) mods[0] = &mod; mods[1] = 0; - val[0] = ((del_dn_data *)callback_data)->dn; + val[0] = ((memberof_del_dn_data *)callback_data)->dn; val[1] = 0; mod.mod_op = LDAP_MOD_DELETE; - mod.mod_type = ((del_dn_data *)callback_data)->type; + mod.mod_type = ((memberof_del_dn_data *)callback_data)->type; mod.mod_values = val; slapi_modify_internal_set_pb( mod_pb, slapi_entry_get_dn(e), mods, 0, 0, - ipamo_get_plugin_id(), 0); + memberof_get_plugin_id(), 0); slapi_modify_internal_pb(mod_pb); @@ -473,7 +495,13 @@ int ipamo_del_dn_type_callback(Slapi_Entry *e, void *callback_data) return rc; } -int ipamo_call_foreach_dn(Slapi_PBlock *pb, char *dn, +/* + * Does a callback search of "type=dn" under the db suffix that "dn" is in. + * If "dn" is a user, you'd want "type" to be "member". If "dn" is a group, + * you could want type to be either "member" or "memberOf" depending on the + * case. + */ +int memberof_call_foreach_dn(Slapi_PBlock *pb, char *dn, char *type, plugin_search_entry_callback callback, void *callback_data) { int rc = 0; @@ -494,15 +522,9 @@ int ipamo_call_foreach_dn(Slapi_PBlock *pb, char *dn, base_sdn = (Slapi_DN*)slapi_be_getsuffix(be,0); } - 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) @@ -510,7 +532,7 @@ int ipamo_call_foreach_dn(Slapi_PBlock *pb, char *dn, slapi_search_internal_set_pb(search_pb, slapi_sdn_get_dn(base_sdn), LDAP_SCOPE_SUBTREE, filter_str, 0, 0, 0, 0, - ipamo_get_plugin_id(), + memberof_get_plugin_id(), 0); slapi_search_internal_callback_pb(search_pb, @@ -526,24 +548,27 @@ int ipamo_call_foreach_dn(Slapi_PBlock *pb, char *dn, } /* - * ipamo_postop_modrdn() + * memberof_postop_modrdn() * * All entries with a memberOf attribute that contains the old group DN get retrieved * and have the old group DN deleted and the new group DN added to their memberOf attribute */ -int ipamo_postop_modrdn(Slapi_PBlock *pb) +int memberof_postop_modrdn(Slapi_PBlock *pb) { int ret = 0; - slapi_log_error( SLAPI_LOG_TRACE, IPAMO_PLUGIN_SUBSYSTEM, - "--> ipamo_postop_modrdn\n" ); + slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, + "--> memberof_postop_modrdn\n" ); - if(ipamo_oktodo(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 ); @@ -555,30 +580,44 @@ int ipamo_postop_modrdn(Slapi_PBlock *pb) } /* is the entry of interest? */ - if(pre_dn && post_dn && - !slapi_filter_test_simple(post_e, ipa_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; - ipamo_lock(); + memberof_lock(); - if(0 == slapi_entry_attr_find(post_e, IPA_GROUP_ATTR, &attr)) + /* get a list of member attributes present in the group + * entry that is being renamed. */ + if(0 == slapi_entry_attr_find(post_e, configCopy.groupattr, &attr)) { - ipamo_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. */ - ipamo_replace_dn_from_groups(pb, pre_dn, post_dn); + memberof_replace_dn_from_groups(pb, &configCopy, pre_dn, post_dn); - ipamo_unlock(); + memberof_unlock(); + + memberof_free_config(&configCopy); } } - slapi_log_error( SLAPI_LOG_TRACE, IPAMO_PLUGIN_SUBSYSTEM, - "<-- ipamo_postop_modrdn\n" ); + slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, + "<-- memberof_postop_modrdn\n" ); return ret; } @@ -589,16 +628,17 @@ typedef struct _replace_dn_data char *type; } replace_dn_data; -int ipamo_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, IPA_GROUP_ATTR}; + replace_dn_data data = {pre_dn, post_dn, config->groupattr}; - return ipamo_call_foreach_dn(pb, pre_dn, IPA_GROUP_ATTR, - ipamo_replace_dn_type_callback, &data); + return memberof_call_foreach_dn(pb, pre_dn, config->groupattr, + memberof_replace_dn_type_callback, &data); } -int ipamo_replace_dn_type_callback(Slapi_Entry *e, void *callback_data) +int memberof_replace_dn_type_callback(Slapi_Entry *e, void *callback_data) { int rc = 0; LDAPMod delmod; @@ -631,7 +671,7 @@ int ipamo_replace_dn_type_callback(Slapi_Entry *e, void *callback_data) slapi_modify_internal_set_pb( mod_pb, slapi_entry_get_dn(e), mods, 0, 0, - ipamo_get_plugin_id(), 0); + memberof_get_plugin_id(), 0); slapi_modify_internal_pb(mod_pb); @@ -645,7 +685,7 @@ int ipamo_replace_dn_type_callback(Slapi_Entry *e, void *callback_data) } /* - * ipamo_postop_modify() + * memberof_postop_modify() * * Added members are retrieved and have the group DN added to their memberOf attribute * Deleted members are retrieved and have the group DN deleted from their memberOf attribute @@ -659,7 +699,7 @@ int ipamo_replace_dn_type_callback(Slapi_Entry *e, void *callback_data) * current restrictions i.e. originally adding members in sorted order would allow * us to sort one list only (the new one) but that is under server control, not this plugin */ -int ipamo_postop_modify(Slapi_PBlock *pb) +int memberof_postop_modify(Slapi_PBlock *pb) { int ret = 0; char *dn = 0; @@ -668,12 +708,16 @@ int ipamo_postop_modify(Slapi_PBlock *pb) LDAPMod **mods; Slapi_Mod *next_mod = 0; - slapi_log_error( SLAPI_LOG_TRACE, IPAMO_PLUGIN_SUBSYSTEM, - "--> ipamo_postop_modify\n" ); + slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, + "--> memberof_postop_modify\n" ); - if(ipamo_oktodo(pb) && - (dn = ipamo_getdn(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(); @@ -683,14 +727,40 @@ int ipamo_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,IPA_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); - ipamo_lock(); + memberof_lock(); /* the modify op decides the function */ switch(op & ~LDAP_MOD_BVALUES) @@ -698,7 +768,7 @@ int ipamo_postop_modify(Slapi_PBlock *pb) case LDAP_MOD_ADD: { /* add group DN to targets */ - ipamo_add_smod_list(pb, dn, smod); + memberof_add_smod_list(pb, &configCopy, dn, smod); break; } @@ -706,16 +776,16 @@ int ipamo_postop_modify(Slapi_PBlock *pb) { /* If there are no values in the smod, we should * just do a replace instead. The user is just - * trying to delete all members from this this + * trying to delete all members from this group * entry, which the replace code deals with. */ if (slapi_mod_get_num_values(smod) == 0) { - ipamo_replace_list(pb, dn); + memberof_replace_list(pb, &configCopy, dn); } else { /* remove group DN from target values in smod*/ - ipamo_del_smod_list(pb, dn, smod); + memberof_del_smod_list(pb, &configCopy, dn, smod); } break; } @@ -723,7 +793,7 @@ int ipamo_postop_modify(Slapi_PBlock *pb) case LDAP_MOD_REPLACE: { /* replace current values */ - ipamo_replace_list(pb, dn); + memberof_replace_list(pb, &configCopy, dn); break; } @@ -731,74 +801,95 @@ int ipamo_postop_modify(Slapi_PBlock *pb) { slapi_log_error( SLAPI_LOG_PLUGIN, - IPAMO_PLUGIN_SUBSYSTEM, - "ipamo_postop_modify: unknown mod type\n" ); + MEMBEROF_PLUGIN_SUBSYSTEM, + "memberof_postop_modify: unknown mod type\n" ); break; } } - ipamo_unlock(); + memberof_unlock(); } slapi_mod_done(next_mod); smod = slapi_mods_get_next_smod(smods, next_mod); } + if (config_copied) + { + memberof_free_config(&configCopy); + } + slapi_mod_free(&next_mod); slapi_mods_free(&smods); } - slapi_log_error( SLAPI_LOG_TRACE, IPAMO_PLUGIN_SUBSYSTEM, - "<-- ipamo_postop_modify\n" ); + slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, + "<-- memberof_postop_modify\n" ); return ret; } /* - * ipamo_postop_add() + * memberof_postop_add() * * All members in the membership attribute of the new entry get retrieved * and have the group DN added to their memberOf attribute */ -int ipamo_postop_add(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, IPAMO_PLUGIN_SUBSYSTEM, - "--> ipamo_postop_add\n" ); + slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, + "--> memberof_postop_add\n" ); - if(ipamo_oktodo(pb) && (dn = ipamo_getdn(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, ipa_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; - ipamo_lock(); + memberof_lock(); - if(0 == slapi_entry_attr_find(e, IPA_GROUP_ATTR, &attr)) + if(0 == slapi_entry_attr_find(e, configCopy.groupattr, &attr)) { - ipamo_add_attr_list(pb, dn, attr); + memberof_add_attr_list(pb, &configCopy, dn, attr); } - ipamo_unlock(); + memberof_unlock(); + + memberof_free_config(&configCopy); } } - slapi_log_error( SLAPI_LOG_TRACE, IPAMO_PLUGIN_SUBSYSTEM, - "<-- ipamo_postop_add\n" ); + slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, + "<-- memberof_postop_add\n" ); return ret; } /*** Support functions ***/ /* - * ipamo_oktodo() + * memberof_oktodo() * * Check that the op succeeded * Note: we also respond to replicated ops so we don't test for that @@ -807,18 +898,18 @@ int ipamo_postop_add(Slapi_PBlock *pb) * not the network system state * */ -int ipamo_oktodo(Slapi_PBlock *pb) +int memberof_oktodo(Slapi_PBlock *pb) { int ret = 1; int oprc = 0; - slapi_log_error( SLAPI_LOG_TRACE, IPAMO_PLUGIN_SUBSYSTEM, - "--> ipamo_postop_oktodo\n" ); + slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, + "--> memberof_postop_oktodo\n" ); if(slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &oprc) != 0) { - slapi_log_error( SLAPI_LOG_FATAL, IPAMO_PLUGIN_SUBSYSTEM, - "ipamo_postop_oktodo: could not get parameters\n" ); + slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, + "memberof_postop_oktodo: could not get parameters\n" ); ret = -1; } @@ -829,19 +920,19 @@ int ipamo_oktodo(Slapi_PBlock *pb) ret = 0; } - slapi_log_error( SLAPI_LOG_TRACE, IPAMO_PLUGIN_SUBSYSTEM, - "<-- ipamo_postop_oktodo\n" ); + slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, + "<-- memberof_postop_oktodo\n" ); return ret; } /* - * ipamo_getdn() + * memberof_getdn() * * Get dn of target entry * */ -char *ipamo_getdn(Slapi_PBlock *pb) +char *memberof_getdn(Slapi_PBlock *pb) { char *dn = 0; @@ -851,7 +942,7 @@ char *ipamo_getdn(Slapi_PBlock *pb) } /* - * ipamo_modop_one() + * memberof_modop_one() * * Perform op on memberof attribute of op_to using op_this as the value * However, if op_to happens to be a group, we must arrange for the group @@ -860,29 +951,31 @@ char *ipamo_getdn(Slapi_PBlock *pb) * * Also, we must not delete entries that are a member of the group */ -int ipamo_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 ipamo_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); } -/* ipamo_modop_one_r() +/* memberof_modop_one_r() * * recursive function to perform above (most things don't need the replace arg) */ -int ipamo_modop_one_r(Slapi_PBlock *pb, int mod_op, char *group_dn, - char *op_this, char *op_to, ipamostringll *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 ipamo_modop_one_replace_r( - pb, mod_op, group_dn, op_this, 0, op_to, stack); + return memberof_modop_one_replace_r( + pb, config, mod_op, group_dn, op_this, 0, op_to, stack); } -/* ipamo_modop_one_replace_r() +/* memberof_modop_one_replace_r() * * recursive function to perform above (with added replace arg) */ -int ipamo_modop_one_replace_r(Slapi_PBlock *pb, int mod_op, char *group_dn, - char *op_this, char *replace_with, char *op_to, ipamostringll *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; @@ -891,30 +984,84 @@ int ipamo_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] = {IPA_GROUP_ATTR,0}; + char *attrlist[2] = {config->groupattr,0}; Slapi_DN *op_to_sdn = 0; Slapi_Entry *e = 0; - ipamostringll *ll = 0; + memberofstringll *ll = 0; char *op_str = 0; + 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, - &e, ipamo_get_plugin_id()); - slapi_sdn_free(&op_to_sdn); + &e, memberof_get_plugin_id()); if(!e) { + /* In the case of a delete, we need to worry about the + * missing entry being a nested group. There's a small + * window where another thread may have deleted a nested + * group that our group_dn entry refers to. This has the + * potential of us missing some indirect member entries + * that need to be updated. */ if(LDAP_MOD_DELETE == mod_op) { - /* in the case of delete we must guard against - * having groups in a nested chain having been - * deleted during the window of opportunity - * and we must fall back to testing all members - * of the (potentially deleted group) for valid - * membership given the delete operation that - * triggered this operation - */ - ipamo_test_membership(pb, group_dn); + Slapi_PBlock *search_pb = slapi_pblock_new(); + Slapi_DN *base_sdn = 0; + Slapi_Backend *be = 0; + char *filter_str = 0; + int n_entries = 0; + + /* We can't tell for sure if the op_to entry is a + * user or a group since the entry doesn't exist + * anymore. We can safely ignore the missing entry + * if no other entries have a memberOf attribute that + * points to the missing entry. */ + be = slapi_be_select(op_to_sdn); + if(be) + { + base_sdn = (Slapi_DN*)slapi_be_getsuffix(be,0); + } + + if(base_sdn) + { + filter_str = slapi_ch_smprintf("(%s=%s)", + config->memberof_attr, op_to); + } + + if(filter_str) + { + slapi_search_internal_set_pb(search_pb, slapi_sdn_get_dn(base_sdn), + LDAP_SCOPE_SUBTREE, filter_str, 0, 0, 0, 0, + memberof_get_plugin_id(), 0); + + if (slapi_search_internal_pb(search_pb)) + { + /* get result and log an error */ + int res = 0; + slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &res); + slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, + "memberof_modop_one_replace_r: error searching for members: " + "%d", res); + } else { + slapi_pblock_get(search_pb, SLAPI_NENTRIES, &n_entries); + + if(n_entries > 0) + { + /* We want to fixup the membership for the + * entries that referred to the missing group + * entry. This will fix the references to + * the missing group as well as the group + * represented by op_this. */ + memberof_test_membership(pb, config, op_to); + } + } + + slapi_free_search_results_internal(search_pb); + slapi_ch_free_string(&filter_str); + } + + slapi_pblock_destroy(search_pb); } goto bail; @@ -928,16 +1075,23 @@ int ipamo_modop_one_replace_r(Slapi_PBlock *pb, int mod_op, char *group_dn, { op_str = "ADD"; } + else if(LDAP_MOD_REPLACE == mod_op) + { + op_str = "REPLACE"; + } + else + { + op_str = "UNKNOWN"; + } - slapi_log_error( SLAPI_LOG_PLUGIN, IPAMO_PLUGIN_SUBSYSTEM, - "ipamo_modop_one_r: %s %s in %s\n" + slapi_log_error( SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, + "memberof_modop_one_replace_r: %s %s in %s\n" ,op_str, op_this, op_to); - if(!slapi_filter_test_simple(e, ipa_group_filter)) + if(!slapi_filter_test_simple(e, config->group_filter)) { /* group */ Slapi_Value *ll_dn_val = 0; - Slapi_Value *to_dn_val = slapi_value_new_string(op_to); Slapi_Attr *members = 0; ll = stack; @@ -947,16 +1101,15 @@ int ipamo_modop_one_replace_r(Slapi_PBlock *pb, int mod_op, char *group_dn, { ll_dn_val = slapi_value_new_string(ll->dn); - if(0 == ipamo_compare(&ll_dn_val, &to_dn_val)) + if(0 == memberof_compare(config, &ll_dn_val, &to_dn_val)) { - slapi_value_free(&to_dn_val); slapi_value_free(&ll_dn_val); /* someone set up infinitely recursive groups - bail out */ - slapi_log_error( SLAPI_LOG_FATAL, - IPAMO_PLUGIN_SUBSYSTEM, - "ipamo_modop_one_r: group recursion" + slapi_log_error( SLAPI_LOG_PLUGIN, + MEMBEROF_PLUGIN_SUBSYSTEM, + "memberof_modop_one_replace_r: group recursion" " detected in %s\n" ,op_to); goto bail; @@ -966,22 +1119,20 @@ int ipamo_modop_one_replace_r(Slapi_PBlock *pb, int mod_op, char *group_dn, ll = ll->next; } - slapi_value_free(&to_dn_val); - /* do op on group */ slapi_log_error( SLAPI_LOG_PLUGIN, - IPAMO_PLUGIN_SUBSYSTEM, - "ipamo_modop_one_r: descending into group %s\n", + MEMBEROF_PLUGIN_SUBSYSTEM, + "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 = (ipamostringll*)slapi_ch_malloc(sizeof(ipamostringll)); + ll = (memberofstringll*)slapi_ch_malloc(sizeof(memberofstringll)); ll->dn = op_to; ll->next = stack; - slapi_entry_attr_find( e, IPA_GROUP_ATTR, &members ); + slapi_entry_attr_find( e, config->groupattr, &members ); if(members) { - ipamo_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); } { @@ -997,137 +1148,109 @@ int ipamo_modop_one_replace_r(Slapi_PBlock *pb, int mod_op, char *group_dn, } /* continue with operation */ { - Slapi_Value *to_dn_val = slapi_value_new_string(op_to); - Slapi_Value *this_dn_val = slapi_value_new_string(op_this); - /* We want to avoid listing a group as a memberOf itself * in case someone set up a circular grouping. */ - if (0 == ipamo_compare(&this_dn_val, &to_dn_val)) + if (0 == memberof_compare(config, &this_dn_val, &to_dn_val)) { slapi_log_error( SLAPI_LOG_PLUGIN, - IPAMO_PLUGIN_SUBSYSTEM, - "ipamo_modop_one_r: not processing memberOf " + MEMBEROF_PLUGIN_SUBSYSTEM, + "memberof_modop_one_replace_r: not processing memberOf " "operations on self entry: %s\n", this_dn_val); - slapi_value_free(&to_dn_val); - slapi_value_free(&this_dn_val); goto bail; } - /* We don't need the Slapi_Value copies of the DN's anymore */ - slapi_value_free(&to_dn_val); - slapi_value_free(&this_dn_val); - - if(stack && LDAP_MOD_DELETE == mod_op) + /* For add and del modify operations, we just regenerate the + * memberOf attribute. */ + if(LDAP_MOD_DELETE == mod_op || LDAP_MOD_ADD == mod_op) { - if(ipamo_is_legit_member(pb, group_dn, - op_this, op_to, stack)) + /* find parent groups and replace our member attr */ + memberof_fix_memberof_callback(e, config); + } else { + /* single entry - do mod */ + mod_pb = slapi_pblock_new(); + + mods[0] = &mod; + if(LDAP_MOD_REPLACE == mod_op) { - /* entry is member some other way too */ - slapi_log_error( SLAPI_LOG_PLUGIN, - IPAMO_PLUGIN_SUBSYSTEM, - "ipamo_modop_one_r: not deleting %s\n" - ,op_to); - goto bail; + mods[1] = &replace_mod; + mods[2] = 0; + } + else + { + mods[1] = 0; } - } - - /* single entry - do mod */ - mod_pb = slapi_pblock_new(); - - mods[0] = &mod; - if(LDAP_MOD_REPLACE == mod_op) - { - mods[1] = &replace_mod; - mods[2] = 0; - } - else - { - mods[1] = 0; - } - - val[0] = op_this; - val[1] = 0; - - mod.mod_op = LDAP_MOD_REPLACE == mod_op?LDAP_MOD_DELETE:mod_op; - mod.mod_type = IPA_MEMBEROF_ATTR; - mod.mod_values = val; - if(LDAP_MOD_REPLACE == mod_op) - { - replace_val[0] = replace_with; - replace_val[1] = 0; + val[0] = op_this; + val[1] = 0; + mod.mod_op = LDAP_MOD_REPLACE == mod_op?LDAP_MOD_DELETE:mod_op; + mod.mod_type = config->memberof_attr; + mod.mod_values = val; - replace_mod.mod_op = LDAP_MOD_ADD; - replace_mod.mod_type = IPA_MEMBEROF_ATTR; - replace_mod.mod_values = replace_val; - } + if(LDAP_MOD_REPLACE == mod_op) + { + replace_val[0] = replace_with; + replace_val[1] = 0; - slapi_modify_internal_set_pb( - mod_pb, op_to, - mods, 0, 0, - ipamo_get_plugin_id(), 0); + replace_mod.mod_op = LDAP_MOD_ADD; + replace_mod.mod_type = config->memberof_attr; + replace_mod.mod_values = replace_val; + } - slapi_modify_internal_pb(mod_pb); + slapi_modify_internal_set_pb( + mod_pb, op_to, + mods, 0, 0, + memberof_get_plugin_id(), 0); - slapi_pblock_get(mod_pb, - SLAPI_PLUGIN_INTOP_RESULT, - &rc); + slapi_modify_internal_pb(mod_pb); - slapi_pblock_destroy(mod_pb); + slapi_pblock_get(mod_pb, + SLAPI_PLUGIN_INTOP_RESULT, + &rc); - if(LDAP_MOD_DELETE == mod_op) - { - /* fix up membership for groups that have been orphaned */ - ipamo_test_membership_callback(e, 0); - } - - if(LDAP_MOD_ADD == mod_op) - { - /* If we failed to update memberOf for op_to, we shouldn't - * try to fix up membership for parent groups. */ - if (rc == 0) { - /* fix up membership for groups that are now in scope */ - ipamo_add_membership(pb, op_this, op_to); - } + slapi_pblock_destroy(mod_pb); } } bail: + slapi_sdn_free(&op_to_sdn); + slapi_value_free(&to_dn_val); + slapi_value_free(&this_dn_val); slapi_entry_free(e); return rc; } /* - * ipamo_add_one() + * memberof_add_one() * * Add addthis DN to the memberof attribute of addto * */ -int ipamo_add_one(Slapi_PBlock *pb, char *addthis, char *addto) +int memberof_add_one(Slapi_PBlock *pb, MemberOfConfig *config, char *addthis, char *addto) { - return ipamo_modop_one(pb, LDAP_MOD_ADD, addthis, addto); + return memberof_modop_one(pb, config, LDAP_MOD_ADD, addthis, addto); } /* - * ipamo_del_one() + * memberof_del_one() * * Delete delthis DN from the memberof attribute of delfrom * */ -int ipamo_del_one(Slapi_PBlock *pb, char *delthis, char *delfrom) +int memberof_del_one(Slapi_PBlock *pb, MemberOfConfig *config, char *delthis, char *delfrom) { - return ipamo_modop_one(pb, LDAP_MOD_DELETE, delthis, delfrom); + return memberof_modop_one(pb, config, LDAP_MOD_DELETE, delthis, delfrom); } /* - * ipamo_mod_smod_list() + * memberof_mod_smod_list() * * Perform mod for group DN to the memberof attribute of the list of targets * */ -int ipamo_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); @@ -1159,7 +1282,7 @@ int ipamo_mod_smod_list(Slapi_PBlock *pb, int mod, char *group_dn, Slapi_Mod *sm strncpy(dn_str, bv->bv_val, (size_t)bv->bv_len); - ipamo_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); } @@ -1171,103 +1294,117 @@ int ipamo_mod_smod_list(Slapi_PBlock *pb, int mod, char *group_dn, Slapi_Mod *sm } /* - * ipamo_add_smod_list() + * memberof_add_smod_list() * * Add group DN to the memberof attribute of the list of targets * */ -int ipamo_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 ipamo_mod_smod_list(pb, LDAP_MOD_ADD, groupdn, smod); + return memberof_mod_smod_list(pb, config, LDAP_MOD_ADD, groupdn, smod); } /* - * ipamo_del_smod_list() + * memberof_del_smod_list() * * Remove group DN from the memberof attribute of the list of targets * */ -int ipamo_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 ipamo_mod_smod_list(pb, LDAP_MOD_DELETE, groupdn, smod); + return memberof_mod_smod_list(pb, config, LDAP_MOD_DELETE, groupdn, smod); } /** * Plugin identity mgmt */ -void ipamo_set_plugin_id(void * plugin_id) +void memberof_set_plugin_id(void * plugin_id) { _PluginID=plugin_id; } -void * ipamo_get_plugin_id() +void * memberof_get_plugin_id() { return _PluginID; } /* - * ipamo_mod_attr_list() + * memberof_mod_attr_list() * * Perform mod for group DN to the memberof attribute of the list of targets * */ -int ipamo_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 ipamo_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 ipamo_mod_attr_list_r(Slapi_PBlock *pb, int mod, char *group_dn, char *op_this, - Slapi_Attr *attr, ipamostringll *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; + Slapi_Value *op_this_val = 0; int last_size = 0; char *last_str = 0; int hint = slapi_attr_first_value(attr, &val); + op_this_val = slapi_value_new_string(op_this); + while(val) { char *dn_str = 0; - struct berval *bv = (struct berval *)slapi_value_get_berval(val); + struct berval *bv = 0; - if(last_size > bv->bv_len) - { - dn_str = last_str; - } - else + /* We don't want to process a memberOf operation on ourselves. */ + if(0 != memberof_compare(config, &val, &op_this_val)) { - int the_size = (bv->bv_len * 2) + 1; + bv = (struct berval *)slapi_value_get_berval(val); - if(last_str) - slapi_ch_free_string(&last_str); + if(last_size > bv->bv_len) + { + dn_str = last_str; + } + else + { + int the_size = (bv->bv_len * 2) + 1; - dn_str = (char*)slapi_ch_malloc(the_size); + if(last_str) + slapi_ch_free_string(&last_str); - last_str = dn_str; - last_size = the_size; - } + dn_str = (char*)slapi_ch_malloc(the_size); - memset(dn_str, 0, last_size); + last_str = dn_str; + last_size = the_size; + } - strncpy(dn_str, bv->bv_val, (size_t)bv->bv_len); + memset(dn_str, 0, last_size); - /* If we're doing a replace (as we would in the MODRDN case), we need - * to specify the new group DN value */ - if(mod == LDAP_MOD_REPLACE) - { - ipamo_modop_one_replace_r(pb, mod, group_dn, op_this, group_dn, - dn_str, stack); - } - else - { - ipamo_modop_one_r(pb, mod, group_dn, op_this, dn_str, stack); + strncpy(dn_str, bv->bv_val, (size_t)bv->bv_len); + + /* If we're doing a replace (as we would in the MODRDN case), we need + * to specify the new group DN value */ + if(mod == LDAP_MOD_REPLACE) + { + memberof_modop_one_replace_r(pb, config, mod, group_dn, op_this, + group_dn, dn_str, stack); + } + else + { + memberof_modop_one_r(pb, config, mod, group_dn, op_this, dn_str, stack); + } } hint = slapi_attr_next_value(attr, hint, &val); } + slapi_value_free(&op_this_val); + if(last_str) slapi_ch_free_string(&last_str); @@ -1275,34 +1412,37 @@ int ipamo_mod_attr_list_r(Slapi_PBlock *pb, int mod, char *group_dn, char *op_th } /* - * ipamo_add_attr_list() + * memberof_add_attr_list() * * Add group DN to the memberof attribute of the list of targets * */ -int ipamo_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 ipamo_mod_attr_list(pb, LDAP_MOD_ADD, groupdn, attr); + return memberof_mod_attr_list(pb, config, LDAP_MOD_ADD, groupdn, attr); } /* - * ipamo_del_attr_list() + * memberof_del_attr_list() * * Remove group DN from the memberof attribute of the list of targets * */ -int ipamo_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 ipamo_mod_attr_list(pb, LDAP_MOD_DELETE, groupdn, attr); + return memberof_mod_attr_list(pb, config, LDAP_MOD_DELETE, groupdn, attr); } /* - * ipamo_moddn_attr_list() + * memberof_moddn_attr_list() * * Perform mod for group DN to the memberof attribute of the list of targets * */ -int ipamo_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; @@ -1336,7 +1476,7 @@ int ipamo_moddn_attr_list(Slapi_PBlock *pb, char *pre_dn, char *post_dn, Slapi_A strncpy(dn_str, bv->bv_val, (size_t)bv->bv_len); - ipamo_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); @@ -1348,46 +1488,112 @@ int ipamo_moddn_attr_list(Slapi_PBlock *pb, char *pre_dn, char *post_dn, Slapi_A return rc; } -typedef struct _ipamo_add_groups +/* memberof_get_groups() + * + * Gets a list of all groups that an entry is a member of. + * This is done by looking only at member attribute values. + * A Slapi_ValueSet* is returned. It is up to the caller to + * free it. + */ +Slapi_ValueSet *memberof_get_groups(MemberOfConfig *config, char *memberdn) { - char *target_dn; - char *group_dn; -} ipamo_add_groups; + Slapi_Value *memberdn_val = slapi_value_new_string(memberdn); + Slapi_ValueSet *groupvals = slapi_valueset_new(); + memberof_get_groups_data data = {config, memberdn_val, &groupvals}; -int ipamo_add_membership(Slapi_PBlock *pb, char *op_this, char *op_to) -{ - ipamo_add_groups data = {op_to, op_this}; + memberof_get_groups_r(config, memberdn, &data); + + slapi_value_free(&memberdn_val); + + return groupvals; +} - return ipamo_call_foreach_dn(pb, op_this, IPA_GROUP_ATTR, - ipamo_add_groups_search_callback, &data); +int memberof_get_groups_r(MemberOfConfig *config, char *memberdn, memberof_get_groups_data *data) +{ + /* Search for member=<memberdn> + * For each match, add it to the list, recurse and do same search */ + return memberof_call_foreach_dn(NULL, memberdn, config->groupattr, + memberof_get_groups_callback, data); } -int ipamo_add_groups_search_callback(Slapi_Entry *e, void *callback_data) +/* memberof_get_groups_callback() + * + * Callback to perform work of memberof_get_groups() + */ +int memberof_get_groups_callback(Slapi_Entry *e, void *callback_data) { - return ipamo_add_one(0, slapi_entry_get_dn(e), - ((ipamo_add_groups*)callback_data)->target_dn); + char *group_dn = slapi_entry_get_dn(e); + Slapi_Value *group_dn_val = 0; + Slapi_ValueSet *groupvals = *((memberof_get_groups_data*)callback_data)->groupvals; + + /* get the DN of the group */ + group_dn_val = slapi_value_new_string(group_dn); + + /* check if e is the same as our original member entry */ + if (0 == memberof_compare(((memberof_get_groups_data*)callback_data)->config, + &((memberof_get_groups_data*)callback_data)->memberdn_val, &group_dn_val)) + { + /* A recursive group caused us to find our original + * entry we passed to memberof_get_groups(). We just + * skip processing this entry. */ + slapi_log_error( SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, + "memberof_get_groups_callback: group recursion" + " detected in %s\n" ,group_dn); + slapi_value_free(&group_dn_val); + goto bail; + + } + + /* have we been here before? */ + if (groupvals && + slapi_valueset_find(((memberof_get_groups_data*)callback_data)->config->group_slapiattr, + groupvals, group_dn_val)) + { + /* we either hit a recursive grouping, or an entry is + * a member of a group through multiple paths. Either + * way, we can just skip processing this entry since we've + * already gone through this part of the grouping hierarchy. */ + slapi_log_error( SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, + "memberof_get_groups_callback: possible group recursion" + " detected in %s\n" ,group_dn); + slapi_value_free(&group_dn_val); + goto bail; + } + + /* Push group_dn_val into the valueset. This memory is now owned + * by the valueset. */ + slapi_valueset_add_value_ext(groupvals, group_dn_val, SLAPI_VALUE_FLAG_PASSIN); + + /* now recurse to find parent groups of e */ + memberof_get_groups_r(((memberof_get_groups_data*)callback_data)->config, + group_dn, callback_data); + + bail: + return 0; } -/* ipamo_is_group_member() - * tests membership of memberdn in group groupdn +/* memberof_is_direct_member() + * + * tests for direct membership of memberdn in group groupdn * returns non-zero when true, zero otherwise */ -int ipamo_is_group_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] = {IPA_GROUP_ATTR,0}; + char *attrlist[2] = {config->groupattr,0}; Slapi_Entry *group_e = 0; Slapi_Attr *attr = 0; sdn = slapi_sdn_new_dn_byref(slapi_value_get_string(groupdn)); slapi_search_internal_get_entry(sdn, attrlist, - &group_e, ipamo_get_plugin_id()); + &group_e, memberof_get_plugin_id()); if(group_e) { - slapi_entry_attr_find(group_e, IPA_GROUP_ATTR, &attr ); + slapi_entry_attr_find(group_e, config->groupattr, &attr ); if(attr) { rc = 0 == slapi_attr_value_find( @@ -1400,9 +1606,14 @@ int ipamo_is_group_member(Slapi_Value *groupdn, Slapi_Value *memberdn) return rc; } -/* ipamo_memberof_search_callback() +/* memberof_test_membership() + * + * Finds all entries who are a "memberOf" the group + * represented by "group_dn". For each matching entry, we + * call memberof_test_membership_callback(). + * * for each attribute in the memberof attribute - * determine if the entry is still a member + * determine if the entry is still a member. * * test each for direct membership * move groups entry is memberof to member group @@ -1410,13 +1621,20 @@ int ipamo_is_group_member(Slapi_Value *groupdn, Slapi_Value *memberdn) * iterate until a pass fails to move a group over to member groups * remaining groups should be deleted */ -int ipamo_test_membership(Slapi_PBlock *pb, char *dn) +int memberof_test_membership(Slapi_PBlock *pb, MemberOfConfig *config, char *group_dn) { - return ipamo_call_foreach_dn(pb, dn, IPA_MEMBEROF_ATTR, - ipamo_test_membership_callback ,0); + return memberof_call_foreach_dn(pb, group_dn, config->memberof_attr, + memberof_test_membership_callback , config); } -int ipamo_test_membership_callback(Slapi_Entry *e, void *callback_data) +/* + * memberof_test_membership_callback() + * + * A callback function to do the work of memberof_test_membership(). + * Note that this not only tests membership, but updates the memberOf + * attributes in the entry to be correct. + */ +int memberof_test_membership_callback(Slapi_Entry *e, void *callback_data) { int rc = 0; Slapi_Attr *attr = 0; @@ -1424,6 +1642,7 @@ int ipamo_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)); @@ -1433,7 +1652,7 @@ int ipamo_test_membership_callback(Slapi_Entry *e, void *callback_data) } /* divide groups into member and non-member lists */ - slapi_entry_attr_find(e, IPA_MEMBEROF_ATTR, &attr ); + slapi_entry_attr_find(e, config->memberof_attr, &attr ); if(attr) { slapi_attr_get_numvalues( attr, &total); @@ -1459,8 +1678,8 @@ int ipamo_test_membership_callback(Slapi_Entry *e, void *callback_data) while(val) { - /* test for membership */ - if(ipamo_is_group_member(val, entry_dn)) + /* test for direct membership */ + if(memberof_is_direct_member(config, val, entry_dn)) { /* it is a member */ member_array[m_index] = val; @@ -1485,12 +1704,19 @@ int ipamo_test_membership_callback(Slapi_Entry *e, void *callback_data) { member_found = 0; + /* For each group that this entry is a verified member of, see if + * any of the candidate groups are members. If they are, add them + * to the list of verified groups that this entry is a member of. + */ while(outer_index < m_index) { int inner_index = 0; while(inner_index < c_index) { + /* Check for a special value in this position + * that indicates that the candidate was moved + * to the member array. */ if((void*)1 == candidate_array[inner_index]) { @@ -1499,7 +1725,8 @@ int ipamo_test_membership_callback(Slapi_Entry *e, void *callback_data) continue; } - if(ipamo_is_group_member( + if(memberof_is_direct_member( + config, candidate_array[inner_index], member_array[outer_index])) { @@ -1527,6 +1754,9 @@ int ipamo_test_membership_callback(Slapi_Entry *e, void *callback_data) outer_index = 0; while(outer_index < c_index) { + /* Check for a special value in this position + * that indicates that the candidate was moved + * to the member array. */ if((void*)1 == candidate_array[outer_index]) { /* item moved, skip */ @@ -1534,8 +1764,8 @@ int ipamo_test_membership_callback(Slapi_Entry *e, void *callback_data) continue; } - ipamo_del_one( - 0, + memberof_del_one( + 0, config, (char*)slapi_value_get_string( candidate_array[outer_index]), (char*)slapi_value_get_string(entry_dn)); @@ -1565,12 +1795,12 @@ bail: } /* - * ipamo_replace_list() + * memberof_replace_list() * * Perform replace the group DN list in the memberof attribute of the list of targets * */ -int ipamo_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; @@ -1582,8 +1812,8 @@ int ipamo_replace_list(Slapi_PBlock *pb, char *group_dn) if(pre_e && post_e) { - slapi_entry_attr_find( pre_e, IPA_GROUP_ATTR, &pre_attr ); - slapi_entry_attr_find( post_e, IPA_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) @@ -1606,17 +1836,25 @@ int ipamo_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 = (Slapi_Value**) slapi_ch_malloc(sizeof(Slapi_Value*)*pre_total); - ipamo_load_array(pre_array, pre_attr); + memberof_load_array(pre_array, pre_attr); qsort( pre_array, pre_total, sizeof(Slapi_Value*), - ipamo_compare); + memberof_qsort_compare); } if(post_total) @@ -1624,14 +1862,16 @@ int ipamo_replace_list(Slapi_PBlock *pb, char *group_dn) post_array = (Slapi_Value**) slapi_ch_malloc(sizeof(Slapi_Value*)*post_total); - ipamo_load_array(post_array, post_attr); + memberof_load_array(post_array, post_attr); qsort( post_array, post_total, sizeof(Slapi_Value*), - ipamo_compare); + memberof_qsort_compare); } + qsortConfig = 0; + /* work through arrays, following these rules: in pre, in post, do nothing @@ -1643,8 +1883,8 @@ int ipamo_replace_list(Slapi_PBlock *pb, char *group_dn) if(pre_index == pre_total) { /* add the rest of post */ - ipamo_add_one( - pb, + memberof_add_one( + pb, config, group_dn, (char*)slapi_value_get_string( post_array[post_index])); @@ -1654,8 +1894,8 @@ int ipamo_replace_list(Slapi_PBlock *pb, char *group_dn) else if(post_index == post_total) { /* delete the rest of pre */ - ipamo_del_one( - pb, + memberof_del_one( + pb, config, group_dn, (char*)slapi_value_get_string( pre_array[pre_index])); @@ -1665,15 +1905,16 @@ int ipamo_replace_list(Slapi_PBlock *pb, char *group_dn) else { /* decide what to do */ - int cmp = ipamo_compare( + int cmp = memberof_compare( + config, &(pre_array[pre_index]), &(post_array[post_index])); if(cmp < 0) { /* delete pre array */ - ipamo_del_one( - pb, + memberof_del_one( + pb, config, group_dn, (char*)slapi_value_get_string( pre_array[pre_index])); @@ -1683,8 +1924,8 @@ int ipamo_replace_list(Slapi_PBlock *pb, char *group_dn) else if(cmp > 0) { /* add post array */ - ipamo_add_one( - pb, + memberof_add_one( + pb, config, group_dn, (char*)slapi_value_get_string( post_array[post_index])); @@ -1706,11 +1947,11 @@ int ipamo_replace_list(Slapi_PBlock *pb, char *group_dn) return 0; } -/* ipamo_load_array() +/* memberof_load_array() * * put attribute values in array structure */ -void ipamo_load_array(Slapi_Value **array, Slapi_Attr *attr) +void memberof_load_array(Slapi_Value **array, Slapi_Attr *attr) { Slapi_Value *val = 0; int hint = slapi_attr_first_value(attr, &val); @@ -1723,228 +1964,60 @@ void ipamo_load_array(Slapi_Value **array, Slapi_Attr *attr) } } -/* ipamo_compare() +/* memberof_compare() * * compare two attr values */ -int ipamo_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, IPA_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)); } -/* ipamo_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 *ipamo_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); - - return ret; -} - -/* ipamo_is_legit_member() - * - * before we rush to remove this group from the entry - * we need to be sure that the entry is not a member - * of the group for another legitimate reason i.e. - * that it is not itself a direct member of the group, - * and that all groups in its memberof attribute except - * the second from bottom one of our stack do not appear - * in the membership attribute of the group -*/ -int ipamo_is_legit_member(Slapi_PBlock *pb, char *group_dn, - char *op_this, char *op_to, ipamostringll *stack) -{ - int rc = 0; - Slapi_DN *group_sdn = 0; - Slapi_Entry *group_e = 0; - Slapi_DN *opto_sdn = 0; - Slapi_Entry *opto_e = 0; - char *filter_str = 0; - Slapi_Filter *filter = 0; - int filter_size = 0; - ipamostringll *ll = 0; - char *attrlist[2] = {IPA_GROUP_ATTR,0}; - char *optolist[2] = {IPA_MEMBEROF_ATTR,0}; - Slapi_Attr *memberof = 0; - Slapi_Value *memberdn = 0; - int hint = 0; - char *delete_group_dn = 0; - - slapi_log_error( SLAPI_LOG_TRACE, IPAMO_PLUGIN_SUBSYSTEM, - "--> ipamo_is_legit_member\n" ); - - /* first test entry */ - group_sdn = slapi_sdn_new_dn_byref(op_this); - slapi_search_internal_get_entry( group_sdn, attrlist, - &group_e, ipamo_get_plugin_id()); - slapi_sdn_free(&group_sdn); - - if(!group_e) - { - goto bail; - } - - filter_size = 2 * - (strlen(IPA_GROUP_ATTR) + - strlen(op_to) + 4); /* 4 for (=) + null */ - filter_str = (char*)slapi_ch_malloc(filter_size); - - sprintf(filter_str, "(%s=%s)", IPA_GROUP_ATTR, op_to); - - filter = ipamo_string2filter(filter_str); - - if(!slapi_filter_test_simple(group_e, filter)) - { - /* entry is direct member */ - slapi_log_error( SLAPI_LOG_PLUGIN, IPAMO_PLUGIN_SUBSYSTEM, - "ipamo_is_legit_member: %s direct member of %s\n" - ,op_to,op_this); - slapi_filter_free(filter,0); - rc = 1; - goto bail; - } - - slapi_filter_free(filter,0); - - /* test all group dns in stack - the top dn is the group we remove the entry from - second from bottom dn is being removed from the - bottom group, we ignore those two - */ - ll = stack; - - /* need to be 2 items left on the stack */ - while( ll && - ll->next && - ((ipamostringll*)ll->next)->next) - { - ll = ll->next; - } - - if(!ll || !ll->next) - { - /* tight recursion, bail */ - goto bail; - } - - delete_group_dn = ((ipamostringll*)ll->next)->dn; - - /* get the target entry memberof attribute */ - opto_sdn = slapi_sdn_new_dn_byref(op_to); - slapi_search_internal_get_entry( opto_sdn, optolist, - &opto_e, ipamo_get_plugin_id()); - slapi_sdn_free(&opto_sdn); - - if(opto_e) - { - slapi_entry_attr_find(opto_e, IPA_MEMBEROF_ATTR, &memberof); - } - - if(0 == memberof) - { - goto bail; - } - - /* iterate through memberof values and test against group membership */ - hint = slapi_attr_first_value(memberof, &memberdn); - - while(memberdn) - { - char *dn = (char*)slapi_value_get_string(memberdn); - int current_size = - (strlen(IPA_GROUP_ATTR) + - strlen(dn) + 4); /* 4 for (=) + null */ - - /* disregard the group being removed */ - if(0 == strcmp(dn, delete_group_dn)) - { - hint = slapi_attr_next_value(memberof, hint, &memberdn); - continue; - } - - if(current_size > filter_size) - { - filter_size = 2 * current_size; - filter_str = slapi_ch_realloc( - filter_str, filter_size); - } - - sprintf(filter_str, "(%s=%s)", IPA_GROUP_ATTR, dn); - filter = ipamo_string2filter(filter_str); - - if(!slapi_filter_test_simple(group_e, filter)) - { - /* another group allows entry */ - slapi_log_error( SLAPI_LOG_PLUGIN, IPAMO_PLUGIN_SUBSYSTEM, - "ipamo_is_legit_member: %s is group member of %s\n" - ,op_to,dn); - slapi_filter_free(filter,0); - - rc = 1; - goto bail; - } - - slapi_filter_free(filter,0); - - hint = slapi_attr_next_value(memberof, hint, &memberdn); - } - -bail: - slapi_entry_free(group_e); - slapi_entry_free(opto_e); - slapi_ch_free_string(&filter_str); + Slapi_Value *val1 = *((Slapi_Value **)a); + Slapi_Value *val2 = *((Slapi_Value **)b); - slapi_log_error( SLAPI_LOG_TRACE, IPAMO_PLUGIN_SUBSYSTEM, - "<-- ipamo_is_legit_member\n" ); - return rc; + return slapi_attr_value_cmp( + qsortConfig->group_slapiattr, + slapi_value_get_berval(val1), + slapi_value_get_berval(val2)); } -void ipamo_lock() +void memberof_lock() { - slapi_lock_mutex(ipamo_operation_lock); + slapi_lock_mutex(memberof_operation_lock); } -void ipamo_unlock() +void memberof_unlock() { - slapi_unlock_mutex(ipamo_operation_lock); + slapi_unlock_mutex(memberof_operation_lock); } -/* - * - */ - typedef struct _task_data { char *dn; char *filter_str; } task_data; -void ipamo_memberof_fixup_task_thread(void *arg) +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; @@ -1956,8 +2029,24 @@ void ipamo_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 = ipamo_fix_memberof(td->dn, td->filter_str); + rc = memberof_fix_memberof(&configCopy, td->dn, td->filter_str); + + /* release the memberOf operation lock */ + memberof_unlock(); + + memberof_free_config(&configCopy); slapi_task_log_notice(task, "Memberof task finished."); slapi_task_log_status(task, "Memberof task finished."); @@ -1983,7 +2072,7 @@ const char *fetch_attr(Slapi_Entry *e, const char *attrname, return slapi_value_get_string(val); } -int ipamo_task_add(Slapi_PBlock *pb, Slapi_Entry *e, +int memberof_task_add(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg) { @@ -2025,18 +2114,18 @@ int ipamo_task_add(Slapi_PBlock *pb, Slapi_Entry *e, task = slapi_new_task(slapi_entry_get_ndn(e)); /* register our destructor for cleaning up our private data */ - slapi_task_set_destructor_fn(task, ipamo_task_destructor); + slapi_task_set_destructor_fn(task, memberof_task_destructor); /* Stash a pointer to our data in the task */ slapi_task_set_data(task, mytaskdata); /* start the sample task as a separate thread */ - thread = PR_CreateThread(PR_USER_THREAD, ipamo_memberof_fixup_task_thread, + thread = PR_CreateThread(PR_USER_THREAD, memberof_fixup_task_thread, (void *)task, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE); if (thread == NULL) { - slapi_log_error( SLAPI_LOG_FATAL, IPAMO_PLUGIN_SUBSYSTEM, + slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, "unable to create task thread!\n"); *returncode = LDAP_OPERATIONS_ERROR; rv = SLAPI_DSE_CALLBACK_ERROR; @@ -2050,7 +2139,7 @@ out: } void -ipamo_task_destructor(Slapi_Task *task) +memberof_task_destructor(Slapi_Task *task) { if (task) { task_data *mydata = (task_data *)slapi_task_get_data(task); @@ -2063,7 +2152,7 @@ ipamo_task_destructor(Slapi_Task *task) } } -int ipamo_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(); @@ -2071,12 +2160,12 @@ int ipamo_fix_memberof(char *dn, char *filter_str) slapi_search_internal_set_pb(search_pb, dn, LDAP_SCOPE_SUBTREE, filter_str, 0, 0, 0, 0, - ipamo_get_plugin_id(), + memberof_get_plugin_id(), 0); rc = slapi_search_internal_callback_pb(search_pb, - 0, - 0, ipamo_fix_memberof_callback, + config, + 0, memberof_fix_memberof_callback, 0); slapi_pblock_destroy(search_pb); @@ -2084,28 +2173,72 @@ int ipamo_fix_memberof(char *dn, char *filter_str) return rc; } -/* ipamo_fix_memberof_callback() +/* memberof_fix_memberof_callback() * Add initial and/or fix up broken group list in entry * - * 1. Make sure direct membership groups are in the entry - * 2. Add all groups that current group list allows through nested membership - * 3. Trim groups that have no relationship to entry + * 1. Remove all present memberOf values + * 2. Add direct group membership memberOf values + * 3. Add indirect group membership memberOf values */ -int ipamo_fix_memberof_callback(Slapi_Entry *e, void *callback_data) +int memberof_fix_memberof_callback(Slapi_Entry *e, void *callback_data) { int rc = 0; char *dn = slapi_entry_get_dn(e); - ipamo_add_groups data = {dn, dn}; + MemberOfConfig *config = (MemberOfConfig *)callback_data; + memberof_del_dn_data del_data = {0, config->memberof_attr}; + Slapi_ValueSet *groups = 0; + + /* get a list of all of the groups this user belongs to */ + groups = memberof_get_groups(config, dn); - /* step 1. and step 2. */ - rc = ipamo_call_foreach_dn(0, dn, IPA_GROUP_ATTR, - ipamo_add_groups_search_callback, &data); - if(0 == rc) + /* If we found some groups, replace the existing memberOf attribute + * with the found values. */ + if (groups && slapi_valueset_count(groups)) { - /* step 3. */ - rc = ipamo_test_membership_callback(e, 0); + Slapi_PBlock *mod_pb = slapi_pblock_new(); + Slapi_Value *val = 0; + Slapi_Mod *smod; + LDAPMod **mods = (LDAPMod **) slapi_ch_malloc(2 * sizeof(LDAPMod *)); + int hint = 0; + + /* NGK - need to allocate the smod */ + smod = slapi_mod_new(); + slapi_mod_init(smod, 0); + slapi_mod_set_operation(smod, LDAP_MOD_REPLACE | LDAP_MOD_BVALUES); + slapi_mod_set_type(smod, config->memberof_attr); + + /* Loop through all of our values and add them to smod */ + hint = slapi_valueset_first_value(groups, &val); + while (val) + { + /* this makes a copy of the berval */ + slapi_mod_add_value(smod, slapi_value_get_berval(val)); + hint = slapi_valueset_next_value(groups, hint, &val); + } + + mods[0] = slapi_mod_get_ldapmod_passout(smod); + mods[1] = 0; + + slapi_modify_internal_set_pb( + mod_pb, dn, mods, 0, 0, + memberof_get_plugin_id(), 0); + + slapi_modify_internal_pb(mod_pb); + + slapi_pblock_get(mod_pb, SLAPI_PLUGIN_INTOP_RESULT, &rc); + + ldap_mods_free(mods, 1); + slapi_mod_free(&smod); + /* NGK - need to free the smod */ + slapi_pblock_destroy(mod_pb); + } else { + /* No groups were found, so remove the memberOf attribute + * from this entry. */ + memberof_del_dn_type_callback(e, &del_data); } + slapi_valueset_free(groups); + return rc; } diff --git a/ipa-server/ipa-slapi-plugins/ipa-memberof/ipa-memberof.h b/ipa-server/ipa-slapi-plugins/ipa-memberof/ipa-memberof.h new file mode 100644 index 000000000..3e7b5cf4b --- /dev/null +++ b/ipa-server/ipa-slapi-plugins/ipa-memberof/ipa-memberof.h @@ -0,0 +1,100 @@ +/** 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 + +/* + * ipa-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 <dirsrv/slapi-plugin.h> +#include <nspr.h> + +/****** secrets *********/ +/*from FDS slapi-private.h + * until we get a proper api for access + */ +#define SLAPI_DSE_CALLBACK_OK (1) +#define SLAPI_DSE_CALLBACK_ERROR (-1) +#define SLAPI_DSE_CALLBACK_DO_NOT_APPLY (0) +#define SLAPI_DSE_RETURNTEXT_SIZE 512 +#define DSE_FLAG_PREOP 0x0002 +/*********** end secrets **********/ +/* + * macros + */ +#define MEMBEROF_PLUGIN_SUBSYSTEM "ipa-memberof-plugin" /* used for logging */ +#define MEMBEROF_GROUP_ATTR "member" +#define MEMBEROF_ATTR "memberOf" + + +/* + * 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); +void memberof_free_config(MemberOfConfig *config); +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/ipa-server/ipa-slapi-plugins/ipa-memberof/ipa-memberof_config.c b/ipa-server/ipa-slapi-plugins/ipa-memberof/ipa-memberof_config.c new file mode 100644 index 000000000..b2bd374ad --- /dev/null +++ b/ipa-server/ipa-slapi-plugins/ipa-memberof/ipa-memberof_config.c @@ -0,0 +1,312 @@ +/** 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 "ipa-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_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 */ + 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, + dont_allow_that,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_apply_config() + * + * Just use hardcoded config values. + */ +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_ch_strdup(MEMBEROF_GROUP_ATTR); + memberof_attr = slapi_ch_strdup(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_free_config() + * + * Free's the contents of a config structure. + */ +void +memberof_free_config(MemberOfConfig *config) +{ + if (config) + { + slapi_ch_free_string(&config->groupattr); + slapi_filter_free(config->group_filter, 1); + slapi_attr_free(&config->group_slapiattr); + slapi_ch_free_string(&config->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); +} |