/*
SSSD memberof module
Copyright (C) Simo Sorce <idra@samba.org> 2008-2011
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
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, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <dhash.h>
#include "ldb_module.h"
#ifndef MAX
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
#endif
struct mbof_val_array {
int num;
};
struct mbof_dn_array {
int num;
};
struct mbof_dn {
};
struct mbof_ctx {
};
struct mbof_add_operation {
};
struct mbof_memberuid_op {
};
struct mbof_add_ctx {
bool terminate;
int num_muops;
int cur_muop;
};
struct mbof_del_ancestors_ctx {
int num_direct;
int cur;
};
struct mbof_del_operation {
int num_children;
int next_child;
int num_parents;
int cur_parent;
};
struct mbof_mod_ctx;
struct mbof_del_ctx {
int num_mus;
int num_muops;
int cur_muop;
int num_ghops;
int cur_ghop;
bool is_mod;
};
struct mbof_mod_del_op {
};
struct mbof_mod_ctx {
bool terminate;
};
struct ldb_request *req)
{
if (!ctx) {
return NULL;
}
return ctx;
}
{
}
{
}
const char *objectclass)
{
int i;
if (!el) {
return LDB_ERR_OPERATIONS_ERROR;
}
for (i = 0; i < el->num_values; i++) {
return LDB_SUCCESS;
}
}
return LDB_ERR_NO_SUCH_ATTRIBUTE;
}
{
}
{
}
struct mbof_memberuid_op **_muops,
int *_num_muops,
int flags,
const char *name,
const char *element_name)
{
int i;
if (muops) {
for (i = 0; i < num_muops; i++) {
break;
}
}
}
if (!op) {
struct mbof_memberuid_op,
num_muops + 1);
if (!muops) {
return LDB_ERR_OPERATIONS_ERROR;
}
num_muops++;
*_num_muops = num_muops;
}
return LDB_ERR_OPERATIONS_ERROR;
}
return LDB_ERR_OPERATIONS_ERROR;
}
}
/* we already have this value, get out*/
return LDB_SUCCESS;
}
}
if (!val) {
return LDB_ERR_OPERATIONS_ERROR;
}
return LDB_ERR_OPERATIONS_ERROR;
}
return LDB_SUCCESS;
}
/* add operation */
/* An add operation is quite simple.
* First of all a new object cannot yet have parents, so the only memberof
* attribute that can be added to any member contains just one object DN.
*
* The real add operation is done first, to assure nothing else fails.
* Then we list all members of the object just created, and for each member
* we create an "add operation" and we pass it a parent list of one member
* (the object we just added again).
*
* For each add operation we lookup the object we want to operate on.
* We take the list of memberof attributes and sort out which parents are
* still missing from the parent list we have provided.
* We modify the object memberof attributes to reflect the new memberships.
* Then we list all members of this object, and for each once again we create
* an "add operation" as we did in the initial object.
*
* Processing stops when the target object does not have members or when it
* already has all the parents (can happen if nested groups create loops).
*
* Group cache unrolling:
* Every time we add a memberof attribute to an actual user object,
* we proceed to store the user name.
*
* At the end we will add a memberuid attribute to our new object that
* includes all direct and indirect user members names.
*
* Group objects can also contain a "ghost" attribute. A ghost attribute
* represents a user that is a member of the group but has not yet been
*
* If an object being added contains a "ghost" attribute, the ghost attribute
* is in turn copied to all parents of that object so that retrieving a
* group returns both its direct and indirect members. The ghost attribute is
* similar to the memberuid attribute in many respects. One difference is that
* the memberuid attribute is completely generated and managed by the memberof
* plugin - in contrast, the ghost attribute is added to the entry that "owns"
* it and only propagated to parent groups.
*/
struct mbof_dn_array *parents,
{
/* test if this is a duplicate */
/* FIXME: this is not efficient */
do {
if (lastop) {
} else {
}
/* FIXME: check if this is right, might have to compare parents */
/* duplicate found */
return LDB_SUCCESS;
}
}
if (!addop) {
return LDB_ERR_OPERATIONS_ERROR;
}
} else {
}
return LDB_SUCCESS;
}
struct ldb_message *entry,
struct mbof_dn_array *parents,
unsigned int num_gh_vals)
{
int ret;
int i, j;
/* no parents attributes ... */
return LDB_SUCCESS;
}
switch (ret) {
case LDB_SUCCESS:
/* it's a group object, continue */
break;
/* it is not a group object, just return */
return LDB_SUCCESS;
default:
/* an error occurred, return */
return ret;
}
"will add %d ghost users to %d parents\n",
for (j = 0; j < num_gh_vals; j++) {
DB_GHOST);
if (ret != LDB_SUCCESS) {
return ret;
}
}
}
return LDB_SUCCESS;
}
struct ldb_request *req);
struct ldb_message *entry,
struct mbof_dn_array *parents);
{
int i, ret;
if (strcmp("@MEMBEROF-REBUILD",
}
/* do not manipulate other control entries */
}
/* check if memberof is specified */
if (el) {
"Error: the memberof attribute is readonly.");
return LDB_ERR_UNWILLING_TO_PERFORM;
}
/* check if memberuid is specified */
if (el) {
"Error: the memberuid attribute is readonly.");
return LDB_ERR_UNWILLING_TO_PERFORM;
}
if (!ctx) {
return LDB_ERR_OPERATIONS_ERROR;
}
if (!add_ctx) {
return LDB_ERR_OPERATIONS_ERROR;
}
return LDB_ERR_OPERATIONS_ERROR;
}
/* continue with normal ops if there are no members */
if (!el) {
goto done;
}
if (!parents) {
return LDB_ERR_OPERATIONS_ERROR;
}
return LDB_ERR_OPERATIONS_ERROR;
}
/* process new members */
/* check we are not adding ourselves as member as well */
for (i = 0; i < el->num_values; i++) {
return LDB_ERR_INVALID_DN_SYNTAX;
}
"Adding self as member is not permitted! Skipping");
continue;
}
if (ret != LDB_SUCCESS) {
return ret;
}
}
done:
/* add original object */
req);
if (ret != LDB_SUCCESS) {
return ret;
}
}
{
int ret;
if (!ares) {
}
}
case LDB_REPLY_ENTRY:
/* shouldn't happen */
case LDB_REPLY_REFERRAL:
/* ignore */
break;
case LDB_REPLY_DONE:
}
/* first operation */
}
/* next operation */
}
else {
/* no more operations */
}
}
else {
}
}
if (ret != LDB_SUCCESS) {
}
}
return LDB_SUCCESS;
}
{
DB_MEMBEROF, NULL };
int ret;
/* mark the operation as being handled */
if (ret != LDB_SUCCESS) {
return ret;
}
}
{
int ret;
if (!ares) {
}
}
case LDB_REPLY_ENTRY:
"Found multiple entries for (%s)",
/* more than one entry per dn ?? db corrupted ? */
}
}
break;
case LDB_REPLY_REFERRAL:
/* ignore */
break;
case LDB_REPLY_DONE:
/* this target does not exists, save as missing */
if (ret != LDB_SUCCESS) {
}
/* now try the next operation */
}
else {
/* no more operations */
}
}
else {
}
}
if (ret != LDB_SUCCESS) {
}
}
else {
if (ret != LDB_SUCCESS) {
}
}
return LDB_SUCCESS;
}
return LDB_SUCCESS;
}
/* if it is a group, add all members for cascade effect
* add memberof attribute to this entry
*/
{
int i, j, ret;
const char *val;
const char *name;
if (!parents) {
return LDB_ERR_OPERATIONS_ERROR;
}
/* can't be more than the immediate parent */
return LDB_ERR_OPERATIONS_ERROR;
}
/* create new parent set for this entry */
/* never add yourself as memberof */
continue;
}
}
/* remove entries that are already there */
if (el) {
if (!tmp_ctx) return LDB_ERR_OPERATIONS_ERROR;
for (i = 0; i < el->num_values; i++) {
if (!elval_dn) {
return LDB_ERR_OPERATIONS_ERROR;
}
/* duplicate found */
break;
}
}
/* remove duplicate */
}
}
}
/* already contains all parents as memberof, skip to next */
}
return mbof_add_muop(add_ctx);
}
else {
/* that was the last entry, get out */
}
}
}
/* if it is a group add all members */
if (el) {
for (i = 0; i < el->num_values; i++) {
if (!valdn) {
return LDB_ERR_OPERATIONS_ERROR;
}
if (!ldb_dn_validate(valdn)) {
"Invalid DN syntax for member [%s]",
return LDB_ERR_INVALID_DN_SYNTAX;
}
if (ret != LDB_SUCCESS) {
return ret;
}
}
}
/* check if we need to store memberuid ops for this entry */
switch (ret) {
case LDB_SUCCESS:
/* it's a user object */
if (!name) {
return LDB_ERR_OPERATIONS_ERROR;
}
if (ret != LDB_SUCCESS) {
return ret;
}
}
break;
/* it is not a user object, continue */
break;
default:
/* an error occurred, return */
return ret;
}
if (ret != LDB_SUCCESS) {
return ret;
}
/* we are done with the entry now */
/* add memberof to entry */
if (!msg) return LDB_ERR_OPERATIONS_ERROR;
if (ret != LDB_SUCCESS) {
return ret;
}
return LDB_ERR_OPERATIONS_ERROR;
}
return LDB_ERR_OPERATIONS_ERROR;
}
j++;
}
el->num_values = j;
if (ret != LDB_SUCCESS) {
return ret;
}
}
struct ldb_message *entry,
struct mbof_dn_array *parents)
{
/* No ghel attribute, just return success */
return LDB_SUCCESS;
}
}
{
if (!mdn) {
return LDB_ERR_OPERATIONS_ERROR;
}
/* add to the list */
return LDB_SUCCESS;
}
/* remove unexisting members and add memberuid attribute */
{
const char *val;
num = 0;
num++;
}
if (num == 0) {
return LDB_ERR_OPERATIONS_ERROR;
}
if (!msg) return LDB_ERR_OPERATIONS_ERROR;
if (ret != LDB_SUCCESS) {
return ret;
}
return LDB_ERR_OPERATIONS_ERROR;
}
return LDB_ERR_OPERATIONS_ERROR;
}
}
if (ret != LDB_SUCCESS) {
return ret;
}
}
{
int ret;
if (!ares) {
}
}
case LDB_REPLY_ENTRY:
/* shouldn't happen */
case LDB_REPLY_REFERRAL:
/* ignore */
break;
case LDB_REPLY_DONE:
}
else {
}
if (ret != LDB_SUCCESS) {
}
}
return LDB_SUCCESS;
}
/* add memberuid attributes to parent groups */
{
int ret;
if (!msg) return LDB_ERR_OPERATIONS_ERROR;
if (ret != LDB_SUCCESS) {
return ret;
}
false, NULL);
if (ret != LDB_SUCCESS) {
return ret;
}
}
{
int ret;
if (!ares) {
}
}
case LDB_REPLY_ENTRY:
/* shouldn't happen */
case LDB_REPLY_REFERRAL:
/* ignore */
break;
case LDB_REPLY_DONE:
}
else {
}
if (ret != LDB_SUCCESS) {
}
}
return LDB_SUCCESS;
}
/* delete operations */
/* The implementation of delete operations is a bit more complex than an add
* operation. This is because we need to recompute memberships of potentially
* quite far descendants and we also have to account for loops and how to
* break them without ending in an endless loop ourselves.
* The difficulty is in the fact that while the member -> memberof link is
* direct, memberof -> member is not as membership is transitive.
*
* Ok, first of all, contrary to the add operation, a delete operation
* involves an existing object that may have existing parents. So, first, we
* search the object itself to get the original membership lists (member and
* memberof) for this object, and we also search for any object that has it as
* one of its members.
* Once we have the results, we store object and parents and proceed with the
* original operation to make sure it is valid.
*
* Once the original op returns we proceed fixing parents (parents being each
* object that has the delete operation target object as member), if any.
*
* For each parent we retrieved we proceed to delete the member attribute that
* points to the object we just deleted. Once done for all parents (or if no
* parents exists), we proceed with the children and descendants.
*
* To handle the children we create a first ancestor operation that reflects
* the delete we just made. We set as parents of this object the parents just
* retrieved with the first search. Then we create a remove list.
*
* The remove list contains all objects in the original memberof list and the
* object dn itself of the original delete operation target object (the first
* ancestor).
*
* An operation is identified by an object that contains a tree of
* descendants:
* The remove list for the children, the immediate parent, and the dn and
* entry of the object this operation is about.
*
* We now proceed with adding a new operation for each original member of the
* first ancestor.
*
* In each operation we must first lookup the target object and each immediate
* parent (all the objects in the tree that have target as a "member").
*
* Then we proceed to calculate the new memberof list that we are going to set
* on the target object.
* The new memberof list starts with including all the objects that have the
* target as their direct member.
* Finally for each entry in this provisional new memberof list we add all its
* memberof elements to the new memberof list (taking care of excluding
* duplicates). This way we are certain all direct and indirect membership are
* accounted for.
*
* At this point we have the final new memberof list for this operation and we
* can proceed to modify the entry.
*
* Once the entry has been modified we proceed again to check if there are any
* children of this entry (the entry has "member"s).
* We create a new remove list that is the difference between the original
* entry memberof list and the new memberof list we just stored back in the
* object.
* Then for each member we create a new operation.
*
* We continue to process operations until no new operations need to be
* performed.
*
* Ordering is important here, se the mbof_del_get_next() function to
* understand how we proceed to select which new operation to process.
*
* As a final operation remove any memberuid corresponding to a removal of
* a memberof field from a user entry. Also if the original entry had a ghost
* attribute, we need to remove that attribute from all its parents as well.
*
* There is one catch though - at the memberof level, we can't know if the
* attribute being removed from a parent group is just inherited from the group
* being removed or also a direct member of the parent group. To make sure
* that the attribute is displayed next time the group is requested, we also
* set expire the parent group at the same time.
*/
struct mbof_del_operation **nextop);
struct ldb_message *entry);
struct ldb_message *entry);
{
char *expression;
const char *dn;
char *clean_dn;
int ret;
/* do not manipulate our control entries */
}
if (!ctx) {
return LDB_ERR_OPERATIONS_ERROR;
}
if (!del_ctx) {
return LDB_ERR_OPERATIONS_ERROR;
}
/* create first entry */
/* the first entry is the parent of all entries and the one where we remove
* member from, it does not get the same treatment as others */
if (!first) {
return LDB_ERR_OPERATIONS_ERROR;
}
if (!dn) {
return LDB_ERR_OPERATIONS_ERROR;
}
if (sret != 0) {
return LDB_ERR_OPERATIONS_ERROR;
}
"(|(distinguishedName=%s)(%s=%s))",
if (!expression) {
return LDB_ERR_OPERATIONS_ERROR;
}
req);
if (ret != LDB_SUCCESS) {
return ret;
}
}
{
int ret;
if (!ares) {
}
}
case LDB_REPLY_ENTRY:
/* more than one entry per dn ?? db corrupted ? */
}
}
} else {
struct ldb_message *,
}
if (!msg) {
}
first->num_parents++;
}
break;
case LDB_REPLY_REFERRAL:
/* ignore */
break;
case LDB_REPLY_DONE:
/* this target does not exists, too bad! */
"Target entry (%s) not found",
}
/* now perform the requested delete, before proceeding further */
if (ret != LDB_SUCCESS) {
}
}
return LDB_SUCCESS;
}
{
int ret;
if (ret != LDB_SUCCESS) {
return ret;
}
}
{
int ret;
if (!ares) {
}
}
}
/* save real call stuff */
/* prep following clean ops */
/* if there are parents there may be memberuids to remove */
if (ret != LDB_SUCCESS) {
}
/* ..or ghost attributes to remove */
if (ret != LDB_SUCCESS) {
}
/* if there are any parents, fire a removal sequence */
}
/* if there are any children, fire a removal sequence */
}
/* see if there are memberuid operations to perform */
return mbof_del_muop(del_ctx);
}
/* see if we need to remove some ghost users */
return mbof_del_ghop(del_ctx);
}
else {
/* no parents nor children, end ops */
}
if (ret != LDB_SUCCESS) {
}
return LDB_SUCCESS;
}
{
const char *val;
int ret;
if (!msg) return LDB_ERR_OPERATIONS_ERROR;
first->cur_parent++;
if (ret != LDB_SUCCESS) {
return ret;
}
return LDB_ERR_OPERATIONS_ERROR;
}
return LDB_ERR_OPERATIONS_ERROR;
}
if (ret != LDB_SUCCESS) {
return ret;
}
}
{
int ret;
if (!ares) {
}
}
}
/* still parents to cleanup, go on */
}
else {
/* continue */
/* if there are any children, fire a removal sequence */
}
/* see if there are memberuid operations to perform */
return mbof_del_muop(del_ctx);
}
/* see if we need to remove some ghost users */
return mbof_del_ghop(del_ctx);
}
else {
/* no children, end ops */
}
}
if (ret != LDB_SUCCESS) {
}
return LDB_SUCCESS;
}
{
int i, ret;
/* prepare del sets */
for (i = 0; i < el->num_values; i++) {
"Invalid dn syntax for member [%s]",
return LDB_ERR_INVALID_DN_SYNTAX;
}
if (ret != LDB_SUCCESS) {
return ret;
}
}
/* now that sets are built, start processing */
}
{
if (!delop) {
return LDB_ERR_OPERATIONS_ERROR;
}
struct mbof_del_operation *,
return LDB_ERR_OPERATIONS_ERROR;
}
parent->num_children++;
return LDB_SUCCESS;
}
{
char *expression;
const char *dn;
char *clean_dn;
int ret;
/* load entry */
if (!dn) {
return LDB_ERR_OPERATIONS_ERROR;
}
if (ret != 0) {
return LDB_ERR_OPERATIONS_ERROR;
}
"(|(distinguishedName=%s)(%s=%s))",
if (!expression) {
return LDB_ERR_OPERATIONS_ERROR;
}
if (ret != LDB_SUCCESS) {
return ret;
}
}
{
int ret;
if (!ares) {
}
}
case LDB_REPLY_ENTRY:
"Found multiple entries for (%s)",
/* more than one entry per dn ?? db corrupted ? */
}
}
} else {
struct ldb_message *,
}
if (!msg) {
}
delop->num_parents++;
}
break;
case LDB_REPLY_REFERRAL:
/* ignore */
break;
case LDB_REPLY_DONE:
/* no target, no party! */
}
/* ok process the entry */
if (ret != LDB_SUCCESS) {
}
}
return LDB_SUCCESS;
}
{
int i;
if (!anc_ctx) {
return LDB_ERR_OPERATIONS_ERROR;
}
if (!new_list) {
return LDB_ERR_OPERATIONS_ERROR;
}
/* at the very least we have a number of memberof elements
* equal to the number of objects that have this entry as
* direct member */
/* attach the list to the operation */
/* do we have any direct parent at all ? */
/* no entries at all, entry ended up being orphaned */
/* skip to directly set the new memberof list for this entry */
return mbof_del_mod_entry(delop);
}
/* fill in the list if we have parents */
struct ldb_dn *,
return LDB_ERR_OPERATIONS_ERROR;
}
for (i = 0; i < delop->num_parents; i++) {
}
/* before proceeding we also need to fetch the ancestors (anew as some may
* have changed by preceeding operations) */
return mbof_del_ancestors(delop);
}
{
int ret;
if (ret != LDB_SUCCESS) {
return ret;
}
}
{
int i, j, ret;
if (!ares) {
}
}
case LDB_REPLY_ENTRY:
"Found multiple entries for (%s)",
/* more than one entry per dn ?? db corrupted ? */
}
}
break;
case LDB_REPLY_REFERRAL:
/* ignore */
break;
case LDB_REPLY_DONE:
/* no target, no party! */
}
/* check entry */
if (el) {
for (i = 0; i < el->num_values; i++) {
if (!valdn) {
"Invalid dn for memberof: (%s)",
}
break;
}
continue;
}
/* do not re-add the original deleted entry by mistake */
continue;
}
struct ldb_dn *,
}
}
}
/* done with this one */
/* check if we need to process any more */
/* ok process the next one */
} else {
/* ok, end of the story, proceed to modify the entry */
}
if (ret != LDB_SUCCESS) {
}
}
return LDB_SUCCESS;
}
{
const char *name;
const char *val;
int i, j, k;
bool is_user;
int ret;
/* if this is a user we need to find out which entries have been
* removed so that we can later schedule removal of memberuid
* attributes from these entries */
switch (ret) {
case LDB_SUCCESS:
/* it's a user object */
is_user = true;
break;
/* it is not a user object, continue */
is_user = false;
break;
default:
/* an error occurred, return */
return ret;
}
if (is_user) {
/* prepare memberuid delete list */
/* copy all original memberof entries, and then later remove
* the ones that will survive in the entry */
return LDB_ERR_OPERATIONS_ERROR;
}
if (!diff) {
return LDB_ERR_OPERATIONS_ERROR;
}
for (i = 0, j = 0; i < el->num_values; i++) {
if (!diff[j]) {
return LDB_ERR_OPERATIONS_ERROR;
}
/* skip the deleted entry if this is a delete op */
continue;
}
}
j++;
}
/* zero terminate array */
}
/* change memberof on entry */
if (!msg) return LDB_ERR_OPERATIONS_ERROR;
if (ret != LDB_SUCCESS) {
return ret;
}
return LDB_ERR_OPERATIONS_ERROR;
}
continue;
if (!val) {
return LDB_ERR_OPERATIONS_ERROR;
}
return LDB_ERR_OPERATIONS_ERROR;
}
j++;
if (is_user) {
/* compare the entry's original memberof list with the new
* one and for each missing entry add a memberuid removal
* operation */
for (k = 0; diff[k]; k++) {
break;
}
}
if (diff[k]) {
talloc_zfree(diff[k]);
for (; diff[k + 1]; k++) {
}
}
}
}
el->num_values = j;
}
else {
if (ret != LDB_SUCCESS) {
return ret;
}
}
/* file memberuid removal operations */
if (!name) {
return LDB_ERR_OPERATIONS_ERROR;
}
for (i = 0; diff[i]; i++) {
if (ret != LDB_SUCCESS) {
return ret;
}
}
}
if (ret != LDB_SUCCESS) {
return ret;
}
}
{
int ret;
if (!ares) {
}
}
case LDB_REPLY_ENTRY:
/* shouldn't happen */
case LDB_REPLY_REFERRAL:
/* ignore */
break;
case LDB_REPLY_DONE:
if (ret != LDB_SUCCESS) {
}
}
return LDB_SUCCESS;
}
struct mbof_dn_array *ael,
struct mbof_val_array *addgh);
{
int i, ret;
/* now verify if this entry is a group and members need to be processed as
* well */
if (el) {
for (i = 0; i < el->num_values; i++) {
"Invalid DN for member: (%s)",
return LDB_ERR_INVALID_DN_SYNTAX;
}
if (ret != LDB_SUCCESS) {
return ret;
}
}
}
/* finally find the next entry to handle */
if (ret != LDB_SUCCESS) {
return ret;
}
if (nextop) {
return mbof_del_execute_op(nextop);
}
/* see if there are memberuid operations to perform */
return mbof_del_muop(del_ctx);
}
/* see if we need to remove some ghost users */
return mbof_del_ghop(del_ctx);
}
/* see if there are follow functions to run */
if (del_ctx->follow_mod) {
}
/* ok, no more ops, this means our job is done */
}
struct mbof_del_operation **nextop)
{
/* first of all, save the current delop in the history */
if (!save) {
return LDB_ERR_OPERATIONS_ERROR;
}
} else {
}
/* Find next one */
/* no children, go for next one */
continue;
}
top->next_child++;
/* verify this operation has not already been performed */
break;
}
}
/* and return the current one */
return LDB_SUCCESS;
}
}
}
/* we have no more ops */
return LDB_SUCCESS;
}
struct ldb_message *entry)
{
char *name;
int ret;
int i;
/* no memberof attributes ... */
return LDB_SUCCESS;
}
switch (ret) {
case LDB_SUCCESS:
/* it's a user object, continue */
break;
/* it is not a user object, just return */
return LDB_SUCCESS;
default:
/* an error occurred, return */
return ret;
}
if (!name) {
return LDB_ERR_OPERATIONS_ERROR;
}
for (i = 0; i < el->num_values; i++) {
"Invalid dn value: [%s]",
}
if (ret != LDB_SUCCESS) {
return ret;
}
}
return LDB_SUCCESS;
}
struct ldb_message *entry,
unsigned int num_gh_vals)
{
int ret;
int i, j;
/* no memberof attributes ... */
return LDB_SUCCESS;
}
switch (ret) {
case LDB_SUCCESS:
/* it's a group object, continue */
break;
/* it is not a group object, just return */
return LDB_SUCCESS;
default:
/* an error occurred, return */
return ret;
}
"will delete %d ghost users from %d parents\n",
for (i = 0; i < mbof->num_values; i++) {
"Invalid dn value: [%s]",
}
"processing ghosts in parent [%s]\n",
for (j = 0; j < num_gh_vals; j++) {
DB_GHOST);
if (ret != LDB_SUCCESS) {
return ret;
}
}
}
return LDB_SUCCESS;
}
struct ldb_message *entry)
{
/* No ghel attribute, just return success */
return LDB_SUCCESS;
}
}
/* del memberuid attributes from parent groups */
{
int ret;
if (!msg) return LDB_ERR_OPERATIONS_ERROR;
if (ret != LDB_SUCCESS) {
return ret;
}
}
{
int ret;
if (!ares) {
}
/* if the attribute was not present it means the db is not
* perfectly consistent but failing here is not useful
* anyway and missing entries cause no harm if we are trying
* to remove them anyway */
}
case LDB_REPLY_ENTRY:
/* shouldn't happen */
case LDB_REPLY_REFERRAL:
/* ignore */
break;
case LDB_REPLY_DONE:
}
/* see if we need to remove some ghost users */
return mbof_del_ghop(del_ctx);
}
/* see if there are follow functions to run */
else if (del_ctx->follow_mod) {
}
else {
}
if (ret != LDB_SUCCESS) {
}
}
return LDB_SUCCESS;
}
/* del ghost attributes from parent groups */
{
int ret;
if (!msg) return LDB_ERR_OPERATIONS_ERROR;
if (ret != LDB_SUCCESS) {
return ret;
}
/* Also expire any parent groups to force reloading direct members in
* case the ghost users we remove now were actually *also* direct members
* of the parent groups
*/
if (ret != LDB_SUCCESS) {
return ret;
}
if (ret != LDB_SUCCESS) {
return ret;
}
if (ret != LDB_SUCCESS) {
return ret;
}
}
{
int ret;
if (!ares) {
}
/* We must treat no such attribute as non-fatal b/c the entry
* might have been directly nested in the parent as well and
* updated with another replace operation.
*/
}
case LDB_REPLY_ENTRY:
/* shouldn't happen */
case LDB_REPLY_REFERRAL:
/* ignore */
break;
case LDB_REPLY_DONE:
}
/* see if there are follow functions to run */
else if (del_ctx->follow_mod) {
}
else {
}
if (ret != LDB_SUCCESS) {
}
}
return LDB_SUCCESS;
}
/* delop may carry on a lot of memory, so we need a function to clean up
* the payload without breaking the delop chain */
{
delop->num_parents = 0;
delop->cur_parent = 0;
}
/* mod operation */
/* A modify operation just implements either an add operation, or a delete
* operation or both (replace) in turn.
* One difference between a modify and a pure add or a pure delete is that
* the object is not created a new or not completely removed, but the setup just
* treats it in the same way children objects are treated in a pure add or delete
* operation. A list of appropriate parents and objects to modify is built, then
* we jump directly in the add or delete code.
* If both add and delete are necessary, delete operations are performed first
* and then a followup add operation is concatenated
*
* Another difference is the ghost users. Because of its semi-managed nature,
* the ghost attribute requires some special care. During a modify operation, the
* ghost attribute can be set to a new list. That list coming, from an
* application, would typically only include the direct ghost
* members. However, we want to keep both direct and indirect ghost members
* in the cache to be able to return them all in a single call. To solve
* that problem, we also iterate over members of the group being modified,
* collect all ghost entries and add them back in case the original modify
* operation wiped them out.
*/
struct ldb_message *entry,
const struct ldb_message_element *membel,
struct mbof_dn_array **_added,
struct mbof_dn_array **_removed);
struct ldb_message *entry,
const struct ldb_message_element *ghel,
const struct ldb_message_element *inherited,
struct mbof_val_array **_added,
struct mbof_val_array **_removed);
struct mbof_dn_array *del,
struct mbof_val_array *delgh);
struct ldb_context *ldb,
const struct ldb_message_element *el,
struct mbof_dn_array **dn_array);
unsigned int num_values,
struct mbof_val_array **val_array);
const struct ldb_message_element *el,
struct mbof_val_array **val_array);
{
int ret;
if (getenv("SSSD_UPGRADE_DB")) {
/* do not do anything during upgrade */
}
/* do not manipulate our control entries */
}
/* check if memberof is specified */
if (el) {
"Error: the memberof attribute is readonly.");
return LDB_ERR_UNWILLING_TO_PERFORM;
}
/* check if memberuid is specified */
if (el) {
"Error: the memberuid attribute is readonly.");
return LDB_ERR_UNWILLING_TO_PERFORM;
}
if (!ctx) {
return LDB_ERR_OPERATIONS_ERROR;
}
if (!mod_ctx) {
return LDB_ERR_OPERATIONS_ERROR;
}
return LDB_ERR_OPERATIONS_ERROR;
}
/* continue with normal ops if there are no members and no ghosts */
return mbof_orig_mod(mod_ctx);
}
/* can't do anything,
* must check first what's on the entry */
req);
if (ret != LDB_SUCCESS) {
return ret;
}
}
{
int ret;
if (!ares) {
}
}
case LDB_REPLY_ENTRY:
"Found multiple entries for (%s)",
/* more than one entry per dn ?? db corrupted ? */
}
}
break;
case LDB_REPLY_REFERRAL:
/* ignore */
break;
case LDB_REPLY_DONE:
/* this target does not exists, too bad! */
}
if (ret != LDB_SUCCESS) {
}
}
return LDB_SUCCESS;
}
{
int ret;
if (ret != LDB_SUCCESS) {
return ret;
}
return LDB_SUCCESS;
}
return LDB_ERR_OPERATIONS_ERROR;
}
if (ret != HASH_SUCCESS) {
return LDB_ERR_OPERATIONS_ERROR;
}
}
{
int ret;
char *expression;
char *clean_dn;
const char *dn;
if (!dn) {
return LDB_ERR_OPERATIONS_ERROR;
}
if (ret != 0) {
return LDB_ERR_OPERATIONS_ERROR;
}
"(&(%s=%s)(%s=%s))",
if (!expression) {
return LDB_ERR_OPERATIONS_ERROR;
}
NULL,
if (ret != LDB_SUCCESS) {
return ret;
}
}
{
int ret;
int i;
if (!ares) {
}
}
case LDB_REPLY_ENTRY:
if (!el) {
break;
}
for (i=0; i < el->num_values; i++) {
/* We already have this user. Don't re-add him */
continue;
}
return LDB_ERR_OPERATIONS_ERROR;
}
return LDB_ERR_OPERATIONS_ERROR;
}
if (ret != HASH_SUCCESS) {
return LDB_ERR_OPERATIONS_ERROR;
}
}
break;
case LDB_REPLY_REFERRAL:
/* ignore */
break;
case LDB_REPLY_DONE:
/* All the children are gathered, let's do the real
* modify operation
*/
if (ret != LDB_SUCCESS) {
}
break;
}
return LDB_SUCCESS;
}
{
int ret;
if (ret != LDB_SUCCESS) {
return ret;
}
}
{
int ret;
if (!ares) {
}
}
}
/* save real call stuff */
/* next step */
} else {
}
if (ret != LDB_SUCCESS) {
}
}
}
return LDB_SUCCESS;
}
{
int ret;
unsigned long num_values;
int i, j;
/* add back the inherited children to entry */
if (!msg) return LDB_ERR_OPERATIONS_ERROR;
/* We only inherit during replaces, so it's safe to only look
* at the replaced set
*/
if (ret != LDB_SUCCESS) {
return ret;
}
if (ret != HASH_SUCCESS) {
return LDB_ERR_OPERATIONS_ERROR;
}
return LDB_ERR_OPERATIONS_ERROR;
}
for (i = 0, j = 0; i < num_values; i++) {
if (dupval) {
continue;
}
return LDB_ERR_OPERATIONS_ERROR;
}
j++;
}
el->num_values = j;
if (el->num_values == 0) {
/* nothing to do */
/* We cannot modify element which has 0 values */
msg->num_elements = 0;
}
if (ret != LDB_SUCCESS) {
return ret;
}
}
{
int ret;
if (!ares) {
}
}
}
if (ret != LDB_SUCCESS) {
}
}
return LDB_SUCCESS;
}
{
int ret;
if (ret != LDB_SUCCESS) {
return ret;
}
if (ret != LDB_SUCCESS) {
return ret;
}
/* Process the operations */
/* if we have something to remove do it first */
}
/* if there is nothing to remove and we have stuff to add
* do it right away */
}
/* the replacement function resulted in a null op,
* nothing to do, return happily */
*done = true;
return LDB_SUCCESS;
}
struct ldb_context *ldb,
struct ldb_message *entry,
const struct ldb_message_element *membel,
struct mbof_dn_array **_added,
struct mbof_dn_array **_removed)
{
int i, j, ret;
if (!membel) {
/* Nothing to do.. */
return LDB_SUCCESS;
}
case LDB_FLAG_MOD_ADD:
if (ret != LDB_SUCCESS) {
return ret;
}
break;
case LDB_FLAG_MOD_DELETE:
if (membel->num_values == 0) {
} else {
}
if (!el) {
/* nothing to do really */
break;
}
if (ret != LDB_SUCCESS) {
return ret;
}
break;
case LDB_FLAG_MOD_REPLACE:
if (el) {
if (ret != LDB_SUCCESS) {
return ret;
}
}
if (el) {
if (ret != LDB_SUCCESS) {
return ret;
}
}
/* remove from arrays values that ended up unchanged */
break;
}
}
/* preexisting one, not removed, nor added */
}
}
i--;
}
}
}
break;
default:
return LDB_ERR_OPERATIONS_ERROR;
}
return LDB_SUCCESS;
}
struct ldb_message *entry,
const struct ldb_message_element *ghel,
const struct ldb_message_element *inherited,
struct mbof_val_array **_added,
struct mbof_val_array **_removed)
{
int i, j, ret;
if (!ghel) {
/* Nothing to do.. */
return LDB_SUCCESS;
}
/* no memberof attributes ... */
return LDB_SUCCESS;
}
case LDB_FLAG_MOD_ADD:
if (ret != LDB_SUCCESS) {
return ret;
}
break;
case LDB_FLAG_MOD_DELETE:
if (ghel->num_values == 0) {
} else {
}
if (!el) {
/* nothing to do really */
break;
}
if (ret != LDB_SUCCESS) {
return ret;
}
break;
case LDB_FLAG_MOD_REPLACE:
if (el) {
if (ret != LDB_SUCCESS) {
return ret;
}
}
if (el) {
if (ret != LDB_SUCCESS) {
return ret;
}
}
if (inherited) {
if (ret != LDB_SUCCESS) {
return ret;
}
}
/* remove from arrays values that ended up unchanged */
break;
}
}
/* preexisting one, not removed, nor added */
}
}
i--;
}
}
}
break;
default:
return LDB_ERR_OPERATIONS_ERROR;
}
return LDB_SUCCESS;
}
struct mbof_dn_array *ael,
struct mbof_val_array *addgh)
{
int i, ret;
/* all the parents + itself */
if (ret != LDB_SUCCESS) {
return ret;
}
if (!add_ctx) {
return LDB_ERR_OPERATIONS_ERROR;
}
/* Build the memberuid add op */
if (ret != LDB_SUCCESS) {
return ret;
}
}
/* Add itself to the list of the parents to also get the memberuid */
return LDB_ERR_OPERATIONS_ERROR;
}
/* Build the member-add array */
if (ret != LDB_SUCCESS) {
return ret;
}
}
}
return mbof_add_muop(add_ctx);
}
struct mbof_dn_array *del,
struct mbof_val_array *delgh)
{
int i, ret;
if (!del_ctx) {
return LDB_ERR_OPERATIONS_ERROR;
}
/* create first entry */
/* the first entry is the parent of all entries and the one where we
* remove member from, it does not get the same treatment as others */
if (!first) {
return LDB_ERR_OPERATIONS_ERROR;
}
/* add followup function if we also have stuff to add */
}
if (ret != LDB_SUCCESS) {
return ret;
}
}
/* prepare del sets */
if (ret != LDB_SUCCESS) {
return ret;
}
}
/* now that sets are built, start processing */
}
/* No member processing, just delete ghosts */
return mbof_del_ghop(del_ctx);
}
struct ldb_context *ldb,
const struct ldb_message_element *el,
struct mbof_dn_array **dn_array)
{
int i;
if (!ar) {
return LDB_ERR_OPERATIONS_ERROR;
}
return LDB_SUCCESS;
}
return LDB_ERR_OPERATIONS_ERROR;
}
return LDB_ERR_INVALID_DN_SYNTAX;
}
}
return LDB_SUCCESS;
}
unsigned int num_values,
struct mbof_val_array **val_array)
{
int i, vi;
if (!var) {
return LDB_ERR_OPERATIONS_ERROR;
}
}
return LDB_SUCCESS;
}
/* We do not care about duplicate values now.
* They will be filtered later */
return LDB_ERR_OPERATIONS_ERROR;
}
/* FIXME - use ldb_val_dup() */
for (i = 0; i < num_values; i++) {
return LDB_ERR_OPERATIONS_ERROR;
}
vi++;
}
return LDB_SUCCESS;
}
const struct ldb_message_element *el,
struct mbof_val_array **val_array)
{
return LDB_SUCCESS;
}
}
/*************************
* Cleanup task routines *
*************************/
struct mbof_member {
const char *name;
bool orig_has_memberof;
bool orig_has_memberuid;
enum { MBOF_GROUP_TO_DO = 0,
};
struct mbof_rcmp_context {
};
const char *name,
struct ldb_message *msg,
struct ldb_message_element **_dest)
{
if (!src) {
return LDB_ERR_NO_SUCH_ATTRIBUTE;
}
if (!dest) {
return LDB_ERR_OPERATIONS_ERROR;
}
return LDB_SUCCESS;
}
struct mbof_member *parent,
struct mbof_member *mem);
struct ldb_request *req)
{
int ret;
if (!ctx) {
return LDB_ERR_OPERATIONS_ERROR;
}
if (ret != HASH_SUCCESS) {
return LDB_ERR_OPERATIONS_ERROR;
}
if (ret != LDB_SUCCESS) {
return ret;
}
}
{
const char *name;
int ret;
if (!ares) {
}
}
case LDB_REPLY_ENTRY:
if (!usr) {
}
if (name) {
}
usr->orig_has_memberof = true;
}
if (ret != HASH_SUCCESS) {
}
break;
case LDB_REPLY_REFERRAL:
/* ignore */
break;
case LDB_REPLY_DONE:
/* and now search groups */
return mbof_rcmp_search_groups(ctx);
}
return LDB_SUCCESS;
}
{
int ret;
if (ret != HASH_SUCCESS) {
}
if (ret != LDB_SUCCESS) {
return ret;
}
}
{
const char *name;
int i, j;
int ret;
if (!ares) {
}
}
case LDB_REPLY_ENTRY:
if (!grp) {
}
if (name) {
}
grp->orig_has_memberof = true;
}
grp->orig_has_memberuid = true;
}
}
if (ret != HASH_SUCCESS) {
}
break;
case LDB_REPLY_REFERRAL:
/* ignore */
break;
case LDB_REPLY_DONE:
if (!ctx->group_list) {
/* no groups ? */
}
/* for each group compute the members list */
/* no members */
continue;
}
/* we have at most num_values group members */
}
for (i = 0, j = 0; i < el->num_values; i++) {
switch (ret) {
case HASH_SUCCESS:
j++;
break;
case HASH_ERROR_KEY_NOT_FOUND:
/* not a user, see if it is a group */
if (ret != HASH_SUCCESS) {
if (ret != HASH_ERROR_KEY_NOT_FOUND) {
}
}
if (ret == HASH_ERROR_KEY_NOT_FOUND) {
/* not a known user, nor a known group ?
give a warning an continue */
"member attribute [%s] has no corresponding"
break;
}
j++;
break;
default:
}
}
/* terminate */
}
/* now generate correct memberof tables */
/* move to end of list and mark as done.
* NOTE: this is not efficient, but will do for now */
/* verify if members need updating */
continue;
}
if (ret != LDB_SUCCESS) {
}
}
}
/* ok all done, now go on and modify the tree */
return mbof_rcmp_update(ctx);
}
return LDB_SUCCESS;
}
struct mbof_member *parent,
struct mbof_member *mem)
{
int ret;
/* ignore loops */
if (ret != HASH_SUCCESS) {
return LDB_ERR_OPERATIONS_ERROR;
}
} else {
if (ret != HASH_SUCCESS) {
if (ret != HASH_ERROR_KEY_NOT_FOUND) {
/* fatal error */
return LDB_ERR_OPERATIONS_ERROR;
}
}
}
if (ret == HASH_ERROR_KEY_NOT_FOUND) {
/* it's missing, update member */
if (ret != HASH_SUCCESS) {
return LDB_ERR_OPERATIONS_ERROR;
}
/* add corresponding memuid to the group */
if (ret != LDB_SUCCESS) {
return ret;
}
}
/* if we updated a group, mark it as TO DO again */
}
}
/* now see if the parent has memberofs to pass down */
if (ret != HASH_SUCCESS) {
return LDB_ERR_OPERATIONS_ERROR;
}
return LDB_ERR_OPERATIONS_ERROR;
}
}
/* finally, if it was made TO DO move it to the head */
}
return LDB_SUCCESS;
}
{
int ret;
/* exclude self */
return true;
}
/* check if we already have it */
if (ret != HASH_SUCCESS) {
if (ret != HASH_ERROR_KEY_NOT_FOUND) {
/* fatal error */
return false;
}
/* was not already here, add it and mark group as TO DO */
if (ret != HASH_SUCCESS) {
return LDB_ERR_OPERATIONS_ERROR;
}
}
/* add corresponding memuid to the group */
if (ret != LDB_SUCCESS) {
return false;
}
}
}
return true;
}
{
int n;
return LDB_ERR_OPERATIONS_ERROR;
}
return LDB_ERR_OPERATIONS_ERROR;
}
}
struct ldb_val, n + 1);
if (!vals) {
return LDB_ERR_OPERATIONS_ERROR;
}
return LDB_SUCCESS;
}
{
struct mbof_member *x = NULL;
unsigned long count;
int flags;
int ret, i;
/* we process all users first and then all groups */
/* take the next entry and remove it from the list */
}
else if (ctx->group_list) {
/* take the next entry and remove it from the list */
x = ctx->group_list;
}
else {
/* processing terminated, return */
ret = LDB_SUCCESS;
goto done;
}
if (!msg) {
goto done;
}
/* process memberof */
if (x->memberofs) {
if (ret != HASH_SUCCESS) {
goto done;
}
if (x->orig_has_memberof) {
} else {
}
if (ret != LDB_SUCCESS) {
goto done;
}
goto done;
}
for (i = 0; i < count; i++) {
}
} else if (x->orig_has_memberof) {
if (ret != LDB_SUCCESS) {
goto done;
}
}
/* process memberuid */
if (x->memuids) {
if (x->orig_has_memberuid) {
} else {
}
if (ret != LDB_SUCCESS) {
goto done;
}
}
else if (x->orig_has_memberuid) {
if (ret != LDB_SUCCESS) {
goto done;
}
}
if (ret != LDB_SUCCESS) {
goto done;
}
/* fire next call */
done:
/* all users and groups have been processed */
}
{
if (!ares) {
}
}
case LDB_REPLY_ENTRY:
/* shouldn't happen */
case LDB_REPLY_REFERRAL:
/* ignore */
break;
case LDB_REPLY_DONE:
/* update the next one */
return mbof_rcmp_update(ctx);
}
return LDB_SUCCESS;
}
/* module init code */
{
int ret;
/* set syntaxes for member and memberof so that comparisons in filters and
* such are done right */
if (ret != 0) return LDB_ERR_OPERATIONS_ERROR;
if (ret != 0) return LDB_ERR_OPERATIONS_ERROR;
return ldb_next_init(module);
}
.name = "memberof",
.add = memberof_add,
.modify = memberof_mod,
.del = memberof_del,
};
{
#if defined(SSS_LDB_VERSION_CHECK) && defined(LDB_MODULE_CHECK_VERSION)
#endif /* SSS_LDB_VERSION_CHECK && LDB_MODULE_CHECK_VERSION */
return ldb_register_module(&ldb_memberof_module_ops);
}