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