[Fedora-directory-commits] ldapserver/ldap/servers/plugins/memberof memberof.c, NONE, 1.1

Nathan Kinder (nkinder) fedora-directory-commits at redhat.com
Tue Feb 19 06:04:58 UTC 2008


Author: nkinder

Update of /cvs/dirsec/ldapserver/ldap/servers/plugins/memberof
In directory cvs-int.fedora.redhat.com:/tmp/cvs-serv13952/ldap/servers/plugins/memberof

Added Files:
	memberof.c 
Log Message:
Initial import of memberof plugin from FreeIPA (refactored from changeset 640:9c57bd91b32f if ipa-memberof.c).



--- NEW FILE memberof.c ---
/** 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. 
 *
 * Authors: 
 * Pete Rowley <prowley at redhat.com>
 *
 * Copyright (C) 2007 Red Hat, Inc.
 * All rights reserved.
 * END COPYRIGHT BLOCK
 **/

/* The memberof plugin updates the memberof attribute of entries
 * based on modifications performed on groupofuniquenames entries
 *
 * In addition the plugin provides a DS task that may be started
 * administrative clients and that creates the initial memberof
 * list for imported entries and/or fixes the memberof list of
 * existing entries that have inconsistent state (for example,
 * if the memberof attribute was incorrectly edited directly) 
 *
 * To start the memberof task add an entry like:
 *
 * dn: cn=memberof task 2, cn=memberof task, cn=tasks, cn=config
 * objectClass: top
 * objectClass: extensibleObject
 * cn: sample task
 * basedn: dc=example, dc=com
 * filter: (uid=test4)
 *
 * where "basedn" is required and refers to the top most node to perform the
 * task on, and where "filter" is an optional attribute that provides a filter
 * describing the entries to be worked on
 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include "slapi-plugin.h"
#include "dirver.h"
#include <dirlite_strings.h> /* PLUGIN_MAGIC_VENDOR_STR */

#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_ATTR_TYPE "uid"
#define MEMBEROF_GROUP_FILTER "(" MEMBEROF_GROUP_ATTR "=*)"

#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;

typedef struct _memberofstringll
{
	char *dn;
	void *next;
} memberofstringll;



/****** secrets *********/

/*from FDS slap.h
 * 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.
 */
struct _slapi_task {
    struct _slapi_task *next;
    char *task_dn;
    int task_exitcode;          /* for the end user */
    int task_state;             /* (see above) */
    int task_progress;          /* number between 0 and task_work */
    int task_work;              /* "units" of work to be done */
    int task_flags;             /* (see above) */

    /* it is the task's responsibility to allocate this memory & free it: */
    char *task_status;          /* transient status info */
    char *task_log;             /* appended warnings, etc */

    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;
};

/****** secrets ********/


/*** function prototypes ***/

/* exported functions */
int memberof_postop_init(Slapi_PBlock *pb );

/* plugin callbacks */ 
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 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 memberofd_replace_list(Slapi_PBlock *pb, 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 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_call_foreach_dn(Slapi_PBlock *pb, char *dn,
	char *type, plugin_search_entry_callback callback,  void *callback_data);
static int memberof_is_group_member(Slapi_Value *groupdn, Slapi_Value *memberdn);
static int memberof_test_membership(Slapi_PBlock *pb, char *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_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_task_add(Slapi_PBlock *pb, Slapi_Entry *e,
                    Slapi_Entry *eAfter, int *returncode, char *returntext,
                    void *arg);
static const char *fetch_attr(Slapi_Entry *e, const char *attrname,
                                              const char *default_val);
[...1631 lines suppressed...]
		"<-- memberof_is_legit_member\n" );
	return rc;
}

void memberof_lock()
{
	slapi_lock_mutex(memberof_operation_lock);
}

void memberof_unlock()
{
	slapi_unlock_mutex(memberof_operation_lock);
}

/* 
 *
 */
 
typedef struct _task_data
{
	char *dn;
	char *filter_str;
	Slapi_Task *task;
} task_data;

void memberof_memberof_fixup_task_thread(void *arg)
{
	task_data *td = (task_data *)arg;
	Slapi_Task *task = td->task;
	int rc = 0;

	task->task_work = 1;
	task->task_progress = 0;
	task->task_state = SLAPI_TASK_RUNNING;

	slapi_task_status_changed(task);

	slapi_task_log_notice(task, "Memberof task starts (arg: %s) ...\n", 
								td->filter_str);

	/* do real work */
	rc = memberof_fix_memberof(td->dn, td->filter_str);

	slapi_task_log_notice(task, "Memberof task finished.");
	slapi_task_log_status(task, "Memberof task finished.");

	task->task_progress = 1;
	task->task_exitcode = rc;
	task->task_state = SLAPI_TASK_FINISHED;
	slapi_task_status_changed(task);

	slapi_ch_free_string(&td->dn);
	slapi_ch_free_string(&td->filter_str);

	{
		/* make the compiler happy */
		void *ptd = td;
		slapi_ch_free(&ptd);
	}
}

/* extract a single value from the entry (as a string) -- if it's not in the
 * entry, the default will be returned (which can be NULL).
 * you do not need to free anything returned by this.
 */
const char *fetch_attr(Slapi_Entry *e, const char *attrname,
                                              const char *default_val)
{
	Slapi_Attr *attr;
	Slapi_Value *val = NULL;

	if (slapi_entry_attr_find(e, attrname, &attr) != 0)
		return default_val;
	slapi_attr_first_value(attr, &val);
	return slapi_value_get_string(val);
}

int memberof_task_add(Slapi_PBlock *pb, Slapi_Entry *e,
                    Slapi_Entry *eAfter, int *returncode, char *returntext,
                    void *arg)
{
	PRThread *thread = NULL;
	int rv = SLAPI_DSE_CALLBACK_OK;
	task_data *mytaskdata = NULL;
	Slapi_Task *task = NULL;
	const char *filter;
	const char *dn = 0;

	*returncode = LDAP_SUCCESS;
	/* get arg(s) */
	if ((dn = fetch_attr(e, "basedn", 0)) == NULL)
	{
		*returncode = LDAP_OBJECT_CLASS_VIOLATION;
		rv = SLAPI_DSE_CALLBACK_ERROR;
		goto out;
	}

	if ((filter = fetch_attr(e, "filter", "(objectclass=inetuser)")) == NULL)
	{
		*returncode = LDAP_OBJECT_CLASS_VIOLATION;
		rv = SLAPI_DSE_CALLBACK_ERROR;
		goto out;
	}

	/* allocate new task now */
	task = slapi_new_task(slapi_entry_get_ndn(e));
	task->task_state = SLAPI_TASK_SETUP;
	task->task_work = 1;
	task->task_progress = 0;

	/* create a pblock to pass the necessary info to the task thread */
	mytaskdata = (task_data*)slapi_ch_malloc(sizeof(task_data));
	if (mytaskdata == NULL)
	{
		*returncode = LDAP_OPERATIONS_ERROR;
		rv = SLAPI_DSE_CALLBACK_ERROR;
		goto out;
	}
	mytaskdata->dn = slapi_ch_strdup(dn);
	mytaskdata->filter_str = slapi_ch_strdup(filter);
	mytaskdata->task = task;

	/* start the sample task as a separate thread */
	thread = PR_CreateThread(PR_USER_THREAD, memberof_memberof_fixup_task_thread,
		(void *)mytaskdata, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
		PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE);
	if (thread == NULL)
	{
		slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
			"unable to create task thread!\n");
		*returncode = LDAP_OPERATIONS_ERROR;
		rv = SLAPI_DSE_CALLBACK_ERROR;

		slapi_ch_free_string(&mytaskdata->dn);
		slapi_ch_free_string(&mytaskdata->filter_str);

		{
			void *ptask = mytaskdata;
			slapi_ch_free(&ptask);
			goto out;
		}
	}

	/* thread successful -- don't free the pb, let the thread do that. */
	return SLAPI_DSE_CALLBACK_OK;

out:
	if (task)
	{
		slapi_destroy_task(task);
	}
	return rv;
}

int memberof_fix_memberof(char *dn, char *filter_str)
{
	int rc = 0;
	Slapi_PBlock *search_pb = slapi_pblock_new();

	slapi_search_internal_set_pb(search_pb, dn,
		LDAP_SCOPE_SUBTREE, filter_str, 0, 0,
		0, 0,
		memberof_get_plugin_id(),
		0);	

	rc = slapi_search_internal_callback_pb(search_pb,
		0,
		0, memberof_fix_memberof_callback,
		0);

	slapi_pblock_destroy(search_pb);

	return rc;
}

/* 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
 */
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};

	/* step 1. and step 2. */
	rc = memberof_call_foreach_dn(0, dn, MEMBEROF_GROUP_ATTR, 
		memberof_add_groups_search_callback, &data);
	if(0 == rc)
	{
		/* step 3. */
		rc = memberof_test_membership_callback(e, 0);
	}

	return rc;
}





More information about the Fedora-directory-commits mailing list