4
Copyright (C) Simo Sorce <idra@samba.org> 2008-2011
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.
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.
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/>.
20
/* Temporary workaround, will be fixed in ldb upstream soon */
22
#define LDB_VERSION "0.9.22"
26
#include "ldb_module.h"
27
#include "util/util.h"
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"
38
#define talloc_zfree(ptr) do { talloc_free(ptr); ptr = NULL; } while(0)
42
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
45
struct mbof_dn_array {
56
struct ldb_module *module;
57
struct ldb_request *req;
59
struct ldb_control **ret_ctrls;
60
struct ldb_extended *ret_resp;
63
struct mbof_add_operation {
64
struct mbof_add_ctx *add_ctx;
65
struct mbof_add_operation *next;
67
struct mbof_dn_array *parents;
68
struct ldb_dn *entry_dn;
70
struct ldb_message *entry;
73
struct mbof_memberuid_op {
75
struct ldb_message_element *el;
81
struct mbof_add_operation *add_list;
82
struct mbof_add_operation *current_op;
84
struct ldb_message *msg;
85
struct ldb_dn *msg_dn;
88
struct mbof_dn *missing;
90
struct mbof_memberuid_op *muops;
95
struct mbof_del_ancestors_ctx {
96
struct mbof_dn_array *new_list;
100
struct ldb_message *entry;
103
struct mbof_del_operation {
104
struct mbof_del_ctx *del_ctx;
105
struct mbof_del_operation *parent;
106
struct mbof_del_operation **children;
110
struct ldb_dn *entry_dn;
112
struct ldb_message *entry;
113
struct ldb_message **parents;
117
struct mbof_del_ancestors_ctx *anc_ctx;
122
struct mbof_del_ctx {
123
struct mbof_ctx *ctx;
125
struct mbof_del_operation *first;
126
struct mbof_dn *history;
128
struct ldb_message **mus;
131
struct mbof_memberuid_op *muops;
135
struct mbof_mod_ctx *follow_mod;
139
struct mbof_mod_ctx {
140
struct mbof_ctx *ctx;
142
const struct ldb_message_element *membel;
143
struct ldb_message *entry;
145
struct mbof_dn_array *to_add;
147
struct ldb_message *msg;
151
static struct mbof_ctx *mbof_init(struct ldb_module *module,
152
struct ldb_request *req)
154
struct mbof_ctx *ctx;
156
ctx = talloc_zero(req, struct mbof_ctx);
161
ctx->module = module;
167
static int entry_is_user_object(struct ldb_message *entry)
169
struct ldb_message_element *el;
173
el = ldb_msg_find_element(entry, DB_OC);
175
return LDB_ERR_OPERATIONS_ERROR;
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) {
186
return LDB_ERR_NO_SUCH_ATTRIBUTE;
189
static int mbof_append_muop(TALLOC_CTX *memctx,
190
struct mbof_memberuid_op **_muops,
193
struct ldb_dn *parent,
196
struct mbof_memberuid_op *muops = *_muops;
197
int num_muops = *_num_muops;
198
struct mbof_memberuid_op *op;
204
for (i = 0; i < num_muops; i++) {
205
if (ldb_dn_compare(parent, muops[i].dn) == 0) {
212
muops = talloc_realloc(memctx, muops,
213
struct mbof_memberuid_op,
216
return LDB_ERR_OPERATIONS_ERROR;
218
op = &muops[num_muops];
221
*_num_muops = num_muops;
228
op->el = talloc_zero(muops, struct ldb_message_element);
230
return LDB_ERR_OPERATIONS_ERROR;
232
op->el->name = talloc_strdup(op->el, DB_MEMBERUID);
234
return LDB_ERR_OPERATIONS_ERROR;
236
op->el->flags = flags;
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*/
246
val = talloc_realloc(op->el, op->el->values,
247
struct ldb_val, op->el->num_values + 1);
249
return LDB_ERR_OPERATIONS_ERROR;
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;
255
val[op->el->num_values].length = strlen(name);
257
op->el->values = val;
258
op->el->num_values++;
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.
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).
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.
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).
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.
289
* At the end we will add a memberuid attribute to our new object that
290
* includes all direct and indeirect user members names.
293
static int mbof_append_addop(struct mbof_add_ctx *add_ctx,
294
struct mbof_dn_array *parents,
295
struct ldb_dn *entry_dn)
297
struct mbof_add_operation *lastop = NULL;
298
struct mbof_add_operation *addop;
300
/* test if this is a duplicate */
301
/* FIXME: this is not efficient */
302
if (add_ctx->add_list) {
305
lastop = lastop->next;
307
lastop = add_ctx->add_list;
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 */
315
} while (lastop->next);
318
addop = talloc_zero(add_ctx, struct mbof_add_operation);
320
return LDB_ERR_OPERATIONS_ERROR;
323
addop->add_ctx = add_ctx;
324
addop->parents = parents;
325
addop->entry_dn = entry_dn;
327
if (add_ctx->add_list) {
328
lastop->next = addop;
330
add_ctx->add_list = addop;
336
static int memberof_recompute_task(struct ldb_module *module,
337
struct ldb_request *req);
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);
353
static int memberof_add(struct ldb_module *module, struct ldb_request *req)
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;
364
if (ldb_dn_is_special(req->op.add.message->dn)) {
366
if (strcmp("@MEMBEROF-REBUILD",
367
ldb_dn_get_linearized(req->op.add.message->dn)) == 0) {
368
return memberof_recompute_task(module, req);
371
/* do not manipulate other control entries */
372
return ldb_next_request(module, req);
375
/* check if memberof is specified */
376
el = ldb_msg_find_element(req->op.add.message, DB_MEMBEROF);
378
ldb_debug(ldb, LDB_DEBUG_ERROR,
379
"Error: the memberof attribute is readonly.");
380
return LDB_ERR_UNWILLING_TO_PERFORM;
383
/* check if memberuid is specified */
384
el = ldb_msg_find_element(req->op.add.message, DB_MEMBERUID);
386
ldb_debug(ldb, LDB_DEBUG_ERROR,
387
"Error: the memberuid attribute is readonly.");
388
return LDB_ERR_UNWILLING_TO_PERFORM;
391
ctx = mbof_init(module, req);
393
return LDB_ERR_OPERATIONS_ERROR;
396
add_ctx = talloc_zero(ctx, struct mbof_add_ctx);
398
return LDB_ERR_OPERATIONS_ERROR;
402
add_ctx->msg = ldb_msg_copy(add_ctx, req->op.add.message);
404
return LDB_ERR_OPERATIONS_ERROR;
406
add_ctx->msg_dn = add_ctx->msg->dn;
408
/* continue with normal ops if there are no members */
409
el = ldb_msg_find_element(add_ctx->msg, DB_MEMBER);
411
add_ctx->terminate = true;
415
parents = talloc_zero(add_ctx, struct mbof_dn_array);
417
return LDB_ERR_OPERATIONS_ERROR;
419
parents->dns = talloc_array(parents, struct ldb_dn *, 1);
421
return LDB_ERR_OPERATIONS_ERROR;
423
parents->dns[0] = add_ctx->msg_dn;
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;
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");
440
ret = mbof_append_addop(add_ctx, parents, valdn);
441
if (ret != LDB_SUCCESS) {
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,
452
if (ret != LDB_SUCCESS) {
456
return ldb_next_request(module, add_req);
459
static int mbof_add_callback(struct ldb_request *req,
460
struct ldb_reply *ares)
462
struct mbof_add_ctx *add_ctx;
463
struct mbof_ctx *ctx;
466
add_ctx = talloc_get_type(req->context, struct mbof_add_ctx);
470
return ldb_module_done(ctx->req, NULL, NULL,
471
LDB_ERR_OPERATIONS_ERROR);
473
if (ares->error != LDB_SUCCESS) {
474
return ldb_module_done(ctx->req,
480
switch (ares->type) {
481
case LDB_REPLY_ENTRY:
482
/* shouldn't happen */
484
return ldb_module_done(ctx->req, NULL, NULL,
485
LDB_ERR_OPERATIONS_ERROR);
486
case LDB_REPLY_REFERRAL:
491
if (add_ctx->terminate) {
492
return ldb_module_done(ctx->req,
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);
504
else if (add_ctx->current_op->next) {
506
ret = mbof_next_add(add_ctx->current_op->next);
509
/* no more operations */
510
if (add_ctx->missing) {
511
ret = mbof_add_cleanup(add_ctx);
513
else if (add_ctx->muops) {
514
ret = mbof_add_muop(add_ctx);
517
return ldb_module_done(ctx->req,
524
if (ret != LDB_SUCCESS) {
526
return ldb_module_done(ctx->req, NULL, NULL, ret);
534
static int mbof_next_add(struct mbof_add_operation *addop)
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;
544
add_ctx = addop->add_ctx;
546
ldb = ldb_module_get_ctx(ctx->module);
548
/* mark the operation as being handled */
549
add_ctx->current_op = addop;
551
ret = ldb_build_search_req(&req, ldb, ctx,
552
addop->entry_dn, LDB_SCOPE_BASE,
554
addop, mbof_next_add_callback,
556
if (ret != LDB_SUCCESS) {
560
return ldb_request(ldb, req);
563
static int mbof_next_add_callback(struct ldb_request *req,
564
struct ldb_reply *ares)
566
struct mbof_add_operation *addop;
567
struct mbof_add_ctx *add_ctx;
568
struct ldb_context *ldb;
569
struct mbof_ctx *ctx;
572
addop = talloc_get_type(req->context, struct mbof_add_operation);
573
add_ctx = addop->add_ctx;
575
ldb = ldb_module_get_ctx(ctx->module);
578
return ldb_module_done(ctx->req, NULL, NULL,
579
LDB_ERR_OPERATIONS_ERROR);
581
if (ares->error != LDB_SUCCESS) {
582
return ldb_module_done(ctx->req,
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);
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);
606
case LDB_REPLY_REFERRAL:
612
if (addop->entry == NULL) {
613
ldb_debug(ldb, LDB_DEBUG_TRACE, "Entry not found (%s)",
614
ldb_dn_get_linearized(addop->entry_dn));
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);
621
/* now try the next operation */
622
if (add_ctx->current_op->next) {
623
ret = mbof_next_add(add_ctx->current_op->next);
626
/* no more operations */
627
if (add_ctx->missing) {
628
ret = mbof_add_cleanup(add_ctx);
630
else if (add_ctx->muops) {
631
ret = mbof_add_muop(add_ctx);
634
return ldb_module_done(ctx->req,
640
if (ret != LDB_SUCCESS) {
641
return ldb_module_done(ctx->req, NULL, NULL, ret);
645
ret = mbof_add_operation(addop);
646
if (ret != LDB_SUCCESS) {
647
return ldb_module_done(ctx->req, NULL, NULL, ret);
657
/* if it is a group, add all members for cascade effect
658
* add memberof attribute to this entry
660
static int mbof_add_operation(struct mbof_add_operation *addop)
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;
677
add_ctx = addop->add_ctx;
679
ldb = ldb_module_get_ctx(ctx->module);
681
parents = talloc_zero(add_ctx, struct mbof_dn_array);
683
return LDB_ERR_OPERATIONS_ERROR;
685
/* can't be more than the immediate parent */
686
parents->dns = talloc_array(parents, struct ldb_dn *,
687
addop->parents->num);
689
return LDB_ERR_OPERATIONS_ERROR;
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) {
698
parents->dns[parents->num] = addop->parents->dns[i];
702
/* remove entries that are already there */
703
el = ldb_msg_find_element(addop->entry, DB_MEMBEROF);
706
tmp_ctx = talloc_new(addop);
707
if (!tmp_ctx) return LDB_ERR_OPERATIONS_ERROR;
709
for (i = 0; i < el->num_values; i++) {
710
elval_dn = ldb_dn_from_ldb_val(tmp_ctx, ldb, &el->values[i]);
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;
717
for (j = 0; j < parents->num; j++) {
718
if (ldb_dn_compare(parents->dns[j], elval_dn) == 0) {
719
/* duplicate found */
723
if (j < parents->num) {
724
/* remove duplicate */
725
for (;j+1 < parents->num; j++) {
726
parents->dns[j] = parents->dns[j+1];
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);
739
return mbof_next_add(addop->next);
741
else if (add_ctx->muops) {
742
return mbof_add_muop(add_ctx);
745
/* that was the last entry, get out */
746
return ldb_module_done(ctx->req,
752
talloc_free(tmp_ctx);
755
/* if it is a group add all members */
756
el = ldb_msg_find_element(addop->entry, DB_MEMBER);
758
for (i = 0; i < el->num_values; i++) {
759
valdn = ldb_dn_from_ldb_val(add_ctx, ldb, &el->values[i]);
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;
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;
771
ret = mbof_append_addop(add_ctx, parents, valdn);
772
if (ret != LDB_SUCCESS) {
778
/* check if we need to store memberuid ops for this entry */
779
ret = entry_is_user_object(addop->entry);
782
/* it's a user object */
783
name = ldb_msg_find_attr_as_string(addop->entry, DB_NAME, NULL);
785
return LDB_ERR_OPERATIONS_ERROR;
788
for (i = 0; i < parents->num; i++) {
789
ret = mbof_append_muop(add_ctx, &add_ctx->muops,
792
parents->dns[i], name);
793
if (ret != LDB_SUCCESS) {
800
case LDB_ERR_NO_SUCH_ATTRIBUTE:
801
/* it is not a user object, continue */
805
/* an error occured, return */
809
/* we are done with the entry now */
810
talloc_free(addop->entry);
813
/* add memberof to entry */
814
msg = ldb_msg_new(addop);
815
if (!msg) return LDB_ERR_OPERATIONS_ERROR;
817
msg->dn = addop->entry_dn;
819
ret = ldb_msg_add_empty(msg, DB_MEMBEROF, LDB_FLAG_MOD_ADD, &el);
820
if (ret != LDB_SUCCESS) {
823
el->values = talloc_array(msg, struct ldb_val, parents->num);
825
return LDB_ERR_OPERATIONS_ERROR;
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;
839
ret = ldb_build_mod_req(&mod_req, ldb, add_ctx,
841
add_ctx, mbof_add_callback,
843
if (ret != LDB_SUCCESS) {
846
talloc_steal(mod_req, msg);
848
return ldb_next_request(ctx->module, mod_req);
851
static int mbof_add_missing(struct mbof_add_ctx *add_ctx, struct ldb_dn *dn)
855
mdn = talloc(add_ctx, struct mbof_dn);
857
return LDB_ERR_OPERATIONS_ERROR;
859
mdn->dn = talloc_steal(mdn, dn);
861
/* add to the list */
862
mdn->next = add_ctx->missing;
863
add_ctx->missing = mdn;
868
/* remove unexisting members and add memberuid attribute */
869
static int mbof_add_cleanup(struct mbof_add_ctx *add_ctx)
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;
881
ldb = ldb_module_get_ctx(ctx->module);
884
for (iter = add_ctx->missing; iter; iter = iter->next) {
888
return LDB_ERR_OPERATIONS_ERROR;
891
msg = ldb_msg_new(add_ctx);
892
if (!msg) return LDB_ERR_OPERATIONS_ERROR;
894
msg->dn = add_ctx->msg_dn;
896
ret = ldb_msg_add_empty(msg, DB_MEMBER, LDB_FLAG_MOD_DELETE, &el);
897
if (ret != LDB_SUCCESS) {
900
el->values = talloc_array(msg, struct ldb_val, num);
902
return LDB_ERR_OPERATIONS_ERROR;
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;
914
ret = ldb_build_mod_req(&mod_req, ldb, add_ctx,
916
add_ctx, mbof_add_cleanup_callback,
918
if (ret != LDB_SUCCESS) {
922
return ldb_next_request(ctx->module, mod_req);
925
static int mbof_add_cleanup_callback(struct ldb_request *req,
926
struct ldb_reply *ares)
928
struct mbof_add_ctx *add_ctx;
929
struct mbof_ctx *ctx;
932
add_ctx = talloc_get_type(req->context, struct mbof_add_ctx);
936
return ldb_module_done(ctx->req, NULL, NULL,
937
LDB_ERR_OPERATIONS_ERROR);
939
if (ares->error != LDB_SUCCESS) {
940
return ldb_module_done(ctx->req,
946
switch (ares->type) {
947
case LDB_REPLY_ENTRY:
948
/* shouldn't happen */
950
return ldb_module_done(ctx->req, NULL, NULL,
951
LDB_ERR_OPERATIONS_ERROR);
952
case LDB_REPLY_REFERRAL:
957
if (add_ctx->muops) {
958
ret = mbof_add_muop(add_ctx);
961
return ldb_module_done(ctx->req,
967
if (ret != LDB_SUCCESS) {
969
return ldb_module_done(ctx->req, NULL, NULL, ret);
977
/* add memberuid attributes to parent groups */
978
static int mbof_add_muop(struct mbof_add_ctx *add_ctx)
980
struct ldb_context *ldb;
981
struct ldb_message *msg;
982
struct ldb_request *mod_req;
983
struct mbof_ctx *ctx;
987
ldb = ldb_module_get_ctx(ctx->module);
989
msg = ldb_msg_new(add_ctx);
990
if (!msg) return LDB_ERR_OPERATIONS_ERROR;
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;
996
ret = ldb_build_mod_req(&mod_req, ldb, add_ctx,
998
add_ctx, mbof_add_muop_callback,
1000
if (ret != LDB_SUCCESS) {
1004
return ldb_next_request(ctx->module, mod_req);
1007
static int mbof_add_muop_callback(struct ldb_request *req,
1008
struct ldb_reply *ares)
1010
struct mbof_add_ctx *add_ctx;
1011
struct mbof_ctx *ctx;
1014
add_ctx = talloc_get_type(req->context, struct mbof_add_ctx);
1018
return ldb_module_done(ctx->req, NULL, NULL,
1019
LDB_ERR_OPERATIONS_ERROR);
1021
if (ares->error != LDB_SUCCESS) {
1022
return ldb_module_done(ctx->req,
1028
switch (ares->type) {
1029
case LDB_REPLY_ENTRY:
1030
/* shouldn't happen */
1032
return ldb_module_done(ctx->req, NULL, NULL,
1033
LDB_ERR_OPERATIONS_ERROR);
1034
case LDB_REPLY_REFERRAL:
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);
1044
return ldb_module_done(ctx->req,
1050
if (ret != LDB_SUCCESS) {
1052
return ldb_module_done(ctx->req, NULL, NULL, ret);
1063
/* delete operations */
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.
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.
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.
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.
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.
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
1095
* An operation is identified by an object that contains a tree of
1097
* The remove list for the children, the immediate parent, and the dn and
1098
* entry of the object this operation is about.
1100
* We now proceed with adding a new operation for each original member of the
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").
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
1115
* At this point we have the final new memberof list for this operation and we
1116
* can proceed to modify the entry.
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
1123
* Then for each member we create a new operation.
1125
* We continue to process operations until no new operations need to be
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.
1131
* As a final operation remove any memberuid corresponding to a removal of
1132
* a memberof field from a user entry
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);
1167
static int memberof_del(struct ldb_module *module, struct ldb_request *req)
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;
1177
struct mbof_del_ctx *del_ctx;
1178
struct mbof_ctx *ctx;
1182
if (ldb_dn_is_special(req->op.del.dn)) {
1183
/* do not manipulate our control entries */
1184
return ldb_next_request(module, req);
1187
ctx = mbof_init(module, req);
1189
return LDB_ERR_OPERATIONS_ERROR;
1192
del_ctx = talloc_zero(ctx, struct mbof_del_ctx);
1195
return LDB_ERR_OPERATIONS_ERROR;
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);
1205
return LDB_ERR_OPERATIONS_ERROR;
1207
del_ctx->first = first;
1209
first->del_ctx = del_ctx;
1210
first->entry_dn = req->op.del.dn;
1212
dn = ldb_dn_get_linearized(req->op.del.dn);
1215
return LDB_ERR_OPERATIONS_ERROR;
1218
sret = sss_filter_sanitize(del_ctx, dn, &clean_dn);
1221
return LDB_ERR_OPERATIONS_ERROR;
1224
expression = talloc_asprintf(del_ctx,
1225
"(|(distinguishedName=%s)(%s=%s))",
1226
clean_dn, DB_MEMBER, clean_dn);
1229
return LDB_ERR_OPERATIONS_ERROR;
1231
talloc_zfree(clean_dn);
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,
1238
if (ret != LDB_SUCCESS) {
1243
return ldb_request(ldb, search);
1246
static int mbof_del_search_callback(struct ldb_request *req,
1247
struct ldb_reply *ares)
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;
1256
first = talloc_get_type(req->context, struct mbof_del_operation);
1257
del_ctx = first->del_ctx;
1259
ldb = ldb_module_get_ctx(ctx->module);
1262
return ldb_module_done(ctx->req, NULL, NULL,
1263
LDB_ERR_OPERATIONS_ERROR);
1265
if (ares->error != LDB_SUCCESS) {
1266
return ldb_module_done(ctx->req,
1272
switch (ares->type) {
1273
case LDB_REPLY_ENTRY:
1274
msg = ares->message;
1276
if (ldb_dn_compare(msg->dn, ctx->req->op.del.dn) == 0) {
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);
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);
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);
1297
msg = talloc_steal(first->parents, ares->message);
1299
return ldb_module_done(ctx->req, NULL, NULL,
1300
LDB_ERR_OPERATIONS_ERROR);
1302
first->parents[first->num_parents] = msg;
1303
first->num_parents++;
1306
case LDB_REPLY_REFERRAL:
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);
1320
/* now perform the requested delete, before proceeding further */
1321
ret = mbof_orig_del(del_ctx);
1322
if (ret != LDB_SUCCESS) {
1324
return ldb_module_done(ctx->req, NULL, NULL, ret);
1332
static int mbof_orig_del(struct mbof_del_ctx *del_ctx)
1334
struct ldb_request *del_req;
1335
struct mbof_ctx *ctx;
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,
1344
if (ret != LDB_SUCCESS) {
1348
return ldb_next_request(ctx->module, del_req);
1351
static int mbof_orig_del_callback(struct ldb_request *req,
1352
struct ldb_reply *ares)
1354
struct ldb_context *ldb;
1355
struct mbof_del_ctx *del_ctx;
1356
struct mbof_ctx *ctx;
1359
del_ctx = talloc_get_type(req->context, struct mbof_del_ctx);
1361
ldb = ldb_module_get_ctx(ctx->module);
1364
return ldb_module_done(ctx->req, NULL, NULL,
1365
LDB_ERR_OPERATIONS_ERROR);
1367
if (ares->error != LDB_SUCCESS) {
1368
return ldb_module_done(ctx->req,
1374
if (ares->type != LDB_REPLY_DONE) {
1376
ldb_set_errstring(ldb, "Invalid reply type!");
1377
return ldb_module_done(ctx->req, NULL, NULL,
1378
LDB_ERR_OPERATIONS_ERROR);
1381
/* save real call stuff */
1382
ctx->ret_ctrls = talloc_steal(ctx, ares->controls);
1383
ctx->ret_resp = talloc_steal(ctx, ares->response);
1385
/* prep following clean ops */
1386
if (del_ctx->first->num_parents) {
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) {
1392
return ldb_module_done(ctx->req, NULL, NULL, ret);
1395
/* if there are any parents, fire a removal sequence */
1396
ret = mbof_del_cleanup_parents(del_ctx);
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);
1402
/* see if there are memberuid operations to perform */
1403
else if (del_ctx->muops) {
1404
return mbof_del_muop(del_ctx);
1407
/* no parents nor children, end ops */
1408
return ldb_module_done(ctx->req,
1413
if (ret != LDB_SUCCESS) {
1415
return ldb_module_done(ctx->req, NULL, NULL, ret);
1422
static int mbof_del_cleanup_parents(struct mbof_del_ctx *del_ctx)
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;
1433
first = del_ctx->first;
1435
ldb = ldb_module_get_ctx(ctx->module);
1437
msg = ldb_msg_new(first->parents);
1438
if (!msg) return LDB_ERR_OPERATIONS_ERROR;
1440
msg->dn = first->parents[first->cur_parent]->dn;
1441
first->cur_parent++;
1443
ret = ldb_msg_add_empty(msg, DB_MEMBER, LDB_FLAG_MOD_DELETE, &el);
1444
if (ret != LDB_SUCCESS) {
1447
el->values = talloc_array(msg, struct ldb_val, 1);
1449
return LDB_ERR_OPERATIONS_ERROR;
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;
1459
ret = ldb_build_mod_req(&mod_req, ldb, first->parents,
1461
del_ctx, mbof_del_clean_par_callback,
1463
if (ret != LDB_SUCCESS) {
1467
return ldb_next_request(ctx->module, mod_req);
1470
static int mbof_del_clean_par_callback(struct ldb_request *req,
1471
struct ldb_reply *ares)
1473
struct mbof_del_operation *first;
1474
struct ldb_context *ldb;
1475
struct mbof_del_ctx *del_ctx;
1476
struct mbof_ctx *ctx;
1479
del_ctx = talloc_get_type(req->context, struct mbof_del_ctx);
1480
first = del_ctx->first;
1482
ldb = ldb_module_get_ctx(ctx->module);
1485
return ldb_module_done(ctx->req, NULL, NULL,
1486
LDB_ERR_OPERATIONS_ERROR);
1489
if (ares->error != LDB_SUCCESS) {
1490
return ldb_module_done(ctx->req,
1496
if (ares->type != LDB_REPLY_DONE) {
1498
ldb_set_errstring(ldb, "Invalid reply type!");
1499
return ldb_module_done(ctx->req, NULL, NULL,
1500
LDB_ERR_OPERATIONS_ERROR);
1503
if (first->num_parents > first->cur_parent) {
1504
/* still parents to cleanup, go on */
1505
ret = mbof_del_cleanup_parents(del_ctx);
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);
1513
/* see if there are memberuid operations to perform */
1514
else if (del_ctx->muops) {
1515
return mbof_del_muop(del_ctx);
1518
/* no children, end ops */
1519
return ldb_module_done(ctx->req,
1526
if (ret != LDB_SUCCESS) {
1528
return ldb_module_done(ctx->req, NULL, NULL, ret);
1535
static int mbof_del_cleanup_children(struct mbof_del_ctx *del_ctx)
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;
1544
first = del_ctx->first;
1546
ldb = ldb_module_get_ctx(ctx->module);
1548
el = ldb_msg_find_element(first->entry, DB_MEMBER);
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;
1559
ret = mbof_append_delop(first, valdn);
1560
if (ret != LDB_SUCCESS) {
1565
/* now that sets are built, start processing */
1566
return mbof_del_execute_op(first->children[0]);
1569
static int mbof_append_delop(struct mbof_del_operation *parent,
1570
struct ldb_dn *entry_dn)
1572
struct mbof_del_operation *delop;
1574
delop = talloc_zero(parent, struct mbof_del_operation);
1576
return LDB_ERR_OPERATIONS_ERROR;
1579
delop->del_ctx = parent->del_ctx;
1580
delop->parent = parent;
1581
delop->entry_dn = entry_dn;
1583
parent->children = talloc_realloc(parent, parent->children,
1584
struct mbof_del_operation *,
1585
parent->num_children +1);
1586
if (!parent->children) {
1588
return LDB_ERR_OPERATIONS_ERROR;
1591
parent->children[parent->num_children] = delop;
1592
parent->num_children++;
1597
static int mbof_del_execute_op(struct mbof_del_operation *delop)
1599
struct mbof_del_ctx *del_ctx;
1600
struct mbof_ctx *ctx;
1601
struct ldb_context *ldb;
1602
struct ldb_request *search;
1606
static const char *attrs[] = { DB_OC, DB_NAME,
1607
DB_MEMBER, DB_MEMBEROF, NULL };
1610
del_ctx = delop->del_ctx;
1612
ldb = ldb_module_get_ctx(ctx->module);
1615
dn = ldb_dn_get_linearized(delop->entry_dn);
1617
return LDB_ERR_OPERATIONS_ERROR;
1620
ret = sss_filter_sanitize(del_ctx, dn, &clean_dn);
1622
return LDB_ERR_OPERATIONS_ERROR;
1625
expression = talloc_asprintf(del_ctx,
1626
"(|(distinguishedName=%s)(%s=%s))",
1627
clean_dn, DB_MEMBER, clean_dn);
1629
return LDB_ERR_OPERATIONS_ERROR;
1631
talloc_zfree(clean_dn);
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,
1638
if (ret != LDB_SUCCESS) {
1643
return ldb_request(ldb, search);
1646
static int mbof_del_exop_search_callback(struct ldb_request *req,
1647
struct ldb_reply *ares)
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;
1656
delop = talloc_get_type(req->context, struct mbof_del_operation);
1657
del_ctx = delop->del_ctx;
1659
ldb = ldb_module_get_ctx(ctx->module);
1662
return ldb_module_done(ctx->req, NULL, NULL,
1663
LDB_ERR_OPERATIONS_ERROR);
1665
if (ares->error != LDB_SUCCESS) {
1666
return ldb_module_done(ctx->req,
1672
switch (ares->type) {
1673
case LDB_REPLY_ENTRY:
1674
msg = ares->message;
1676
if (ldb_dn_compare(msg->dn, delop->entry_dn) == 0) {
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);
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);
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);
1700
msg = talloc_steal(delop->parents, msg);
1702
return ldb_module_done(ctx->req, NULL, NULL,
1703
LDB_ERR_OPERATIONS_ERROR);
1705
delop->parents[delop->num_parents] = msg;
1706
delop->num_parents++;
1709
case LDB_REPLY_REFERRAL:
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);
1720
/* ok process the entry */
1721
ret = mbof_del_execute_cont(delop);
1723
if (ret != LDB_SUCCESS) {
1724
return ldb_module_done(ctx->req, NULL, NULL,
1725
LDB_ERR_OPERATIONS_ERROR);
1733
static int mbof_del_execute_cont(struct mbof_del_operation *delop)
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;
1742
del_ctx = delop->del_ctx;
1743
parent = delop->parent;
1746
anc_ctx = talloc_zero(delop, struct mbof_del_ancestors_ctx);
1748
return LDB_ERR_OPERATIONS_ERROR;
1750
delop->anc_ctx = anc_ctx;
1752
new_list = talloc_zero(anc_ctx, struct mbof_dn_array);
1754
return LDB_ERR_OPERATIONS_ERROR;
1757
/* at the very least we have a number of memberof elements
1758
* equal to the number of objects that have this entry as
1760
new_list->num = delop->num_parents;
1762
/* attach the list to the operation */
1763
delop->anc_ctx->new_list = new_list;
1764
delop->anc_ctx->num_direct = new_list->num;
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 */
1771
return mbof_del_mod_entry(delop);
1774
/* fill in the list if we have parents */
1775
new_list->dns = talloc_zero_array(new_list,
1778
if (!new_list->dns) {
1779
return LDB_ERR_OPERATIONS_ERROR;
1781
for (i = 0; i < delop->num_parents; i++) {
1782
new_list->dns[i] = delop->parents[i]->dn;
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);
1790
static int mbof_del_ancestors(struct mbof_del_operation *delop)
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;
1801
del_ctx = delop->del_ctx;
1803
ldb = ldb_module_get_ctx(ctx->module);
1804
anc_ctx = delop->anc_ctx;
1805
new_list = anc_ctx->new_list;
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,
1812
if (ret != LDB_SUCCESS) {
1816
return ldb_request(ldb, search);
1819
static int mbof_del_anc_callback(struct ldb_request *req,
1820
struct ldb_reply *ares)
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;
1833
delop = talloc_get_type(req->context, struct mbof_del_operation);
1834
del_ctx = delop->del_ctx;
1836
ldb = ldb_module_get_ctx(ctx->module);
1837
anc_ctx = delop->anc_ctx;
1838
new_list = anc_ctx->new_list;
1841
return ldb_module_done(ctx->req, NULL, NULL,
1842
LDB_ERR_OPERATIONS_ERROR);
1844
if (ares->error != LDB_SUCCESS) {
1845
return ldb_module_done(ctx->req,
1851
switch (ares->type) {
1852
case LDB_REPLY_ENTRY:
1853
msg = ares->message;
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);
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);
1870
case LDB_REPLY_REFERRAL:
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);
1882
el = ldb_msg_find_element(anc_ctx->entry, DB_MEMBEROF);
1884
for (i = 0; i < el->num_values; i++) {
1885
valdn = ldb_dn_from_ldb_val(new_list, ldb, &el->values[i]);
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);
1893
for (j = 0; j < new_list->num; j++) {
1894
if (ldb_dn_compare(valdn, new_list->dns[j]) == 0)
1897
if (j < new_list->num) {
1901
/* do not re-add the original deleted entry by mistake */
1902
if (ldb_dn_compare(valdn, del_ctx->first->entry_dn) == 0) {
1906
new_list->dns = talloc_realloc(new_list,
1910
if (!new_list->dns) {
1911
return ldb_module_done(ctx->req, NULL, NULL,
1912
LDB_ERR_OPERATIONS_ERROR);
1914
new_list->dns[new_list->num] = valdn;
1919
/* done with this one */
1920
talloc_free(anc_ctx->entry);
1921
anc_ctx->entry = NULL;
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);
1929
/* ok, end of the story, proceed to modify the entry */
1930
ret = mbof_del_mod_entry(delop);
1933
if (ret != LDB_SUCCESS) {
1934
return ldb_module_done(ctx->req, NULL, NULL,
1935
LDB_ERR_OPERATIONS_ERROR);
1943
static int mbof_del_mod_entry(struct mbof_del_operation *delop)
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;
1959
del_ctx = delop->del_ctx;
1961
ldb = ldb_module_get_ctx(ctx->module);
1962
new_list = delop->anc_ctx->new_list;
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);
1970
/* it's a user object */
1973
case LDB_ERR_NO_SUCH_ATTRIBUTE:
1974
/* it is not a user object, continue */
1978
/* an error occured, return */
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;
1990
diff = talloc_array(del_ctx->muops, struct ldb_dn *,
1991
el->num_values + 1);
1993
return LDB_ERR_OPERATIONS_ERROR;
1995
for (i = 0, j = 0; i < el->num_values; i++) {
1996
diff[j] = ldb_dn_from_ldb_val(diff, ldb, &el->values[i]);
1998
return LDB_ERR_OPERATIONS_ERROR;
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) {
2008
/* zero terminate array */
2012
/* change memberof on entry */
2013
msg = ldb_msg_new(delop);
2014
if (!msg) return LDB_ERR_OPERATIONS_ERROR;
2016
msg->dn = delop->entry_dn;
2018
if (new_list->num) {
2019
ret = ldb_msg_add_empty(msg, DB_MEMBEROF, LDB_FLAG_MOD_REPLACE, &el);
2020
if (ret != LDB_SUCCESS) {
2024
el->values = talloc_array(el, struct ldb_val, new_list->num);
2026
return LDB_ERR_OPERATIONS_ERROR;
2028
for (i = 0, j = 0; i < new_list->num; i++) {
2029
if (ldb_dn_compare(new_list->dns[i], msg->dn) == 0)
2031
val = ldb_dn_get_linearized(new_list->dns[i]);
2033
return LDB_ERR_OPERATIONS_ERROR;
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;
2043
/* compare the entry's original memberof list with the new
2044
* one and for each missing entry add a memberuid removal
2046
for (k = 0; diff[k]; k++) {
2047
if (ldb_dn_compare(new_list->dns[i], diff[k]) == 0) {
2052
talloc_zfree(diff[k]);
2053
for (; diff[k + 1]; k++) {
2054
diff[k] = diff[k + 1];
2064
ret = ldb_msg_add_empty(msg, DB_MEMBEROF, LDB_FLAG_MOD_DELETE, &el);
2065
if (ret != LDB_SUCCESS) {
2070
if (is_user && diff[0]) {
2071
/* file memberuid removal operations */
2072
name = ldb_msg_find_attr_as_string(delop->entry, DB_NAME, NULL);
2074
return LDB_ERR_OPERATIONS_ERROR;
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,
2082
if (ret != LDB_SUCCESS) {
2088
ret = ldb_build_mod_req(&mod_req, ldb, delop,
2090
delop, mbof_del_mod_callback,
2092
if (ret != LDB_SUCCESS) {
2095
talloc_steal(mod_req, msg);
2097
return ldb_next_request(ctx->module, mod_req);
2100
static int mbof_del_mod_callback(struct ldb_request *req,
2101
struct ldb_reply *ares)
2103
struct mbof_del_operation *delop;
2104
struct mbof_del_ctx *del_ctx;
2105
struct ldb_context *ldb;
2106
struct mbof_ctx *ctx;
2109
delop = talloc_get_type(req->context, struct mbof_del_operation);
2110
del_ctx = delop->del_ctx;
2112
ldb = ldb_module_get_ctx(ctx->module);
2115
return ldb_module_done(ctx->req, NULL, NULL,
2116
LDB_ERR_OPERATIONS_ERROR);
2118
if (ares->error != LDB_SUCCESS) {
2119
return ldb_module_done(ctx->req,
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 */
2130
return ldb_module_done(ctx->req, NULL, NULL,
2131
LDB_ERR_OPERATIONS_ERROR);
2132
case LDB_REPLY_REFERRAL:
2137
case LDB_REPLY_DONE:
2139
ret = mbof_del_progeny(delop);
2141
if (ret != LDB_SUCCESS) {
2143
return ldb_module_done(ctx->req, NULL, NULL, ret);
2150
static int mbof_mod_add(struct mbof_mod_ctx *mod_ctx,
2151
struct mbof_dn_array *ael);
2153
static int mbof_del_progeny(struct mbof_del_operation *delop)
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;
2163
del_ctx = delop->del_ctx;
2165
ldb = ldb_module_get_ctx(ctx->module);
2167
/* now verify if this entry is a group and members need to be processed as
2170
el = ldb_msg_find_element(delop->entry, DB_MEMBER);
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;
2180
ret = mbof_append_delop(delop, valdn);
2181
if (ret != LDB_SUCCESS) {
2187
/* finally find the next entry to handle */
2188
ret = mbof_del_get_next(delop, &nextop);
2189
if (ret != LDB_SUCCESS) {
2193
free_delop_contents(delop);
2196
return mbof_del_execute_op(nextop);
2199
/* see if there are memberuid operations to perform */
2200
if (del_ctx->muops) {
2201
return mbof_del_muop(del_ctx);
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);
2209
/* ok, no more ops, this means our job is done */
2210
return ldb_module_done(ctx->req,
2216
static int mbof_del_get_next(struct mbof_del_operation *delop,
2217
struct mbof_del_operation **nextop)
2219
struct mbof_del_operation *top, *cop;
2220
struct mbof_del_ctx *del_ctx;
2221
struct mbof_dn *save, *tmp;
2223
del_ctx = delop->del_ctx;
2225
/* first of all, save the current delop in the history */
2226
save = talloc_zero(del_ctx, struct mbof_dn);
2228
return LDB_ERR_OPERATIONS_ERROR;
2230
save->dn = delop->entry_dn;
2232
if (del_ctx->history) {
2233
tmp = del_ctx->history;
2234
while (tmp->next) tmp = tmp->next;
2237
del_ctx->history = save;
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 */
2247
while (top->next_child < top->num_children) {
2248
cop = top->children[top->next_child];
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) {
2258
/* and return the current one */
2265
/* we have no more ops */
2270
static int mbof_del_fill_muop(struct mbof_del_ctx *del_ctx,
2271
struct ldb_message *entry)
2273
struct ldb_message_element *el;
2278
el = ldb_msg_find_element(entry, DB_MEMBEROF);
2279
if (!el || el->num_values == 0) {
2280
/* no memberof attributes ... */
2284
ret = entry_is_user_object(entry);
2287
/* it's a user object, continue */
2290
case LDB_ERR_NO_SUCH_ATTRIBUTE:
2291
/* it is not a user object, just return */
2295
/* an error occured, return */
2299
name = talloc_strdup(del_ctx,
2300
ldb_msg_find_attr_as_string(entry, DB_NAME, NULL));
2302
return LDB_ERR_OPERATIONS_ERROR;
2305
for (i = 0; i < el->num_values; i++) {
2306
struct ldb_dn *valdn;
2308
valdn = ldb_dn_from_ldb_val(del_ctx->muops,
2309
ldb_module_get_ctx(del_ctx->ctx->module),
2311
if (!valdn || !ldb_dn_validate(valdn)) {
2312
ldb_debug(ldb_module_get_ctx(del_ctx->ctx->module),
2314
"Invalid dn value: [%s]",
2315
(const char *)el->values[i].data);
2318
ret = mbof_append_muop(del_ctx, &del_ctx->muops,
2319
&del_ctx->num_muops,
2320
LDB_FLAG_MOD_DELETE,
2322
if (ret != LDB_SUCCESS) {
2330
/* del memberuid attributes to parent groups */
2331
static int mbof_del_muop(struct mbof_del_ctx *del_ctx)
2333
struct ldb_context *ldb;
2334
struct ldb_message *msg;
2335
struct ldb_request *mod_req;
2336
struct mbof_ctx *ctx;
2340
ldb = ldb_module_get_ctx(ctx->module);
2342
msg = ldb_msg_new(del_ctx);
2343
if (!msg) return LDB_ERR_OPERATIONS_ERROR;
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;
2349
ret = ldb_build_mod_req(&mod_req, ldb, del_ctx,
2351
del_ctx, mbof_del_muop_callback,
2353
if (ret != LDB_SUCCESS) {
2357
return ldb_next_request(ctx->module, mod_req);
2360
static int mbof_del_muop_callback(struct ldb_request *req,
2361
struct ldb_reply *ares)
2363
struct mbof_del_ctx *del_ctx;
2364
struct mbof_ctx *ctx;
2367
del_ctx = talloc_get_type(req->context, struct mbof_del_ctx);
2371
return ldb_module_done(ctx->req, NULL, NULL,
2372
LDB_ERR_OPERATIONS_ERROR);
2374
if (ares->error != LDB_SUCCESS) {
2375
return ldb_module_done(ctx->req,
2381
switch (ares->type) {
2382
case LDB_REPLY_ENTRY:
2383
/* shouldn't happen */
2385
return ldb_module_done(ctx->req, NULL, NULL,
2386
LDB_ERR_OPERATIONS_ERROR);
2387
case LDB_REPLY_REFERRAL:
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);
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);
2402
return ldb_module_done(ctx->req,
2408
if (ret != LDB_SUCCESS) {
2410
return ldb_module_done(ctx->req, NULL, NULL, ret);
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)
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;
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 */
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);
2454
static int memberof_mod(struct ldb_module *module, struct ldb_request *req)
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;
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);
2469
/* check if memberof is specified */
2470
el = ldb_msg_find_element(req->op.mod.message, DB_MEMBEROF);
2472
ldb_debug(ldb, LDB_DEBUG_ERROR,
2473
"Error: the memberof attribute is readonly.");
2474
return LDB_ERR_UNWILLING_TO_PERFORM;
2477
/* check if memberuid is specified */
2478
el = ldb_msg_find_element(req->op.mod.message, DB_MEMBERUID);
2480
ldb_debug(ldb, LDB_DEBUG_ERROR,
2481
"Error: the memberuid attribute is readonly.");
2482
return LDB_ERR_UNWILLING_TO_PERFORM;
2485
ctx = mbof_init(module, req);
2487
return LDB_ERR_OPERATIONS_ERROR;
2490
mod_ctx = talloc_zero(ctx, struct mbof_mod_ctx);
2493
return LDB_ERR_OPERATIONS_ERROR;
2497
mod_ctx->msg = ldb_msg_copy(mod_ctx, req->op.mod.message);
2498
if (!mod_ctx->msg) {
2499
return LDB_ERR_OPERATIONS_ERROR;
2502
/* continue with normal ops if there are no members */
2503
el = ldb_msg_find_element(mod_ctx->msg, DB_MEMBER);
2505
mod_ctx->terminate = true;
2506
return mbof_orig_mod(mod_ctx);
2509
mod_ctx->membel = el;
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,
2516
mod_ctx, mbof_mod_callback,
2518
if (ret != LDB_SUCCESS) {
2523
return ldb_request(ldb, search);
2527
static int mbof_mod_callback(struct ldb_request *req,
2528
struct ldb_reply *ares)
2530
struct mbof_mod_ctx *mod_ctx;
2531
struct ldb_context *ldb;
2532
struct mbof_ctx *ctx;
2535
mod_ctx = talloc_get_type(req->context, struct mbof_mod_ctx);
2537
ldb = ldb_module_get_ctx(ctx->module);
2540
return ldb_module_done(ctx->req, NULL, NULL,
2541
LDB_ERR_OPERATIONS_ERROR);
2543
if (ares->error != LDB_SUCCESS) {
2544
return ldb_module_done(ctx->req,
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);
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);
2567
case LDB_REPLY_REFERRAL:
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);
2580
ret = mbof_orig_mod(mod_ctx);
2581
if (ret != LDB_SUCCESS) {
2583
return ldb_module_done(ctx->req, NULL, NULL, ret);
2591
static int mbof_orig_mod(struct mbof_mod_ctx *mod_ctx)
2593
struct ldb_request *mod_req;
2594
struct ldb_context *ldb;
2595
struct mbof_ctx *ctx;
2599
ldb = ldb_module_get_ctx(ctx->module);
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,
2605
if (ret != LDB_SUCCESS) {
2609
return ldb_next_request(ctx->module, mod_req);
2612
static int mbof_orig_mod_callback(struct ldb_request *req,
2613
struct ldb_reply *ares)
2615
struct ldb_context *ldb;
2616
struct mbof_mod_ctx *mod_ctx;
2617
struct mbof_ctx *ctx;
2620
mod_ctx = talloc_get_type(req->context, struct mbof_mod_ctx);
2622
ldb = ldb_module_get_ctx(ctx->module);
2625
return ldb_module_done(ctx->req, NULL, NULL,
2626
LDB_ERR_OPERATIONS_ERROR);
2628
if (ares->error != LDB_SUCCESS) {
2629
return ldb_module_done(ctx->req,
2635
if (ares->type != LDB_REPLY_DONE) {
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);
2643
/* save real call stuff */
2644
ctx->ret_ctrls = talloc_steal(ctx, ares->controls);
2645
ctx->ret_resp = talloc_steal(ctx, ares->response);
2647
if (!mod_ctx->terminate) {
2649
ret = mbof_mod_process(mod_ctx, &mod_ctx->terminate);
2650
if (ret != LDB_SUCCESS) {
2652
return ldb_module_done(ctx->req, NULL, NULL, ret);
2656
if (mod_ctx->terminate) {
2658
return ldb_module_done(ctx->req,
2668
static int mbof_mod_process(struct mbof_mod_ctx *mod_ctx, bool *done)
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;
2678
ldb = ldb_module_get_ctx(ctx->module);
2680
switch (mod_ctx->membel->flags) {
2681
case LDB_FLAG_MOD_ADD:
2683
ret = mbof_fill_dn_array(mod_ctx, ldb, mod_ctx->membel, &added);
2684
if (ret != LDB_SUCCESS) {
2688
return mbof_mod_add(mod_ctx, added);
2690
case LDB_FLAG_MOD_DELETE:
2692
if (mod_ctx->membel->num_values == 0) {
2693
el = ldb_msg_find_element(mod_ctx->entry, DB_MEMBER);
2695
el = mod_ctx->membel;
2699
/* nothing to do really */
2704
ret = mbof_fill_dn_array(mod_ctx, ldb, el, &removed);
2705
if (ret != LDB_SUCCESS) {
2709
return mbof_mod_delete(mod_ctx, removed);
2711
case LDB_FLAG_MOD_REPLACE:
2714
el = ldb_msg_find_element(mod_ctx->entry, DB_MEMBER);
2716
ret = mbof_fill_dn_array(mod_ctx, ldb, el, &removed);
2717
if (ret != LDB_SUCCESS) {
2723
el = mod_ctx->membel;
2725
ret = mbof_fill_dn_array(mod_ctx, ldb, el, &added);
2726
if (ret != LDB_SUCCESS) {
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) {
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];
2745
for (j = i; j+1 < added->num; j++) {
2746
added->dns[j] = added->dns[j+1];
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;
2760
/* if we have something to remove do it first */
2761
if (removed && removed->num) {
2762
return mbof_mod_delete(mod_ctx, removed);
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);
2771
/* the replacement function resulted in a null op,
2772
* nothing to do, return happily */
2777
return LDB_ERR_OPERATIONS_ERROR;
2780
static int mbof_mod_add(struct mbof_mod_ctx *mod_ctx,
2781
struct mbof_dn_array *ael)
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;
2791
ldb = ldb_module_get_ctx(ctx->module);
2793
el = ldb_msg_find_element(mod_ctx->entry, DB_MEMBEROF);
2795
/* all the parents + itself */
2796
ret = mbof_fill_dn_array(mod_ctx, ldb, el, &parents);
2797
if (ret != LDB_SUCCESS) {
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;
2806
parents->dns[parents->num] = mod_ctx->entry->dn;
2809
add_ctx = talloc_zero(mod_ctx, struct mbof_add_ctx);
2811
return LDB_ERR_OPERATIONS_ERROR;
2814
add_ctx->msg_dn = mod_ctx->msg->dn;
2816
for (i = 0; i < ael->num; i++) {
2817
ret = mbof_append_addop(add_ctx, parents, ael->dns[i]);
2818
if (ret != LDB_SUCCESS) {
2823
return mbof_next_add(add_ctx->add_list);
2826
static int mbof_mod_delete(struct mbof_mod_ctx *mod_ctx,
2827
struct mbof_dn_array *del)
2829
struct mbof_del_operation *first;
2830
struct mbof_del_ctx *del_ctx;
2831
struct mbof_ctx *ctx;
2836
del_ctx = talloc_zero(mod_ctx, struct mbof_del_ctx);
2838
return LDB_ERR_OPERATIONS_ERROR;
2841
del_ctx->is_mod = true;
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);
2848
return LDB_ERR_OPERATIONS_ERROR;
2850
del_ctx->first = first;
2852
first->del_ctx = del_ctx;
2853
first->entry = mod_ctx->entry;
2854
first->entry_dn = mod_ctx->entry->dn;
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) {
2864
/* add followup function if we also have stuff to add */
2865
if (mod_ctx->to_add) {
2866
del_ctx->follow_mod = mod_ctx;
2869
/* now that sets are built, start processing */
2870
return mbof_del_execute_op(first->children[0]);
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)
2878
struct mbof_dn_array *ar;
2879
struct ldb_dn *valdn;
2882
ar = talloc_zero(memctx, struct mbof_dn_array);
2884
return LDB_ERR_OPERATIONS_ERROR;
2888
if (!el || el->num_values == 0) {
2892
ar->dns = talloc_array(ar, struct ldb_dn *, el->num_values);
2894
return LDB_ERR_OPERATIONS_ERROR;
2896
ar->num = el->num_values;
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;
2912
/*************************
2913
* Cleanup task routines *
2914
*************************/
2916
struct mbof_member {
2917
struct mbof_member *prev;
2918
struct mbof_member *next;
2922
bool orig_has_memberof;
2923
bool orig_has_memberuid;
2924
struct ldb_message_element *orig_members;
2926
struct mbof_member **members;
2928
hash_table_t *memberofs;
2930
struct ldb_message_element *memuids;
2932
enum { MBOF_GROUP_TO_DO = 0,
2935
MBOF_ITER_ERROR } status;
2938
struct mbof_rcmp_context {
2939
struct ldb_module *module;
2940
struct ldb_request *req;
2942
struct mbof_member *user_list;
2943
hash_table_t *user_table;
2945
struct mbof_member *group_list;
2946
hash_table_t *group_table;
2949
static void *hash_alloc(const size_t size, void *pvt)
2951
return talloc_size(pvt, size);
2954
static void hash_free(void *ptr, void *pvt)
2959
static int mbof_steal_msg_el(TALLOC_CTX *memctx,
2961
struct ldb_message *msg,
2962
struct ldb_message_element **_dest)
2964
struct ldb_message_element *src;
2965
struct ldb_message_element *dest;
2967
src = ldb_msg_find_element(msg, name);
2969
return LDB_ERR_NO_SUCH_ATTRIBUTE;
2972
dest = talloc_zero(memctx, struct ldb_message_element);
2974
return LDB_ERR_OPERATIONS_ERROR;
2978
talloc_steal(dest, dest->name);
2979
talloc_steal(dest, dest->values);
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);
2999
static int memberof_recompute_task(struct ldb_module *module,
3000
struct ldb_request *req)
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;
3009
ctx = talloc_zero(req, struct mbof_rcmp_context);
3011
return LDB_ERR_OPERATIONS_ERROR;
3013
ctx->module = module;
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;
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) {
3030
return ldb_request(ldb, src_req);
3033
static int mbof_rcmp_usr_callback(struct ldb_request *req,
3034
struct ldb_reply *ares)
3036
struct mbof_rcmp_context *ctx;
3037
struct mbof_member *usr;
3043
ctx = talloc_get_type(req->context, struct mbof_rcmp_context);
3046
return ldb_module_done(ctx->req, NULL, NULL,
3047
LDB_ERR_OPERATIONS_ERROR);
3049
if (ares->error != LDB_SUCCESS) {
3050
return ldb_module_done(ctx->req,
3056
switch (ares->type) {
3057
case LDB_REPLY_ENTRY:
3059
usr = talloc_zero(ctx, struct mbof_member);
3061
return ldb_module_done(ctx->req, NULL, NULL,
3062
LDB_ERR_OPERATIONS_ERROR);
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);
3069
usr->name = talloc_steal(usr, name);
3072
if (ldb_msg_find_element(ares->message, DB_MEMBEROF)) {
3073
usr->orig_has_memberof = true;
3076
DLIST_ADD(ctx->user_list, usr);
3078
key.type = HASH_KEY_STRING;
3079
key.str = discard_const(ldb_dn_get_linearized(usr->dn));
3080
value.type = HASH_VALUE_PTR;
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);
3091
case LDB_REPLY_REFERRAL:
3095
case LDB_REPLY_DONE:
3098
/* and now search groups */
3099
return mbof_rcmp_search_groups(ctx);
3106
static int mbof_rcmp_search_groups(struct mbof_rcmp_context *ctx)
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;
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);
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) {
3130
return ldb_request(ldb, req);
3133
static int mbof_rcmp_grp_callback(struct ldb_request *req,
3134
struct ldb_reply *ares)
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;
3147
ctx = talloc_get_type(req->context, struct mbof_rcmp_context);
3148
ldb = ldb_module_get_ctx(ctx->module);
3151
return ldb_module_done(ctx->req, NULL, NULL,
3152
LDB_ERR_OPERATIONS_ERROR);
3154
if (ares->error != LDB_SUCCESS) {
3155
return ldb_module_done(ctx->req,
3161
switch (ares->type) {
3162
case LDB_REPLY_ENTRY:
3164
grp = talloc_zero(ctx, struct mbof_member);
3166
return ldb_module_done(ctx->req, NULL, NULL,
3167
LDB_ERR_OPERATIONS_ERROR);
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);
3175
grp->name = talloc_steal(grp, name);
3178
if (ldb_msg_find_element(ares->message, DB_MEMBEROF)) {
3179
grp->orig_has_memberof = true;
3182
if (ldb_msg_find_element(ares->message, DB_MEMBERUID)) {
3183
grp->orig_has_memberuid = true;
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);
3193
DLIST_ADD(ctx->group_list, grp);
3195
key.type = HASH_KEY_STRING;
3196
key.str = discard_const(ldb_dn_get_linearized(grp->dn));
3197
value.type = HASH_VALUE_PTR;
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);
3208
case LDB_REPLY_REFERRAL:
3212
case LDB_REPLY_DONE:
3215
if (!ctx->group_list) {
3217
return ldb_module_done(ctx->req, NULL, NULL, LDB_SUCCESS);
3220
/* for each group compute the members list */
3221
for (iter = ctx->group_list; iter; iter = iter->next) {
3223
el = iter->orig_members;
3224
if (!el || el->num_values == 0) {
3229
/* we have at most num_values group members */
3230
iter->members = talloc_array(iter, struct mbof_member *,
3232
if (!iter->members) {
3233
return ldb_module_done(ctx->req, NULL, NULL,
3234
LDB_ERR_OPERATIONS_ERROR);
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;
3241
ret = hash_lookup(ctx->user_table, &key, &value);
3244
iter->members[j] = (struct mbof_member *)value.ptr;
3248
case HASH_ERROR_KEY_NOT_FOUND:
3249
/* not a user, see if it is a group */
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);
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);
3267
iter->members[j] = (struct mbof_member *)value.ptr;
3272
return ldb_module_done(ctx->req, NULL, NULL,
3273
LDB_ERR_OPERATIONS_ERROR);
3277
iter->members[j] = NULL;
3279
talloc_zfree(iter->orig_members);
3282
/* now generate correct memberof tables */
3283
while (ctx->group_list->status == MBOF_GROUP_TO_DO) {
3285
grp = ctx->group_list;
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;
3292
/* verify if members need updating */
3293
if (!grp->members) {
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);
3305
/* ok all done, now go on and modify the tree */
3306
return mbof_rcmp_update(ctx);
3313
static int mbof_member_update(struct mbof_rcmp_context *ctx,
3314
struct mbof_member *parent,
3315
struct mbof_member *mem)
3322
if (parent == mem) return LDB_SUCCESS;
3324
key.type = HASH_KEY_STRING;
3325
key.str = discard_const(ldb_dn_get_linearized(parent->dn));
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;
3334
ret = HASH_ERROR_KEY_NOT_FOUND;
3338
ret = hash_lookup(mem->memberofs, &key, &value);
3339
if (ret != HASH_SUCCESS) {
3340
if (ret != HASH_ERROR_KEY_NOT_FOUND) {
3342
return LDB_ERR_OPERATIONS_ERROR;
3347
if (ret == HASH_ERROR_KEY_NOT_FOUND) {
3349
/* it's missing, update member */
3350
value.type = HASH_VALUE_PTR;
3353
ret = hash_enter(mem->memberofs, &key, &value);
3354
if (ret != HASH_SUCCESS) {
3355
return LDB_ERR_OPERATIONS_ERROR;
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) {
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;
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;
3378
if (mem->status == MBOF_ITER_ERROR) {
3379
return LDB_ERR_OPERATIONS_ERROR;
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);
3391
static bool mbof_member_iter(hash_entry_t *item, void *user_data)
3393
struct mbof_member *parent;
3394
struct mbof_member *mem;
3398
mem = talloc_get_type(user_data, struct mbof_member);
3401
if (strcmp(item->key.str, ldb_dn_get_linearized(mem->dn)) == 0) {
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) {
3410
mem->status = MBOF_ITER_ERROR;
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;
3420
if (mem->status == MBOF_GROUP_DONE) {
3421
mem->status = MBOF_GROUP_TO_DO;
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;
3438
static int mbof_add_memuid(struct mbof_member *grp, const char *user)
3440
struct ldb_val *vals;
3443
if (!grp->memuids) {
3444
grp->memuids = talloc_zero(grp, struct ldb_message_element);
3445
if (!grp->memuids) {
3446
return LDB_ERR_OPERATIONS_ERROR;
3449
grp->memuids->name = talloc_strdup(grp->memuids, DB_MEMBERUID);
3450
if (!grp->memuids->name) {
3451
return LDB_ERR_OPERATIONS_ERROR;
3455
n = grp->memuids->num_values;
3456
vals = talloc_realloc(grp->memuids,
3457
grp->memuids->values,
3458
struct ldb_val, n + 1);
3460
return LDB_ERR_OPERATIONS_ERROR;
3463
vals[n].data = (uint8_t *)talloc_strdup(vals, user);
3464
vals[n].length = strlen(user);
3466
grp->memuids->values = vals;
3467
grp->memuids->num_values = n + 1;
3472
static int mbof_rcmp_update(struct mbof_rcmp_context *ctx)
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;
3480
unsigned long count;
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 */
3488
DLIST_REMOVE(ctx->user_list, x);
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);
3496
/* processing terminated, return */
3501
msg = ldb_msg_new(ctx);
3503
ret = LDB_ERR_OPERATIONS_ERROR;
3509
/* process memberof */
3511
ret = hash_keys(x->memberofs, &count, &keys);
3512
if (ret != HASH_SUCCESS) {
3513
ret = LDB_ERR_OPERATIONS_ERROR;
3517
if (x->orig_has_memberof) {
3518
flags = LDB_FLAG_MOD_REPLACE;
3520
flags = LDB_FLAG_MOD_ADD;
3523
ret = ldb_msg_add_empty(msg, DB_MEMBEROF, flags, &el);
3524
if (ret != LDB_SUCCESS) {
3528
el->values = talloc_array(el, struct ldb_val, count);
3530
ret = LDB_ERR_OPERATIONS_ERROR;
3533
el->num_values = count;
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);
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) {
3546
/* process memberuid */
3548
if (x->orig_has_memberuid) {
3549
flags = LDB_FLAG_MOD_REPLACE;
3551
flags = LDB_FLAG_MOD_ADD;
3554
ret = ldb_msg_add(msg, x->memuids, flags);
3555
if (ret != LDB_SUCCESS) {
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) {
3566
ret = ldb_build_mod_req(&req, ldb, ctx, msg, NULL,
3567
ctx, mbof_rcmp_mod_callback,
3569
if (ret != LDB_SUCCESS) {
3572
talloc_steal(req, msg);
3574
/* fire next call */
3575
return ldb_next_request(ctx->module, req);
3578
/* all users and groups have been processed */
3579
return ldb_module_done(ctx->req, NULL, NULL, ret);
3582
static int mbof_rcmp_mod_callback(struct ldb_request *req,
3583
struct ldb_reply *ares)
3585
struct ldb_context *ldb;
3586
struct mbof_rcmp_context *ctx;
3588
ctx = talloc_get_type(req->context, struct mbof_rcmp_context);
3589
ldb = ldb_module_get_ctx(ctx->module);
3592
return ldb_module_done(ctx->req, NULL, NULL,
3593
LDB_ERR_OPERATIONS_ERROR);
3595
if (ares->error != LDB_SUCCESS) {
3596
return ldb_module_done(ctx->req,
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 */
3607
return ldb_module_done(ctx->req, NULL, NULL,
3608
LDB_ERR_OPERATIONS_ERROR);
3609
case LDB_REPLY_REFERRAL:
3614
case LDB_REPLY_DONE:
3617
/* update the next one */
3618
return mbof_rcmp_update(ctx);
3626
/* module init code */
3628
static int memberof_init(struct ldb_module *module)
3630
struct ldb_context *ldb = ldb_module_get_ctx(module);
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;
3638
ret = ldb_schema_attribute_add(ldb, DB_MEMBEROF, 0, LDB_SYNTAX_DN);
3639
if (ret != 0) return LDB_ERR_OPERATIONS_ERROR;
3641
return ldb_next_init(module);
3644
const struct ldb_module_ops ldb_memberof_module_ops = {
3646
.init_context = memberof_init,
3647
.add = memberof_add,
3648
.modify = memberof_mod,
3649
.del = memberof_del,
3652
int ldb_init_module(const char *version)
3654
#ifdef LDB_MODULE_CHECK_VERSION
3655
LDB_MODULE_CHECK_VERSION(version);
3657
return ldb_register_module(&ldb_memberof_module_ops);