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