~ubuntu-branches/ubuntu/saucy/sssd/saucy

« back to all changes in this revision

Viewing changes to src/ldb_modules/memberof.c

  • Committer: Stéphane Graber
  • Date: 2011-06-15 16:23:14 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: stgraber@ubuntu.com-20110615162314-rbhoppnpaxfqo5q7
Merge 1.5.8

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
   SSSD memberof module
 
3
 
 
4
   Copyright (C) Simo Sorce <idra@samba.org> 2008-2011
 
5
 
 
6
   This program is free software; you can redistribute it and/or modify
 
7
   it under the terms of the GNU General Public License as published by
 
8
   the Free Software Foundation; either version 3 of the License, or
 
9
   (at your option) any later version.
 
10
 
 
11
   This program is distributed in the hope that it will be useful,
 
12
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
   GNU General Public License for more details.
 
15
 
 
16
   You should have received a copy of the GNU General Public License
 
17
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
18
*/
 
19
 
 
20
/* Temporary workaround, will be fixed in ldb upstream soon */
 
21
#ifndef LDB_VERSION
 
22
#define LDB_VERSION "0.9.22"
 
23
#endif
 
24
 
 
25
#include <string.h>
 
26
#include "ldb_module.h"
 
27
#include "util/util.h"
 
28
#include "dhash.h"
 
29
 
 
30
#define DB_MEMBER "member"
 
31
#define DB_MEMBEROF "memberof"
 
32
#define DB_MEMBERUID "memberuid"
 
33
#define DB_NAME "name"
 
34
#define DB_USER_CLASS "user"
 
35
#define DB_OC "objectClass"
 
36
 
 
37
#ifndef talloc_zfree
 
38
#define talloc_zfree(ptr) do { talloc_free(ptr); ptr = NULL; } while(0)
 
39
#endif
 
40
 
 
41
#ifndef MAX
 
42
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
 
43
#endif
 
44
 
 
45
struct mbof_dn_array {
 
46
    struct ldb_dn **dns;
 
47
    int num;
 
48
};
 
49
 
 
50
struct mbof_dn {
 
51
    struct mbof_dn *next;
 
52
    struct ldb_dn *dn;
 
53
};
 
54
 
 
55
struct mbof_ctx {
 
56
    struct ldb_module *module;
 
57
    struct ldb_request *req;
 
58
 
 
59
    struct ldb_control **ret_ctrls;
 
60
    struct ldb_extended *ret_resp;
 
61
};
 
62
 
 
63
struct mbof_add_operation {
 
64
    struct mbof_add_ctx *add_ctx;
 
65
    struct mbof_add_operation *next;
 
66
 
 
67
    struct mbof_dn_array *parents;
 
68
    struct ldb_dn *entry_dn;
 
69
 
 
70
    struct ldb_message *entry;
 
71
};
 
72
 
 
73
struct mbof_memberuid_op {
 
74
    struct ldb_dn *dn;
 
75
    struct ldb_message_element *el;
 
76
};
 
77
 
 
78
struct mbof_add_ctx {
 
79
    struct mbof_ctx *ctx;
 
80
 
 
81
    struct mbof_add_operation *add_list;
 
82
    struct mbof_add_operation *current_op;
 
83
 
 
84
    struct ldb_message *msg;
 
85
    struct ldb_dn *msg_dn;
 
86
    bool terminate;
 
87
 
 
88
    struct mbof_dn *missing;
 
89
 
 
90
    struct mbof_memberuid_op *muops;
 
91
    int num_muops;
 
92
    int cur_muop;
 
93
};
 
94
 
 
95
struct mbof_del_ancestors_ctx {
 
96
    struct mbof_dn_array *new_list;
 
97
    int num_direct;
 
98
    int cur;
 
99
 
 
100
    struct ldb_message *entry;
 
101
};
 
102
 
 
103
struct mbof_del_operation {
 
104
    struct mbof_del_ctx *del_ctx;
 
105
    struct mbof_del_operation *parent;
 
106
    struct mbof_del_operation **children;
 
107
    int num_children;
 
108
    int next_child;
 
109
 
 
110
    struct ldb_dn *entry_dn;
 
111
 
 
112
    struct ldb_message *entry;
 
113
    struct ldb_message **parents;
 
114
    int num_parents;
 
115
    int cur_parent;
 
116
 
 
117
    struct mbof_del_ancestors_ctx *anc_ctx;
 
118
};
 
119
 
 
120
struct mbof_mod_ctx;
 
121
 
 
122
struct mbof_del_ctx {
 
123
    struct mbof_ctx *ctx;
 
124
 
 
125
    struct mbof_del_operation *first;
 
126
    struct mbof_dn *history;
 
127
 
 
128
    struct ldb_message **mus;
 
129
    int num_mus;
 
130
 
 
131
    struct mbof_memberuid_op *muops;
 
132
    int num_muops;
 
133
    int cur_muop;
 
134
 
 
135
    struct mbof_mod_ctx *follow_mod;
 
136
    bool is_mod;
 
137
};
 
138
 
 
139
struct mbof_mod_ctx {
 
140
    struct mbof_ctx *ctx;
 
141
 
 
142
    const struct ldb_message_element *membel;
 
143
    struct ldb_message *entry;
 
144
 
 
145
    struct mbof_dn_array *to_add;
 
146
 
 
147
    struct ldb_message *msg;
 
148
    bool terminate;
 
149
};
 
150
 
 
151
static struct mbof_ctx *mbof_init(struct ldb_module *module,
 
152
                                  struct ldb_request *req)
 
153
{
 
154
    struct mbof_ctx *ctx;
 
155
 
 
156
    ctx = talloc_zero(req, struct mbof_ctx);
 
157
    if (!ctx) {
 
158
        return NULL;
 
159
    }
 
160
 
 
161
    ctx->module = module;
 
162
    ctx->req = req;
 
163
 
 
164
    return ctx;
 
165
}
 
166
 
 
167
static int entry_is_user_object(struct ldb_message *entry)
 
168
{
 
169
    struct ldb_message_element *el;
 
170
    struct ldb_val *val;
 
171
    int i;
 
172
 
 
173
    el = ldb_msg_find_element(entry, DB_OC);
 
174
    if (!el) {
 
175
        return LDB_ERR_OPERATIONS_ERROR;
 
176
    }
 
177
 
 
178
    /* see if this is a user */
 
179
    for (i = 0; i < el->num_values; i++) {
 
180
        val = &(el->values[i]);
 
181
        if (strncasecmp(DB_USER_CLASS, (char *)val->data, val->length) == 0) {
 
182
            return LDB_SUCCESS;
 
183
        }
 
184
    }
 
185
 
 
186
    return LDB_ERR_NO_SUCH_ATTRIBUTE;
 
187
}
 
188
 
 
189
static int mbof_append_muop(TALLOC_CTX *memctx,
 
190
                            struct mbof_memberuid_op **_muops,
 
191
                            int *_num_muops,
 
192
                            int flags,
 
193
                            struct ldb_dn *parent,
 
194
                            const char *name)
 
195
{
 
196
    struct mbof_memberuid_op *muops = *_muops;
 
197
    int num_muops = *_num_muops;
 
198
    struct mbof_memberuid_op *op;
 
199
    struct ldb_val *val;
 
200
    int i;
 
201
 
 
202
    op = NULL;
 
203
    if (muops) {
 
204
        for (i = 0; i < num_muops; i++) {
 
205
            if (ldb_dn_compare(parent, muops[i].dn) == 0) {
 
206
                op = &muops[i];
 
207
                break;
 
208
            }
 
209
        }
 
210
    }
 
211
    if (!op) {
 
212
        muops = talloc_realloc(memctx, muops,
 
213
                               struct mbof_memberuid_op,
 
214
                               num_muops + 1);
 
215
        if (!muops) {
 
216
            return LDB_ERR_OPERATIONS_ERROR;
 
217
        }
 
218
        op = &muops[num_muops];
 
219
        num_muops++;
 
220
        *_muops = muops;
 
221
        *_num_muops = num_muops;
 
222
 
 
223
        op->dn = parent;
 
224
        op->el = NULL;
 
225
    }
 
226
 
 
227
    if (!op->el) {
 
228
        op->el = talloc_zero(muops, struct ldb_message_element);
 
229
        if (!op->el) {
 
230
            return LDB_ERR_OPERATIONS_ERROR;
 
231
        }
 
232
        op->el->name = talloc_strdup(op->el, DB_MEMBERUID);
 
233
        if (!op->el->name) {
 
234
            return LDB_ERR_OPERATIONS_ERROR;
 
235
        }
 
236
        op->el->flags = flags;
 
237
    }
 
238
 
 
239
    for (i = 0; i < op->el->num_values; i++) {
 
240
        if (strcmp((char *)op->el->values[i].data, name) == 0) {
 
241
            /* we already have this value, get out*/
 
242
            return LDB_SUCCESS;
 
243
        }
 
244
    }
 
245
 
 
246
    val = talloc_realloc(op->el, op->el->values,
 
247
                         struct ldb_val, op->el->num_values + 1);
 
248
    if (!val) {
 
249
        return LDB_ERR_OPERATIONS_ERROR;
 
250
    }
 
251
    val[op->el->num_values].data = (uint8_t *)talloc_strdup(val, name);
 
252
    if (!val[op->el->num_values].data) {
 
253
        return LDB_ERR_OPERATIONS_ERROR;
 
254
    }
 
255
    val[op->el->num_values].length = strlen(name);
 
256
 
 
257
    op->el->values = val;
 
258
    op->el->num_values++;
 
259
 
 
260
    return LDB_SUCCESS;
 
261
}
 
262
 
 
263
 
 
264
/* add operation */
 
265
 
 
266
/* An add operation is quite simple.
 
267
 * First of all a new object cannot yet have parents, so the only memberof
 
268
 * attribute that can be added to any member contains just one object DN.
 
269
 *
 
270
 * The real add operation is done first, to assure nothing else fails.
 
271
 * Then we list all members of the object just created, and for each member
 
272
 * we create an "add operation" and we pass it a parent list of one member
 
273
 * (the object we just added again).
 
274
 *
 
275
 * For each add operation we lookup the object we want to operate on.
 
276
 * We take the list of memberof attributes and sort out which parents are
 
277
 * still missing from the parent list we have provided.
 
278
 * We modify the object memberof attributes to reflect the new memberships.
 
279
 * Then we list all members of this object, and for each once again we create
 
280
 * an "add operation" as we did in the initial object.
 
281
 *
 
282
 * Processing stops when the target object does not have members or when it
 
283
 * already has all the parents (can happen if nested groups create loops).
 
284
 *
 
285
 * Group cache unrolling:
 
286
 * Every time we add a memberof attribute to an actual user object,
 
287
 * we proceed to store the user name.
 
288
 *
 
289
 * At the end we will add a memberuid attribute to our new object that
 
290
 * includes all direct and indeirect user members names.
 
291
 */
 
292
 
 
293
static int mbof_append_addop(struct mbof_add_ctx *add_ctx,
 
294
                             struct mbof_dn_array *parents,
 
295
                             struct ldb_dn *entry_dn)
 
296
{
 
297
    struct mbof_add_operation *lastop = NULL;
 
298
    struct mbof_add_operation *addop;
 
299
 
 
300
    /* test if this is a duplicate */
 
301
    /* FIXME: this is not efficient */
 
302
    if (add_ctx->add_list) {
 
303
        do {
 
304
            if (lastop) {
 
305
                lastop = lastop->next;
 
306
            } else {
 
307
                lastop = add_ctx->add_list;
 
308
            }
 
309
 
 
310
            /* FIXME: check if this is right, might have to compare parents */
 
311
            if (ldb_dn_compare(lastop->entry_dn, entry_dn) == 0) {
 
312
                /* duplicate found */
 
313
                return LDB_SUCCESS;
 
314
            }
 
315
        } while (lastop->next);
 
316
    }
 
317
 
 
318
    addop = talloc_zero(add_ctx, struct mbof_add_operation);
 
319
    if (!addop) {
 
320
        return LDB_ERR_OPERATIONS_ERROR;
 
321
    }
 
322
 
 
323
    addop->add_ctx = add_ctx;
 
324
    addop->parents = parents;
 
325
    addop->entry_dn = entry_dn;
 
326
 
 
327
    if (add_ctx->add_list) {
 
328
        lastop->next = addop;
 
329
    } else {
 
330
        add_ctx->add_list = addop;
 
331
    }
 
332
 
 
333
    return LDB_SUCCESS;
 
334
}
 
335
 
 
336
static int memberof_recompute_task(struct ldb_module *module,
 
337
                                   struct ldb_request *req);
 
338
 
 
339
static int mbof_add_callback(struct ldb_request *req,
 
340
                             struct ldb_reply *ares);
 
341
static int mbof_next_add(struct mbof_add_operation *addop);
 
342
static int mbof_next_add_callback(struct ldb_request *req,
 
343
                                  struct ldb_reply *ares);
 
344
static int mbof_add_operation(struct mbof_add_operation *addop);
 
345
static int mbof_add_missing(struct mbof_add_ctx *add_ctx, struct ldb_dn *dn);
 
346
static int mbof_add_cleanup(struct mbof_add_ctx *add_ctx);
 
347
static int mbof_add_cleanup_callback(struct ldb_request *req,
 
348
                                     struct ldb_reply *ares);
 
349
static int mbof_add_muop(struct mbof_add_ctx *add_ctx);
 
350
static int mbof_add_muop_callback(struct ldb_request *req,
 
351
                                  struct ldb_reply *ares);
 
352
 
 
353
static int memberof_add(struct ldb_module *module, struct ldb_request *req)
 
354
{
 
355
    struct ldb_context *ldb = ldb_module_get_ctx(module);
 
356
    struct mbof_add_ctx *add_ctx;
 
357
    struct mbof_ctx *ctx;
 
358
    struct ldb_request *add_req;
 
359
    struct ldb_message_element *el;
 
360
    struct mbof_dn_array *parents;
 
361
    struct ldb_dn *valdn;
 
362
    int i, ret;
 
363
 
 
364
    if (ldb_dn_is_special(req->op.add.message->dn)) {
 
365
 
 
366
        if (strcmp("@MEMBEROF-REBUILD",
 
367
                   ldb_dn_get_linearized(req->op.add.message->dn)) == 0) {
 
368
            return memberof_recompute_task(module, req);
 
369
        }
 
370
 
 
371
        /* do not manipulate other control entries */
 
372
        return ldb_next_request(module, req);
 
373
    }
 
374
 
 
375
    /* check if memberof is specified */
 
376
    el = ldb_msg_find_element(req->op.add.message, DB_MEMBEROF);
 
377
    if (el) {
 
378
        ldb_debug(ldb, LDB_DEBUG_ERROR,
 
379
                  "Error: the memberof attribute is readonly.");
 
380
        return LDB_ERR_UNWILLING_TO_PERFORM;
 
381
    }
 
382
 
 
383
    /* check if memberuid is specified */
 
384
    el = ldb_msg_find_element(req->op.add.message, DB_MEMBERUID);
 
385
    if (el) {
 
386
        ldb_debug(ldb, LDB_DEBUG_ERROR,
 
387
                  "Error: the memberuid attribute is readonly.");
 
388
        return LDB_ERR_UNWILLING_TO_PERFORM;
 
389
    }
 
390
 
 
391
    ctx = mbof_init(module, req);
 
392
    if (!ctx) {
 
393
        return LDB_ERR_OPERATIONS_ERROR;
 
394
    }
 
395
 
 
396
    add_ctx = talloc_zero(ctx, struct mbof_add_ctx);
 
397
    if (!add_ctx) {
 
398
        return LDB_ERR_OPERATIONS_ERROR;
 
399
    }
 
400
    add_ctx->ctx = ctx;
 
401
 
 
402
    add_ctx->msg = ldb_msg_copy(add_ctx, req->op.add.message);
 
403
    if (!add_ctx->msg) {
 
404
        return LDB_ERR_OPERATIONS_ERROR;
 
405
    }
 
406
    add_ctx->msg_dn = add_ctx->msg->dn;
 
407
 
 
408
    /* continue with normal ops if there are no members */
 
409
    el = ldb_msg_find_element(add_ctx->msg, DB_MEMBER);
 
410
    if (!el) {
 
411
        add_ctx->terminate = true;
 
412
        goto done;
 
413
    }
 
414
 
 
415
    parents = talloc_zero(add_ctx, struct mbof_dn_array);
 
416
    if (!parents) {
 
417
        return LDB_ERR_OPERATIONS_ERROR;
 
418
    }
 
419
    parents->dns = talloc_array(parents, struct ldb_dn *, 1);
 
420
    if (!parents->dns) {
 
421
        return LDB_ERR_OPERATIONS_ERROR;
 
422
    }
 
423
    parents->dns[0] = add_ctx->msg_dn;
 
424
    parents->num = 1;
 
425
 
 
426
    /* process new members */
 
427
    /* check we are not adding ourselves as member as well */
 
428
    for (i = 0; i < el->num_values; i++) {
 
429
        valdn = ldb_dn_from_ldb_val(add_ctx, ldb, &el->values[i]);
 
430
        if (!valdn || !ldb_dn_validate(valdn)) {
 
431
            ldb_debug(ldb, LDB_DEBUG_ERROR, "Invalid dn value: [%s]",
 
432
                                            (const char *)el->values[i].data);
 
433
            return LDB_ERR_INVALID_DN_SYNTAX;
 
434
        }
 
435
        if (ldb_dn_compare(valdn, req->op.add.message->dn) == 0) {
 
436
            ldb_debug(ldb, LDB_DEBUG_ERROR,
 
437
                      "Adding self as member is not permitted! Skipping");
 
438
            continue;
 
439
        }
 
440
        ret = mbof_append_addop(add_ctx, parents, valdn);
 
441
        if (ret != LDB_SUCCESS) {
 
442
            return ret;
 
443
        }
 
444
    }
 
445
 
 
446
done:
 
447
    /* add original object */
 
448
    ret = ldb_build_add_req(&add_req, ldb, add_ctx,
 
449
                            add_ctx->msg, req->controls,
 
450
                            add_ctx, mbof_add_callback,
 
451
                            req);
 
452
    if (ret != LDB_SUCCESS) {
 
453
        return ret;
 
454
    }
 
455
 
 
456
    return ldb_next_request(module, add_req);
 
457
}
 
458
 
 
459
static int mbof_add_callback(struct ldb_request *req,
 
460
                             struct ldb_reply *ares)
 
461
{
 
462
    struct mbof_add_ctx *add_ctx;
 
463
    struct mbof_ctx *ctx;
 
464
    int ret;
 
465
 
 
466
    add_ctx = talloc_get_type(req->context, struct mbof_add_ctx);
 
467
    ctx = add_ctx->ctx;
 
468
 
 
469
    if (!ares) {
 
470
        return ldb_module_done(ctx->req, NULL, NULL,
 
471
                               LDB_ERR_OPERATIONS_ERROR);
 
472
    }
 
473
    if (ares->error != LDB_SUCCESS) {
 
474
        return ldb_module_done(ctx->req,
 
475
                               ares->controls,
 
476
                               ares->response,
 
477
                               ares->error);
 
478
    }
 
479
 
 
480
    switch (ares->type) {
 
481
    case LDB_REPLY_ENTRY:
 
482
        /* shouldn't happen */
 
483
        talloc_zfree(ares);
 
484
        return ldb_module_done(ctx->req, NULL, NULL,
 
485
                               LDB_ERR_OPERATIONS_ERROR);
 
486
    case LDB_REPLY_REFERRAL:
 
487
        /* ignore */
 
488
        break;
 
489
 
 
490
    case LDB_REPLY_DONE:
 
491
        if (add_ctx->terminate) {
 
492
            return ldb_module_done(ctx->req,
 
493
                                   ctx->ret_ctrls,
 
494
                                   ctx->ret_resp,
 
495
                                   LDB_SUCCESS);
 
496
        }
 
497
 
 
498
        if (add_ctx->current_op == NULL) {
 
499
            /* first operation */
 
500
            ctx->ret_ctrls = talloc_steal(ctx, ares->controls);
 
501
            ctx->ret_resp = talloc_steal(ctx, ares->response);
 
502
            ret = mbof_next_add(add_ctx->add_list);
 
503
        }
 
504
        else if (add_ctx->current_op->next) {
 
505
            /* next operation */
 
506
            ret = mbof_next_add(add_ctx->current_op->next);
 
507
        }
 
508
        else {
 
509
            /* no more operations */
 
510
            if (add_ctx->missing) {
 
511
                ret = mbof_add_cleanup(add_ctx);
 
512
            }
 
513
            else if (add_ctx->muops) {
 
514
                ret = mbof_add_muop(add_ctx);
 
515
            }
 
516
            else {
 
517
                return ldb_module_done(ctx->req,
 
518
                                       ctx->ret_ctrls,
 
519
                                       ctx->ret_resp,
 
520
                                       LDB_SUCCESS);
 
521
            }
 
522
        }
 
523
 
 
524
        if (ret != LDB_SUCCESS) {
 
525
            talloc_zfree(ares);
 
526
            return ldb_module_done(ctx->req, NULL, NULL, ret);
 
527
        }
 
528
    }
 
529
 
 
530
    talloc_zfree(ares);
 
531
    return LDB_SUCCESS;
 
532
}
 
533
 
 
534
static int mbof_next_add(struct mbof_add_operation *addop)
 
535
{
 
536
    static const char *attrs[] = { DB_OC, DB_NAME,
 
537
                                   DB_MEMBER, DB_MEMBEROF, NULL };
 
538
    struct ldb_context *ldb;
 
539
    struct ldb_request *req;
 
540
    struct mbof_add_ctx *add_ctx;
 
541
    struct mbof_ctx *ctx;
 
542
    int ret;
 
543
 
 
544
    add_ctx = addop->add_ctx;
 
545
    ctx = add_ctx->ctx;
 
546
    ldb = ldb_module_get_ctx(ctx->module);
 
547
 
 
548
    /* mark the operation as being handled */
 
549
    add_ctx->current_op = addop;
 
550
 
 
551
    ret = ldb_build_search_req(&req, ldb, ctx,
 
552
                               addop->entry_dn, LDB_SCOPE_BASE,
 
553
                               NULL, attrs, NULL,
 
554
                               addop, mbof_next_add_callback,
 
555
                               ctx->req);
 
556
    if (ret != LDB_SUCCESS) {
 
557
        return ret;
 
558
    }
 
559
 
 
560
    return ldb_request(ldb, req);
 
561
}
 
562
 
 
563
static int mbof_next_add_callback(struct ldb_request *req,
 
564
                                  struct ldb_reply *ares)
 
565
{
 
566
    struct mbof_add_operation *addop;
 
567
    struct mbof_add_ctx *add_ctx;
 
568
    struct ldb_context *ldb;
 
569
    struct mbof_ctx *ctx;
 
570
    int ret;
 
571
 
 
572
    addop = talloc_get_type(req->context, struct mbof_add_operation);
 
573
    add_ctx = addop->add_ctx;
 
574
    ctx = add_ctx->ctx;
 
575
    ldb = ldb_module_get_ctx(ctx->module);
 
576
 
 
577
    if (!ares) {
 
578
        return ldb_module_done(ctx->req, NULL, NULL,
 
579
                               LDB_ERR_OPERATIONS_ERROR);
 
580
    }
 
581
    if (ares->error != LDB_SUCCESS) {
 
582
        return ldb_module_done(ctx->req,
 
583
                               ares->controls,
 
584
                               ares->response,
 
585
                               ares->error);
 
586
    }
 
587
 
 
588
    switch (ares->type) {
 
589
    case LDB_REPLY_ENTRY:
 
590
        if (addop->entry != NULL) {
 
591
            ldb_debug(ldb, LDB_DEBUG_TRACE,
 
592
                           "Found multiple entries for (%s)",
 
593
                           ldb_dn_get_linearized(addop->entry_dn));
 
594
            /* more than one entry per dn ?? db corrupted ? */
 
595
            return ldb_module_done(ctx->req, NULL, NULL,
 
596
                                   LDB_ERR_OPERATIONS_ERROR);
 
597
        }
 
598
 
 
599
        addop->entry = talloc_steal(addop, ares->message);
 
600
        if (addop->entry == NULL) {
 
601
            return ldb_module_done(ctx->req, NULL, NULL,
 
602
                                   LDB_ERR_OPERATIONS_ERROR);
 
603
        }
 
604
 
 
605
        break;
 
606
    case LDB_REPLY_REFERRAL:
 
607
        /* ignore */
 
608
        break;
 
609
 
 
610
    case LDB_REPLY_DONE:
 
611
        talloc_zfree(ares);
 
612
        if (addop->entry == NULL) {
 
613
            ldb_debug(ldb, LDB_DEBUG_TRACE, "Entry not found (%s)",
 
614
                           ldb_dn_get_linearized(addop->entry_dn));
 
615
 
 
616
            /* this target does not exists, save as missing */
 
617
            ret = mbof_add_missing(add_ctx, addop->entry_dn);
 
618
            if (ret != LDB_SUCCESS) {
 
619
                return ldb_module_done(ctx->req, NULL, NULL, ret);
 
620
            }
 
621
            /* now try the next operation */
 
622
            if (add_ctx->current_op->next) {
 
623
                ret = mbof_next_add(add_ctx->current_op->next);
 
624
            }
 
625
            else {
 
626
                /* no more operations */
 
627
                if (add_ctx->missing) {
 
628
                    ret = mbof_add_cleanup(add_ctx);
 
629
                }
 
630
                else if (add_ctx->muops) {
 
631
                    ret = mbof_add_muop(add_ctx);
 
632
                }
 
633
                else {
 
634
                    return ldb_module_done(ctx->req,
 
635
                                           ctx->ret_ctrls,
 
636
                                           ctx->ret_resp,
 
637
                                           LDB_SUCCESS);
 
638
                }
 
639
            }
 
640
            if (ret != LDB_SUCCESS) {
 
641
                return ldb_module_done(ctx->req, NULL, NULL, ret);
 
642
            }
 
643
        }
 
644
        else {
 
645
            ret = mbof_add_operation(addop);
 
646
            if (ret != LDB_SUCCESS) {
 
647
                return ldb_module_done(ctx->req, NULL, NULL, ret);
 
648
            }
 
649
        }
 
650
        return LDB_SUCCESS;
 
651
    }
 
652
 
 
653
    talloc_zfree(ares);
 
654
    return LDB_SUCCESS;
 
655
}
 
656
 
 
657
/* if it is a group, add all members for cascade effect
 
658
 * add memberof attribute to this entry
 
659
 */
 
660
static int mbof_add_operation(struct mbof_add_operation *addop)
 
661
{
 
662
 
 
663
    TALLOC_CTX *tmp_ctx;
 
664
    struct mbof_ctx *ctx;
 
665
    struct mbof_add_ctx *add_ctx;
 
666
    struct ldb_context *ldb;
 
667
    struct ldb_message_element *el;
 
668
    struct ldb_request *mod_req;
 
669
    struct ldb_message *msg;
 
670
    struct ldb_dn *elval_dn;
 
671
    struct ldb_dn *valdn;
 
672
    struct mbof_dn_array *parents;
 
673
    int i, j, ret;
 
674
    const char *val;
 
675
    const char *name;
 
676
 
 
677
    add_ctx = addop->add_ctx;
 
678
    ctx = add_ctx->ctx;
 
679
    ldb = ldb_module_get_ctx(ctx->module);
 
680
 
 
681
    parents = talloc_zero(add_ctx, struct mbof_dn_array);
 
682
    if (!parents) {
 
683
        return LDB_ERR_OPERATIONS_ERROR;
 
684
    }
 
685
    /* can't be more than the immediate parent */
 
686
    parents->dns = talloc_array(parents, struct ldb_dn *,
 
687
                                addop->parents->num);
 
688
    if (!parents->dns) {
 
689
        return LDB_ERR_OPERATIONS_ERROR;
 
690
    }
 
691
 
 
692
    /* create new parent set for this entry */
 
693
    for (i = 0; i < addop->parents->num; i++) {
 
694
        /* never add yourself as memberof */
 
695
        if (ldb_dn_compare(addop->parents->dns[i], addop->entry_dn) == 0) {
 
696
            continue;
 
697
        }
 
698
        parents->dns[parents->num] = addop->parents->dns[i];
 
699
        parents->num++;
 
700
    }
 
701
 
 
702
    /* remove entries that are already there */
 
703
    el = ldb_msg_find_element(addop->entry, DB_MEMBEROF);
 
704
    if (el) {
 
705
 
 
706
        tmp_ctx = talloc_new(addop);
 
707
        if (!tmp_ctx) return LDB_ERR_OPERATIONS_ERROR;
 
708
 
 
709
        for (i = 0; i < el->num_values; i++) {
 
710
            elval_dn = ldb_dn_from_ldb_val(tmp_ctx, ldb, &el->values[i]);
 
711
            if (!elval_dn) {
 
712
                ldb_debug(ldb, LDB_DEBUG_TRACE, "Invalid DN in memberof [%s]",
 
713
                                            (const char *)el->values[i].data);
 
714
                talloc_free(tmp_ctx);
 
715
                return LDB_ERR_OPERATIONS_ERROR;
 
716
            }
 
717
            for (j = 0; j < parents->num; j++) {
 
718
                if (ldb_dn_compare(parents->dns[j], elval_dn) == 0) {
 
719
                    /* duplicate found */
 
720
                    break;
 
721
                }
 
722
            }
 
723
            if (j < parents->num) {
 
724
                /* remove duplicate */
 
725
                for (;j+1 < parents->num; j++) {
 
726
                    parents->dns[j] = parents->dns[j+1];
 
727
                }
 
728
                parents->num--;
 
729
            }
 
730
        }
 
731
 
 
732
        if (parents->num == 0) {
 
733
            /* already contains all parents as memberof, skip to next */
 
734
            talloc_free(tmp_ctx);
 
735
            talloc_free(addop->entry);
 
736
            addop->entry = NULL;
 
737
 
 
738
            if (addop->next) {
 
739
                return mbof_next_add(addop->next);
 
740
            }
 
741
            else if (add_ctx->muops) {
 
742
                return mbof_add_muop(add_ctx);
 
743
            }
 
744
            else {
 
745
                /* that was the last entry, get out */
 
746
                return ldb_module_done(ctx->req,
 
747
                                       ctx->ret_ctrls,
 
748
                                       ctx->ret_resp,
 
749
                                       LDB_SUCCESS);
 
750
            }
 
751
        }
 
752
        talloc_free(tmp_ctx);
 
753
    }
 
754
 
 
755
    /* if it is a group add all members */
 
756
    el = ldb_msg_find_element(addop->entry, DB_MEMBER);
 
757
    if (el) {
 
758
        for (i = 0; i < el->num_values; i++) {
 
759
            valdn = ldb_dn_from_ldb_val(add_ctx, ldb, &el->values[i]);
 
760
            if (!valdn) {
 
761
                ldb_debug(ldb, LDB_DEBUG_TRACE, "Invalid DN in member [%s]",
 
762
                                            (const char *)el->values[i].data);
 
763
                return LDB_ERR_OPERATIONS_ERROR;
 
764
            }
 
765
            if (!ldb_dn_validate(valdn)) {
 
766
                ldb_debug(ldb, LDB_DEBUG_TRACE,
 
767
                               "Invalid DN syntax for member [%s]",
 
768
                                            (const char *)el->values[i].data);
 
769
                return LDB_ERR_INVALID_DN_SYNTAX;
 
770
            }
 
771
            ret = mbof_append_addop(add_ctx, parents, valdn);
 
772
            if (ret != LDB_SUCCESS) {
 
773
                return ret;
 
774
            }
 
775
        }
 
776
    }
 
777
 
 
778
    /* check if we need to store memberuid ops for this entry */
 
779
    ret = entry_is_user_object(addop->entry);
 
780
    switch (ret) {
 
781
    case LDB_SUCCESS:
 
782
        /* it's a user object  */
 
783
        name = ldb_msg_find_attr_as_string(addop->entry, DB_NAME, NULL);
 
784
        if (!name) {
 
785
            return LDB_ERR_OPERATIONS_ERROR;
 
786
        }
 
787
 
 
788
        for (i = 0; i < parents->num; i++) {
 
789
            ret = mbof_append_muop(add_ctx, &add_ctx->muops,
 
790
                                   &add_ctx->num_muops,
 
791
                                   LDB_FLAG_MOD_ADD,
 
792
                                   parents->dns[i], name);
 
793
            if (ret != LDB_SUCCESS) {
 
794
                return ret;
 
795
            }
 
796
        }
 
797
 
 
798
        break;
 
799
 
 
800
    case LDB_ERR_NO_SUCH_ATTRIBUTE:
 
801
        /* it is not a user object, continue */
 
802
        break;
 
803
 
 
804
    default:
 
805
        /* an error occured, return */
 
806
        return ret;
 
807
    }
 
808
 
 
809
    /* we are done with the entry now */
 
810
    talloc_free(addop->entry);
 
811
    addop->entry = NULL;
 
812
 
 
813
    /* add memberof to entry */
 
814
    msg = ldb_msg_new(addop);
 
815
    if (!msg) return LDB_ERR_OPERATIONS_ERROR;
 
816
 
 
817
    msg->dn = addop->entry_dn;
 
818
 
 
819
    ret = ldb_msg_add_empty(msg, DB_MEMBEROF, LDB_FLAG_MOD_ADD, &el);
 
820
    if (ret != LDB_SUCCESS) {
 
821
        return ret;
 
822
    }
 
823
    el->values = talloc_array(msg, struct ldb_val, parents->num);
 
824
    if (!el->values) {
 
825
        return LDB_ERR_OPERATIONS_ERROR;
 
826
    }
 
827
    for (i = 0, j = 0; i < parents->num; i++) {
 
828
        if (ldb_dn_compare(parents->dns[i], msg->dn) == 0) continue;
 
829
        val = ldb_dn_get_linearized(parents->dns[i]);
 
830
        el->values[j].length = strlen(val);
 
831
        el->values[j].data = (uint8_t *)talloc_strdup(el->values, val);
 
832
        if (!el->values[j].data) {
 
833
            return LDB_ERR_OPERATIONS_ERROR;
 
834
        }
 
835
        j++;
 
836
    }
 
837
    el->num_values = j;
 
838
 
 
839
    ret = ldb_build_mod_req(&mod_req, ldb, add_ctx,
 
840
                            msg, NULL,
 
841
                            add_ctx, mbof_add_callback,
 
842
                            ctx->req);
 
843
    if (ret != LDB_SUCCESS) {
 
844
        return ret;
 
845
    }
 
846
    talloc_steal(mod_req, msg);
 
847
 
 
848
    return ldb_next_request(ctx->module, mod_req);
 
849
}
 
850
 
 
851
static int mbof_add_missing(struct mbof_add_ctx *add_ctx, struct ldb_dn *dn)
 
852
{
 
853
    struct mbof_dn *mdn;
 
854
 
 
855
    mdn = talloc(add_ctx, struct mbof_dn);
 
856
    if (!mdn) {
 
857
        return LDB_ERR_OPERATIONS_ERROR;
 
858
    }
 
859
    mdn->dn = talloc_steal(mdn, dn);
 
860
 
 
861
    /* add to the list */
 
862
    mdn->next = add_ctx->missing;
 
863
    add_ctx->missing = mdn;
 
864
 
 
865
    return LDB_SUCCESS;
 
866
}
 
867
 
 
868
/* remove unexisting members and add memberuid attribute */
 
869
static int mbof_add_cleanup(struct mbof_add_ctx *add_ctx)
 
870
{
 
871
    struct ldb_context *ldb;
 
872
    struct ldb_message *msg;
 
873
    struct ldb_request *mod_req;
 
874
    struct ldb_message_element *el;
 
875
    struct mbof_ctx *ctx;
 
876
    struct mbof_dn *iter;
 
877
    const char *val;
 
878
    int ret, i, num;
 
879
 
 
880
    ctx = add_ctx->ctx;
 
881
    ldb = ldb_module_get_ctx(ctx->module);
 
882
 
 
883
    num = 0;
 
884
    for (iter = add_ctx->missing; iter; iter = iter->next) {
 
885
        num++;
 
886
    }
 
887
    if (num == 0) {
 
888
        return LDB_ERR_OPERATIONS_ERROR;
 
889
    }
 
890
 
 
891
    msg = ldb_msg_new(add_ctx);
 
892
    if (!msg) return LDB_ERR_OPERATIONS_ERROR;
 
893
 
 
894
    msg->dn = add_ctx->msg_dn;
 
895
 
 
896
    ret = ldb_msg_add_empty(msg, DB_MEMBER, LDB_FLAG_MOD_DELETE, &el);
 
897
    if (ret != LDB_SUCCESS) {
 
898
        return ret;
 
899
    }
 
900
    el->values = talloc_array(msg, struct ldb_val, num);
 
901
    if (!el->values) {
 
902
        return LDB_ERR_OPERATIONS_ERROR;
 
903
    }
 
904
    el->num_values = num;
 
905
    for (i = 0, iter = add_ctx->missing; iter; iter = iter->next, i++) {
 
906
        val = ldb_dn_get_linearized(iter->dn);
 
907
        el->values[i].length = strlen(val);
 
908
        el->values[i].data = (uint8_t *)talloc_strdup(el->values, val);
 
909
        if (!el->values[i].data) {
 
910
            return LDB_ERR_OPERATIONS_ERROR;
 
911
        }
 
912
    }
 
913
 
 
914
    ret = ldb_build_mod_req(&mod_req, ldb, add_ctx,
 
915
                            msg, NULL,
 
916
                            add_ctx, mbof_add_cleanup_callback,
 
917
                            ctx->req);
 
918
    if (ret != LDB_SUCCESS) {
 
919
        return ret;
 
920
    }
 
921
 
 
922
    return ldb_next_request(ctx->module, mod_req);
 
923
}
 
924
 
 
925
static int mbof_add_cleanup_callback(struct ldb_request *req,
 
926
                                     struct ldb_reply *ares)
 
927
{
 
928
    struct mbof_add_ctx *add_ctx;
 
929
    struct mbof_ctx *ctx;
 
930
    int ret;
 
931
 
 
932
    add_ctx = talloc_get_type(req->context, struct mbof_add_ctx);
 
933
    ctx = add_ctx->ctx;
 
934
 
 
935
    if (!ares) {
 
936
        return ldb_module_done(ctx->req, NULL, NULL,
 
937
                               LDB_ERR_OPERATIONS_ERROR);
 
938
    }
 
939
    if (ares->error != LDB_SUCCESS) {
 
940
        return ldb_module_done(ctx->req,
 
941
                               ares->controls,
 
942
                               ares->response,
 
943
                               ares->error);
 
944
    }
 
945
 
 
946
    switch (ares->type) {
 
947
    case LDB_REPLY_ENTRY:
 
948
        /* shouldn't happen */
 
949
        talloc_zfree(ares);
 
950
        return ldb_module_done(ctx->req, NULL, NULL,
 
951
                               LDB_ERR_OPERATIONS_ERROR);
 
952
    case LDB_REPLY_REFERRAL:
 
953
        /* ignore */
 
954
        break;
 
955
 
 
956
    case LDB_REPLY_DONE:
 
957
        if (add_ctx->muops) {
 
958
            ret = mbof_add_muop(add_ctx);
 
959
        }
 
960
        else {
 
961
            return ldb_module_done(ctx->req,
 
962
                                   ctx->ret_ctrls,
 
963
                                   ctx->ret_resp,
 
964
                                   LDB_SUCCESS);
 
965
        }
 
966
 
 
967
        if (ret != LDB_SUCCESS) {
 
968
            talloc_zfree(ares);
 
969
            return ldb_module_done(ctx->req, NULL, NULL, ret);
 
970
        }
 
971
    }
 
972
 
 
973
    talloc_zfree(ares);
 
974
    return LDB_SUCCESS;
 
975
}
 
976
 
 
977
/* add memberuid attributes to parent groups */
 
978
static int mbof_add_muop(struct mbof_add_ctx *add_ctx)
 
979
{
 
980
    struct ldb_context *ldb;
 
981
    struct ldb_message *msg;
 
982
    struct ldb_request *mod_req;
 
983
    struct mbof_ctx *ctx;
 
984
    int ret;
 
985
 
 
986
    ctx = add_ctx->ctx;
 
987
    ldb = ldb_module_get_ctx(ctx->module);
 
988
 
 
989
    msg = ldb_msg_new(add_ctx);
 
990
    if (!msg) return LDB_ERR_OPERATIONS_ERROR;
 
991
 
 
992
    msg->dn = add_ctx->muops[add_ctx->cur_muop].dn;
 
993
    msg->elements = add_ctx->muops[add_ctx->cur_muop].el;
 
994
    msg->num_elements = 1;
 
995
 
 
996
    ret = ldb_build_mod_req(&mod_req, ldb, add_ctx,
 
997
                            msg, NULL,
 
998
                            add_ctx, mbof_add_muop_callback,
 
999
                            ctx->req);
 
1000
    if (ret != LDB_SUCCESS) {
 
1001
        return ret;
 
1002
    }
 
1003
 
 
1004
    return ldb_next_request(ctx->module, mod_req);
 
1005
}
 
1006
 
 
1007
static int mbof_add_muop_callback(struct ldb_request *req,
 
1008
                                  struct ldb_reply *ares)
 
1009
{
 
1010
    struct mbof_add_ctx *add_ctx;
 
1011
    struct mbof_ctx *ctx;
 
1012
    int ret;
 
1013
 
 
1014
    add_ctx = talloc_get_type(req->context, struct mbof_add_ctx);
 
1015
    ctx = add_ctx->ctx;
 
1016
 
 
1017
    if (!ares) {
 
1018
        return ldb_module_done(ctx->req, NULL, NULL,
 
1019
                               LDB_ERR_OPERATIONS_ERROR);
 
1020
    }
 
1021
    if (ares->error != LDB_SUCCESS) {
 
1022
        return ldb_module_done(ctx->req,
 
1023
                               ares->controls,
 
1024
                               ares->response,
 
1025
                               ares->error);
 
1026
    }
 
1027
 
 
1028
    switch (ares->type) {
 
1029
    case LDB_REPLY_ENTRY:
 
1030
        /* shouldn't happen */
 
1031
        talloc_zfree(ares);
 
1032
        return ldb_module_done(ctx->req, NULL, NULL,
 
1033
                               LDB_ERR_OPERATIONS_ERROR);
 
1034
    case LDB_REPLY_REFERRAL:
 
1035
        /* ignore */
 
1036
        break;
 
1037
 
 
1038
    case LDB_REPLY_DONE:
 
1039
        add_ctx->cur_muop++;
 
1040
        if (add_ctx->cur_muop < add_ctx->num_muops) {
 
1041
            ret = mbof_add_muop(add_ctx);
 
1042
        }
 
1043
        else {
 
1044
            return ldb_module_done(ctx->req,
 
1045
                                   ctx->ret_ctrls,
 
1046
                                   ctx->ret_resp,
 
1047
                                   LDB_SUCCESS);
 
1048
        }
 
1049
 
 
1050
        if (ret != LDB_SUCCESS) {
 
1051
            talloc_zfree(ares);
 
1052
            return ldb_module_done(ctx->req, NULL, NULL, ret);
 
1053
        }
 
1054
    }
 
1055
 
 
1056
    talloc_zfree(ares);
 
1057
    return LDB_SUCCESS;
 
1058
}
 
1059
 
 
1060
 
 
1061
 
 
1062
 
 
1063
/* delete operations */
 
1064
 
 
1065
/* The implementation of delete operations is a bit more complex than an add
 
1066
 * operation. This is because we need to recompute memberships of potentially
 
1067
 * quite far descendants and we also have to account for loops and how to
 
1068
 * break them without ending in an endless loop ourselves.
 
1069
 * The difficulty is in the fact that while the member -> memberof link is
 
1070
 * direct, memberof -> member is not as membership is transitive.
 
1071
 *
 
1072
 * Ok, first of all, contrary to the add operation, a delete operation
 
1073
 * involves an existing object that may have existing parents. So, first, we
 
1074
 * search  the object itself to get the original membership lists (member and
 
1075
 * memberof) for this object, and we also search for any object that has it as
 
1076
 * one of its members.
 
1077
 * Once we have the results, we store object and parents and proceed with the
 
1078
 * original operation to make sure it is valid.
 
1079
 *
 
1080
 * Once the original op returns we proceed fixing parents (parents being each
 
1081
 * object that has the delete operation target object as member), if any.
 
1082
 *
 
1083
 * For each parent we retrieved we proceed to delete the member attribute that
 
1084
 * points to the object we just deleted. Once done for all parents (or if no
 
1085
 * parents exists), we proceed with the children and descendants.
 
1086
 *
 
1087
 * To handle the children we create a first ancestor operation that reflects
 
1088
 * the delete we just made. We set as parents of this object the parents just
 
1089
 * retrieved with the first search. Then we create a remove list.
 
1090
 *
 
1091
 * The remove list contains all objects in the original memberof list and the
 
1092
 * object dn itself of the original delete operation target object (the first
 
1093
 * ancestor).
 
1094
 *
 
1095
 * An operation is identified by an object that contains a tree of
 
1096
 * descendants:
 
1097
 * The remove list for the children, the immediate parent, and the dn and
 
1098
 * entry of the object this operation is about.
 
1099
 *
 
1100
 * We now proceed with adding a new operation for each original member of the
 
1101
 * first ancestor.
 
1102
 *
 
1103
 * In each operation we must first lookup the target object and each immediate
 
1104
 * parent (all the objects in the tree that have target as a "member").
 
1105
 *
 
1106
 * Then we proceed to calculate the new memberof list that we are going to set
 
1107
 * on the target object.
 
1108
 * The new memberof list starts with including all the objects that have the
 
1109
 * target as their direct member.
 
1110
 * Finally for each entry in this provisional new memberof list we add all its
 
1111
 * memberof elements to the new memberof list (taking care of excluding
 
1112
 * duplicates). This way we are certain all direct and indirect membership are
 
1113
 * accounted for.
 
1114
 *
 
1115
 * At this point we have the final new memberof list for this operation and we
 
1116
 * can proceed to modify the entry.
 
1117
 *
 
1118
 * Once the entry has been modified we proceed again to check if there are any
 
1119
 * children of this entry (the entry has "member"s).
 
1120
 * We create a new remove list that is the difference between the original
 
1121
 * entry memberof list and the new memberof list we just stored back in the
 
1122
 * object.
 
1123
 * Then for each member we create a new operation.
 
1124
 *
 
1125
 * We continue to process operations until no new operations need to be
 
1126
 * performed.
 
1127
 *
 
1128
 * Ordering is important here, se the mbof_del_get_next() function to
 
1129
 * understand how we proceed to select which new operation to process.
 
1130
 *
 
1131
 * As a final operation remove any memberuid corresponding to a removal of
 
1132
 * a memberof field from a user entry
 
1133
 */
 
1134
 
 
1135
static int mbof_del_search_callback(struct ldb_request *req,
 
1136
                                    struct ldb_reply *ares);
 
1137
static int mbof_orig_del(struct mbof_del_ctx *ctx);
 
1138
static int mbof_orig_del_callback(struct ldb_request *req,
 
1139
                                  struct ldb_reply *ares);
 
1140
static int mbof_del_cleanup_parents(struct mbof_del_ctx *del_ctx);
 
1141
static int mbof_del_clean_par_callback(struct ldb_request *req,
 
1142
                                       struct ldb_reply *ares);
 
1143
static int mbof_del_cleanup_children(struct mbof_del_ctx *del_ctx);
 
1144
static int mbof_append_delop(struct mbof_del_operation *parent,
 
1145
                             struct ldb_dn *entry_dn);
 
1146
static int mbof_del_execute_op(struct mbof_del_operation *delop);
 
1147
static int mbof_del_exop_search_callback(struct ldb_request *req,
 
1148
                                         struct ldb_reply *ares);
 
1149
static int mbof_del_execute_cont(struct mbof_del_operation *delop);
 
1150
static int mbof_del_ancestors(struct mbof_del_operation *delop);
 
1151
static int mbof_del_anc_callback(struct ldb_request *req,
 
1152
                                 struct ldb_reply *ares);
 
1153
static int mbof_del_mod_entry(struct mbof_del_operation *delop);
 
1154
static int mbof_del_mod_callback(struct ldb_request *req,
 
1155
                                 struct ldb_reply *ares);
 
1156
static int mbof_del_progeny(struct mbof_del_operation *delop);
 
1157
static int mbof_del_get_next(struct mbof_del_operation *delop,
 
1158
                             struct mbof_del_operation **nextop);
 
1159
static int mbof_del_fill_muop(struct mbof_del_ctx *del_ctx,
 
1160
                              struct ldb_message *entry);
 
1161
static int mbof_del_muop(struct mbof_del_ctx *ctx);
 
1162
static int mbof_del_muop_callback(struct ldb_request *req,
 
1163
                                  struct ldb_reply *ares);
 
1164
static void free_delop_contents(struct mbof_del_operation *delop);
 
1165
 
 
1166
 
 
1167
static int memberof_del(struct ldb_module *module, struct ldb_request *req)
 
1168
{
 
1169
    static const char *attrs[] = { DB_OC, DB_NAME,
 
1170
                                   DB_MEMBER, DB_MEMBEROF, NULL };
 
1171
    struct ldb_context *ldb = ldb_module_get_ctx(module);
 
1172
    struct mbof_del_operation *first;
 
1173
    struct ldb_request *search;
 
1174
    char *expression;
 
1175
    const char *dn;
 
1176
    char *clean_dn;
 
1177
    struct mbof_del_ctx *del_ctx;
 
1178
    struct mbof_ctx *ctx;
 
1179
    int ret;
 
1180
    errno_t sret;
 
1181
 
 
1182
    if (ldb_dn_is_special(req->op.del.dn)) {
 
1183
        /* do not manipulate our control entries */
 
1184
        return ldb_next_request(module, req);
 
1185
    }
 
1186
 
 
1187
    ctx = mbof_init(module, req);
 
1188
    if (!ctx) {
 
1189
        return LDB_ERR_OPERATIONS_ERROR;
 
1190
    }
 
1191
 
 
1192
    del_ctx = talloc_zero(ctx, struct mbof_del_ctx);
 
1193
    if (!del_ctx) {
 
1194
        talloc_free(ctx);
 
1195
        return LDB_ERR_OPERATIONS_ERROR;
 
1196
    }
 
1197
    del_ctx->ctx = ctx;
 
1198
 
 
1199
    /* create first entry */
 
1200
    /* the first entry is the parent of all entries and the one where we remove
 
1201
     * member from, it does not get the same treatment as others */
 
1202
    first = talloc_zero(del_ctx, struct mbof_del_operation);
 
1203
    if (!first) {
 
1204
        talloc_free(ctx);
 
1205
        return LDB_ERR_OPERATIONS_ERROR;
 
1206
    }
 
1207
    del_ctx->first = first;
 
1208
 
 
1209
    first->del_ctx = del_ctx;
 
1210
    first->entry_dn = req->op.del.dn;
 
1211
 
 
1212
    dn = ldb_dn_get_linearized(req->op.del.dn);
 
1213
    if (!dn) {
 
1214
        talloc_free(ctx);
 
1215
        return LDB_ERR_OPERATIONS_ERROR;
 
1216
    }
 
1217
 
 
1218
    sret = sss_filter_sanitize(del_ctx, dn, &clean_dn);
 
1219
    if (sret != 0) {
 
1220
        talloc_free(ctx);
 
1221
        return LDB_ERR_OPERATIONS_ERROR;
 
1222
    }
 
1223
 
 
1224
    expression = talloc_asprintf(del_ctx,
 
1225
                                 "(|(distinguishedName=%s)(%s=%s))",
 
1226
                                 clean_dn, DB_MEMBER, clean_dn);
 
1227
    if (!expression) {
 
1228
        talloc_free(ctx);
 
1229
        return LDB_ERR_OPERATIONS_ERROR;
 
1230
    }
 
1231
    talloc_zfree(clean_dn);
 
1232
 
 
1233
    ret = ldb_build_search_req(&search, ldb, del_ctx,
 
1234
                               NULL, LDB_SCOPE_SUBTREE,
 
1235
                               expression, attrs, NULL,
 
1236
                               first, mbof_del_search_callback,
 
1237
                               req);
 
1238
    if (ret != LDB_SUCCESS) {
 
1239
        talloc_free(ctx);
 
1240
        return ret;
 
1241
    }
 
1242
 
 
1243
    return ldb_request(ldb, search);
 
1244
}
 
1245
 
 
1246
static int mbof_del_search_callback(struct ldb_request *req,
 
1247
                                    struct ldb_reply *ares)
 
1248
{
 
1249
    struct mbof_del_operation *first;
 
1250
    struct ldb_context *ldb;
 
1251
    struct ldb_message *msg;
 
1252
    struct mbof_del_ctx *del_ctx;
 
1253
    struct mbof_ctx *ctx;
 
1254
    int ret;
 
1255
 
 
1256
    first = talloc_get_type(req->context, struct mbof_del_operation);
 
1257
    del_ctx = first->del_ctx;
 
1258
    ctx = del_ctx->ctx;
 
1259
    ldb = ldb_module_get_ctx(ctx->module);
 
1260
 
 
1261
    if (!ares) {
 
1262
        return ldb_module_done(ctx->req, NULL, NULL,
 
1263
                               LDB_ERR_OPERATIONS_ERROR);
 
1264
    }
 
1265
    if (ares->error != LDB_SUCCESS) {
 
1266
        return ldb_module_done(ctx->req,
 
1267
                               ares->controls,
 
1268
                               ares->response,
 
1269
                               ares->error);
 
1270
    }
 
1271
 
 
1272
    switch (ares->type) {
 
1273
    case LDB_REPLY_ENTRY:
 
1274
        msg = ares->message;
 
1275
 
 
1276
        if (ldb_dn_compare(msg->dn, ctx->req->op.del.dn) == 0) {
 
1277
 
 
1278
            if (first->entry != NULL) {
 
1279
                /* more than one entry per dn ?? db corrupted ? */
 
1280
                return ldb_module_done(ctx->req, NULL, NULL,
 
1281
                                       LDB_ERR_OPERATIONS_ERROR);
 
1282
            }
 
1283
 
 
1284
            first->entry = talloc_steal(first, msg);
 
1285
            if (first->entry == NULL) {
 
1286
                return ldb_module_done(ctx->req, NULL, NULL,
 
1287
                                       LDB_ERR_OPERATIONS_ERROR);
 
1288
            }
 
1289
        } else {
 
1290
            first->parents = talloc_realloc(first, first->parents,
 
1291
                                             struct ldb_message *,
 
1292
                                             first->num_parents + 1);
 
1293
            if (!first->parents) {
 
1294
                return ldb_module_done(ctx->req, NULL, NULL,
 
1295
                                       LDB_ERR_OPERATIONS_ERROR);
 
1296
            }
 
1297
            msg = talloc_steal(first->parents, ares->message);
 
1298
            if (!msg) {
 
1299
                return ldb_module_done(ctx->req, NULL, NULL,
 
1300
                                       LDB_ERR_OPERATIONS_ERROR);
 
1301
            }
 
1302
            first->parents[first->num_parents] = msg;
 
1303
            first->num_parents++;
 
1304
        }
 
1305
        break;
 
1306
    case LDB_REPLY_REFERRAL:
 
1307
        /* ignore */
 
1308
        break;
 
1309
 
 
1310
    case LDB_REPLY_DONE:
 
1311
        if (first->entry == NULL) {
 
1312
            /* this target does not exists, too bad! */
 
1313
            ldb_debug(ldb, LDB_DEBUG_TRACE,
 
1314
                           "Target entry (%s) not found",
 
1315
                           ldb_dn_get_linearized(first->entry_dn));
 
1316
            return ldb_module_done(ctx->req, NULL, NULL,
 
1317
                                   LDB_ERR_NO_SUCH_OBJECT);
 
1318
        }
 
1319
 
 
1320
        /* now perform the requested delete, before proceeding further */
 
1321
        ret =  mbof_orig_del(del_ctx);
 
1322
        if (ret != LDB_SUCCESS) {
 
1323
            talloc_zfree(ares);
 
1324
            return ldb_module_done(ctx->req, NULL, NULL, ret);
 
1325
        }
 
1326
    }
 
1327
 
 
1328
    talloc_zfree(ares);
 
1329
    return LDB_SUCCESS;
 
1330
}
 
1331
 
 
1332
static int mbof_orig_del(struct mbof_del_ctx *del_ctx)
 
1333
{
 
1334
    struct ldb_request *del_req;
 
1335
    struct mbof_ctx *ctx;
 
1336
    int ret;
 
1337
 
 
1338
    ctx = del_ctx->ctx;
 
1339
 
 
1340
    ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ctx->module),
 
1341
                            ctx->req, ctx->req->op.del.dn, NULL,
 
1342
                            del_ctx, mbof_orig_del_callback,
 
1343
                            ctx->req);
 
1344
    if (ret != LDB_SUCCESS) {
 
1345
        return ret;
 
1346
    }
 
1347
 
 
1348
    return ldb_next_request(ctx->module, del_req);
 
1349
}
 
1350
 
 
1351
static int mbof_orig_del_callback(struct ldb_request *req,
 
1352
                                  struct ldb_reply *ares)
 
1353
{
 
1354
    struct ldb_context *ldb;
 
1355
    struct mbof_del_ctx *del_ctx;
 
1356
    struct mbof_ctx *ctx;
 
1357
    int ret;
 
1358
 
 
1359
    del_ctx = talloc_get_type(req->context, struct mbof_del_ctx);
 
1360
    ctx = del_ctx->ctx;
 
1361
    ldb = ldb_module_get_ctx(ctx->module);
 
1362
 
 
1363
    if (!ares) {
 
1364
        return ldb_module_done(ctx->req, NULL, NULL,
 
1365
                               LDB_ERR_OPERATIONS_ERROR);
 
1366
    }
 
1367
    if (ares->error != LDB_SUCCESS) {
 
1368
        return ldb_module_done(ctx->req,
 
1369
                               ares->controls,
 
1370
                               ares->response,
 
1371
                               ares->error);
 
1372
    }
 
1373
 
 
1374
    if (ares->type != LDB_REPLY_DONE) {
 
1375
        talloc_zfree(ares);
 
1376
        ldb_set_errstring(ldb, "Invalid reply type!");
 
1377
        return ldb_module_done(ctx->req, NULL, NULL,
 
1378
                               LDB_ERR_OPERATIONS_ERROR);
 
1379
    }
 
1380
 
 
1381
    /* save real call stuff */
 
1382
    ctx->ret_ctrls = talloc_steal(ctx, ares->controls);
 
1383
    ctx->ret_resp = talloc_steal(ctx, ares->response);
 
1384
 
 
1385
    /* prep following clean ops */
 
1386
    if (del_ctx->first->num_parents) {
 
1387
 
 
1388
        /* if there are parents there may be memberuids to remove */
 
1389
        ret = mbof_del_fill_muop(del_ctx, del_ctx->first->entry);
 
1390
        if (ret != LDB_SUCCESS) {
 
1391
            talloc_zfree(ares);
 
1392
            return ldb_module_done(ctx->req, NULL, NULL, ret);
 
1393
        }
 
1394
 
 
1395
        /* if there are any parents, fire a removal sequence */
 
1396
        ret = mbof_del_cleanup_parents(del_ctx);
 
1397
    }
 
1398
    else if (ldb_msg_find_element(del_ctx->first->entry, DB_MEMBER)) {
 
1399
        /* if there are any children, fire a removal sequence */
 
1400
        ret = mbof_del_cleanup_children(del_ctx);
 
1401
    }
 
1402
    /* see if there are memberuid operations to perform */
 
1403
    else if (del_ctx->muops) {
 
1404
        return mbof_del_muop(del_ctx);
 
1405
    }
 
1406
    else {
 
1407
        /* no parents nor children, end ops */
 
1408
        return ldb_module_done(ctx->req,
 
1409
                               ares->controls,
 
1410
                               ares->response,
 
1411
                               LDB_SUCCESS);
 
1412
    }
 
1413
    if (ret != LDB_SUCCESS) {
 
1414
        talloc_zfree(ares);
 
1415
        return ldb_module_done(ctx->req, NULL, NULL, ret);
 
1416
    }
 
1417
 
 
1418
    talloc_zfree(ares);
 
1419
    return LDB_SUCCESS;
 
1420
}
 
1421
 
 
1422
static int mbof_del_cleanup_parents(struct mbof_del_ctx *del_ctx)
 
1423
{
 
1424
    struct mbof_del_operation *first;
 
1425
    struct mbof_ctx *ctx;
 
1426
    struct ldb_context *ldb;
 
1427
    struct ldb_request *mod_req;
 
1428
    struct ldb_message *msg;
 
1429
    struct ldb_message_element *el;
 
1430
    const char *val;
 
1431
    int ret;
 
1432
 
 
1433
    first = del_ctx->first;
 
1434
    ctx = del_ctx->ctx;
 
1435
    ldb = ldb_module_get_ctx(ctx->module);
 
1436
 
 
1437
    msg = ldb_msg_new(first->parents);
 
1438
    if (!msg) return LDB_ERR_OPERATIONS_ERROR;
 
1439
 
 
1440
    msg->dn = first->parents[first->cur_parent]->dn;
 
1441
    first->cur_parent++;
 
1442
 
 
1443
    ret = ldb_msg_add_empty(msg, DB_MEMBER, LDB_FLAG_MOD_DELETE, &el);
 
1444
    if (ret != LDB_SUCCESS) {
 
1445
        return ret;
 
1446
    }
 
1447
    el->values = talloc_array(msg, struct ldb_val, 1);
 
1448
    if (!el->values) {
 
1449
        return LDB_ERR_OPERATIONS_ERROR;
 
1450
    }
 
1451
    val = ldb_dn_get_linearized(first->entry_dn);
 
1452
    el->values[0].length = strlen(val);
 
1453
    el->values[0].data = (uint8_t *)talloc_strdup(el->values, val);
 
1454
    if (!el->values[0].data) {
 
1455
        return LDB_ERR_OPERATIONS_ERROR;
 
1456
    }
 
1457
    el->num_values = 1;
 
1458
 
 
1459
    ret = ldb_build_mod_req(&mod_req, ldb, first->parents,
 
1460
                            msg, NULL,
 
1461
                            del_ctx, mbof_del_clean_par_callback,
 
1462
                            ctx->req);
 
1463
    if (ret != LDB_SUCCESS) {
 
1464
        return ret;
 
1465
    }
 
1466
 
 
1467
    return ldb_next_request(ctx->module, mod_req);
 
1468
}
 
1469
 
 
1470
static int mbof_del_clean_par_callback(struct ldb_request *req,
 
1471
                                       struct ldb_reply *ares)
 
1472
{
 
1473
    struct mbof_del_operation *first;
 
1474
    struct ldb_context *ldb;
 
1475
    struct mbof_del_ctx *del_ctx;
 
1476
    struct mbof_ctx *ctx;
 
1477
    int ret;
 
1478
 
 
1479
    del_ctx = talloc_get_type(req->context, struct mbof_del_ctx);
 
1480
    first = del_ctx->first;
 
1481
    ctx = del_ctx->ctx;
 
1482
    ldb = ldb_module_get_ctx(ctx->module);
 
1483
 
 
1484
    if (!ares) {
 
1485
        return ldb_module_done(ctx->req, NULL, NULL,
 
1486
                               LDB_ERR_OPERATIONS_ERROR);
 
1487
    }
 
1488
 
 
1489
    if (ares->error != LDB_SUCCESS) {
 
1490
        return ldb_module_done(ctx->req,
 
1491
                               ares->controls,
 
1492
                               ares->response,
 
1493
                               ares->error);
 
1494
    }
 
1495
 
 
1496
    if (ares->type != LDB_REPLY_DONE) {
 
1497
        talloc_zfree(ares);
 
1498
        ldb_set_errstring(ldb, "Invalid reply type!");
 
1499
        return ldb_module_done(ctx->req, NULL, NULL,
 
1500
                               LDB_ERR_OPERATIONS_ERROR);
 
1501
    }
 
1502
 
 
1503
    if (first->num_parents > first->cur_parent) {
 
1504
        /* still parents to cleanup, go on */
 
1505
        ret = mbof_del_cleanup_parents(del_ctx);
 
1506
    }
 
1507
    else {
 
1508
        /* continue */
 
1509
        if (ldb_msg_find_element(first->entry, DB_MEMBER)) {
 
1510
            /* if there are any children, fire a removal sequence */
 
1511
            ret = mbof_del_cleanup_children(del_ctx);
 
1512
        }
 
1513
        /* see if there are memberuid operations to perform */
 
1514
        else if (del_ctx->muops) {
 
1515
            return mbof_del_muop(del_ctx);
 
1516
        }
 
1517
        else {
 
1518
            /* no children, end ops */
 
1519
            return ldb_module_done(ctx->req,
 
1520
                                   ctx->ret_ctrls,
 
1521
                                   ctx->ret_resp,
 
1522
                                   LDB_SUCCESS);
 
1523
        }
 
1524
    }
 
1525
 
 
1526
    if (ret != LDB_SUCCESS) {
 
1527
        talloc_zfree(ares);
 
1528
        return ldb_module_done(ctx->req, NULL, NULL, ret);
 
1529
    }
 
1530
 
 
1531
    talloc_zfree(ares);
 
1532
    return LDB_SUCCESS;
 
1533
}
 
1534
 
 
1535
static int mbof_del_cleanup_children(struct mbof_del_ctx *del_ctx)
 
1536
{
 
1537
    struct mbof_del_operation *first;
 
1538
    struct mbof_ctx *ctx;
 
1539
    struct ldb_context *ldb;
 
1540
    const struct ldb_message_element *el;
 
1541
    struct ldb_dn *valdn;
 
1542
    int i, ret;
 
1543
 
 
1544
    first = del_ctx->first;
 
1545
    ctx = del_ctx->ctx;
 
1546
    ldb = ldb_module_get_ctx(ctx->module);
 
1547
 
 
1548
    el = ldb_msg_find_element(first->entry, DB_MEMBER);
 
1549
 
 
1550
    /* prepare del sets */
 
1551
    for (i = 0; i < el->num_values; i++) {
 
1552
        valdn = ldb_dn_from_ldb_val(first, ldb, &el->values[i]);
 
1553
        if (!valdn || !ldb_dn_validate(valdn)) {
 
1554
            ldb_debug(ldb, LDB_DEBUG_TRACE,
 
1555
                           "Invalid dn syntax for member [%s]",
 
1556
                                        (const char *)el->values[i].data);
 
1557
            return LDB_ERR_INVALID_DN_SYNTAX;
 
1558
        }
 
1559
        ret = mbof_append_delop(first, valdn);
 
1560
        if (ret != LDB_SUCCESS) {
 
1561
            return ret;
 
1562
        }
 
1563
    }
 
1564
 
 
1565
    /* now that sets are built, start processing */
 
1566
    return mbof_del_execute_op(first->children[0]);
 
1567
}
 
1568
 
 
1569
static int mbof_append_delop(struct mbof_del_operation *parent,
 
1570
                             struct ldb_dn *entry_dn)
 
1571
{
 
1572
    struct mbof_del_operation *delop;
 
1573
 
 
1574
    delop = talloc_zero(parent, struct mbof_del_operation);
 
1575
    if (!delop) {
 
1576
        return LDB_ERR_OPERATIONS_ERROR;
 
1577
    }
 
1578
 
 
1579
    delop->del_ctx = parent->del_ctx;
 
1580
    delop->parent = parent;
 
1581
    delop->entry_dn = entry_dn;
 
1582
 
 
1583
    parent->children = talloc_realloc(parent, parent->children,
 
1584
                                      struct mbof_del_operation *,
 
1585
                                      parent->num_children +1);
 
1586
    if (!parent->children) {
 
1587
        talloc_free(delop);
 
1588
        return LDB_ERR_OPERATIONS_ERROR;
 
1589
    }
 
1590
 
 
1591
    parent->children[parent->num_children] = delop;
 
1592
    parent->num_children++;
 
1593
 
 
1594
    return LDB_SUCCESS;
 
1595
}
 
1596
 
 
1597
static int mbof_del_execute_op(struct mbof_del_operation *delop)
 
1598
{
 
1599
    struct mbof_del_ctx *del_ctx;
 
1600
    struct mbof_ctx *ctx;
 
1601
    struct ldb_context *ldb;
 
1602
    struct ldb_request *search;
 
1603
    char *expression;
 
1604
    const char *dn;
 
1605
    char *clean_dn;
 
1606
    static const char *attrs[] = { DB_OC, DB_NAME,
 
1607
                                   DB_MEMBER, DB_MEMBEROF, NULL };
 
1608
    int ret;
 
1609
 
 
1610
    del_ctx = delop->del_ctx;
 
1611
    ctx = del_ctx->ctx;
 
1612
    ldb = ldb_module_get_ctx(ctx->module);
 
1613
 
 
1614
    /* load entry */
 
1615
    dn = ldb_dn_get_linearized(delop->entry_dn);
 
1616
    if (!dn) {
 
1617
        return LDB_ERR_OPERATIONS_ERROR;
 
1618
    }
 
1619
 
 
1620
    ret = sss_filter_sanitize(del_ctx, dn, &clean_dn);
 
1621
    if (ret != 0) {
 
1622
        return LDB_ERR_OPERATIONS_ERROR;
 
1623
    }
 
1624
 
 
1625
    expression = talloc_asprintf(del_ctx,
 
1626
                                 "(|(distinguishedName=%s)(%s=%s))",
 
1627
                                 clean_dn, DB_MEMBER, clean_dn);
 
1628
    if (!expression) {
 
1629
        return LDB_ERR_OPERATIONS_ERROR;
 
1630
    }
 
1631
    talloc_zfree(clean_dn);
 
1632
 
 
1633
    ret = ldb_build_search_req(&search, ldb, delop,
 
1634
                               NULL, LDB_SCOPE_SUBTREE,
 
1635
                               expression, attrs, NULL,
 
1636
                               delop, mbof_del_exop_search_callback,
 
1637
                               ctx->req);
 
1638
    if (ret != LDB_SUCCESS) {
 
1639
        talloc_free(ctx);
 
1640
        return ret;
 
1641
    }
 
1642
 
 
1643
    return ldb_request(ldb, search);
 
1644
}
 
1645
 
 
1646
static int mbof_del_exop_search_callback(struct ldb_request *req,
 
1647
                                         struct ldb_reply *ares)
 
1648
{
 
1649
    struct mbof_del_operation *delop;
 
1650
    struct mbof_del_ctx *del_ctx;
 
1651
    struct ldb_context *ldb;
 
1652
    struct mbof_ctx *ctx;
 
1653
    struct ldb_message *msg;
 
1654
    int ret;
 
1655
 
 
1656
    delop = talloc_get_type(req->context, struct mbof_del_operation);
 
1657
    del_ctx = delop->del_ctx;
 
1658
    ctx = del_ctx->ctx;
 
1659
    ldb = ldb_module_get_ctx(ctx->module);
 
1660
 
 
1661
    if (!ares) {
 
1662
        return ldb_module_done(ctx->req, NULL, NULL,
 
1663
                               LDB_ERR_OPERATIONS_ERROR);
 
1664
    }
 
1665
    if (ares->error != LDB_SUCCESS) {
 
1666
        return ldb_module_done(ctx->req,
 
1667
                               ares->controls,
 
1668
                               ares->response,
 
1669
                               ares->error);
 
1670
    }
 
1671
 
 
1672
    switch (ares->type) {
 
1673
    case LDB_REPLY_ENTRY:
 
1674
        msg = ares->message;
 
1675
 
 
1676
        if (ldb_dn_compare(msg->dn, delop->entry_dn) == 0) {
 
1677
 
 
1678
            if (delop->entry != NULL) {
 
1679
                ldb_debug(ldb, LDB_DEBUG_TRACE,
 
1680
                               "Found multiple entries for (%s)",
 
1681
                               ldb_dn_get_linearized(delop->entry_dn));
 
1682
                /* more than one entry per dn ?? db corrupted ? */
 
1683
                return ldb_module_done(ctx->req, NULL, NULL,
 
1684
                                       LDB_ERR_OPERATIONS_ERROR);
 
1685
            }
 
1686
 
 
1687
            delop->entry = talloc_steal(delop, msg);
 
1688
            if (delop->entry == NULL) {
 
1689
                return ldb_module_done(ctx->req, NULL, NULL,
 
1690
                                       LDB_ERR_OPERATIONS_ERROR);
 
1691
            }
 
1692
        } else {
 
1693
            delop->parents = talloc_realloc(delop, delop->parents,
 
1694
                                            struct ldb_message *,
 
1695
                                            delop->num_parents + 1);
 
1696
            if (!delop->parents) {
 
1697
                return ldb_module_done(ctx->req, NULL, NULL,
 
1698
                                       LDB_ERR_OPERATIONS_ERROR);
 
1699
            }
 
1700
            msg = talloc_steal(delop->parents, msg);
 
1701
            if (!msg) {
 
1702
                return ldb_module_done(ctx->req, NULL, NULL,
 
1703
                                       LDB_ERR_OPERATIONS_ERROR);
 
1704
            }
 
1705
            delop->parents[delop->num_parents] = msg;
 
1706
            delop->num_parents++;
 
1707
        }
 
1708
        break;
 
1709
    case LDB_REPLY_REFERRAL:
 
1710
        /* ignore */
 
1711
        break;
 
1712
 
 
1713
    case LDB_REPLY_DONE:
 
1714
        if (delop->entry == NULL) {
 
1715
            /* no target, no party! */
 
1716
            return ldb_module_done(ctx->req, NULL, NULL,
 
1717
                                   LDB_ERR_OPERATIONS_ERROR);
 
1718
        }
 
1719
 
 
1720
        /* ok process the entry */
 
1721
        ret = mbof_del_execute_cont(delop);
 
1722
 
 
1723
        if (ret != LDB_SUCCESS) {
 
1724
            return ldb_module_done(ctx->req, NULL, NULL,
 
1725
                                   LDB_ERR_OPERATIONS_ERROR);
 
1726
        }
 
1727
    }
 
1728
 
 
1729
    talloc_zfree(ares);
 
1730
    return LDB_SUCCESS;
 
1731
}
 
1732
 
 
1733
static int mbof_del_execute_cont(struct mbof_del_operation *delop)
 
1734
{
 
1735
    struct mbof_del_ancestors_ctx *anc_ctx;
 
1736
    struct mbof_del_operation *parent;
 
1737
    struct mbof_del_ctx *del_ctx;
 
1738
    struct mbof_ctx *ctx;
 
1739
    struct mbof_dn_array *new_list;
 
1740
    int i;
 
1741
 
 
1742
    del_ctx = delop->del_ctx;
 
1743
    parent = delop->parent;
 
1744
    ctx = del_ctx->ctx;
 
1745
 
 
1746
    anc_ctx = talloc_zero(delop, struct mbof_del_ancestors_ctx);
 
1747
    if (!anc_ctx) {
 
1748
        return LDB_ERR_OPERATIONS_ERROR;
 
1749
    }
 
1750
    delop->anc_ctx = anc_ctx;
 
1751
 
 
1752
    new_list = talloc_zero(anc_ctx, struct mbof_dn_array);
 
1753
    if (!new_list) {
 
1754
        return LDB_ERR_OPERATIONS_ERROR;
 
1755
    }
 
1756
 
 
1757
    /* at the very least we have a number of memberof elements
 
1758
     * equal to the number of objects that have this entry as
 
1759
     * direct member */
 
1760
    new_list->num = delop->num_parents;
 
1761
 
 
1762
    /* attach the list to the operation */
 
1763
    delop->anc_ctx->new_list = new_list;
 
1764
    delop->anc_ctx->num_direct = new_list->num;
 
1765
 
 
1766
    /* do we have any direct parent at all ? */
 
1767
    if (new_list->num == 0) {
 
1768
        /* no entries at all, entry ended up being orphaned */
 
1769
        /* skip to directly set the new memberof list for this entry */
 
1770
 
 
1771
        return mbof_del_mod_entry(delop);
 
1772
    }
 
1773
 
 
1774
    /* fill in the list if we have parents */
 
1775
    new_list->dns = talloc_zero_array(new_list,
 
1776
                                      struct ldb_dn *,
 
1777
                                      new_list->num);
 
1778
    if (!new_list->dns) {
 
1779
        return LDB_ERR_OPERATIONS_ERROR;
 
1780
    }
 
1781
    for (i = 0; i < delop->num_parents; i++) {
 
1782
        new_list->dns[i] = delop->parents[i]->dn;
 
1783
    }
 
1784
 
 
1785
    /* before proceeding we also need to fetch the ancestors (anew as some may
 
1786
     * have changed by preceeding operations) */
 
1787
    return mbof_del_ancestors(delop);
 
1788
}
 
1789
 
 
1790
static int mbof_del_ancestors(struct mbof_del_operation *delop)
 
1791
{
 
1792
    struct mbof_del_ancestors_ctx *anc_ctx;
 
1793
    struct mbof_del_ctx *del_ctx;
 
1794
    struct mbof_ctx *ctx;
 
1795
    struct ldb_context *ldb;
 
1796
    struct mbof_dn_array *new_list;
 
1797
    static const char *attrs[] = { DB_MEMBEROF, NULL };
 
1798
    struct ldb_request *search;
 
1799
    int ret;
 
1800
 
 
1801
    del_ctx = delop->del_ctx;
 
1802
    ctx = del_ctx->ctx;
 
1803
    ldb = ldb_module_get_ctx(ctx->module);
 
1804
    anc_ctx = delop->anc_ctx;
 
1805
    new_list = anc_ctx->new_list;
 
1806
 
 
1807
    ret = ldb_build_search_req(&search, ldb, anc_ctx,
 
1808
                               new_list->dns[anc_ctx->cur],
 
1809
                               LDB_SCOPE_BASE, NULL, attrs, NULL,
 
1810
                               delop, mbof_del_anc_callback,
 
1811
                               ctx->req);
 
1812
    if (ret != LDB_SUCCESS) {
 
1813
        return ret;
 
1814
    }
 
1815
 
 
1816
    return ldb_request(ldb, search);
 
1817
}
 
1818
 
 
1819
static int mbof_del_anc_callback(struct ldb_request *req,
 
1820
                                 struct ldb_reply *ares)
 
1821
{
 
1822
    struct mbof_del_ancestors_ctx *anc_ctx;
 
1823
    struct mbof_del_operation *delop;
 
1824
    struct mbof_del_ctx *del_ctx;
 
1825
    struct mbof_ctx *ctx;
 
1826
    struct ldb_context *ldb;
 
1827
    struct ldb_message *msg;
 
1828
    const struct ldb_message_element *el;
 
1829
    struct mbof_dn_array *new_list;
 
1830
    struct ldb_dn *valdn;
 
1831
    int i, j, ret;
 
1832
 
 
1833
    delop = talloc_get_type(req->context, struct mbof_del_operation);
 
1834
    del_ctx = delop->del_ctx;
 
1835
    ctx = del_ctx->ctx;
 
1836
    ldb = ldb_module_get_ctx(ctx->module);
 
1837
    anc_ctx = delop->anc_ctx;
 
1838
    new_list = anc_ctx->new_list;
 
1839
 
 
1840
    if (!ares) {
 
1841
        return ldb_module_done(ctx->req, NULL, NULL,
 
1842
                               LDB_ERR_OPERATIONS_ERROR);
 
1843
    }
 
1844
    if (ares->error != LDB_SUCCESS) {
 
1845
        return ldb_module_done(ctx->req,
 
1846
                               ares->controls,
 
1847
                               ares->response,
 
1848
                               ares->error);
 
1849
    }
 
1850
 
 
1851
    switch (ares->type) {
 
1852
    case LDB_REPLY_ENTRY:
 
1853
        msg = ares->message;
 
1854
 
 
1855
        if (anc_ctx->entry != NULL) {
 
1856
            ldb_debug(ldb, LDB_DEBUG_TRACE,
 
1857
                           "Found multiple entries for (%s)",
 
1858
                           ldb_dn_get_linearized(anc_ctx->entry->dn));
 
1859
            /* more than one entry per dn ?? db corrupted ? */
 
1860
            return ldb_module_done(ctx->req, NULL, NULL,
 
1861
                                   LDB_ERR_OPERATIONS_ERROR);
 
1862
        }
 
1863
 
 
1864
        anc_ctx->entry = talloc_steal(anc_ctx, msg);
 
1865
        if (anc_ctx->entry == NULL) {
 
1866
            return ldb_module_done(ctx->req, NULL, NULL,
 
1867
                                   LDB_ERR_OPERATIONS_ERROR);
 
1868
        }
 
1869
        break;
 
1870
    case LDB_REPLY_REFERRAL:
 
1871
        /* ignore */
 
1872
        break;
 
1873
 
 
1874
    case LDB_REPLY_DONE:
 
1875
        if (anc_ctx->entry == NULL) {
 
1876
            /* no target, no party! */
 
1877
            return ldb_module_done(ctx->req, NULL, NULL,
 
1878
                                   LDB_ERR_OPERATIONS_ERROR);
 
1879
        }
 
1880
 
 
1881
        /* check entry */
 
1882
        el = ldb_msg_find_element(anc_ctx->entry, DB_MEMBEROF);
 
1883
        if (el) {
 
1884
            for (i = 0; i < el->num_values; i++) {
 
1885
                valdn = ldb_dn_from_ldb_val(new_list, ldb, &el->values[i]);
 
1886
                if (!valdn) {
 
1887
                    ldb_debug(ldb, LDB_DEBUG_TRACE,
 
1888
                                   "Invalid dn for memberof: (%s)",
 
1889
                                   (const char *)el->values[i].data);
 
1890
                    return ldb_module_done(ctx->req, NULL, NULL,
 
1891
                                           LDB_ERR_OPERATIONS_ERROR);
 
1892
                }
 
1893
                for (j = 0; j < new_list->num; j++) {
 
1894
                    if (ldb_dn_compare(valdn, new_list->dns[j]) == 0)
 
1895
                        break;
 
1896
                }
 
1897
                if (j < new_list->num) {
 
1898
                    talloc_free(valdn);
 
1899
                    continue;
 
1900
                }
 
1901
                /* do not re-add the original deleted entry by mistake */
 
1902
                if (ldb_dn_compare(valdn, del_ctx->first->entry_dn) == 0) {
 
1903
                    talloc_free(valdn);
 
1904
                    continue;
 
1905
                }
 
1906
                new_list->dns = talloc_realloc(new_list,
 
1907
                                               new_list->dns,
 
1908
                                               struct ldb_dn *,
 
1909
                                               new_list->num + 1);
 
1910
                if (!new_list->dns) {
 
1911
                    return ldb_module_done(ctx->req, NULL, NULL,
 
1912
                                           LDB_ERR_OPERATIONS_ERROR);
 
1913
                }
 
1914
                new_list->dns[new_list->num] = valdn;
 
1915
                new_list->num++;
 
1916
            }
 
1917
        }
 
1918
 
 
1919
        /* done with this one */
 
1920
        talloc_free(anc_ctx->entry);
 
1921
        anc_ctx->entry = NULL;
 
1922
        anc_ctx->cur++;
 
1923
 
 
1924
        /* check if we need to process any more */
 
1925
        if (anc_ctx->cur < anc_ctx->num_direct) {
 
1926
            /* ok process the next one */
 
1927
            ret = mbof_del_ancestors(delop);
 
1928
        } else {
 
1929
            /* ok, end of the story, proceed to modify the entry */
 
1930
            ret = mbof_del_mod_entry(delop);
 
1931
        }
 
1932
 
 
1933
        if (ret != LDB_SUCCESS) {
 
1934
            return ldb_module_done(ctx->req, NULL, NULL,
 
1935
                                   LDB_ERR_OPERATIONS_ERROR);
 
1936
        }
 
1937
    }
 
1938
 
 
1939
    talloc_zfree(ares);
 
1940
    return LDB_SUCCESS;
 
1941
}
 
1942
 
 
1943
static int mbof_del_mod_entry(struct mbof_del_operation *delop)
 
1944
{
 
1945
    struct mbof_del_ctx *del_ctx;
 
1946
    struct mbof_ctx *ctx;
 
1947
    struct ldb_context *ldb;
 
1948
    struct mbof_dn_array *new_list;
 
1949
    struct ldb_request *mod_req;
 
1950
    struct ldb_message *msg;
 
1951
    struct ldb_message_element *el;
 
1952
    struct ldb_dn **diff = NULL;
 
1953
    const char *name;
 
1954
    const char *val;
 
1955
    int i, j, k;
 
1956
    bool is_user;
 
1957
    int ret;
 
1958
 
 
1959
    del_ctx = delop->del_ctx;
 
1960
    ctx = del_ctx->ctx;
 
1961
    ldb = ldb_module_get_ctx(ctx->module);
 
1962
    new_list = delop->anc_ctx->new_list;
 
1963
 
 
1964
    /* if this is a user we need to find out which entries have been
 
1965
     * removed so that we can later schedule removal of memberuid
 
1966
     * attributes from these entries */
 
1967
    ret = entry_is_user_object(delop->entry);
 
1968
    switch (ret) {
 
1969
    case LDB_SUCCESS:
 
1970
        /* it's a user object  */
 
1971
        is_user = true;
 
1972
        break;
 
1973
    case LDB_ERR_NO_SUCH_ATTRIBUTE:
 
1974
        /* it is not a user object, continue */
 
1975
        is_user = false;
 
1976
        break;
 
1977
    default:
 
1978
        /* an error occured, return */
 
1979
        return ret;
 
1980
    }
 
1981
 
 
1982
    if (is_user) {
 
1983
        /* prepare memberuid delete list */
 
1984
        /* copy all original memberof entries, and then later remove
 
1985
         * the ones that will survive in the entry */
 
1986
        el = ldb_msg_find_element(delop->entry, DB_MEMBEROF);
 
1987
        if (!el || !el->num_values) {
 
1988
            return LDB_ERR_OPERATIONS_ERROR;
 
1989
        }
 
1990
        diff = talloc_array(del_ctx->muops, struct ldb_dn *,
 
1991
                            el->num_values + 1);
 
1992
        if (!diff) {
 
1993
            return LDB_ERR_OPERATIONS_ERROR;
 
1994
        }
 
1995
        for (i = 0, j = 0; i < el->num_values; i++) {
 
1996
            diff[j] = ldb_dn_from_ldb_val(diff, ldb, &el->values[i]);
 
1997
            if (!diff[j]) {
 
1998
                return LDB_ERR_OPERATIONS_ERROR;
 
1999
            }
 
2000
            /* skip the deleted entry if this is a delete op */
 
2001
            if (!del_ctx->is_mod) {
 
2002
                if (ldb_dn_compare(del_ctx->first->entry_dn, diff[j]) == 0) {
 
2003
                    continue;
 
2004
                }
 
2005
            }
 
2006
            j++;
 
2007
        }
 
2008
        /* zero terminate array */
 
2009
        diff[j] = NULL;
 
2010
    }
 
2011
 
 
2012
    /* change memberof on entry */
 
2013
    msg = ldb_msg_new(delop);
 
2014
    if (!msg) return LDB_ERR_OPERATIONS_ERROR;
 
2015
 
 
2016
    msg->dn = delop->entry_dn;
 
2017
 
 
2018
    if (new_list->num) {
 
2019
        ret = ldb_msg_add_empty(msg, DB_MEMBEROF, LDB_FLAG_MOD_REPLACE, &el);
 
2020
        if (ret != LDB_SUCCESS) {
 
2021
            return ret;
 
2022
        }
 
2023
 
 
2024
        el->values = talloc_array(el, struct ldb_val, new_list->num);
 
2025
        if (!el->values) {
 
2026
            return LDB_ERR_OPERATIONS_ERROR;
 
2027
        }
 
2028
        for (i = 0, j = 0; i < new_list->num; i++) {
 
2029
            if (ldb_dn_compare(new_list->dns[i], msg->dn) == 0)
 
2030
                continue;
 
2031
            val = ldb_dn_get_linearized(new_list->dns[i]);
 
2032
            if (!val) {
 
2033
                return LDB_ERR_OPERATIONS_ERROR;
 
2034
            }
 
2035
            el->values[j].length = strlen(val);
 
2036
            el->values[j].data = (uint8_t *)talloc_strdup(el->values, val);
 
2037
            if (!el->values[j].data) {
 
2038
                return LDB_ERR_OPERATIONS_ERROR;
 
2039
            }
 
2040
            j++;
 
2041
 
 
2042
            if (is_user) {
 
2043
                /* compare the entry's original memberof list with the new
 
2044
                 * one and for each missing entry add a memberuid removal
 
2045
                 * operation */
 
2046
                for (k = 0; diff[k]; k++) {
 
2047
                    if (ldb_dn_compare(new_list->dns[i], diff[k]) == 0) {
 
2048
                        break;
 
2049
                    }
 
2050
                }
 
2051
                if (diff[k]) {
 
2052
                    talloc_zfree(diff[k]);
 
2053
                    for (; diff[k + 1]; k++) {
 
2054
                        diff[k] = diff[k + 1];
 
2055
                    }
 
2056
                    diff[k] = NULL;
 
2057
                }
 
2058
            }
 
2059
        }
 
2060
        el->num_values = j;
 
2061
 
 
2062
    }
 
2063
    else {
 
2064
        ret = ldb_msg_add_empty(msg, DB_MEMBEROF, LDB_FLAG_MOD_DELETE, &el);
 
2065
        if (ret != LDB_SUCCESS) {
 
2066
            return ret;
 
2067
        }
 
2068
    }
 
2069
 
 
2070
    if (is_user && diff[0]) {
 
2071
        /* file memberuid removal operations */
 
2072
        name = ldb_msg_find_attr_as_string(delop->entry, DB_NAME, NULL);
 
2073
        if (!name) {
 
2074
            return LDB_ERR_OPERATIONS_ERROR;
 
2075
        }
 
2076
 
 
2077
        for (i = 0; diff[i]; i++) {
 
2078
            ret = mbof_append_muop(del_ctx, &del_ctx->muops,
 
2079
                                   &del_ctx->num_muops,
 
2080
                                   LDB_FLAG_MOD_DELETE,
 
2081
                                   diff[i], name);
 
2082
            if (ret != LDB_SUCCESS) {
 
2083
                return ret;
 
2084
            }
 
2085
        }
 
2086
    }
 
2087
 
 
2088
    ret = ldb_build_mod_req(&mod_req, ldb, delop,
 
2089
                            msg, NULL,
 
2090
                            delop, mbof_del_mod_callback,
 
2091
                            ctx->req);
 
2092
    if (ret != LDB_SUCCESS) {
 
2093
        return ret;
 
2094
    }
 
2095
    talloc_steal(mod_req, msg);
 
2096
 
 
2097
    return ldb_next_request(ctx->module, mod_req);
 
2098
}
 
2099
 
 
2100
static int mbof_del_mod_callback(struct ldb_request *req,
 
2101
                                 struct ldb_reply *ares)
 
2102
{
 
2103
    struct mbof_del_operation *delop;
 
2104
    struct mbof_del_ctx *del_ctx;
 
2105
    struct ldb_context *ldb;
 
2106
    struct mbof_ctx *ctx;
 
2107
    int ret;
 
2108
 
 
2109
    delop = talloc_get_type(req->context, struct mbof_del_operation);
 
2110
    del_ctx = delop->del_ctx;
 
2111
    ctx = del_ctx->ctx;
 
2112
    ldb = ldb_module_get_ctx(ctx->module);
 
2113
 
 
2114
    if (!ares) {
 
2115
        return ldb_module_done(ctx->req, NULL, NULL,
 
2116
                               LDB_ERR_OPERATIONS_ERROR);
 
2117
    }
 
2118
    if (ares->error != LDB_SUCCESS) {
 
2119
        return ldb_module_done(ctx->req,
 
2120
                               ares->controls,
 
2121
                               ares->response,
 
2122
                               ares->error);
 
2123
    }
 
2124
 
 
2125
    switch (ares->type) {
 
2126
    case LDB_REPLY_ENTRY:
 
2127
        ldb_debug(ldb, LDB_DEBUG_TRACE, "Got an entry on a non search op ?!");
 
2128
        /* shouldn't happen */
 
2129
        talloc_zfree(ares);
 
2130
        return ldb_module_done(ctx->req, NULL, NULL,
 
2131
                               LDB_ERR_OPERATIONS_ERROR);
 
2132
    case LDB_REPLY_REFERRAL:
 
2133
        /* ignore */
 
2134
        talloc_zfree(ares);
 
2135
        break;
 
2136
 
 
2137
    case LDB_REPLY_DONE:
 
2138
 
 
2139
        ret = mbof_del_progeny(delop);
 
2140
 
 
2141
        if (ret != LDB_SUCCESS) {
 
2142
            talloc_zfree(ares);
 
2143
            return ldb_module_done(ctx->req, NULL, NULL, ret);
 
2144
        }
 
2145
    }
 
2146
 
 
2147
    return LDB_SUCCESS;
 
2148
}
 
2149
 
 
2150
static int mbof_mod_add(struct mbof_mod_ctx *mod_ctx,
 
2151
                        struct mbof_dn_array *ael);
 
2152
 
 
2153
static int mbof_del_progeny(struct mbof_del_operation *delop)
 
2154
{
 
2155
    struct mbof_ctx *ctx;
 
2156
    struct mbof_del_ctx *del_ctx;
 
2157
    struct mbof_del_operation *nextop;
 
2158
    const struct ldb_message_element *el;
 
2159
    struct ldb_context *ldb;
 
2160
    struct ldb_dn *valdn;
 
2161
    int i, ret;
 
2162
 
 
2163
    del_ctx = delop->del_ctx;
 
2164
    ctx = del_ctx->ctx;
 
2165
    ldb = ldb_module_get_ctx(ctx->module);
 
2166
 
 
2167
    /* now verify if this entry is a group and members need to be processed as
 
2168
     * well */
 
2169
 
 
2170
    el = ldb_msg_find_element(delop->entry, DB_MEMBER);
 
2171
    if (el) {
 
2172
        for (i = 0; i < el->num_values; i++) {
 
2173
            valdn = ldb_dn_from_ldb_val(delop, ldb, &el->values[i]);
 
2174
            if (!valdn || !ldb_dn_validate(valdn)) {
 
2175
                ldb_debug(ldb, LDB_DEBUG_TRACE,
 
2176
                               "Invalid DN for member: (%s)",
 
2177
                               (const char *)el->values[i].data);
 
2178
                return LDB_ERR_INVALID_DN_SYNTAX;
 
2179
            }
 
2180
            ret = mbof_append_delop(delop, valdn);
 
2181
            if (ret != LDB_SUCCESS) {
 
2182
                return ret;
 
2183
            }
 
2184
        }
 
2185
    }
 
2186
 
 
2187
    /* finally find the next entry to handle */
 
2188
    ret = mbof_del_get_next(delop, &nextop);
 
2189
    if (ret != LDB_SUCCESS) {
 
2190
        return ret;
 
2191
    }
 
2192
 
 
2193
    free_delop_contents(delop);
 
2194
 
 
2195
    if (nextop) {
 
2196
        return mbof_del_execute_op(nextop);
 
2197
    }
 
2198
 
 
2199
    /* see if there are memberuid operations to perform */
 
2200
    if (del_ctx->muops) {
 
2201
        return mbof_del_muop(del_ctx);
 
2202
    }
 
2203
    /* see if there are follow functions to run */
 
2204
    if (del_ctx->follow_mod) {
 
2205
        return mbof_mod_add(del_ctx->follow_mod,
 
2206
                            del_ctx->follow_mod->to_add);
 
2207
    }
 
2208
 
 
2209
    /* ok, no more ops, this means our job is done */
 
2210
    return ldb_module_done(ctx->req,
 
2211
                           ctx->ret_ctrls,
 
2212
                           ctx->ret_resp,
 
2213
                           LDB_SUCCESS);
 
2214
}
 
2215
 
 
2216
static int mbof_del_get_next(struct mbof_del_operation *delop,
 
2217
                             struct mbof_del_operation **nextop)
 
2218
{
 
2219
    struct mbof_del_operation *top, *cop;
 
2220
    struct mbof_del_ctx *del_ctx;
 
2221
    struct mbof_dn *save, *tmp;
 
2222
 
 
2223
    del_ctx = delop->del_ctx;
 
2224
 
 
2225
    /* first of all, save the current delop in the history */
 
2226
    save = talloc_zero(del_ctx, struct mbof_dn);
 
2227
    if (!save) {
 
2228
        return LDB_ERR_OPERATIONS_ERROR;
 
2229
    }
 
2230
    save->dn = delop->entry_dn;
 
2231
 
 
2232
    if (del_ctx->history) {
 
2233
        tmp = del_ctx->history;
 
2234
        while (tmp->next) tmp = tmp->next;
 
2235
        tmp->next = save;
 
2236
    } else {
 
2237
        del_ctx->history = save;
 
2238
    }
 
2239
 
 
2240
    /* Find next one */
 
2241
    for (top = delop; top; top = top->parent) {
 
2242
        if (top->num_children == 0 || top->next_child >= top->num_children) {
 
2243
            /* no children, go for next one */
 
2244
            continue;
 
2245
        }
 
2246
 
 
2247
        while (top->next_child < top->num_children) {
 
2248
            cop = top->children[top->next_child];
 
2249
            top->next_child++;
 
2250
 
 
2251
            /* verify this operation has not already been performed */
 
2252
            for (tmp = del_ctx->history; tmp; tmp = tmp->next) {
 
2253
                if (ldb_dn_compare(tmp->dn, cop->entry_dn) == 0) {
 
2254
                    break;
 
2255
                }
 
2256
            }
 
2257
            if (tmp == NULL) {
 
2258
                /* and return the current one */
 
2259
                *nextop = cop;
 
2260
                return LDB_SUCCESS;
 
2261
            }
 
2262
        }
 
2263
    }
 
2264
 
 
2265
    /* we have no more ops */
 
2266
    *nextop = NULL;
 
2267
    return LDB_SUCCESS;
 
2268
}
 
2269
 
 
2270
static int mbof_del_fill_muop(struct mbof_del_ctx *del_ctx,
 
2271
                              struct ldb_message *entry)
 
2272
{
 
2273
    struct ldb_message_element *el;
 
2274
    char *name;
 
2275
    int ret;
 
2276
    int i;
 
2277
 
 
2278
    el = ldb_msg_find_element(entry, DB_MEMBEROF);
 
2279
    if (!el || el->num_values == 0) {
 
2280
        /* no memberof attributes ... */
 
2281
        return LDB_SUCCESS;
 
2282
    }
 
2283
 
 
2284
    ret = entry_is_user_object(entry);
 
2285
    switch (ret) {
 
2286
    case LDB_SUCCESS:
 
2287
        /* it's a user object, continue */
 
2288
        break;
 
2289
 
 
2290
    case LDB_ERR_NO_SUCH_ATTRIBUTE:
 
2291
        /* it is not a user object, just return */
 
2292
        return LDB_SUCCESS;
 
2293
 
 
2294
    default:
 
2295
        /* an error occured, return */
 
2296
        return ret;
 
2297
    }
 
2298
 
 
2299
    name = talloc_strdup(del_ctx,
 
2300
                         ldb_msg_find_attr_as_string(entry, DB_NAME, NULL));
 
2301
    if (!name) {
 
2302
        return LDB_ERR_OPERATIONS_ERROR;
 
2303
    }
 
2304
 
 
2305
    for (i = 0; i < el->num_values; i++) {
 
2306
        struct ldb_dn *valdn;
 
2307
 
 
2308
        valdn = ldb_dn_from_ldb_val(del_ctx->muops,
 
2309
                                    ldb_module_get_ctx(del_ctx->ctx->module),
 
2310
                                    &el->values[i]);
 
2311
        if (!valdn || !ldb_dn_validate(valdn)) {
 
2312
            ldb_debug(ldb_module_get_ctx(del_ctx->ctx->module),
 
2313
                      LDB_DEBUG_ERROR,
 
2314
                      "Invalid dn value: [%s]",
 
2315
                      (const char *)el->values[i].data);
 
2316
        }
 
2317
 
 
2318
        ret = mbof_append_muop(del_ctx, &del_ctx->muops,
 
2319
                               &del_ctx->num_muops,
 
2320
                               LDB_FLAG_MOD_DELETE,
 
2321
                               valdn, name);
 
2322
        if (ret != LDB_SUCCESS) {
 
2323
            return ret;
 
2324
        }
 
2325
    }
 
2326
 
 
2327
    return LDB_SUCCESS;
 
2328
}
 
2329
 
 
2330
/* del memberuid attributes to parent groups */
 
2331
static int mbof_del_muop(struct mbof_del_ctx *del_ctx)
 
2332
{
 
2333
    struct ldb_context *ldb;
 
2334
    struct ldb_message *msg;
 
2335
    struct ldb_request *mod_req;
 
2336
    struct mbof_ctx *ctx;
 
2337
    int ret;
 
2338
 
 
2339
    ctx = del_ctx->ctx;
 
2340
    ldb = ldb_module_get_ctx(ctx->module);
 
2341
 
 
2342
    msg = ldb_msg_new(del_ctx);
 
2343
    if (!msg) return LDB_ERR_OPERATIONS_ERROR;
 
2344
 
 
2345
    msg->dn = del_ctx->muops[del_ctx->cur_muop].dn;
 
2346
    msg->elements = del_ctx->muops[del_ctx->cur_muop].el;
 
2347
    msg->num_elements = 1;
 
2348
 
 
2349
    ret = ldb_build_mod_req(&mod_req, ldb, del_ctx,
 
2350
                            msg, NULL,
 
2351
                            del_ctx, mbof_del_muop_callback,
 
2352
                            ctx->req);
 
2353
    if (ret != LDB_SUCCESS) {
 
2354
        return ret;
 
2355
    }
 
2356
 
 
2357
    return ldb_next_request(ctx->module, mod_req);
 
2358
}
 
2359
 
 
2360
static int mbof_del_muop_callback(struct ldb_request *req,
 
2361
                                  struct ldb_reply *ares)
 
2362
{
 
2363
    struct mbof_del_ctx *del_ctx;
 
2364
    struct mbof_ctx *ctx;
 
2365
    int ret;
 
2366
 
 
2367
    del_ctx = talloc_get_type(req->context, struct mbof_del_ctx);
 
2368
    ctx = del_ctx->ctx;
 
2369
 
 
2370
    if (!ares) {
 
2371
        return ldb_module_done(ctx->req, NULL, NULL,
 
2372
                               LDB_ERR_OPERATIONS_ERROR);
 
2373
    }
 
2374
    if (ares->error != LDB_SUCCESS) {
 
2375
        return ldb_module_done(ctx->req,
 
2376
                               ares->controls,
 
2377
                               ares->response,
 
2378
                               ares->error);
 
2379
    }
 
2380
 
 
2381
    switch (ares->type) {
 
2382
    case LDB_REPLY_ENTRY:
 
2383
        /* shouldn't happen */
 
2384
        talloc_zfree(ares);
 
2385
        return ldb_module_done(ctx->req, NULL, NULL,
 
2386
                               LDB_ERR_OPERATIONS_ERROR);
 
2387
    case LDB_REPLY_REFERRAL:
 
2388
        /* ignore */
 
2389
        break;
 
2390
 
 
2391
    case LDB_REPLY_DONE:
 
2392
        del_ctx->cur_muop++;
 
2393
        if (del_ctx->cur_muop < del_ctx->num_muops) {
 
2394
            ret = mbof_del_muop(del_ctx);
 
2395
        }
 
2396
        /* see if there are follow functions to run */
 
2397
        else if (del_ctx->follow_mod) {
 
2398
            return mbof_mod_add(del_ctx->follow_mod,
 
2399
                                del_ctx->follow_mod->to_add);
 
2400
        }
 
2401
        else {
 
2402
            return ldb_module_done(ctx->req,
 
2403
                                   ctx->ret_ctrls,
 
2404
                                   ctx->ret_resp,
 
2405
                                   LDB_SUCCESS);
 
2406
        }
 
2407
 
 
2408
        if (ret != LDB_SUCCESS) {
 
2409
            talloc_zfree(ares);
 
2410
            return ldb_module_done(ctx->req, NULL, NULL, ret);
 
2411
        }
 
2412
    }
 
2413
 
 
2414
    talloc_zfree(ares);
 
2415
    return LDB_SUCCESS;
 
2416
}
 
2417
 
 
2418
/* delop may carry on a lot of memory, so we need a function to clean up
 
2419
 * the payload without breaking the delop chain */
 
2420
static void free_delop_contents(struct mbof_del_operation *delop)
 
2421
{
 
2422
    talloc_zfree(delop->entry);
 
2423
    talloc_zfree(delop->parents);
 
2424
    talloc_zfree(delop->anc_ctx);
 
2425
    delop->num_parents = 0;
 
2426
    delop->cur_parent = 0;
 
2427
}
 
2428
 
 
2429
/* mod operation */
 
2430
 
 
2431
/* A modify operation just implements either an add operation, or a delete
 
2432
 * operation or both (replace) in turn.
 
2433
 * The only difference between a modify and a pure add or a pure delete is that
 
2434
 * the object is not created a new or not completely removed, but the setup just
 
2435
 * treats it in the same way children objects are treated in a pure add or delete
 
2436
 * operation. A list of appropriate parents and objects to modify is built, then
 
2437
 * we jump directly in the add or delete code.
 
2438
 * If both add and delete are necessary, delete operations are performed first
 
2439
 * and then a followup add operation is concatenated */
 
2440
 
 
2441
static int mbof_mod_callback(struct ldb_request *req,
 
2442
                             struct ldb_reply *ares);
 
2443
static int mbof_orig_mod(struct mbof_mod_ctx *mod_ctx);
 
2444
static int mbof_orig_mod_callback(struct ldb_request *req,
 
2445
                                  struct ldb_reply *ares);
 
2446
static int mbof_mod_process(struct mbof_mod_ctx *mod_ctx, bool *done);
 
2447
static int mbof_mod_delete(struct mbof_mod_ctx *mod_ctx,
 
2448
                           struct mbof_dn_array *del);
 
2449
static int mbof_fill_dn_array(TALLOC_CTX *memctx,
 
2450
                              struct ldb_context *ldb,
 
2451
                              const struct ldb_message_element *el,
 
2452
                              struct mbof_dn_array **dn_array);
 
2453
 
 
2454
static int memberof_mod(struct ldb_module *module, struct ldb_request *req)
 
2455
{
 
2456
    struct ldb_message_element *el;
 
2457
    struct mbof_mod_ctx *mod_ctx;
 
2458
    struct mbof_ctx *ctx;
 
2459
    static const char *attrs[] = {DB_MEMBER, DB_MEMBEROF, NULL};
 
2460
    struct ldb_context *ldb = ldb_module_get_ctx(module);
 
2461
    struct ldb_request *search;
 
2462
    int ret;
 
2463
 
 
2464
    if (ldb_dn_is_special(req->op.mod.message->dn)) {
 
2465
        /* do not manipulate our control entries */
 
2466
        return ldb_next_request(module, req);
 
2467
    }
 
2468
 
 
2469
    /* check if memberof is specified */
 
2470
    el = ldb_msg_find_element(req->op.mod.message, DB_MEMBEROF);
 
2471
    if (el) {
 
2472
        ldb_debug(ldb, LDB_DEBUG_ERROR,
 
2473
                  "Error: the memberof attribute is readonly.");
 
2474
        return LDB_ERR_UNWILLING_TO_PERFORM;
 
2475
    }
 
2476
 
 
2477
    /* check if memberuid is specified */
 
2478
    el = ldb_msg_find_element(req->op.mod.message, DB_MEMBERUID);
 
2479
    if (el) {
 
2480
        ldb_debug(ldb, LDB_DEBUG_ERROR,
 
2481
                  "Error: the memberuid attribute is readonly.");
 
2482
        return LDB_ERR_UNWILLING_TO_PERFORM;
 
2483
    }
 
2484
 
 
2485
    ctx = mbof_init(module, req);
 
2486
    if (!ctx) {
 
2487
        return LDB_ERR_OPERATIONS_ERROR;
 
2488
    }
 
2489
 
 
2490
    mod_ctx = talloc_zero(ctx, struct mbof_mod_ctx);
 
2491
    if (!mod_ctx) {
 
2492
        talloc_free(ctx);
 
2493
        return LDB_ERR_OPERATIONS_ERROR;
 
2494
    }
 
2495
    mod_ctx->ctx = ctx;
 
2496
 
 
2497
    mod_ctx->msg = ldb_msg_copy(mod_ctx, req->op.mod.message);
 
2498
    if (!mod_ctx->msg) {
 
2499
        return LDB_ERR_OPERATIONS_ERROR;
 
2500
    }
 
2501
 
 
2502
    /* continue with normal ops if there are no members */
 
2503
    el = ldb_msg_find_element(mod_ctx->msg, DB_MEMBER);
 
2504
    if (!el) {
 
2505
        mod_ctx->terminate = true;
 
2506
        return mbof_orig_mod(mod_ctx);
 
2507
    }
 
2508
 
 
2509
    mod_ctx->membel = el;
 
2510
 
 
2511
    /* can't do anything,
 
2512
     * must check first what's on the entry */
 
2513
    ret = ldb_build_search_req(&search, ldb, mod_ctx,
 
2514
                               mod_ctx->msg->dn, LDB_SCOPE_BASE,
 
2515
                               NULL, attrs, NULL,
 
2516
                               mod_ctx, mbof_mod_callback,
 
2517
                               req);
 
2518
    if (ret != LDB_SUCCESS) {
 
2519
        talloc_free(ctx);
 
2520
        return ret;
 
2521
    }
 
2522
 
 
2523
    return ldb_request(ldb, search);
 
2524
}
 
2525
 
 
2526
 
 
2527
static int mbof_mod_callback(struct ldb_request *req,
 
2528
                             struct ldb_reply *ares)
 
2529
{
 
2530
    struct mbof_mod_ctx *mod_ctx;
 
2531
    struct ldb_context *ldb;
 
2532
    struct mbof_ctx *ctx;
 
2533
    int ret;
 
2534
 
 
2535
    mod_ctx = talloc_get_type(req->context, struct mbof_mod_ctx);
 
2536
    ctx = mod_ctx->ctx;
 
2537
    ldb = ldb_module_get_ctx(ctx->module);
 
2538
 
 
2539
    if (!ares) {
 
2540
        return ldb_module_done(ctx->req, NULL, NULL,
 
2541
                               LDB_ERR_OPERATIONS_ERROR);
 
2542
    }
 
2543
    if (ares->error != LDB_SUCCESS) {
 
2544
        return ldb_module_done(ctx->req,
 
2545
                               ares->controls,
 
2546
                               ares->response,
 
2547
                               ares->error);
 
2548
    }
 
2549
 
 
2550
    switch (ares->type) {
 
2551
    case LDB_REPLY_ENTRY:
 
2552
        if (mod_ctx->entry != NULL) {
 
2553
            ldb_debug(ldb, LDB_DEBUG_TRACE,
 
2554
                           "Found multiple entries for (%s)",
 
2555
                           ldb_dn_get_linearized(mod_ctx->msg->dn));
 
2556
            /* more than one entry per dn ?? db corrupted ? */
 
2557
            return ldb_module_done(ctx->req, NULL, NULL,
 
2558
                                   LDB_ERR_OPERATIONS_ERROR);
 
2559
        }
 
2560
 
 
2561
        mod_ctx->entry = talloc_steal(mod_ctx, ares->message);
 
2562
        if (mod_ctx->entry == NULL) {
 
2563
            return ldb_module_done(ctx->req, NULL, NULL,
 
2564
                                   LDB_ERR_OPERATIONS_ERROR);
 
2565
        }
 
2566
        break;
 
2567
    case LDB_REPLY_REFERRAL:
 
2568
        /* ignore */
 
2569
        break;
 
2570
 
 
2571
    case LDB_REPLY_DONE:
 
2572
        if (mod_ctx->entry == NULL) {
 
2573
            ldb_debug(ldb, LDB_DEBUG_TRACE, "Entry not found (%s)",
 
2574
                           ldb_dn_get_linearized(mod_ctx->msg->dn));
 
2575
            /* this target does not exists, too bad! */
 
2576
            return ldb_module_done(ctx->req, NULL, NULL,
 
2577
                                   LDB_ERR_NO_SUCH_OBJECT);
 
2578
        }
 
2579
 
 
2580
        ret = mbof_orig_mod(mod_ctx);
 
2581
        if (ret != LDB_SUCCESS) {
 
2582
            talloc_zfree(ares);
 
2583
            return ldb_module_done(ctx->req, NULL, NULL, ret);
 
2584
        }
 
2585
    }
 
2586
 
 
2587
    talloc_zfree(ares);
 
2588
    return LDB_SUCCESS;
 
2589
}
 
2590
 
 
2591
static int mbof_orig_mod(struct mbof_mod_ctx *mod_ctx)
 
2592
{
 
2593
    struct ldb_request *mod_req;
 
2594
    struct ldb_context *ldb;
 
2595
    struct mbof_ctx *ctx;
 
2596
    int ret;
 
2597
 
 
2598
    ctx = mod_ctx->ctx;
 
2599
    ldb = ldb_module_get_ctx(ctx->module);
 
2600
 
 
2601
    ret = ldb_build_mod_req(&mod_req, ldb, ctx->req,
 
2602
                            mod_ctx->msg, ctx->req->controls,
 
2603
                            mod_ctx, mbof_orig_mod_callback,
 
2604
                            ctx->req);
 
2605
    if (ret != LDB_SUCCESS) {
 
2606
        return ret;
 
2607
    }
 
2608
 
 
2609
    return ldb_next_request(ctx->module, mod_req);
 
2610
}
 
2611
 
 
2612
static int mbof_orig_mod_callback(struct ldb_request *req,
 
2613
                                  struct ldb_reply *ares)
 
2614
{
 
2615
    struct ldb_context *ldb;
 
2616
    struct mbof_mod_ctx *mod_ctx;
 
2617
    struct mbof_ctx *ctx;
 
2618
    int ret;
 
2619
 
 
2620
    mod_ctx = talloc_get_type(req->context, struct mbof_mod_ctx);
 
2621
    ctx = mod_ctx->ctx;
 
2622
    ldb = ldb_module_get_ctx(ctx->module);
 
2623
 
 
2624
    if (!ares) {
 
2625
        return ldb_module_done(ctx->req, NULL, NULL,
 
2626
                               LDB_ERR_OPERATIONS_ERROR);
 
2627
    }
 
2628
    if (ares->error != LDB_SUCCESS) {
 
2629
        return ldb_module_done(ctx->req,
 
2630
                               ares->controls,
 
2631
                               ares->response,
 
2632
                               ares->error);
 
2633
    }
 
2634
 
 
2635
    if (ares->type != LDB_REPLY_DONE) {
 
2636
        talloc_zfree(ares);
 
2637
        ldb_debug(ldb, LDB_DEBUG_TRACE, "Invalid reply type!");
 
2638
        ldb_set_errstring(ldb, "Invalid reply type!");
 
2639
        return ldb_module_done(ctx->req, NULL, NULL,
 
2640
                               LDB_ERR_OPERATIONS_ERROR);
 
2641
    }
 
2642
 
 
2643
    /* save real call stuff */
 
2644
    ctx->ret_ctrls = talloc_steal(ctx, ares->controls);
 
2645
    ctx->ret_resp = talloc_steal(ctx, ares->response);
 
2646
 
 
2647
    if (!mod_ctx->terminate) {
 
2648
        /* next step */
 
2649
        ret = mbof_mod_process(mod_ctx, &mod_ctx->terminate);
 
2650
        if (ret != LDB_SUCCESS) {
 
2651
            talloc_zfree(ares);
 
2652
            return ldb_module_done(ctx->req, NULL, NULL, ret);
 
2653
        }
 
2654
    }
 
2655
 
 
2656
    if (mod_ctx->terminate) {
 
2657
        talloc_zfree(ares);
 
2658
        return ldb_module_done(ctx->req,
 
2659
                               ctx->ret_ctrls,
 
2660
                               ctx->ret_resp,
 
2661
                               LDB_SUCCESS);
 
2662
    }
 
2663
 
 
2664
    talloc_zfree(ares);
 
2665
    return LDB_SUCCESS;
 
2666
}
 
2667
 
 
2668
static int mbof_mod_process(struct mbof_mod_ctx *mod_ctx, bool *done)
 
2669
{
 
2670
    const struct ldb_message_element *el;
 
2671
    struct ldb_context *ldb;
 
2672
    struct mbof_ctx *ctx;
 
2673
    struct mbof_dn_array *removed;
 
2674
    struct mbof_dn_array *added;
 
2675
    int i, j, ret;
 
2676
 
 
2677
    ctx = mod_ctx->ctx;
 
2678
    ldb = ldb_module_get_ctx(ctx->module);
 
2679
 
 
2680
    switch (mod_ctx->membel->flags) {
 
2681
    case LDB_FLAG_MOD_ADD:
 
2682
 
 
2683
        ret = mbof_fill_dn_array(mod_ctx, ldb, mod_ctx->membel, &added);
 
2684
        if (ret != LDB_SUCCESS) {
 
2685
            return ret;
 
2686
        }
 
2687
 
 
2688
        return mbof_mod_add(mod_ctx, added);
 
2689
 
 
2690
    case LDB_FLAG_MOD_DELETE:
 
2691
 
 
2692
        if (mod_ctx->membel->num_values == 0) {
 
2693
            el = ldb_msg_find_element(mod_ctx->entry, DB_MEMBER);
 
2694
        } else {
 
2695
            el = mod_ctx->membel;
 
2696
        }
 
2697
 
 
2698
        if (!el) {
 
2699
            /* nothing to do really */
 
2700
            *done = true;
 
2701
            return LDB_SUCCESS;
 
2702
        }
 
2703
 
 
2704
        ret = mbof_fill_dn_array(mod_ctx, ldb, el, &removed);
 
2705
        if (ret != LDB_SUCCESS) {
 
2706
            return ret;
 
2707
        }
 
2708
 
 
2709
        return mbof_mod_delete(mod_ctx, removed);
 
2710
 
 
2711
    case LDB_FLAG_MOD_REPLACE:
 
2712
 
 
2713
        removed = NULL;
 
2714
        el = ldb_msg_find_element(mod_ctx->entry, DB_MEMBER);
 
2715
        if (el) {
 
2716
            ret = mbof_fill_dn_array(mod_ctx, ldb, el, &removed);
 
2717
            if (ret != LDB_SUCCESS) {
 
2718
                return ret;
 
2719
            }
 
2720
        }
 
2721
 
 
2722
        added = NULL;
 
2723
        el = mod_ctx->membel;
 
2724
        if (el) {
 
2725
            ret = mbof_fill_dn_array(mod_ctx, ldb, el, &added);
 
2726
            if (ret != LDB_SUCCESS) {
 
2727
                return ret;
 
2728
            }
 
2729
        }
 
2730
 
 
2731
        /* remove from arrays values that ended up unchanged */
 
2732
        if (removed && removed->num && added && added->num) {
 
2733
            for (i = 0; i < added->num; i++) {
 
2734
                for (j = 0; j < removed->num; j++) {
 
2735
                    if (ldb_dn_compare(added->dns[i], removed->dns[j]) == 0) {
 
2736
                        break;
 
2737
                    }
 
2738
                }
 
2739
                if (j < removed->num) {
 
2740
                    /* preexisting one, not removed, nor added */
 
2741
                    for (; j+1 < removed->num; j++) {
 
2742
                        removed->dns[j] = removed->dns[j+1];
 
2743
                    }
 
2744
                    removed->num--;
 
2745
                    for (j = i; j+1 < added->num; j++) {
 
2746
                        added->dns[j] = added->dns[j+1];
 
2747
                    }
 
2748
                    added->num--;
 
2749
                    i--;
 
2750
                }
 
2751
            }
 
2752
        }
 
2753
 
 
2754
        /* if we need to add something put it away so that it
 
2755
         * can be done after all delete operations are over */
 
2756
        if (added && added->num) {
 
2757
            mod_ctx->to_add = added;
 
2758
        }
 
2759
 
 
2760
        /* if we have something to remove do it first */
 
2761
        if (removed && removed->num) {
 
2762
            return mbof_mod_delete(mod_ctx, removed);
 
2763
        }
 
2764
 
 
2765
        /* if there is nothing to remove and we have stuff to add
 
2766
         * do it right away */
 
2767
        if (mod_ctx->to_add) {
 
2768
            return mbof_mod_add(mod_ctx, added);
 
2769
        }
 
2770
 
 
2771
        /* the replacement function resulted in a null op,
 
2772
         * nothing to do, return happily */
 
2773
        *done = true;
 
2774
        return LDB_SUCCESS;
 
2775
    }
 
2776
 
 
2777
    return LDB_ERR_OPERATIONS_ERROR;
 
2778
}
 
2779
 
 
2780
static int mbof_mod_add(struct mbof_mod_ctx *mod_ctx,
 
2781
                        struct mbof_dn_array *ael)
 
2782
{
 
2783
    const struct ldb_message_element *el;
 
2784
    struct mbof_dn_array *parents;
 
2785
    struct mbof_add_ctx *add_ctx;
 
2786
    struct ldb_context *ldb;
 
2787
    struct mbof_ctx *ctx;
 
2788
    int i, ret;
 
2789
 
 
2790
    ctx = mod_ctx->ctx;
 
2791
    ldb = ldb_module_get_ctx(ctx->module);
 
2792
 
 
2793
    el = ldb_msg_find_element(mod_ctx->entry, DB_MEMBEROF);
 
2794
 
 
2795
    /* all the parents + itself */
 
2796
    ret = mbof_fill_dn_array(mod_ctx, ldb, el, &parents);
 
2797
    if (ret != LDB_SUCCESS) {
 
2798
        return ret;
 
2799
    }
 
2800
 
 
2801
    parents->dns = talloc_realloc(parents, parents->dns,
 
2802
                                  struct ldb_dn *, parents->num + 1);
 
2803
    if (!parents->dns) {
 
2804
        return LDB_ERR_OPERATIONS_ERROR;
 
2805
    }
 
2806
    parents->dns[parents->num] = mod_ctx->entry->dn;
 
2807
    parents->num++;
 
2808
 
 
2809
    add_ctx = talloc_zero(mod_ctx, struct mbof_add_ctx);
 
2810
    if (!add_ctx) {
 
2811
        return LDB_ERR_OPERATIONS_ERROR;
 
2812
    }
 
2813
    add_ctx->ctx = ctx;
 
2814
    add_ctx->msg_dn = mod_ctx->msg->dn;
 
2815
 
 
2816
    for (i = 0; i < ael->num; i++) {
 
2817
        ret = mbof_append_addop(add_ctx, parents, ael->dns[i]);
 
2818
        if (ret != LDB_SUCCESS) {
 
2819
            return ret;
 
2820
        }
 
2821
    }
 
2822
 
 
2823
    return mbof_next_add(add_ctx->add_list);
 
2824
}
 
2825
 
 
2826
static int mbof_mod_delete(struct mbof_mod_ctx *mod_ctx,
 
2827
                           struct mbof_dn_array *del)
 
2828
{
 
2829
    struct mbof_del_operation *first;
 
2830
    struct mbof_del_ctx *del_ctx;
 
2831
    struct mbof_ctx *ctx;
 
2832
    int i, ret;
 
2833
 
 
2834
    ctx = mod_ctx->ctx;
 
2835
 
 
2836
    del_ctx = talloc_zero(mod_ctx, struct mbof_del_ctx);
 
2837
    if (!del_ctx) {
 
2838
        return LDB_ERR_OPERATIONS_ERROR;
 
2839
    }
 
2840
    del_ctx->ctx = ctx;
 
2841
    del_ctx->is_mod = true;
 
2842
 
 
2843
    /* create first entry */
 
2844
    /* the first entry is the parent of all entries and the one where we
 
2845
     * remove member from, it does not get the same treatment as others */
 
2846
    first = talloc_zero(del_ctx, struct mbof_del_operation);
 
2847
    if (!first) {
 
2848
        return LDB_ERR_OPERATIONS_ERROR;
 
2849
    }
 
2850
    del_ctx->first = first;
 
2851
 
 
2852
    first->del_ctx = del_ctx;
 
2853
    first->entry = mod_ctx->entry;
 
2854
    first->entry_dn = mod_ctx->entry->dn;
 
2855
 
 
2856
    /* prepare del sets */
 
2857
    for (i = 0; i < del->num; i++) {
 
2858
        ret = mbof_append_delop(first, del->dns[i]);
 
2859
        if (ret != LDB_SUCCESS) {
 
2860
            return ret;
 
2861
        }
 
2862
    }
 
2863
 
 
2864
    /* add followup function if we also have stuff to add */
 
2865
    if (mod_ctx->to_add) {
 
2866
        del_ctx->follow_mod = mod_ctx;
 
2867
    }
 
2868
 
 
2869
    /* now that sets are built, start processing */
 
2870
    return mbof_del_execute_op(first->children[0]);
 
2871
}
 
2872
 
 
2873
static int mbof_fill_dn_array(TALLOC_CTX *memctx,
 
2874
                              struct ldb_context *ldb,
 
2875
                              const struct ldb_message_element *el,
 
2876
                              struct mbof_dn_array **dn_array)
 
2877
{
 
2878
    struct mbof_dn_array *ar;
 
2879
    struct ldb_dn *valdn;
 
2880
    int i;
 
2881
 
 
2882
    ar = talloc_zero(memctx, struct mbof_dn_array);
 
2883
    if (!ar) {
 
2884
        return LDB_ERR_OPERATIONS_ERROR;
 
2885
    }
 
2886
    *dn_array = ar;
 
2887
 
 
2888
    if (!el || el->num_values == 0) {
 
2889
        return LDB_SUCCESS;
 
2890
    }
 
2891
 
 
2892
    ar->dns = talloc_array(ar, struct ldb_dn *, el->num_values);
 
2893
    if (!ar->dns) {
 
2894
        return LDB_ERR_OPERATIONS_ERROR;
 
2895
    }
 
2896
    ar->num = el->num_values;
 
2897
 
 
2898
    for (i = 0; i < ar->num; i++) {
 
2899
        valdn = ldb_dn_from_ldb_val(ar, ldb, &el->values[i]);
 
2900
        if (!valdn || !ldb_dn_validate(valdn)) {
 
2901
            ldb_debug(ldb, LDB_DEBUG_TRACE, "Invalid dn value: [%s]",
 
2902
                                            (const char *)el->values[i].data);
 
2903
            return LDB_ERR_INVALID_DN_SYNTAX;
 
2904
        }
 
2905
        ar->dns[i] = valdn;
 
2906
    }
 
2907
 
 
2908
    return LDB_SUCCESS;
 
2909
}
 
2910
 
 
2911
 
 
2912
/*************************
 
2913
 * Cleanup task routines *
 
2914
 *************************/
 
2915
 
 
2916
struct mbof_member {
 
2917
    struct mbof_member *prev;
 
2918
    struct mbof_member *next;
 
2919
 
 
2920
    struct ldb_dn *dn;
 
2921
    const char *name;
 
2922
    bool orig_has_memberof;
 
2923
    bool orig_has_memberuid;
 
2924
    struct ldb_message_element *orig_members;
 
2925
 
 
2926
    struct mbof_member **members;
 
2927
 
 
2928
    hash_table_t *memberofs;
 
2929
 
 
2930
    struct ldb_message_element *memuids;
 
2931
 
 
2932
    enum { MBOF_GROUP_TO_DO = 0,
 
2933
           MBOF_GROUP_DONE,
 
2934
           MBOF_USER,
 
2935
           MBOF_ITER_ERROR } status;
 
2936
};
 
2937
 
 
2938
struct mbof_rcmp_context {
 
2939
    struct ldb_module *module;
 
2940
    struct ldb_request *req;
 
2941
 
 
2942
    struct mbof_member *user_list;
 
2943
    hash_table_t *user_table;
 
2944
 
 
2945
    struct mbof_member *group_list;
 
2946
    hash_table_t *group_table;
 
2947
};
 
2948
 
 
2949
static void *hash_alloc(const size_t size, void *pvt)
 
2950
{
 
2951
    return talloc_size(pvt, size);
 
2952
}
 
2953
 
 
2954
static void hash_free(void *ptr, void *pvt)
 
2955
{
 
2956
    talloc_free(ptr);
 
2957
}
 
2958
 
 
2959
static int mbof_steal_msg_el(TALLOC_CTX *memctx,
 
2960
                             const char *name,
 
2961
                             struct ldb_message *msg,
 
2962
                             struct ldb_message_element **_dest)
 
2963
{
 
2964
    struct ldb_message_element *src;
 
2965
    struct ldb_message_element *dest;
 
2966
 
 
2967
    src = ldb_msg_find_element(msg, name);
 
2968
    if (!src) {
 
2969
        return LDB_ERR_NO_SUCH_ATTRIBUTE;
 
2970
    }
 
2971
 
 
2972
    dest = talloc_zero(memctx, struct ldb_message_element);
 
2973
    if (!dest) {
 
2974
        return LDB_ERR_OPERATIONS_ERROR;
 
2975
    }
 
2976
 
 
2977
    *dest = *src;
 
2978
    talloc_steal(dest, dest->name);
 
2979
    talloc_steal(dest, dest->values);
 
2980
 
 
2981
    *_dest = dest;
 
2982
    return LDB_SUCCESS;
 
2983
}
 
2984
 
 
2985
static int mbof_rcmp_usr_callback(struct ldb_request *req,
 
2986
                                  struct ldb_reply *ares);
 
2987
static int mbof_rcmp_search_groups(struct mbof_rcmp_context *ctx);
 
2988
static int mbof_rcmp_grp_callback(struct ldb_request *req,
 
2989
                                  struct ldb_reply *ares);
 
2990
static int mbof_member_update(struct mbof_rcmp_context *ctx,
 
2991
                              struct mbof_member *parent,
 
2992
                              struct mbof_member *mem);
 
2993
static bool mbof_member_iter(hash_entry_t *item, void *user_data);
 
2994
static int mbof_add_memuid(struct mbof_member *grp, const char *user);
 
2995
static int mbof_rcmp_update(struct mbof_rcmp_context *ctx);
 
2996
static int mbof_rcmp_mod_callback(struct ldb_request *req,
 
2997
                                  struct ldb_reply *ares);
 
2998
 
 
2999
static int memberof_recompute_task(struct ldb_module *module,
 
3000
                                   struct ldb_request *req)
 
3001
{
 
3002
    struct ldb_context *ldb = ldb_module_get_ctx(module);
 
3003
    static const char *attrs[] = { DB_NAME, DB_MEMBEROF, NULL };
 
3004
    static const char *filter = "(objectclass=user)";
 
3005
    struct mbof_rcmp_context *ctx;
 
3006
    struct ldb_request *src_req;
 
3007
    int ret;
 
3008
 
 
3009
    ctx = talloc_zero(req, struct mbof_rcmp_context);
 
3010
    if (!ctx) {
 
3011
        return LDB_ERR_OPERATIONS_ERROR;
 
3012
    }
 
3013
    ctx->module = module;
 
3014
    ctx->req = req;
 
3015
 
 
3016
    ret = hash_create_ex(1024, &ctx->user_table, 0, 0, 0, 0,
 
3017
                         hash_alloc, hash_free, ctx, NULL, NULL);
 
3018
    if (ret != HASH_SUCCESS) {
 
3019
        return LDB_ERR_OPERATIONS_ERROR;
 
3020
    }
 
3021
 
 
3022
    ret = ldb_build_search_req(&src_req, ldb, ctx,
 
3023
                               NULL, LDB_SCOPE_SUBTREE,
 
3024
                               filter, attrs, NULL,
 
3025
                               ctx, mbof_rcmp_usr_callback, ctx->req);
 
3026
    if (ret != LDB_SUCCESS) {
 
3027
        return ret;
 
3028
    }
 
3029
 
 
3030
    return ldb_request(ldb, src_req);
 
3031
}
 
3032
 
 
3033
static int mbof_rcmp_usr_callback(struct ldb_request *req,
 
3034
                                  struct ldb_reply *ares)
 
3035
{
 
3036
    struct mbof_rcmp_context *ctx;
 
3037
    struct mbof_member *usr;
 
3038
    hash_value_t value;
 
3039
    hash_key_t key;
 
3040
    const char *name;
 
3041
    int ret;
 
3042
 
 
3043
    ctx = talloc_get_type(req->context, struct mbof_rcmp_context);
 
3044
 
 
3045
    if (!ares) {
 
3046
        return ldb_module_done(ctx->req, NULL, NULL,
 
3047
                               LDB_ERR_OPERATIONS_ERROR);
 
3048
    }
 
3049
    if (ares->error != LDB_SUCCESS) {
 
3050
        return ldb_module_done(ctx->req,
 
3051
                               ares->controls,
 
3052
                               ares->response,
 
3053
                               ares->error);
 
3054
    }
 
3055
 
 
3056
    switch (ares->type) {
 
3057
    case LDB_REPLY_ENTRY:
 
3058
 
 
3059
        usr = talloc_zero(ctx, struct mbof_member);
 
3060
        if (!usr) {
 
3061
            return ldb_module_done(ctx->req, NULL, NULL,
 
3062
                                   LDB_ERR_OPERATIONS_ERROR);
 
3063
        }
 
3064
 
 
3065
        usr->status = MBOF_USER;
 
3066
        usr->dn = talloc_steal(usr, ares->message->dn);
 
3067
        name = ldb_msg_find_attr_as_string(ares->message, DB_NAME, NULL);
 
3068
        if (name) {
 
3069
            usr->name = talloc_steal(usr, name);
 
3070
        }
 
3071
 
 
3072
        if (ldb_msg_find_element(ares->message, DB_MEMBEROF)) {
 
3073
            usr->orig_has_memberof = true;
 
3074
        }
 
3075
 
 
3076
        DLIST_ADD(ctx->user_list, usr);
 
3077
 
 
3078
        key.type = HASH_KEY_STRING;
 
3079
        key.str = discard_const(ldb_dn_get_linearized(usr->dn));
 
3080
        value.type = HASH_VALUE_PTR;
 
3081
        value.ptr = usr;
 
3082
 
 
3083
        ret = hash_enter(ctx->user_table, &key, &value);
 
3084
        if (ret != HASH_SUCCESS) {
 
3085
            return ldb_module_done(ctx->req, NULL, NULL,
 
3086
                                   LDB_ERR_OPERATIONS_ERROR);
 
3087
        }
 
3088
 
 
3089
        break;
 
3090
 
 
3091
    case LDB_REPLY_REFERRAL:
 
3092
        /* ignore */
 
3093
        break;
 
3094
 
 
3095
    case LDB_REPLY_DONE:
 
3096
        talloc_zfree(ares);
 
3097
 
 
3098
        /* and now search groups */
 
3099
        return mbof_rcmp_search_groups(ctx);
 
3100
    }
 
3101
 
 
3102
    talloc_zfree(ares);
 
3103
    return LDB_SUCCESS;
 
3104
}
 
3105
 
 
3106
static int mbof_rcmp_search_groups(struct mbof_rcmp_context *ctx)
 
3107
{
 
3108
    struct ldb_context *ldb = ldb_module_get_ctx(ctx->module);
 
3109
    static const char *attrs[] = { DB_MEMBEROF, DB_MEMBERUID,
 
3110
                                   DB_NAME, DB_MEMBER, NULL };
 
3111
    static const char *filter = "(objectclass=group)";
 
3112
    struct ldb_request *req;
 
3113
    int ret;
 
3114
 
 
3115
    ret = hash_create_ex(1024, &ctx->group_table, 0, 0, 0, 0,
 
3116
                         hash_alloc, hash_free, ctx, NULL, NULL);
 
3117
    if (ret != HASH_SUCCESS) {
 
3118
        return ldb_module_done(ctx->req, NULL, NULL,
 
3119
                               LDB_ERR_OPERATIONS_ERROR);
 
3120
    }
 
3121
 
 
3122
    ret = ldb_build_search_req(&req, ldb, ctx,
 
3123
                               NULL, LDB_SCOPE_SUBTREE,
 
3124
                               filter, attrs, NULL,
 
3125
                               ctx, mbof_rcmp_grp_callback, ctx->req);
 
3126
    if (ret != LDB_SUCCESS) {
 
3127
        return ret;
 
3128
    }
 
3129
 
 
3130
    return ldb_request(ldb, req);
 
3131
}
 
3132
 
 
3133
static int mbof_rcmp_grp_callback(struct ldb_request *req,
 
3134
                                  struct ldb_reply *ares)
 
3135
{
 
3136
    struct ldb_context *ldb;
 
3137
    struct mbof_rcmp_context *ctx;
 
3138
    struct ldb_message_element *el;
 
3139
    struct mbof_member *iter;
 
3140
    struct mbof_member *grp;
 
3141
    hash_value_t value;
 
3142
    hash_key_t key;
 
3143
    const char *name;
 
3144
    int i, j;
 
3145
    int ret;
 
3146
 
 
3147
    ctx = talloc_get_type(req->context, struct mbof_rcmp_context);
 
3148
    ldb = ldb_module_get_ctx(ctx->module);
 
3149
 
 
3150
    if (!ares) {
 
3151
        return ldb_module_done(ctx->req, NULL, NULL,
 
3152
                               LDB_ERR_OPERATIONS_ERROR);
 
3153
    }
 
3154
    if (ares->error != LDB_SUCCESS) {
 
3155
        return ldb_module_done(ctx->req,
 
3156
                               ares->controls,
 
3157
                               ares->response,
 
3158
                               ares->error);
 
3159
    }
 
3160
 
 
3161
    switch (ares->type) {
 
3162
    case LDB_REPLY_ENTRY:
 
3163
 
 
3164
        grp = talloc_zero(ctx, struct mbof_member);
 
3165
        if (!grp) {
 
3166
            return ldb_module_done(ctx->req, NULL, NULL,
 
3167
                                   LDB_ERR_OPERATIONS_ERROR);
 
3168
        }
 
3169
 
 
3170
        grp->status = MBOF_GROUP_TO_DO;
 
3171
        grp->dn = talloc_steal(grp, ares->message->dn);
 
3172
        grp->name = ldb_msg_find_attr_as_string(ares->message, DB_NAME, NULL);
 
3173
        name = ldb_msg_find_attr_as_string(ares->message, DB_NAME, NULL);
 
3174
        if (name) {
 
3175
            grp->name = talloc_steal(grp, name);
 
3176
        }
 
3177
 
 
3178
        if (ldb_msg_find_element(ares->message, DB_MEMBEROF)) {
 
3179
            grp->orig_has_memberof = true;
 
3180
        }
 
3181
 
 
3182
        if (ldb_msg_find_element(ares->message, DB_MEMBERUID)) {
 
3183
            grp->orig_has_memberuid = true;
 
3184
        }
 
3185
 
 
3186
        ret = mbof_steal_msg_el(grp, DB_MEMBER,
 
3187
                                ares->message, &grp->orig_members);
 
3188
        if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_ATTRIBUTE) {
 
3189
            return ldb_module_done(ctx->req, NULL, NULL,
 
3190
                                   LDB_ERR_OPERATIONS_ERROR);
 
3191
        }
 
3192
 
 
3193
        DLIST_ADD(ctx->group_list, grp);
 
3194
 
 
3195
        key.type = HASH_KEY_STRING;
 
3196
        key.str = discard_const(ldb_dn_get_linearized(grp->dn));
 
3197
        value.type = HASH_VALUE_PTR;
 
3198
        value.ptr = grp;
 
3199
 
 
3200
        ret = hash_enter(ctx->group_table, &key, &value);
 
3201
        if (ret != HASH_SUCCESS) {
 
3202
            return ldb_module_done(ctx->req, NULL, NULL,
 
3203
                                   LDB_ERR_OPERATIONS_ERROR);
 
3204
        }
 
3205
 
 
3206
        break;
 
3207
 
 
3208
    case LDB_REPLY_REFERRAL:
 
3209
        /* ignore */
 
3210
        break;
 
3211
 
 
3212
    case LDB_REPLY_DONE:
 
3213
        talloc_zfree(ares);
 
3214
 
 
3215
        if (!ctx->group_list) {
 
3216
            /* no groups ? */
 
3217
            return ldb_module_done(ctx->req, NULL, NULL, LDB_SUCCESS);
 
3218
        }
 
3219
 
 
3220
        /* for each group compute the members list */
 
3221
        for (iter = ctx->group_list; iter; iter = iter->next) {
 
3222
 
 
3223
            el = iter->orig_members;
 
3224
            if (!el || el->num_values == 0) {
 
3225
                /* no members */
 
3226
                continue;
 
3227
            }
 
3228
 
 
3229
            /* we have at most num_values group members */
 
3230
            iter->members = talloc_array(iter, struct mbof_member *,
 
3231
                                         el->num_values +1);
 
3232
            if (!iter->members) {
 
3233
                return ldb_module_done(ctx->req, NULL, NULL,
 
3234
                                       LDB_ERR_OPERATIONS_ERROR);
 
3235
            }
 
3236
 
 
3237
            for (i = 0, j = 0; i < el->num_values; i++) {
 
3238
                key.type = HASH_KEY_STRING;
 
3239
                key.str = (char *)el->values[i].data;
 
3240
 
 
3241
                ret = hash_lookup(ctx->user_table, &key, &value);
 
3242
                switch (ret) {
 
3243
                case HASH_SUCCESS:
 
3244
                    iter->members[j] = (struct mbof_member *)value.ptr;
 
3245
                    j++;
 
3246
                    break;
 
3247
 
 
3248
                case HASH_ERROR_KEY_NOT_FOUND:
 
3249
                    /* not a user, see if it is a group */
 
3250
 
 
3251
                    ret = hash_lookup(ctx->group_table, &key, &value);
 
3252
                    if (ret != HASH_SUCCESS) {
 
3253
                        if (ret != HASH_ERROR_KEY_NOT_FOUND) {
 
3254
                            return ldb_module_done(ctx->req, NULL, NULL,
 
3255
                                                   LDB_ERR_OPERATIONS_ERROR);
 
3256
                        }
 
3257
                    }
 
3258
                    if (ret == HASH_ERROR_KEY_NOT_FOUND) {
 
3259
                        /* not a known user, nor a known group ?
 
3260
                           give a warning an continue */
 
3261
                        ldb_debug(ldb, LDB_DEBUG_ERROR,
 
3262
                                  "member attribute [%s] has no corresponding"
 
3263
                                  " entry!", key.str);
 
3264
                        break;
 
3265
                    }
 
3266
 
 
3267
                    iter->members[j] = (struct mbof_member *)value.ptr;
 
3268
                    j++;
 
3269
                    break;
 
3270
 
 
3271
                default:
 
3272
                    return ldb_module_done(ctx->req, NULL, NULL,
 
3273
                                           LDB_ERR_OPERATIONS_ERROR);
 
3274
                }
 
3275
            }
 
3276
            /* terminate */
 
3277
            iter->members[j] = NULL;
 
3278
 
 
3279
            talloc_zfree(iter->orig_members);
 
3280
        }
 
3281
 
 
3282
        /* now generate correct memberof tables */
 
3283
        while (ctx->group_list->status == MBOF_GROUP_TO_DO) {
 
3284
 
 
3285
            grp = ctx->group_list;
 
3286
 
 
3287
            /* move to end of list and mark as done.
 
3288
             * NOTE: this is not efficient, but will do for now */
 
3289
            DLIST_DEMOTE(ctx->group_list, grp, struct mbof_member *);
 
3290
            grp->status = MBOF_GROUP_DONE;
 
3291
 
 
3292
            /* verify if members need updating */
 
3293
            if (!grp->members) {
 
3294
                continue;
 
3295
            }
 
3296
            for (i = 0; grp->members[i]; i++) {
 
3297
                ret = mbof_member_update(ctx, grp, grp->members[i]);
 
3298
                if (ret != LDB_SUCCESS) {
 
3299
                    return ldb_module_done(ctx->req, NULL, NULL,
 
3300
                                           LDB_ERR_OPERATIONS_ERROR);
 
3301
                }
 
3302
            }
 
3303
        }
 
3304
 
 
3305
        /* ok all done, now go on and modify the tree */
 
3306
        return mbof_rcmp_update(ctx);
 
3307
    }
 
3308
 
 
3309
    talloc_zfree(ares);
 
3310
    return LDB_SUCCESS;
 
3311
}
 
3312
 
 
3313
static int mbof_member_update(struct mbof_rcmp_context *ctx,
 
3314
                              struct mbof_member *parent,
 
3315
                              struct mbof_member *mem)
 
3316
{
 
3317
    hash_value_t value;
 
3318
    hash_key_t key;
 
3319
    int ret;
 
3320
 
 
3321
    /* ignore loops */
 
3322
    if (parent == mem) return LDB_SUCCESS;
 
3323
 
 
3324
    key.type = HASH_KEY_STRING;
 
3325
    key.str = discard_const(ldb_dn_get_linearized(parent->dn));
 
3326
 
 
3327
    if (!mem->memberofs) {
 
3328
        ret = hash_create_ex(32, &mem->memberofs, 0, 0, 0, 0,
 
3329
                             hash_alloc, hash_free, mem, NULL, NULL);
 
3330
        if (ret != HASH_SUCCESS) {
 
3331
            return LDB_ERR_OPERATIONS_ERROR;
 
3332
        }
 
3333
 
 
3334
        ret = HASH_ERROR_KEY_NOT_FOUND;
 
3335
 
 
3336
    } else {
 
3337
 
 
3338
        ret = hash_lookup(mem->memberofs, &key, &value);
 
3339
        if (ret != HASH_SUCCESS) {
 
3340
            if (ret != HASH_ERROR_KEY_NOT_FOUND) {
 
3341
                /* fatal error */
 
3342
                return LDB_ERR_OPERATIONS_ERROR;
 
3343
            }
 
3344
        }
 
3345
    }
 
3346
 
 
3347
    if (ret == HASH_ERROR_KEY_NOT_FOUND) {
 
3348
 
 
3349
        /* it's missing, update member */
 
3350
        value.type = HASH_VALUE_PTR;
 
3351
        value.ptr = parent;
 
3352
 
 
3353
        ret = hash_enter(mem->memberofs, &key, &value);
 
3354
        if (ret != HASH_SUCCESS) {
 
3355
            return LDB_ERR_OPERATIONS_ERROR;
 
3356
        }
 
3357
 
 
3358
        if (mem->status == MBOF_USER) {
 
3359
            /* add corresponding memuid to the group */
 
3360
            ret = mbof_add_memuid(parent, mem->name);
 
3361
            if (ret != LDB_SUCCESS) {
 
3362
                return ret;
 
3363
            }
 
3364
        }
 
3365
 
 
3366
        /* if we updated a group, mark it as TO DO again */
 
3367
        if (mem->status == MBOF_GROUP_DONE) {
 
3368
            mem->status = MBOF_GROUP_TO_DO;
 
3369
        }
 
3370
    }
 
3371
 
 
3372
    /* now see if the parent has memberofs to pass down */
 
3373
    if (parent->memberofs) {
 
3374
        ret = hash_iterate(parent->memberofs, mbof_member_iter, mem);
 
3375
        if (ret != HASH_SUCCESS) {
 
3376
            return LDB_ERR_OPERATIONS_ERROR;
 
3377
        }
 
3378
        if (mem->status == MBOF_ITER_ERROR) {
 
3379
            return LDB_ERR_OPERATIONS_ERROR;
 
3380
        }
 
3381
    }
 
3382
 
 
3383
    /* finally, if it was made TO DO move it to the head */
 
3384
    if (mem->status == MBOF_GROUP_TO_DO) {
 
3385
        DLIST_PROMOTE(ctx->group_list, mem);
 
3386
    }
 
3387
 
 
3388
    return LDB_SUCCESS;
 
3389
}
 
3390
 
 
3391
static bool mbof_member_iter(hash_entry_t *item, void *user_data)
 
3392
{
 
3393
    struct mbof_member *parent;
 
3394
    struct mbof_member *mem;
 
3395
    hash_value_t value;
 
3396
    int ret;
 
3397
 
 
3398
    mem = talloc_get_type(user_data, struct mbof_member);
 
3399
 
 
3400
    /* exclude self */
 
3401
    if (strcmp(item->key.str, ldb_dn_get_linearized(mem->dn)) == 0) {
 
3402
        return true;
 
3403
    }
 
3404
 
 
3405
    /* check if we already have it */
 
3406
    ret = hash_lookup(mem->memberofs, &item->key, &value);
 
3407
    if (ret != HASH_SUCCESS) {
 
3408
        if (ret != HASH_ERROR_KEY_NOT_FOUND) {
 
3409
            /* fatal error */
 
3410
            mem->status = MBOF_ITER_ERROR;
 
3411
            return false;
 
3412
        }
 
3413
 
 
3414
        /* was not already here, add it and mark group as TO DO */
 
3415
        ret = hash_enter(mem->memberofs, &item->key, &item->value);
 
3416
        if (ret != HASH_SUCCESS) {
 
3417
            return LDB_ERR_OPERATIONS_ERROR;
 
3418
        }
 
3419
 
 
3420
        if (mem->status == MBOF_GROUP_DONE) {
 
3421
            mem->status = MBOF_GROUP_TO_DO;
 
3422
        }
 
3423
 
 
3424
        if (mem->status == MBOF_USER) {
 
3425
            /* add corresponding memuid to the group */
 
3426
            parent = (struct mbof_member *)item->value.ptr;
 
3427
            ret = mbof_add_memuid(parent, mem->name);
 
3428
            if (ret != LDB_SUCCESS) {
 
3429
                mem->status = MBOF_ITER_ERROR;
 
3430
                return false;
 
3431
            }
 
3432
        }
 
3433
    }
 
3434
 
 
3435
    return true;
 
3436
}
 
3437
 
 
3438
static int mbof_add_memuid(struct mbof_member *grp, const char *user)
 
3439
{
 
3440
    struct ldb_val *vals;
 
3441
    int n;
 
3442
 
 
3443
    if (!grp->memuids) {
 
3444
        grp->memuids = talloc_zero(grp, struct ldb_message_element);
 
3445
        if (!grp->memuids) {
 
3446
            return LDB_ERR_OPERATIONS_ERROR;
 
3447
        }
 
3448
 
 
3449
        grp->memuids->name = talloc_strdup(grp->memuids, DB_MEMBERUID);
 
3450
        if (!grp->memuids->name) {
 
3451
            return LDB_ERR_OPERATIONS_ERROR;
 
3452
        }
 
3453
    }
 
3454
 
 
3455
    n = grp->memuids->num_values;
 
3456
    vals = talloc_realloc(grp->memuids,
 
3457
                          grp->memuids->values,
 
3458
                          struct ldb_val, n + 1);
 
3459
    if (!vals) {
 
3460
        return LDB_ERR_OPERATIONS_ERROR;
 
3461
    }
 
3462
 
 
3463
    vals[n].data = (uint8_t *)talloc_strdup(vals, user);
 
3464
    vals[n].length = strlen(user);
 
3465
 
 
3466
    grp->memuids->values = vals;
 
3467
    grp->memuids->num_values = n + 1;
 
3468
 
 
3469
    return LDB_SUCCESS;
 
3470
}
 
3471
 
 
3472
static int mbof_rcmp_update(struct mbof_rcmp_context *ctx)
 
3473
{
 
3474
    struct ldb_context *ldb = ldb_module_get_ctx(ctx->module);
 
3475
    struct ldb_message_element *el;
 
3476
    struct ldb_message *msg = NULL;
 
3477
    struct ldb_request *req;
 
3478
    struct mbof_member *x = NULL;
 
3479
    hash_key_t *keys;
 
3480
    unsigned long count;
 
3481
    int flags;
 
3482
    int ret, i;
 
3483
 
 
3484
    /* we process all users first and then all groups */
 
3485
    if (ctx->user_list) {
 
3486
        /* take the next entry and remove it from the list */
 
3487
        x = ctx->user_list;
 
3488
        DLIST_REMOVE(ctx->user_list, x);
 
3489
    }
 
3490
    else if (ctx->group_list) {
 
3491
        /* take the next entry and remove it from the list */
 
3492
        x = ctx->group_list;
 
3493
        DLIST_REMOVE(ctx->group_list, x);
 
3494
    }
 
3495
    else {
 
3496
        /* processing terminated, return */
 
3497
        ret = LDB_SUCCESS;
 
3498
        goto done;
 
3499
    }
 
3500
 
 
3501
    msg = ldb_msg_new(ctx);
 
3502
    if (!msg) {
 
3503
        ret = LDB_ERR_OPERATIONS_ERROR;
 
3504
        goto done;
 
3505
    }
 
3506
 
 
3507
    msg->dn = x->dn;
 
3508
 
 
3509
    /* process memberof */
 
3510
    if (x->memberofs) {
 
3511
        ret = hash_keys(x->memberofs, &count, &keys);
 
3512
        if (ret != HASH_SUCCESS) {
 
3513
            ret = LDB_ERR_OPERATIONS_ERROR;
 
3514
            goto done;
 
3515
        }
 
3516
 
 
3517
        if (x->orig_has_memberof) {
 
3518
            flags = LDB_FLAG_MOD_REPLACE;
 
3519
        } else {
 
3520
            flags = LDB_FLAG_MOD_ADD;
 
3521
        }
 
3522
 
 
3523
        ret = ldb_msg_add_empty(msg, DB_MEMBEROF, flags, &el);
 
3524
        if (ret != LDB_SUCCESS) {
 
3525
            goto done;
 
3526
        }
 
3527
 
 
3528
        el->values = talloc_array(el, struct ldb_val, count);
 
3529
        if (!el->values) {
 
3530
            ret = LDB_ERR_OPERATIONS_ERROR;
 
3531
            goto done;
 
3532
        }
 
3533
        el->num_values = count;
 
3534
 
 
3535
        for (i = 0; i < count; i++) {
 
3536
            el->values[i].data = (uint8_t *)keys[i].str;
 
3537
            el->values[i].length = strlen(keys[i].str);
 
3538
        }
 
3539
    } else if (x->orig_has_memberof) {
 
3540
        ret = ldb_msg_add_empty(msg, DB_MEMBEROF, LDB_FLAG_MOD_DELETE, NULL);
 
3541
        if (ret != LDB_SUCCESS) {
 
3542
            goto done;
 
3543
        }
 
3544
    }
 
3545
 
 
3546
    /* process memberuid */
 
3547
    if (x->memuids) {
 
3548
        if (x->orig_has_memberuid) {
 
3549
            flags = LDB_FLAG_MOD_REPLACE;
 
3550
        } else {
 
3551
            flags = LDB_FLAG_MOD_ADD;
 
3552
        }
 
3553
 
 
3554
        ret = ldb_msg_add(msg, x->memuids, flags);
 
3555
        if (ret != LDB_SUCCESS) {
 
3556
            goto done;
 
3557
        }
 
3558
    }
 
3559
    else if (x->orig_has_memberuid) {
 
3560
        ret = ldb_msg_add_empty(msg, DB_MEMBERUID, LDB_FLAG_MOD_DELETE, NULL);
 
3561
        if (ret != LDB_SUCCESS) {
 
3562
            goto done;
 
3563
        }
 
3564
    }
 
3565
 
 
3566
    ret = ldb_build_mod_req(&req, ldb, ctx, msg, NULL,
 
3567
                            ctx, mbof_rcmp_mod_callback,
 
3568
                            ctx->req);
 
3569
    if (ret != LDB_SUCCESS) {
 
3570
        goto done;
 
3571
    }
 
3572
    talloc_steal(req, msg);
 
3573
 
 
3574
    /* fire next call */
 
3575
    return ldb_next_request(ctx->module, req);
 
3576
 
 
3577
done:
 
3578
    /* all users and groups have been processed */
 
3579
    return ldb_module_done(ctx->req, NULL, NULL, ret);
 
3580
}
 
3581
 
 
3582
static int mbof_rcmp_mod_callback(struct ldb_request *req,
 
3583
                                  struct ldb_reply *ares)
 
3584
{
 
3585
    struct ldb_context *ldb;
 
3586
    struct mbof_rcmp_context *ctx;
 
3587
 
 
3588
    ctx = talloc_get_type(req->context, struct mbof_rcmp_context);
 
3589
    ldb = ldb_module_get_ctx(ctx->module);
 
3590
 
 
3591
    if (!ares) {
 
3592
        return ldb_module_done(ctx->req, NULL, NULL,
 
3593
                               LDB_ERR_OPERATIONS_ERROR);
 
3594
    }
 
3595
    if (ares->error != LDB_SUCCESS) {
 
3596
        return ldb_module_done(ctx->req,
 
3597
                               ares->controls,
 
3598
                               ares->response,
 
3599
                               ares->error);
 
3600
    }
 
3601
 
 
3602
    switch (ares->type) {
 
3603
    case LDB_REPLY_ENTRY:
 
3604
        ldb_debug(ldb, LDB_DEBUG_TRACE, "Got an entry on a non search op ?!");
 
3605
        /* shouldn't happen */
 
3606
        talloc_zfree(ares);
 
3607
        return ldb_module_done(ctx->req, NULL, NULL,
 
3608
                               LDB_ERR_OPERATIONS_ERROR);
 
3609
    case LDB_REPLY_REFERRAL:
 
3610
        /* ignore */
 
3611
        talloc_zfree(ares);
 
3612
        break;
 
3613
 
 
3614
    case LDB_REPLY_DONE:
 
3615
        talloc_zfree(ares);
 
3616
 
 
3617
        /* update the next one */
 
3618
        return mbof_rcmp_update(ctx);
 
3619
    }
 
3620
 
 
3621
    return LDB_SUCCESS;
 
3622
}
 
3623
 
 
3624
 
 
3625
 
 
3626
/* module init code */
 
3627
 
 
3628
static int memberof_init(struct ldb_module *module)
 
3629
{
 
3630
    struct ldb_context *ldb = ldb_module_get_ctx(module);
 
3631
    int ret;
 
3632
 
 
3633
    /* set syntaxes for member and memberof so that comparisons in filters and
 
3634
     * such are done right */
 
3635
    ret = ldb_schema_attribute_add(ldb, DB_MEMBER, 0, LDB_SYNTAX_DN);
 
3636
    if (ret != 0) return LDB_ERR_OPERATIONS_ERROR;
 
3637
 
 
3638
    ret = ldb_schema_attribute_add(ldb, DB_MEMBEROF, 0, LDB_SYNTAX_DN);
 
3639
    if (ret != 0) return LDB_ERR_OPERATIONS_ERROR;
 
3640
 
 
3641
    return ldb_next_init(module);
 
3642
}
 
3643
 
 
3644
const struct ldb_module_ops ldb_memberof_module_ops = {
 
3645
    .name = "memberof",
 
3646
    .init_context = memberof_init,
 
3647
    .add = memberof_add,
 
3648
    .modify = memberof_mod,
 
3649
    .del = memberof_del,
 
3650
};
 
3651
 
 
3652
int ldb_init_module(const char *version)
 
3653
{
 
3654
#ifdef LDB_MODULE_CHECK_VERSION
 
3655
    LDB_MODULE_CHECK_VERSION(version);
 
3656
#endif
 
3657
    return ldb_register_module(&ldb_memberof_module_ops);
 
3658
}