Line data Source code
1 : /*
2 : SSSD
3 :
4 : Authors:
5 : Pavel Březina <pbrezina@redhat.com>
6 :
7 : Copyright (C) 2013 Red Hat
8 :
9 : This program is free software; you can redistribute it and/or modify
10 : it under the terms of the GNU General Public License as published by
11 : the Free Software Foundation; either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : This program is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU General Public License for more details.
18 :
19 : You should have received a copy of the GNU General Public License
20 : along with this program. If not, see <http://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include <errno.h>
24 : #include <string.h>
25 : #include <tevent.h>
26 : #include <talloc.h>
27 : #include <ldb.h>
28 : #include <dhash.h>
29 : #include <stdint.h>
30 : #include <time.h>
31 :
32 : #include "util/util.h"
33 : #include "util/probes.h"
34 : #include "db/sysdb.h"
35 : #include "providers/ldap/ldap_common.h"
36 : #include "providers/ldap/sdap_async.h"
37 : #include "providers/ldap/sdap_async_private.h"
38 : #include "providers/ldap/sdap_idmap.h"
39 : #include "providers/ipa/ipa_dn.h"
40 :
41 : #define sdap_nested_group_sysdb_search_users(domain, filter) \
42 : sdap_nested_group_sysdb_search((domain), (filter), true)
43 :
44 : #define sdap_nested_group_sysdb_search_groups(domain, filter) \
45 : sdap_nested_group_sysdb_search((domain), (filter), false)
46 :
47 : enum sdap_nested_group_dn_type {
48 : SDAP_NESTED_GROUP_DN_USER,
49 : SDAP_NESTED_GROUP_DN_GROUP,
50 : SDAP_NESTED_GROUP_DN_UNKNOWN
51 : };
52 :
53 : struct sdap_nested_group_member {
54 : enum sdap_nested_group_dn_type type;
55 : const char *dn;
56 : const char *user_filter;
57 : const char *group_filter;
58 : };
59 :
60 : #ifndef EXTERNAL_MEMBERS_CHUNK
61 : #define EXTERNAL_MEMBERS_CHUNK 16
62 : #endif /* EXTERNAL_MEMBERS_CHUNK */
63 :
64 : struct sdap_external_missing_member {
65 : const char **parent_group_dns;
66 : size_t parent_dn_idx;
67 : };
68 :
69 : struct sdap_nested_group_ctx {
70 : struct sss_domain_info *domain;
71 : struct sdap_options *opts;
72 : struct sdap_search_base **user_search_bases;
73 : struct sdap_search_base **group_search_bases;
74 : struct sdap_handle *sh;
75 : hash_table_t *users;
76 : hash_table_t *groups;
77 : hash_table_t *missing_external;
78 : bool try_deref;
79 : int deref_treshold;
80 : int max_nesting_level;
81 : };
82 :
83 : static struct tevent_req *
84 : sdap_nested_group_process_send(TALLOC_CTX *mem_ctx,
85 : struct tevent_context *ev,
86 : struct sdap_nested_group_ctx *group_ctx,
87 : int nesting_level,
88 : struct sysdb_attrs *group);
89 :
90 : static errno_t sdap_nested_group_process_recv(struct tevent_req *req);
91 :
92 : static struct tevent_req *
93 : sdap_nested_group_single_send(TALLOC_CTX *mem_ctx,
94 : struct tevent_context *ev,
95 : struct sdap_nested_group_ctx *group_ctx,
96 : struct sdap_nested_group_member *members,
97 : int num_members,
98 : int num_groups_max,
99 : int nesting_level);
100 :
101 : static errno_t sdap_nested_group_single_recv(struct tevent_req *req);
102 :
103 : static struct tevent_req *
104 : sdap_nested_group_lookup_user_send(TALLOC_CTX *mem_ctx,
105 : struct tevent_context *ev,
106 : struct sdap_nested_group_ctx *group_ctx,
107 : struct sdap_nested_group_member *member);
108 :
109 : static errno_t sdap_nested_group_lookup_user_recv(TALLOC_CTX *mem_ctx,
110 : struct tevent_req *req,
111 : struct sysdb_attrs **_user);
112 :
113 : static struct tevent_req *
114 : sdap_nested_group_lookup_group_send(TALLOC_CTX *mem_ctx,
115 : struct tevent_context *ev,
116 : struct sdap_nested_group_ctx *group_ctx,
117 : struct sdap_nested_group_member *member);
118 :
119 : static errno_t sdap_nested_group_lookup_group_recv(TALLOC_CTX *mem_ctx,
120 : struct tevent_req *req,
121 : struct sysdb_attrs **_group);
122 :
123 : static struct tevent_req *
124 : sdap_nested_group_lookup_unknown_send(TALLOC_CTX *mem_ctx,
125 : struct tevent_context *ev,
126 : struct sdap_nested_group_ctx *group_ctx,
127 : struct sdap_nested_group_member *member);
128 :
129 : static errno_t
130 : sdap_nested_group_lookup_unknown_recv(TALLOC_CTX *mem_ctx,
131 : struct tevent_req *req,
132 : struct sysdb_attrs **_entry,
133 : enum sdap_nested_group_dn_type *_type);
134 :
135 : static struct tevent_req *
136 : sdap_nested_group_deref_send(TALLOC_CTX *mem_ctx,
137 : struct tevent_context *ev,
138 : struct sdap_nested_group_ctx *group_ctx,
139 : struct ldb_message_element *members,
140 : const char *group_dn,
141 : int nesting_level);
142 :
143 : static errno_t sdap_nested_group_deref_recv(struct tevent_req *req);
144 :
145 : static errno_t
146 14 : sdap_nested_group_extract_hash_table(TALLOC_CTX *mem_ctx,
147 : hash_table_t *table,
148 : unsigned long *_num_entries,
149 : struct sysdb_attrs ***_entries)
150 : {
151 14 : struct sysdb_attrs **entries = NULL;
152 14 : struct sysdb_attrs *entry = NULL;
153 : hash_value_t *values;
154 : unsigned long num_entries;
155 : unsigned int i;
156 : bool hret;
157 : errno_t ret;
158 :
159 14 : hret = hash_values(table, &num_entries, &values);
160 14 : if (hret != HASH_SUCCESS) {
161 0 : ret = EIO;
162 0 : goto done;
163 : }
164 :
165 14 : if (num_entries > 0) {
166 10 : entries = talloc_array(mem_ctx, struct sysdb_attrs *, num_entries);
167 10 : if (entries == NULL) {
168 0 : ret = ENOMEM;
169 0 : goto done;
170 : }
171 :
172 31 : for (i = 0; i < num_entries; i++) {
173 21 : entry = talloc_get_type(values[i].ptr, struct sysdb_attrs);
174 21 : entries[i] = talloc_steal(entries, entry);
175 : }
176 : }
177 :
178 14 : if (_num_entries != NULL) {
179 14 : *_num_entries = num_entries;
180 : }
181 :
182 14 : if (_entries != NULL) {
183 14 : *_entries = entries;
184 : }
185 :
186 14 : ret = EOK;
187 :
188 : done:
189 14 : talloc_free(values);
190 :
191 14 : if (ret != EOK) {
192 0 : talloc_free(entries);
193 : }
194 :
195 14 : return ret;
196 : }
197 :
198 28 : static errno_t sdap_nested_group_hash_insert(hash_table_t *table,
199 : const char *entry_key,
200 : void *entry_value,
201 : bool overwrite,
202 : const char *table_name)
203 : {
204 : hash_key_t key;
205 : hash_value_t value;
206 : int hret;
207 :
208 28 : DEBUG(SSSDBG_TRACE_ALL, "Inserting [%s] into hash table [%s]\n",
209 : entry_key, table_name);
210 :
211 28 : key.type = HASH_KEY_STRING;
212 28 : key.str = talloc_strdup(NULL, entry_key);
213 28 : if (key.str == NULL) {
214 0 : return ENOMEM;
215 : }
216 :
217 28 : if (overwrite == false && hash_has_key(table, &key)) {
218 2 : talloc_free(key.str);
219 2 : return EEXIST;
220 : }
221 :
222 26 : value.type = HASH_VALUE_PTR;
223 26 : value.ptr = entry_value;
224 :
225 26 : hret = hash_enter(table, &key, &value);
226 26 : if (hret != HASH_SUCCESS) {
227 0 : talloc_free(key.str);
228 0 : return EIO;
229 : }
230 :
231 26 : talloc_steal(table, key.str);
232 26 : talloc_steal(table, value.ptr);
233 :
234 26 : return EOK;
235 : }
236 :
237 26 : static errno_t sdap_nested_group_hash_entry(hash_table_t *table,
238 : struct sysdb_attrs *entry,
239 : const char *table_name)
240 : {
241 26 : const char *name = NULL;
242 : errno_t ret;
243 :
244 26 : ret = sysdb_attrs_get_string(entry, SYSDB_ORIG_DN, &name);
245 26 : if (ret != EOK) {
246 0 : return ret;
247 : }
248 :
249 26 : return sdap_nested_group_hash_insert(table, name, entry, false, table_name);
250 : }
251 :
252 : static errno_t
253 7 : sdap_nested_group_hash_user(struct sdap_nested_group_ctx *group_ctx,
254 : struct sysdb_attrs *user)
255 : {
256 7 : return sdap_nested_group_hash_entry(group_ctx->users, user, "users");
257 : }
258 :
259 : static errno_t
260 19 : sdap_nested_group_hash_group(struct sdap_nested_group_ctx *group_ctx,
261 : struct sysdb_attrs *group)
262 : {
263 19 : struct sdap_attr_map *map = group_ctx->opts->group_map;
264 : gid_t gid;
265 : errno_t ret;
266 19 : bool posix_group = true;
267 : bool use_id_mapping;
268 : bool can_find_gid;
269 : bool need_filter;
270 :
271 19 : ret = sdap_check_ad_group_type(group_ctx->domain, group_ctx->opts,
272 : group, "", &need_filter);
273 19 : if (ret != EOK) {
274 0 : return ret;
275 : }
276 :
277 19 : if (need_filter) {
278 0 : posix_group = false;
279 0 : gid = 0;
280 : }
281 :
282 19 : use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(
283 19 : group_ctx->opts->idmap_ctx,
284 19 : group_ctx->domain->name,
285 19 : group_ctx->domain->domain_id);
286 :
287 19 : can_find_gid = posix_group && !use_id_mapping;
288 19 : if (can_find_gid) {
289 19 : ret = sysdb_attrs_get_uint32_t(group, map[SDAP_AT_GROUP_GID].sys_name,
290 : &gid);
291 : }
292 19 : if (!can_find_gid || ret == ENOENT || (ret == EOK && gid == 0)) {
293 0 : DEBUG(SSSDBG_TRACE_ALL,
294 : "The group's gid was %s\n", ret == ENOENT ? "missing" : "zero");
295 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
296 : "Marking group as non-posix and setting GID=0!\n");
297 :
298 0 : if (ret == ENOENT || !posix_group) {
299 0 : ret = sysdb_attrs_add_uint32(group,
300 0 : map[SDAP_AT_GROUP_GID].sys_name, 0);
301 0 : if (ret != EOK) {
302 0 : DEBUG(SSSDBG_CRIT_FAILURE,
303 : "Failed to add a GID to non-posix group!\n");
304 0 : return ret;
305 : }
306 : }
307 :
308 0 : ret = sysdb_attrs_add_bool(group, SYSDB_POSIX, false);
309 0 : if (ret != EOK) {
310 0 : DEBUG(SSSDBG_OP_FAILURE,
311 : "Error: Failed to mark group as non-posix!\n");
312 0 : return ret;
313 : }
314 19 : } else if (ret != EOK) {
315 0 : return ret;
316 : }
317 :
318 19 : return sdap_nested_group_hash_entry(group_ctx->groups, group, "groups");
319 : }
320 :
321 3 : static errno_t sdap_nested_group_external_add(hash_table_t *table,
322 : const char *ext_member,
323 : const char *parent_group_dn)
324 : {
325 : hash_key_t key;
326 : hash_value_t value;
327 : int hret;
328 : int ret;
329 : struct sdap_external_missing_member *ext_mem;
330 :
331 3 : key.type = HASH_KEY_STRING;
332 3 : key.str = discard_const(ext_member);
333 :
334 3 : DEBUG(SSSDBG_TRACE_ALL,
335 : "Inserting external member [%s] into external members hash table\n",
336 : ext_member);
337 :
338 3 : hret = hash_lookup(table, &key, &value);
339 3 : switch (hret) {
340 : case HASH_ERROR_KEY_NOT_FOUND:
341 2 : ext_mem = talloc_zero(table, struct sdap_external_missing_member);
342 2 : if (ext_mem == NULL) {
343 0 : return ENOMEM;
344 : }
345 2 : ext_mem->parent_group_dns = talloc_zero_array(ext_mem,
346 : const char *,
347 : EXTERNAL_MEMBERS_CHUNK);
348 2 : if (ext_mem->parent_group_dns == NULL) {
349 0 : talloc_free(ext_mem);
350 0 : return ENOMEM;
351 : }
352 :
353 2 : ret = sdap_nested_group_hash_insert(table, ext_member, ext_mem,
354 : true, "missing external users");
355 2 : if (ret != EOK) {
356 0 : return ret;
357 : }
358 2 : break;
359 :
360 : case HASH_SUCCESS:
361 1 : ext_mem = talloc_get_type(value.ptr,
362 : struct sdap_external_missing_member);
363 2 : if (ext_mem->parent_dn_idx == \
364 1 : talloc_array_length(ext_mem->parent_group_dns)) {
365 1 : ext_mem->parent_group_dns = talloc_realloc(ext_mem,
366 : ext_mem->parent_group_dns,
367 : const char *,
368 : ext_mem->parent_dn_idx + \
369 : EXTERNAL_MEMBERS_CHUNK);
370 1 : if (ext_mem->parent_group_dns == NULL) {
371 0 : talloc_free(ext_mem);
372 0 : return ENOMEM;
373 : }
374 : }
375 1 : break;
376 : default:
377 0 : return EIO;
378 : }
379 :
380 6 : ext_mem->parent_group_dns[ext_mem->parent_dn_idx] = \
381 3 : talloc_strdup(ext_mem->parent_group_dns,
382 : parent_group_dn);
383 3 : if (ext_mem->parent_group_dns[ext_mem->parent_dn_idx] == NULL) {
384 0 : return ENOMEM;
385 : }
386 3 : ext_mem->parent_dn_idx++;
387 :
388 3 : return EOK;
389 : }
390 :
391 38 : static errno_t sdap_nested_group_sysdb_search(struct sss_domain_info *domain,
392 : const char *filter,
393 : bool user)
394 : {
395 : static const char *attrs[] = {SYSDB_CACHE_EXPIRE,
396 : SYSDB_UIDNUM,
397 : NULL};
398 38 : struct ldb_message **msgs = NULL;
399 : size_t count;
400 38 : time_t now = time(NULL);
401 : uint64_t expire;
402 : uid_t uid;
403 : errno_t ret;
404 :
405 38 : if (user) {
406 19 : ret = sysdb_search_users(NULL, domain, filter, attrs,
407 : &count, &msgs);
408 : } else {
409 19 : ret = sysdb_search_groups(NULL, domain, filter, attrs,
410 : &count, &msgs);
411 : }
412 38 : if (ret != EOK) {
413 38 : goto done;
414 : }
415 :
416 0 : if (count != 1) {
417 0 : DEBUG(SSSDBG_OP_FAILURE, "More than one entry found?\n");
418 0 : ret = EFAULT;
419 0 : goto done;
420 : }
421 :
422 : /* we found an object with this origDN in the sysdb,
423 : * check if it is valid */
424 0 : if (user) {
425 0 : uid = ldb_msg_find_attr_as_uint64(msgs[0], SYSDB_UIDNUM, 0);
426 0 : if (uid == 0) {
427 0 : DEBUG(SSSDBG_OP_FAILURE, "User with no UID?\n");
428 0 : ret = EINVAL;
429 0 : goto done;
430 : }
431 : }
432 :
433 0 : expire = ldb_msg_find_attr_as_uint64(msgs[0], SYSDB_CACHE_EXPIRE, 0);
434 0 : if (expire != 0 && expire <= now) {
435 : /* needs refresh */
436 0 : ret = EAGAIN;
437 0 : goto done;
438 : }
439 :
440 : /* valid object */
441 0 : ret = EOK;
442 :
443 : done:
444 38 : talloc_zfree(msgs);
445 38 : return ret;
446 : }
447 :
448 : static errno_t
449 19 : sdap_nested_group_check_cache(struct sdap_options *opts,
450 : struct sss_domain_info *domain,
451 : const char *member_dn,
452 : enum sdap_nested_group_dn_type *_type)
453 : {
454 19 : TALLOC_CTX *tmp_ctx = NULL;
455 19 : struct sdap_domain *sdap_domain = NULL;
456 19 : struct sss_domain_info *member_domain = NULL;
457 19 : char *sanitized_dn = NULL;
458 19 : char *filter = NULL;
459 : errno_t ret;
460 :
461 19 : tmp_ctx = talloc_new(NULL);
462 19 : if (tmp_ctx == NULL) {
463 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
464 0 : return ENOMEM;
465 : }
466 :
467 19 : ret = sss_filter_sanitize(tmp_ctx, member_dn, &sanitized_dn);
468 19 : if (ret != EOK) {
469 0 : goto done;
470 : }
471 :
472 19 : filter = talloc_asprintf(tmp_ctx, "(%s=%s)", SYSDB_ORIG_DN, sanitized_dn);
473 19 : if (filter == NULL) {
474 0 : ret = ENOMEM;
475 0 : goto done;
476 : }
477 :
478 : /* determine correct domain of this member */
479 19 : sdap_domain = sdap_domain_get_by_dn(opts, member_dn);
480 19 : member_domain = sdap_domain == NULL ? domain : sdap_domain->dom;
481 :
482 : /* search in users */
483 : PROBE(SDAP_NESTED_GROUP_SYSDB_SEARCH_USERS_PRE);
484 19 : ret = sdap_nested_group_sysdb_search_users(member_domain, filter);
485 : PROBE(SDAP_NESTED_GROUP_SYSDB_SEARCH_USERS_POST);
486 19 : if (ret == EOK || ret == EAGAIN) {
487 : /* user found */
488 0 : *_type = SDAP_NESTED_GROUP_DN_USER;
489 0 : goto done;
490 19 : } else if (ret != ENOENT) {
491 : /* error */
492 0 : goto done;
493 : }
494 :
495 : /* search in groups */
496 : PROBE(SDAP_NESTED_GROUP_SYSDB_SEARCH_GROUPS_PRE);
497 19 : ret = sdap_nested_group_sysdb_search_groups(member_domain, filter);
498 : PROBE(SDAP_NESTED_GROUP_SYSDB_SEARCH_GROUPS_POST);
499 19 : if (ret == EOK || ret == EAGAIN) {
500 : /* group found */
501 0 : *_type = SDAP_NESTED_GROUP_DN_GROUP;
502 0 : goto done;
503 19 : } else if (ret != ENOENT) {
504 : /* error */
505 0 : goto done;
506 : }
507 :
508 : /* not found in the sysdb */
509 19 : ret = ENOENT;
510 :
511 : done:
512 19 : talloc_free(tmp_ctx);
513 19 : return ret;
514 : }
515 :
516 : static bool
517 38 : sdap_nested_member_is_ent(struct sdap_nested_group_ctx *group_ctx,
518 : const char *dn, char **filter, bool is_user)
519 : {
520 38 : struct sdap_domain *sditer = NULL;
521 38 : bool ret = false;
522 : struct sdap_search_base **search_bases;
523 :
524 57 : DLIST_FOR_EACH(sditer, group_ctx->opts->sdom) {
525 38 : search_bases = is_user ? sditer->user_search_bases : \
526 : sditer->group_search_bases;
527 :
528 38 : ret = sss_ldap_dn_in_search_bases(group_ctx, dn, search_bases,
529 : filter);
530 38 : if (ret == true) {
531 19 : break;
532 : }
533 : }
534 :
535 38 : return ret;
536 : }
537 :
538 : static inline bool
539 19 : sdap_nested_member_is_user(struct sdap_nested_group_ctx *group_ctx,
540 : const char *dn, char **filter)
541 : {
542 19 : return sdap_nested_member_is_ent(group_ctx, dn, filter, true);
543 : }
544 :
545 : static inline bool
546 19 : sdap_nested_member_is_group(struct sdap_nested_group_ctx *group_ctx,
547 : const char *dn, char **filter)
548 : {
549 19 : return sdap_nested_member_is_ent(group_ctx, dn, filter, false);
550 : }
551 :
552 : static errno_t
553 14 : sdap_nested_group_split_members(TALLOC_CTX *mem_ctx,
554 : struct sdap_nested_group_ctx *group_ctx,
555 : int threshold,
556 : int nesting_level,
557 : struct ldb_message_element *members,
558 : struct sdap_nested_group_member **_missing,
559 : int *_num_missing,
560 : int *_num_groups)
561 : {
562 14 : TALLOC_CTX *tmp_ctx = NULL;
563 14 : struct sdap_nested_group_member *missing = NULL;
564 : enum sdap_nested_group_dn_type type;
565 14 : char *dn = NULL;
566 14 : char *user_filter = NULL;
567 14 : char *group_filter = NULL;
568 14 : int num_missing = 0;
569 14 : int num_groups = 0;
570 : hash_key_t key;
571 : bool bret;
572 : bool is_user;
573 : bool is_group;
574 : errno_t ret;
575 : int i;
576 :
577 14 : if (members == NULL) {
578 2 : *_missing = NULL;
579 2 : *_num_missing = 0;
580 2 : *_num_groups = 0;
581 2 : return EOK;
582 : }
583 :
584 12 : tmp_ctx = talloc_new(NULL);
585 12 : if (tmp_ctx == NULL) {
586 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
587 0 : return ENOMEM;
588 : }
589 :
590 12 : missing = talloc_zero_array(tmp_ctx, struct sdap_nested_group_member,
591 : members->num_values);
592 12 : if (missing == NULL) {
593 0 : ret = ENOMEM;
594 0 : goto done;
595 : }
596 :
597 : /* create list of missing members
598 : * skip dn if:
599 : * - is present in user or group hash table
600 : * - is present in sysdb and not expired
601 : * - it is a group and we have reached the maximal nesting level
602 : * - it is not under user nor group search bases
603 : *
604 : * if dn is in sysdb but expired
605 : * - we know what object type it is
606 : *
607 : * if dn is not in hash table or sysdb
608 : * - try to determine type of object by search base that match dn
609 : */
610 31 : for (i = 0; i < members->num_values; i++) {
611 19 : dn = (char*)members->values[i].data;
612 19 : type = SDAP_NESTED_GROUP_DN_UNKNOWN;
613 :
614 : /* check hash tables */
615 19 : key.type = HASH_KEY_STRING;
616 19 : key.str = dn;
617 :
618 19 : bret = hash_has_key(group_ctx->users, &key);
619 19 : if (bret) {
620 0 : continue;
621 : }
622 :
623 19 : bret = hash_has_key(group_ctx->groups, &key);
624 19 : if (bret) {
625 0 : continue;
626 : }
627 :
628 : /* check sysdb */
629 : PROBE(SDAP_NESTED_GROUP_CHECK_CACHE_PRE);
630 19 : ret = sdap_nested_group_check_cache(group_ctx->opts, group_ctx->domain,
631 : dn, &type);
632 : PROBE(SDAP_NESTED_GROUP_CHECK_CACHE_POST);
633 19 : if (ret == EOK) {
634 : /* found and valid */
635 0 : DEBUG(SSSDBG_TRACE_ALL, "[%s] found in cache, skipping\n", dn);
636 0 : continue;
637 19 : } else if (ret != EAGAIN && ret != ENOENT) {
638 : /* error */
639 0 : goto done;
640 : }
641 :
642 : /* try to determine type by dn */
643 19 : if (type == SDAP_NESTED_GROUP_DN_UNKNOWN) {
644 : /* user */
645 19 : is_user = sdap_nested_member_is_user(group_ctx, dn,
646 : &user_filter);
647 :
648 19 : is_group = sdap_nested_member_is_group(group_ctx, dn,
649 : &group_filter);
650 :
651 19 : if (is_user && is_group) {
652 : /* search bases overlap */
653 0 : DEBUG(SSSDBG_TRACE_ALL, "[%s] is unknown object\n", dn);
654 0 : type = SDAP_NESTED_GROUP_DN_UNKNOWN;
655 19 : } else if (is_user) {
656 8 : DEBUG(SSSDBG_TRACE_ALL, "[%s] is a user\n", dn);
657 8 : type = SDAP_NESTED_GROUP_DN_USER;
658 11 : } else if (is_group) {
659 11 : DEBUG(SSSDBG_TRACE_ALL, "[%s] is a group\n", dn);
660 11 : type = SDAP_NESTED_GROUP_DN_GROUP;
661 : } else {
662 : /* dn is outside search bases */
663 0 : DEBUG(SSSDBG_TRACE_ALL, "[%s] is out of scope of configured "
664 : "search bases, skipping\n", dn);
665 0 : continue;
666 : }
667 : }
668 :
669 : /* check nesting level */
670 19 : if (type == SDAP_NESTED_GROUP_DN_GROUP) {
671 11 : if (nesting_level >= group_ctx->max_nesting_level) {
672 0 : DEBUG(SSSDBG_TRACE_ALL, "[%s] is outside nesting limit "
673 : "(level %d), skipping\n", dn, nesting_level);
674 0 : talloc_zfree(user_filter);
675 0 : talloc_zfree(group_filter);
676 0 : continue;
677 : }
678 : }
679 :
680 19 : missing[num_missing].dn = talloc_strdup(missing, dn);
681 19 : if (missing[num_missing].dn == NULL) {
682 0 : ret = ENOMEM;
683 0 : goto done;
684 : }
685 :
686 19 : missing[num_missing].type = type;
687 19 : missing[num_missing].user_filter = talloc_steal(missing, user_filter);
688 19 : missing[num_missing].group_filter = talloc_steal(missing, group_filter);
689 :
690 19 : num_missing++;
691 19 : if (threshold > 0 && num_missing > threshold) {
692 0 : if (_num_missing) {
693 0 : *_num_missing = num_missing;
694 : }
695 :
696 0 : ret = ERR_DEREF_THRESHOLD;
697 0 : goto done;
698 : }
699 :
700 19 : if (type != SDAP_NESTED_GROUP_DN_USER) {
701 11 : num_groups++;
702 : }
703 : }
704 :
705 12 : missing = talloc_realloc(mem_ctx, missing,
706 : struct sdap_nested_group_member, num_missing);
707 : /* talloc_realloc behaves as talloc_free if 3rd parameter (count) is 0,
708 : * so it's OK to return NULL then
709 : */
710 12 : if (missing == NULL && num_missing > 0) {
711 0 : ret = ENOMEM;
712 0 : goto done;
713 : }
714 :
715 12 : if (_missing) {
716 12 : *_missing = talloc_steal(mem_ctx, missing);
717 : }
718 :
719 12 : if (_num_missing) {
720 12 : *_num_missing = num_missing;
721 : }
722 :
723 12 : if (_num_groups) {
724 12 : *_num_groups = num_groups;
725 : }
726 :
727 12 : ret = EOK;
728 :
729 : done:
730 12 : talloc_free(tmp_ctx);
731 :
732 12 : return ret;
733 : }
734 :
735 : static errno_t
736 14 : sdap_nested_group_add_ext_members(struct sdap_nested_group_ctx *group_ctx,
737 : struct sysdb_attrs *group,
738 : struct ldb_message_element *ext_members)
739 : {
740 : errno_t ret;
741 : const char *ext_member_attr;
742 : const char *orig_dn;
743 :
744 14 : if (ext_members == NULL) {
745 12 : return EOK;
746 : }
747 :
748 2 : ret = sysdb_attrs_get_string(group, SYSDB_ORIG_DN, &orig_dn);
749 2 : if (ret != EOK) {
750 0 : DEBUG(SSSDBG_CRIT_FAILURE, "A group with no originalDN!?!\n");
751 0 : return ret;
752 : }
753 :
754 5 : for (size_t i = 0; i < ext_members->num_values; i++) {
755 3 : ext_member_attr = (const char *) ext_members->values[i].data;
756 :
757 3 : ret = sdap_nested_group_external_add(group_ctx->missing_external,
758 : ext_member_attr,
759 : orig_dn);
760 3 : if (ret != EOK) {
761 0 : DEBUG(SSSDBG_CRIT_FAILURE,
762 : "Cannot add %s into external members [%d]: %s\n",
763 : ext_member_attr, ret, sss_strerror(ret));
764 0 : return ret;
765 : }
766 : }
767 :
768 2 : return EOK;
769 : }
770 :
771 : static struct ldb_message_element *
772 18 : sdap_nested_group_ext_members(struct sdap_options *opts,
773 : struct sysdb_attrs *group)
774 : {
775 : errno_t ret;
776 18 : struct ldb_message_element *ext_members = NULL;
777 :
778 18 : if (opts->ext_ctx == NULL) {
779 14 : return NULL;
780 : }
781 :
782 4 : ret = sysdb_attrs_get_el_ext(group,
783 4 : opts->group_map[SDAP_AT_GROUP_EXT_MEMBER].sys_name,
784 : false, &ext_members);
785 4 : if (ret != EOK && ret != ENOENT) {
786 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to retrieve external member list "
787 : "[%d]: %s\n", ret, sss_strerror(ret));
788 : }
789 :
790 4 : return ext_members;
791 : }
792 :
793 :
794 : struct sdap_nested_group_state {
795 : struct sdap_nested_group_ctx *group_ctx;
796 : };
797 :
798 : static void sdap_nested_group_done(struct tevent_req *subreq);
799 :
800 : struct tevent_req *
801 8 : sdap_nested_group_send(TALLOC_CTX *mem_ctx,
802 : struct tevent_context *ev,
803 : struct sdap_domain *sdom,
804 : struct sdap_options *opts,
805 : struct sdap_handle *sh,
806 : struct sysdb_attrs *group)
807 : {
808 8 : struct sdap_nested_group_state *state = NULL;
809 8 : struct tevent_req *req = NULL;
810 8 : struct tevent_req *subreq = NULL;
811 : errno_t ret;
812 : int i;
813 :
814 : PROBE(SDAP_NESTED_GROUP_SEND);
815 :
816 8 : req = tevent_req_create(mem_ctx, &state, struct sdap_nested_group_state);
817 8 : if (req == NULL) {
818 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
819 0 : return NULL;
820 : }
821 :
822 : /* create main nested group context */
823 8 : state->group_ctx = talloc_zero(state, struct sdap_nested_group_ctx);
824 8 : if (state->group_ctx == NULL) {
825 0 : ret = ENOMEM;
826 0 : goto immediately;
827 : }
828 :
829 8 : ret = sss_hash_create(state->group_ctx, 32, &state->group_ctx->users);
830 8 : if (ret != EOK) {
831 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create hash table [%d]: %s\n",
832 : ret, strerror(ret));
833 0 : goto immediately;
834 : }
835 :
836 8 : ret = sss_hash_create(state->group_ctx, 32, &state->group_ctx->groups);
837 8 : if (ret != EOK) {
838 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create hash table [%d]: %s\n",
839 : ret, strerror(ret));
840 0 : goto immediately;
841 : }
842 :
843 8 : ret = sss_hash_create(state->group_ctx, 32,
844 8 : &state->group_ctx->missing_external);
845 8 : if (ret != EOK) {
846 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create hash table [%d]: %s\n",
847 : ret, strerror(ret));
848 0 : goto immediately;
849 : }
850 :
851 8 : state->group_ctx->try_deref = true;
852 8 : state->group_ctx->deref_treshold = dp_opt_get_int(opts->basic,
853 : SDAP_DEREF_THRESHOLD);
854 8 : state->group_ctx->max_nesting_level = dp_opt_get_int(opts->basic,
855 : SDAP_NESTING_LEVEL);
856 8 : state->group_ctx->domain = sdom->dom;
857 8 : state->group_ctx->opts = opts;
858 8 : state->group_ctx->user_search_bases = sdom->user_search_bases;
859 8 : state->group_ctx->group_search_bases = sdom->group_search_bases;
860 8 : state->group_ctx->sh = sh;
861 8 : state->group_ctx->try_deref = sdap_has_deref_support(sh, opts);
862 :
863 : /* disable deref if threshold <= 0 */
864 8 : if (state->group_ctx->deref_treshold <= 0) {
865 0 : state->group_ctx->try_deref = false;
866 : }
867 :
868 : /* if any search base contains filter, disable dereference. */
869 8 : if (state->group_ctx->try_deref) {
870 0 : for (i = 0; opts->sdom->user_search_bases[i] != NULL; i++) {
871 0 : if (opts->sdom->user_search_bases[i]->filter != NULL) {
872 0 : DEBUG(SSSDBG_TRACE_FUNC, "User search base contains filter, "
873 : "dereference will be disabled\n");
874 0 : state->group_ctx->try_deref = false;
875 0 : break;
876 : }
877 : }
878 : }
879 :
880 8 : if (state->group_ctx->try_deref) {
881 0 : for (i = 0; opts->sdom->group_search_bases[i] != NULL; i++) {
882 0 : if (opts->sdom->group_search_bases[i]->filter != NULL) {
883 0 : DEBUG(SSSDBG_TRACE_FUNC, "Group search base contains filter, "
884 : "dereference will be disabled\n");
885 0 : state->group_ctx->try_deref = false;
886 0 : break;
887 : }
888 : }
889 : }
890 :
891 : /* insert initial group into hash table */
892 8 : ret = sdap_nested_group_hash_group(state->group_ctx, group);
893 8 : if (ret != EOK) {
894 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to insert group into hash table "
895 : "[%d]: %s\n", ret, strerror(ret));
896 0 : goto immediately;
897 : }
898 :
899 : /* resolve group */
900 8 : subreq = sdap_nested_group_process_send(state, ev, state->group_ctx,
901 : 0, group);
902 8 : if (subreq == NULL) {
903 0 : ret = ENOMEM;
904 0 : goto immediately;
905 : }
906 :
907 8 : tevent_req_set_callback(subreq, sdap_nested_group_done, req);
908 :
909 8 : return req;
910 :
911 : immediately:
912 0 : if (ret == EOK) {
913 0 : tevent_req_done(req);
914 : } else {
915 0 : tevent_req_error(req, ret);
916 : }
917 0 : tevent_req_post(req, ev);
918 :
919 0 : return req;
920 : }
921 :
922 8 : static void sdap_nested_group_done(struct tevent_req *subreq)
923 : {
924 8 : struct tevent_req *req = NULL;
925 : errno_t ret;
926 :
927 8 : req = tevent_req_callback_data(subreq, struct tevent_req);
928 :
929 8 : ret = sdap_nested_group_process_recv(subreq);
930 8 : talloc_zfree(subreq);
931 8 : if (ret != EOK) {
932 1 : tevent_req_error(req, ret);
933 9 : return;
934 : }
935 :
936 7 : tevent_req_done(req);
937 : }
938 :
939 8 : errno_t sdap_nested_group_recv(TALLOC_CTX *mem_ctx,
940 : struct tevent_req *req,
941 : unsigned long *_num_users,
942 : struct sysdb_attrs ***_users,
943 : unsigned long *_num_groups,
944 : struct sysdb_attrs ***_groups,
945 : hash_table_t **_missing_external)
946 : {
947 8 : struct sdap_nested_group_state *state = NULL;
948 8 : struct sysdb_attrs **users = NULL;
949 8 : struct sysdb_attrs **groups = NULL;
950 : unsigned long num_users;
951 : unsigned long num_groups;
952 : errno_t ret;
953 :
954 8 : state = tevent_req_data(req, struct sdap_nested_group_state);
955 :
956 : PROBE(SDAP_NESTED_GROUP_RECV);
957 9 : TEVENT_REQ_RETURN_ON_ERROR(req);
958 :
959 7 : ret = sdap_nested_group_extract_hash_table(state, state->group_ctx->users,
960 : &num_users, &users);
961 7 : if (ret != EOK) {
962 0 : return ret;
963 : }
964 :
965 7 : DEBUG(SSSDBG_TRACE_FUNC, "%lu users found in the hash table\n",
966 : num_users);
967 :
968 7 : ret = sdap_nested_group_extract_hash_table(state, state->group_ctx->groups,
969 : &num_groups, &groups);
970 7 : if (ret != EOK) {
971 0 : return ret;
972 : }
973 :
974 7 : DEBUG(SSSDBG_TRACE_FUNC, "%lu groups found in the hash table\n",
975 : num_groups);
976 :
977 7 : if (_num_users != NULL) {
978 7 : *_num_users = num_users;
979 : }
980 :
981 7 : if (_users != NULL) {
982 7 : *_users = talloc_steal(mem_ctx, users);
983 : }
984 :
985 7 : if (_num_groups!= NULL) {
986 7 : *_num_groups = num_groups;
987 : }
988 :
989 7 : if (_groups != NULL) {
990 7 : *_groups = talloc_steal(mem_ctx, groups);
991 : }
992 :
993 7 : if (_missing_external) {
994 7 : *_missing_external = talloc_steal(mem_ctx,
995 : state->group_ctx->missing_external);
996 : }
997 :
998 7 : return EOK;
999 : }
1000 :
1001 : struct sdap_nested_group_process_state {
1002 : struct tevent_context *ev;
1003 : struct sdap_nested_group_ctx *group_ctx;
1004 : struct sdap_nested_group_member *missing;
1005 : int num_missing_total;
1006 : int num_missing_groups;
1007 : struct ldb_message_element *ext_members;
1008 : struct ldb_message_element *members;
1009 : int nesting_level;
1010 : char *group_dn;
1011 : bool deref;
1012 : bool deref_shortcut;
1013 : };
1014 :
1015 : static void sdap_nested_group_process_done(struct tevent_req *subreq);
1016 :
1017 : static struct tevent_req *
1018 18 : sdap_nested_group_process_send(TALLOC_CTX *mem_ctx,
1019 : struct tevent_context *ev,
1020 : struct sdap_nested_group_ctx *group_ctx,
1021 : int nesting_level,
1022 : struct sysdb_attrs *group)
1023 : {
1024 18 : struct sdap_nested_group_process_state *state = NULL;
1025 18 : struct sdap_attr_map *group_map = NULL;
1026 18 : struct tevent_req *req = NULL;
1027 18 : struct tevent_req *subreq = NULL;
1028 18 : const char *orig_dn = NULL;
1029 : errno_t ret;
1030 : int split_threshold;
1031 :
1032 18 : req = tevent_req_create(mem_ctx, &state,
1033 : struct sdap_nested_group_process_state);
1034 18 : if (req == NULL) {
1035 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
1036 0 : return NULL;
1037 : }
1038 :
1039 18 : state->ev = ev;
1040 18 : state->group_ctx = group_ctx;
1041 18 : state->nesting_level = nesting_level;
1042 18 : group_map = state->group_ctx->opts->group_map;
1043 :
1044 : /* get original dn */
1045 18 : ret = sysdb_attrs_get_string(group, SYSDB_ORIG_DN, &orig_dn);
1046 18 : if (ret != EOK) {
1047 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to retrieve original dn "
1048 : "[%d]: %s\n", ret, strerror(ret));
1049 0 : goto immediately;
1050 : }
1051 :
1052 18 : state->group_dn = talloc_strdup(state, orig_dn);
1053 18 : if (state->group_dn == NULL) {
1054 0 : ret = ENOMEM;
1055 0 : goto immediately;
1056 : }
1057 :
1058 18 : DEBUG(SSSDBG_TRACE_INTERNAL, "About to process group [%s]\n", orig_dn);
1059 : PROBE(SDAP_NESTED_GROUP_PROCESS_SEND, state->group_dn);
1060 :
1061 : /* get member list, both direct and external */
1062 18 : state->ext_members = sdap_nested_group_ext_members(state->group_ctx->opts,
1063 : group);
1064 :
1065 18 : ret = sysdb_attrs_get_el_ext(group, group_map[SDAP_AT_GROUP_MEMBER].sys_name,
1066 18 : false, &state->members);
1067 18 : if (ret == ENOENT && state->ext_members == NULL) {
1068 4 : ret = EOK; /* no members, direct or external */
1069 4 : goto immediately;
1070 14 : } else if (ret != EOK && ret != ENOENT) {
1071 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to retrieve member list "
1072 : "[%d]: %s\n", ret, strerror(ret));
1073 0 : goto immediately;
1074 : }
1075 :
1076 28 : split_threshold = state->group_ctx->try_deref ? \
1077 14 : state->group_ctx->deref_treshold : \
1078 : -1;
1079 :
1080 : /* get members that need to be refreshed */
1081 : PROBE(SDAP_NESTED_GROUP_PROCESS_SPLIT_PRE);
1082 70 : ret = sdap_nested_group_split_members(state, state->group_ctx,
1083 : split_threshold,
1084 14 : state->nesting_level,
1085 14 : state->members,
1086 14 : &state->missing,
1087 14 : &state->num_missing_total,
1088 14 : &state->num_missing_groups);
1089 : PROBE(SDAP_NESTED_GROUP_PROCESS_SPLIT_POST);
1090 14 : if (ret == ERR_DEREF_THRESHOLD) {
1091 0 : DEBUG(SSSDBG_TRACE_FUNC,
1092 : "More members were missing than the deref threshold\n");
1093 0 : state->deref_shortcut = true;
1094 14 : } else if (ret != EOK) {
1095 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to split member list "
1096 : "[%d]: %s\n", ret, sss_strerror(ret));
1097 0 : goto immediately;
1098 : }
1099 :
1100 14 : ret = sdap_nested_group_add_ext_members(state->group_ctx,
1101 : group,
1102 14 : state->ext_members);
1103 14 : if (ret != EOK) {
1104 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to split external member list "
1105 : "[%d]: %s\n", ret, sss_strerror(ret));
1106 0 : goto immediately;
1107 : }
1108 :
1109 14 : if (state->num_missing_total == 0
1110 2 : && hash_count(state->group_ctx->missing_external) == 0) {
1111 0 : ret = EOK; /* we're done */
1112 0 : goto immediately;
1113 : }
1114 :
1115 : /* If there are only indirect members of the group, it's still safe to
1116 : * proceed and let the direct lookup code just fall through.
1117 : */
1118 :
1119 14 : DEBUG(SSSDBG_TRACE_INTERNAL,
1120 : "Looking up %d/%d members of group [%s]\n",
1121 : state->num_missing_total,
1122 : state->members ? state->members->num_values : 0,
1123 : orig_dn);
1124 :
1125 : /* process members */
1126 14 : if (group_ctx->try_deref
1127 0 : && state->num_missing_total > group_ctx->deref_treshold) {
1128 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Dereferencing members of group [%s]\n",
1129 : orig_dn);
1130 0 : state->deref = true;
1131 0 : subreq = sdap_nested_group_deref_send(state, ev, group_ctx,
1132 0 : state->members, orig_dn,
1133 0 : state->nesting_level);
1134 : } else {
1135 14 : DEBUG(SSSDBG_TRACE_INTERNAL, "Members of group [%s] will be "
1136 : "processed individually\n", orig_dn);
1137 14 : state->deref = false;
1138 56 : subreq = sdap_nested_group_single_send(state, ev, group_ctx,
1139 14 : state->missing,
1140 14 : state->num_missing_total,
1141 14 : state->num_missing_groups,
1142 14 : state->nesting_level);
1143 : }
1144 14 : if (subreq == NULL) {
1145 0 : ret = ENOMEM;
1146 0 : goto immediately;
1147 : }
1148 :
1149 14 : tevent_req_set_callback(subreq, sdap_nested_group_process_done, req);
1150 :
1151 14 : return req;
1152 :
1153 : immediately:
1154 4 : if (ret == EOK) {
1155 4 : tevent_req_done(req);
1156 : } else {
1157 0 : tevent_req_error(req, ret);
1158 : }
1159 4 : tevent_req_post(req, ev);
1160 :
1161 4 : return req;
1162 : }
1163 :
1164 14 : static void sdap_nested_group_process_done(struct tevent_req *subreq)
1165 : {
1166 14 : struct sdap_nested_group_process_state *state = NULL;
1167 14 : struct tevent_req *req = NULL;
1168 : errno_t ret;
1169 :
1170 14 : req = tevent_req_callback_data(subreq, struct tevent_req);
1171 14 : state = tevent_req_data(req, struct sdap_nested_group_process_state);
1172 :
1173 14 : if (state->deref) {
1174 0 : ret = sdap_nested_group_deref_recv(subreq);
1175 0 : talloc_zfree(subreq);
1176 0 : if (ret == ENOTSUP) {
1177 : /* dereference is not supported, try again without dereference */
1178 0 : state->group_ctx->try_deref = false;
1179 0 : state->deref = false;
1180 :
1181 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Members of group [%s] will be "
1182 : "processed individually\n", state->group_dn);
1183 :
1184 0 : if (state->deref_shortcut == true) {
1185 : /* If we previously short-cut dereference, we need to split the
1186 : * members again to get full list of missing member types
1187 : */
1188 : PROBE(SDAP_NESTED_GROUP_PROCESS_SPLIT_PRE);
1189 0 : ret = sdap_nested_group_split_members(state, state->group_ctx,
1190 : -1,
1191 : state->nesting_level,
1192 : state->members,
1193 : &state->missing,
1194 : &state->num_missing_total,
1195 : &state->num_missing_groups);
1196 : PROBE(SDAP_NESTED_GROUP_PROCESS_SPLIT_POST);
1197 0 : if (ret != EOK) {
1198 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to split member list "
1199 : "[%d]: %s\n",
1200 : ret, sss_strerror(ret));
1201 0 : goto done;
1202 : }
1203 : }
1204 :
1205 0 : subreq = sdap_nested_group_single_send(state,
1206 : state->ev,
1207 : state->group_ctx,
1208 : state->missing,
1209 : state->num_missing_total,
1210 : state->num_missing_groups,
1211 : state->nesting_level);
1212 0 : if (subreq == NULL) {
1213 0 : ret = ENOMEM;
1214 0 : goto done;
1215 : }
1216 :
1217 0 : tevent_req_set_callback(subreq, sdap_nested_group_process_done,
1218 : req);
1219 :
1220 0 : ret = EAGAIN;
1221 : }
1222 : } else {
1223 14 : ret = sdap_nested_group_single_recv(subreq);
1224 14 : talloc_zfree(subreq);
1225 : }
1226 :
1227 : done:
1228 14 : if (ret == EOK) {
1229 11 : tevent_req_done(req);
1230 3 : } else if (ret != EAGAIN) {
1231 3 : tevent_req_error(req, ret);
1232 : }
1233 14 : }
1234 :
1235 18 : static errno_t sdap_nested_group_process_recv(struct tevent_req *req)
1236 : {
1237 : #ifdef HAVE_SYSTEMTAP
1238 : struct sdap_nested_group_process_state *state = NULL;
1239 : state = tevent_req_data(req, struct sdap_nested_group_process_state);
1240 :
1241 : PROBE(SDAP_NESTED_GROUP_PROCESS_RECV, state->group_dn);
1242 : #endif
1243 :
1244 21 : TEVENT_REQ_RETURN_ON_ERROR(req);
1245 :
1246 15 : return EOK;
1247 : }
1248 :
1249 : struct sdap_nested_group_recurse_state {
1250 : struct tevent_context *ev;
1251 : struct sdap_nested_group_ctx *group_ctx;
1252 : struct sysdb_attrs **groups;
1253 : int num_groups;
1254 : int index;
1255 : int nesting_level;
1256 : };
1257 :
1258 : static errno_t sdap_nested_group_recurse_step(struct tevent_req *req);
1259 : static void sdap_nested_group_recurse_done(struct tevent_req *subreq);
1260 :
1261 : static struct tevent_req *
1262 11 : sdap_nested_group_recurse_send(TALLOC_CTX *mem_ctx,
1263 : struct tevent_context *ev,
1264 : struct sdap_nested_group_ctx *group_ctx,
1265 : struct sysdb_attrs **nested_groups,
1266 : int num_groups,
1267 : int nesting_level)
1268 : {
1269 11 : struct sdap_nested_group_recurse_state *state = NULL;
1270 11 : struct tevent_req *req = NULL;
1271 : errno_t ret;
1272 :
1273 11 : req = tevent_req_create(mem_ctx, &state,
1274 : struct sdap_nested_group_recurse_state);
1275 11 : if (req == NULL) {
1276 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
1277 0 : return NULL;
1278 : }
1279 :
1280 11 : state->ev = ev;
1281 11 : state->group_ctx = group_ctx;
1282 11 : state->groups = nested_groups;
1283 11 : state->num_groups = num_groups;
1284 11 : state->index = 0;
1285 11 : state->nesting_level = nesting_level;
1286 :
1287 : /* process each group individually */
1288 11 : ret = sdap_nested_group_recurse_step(req);
1289 11 : if (ret != EAGAIN) {
1290 3 : goto immediately;
1291 : }
1292 :
1293 8 : return req;
1294 :
1295 : immediately:
1296 3 : if (ret == EOK) {
1297 3 : tevent_req_done(req);
1298 : } else {
1299 0 : tevent_req_error(req, ret);
1300 : }
1301 3 : tevent_req_post(req, ev);
1302 :
1303 3 : return req;
1304 : }
1305 :
1306 19 : static errno_t sdap_nested_group_recurse_step(struct tevent_req *req)
1307 : {
1308 19 : struct sdap_nested_group_recurse_state *state = NULL;
1309 19 : struct tevent_req *subreq = NULL;
1310 :
1311 19 : state = tevent_req_data(req, struct sdap_nested_group_recurse_state);
1312 :
1313 19 : if (state->index >= state->num_groups) {
1314 : /* we're done */
1315 9 : return EOK;
1316 : }
1317 :
1318 10 : subreq = sdap_nested_group_process_send(state, state->ev, state->group_ctx,
1319 : state->nesting_level,
1320 10 : state->groups[state->index]);
1321 10 : if (subreq == NULL) {
1322 0 : return ENOMEM;
1323 : }
1324 :
1325 10 : tevent_req_set_callback(subreq, sdap_nested_group_recurse_done, req);
1326 :
1327 10 : state->index++;
1328 :
1329 10 : return EAGAIN;
1330 : }
1331 :
1332 10 : static void sdap_nested_group_recurse_done(struct tevent_req *subreq)
1333 : {
1334 10 : struct tevent_req *req = NULL;
1335 : errno_t ret;
1336 :
1337 10 : req = tevent_req_callback_data(subreq, struct tevent_req);
1338 :
1339 10 : ret = sdap_nested_group_process_recv(subreq);
1340 10 : talloc_zfree(subreq);
1341 10 : if (ret != EOK) {
1342 2 : goto done;
1343 : }
1344 :
1345 8 : ret = sdap_nested_group_recurse_step(req);
1346 :
1347 : done:
1348 10 : if (ret == EOK) {
1349 6 : tevent_req_done(req);
1350 4 : } else if (ret != EAGAIN) {
1351 2 : tevent_req_error(req, ret);
1352 : }
1353 :
1354 10 : return;
1355 : }
1356 :
1357 11 : static errno_t sdap_nested_group_recurse_recv(struct tevent_req *req)
1358 : {
1359 13 : TEVENT_REQ_RETURN_ON_ERROR(req);
1360 :
1361 9 : return EOK;
1362 : }
1363 :
1364 : struct sdap_nested_group_single_state {
1365 : struct tevent_context *ev;
1366 : struct sdap_nested_group_ctx *group_ctx;
1367 : struct sdap_nested_group_member *members;
1368 : int nesting_level;
1369 :
1370 : struct sdap_nested_group_member *current_member;
1371 : int num_members;
1372 : int member_index;
1373 :
1374 : struct sysdb_attrs **nested_groups;
1375 : int num_groups;
1376 : };
1377 :
1378 : static errno_t sdap_nested_group_single_step(struct tevent_req *req);
1379 : static void sdap_nested_group_single_step_done(struct tevent_req *subreq);
1380 : static void sdap_nested_group_single_done(struct tevent_req *subreq);
1381 :
1382 : static struct tevent_req *
1383 14 : sdap_nested_group_single_send(TALLOC_CTX *mem_ctx,
1384 : struct tevent_context *ev,
1385 : struct sdap_nested_group_ctx *group_ctx,
1386 : struct sdap_nested_group_member *members,
1387 : int num_members,
1388 : int num_groups_max,
1389 : int nesting_level)
1390 : {
1391 14 : struct sdap_nested_group_single_state *state = NULL;
1392 14 : struct tevent_req *req = NULL;
1393 : errno_t ret;
1394 :
1395 14 : req = tevent_req_create(mem_ctx, &state,
1396 : struct sdap_nested_group_single_state);
1397 14 : if (req == NULL) {
1398 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
1399 0 : return NULL;
1400 : }
1401 :
1402 14 : state->ev = ev;
1403 14 : state->group_ctx = group_ctx;
1404 14 : state->members = members;
1405 14 : state->nesting_level = nesting_level;
1406 14 : state->current_member = NULL;
1407 14 : state->num_members = num_members;
1408 14 : state->member_index = 0;
1409 14 : state->nested_groups = talloc_zero_array(state, struct sysdb_attrs *,
1410 : num_groups_max);
1411 14 : if (state->nested_groups == NULL) {
1412 0 : ret = ENOMEM;
1413 0 : goto immediately;
1414 : }
1415 14 : state->num_groups = 0; /* we will count exact number of the groups */
1416 :
1417 : /* process each member individually */
1418 14 : ret = sdap_nested_group_single_step(req);
1419 14 : if (ret != EAGAIN) {
1420 2 : goto immediately;
1421 : }
1422 :
1423 12 : return req;
1424 :
1425 : immediately:
1426 2 : if (ret == EOK) {
1427 2 : tevent_req_done(req);
1428 : } else {
1429 0 : tevent_req_error(req, ret);
1430 : }
1431 2 : tevent_req_post(req, ev);
1432 :
1433 2 : return req;
1434 : }
1435 :
1436 32 : static errno_t sdap_nested_group_single_step(struct tevent_req *req)
1437 : {
1438 32 : struct sdap_nested_group_single_state *state = NULL;
1439 32 : struct tevent_req *subreq = NULL;
1440 :
1441 32 : state = tevent_req_data(req, struct sdap_nested_group_single_state);
1442 :
1443 32 : if (state->member_index >= state->num_members) {
1444 : /* we're done */
1445 13 : return EOK;
1446 : }
1447 :
1448 19 : state->current_member = &state->members[state->member_index];
1449 19 : state->member_index++;
1450 :
1451 19 : switch (state->current_member->type) {
1452 : case SDAP_NESTED_GROUP_DN_USER:
1453 8 : subreq = sdap_nested_group_lookup_user_send(state, state->ev,
1454 : state->group_ctx,
1455 : state->current_member);
1456 8 : break;
1457 : case SDAP_NESTED_GROUP_DN_GROUP:
1458 11 : subreq = sdap_nested_group_lookup_group_send(state, state->ev,
1459 : state->group_ctx,
1460 : state->current_member);
1461 11 : break;
1462 : case SDAP_NESTED_GROUP_DN_UNKNOWN:
1463 0 : subreq = sdap_nested_group_lookup_unknown_send(state, state->ev,
1464 : state->group_ctx,
1465 : state->current_member);
1466 0 : break;
1467 : }
1468 :
1469 19 : if (subreq == NULL) {
1470 0 : return ENOMEM;
1471 : }
1472 :
1473 19 : tevent_req_set_callback(subreq, sdap_nested_group_single_step_done, req);
1474 :
1475 19 : return EAGAIN;
1476 : }
1477 :
1478 : static errno_t
1479 19 : sdap_nested_group_single_step_process(struct tevent_req *subreq)
1480 : {
1481 19 : struct sdap_nested_group_single_state *state = NULL;
1482 19 : struct tevent_req *req = NULL;
1483 19 : struct sysdb_attrs *entry = NULL;
1484 19 : enum sdap_nested_group_dn_type type = SDAP_NESTED_GROUP_DN_UNKNOWN;
1485 19 : const char *orig_dn = NULL;
1486 : errno_t ret;
1487 :
1488 19 : req = tevent_req_callback_data(subreq, struct tevent_req);
1489 19 : state = tevent_req_data(req, struct sdap_nested_group_single_state);
1490 :
1491 : /* set correct type if possible */
1492 19 : if (state->current_member->type == SDAP_NESTED_GROUP_DN_UNKNOWN) {
1493 0 : ret = sdap_nested_group_lookup_unknown_recv(state, subreq,
1494 : &entry, &type);
1495 0 : if (ret != EOK) {
1496 0 : goto done;
1497 : }
1498 :
1499 0 : if (entry != NULL) {
1500 0 : state->current_member->type = type;
1501 : }
1502 : }
1503 :
1504 19 : switch (state->current_member->type) {
1505 : case SDAP_NESTED_GROUP_DN_USER:
1506 8 : if (entry == NULL) {
1507 : /* type was not unknown, receive data */
1508 8 : ret = sdap_nested_group_lookup_user_recv(state, subreq, &entry);
1509 8 : if (ret != EOK) {
1510 1 : goto done;
1511 : }
1512 :
1513 7 : if (entry == NULL) {
1514 : /* user not found, continue */
1515 0 : break;
1516 : }
1517 : }
1518 :
1519 : /* save user in hash table */
1520 7 : ret = sdap_nested_group_hash_user(state->group_ctx, entry);
1521 7 : if (ret == EEXIST) {
1522 : /* the user is already present, skip it */
1523 1 : talloc_zfree(entry);
1524 1 : ret = EOK;
1525 1 : goto done;
1526 6 : } else if (ret != EOK) {
1527 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to save user in hash table "
1528 : "[%d]: %s\n", ret, strerror(ret));
1529 0 : goto done;
1530 : }
1531 6 : break;
1532 : case SDAP_NESTED_GROUP_DN_GROUP:
1533 11 : if (entry == NULL) {
1534 : /* type was not unknown, receive data */
1535 11 : ret = sdap_nested_group_lookup_group_recv(state, subreq, &entry);
1536 11 : if (ret != EOK) {
1537 0 : goto done;
1538 : }
1539 :
1540 11 : if (entry == NULL) {
1541 : /* group not found, continue */
1542 0 : break;
1543 : }
1544 : } else {
1545 : /* the type was unknown so we had to pull the group,
1546 : * but we don't want to process it if we have reached
1547 : * the nesting level */
1548 0 : if (state->nesting_level >= state->group_ctx->max_nesting_level) {
1549 0 : ret = sysdb_attrs_get_string(entry, SYSDB_ORIG_DN, &orig_dn);
1550 0 : if (ret != EOK) {
1551 0 : DEBUG(SSSDBG_MINOR_FAILURE,
1552 : "The entry has no originalDN\n");
1553 0 : orig_dn = "invalid";
1554 : }
1555 :
1556 0 : DEBUG(SSSDBG_TRACE_ALL, "[%s] is outside nesting limit "
1557 : "(level %d), skipping\n", orig_dn, state->nesting_level);
1558 0 : break;
1559 : }
1560 : }
1561 :
1562 : /* save group in hash table */
1563 11 : ret = sdap_nested_group_hash_group(state->group_ctx, entry);
1564 11 : if (ret == EEXIST) {
1565 : /* the group is already present, skip it */
1566 1 : talloc_zfree(entry);
1567 1 : ret = EOK;
1568 1 : goto done;
1569 10 : } else if (ret != EOK) {
1570 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to save group in hash table "
1571 : "[%d]: %s\n", ret, strerror(ret));
1572 0 : goto done;
1573 : }
1574 :
1575 : /* remember the group for later processing */
1576 10 : state->nested_groups[state->num_groups] = entry;
1577 10 : state->num_groups++;
1578 :
1579 10 : break;
1580 : case SDAP_NESTED_GROUP_DN_UNKNOWN:
1581 : /* not found in users nor nested_groups, continue */
1582 0 : break;
1583 : }
1584 :
1585 16 : ret = EOK;
1586 :
1587 : done:
1588 19 : return ret;
1589 : }
1590 :
1591 19 : static void sdap_nested_group_single_step_done(struct tevent_req *subreq)
1592 : {
1593 19 : struct sdap_nested_group_single_state *state = NULL;
1594 19 : struct tevent_req *req = NULL;
1595 : errno_t ret;
1596 :
1597 19 : req = tevent_req_callback_data(subreq, struct tevent_req);
1598 19 : state = tevent_req_data(req, struct sdap_nested_group_single_state);
1599 :
1600 : /* process direct members */
1601 19 : ret = sdap_nested_group_single_step_process(subreq);
1602 19 : talloc_zfree(subreq);
1603 19 : if (ret != EOK) {
1604 1 : DEBUG(SSSDBG_CRIT_FAILURE, "Error processing direct membership "
1605 : "[%d]: %s\n", ret, strerror(ret));
1606 1 : goto done;
1607 : }
1608 :
1609 18 : ret = sdap_nested_group_single_step(req);
1610 18 : if (ret == EOK) {
1611 : /* we have processed all direct members,
1612 : * now recurse and process nested groups */
1613 11 : subreq = sdap_nested_group_recurse_send(state, state->ev,
1614 : state->group_ctx,
1615 : state->nested_groups,
1616 : state->num_groups,
1617 11 : state->nesting_level + 1);
1618 11 : if (subreq == NULL) {
1619 0 : ret = ENOMEM;
1620 0 : goto done;
1621 : }
1622 :
1623 11 : tevent_req_set_callback(subreq, sdap_nested_group_single_done, req);
1624 7 : } else if (ret != EAGAIN) {
1625 : /* error */
1626 0 : goto done;
1627 : }
1628 :
1629 : /* we're not done yet */
1630 18 : ret = EAGAIN;
1631 :
1632 : done:
1633 19 : if (ret == EOK) {
1634 : /* tevent_req_error() cannot cope with EOK */
1635 0 : DEBUG(SSSDBG_CRIT_FAILURE, "We should not get here with EOK\n");
1636 0 : tevent_req_error(req, EINVAL);
1637 19 : } else if (ret != EAGAIN) {
1638 1 : tevent_req_error(req, ret);
1639 : }
1640 :
1641 19 : return;
1642 : }
1643 :
1644 11 : static void sdap_nested_group_single_done(struct tevent_req *subreq)
1645 : {
1646 11 : struct tevent_req *req = NULL;
1647 : errno_t ret;
1648 :
1649 11 : req = tevent_req_callback_data(subreq, struct tevent_req);
1650 :
1651 : /* all nested groups are completed */
1652 11 : ret = sdap_nested_group_recurse_recv(subreq);
1653 11 : talloc_zfree(subreq);
1654 11 : if (ret != EOK) {
1655 2 : DEBUG(SSSDBG_CRIT_FAILURE, "Error processing nested groups "
1656 : "[%d]: %s.\n", ret, strerror(ret));
1657 2 : tevent_req_error(req, ret);
1658 2 : return;
1659 : }
1660 :
1661 9 : tevent_req_done(req);
1662 :
1663 9 : return;
1664 : }
1665 :
1666 14 : static errno_t sdap_nested_group_single_recv(struct tevent_req *req)
1667 : {
1668 17 : TEVENT_REQ_RETURN_ON_ERROR(req);
1669 :
1670 11 : return EOK;
1671 : }
1672 :
1673 0 : static errno_t sdap_nested_group_get_ipa_user(TALLOC_CTX *mem_ctx,
1674 : const char *user_dn,
1675 : struct sysdb_ctx *sysdb,
1676 : struct sysdb_attrs **_user)
1677 : {
1678 : TALLOC_CTX *tmp_ctx;
1679 : struct sysdb_attrs *user;
1680 : char *name;
1681 : errno_t ret;
1682 :
1683 0 : tmp_ctx = talloc_new(NULL);
1684 0 : if (tmp_ctx == NULL) {
1685 0 : return ENOMEM;
1686 : }
1687 :
1688 0 : ret = ipa_get_rdn(tmp_ctx, sysdb, user_dn, &name, "uid",
1689 : "cn", "users", "cn", "accounts");
1690 0 : if (ret != EOK) {
1691 0 : goto done;
1692 : }
1693 :
1694 0 : user = sysdb_new_attrs(tmp_ctx);
1695 0 : if (user == NULL) {
1696 0 : ret = ENOMEM;
1697 0 : goto done;
1698 : }
1699 :
1700 0 : ret = sysdb_attrs_add_string(user, SYSDB_NAME, name);
1701 0 : if (ret != EOK) {
1702 0 : goto done;
1703 : }
1704 :
1705 0 : ret = sysdb_attrs_add_string(user, SYSDB_ORIG_DN, user_dn);
1706 0 : if (ret != EOK) {
1707 0 : goto done;
1708 : }
1709 :
1710 0 : ret = sysdb_attrs_add_string(user, SYSDB_OBJECTCLASS, SYSDB_USER_CLASS);
1711 0 : if (ret != EOK) {
1712 0 : goto done;
1713 : }
1714 :
1715 0 : *_user = talloc_steal(mem_ctx, user);
1716 :
1717 : done:
1718 0 : talloc_free(tmp_ctx);
1719 0 : return ret;
1720 : }
1721 :
1722 : struct sdap_nested_group_lookup_user_state {
1723 : struct sysdb_attrs *user;
1724 : };
1725 :
1726 : static void sdap_nested_group_lookup_user_done(struct tevent_req *subreq);
1727 :
1728 : static struct tevent_req *
1729 8 : sdap_nested_group_lookup_user_send(TALLOC_CTX *mem_ctx,
1730 : struct tevent_context *ev,
1731 : struct sdap_nested_group_ctx *group_ctx,
1732 : struct sdap_nested_group_member *member)
1733 : {
1734 8 : struct sdap_nested_group_lookup_user_state *state = NULL;
1735 8 : struct tevent_req *req = NULL;
1736 8 : struct tevent_req *subreq = NULL;
1737 8 : const char **attrs = NULL;
1738 8 : const char *base_filter = NULL;
1739 8 : const char *filter = NULL;
1740 : errno_t ret;
1741 :
1742 8 : req = tevent_req_create(mem_ctx, &state,
1743 : struct sdap_nested_group_lookup_user_state);
1744 8 : if (req == NULL) {
1745 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
1746 0 : return NULL;
1747 : }
1748 :
1749 : PROBE(SDAP_NESTED_GROUP_LOOKUP_USER_SEND);
1750 :
1751 8 : if (group_ctx->opts->schema_type == SDAP_SCHEMA_IPA_V1) {
1752 : /* if the schema is IPA, then just shortcut and guess the name */
1753 0 : ret = sdap_nested_group_get_ipa_user(state, member->dn,
1754 0 : group_ctx->domain->sysdb,
1755 0 : &state->user);
1756 0 : if (ret == EOK) {
1757 0 : goto immediately;
1758 : }
1759 :
1760 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Couldn't parse out user information "
1761 : "based on DN %s, falling back to an LDAP lookup\n", member->dn);
1762 : }
1763 :
1764 : /* only pull down username and originalDN */
1765 8 : attrs = talloc_array(state, const char *, 3);
1766 8 : if (attrs == NULL) {
1767 0 : ret = ENOMEM;
1768 0 : goto immediately;
1769 : }
1770 :
1771 8 : attrs[0] = "objectClass";
1772 8 : attrs[1] = group_ctx->opts->user_map[SDAP_AT_USER_NAME].name;
1773 8 : attrs[2] = NULL;
1774 :
1775 : /* create filter */
1776 8 : base_filter = talloc_asprintf(state, "(objectclass=%s)",
1777 8 : group_ctx->opts->user_map[SDAP_OC_USER].name);
1778 8 : if (base_filter == NULL) {
1779 0 : ret = ENOMEM;
1780 0 : goto immediately;
1781 : }
1782 :
1783 : /* use search base filter if needed */
1784 8 : filter = sdap_combine_filters(state, base_filter, member->user_filter);
1785 8 : if (filter == NULL) {
1786 0 : ret = ENOMEM;
1787 0 : goto immediately;
1788 : }
1789 :
1790 : /* search */
1791 24 : subreq = sdap_get_generic_send(state, ev, group_ctx->opts, group_ctx->sh,
1792 : member->dn, LDAP_SCOPE_BASE, filter, attrs,
1793 8 : group_ctx->opts->user_map,
1794 8 : group_ctx->opts->user_map_cnt,
1795 8 : dp_opt_get_int(group_ctx->opts->basic,
1796 : SDAP_SEARCH_TIMEOUT),
1797 : false);
1798 8 : if (subreq == NULL) {
1799 0 : ret = ENOMEM;
1800 0 : goto immediately;
1801 : }
1802 :
1803 8 : tevent_req_set_callback(subreq, sdap_nested_group_lookup_user_done, req);
1804 :
1805 8 : return req;
1806 :
1807 : immediately:
1808 0 : if (ret == EOK) {
1809 0 : tevent_req_done(req);
1810 : } else {
1811 0 : tevent_req_error(req, ret);
1812 : }
1813 0 : tevent_req_post(req, ev);
1814 :
1815 0 : return req;
1816 : }
1817 :
1818 8 : static void sdap_nested_group_lookup_user_done(struct tevent_req *subreq)
1819 : {
1820 8 : struct sdap_nested_group_lookup_user_state *state = NULL;
1821 8 : struct tevent_req *req = NULL;
1822 8 : struct sysdb_attrs **user = NULL;
1823 8 : size_t count = 0;
1824 : errno_t ret;
1825 :
1826 8 : req = tevent_req_callback_data(subreq, struct tevent_req);
1827 8 : state = tevent_req_data(req, struct sdap_nested_group_lookup_user_state);
1828 :
1829 8 : ret = sdap_get_generic_recv(subreq, state, &count, &user);
1830 8 : talloc_zfree(subreq);
1831 8 : if (ret == ENOENT) {
1832 0 : count = 0;
1833 8 : } else if (ret != EOK) {
1834 1 : goto done;
1835 : }
1836 :
1837 7 : if (count == 1) {
1838 7 : state->user = user[0];
1839 0 : } else if (count == 0) {
1840 : /* group not found */
1841 0 : state->user = NULL;
1842 : } else {
1843 0 : DEBUG(SSSDBG_OP_FAILURE,
1844 : "BASE search returned more than one records\n");
1845 0 : ret = EIO;
1846 0 : goto done;
1847 : }
1848 :
1849 7 : ret = EOK;
1850 :
1851 : done:
1852 8 : if (ret != EOK) {
1853 1 : tevent_req_error(req, ret);
1854 9 : return;
1855 : }
1856 :
1857 7 : tevent_req_done(req);
1858 : }
1859 :
1860 8 : static errno_t sdap_nested_group_lookup_user_recv(TALLOC_CTX *mem_ctx,
1861 : struct tevent_req *req,
1862 : struct sysdb_attrs **_user)
1863 : {
1864 8 : struct sdap_nested_group_lookup_user_state *state = NULL;
1865 8 : state = tevent_req_data(req, struct sdap_nested_group_lookup_user_state);
1866 :
1867 : PROBE(SDAP_NESTED_GROUP_LOOKUP_USER_RECV);
1868 :
1869 9 : TEVENT_REQ_RETURN_ON_ERROR(req);
1870 :
1871 7 : if (_user != NULL) {
1872 7 : *_user = talloc_steal(mem_ctx, state->user);
1873 : }
1874 :
1875 7 : return EOK;
1876 : }
1877 :
1878 : struct sdap_nested_group_lookup_group_state {
1879 : struct sysdb_attrs *group;
1880 : };
1881 :
1882 : static void sdap_nested_group_lookup_group_done(struct tevent_req *subreq);
1883 :
1884 : static struct tevent_req *
1885 11 : sdap_nested_group_lookup_group_send(TALLOC_CTX *mem_ctx,
1886 : struct tevent_context *ev,
1887 : struct sdap_nested_group_ctx *group_ctx,
1888 : struct sdap_nested_group_member *member)
1889 : {
1890 11 : struct sdap_nested_group_lookup_group_state *state = NULL;
1891 11 : struct tevent_req *req = NULL;
1892 11 : struct tevent_req *subreq = NULL;
1893 11 : struct sdap_attr_map *map = group_ctx->opts->group_map;
1894 11 : const char **attrs = NULL;
1895 11 : const char *base_filter = NULL;
1896 11 : const char *filter = NULL;
1897 : char *oc_list;
1898 : errno_t ret;
1899 :
1900 : PROBE(SDAP_NESTED_GROUP_LOOKUP_GROUP_SEND);
1901 :
1902 11 : req = tevent_req_create(mem_ctx, &state,
1903 : struct sdap_nested_group_lookup_group_state);
1904 11 : if (req == NULL) {
1905 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
1906 0 : return NULL;
1907 : }
1908 :
1909 11 : ret = build_attrs_from_map(state, group_ctx->opts->group_map,
1910 : SDAP_OPTS_GROUP, NULL, &attrs, NULL);
1911 11 : if (ret != EOK) {
1912 0 : goto immediately;
1913 : }
1914 :
1915 : /* create filter */
1916 11 : oc_list = sdap_make_oc_list(state, map);
1917 11 : if (oc_list == NULL) {
1918 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create objectClass list.\n");
1919 0 : ret = ENOMEM;
1920 0 : goto immediately;
1921 : }
1922 :
1923 11 : base_filter = talloc_asprintf(attrs, "(&(%s)(%s=*))", oc_list,
1924 11 : map[SDAP_AT_GROUP_NAME].name);
1925 11 : if (base_filter == NULL) {
1926 0 : ret = ENOMEM;
1927 0 : goto immediately;
1928 : }
1929 :
1930 : /* use search base filter if needed */
1931 11 : filter = sdap_combine_filters(state, base_filter, member->group_filter);
1932 11 : if (filter == NULL) {
1933 0 : ret = ENOMEM;
1934 0 : goto immediately;
1935 : }
1936 :
1937 : /* search */
1938 11 : subreq = sdap_get_generic_send(state, ev, group_ctx->opts, group_ctx->sh,
1939 : member->dn, LDAP_SCOPE_BASE, filter, attrs,
1940 : map, SDAP_OPTS_GROUP,
1941 11 : dp_opt_get_int(group_ctx->opts->basic,
1942 : SDAP_SEARCH_TIMEOUT),
1943 : false);
1944 11 : if (subreq == NULL) {
1945 0 : ret = ENOMEM;
1946 0 : goto immediately;
1947 : }
1948 :
1949 11 : tevent_req_set_callback(subreq, sdap_nested_group_lookup_group_done, req);
1950 :
1951 11 : return req;
1952 :
1953 : immediately:
1954 0 : if (ret == EOK) {
1955 0 : tevent_req_done(req);
1956 : } else {
1957 0 : tevent_req_error(req, ret);
1958 : }
1959 0 : tevent_req_post(req, ev);
1960 :
1961 0 : return req;
1962 : }
1963 :
1964 11 : static void sdap_nested_group_lookup_group_done(struct tevent_req *subreq)
1965 : {
1966 11 : struct sdap_nested_group_lookup_group_state *state = NULL;
1967 11 : struct tevent_req *req = NULL;
1968 11 : struct sysdb_attrs **group = NULL;
1969 11 : size_t count = 0;
1970 : errno_t ret;
1971 :
1972 11 : req = tevent_req_callback_data(subreq, struct tevent_req);
1973 11 : state = tevent_req_data(req, struct sdap_nested_group_lookup_group_state);
1974 :
1975 11 : ret = sdap_get_generic_recv(subreq, state, &count, &group);
1976 11 : talloc_zfree(subreq);
1977 11 : if (ret == ENOENT) {
1978 0 : count = 0;
1979 11 : } else if (ret != EOK) {
1980 0 : goto done;
1981 : }
1982 :
1983 11 : if (count == 1) {
1984 11 : state->group = group[0];
1985 0 : } else if (count == 0) {
1986 : /* group not found */
1987 0 : state->group = NULL;
1988 : } else {
1989 0 : DEBUG(SSSDBG_OP_FAILURE,
1990 : "BASE search returned more than one records\n");
1991 0 : ret = EIO;
1992 0 : goto done;
1993 : }
1994 :
1995 11 : ret = EOK;
1996 :
1997 : done:
1998 11 : if (ret != EOK) {
1999 0 : tevent_req_error(req, ret);
2000 11 : return;
2001 : }
2002 :
2003 11 : tevent_req_done(req);
2004 : }
2005 :
2006 11 : static errno_t sdap_nested_group_lookup_group_recv(TALLOC_CTX *mem_ctx,
2007 : struct tevent_req *req,
2008 : struct sysdb_attrs **_group)
2009 : {
2010 11 : struct sdap_nested_group_lookup_group_state *state = NULL;
2011 11 : state = tevent_req_data(req, struct sdap_nested_group_lookup_group_state);
2012 :
2013 : PROBE(SDAP_NESTED_GROUP_LOOKUP_GROUP_RECV);
2014 :
2015 11 : TEVENT_REQ_RETURN_ON_ERROR(req);
2016 :
2017 11 : if (_group != NULL) {
2018 11 : *_group = talloc_steal(mem_ctx, state->group);
2019 : }
2020 :
2021 11 : return EOK;
2022 : }
2023 :
2024 : struct sdap_nested_group_lookup_unknown_state {
2025 : struct tevent_context *ev;
2026 : struct sdap_nested_group_ctx *group_ctx;
2027 : struct sdap_nested_group_member *member;
2028 : enum sdap_nested_group_dn_type type;
2029 : struct sysdb_attrs *entry;
2030 : };
2031 :
2032 : static void
2033 : sdap_nested_group_lookup_unknown_user_done(struct tevent_req *subreq);
2034 :
2035 : static void
2036 : sdap_nested_group_lookup_unknown_group_done(struct tevent_req *subreq);
2037 :
2038 : static struct tevent_req *
2039 0 : sdap_nested_group_lookup_unknown_send(TALLOC_CTX *mem_ctx,
2040 : struct tevent_context *ev,
2041 : struct sdap_nested_group_ctx *group_ctx,
2042 : struct sdap_nested_group_member *member)
2043 : {
2044 0 : struct sdap_nested_group_lookup_unknown_state *state = NULL;
2045 0 : struct tevent_req *req = NULL;
2046 0 : struct tevent_req *subreq = NULL;
2047 : errno_t ret;
2048 :
2049 0 : req = tevent_req_create(mem_ctx, &state,
2050 : struct sdap_nested_group_lookup_unknown_state);
2051 0 : if (req == NULL) {
2052 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
2053 0 : return NULL;
2054 : }
2055 :
2056 : PROBE(SDAP_NESTED_GROUP_LOOKUP_UNKNOWN_SEND);
2057 :
2058 0 : state->ev = ev;
2059 0 : state->group_ctx = group_ctx;
2060 0 : state->member = member;
2061 :
2062 : /* try users first */
2063 0 : subreq = sdap_nested_group_lookup_user_send(state,
2064 0 : state->ev,
2065 0 : state->group_ctx,
2066 0 : state->member);
2067 0 : if (subreq == NULL) {
2068 0 : ret = ENOMEM;
2069 0 : goto immediately;
2070 : }
2071 :
2072 0 : tevent_req_set_callback(subreq, sdap_nested_group_lookup_unknown_user_done,
2073 : req);
2074 :
2075 0 : return req;
2076 :
2077 : immediately:
2078 0 : if (ret == EOK) {
2079 0 : tevent_req_done(req);
2080 : } else {
2081 0 : tevent_req_error(req, ret);
2082 : }
2083 0 : tevent_req_post(req, ev);
2084 :
2085 0 : return req;
2086 : }
2087 :
2088 : static void
2089 0 : sdap_nested_group_lookup_unknown_user_done(struct tevent_req *subreq)
2090 : {
2091 0 : struct sdap_nested_group_lookup_unknown_state *state = NULL;
2092 0 : struct tevent_req *req = NULL;
2093 0 : struct sysdb_attrs *entry = NULL;
2094 : errno_t ret;
2095 :
2096 0 : req = tevent_req_callback_data(subreq, struct tevent_req);
2097 0 : state = tevent_req_data(req, struct sdap_nested_group_lookup_unknown_state);
2098 :
2099 0 : ret = sdap_nested_group_lookup_user_recv(state, subreq, &entry);
2100 0 : talloc_zfree(subreq);
2101 0 : if (ret != EOK) {
2102 0 : goto done;
2103 : }
2104 :
2105 0 : if (entry != NULL) {
2106 : /* found in users */
2107 0 : state->entry = entry;
2108 0 : state->type = SDAP_NESTED_GROUP_DN_USER;
2109 0 : ret = EOK;
2110 0 : goto done;
2111 : }
2112 :
2113 : /* not found in users, try group */
2114 0 : subreq = sdap_nested_group_lookup_group_send(state,
2115 : state->ev,
2116 : state->group_ctx,
2117 : state->member);
2118 0 : if (subreq == NULL) {
2119 0 : ret = ENOMEM;
2120 0 : goto done;
2121 : }
2122 :
2123 0 : tevent_req_set_callback(subreq, sdap_nested_group_lookup_unknown_group_done,
2124 : req);
2125 :
2126 0 : ret = EAGAIN;
2127 :
2128 : done:
2129 0 : if (ret == EOK) {
2130 0 : tevent_req_done(req);
2131 0 : } else if (ret != EAGAIN) {
2132 0 : tevent_req_error(req, ret);
2133 : }
2134 :
2135 0 : return;
2136 : }
2137 :
2138 : static void
2139 0 : sdap_nested_group_lookup_unknown_group_done(struct tevent_req *subreq)
2140 : {
2141 0 : struct sdap_nested_group_lookup_unknown_state *state = NULL;
2142 0 : struct tevent_req *req = NULL;
2143 0 : struct sysdb_attrs *entry = NULL;
2144 : errno_t ret;
2145 :
2146 0 : req = tevent_req_callback_data(subreq, struct tevent_req);
2147 0 : state = tevent_req_data(req, struct sdap_nested_group_lookup_unknown_state);
2148 :
2149 0 : ret = sdap_nested_group_lookup_group_recv(state, subreq, &entry);
2150 0 : talloc_zfree(subreq);
2151 0 : if (ret != EOK) {
2152 0 : goto done;
2153 : }
2154 :
2155 0 : if (entry == NULL) {
2156 : /* not found, end request */
2157 0 : state->entry = NULL;
2158 0 : state->type = SDAP_NESTED_GROUP_DN_UNKNOWN;
2159 : } else {
2160 : /* found in groups */
2161 0 : state->entry = entry;
2162 0 : state->type = SDAP_NESTED_GROUP_DN_GROUP;
2163 : }
2164 :
2165 0 : ret = EOK;
2166 :
2167 : done:
2168 0 : if (ret != EOK) {
2169 0 : tevent_req_error(req, ret);
2170 0 : return;
2171 : }
2172 :
2173 0 : tevent_req_done(req);
2174 : }
2175 :
2176 : static errno_t
2177 0 : sdap_nested_group_lookup_unknown_recv(TALLOC_CTX *mem_ctx,
2178 : struct tevent_req *req,
2179 : struct sysdb_attrs **_entry,
2180 : enum sdap_nested_group_dn_type *_type)
2181 : {
2182 0 : struct sdap_nested_group_lookup_unknown_state *state = NULL;
2183 0 : state = tevent_req_data(req, struct sdap_nested_group_lookup_unknown_state);
2184 :
2185 : PROBE(SDAP_NESTED_GROUP_LOOKUP_UNKNOWN_RECV);
2186 :
2187 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
2188 :
2189 0 : if (_entry != NULL) {
2190 0 : *_entry = talloc_steal(mem_ctx, state->entry);
2191 : }
2192 :
2193 0 : if (_type != NULL) {
2194 0 : *_type = state->type;
2195 : }
2196 :
2197 :
2198 0 : return EOK;
2199 : }
2200 :
2201 : struct sdap_nested_group_deref_state {
2202 : struct tevent_context *ev;
2203 : struct sdap_nested_group_ctx *group_ctx;
2204 : struct ldb_message_element *members;
2205 : int nesting_level;
2206 :
2207 : struct sysdb_attrs **nested_groups;
2208 : int num_groups;
2209 : };
2210 :
2211 : static void sdap_nested_group_deref_direct_done(struct tevent_req *subreq);
2212 : static void sdap_nested_group_deref_done(struct tevent_req *subreq);
2213 :
2214 : static struct tevent_req *
2215 0 : sdap_nested_group_deref_send(TALLOC_CTX *mem_ctx,
2216 : struct tevent_context *ev,
2217 : struct sdap_nested_group_ctx *group_ctx,
2218 : struct ldb_message_element *members,
2219 : const char *group_dn,
2220 : int nesting_level)
2221 : {
2222 0 : struct sdap_nested_group_deref_state *state = NULL;
2223 0 : struct tevent_req *req = NULL;
2224 0 : struct tevent_req *subreq = NULL;
2225 0 : struct sdap_attr_map_info *maps = NULL;
2226 : static const int num_maps = 2;
2227 0 : struct sdap_options *opts = group_ctx->opts;
2228 0 : const char **attrs = NULL;
2229 0 : size_t num_attrs = 0;
2230 : errno_t ret;
2231 :
2232 0 : req = tevent_req_create(mem_ctx, &state,
2233 : struct sdap_nested_group_deref_state);
2234 0 : if (req == NULL) {
2235 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
2236 0 : return NULL;
2237 : }
2238 :
2239 : PROBE(SDAP_NESTED_GROUP_DEREF_SEND);
2240 :
2241 0 : state->ev = ev;
2242 0 : state->group_ctx = group_ctx;
2243 0 : state->members = members;
2244 0 : state->nesting_level = nesting_level;
2245 0 : state->num_groups = 0; /* we will count exact number of the groups */
2246 :
2247 0 : maps = talloc_array(state, struct sdap_attr_map_info, num_maps);
2248 0 : if (maps == NULL) {
2249 0 : ret = ENOMEM;
2250 0 : goto immediately;
2251 : }
2252 :
2253 0 : maps[0].map = opts->user_map;
2254 0 : maps[0].num_attrs = opts->user_map_cnt;
2255 0 : maps[1].map = opts->group_map;
2256 0 : maps[1].num_attrs = SDAP_OPTS_GROUP;
2257 :
2258 : /* pull down the whole group map,
2259 : * but only pull down username and originalDN for users */
2260 0 : ret = build_attrs_from_map(state, opts->group_map, SDAP_OPTS_GROUP,
2261 : NULL, &attrs, &num_attrs);
2262 0 : if (ret != EOK) {
2263 0 : goto immediately;
2264 : }
2265 :
2266 0 : attrs = talloc_realloc(state, attrs, const char *, num_attrs + 2);
2267 0 : if (attrs == NULL) {
2268 0 : ret = ENOMEM;
2269 0 : goto immediately;
2270 : }
2271 :
2272 0 : attrs[num_attrs] = group_ctx->opts->user_map[SDAP_AT_USER_NAME].name;
2273 0 : attrs[num_attrs + 1] = NULL;
2274 :
2275 : /* send request */
2276 0 : subreq = sdap_deref_search_send(state, ev, opts, group_ctx->sh, group_dn,
2277 0 : opts->group_map[SDAP_AT_GROUP_MEMBER].name,
2278 : attrs, num_maps, maps,
2279 : dp_opt_get_int(opts->basic,
2280 : SDAP_SEARCH_TIMEOUT));
2281 0 : if (subreq == NULL) {
2282 0 : ret = ENOMEM;
2283 0 : goto immediately;
2284 : }
2285 :
2286 0 : tevent_req_set_callback(subreq, sdap_nested_group_deref_direct_done, req);
2287 :
2288 0 : return req;
2289 :
2290 : immediately:
2291 0 : if (ret == EOK) {
2292 0 : tevent_req_done(req);
2293 : } else {
2294 0 : tevent_req_error(req, ret);
2295 : }
2296 0 : tevent_req_post(req, ev);
2297 :
2298 0 : return req;
2299 : }
2300 :
2301 : static errno_t
2302 0 : sdap_nested_group_deref_direct_process(struct tevent_req *subreq)
2303 : {
2304 0 : struct sdap_nested_group_deref_state *state = NULL;
2305 0 : struct tevent_req *req = NULL;
2306 0 : struct sdap_options *opts = NULL;
2307 0 : struct sdap_deref_attrs **entries = NULL;
2308 0 : struct ldb_message_element *members = NULL;
2309 0 : const char *orig_dn = NULL;
2310 0 : const char *member_dn = NULL;
2311 0 : size_t num_entries = 0;
2312 : size_t i, j;
2313 : bool member_found;
2314 : errno_t ret;
2315 :
2316 0 : req = tevent_req_callback_data(subreq, struct tevent_req);
2317 0 : state = tevent_req_data(req, struct sdap_nested_group_deref_state);
2318 :
2319 0 : opts = state->group_ctx->opts;
2320 0 : members = state->members;
2321 :
2322 0 : ret = sdap_deref_search_recv(subreq, state, &num_entries, &entries);
2323 0 : if (ret != EOK) {
2324 0 : goto done;
2325 : }
2326 :
2327 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Received %zu dereference results, "
2328 : "about to process them\n", num_entries);
2329 :
2330 : /*
2331 : * We don't have any knowledge about possible number of groups when
2332 : * dereferencing. We expect that every member is a group and we will
2333 : * allocate enough space to hold it. We will shrink the memory later.
2334 : */
2335 0 : state->nested_groups = talloc_zero_array(state, struct sysdb_attrs *,
2336 : num_entries);
2337 0 : if (state->nested_groups == NULL) {
2338 0 : ret = ENOMEM;
2339 0 : goto done;
2340 : }
2341 :
2342 : PROBE(SDAP_NESTED_GROUP_DEREF_PROCESS_PRE);
2343 0 : for (i = 0; i < num_entries; i++) {
2344 0 : ret = sysdb_attrs_get_string(entries[i]->attrs,
2345 : SYSDB_ORIG_DN, &orig_dn);
2346 0 : if (ret != EOK) {
2347 0 : DEBUG(SSSDBG_CRIT_FAILURE, "The entry has no originalDN\n");
2348 0 : goto done;
2349 : }
2350 :
2351 : /* Ensure that all members returned from the deref request are included
2352 : * in the member processing. Sometimes we will get more results back
2353 : * from deref/asq than we got from the initial lookup, as is the case
2354 : * with Active Directory and its range retrieval mechanism.
2355 : */
2356 0 : member_found = false;
2357 0 : for (j = 0; j < members->num_values; j++) {
2358 : /* FIXME: This is inefficient for very large sets of groups */
2359 0 : member_dn = (const char *)members->values[j].data;
2360 0 : if (strcasecmp(orig_dn, member_dn) == 0) {
2361 0 : member_found = true;
2362 0 : break;
2363 : }
2364 : }
2365 :
2366 0 : if (!member_found) {
2367 : /* Append newly found member to member list.
2368 : * Changes in state->members will propagate into sysdb_attrs of
2369 : * the group. */
2370 0 : state->members->values = talloc_realloc(members, members->values,
2371 : struct ldb_val,
2372 : members->num_values + 1);
2373 0 : if (members->values == NULL) {
2374 0 : ret = ENOMEM;
2375 0 : goto done;
2376 : }
2377 :
2378 0 : members->values[members->num_values].data =
2379 0 : (uint8_t *)talloc_strdup(members->values, orig_dn);
2380 0 : if (members->values[members->num_values].data == NULL) {
2381 0 : ret = ENOMEM;
2382 0 : goto done;
2383 : }
2384 :
2385 0 : members->values[members->num_values].length = strlen(orig_dn);
2386 0 : members->num_values++;
2387 : }
2388 :
2389 0 : if (entries[i]->map == opts->user_map) {
2390 : /* we found a user */
2391 :
2392 : /* skip the user if it is not amongst configured search bases */
2393 0 : if (!sdap_nested_member_is_user(state->group_ctx, orig_dn, NULL)) {
2394 0 : continue;
2395 : }
2396 :
2397 : /* save user in hash table */
2398 0 : ret = sdap_nested_group_hash_user(state->group_ctx,
2399 0 : entries[i]->attrs);
2400 0 : if (ret != EOK && ret != EEXIST) {
2401 0 : DEBUG(SSSDBG_CRIT_FAILURE,
2402 : "Unable to save user in hash table "
2403 : "[%d]: %s\n", ret, strerror(ret));
2404 0 : goto done;
2405 : }
2406 :
2407 0 : } else if (entries[i]->map == opts->group_map) {
2408 : /* we found a group */
2409 :
2410 : /* skip the group if we have reached the nesting limit */
2411 0 : if (state->nesting_level >= state->group_ctx->max_nesting_level) {
2412 0 : DEBUG(SSSDBG_TRACE_ALL, "[%s] is outside nesting limit "
2413 : "(level %d), skipping\n", orig_dn, state->nesting_level);
2414 0 : continue;
2415 : }
2416 :
2417 : /* skip the group if it is not amongst configured search bases */
2418 0 : if (!sdap_nested_member_is_group(state->group_ctx, orig_dn, NULL)) {
2419 0 : continue;
2420 : }
2421 :
2422 : /* save group in hash table */
2423 0 : ret = sdap_nested_group_hash_group(state->group_ctx,
2424 0 : entries[i]->attrs);
2425 0 : if (ret == EEXIST) {
2426 0 : continue;
2427 0 : } else if (ret != EOK) {
2428 0 : DEBUG(SSSDBG_CRIT_FAILURE,
2429 : "Unable to save group in hash table "
2430 : "[%d]: %s\n", ret, strerror(ret));
2431 0 : goto done;
2432 : }
2433 :
2434 : /* remember the group for later processing */
2435 0 : state->nested_groups[state->num_groups] = entries[i]->attrs;
2436 0 : state->num_groups++;
2437 :
2438 : } else {
2439 : /* this should never happen, but if it does, do not loop forever */
2440 0 : DEBUG(SSSDBG_MINOR_FAILURE,
2441 : "Entry does not match any known map, skipping\n");
2442 0 : continue;
2443 : }
2444 : }
2445 : PROBE(SDAP_NESTED_GROUP_DEREF_PROCESS_POST);
2446 :
2447 : /* adjust size of nested groups array */
2448 0 : if (state->num_groups > 0) {
2449 0 : state->nested_groups = talloc_realloc(state, state->nested_groups,
2450 : struct sysdb_attrs *,
2451 : state->num_groups);
2452 0 : if (state->nested_groups == NULL) {
2453 0 : ret = ENOMEM;
2454 0 : goto done;
2455 : }
2456 : } else {
2457 0 : talloc_zfree(state->nested_groups);
2458 : }
2459 :
2460 0 : ret = EOK;
2461 :
2462 : done:
2463 0 : return ret;
2464 : }
2465 :
2466 0 : static void sdap_nested_group_deref_direct_done(struct tevent_req *subreq)
2467 : {
2468 0 : struct sdap_nested_group_deref_state *state = NULL;
2469 0 : struct tevent_req *req = NULL;
2470 : errno_t ret;
2471 :
2472 0 : req = tevent_req_callback_data(subreq, struct tevent_req);
2473 0 : state = tevent_req_data(req, struct sdap_nested_group_deref_state);
2474 :
2475 : /* process direct members */
2476 0 : ret = sdap_nested_group_deref_direct_process(subreq);
2477 0 : talloc_zfree(subreq);
2478 0 : if (ret != EOK) {
2479 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Error processing direct membership "
2480 : "[%d]: %s\n", ret, strerror(ret));
2481 0 : goto done;
2482 : }
2483 :
2484 : /* we have processed all direct members,
2485 : * now recurse and process nested groups */
2486 0 : subreq = sdap_nested_group_recurse_send(state, state->ev,
2487 : state->group_ctx,
2488 : state->nested_groups,
2489 : state->num_groups,
2490 0 : state->nesting_level + 1);
2491 0 : if (subreq == NULL) {
2492 0 : ret = ENOMEM;
2493 0 : goto done;
2494 : }
2495 :
2496 0 : tevent_req_set_callback(subreq, sdap_nested_group_deref_done, req);
2497 :
2498 0 : ret = EAGAIN;
2499 :
2500 : done:
2501 0 : if (ret == EOK) {
2502 : /* tevent_req_error() cannot cope with EOK */
2503 0 : DEBUG(SSSDBG_CRIT_FAILURE, "We should not get here with EOK\n");
2504 0 : tevent_req_error(req, EINVAL);
2505 0 : } else if (ret != EAGAIN) {
2506 0 : tevent_req_error(req, ret);
2507 : }
2508 :
2509 0 : return;
2510 :
2511 : }
2512 :
2513 0 : static void sdap_nested_group_deref_done(struct tevent_req *subreq)
2514 : {
2515 0 : struct tevent_req *req = NULL;
2516 : errno_t ret;
2517 :
2518 0 : req = tevent_req_callback_data(subreq, struct tevent_req);
2519 :
2520 : /* process nested groups */
2521 0 : ret = sdap_nested_group_recurse_recv(subreq);
2522 0 : talloc_zfree(subreq);
2523 :
2524 0 : if (ret == EOK) {
2525 0 : tevent_req_done(req);
2526 : } else {
2527 0 : tevent_req_error(req, ret);
2528 : }
2529 :
2530 0 : return;
2531 : }
2532 :
2533 0 : static errno_t sdap_nested_group_deref_recv(struct tevent_req *req)
2534 : {
2535 : PROBE(SDAP_NESTED_GROUP_DEREF_RECV);
2536 :
2537 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
2538 :
2539 0 : return EOK;
2540 : }
2541 :
2542 : struct sdap_ext_member {
2543 : struct sdap_external_missing_member *missing_mem;
2544 : const char *ext_member_attr;
2545 :
2546 : enum sysdb_member_type member_type;
2547 : struct sss_domain_info *dom;
2548 : struct sysdb_attrs *attrs;
2549 : };
2550 :
2551 : struct sdap_nested_group_lookup_external_state {
2552 : struct tevent_context *ev;
2553 : struct sdap_ext_member_ctx *ext_ctx;
2554 : struct sss_domain_info *group_dom;
2555 : hash_table_t *missing_external;
2556 :
2557 : hash_entry_t *entries;
2558 : unsigned long n_entries;
2559 : unsigned long eniter;
2560 :
2561 : struct sdap_ext_member *ext_members;
2562 :
2563 : ext_member_send_fn_t ext_member_resolve_send;
2564 : ext_member_recv_fn_t ext_member_resolve_recv;
2565 : };
2566 :
2567 : static errno_t
2568 : sdap_nested_group_lookup_external_step(struct tevent_req *req);
2569 : static void
2570 : sdap_nested_group_lookup_external_done(struct tevent_req *subreq);
2571 : static errno_t
2572 : sdap_nested_group_lookup_external_link(struct tevent_req *req);
2573 : static errno_t
2574 : sdap_nested_group_lookup_external_link_member(
2575 : struct sdap_nested_group_lookup_external_state *state,
2576 : struct sdap_ext_member *member);
2577 : static errno_t
2578 : sdap_nested_group_memberof_dn_by_original_dn(
2579 : TALLOC_CTX *mem_ctx,
2580 : struct sss_domain_info *group_dom,
2581 : const char *original_dn,
2582 : const char ***_parents);
2583 :
2584 : struct tevent_req *
2585 1 : sdap_nested_group_lookup_external_send(TALLOC_CTX *mem_ctx,
2586 : struct tevent_context *ev,
2587 : struct sss_domain_info *group_dom,
2588 : struct sdap_ext_member_ctx *ext_ctx,
2589 : hash_table_t *missing_external)
2590 : {
2591 1 : struct sdap_nested_group_lookup_external_state *state = NULL;
2592 1 : struct tevent_req *req = NULL;
2593 : errno_t ret;
2594 :
2595 1 : req = tevent_req_create(mem_ctx, &state,
2596 : struct sdap_nested_group_lookup_external_state);
2597 1 : if (req == NULL) {
2598 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
2599 0 : return NULL;
2600 : }
2601 :
2602 1 : state->ev = ev;
2603 1 : state->group_dom = group_dom;
2604 1 : state->ext_ctx = ext_ctx;
2605 1 : state->missing_external = missing_external;
2606 :
2607 1 : if (state->ext_ctx->ext_member_resolve_send == NULL
2608 1 : || state->ext_ctx->ext_member_resolve_recv == NULL) {
2609 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Wrong private context\n");
2610 0 : ret = EINVAL;
2611 0 : goto immediately;
2612 : }
2613 :
2614 2 : ret = hash_entries(state->missing_external,
2615 2 : &state->n_entries, &state->entries);
2616 1 : if (ret != HASH_SUCCESS) {
2617 0 : DEBUG(SSSDBG_CRIT_FAILURE, "hash_entries returned %d\n", ret);
2618 0 : ret = EIO;
2619 0 : goto immediately;
2620 : }
2621 1 : state->eniter = 0;
2622 :
2623 1 : state->ext_members = talloc_zero_array(state,
2624 : struct sdap_ext_member,
2625 : state->n_entries);
2626 1 : if (state->ext_members == NULL) {
2627 0 : ret = ENOMEM;
2628 0 : goto immediately;
2629 : }
2630 :
2631 1 : ret = sdap_nested_group_lookup_external_step(req);
2632 1 : if (ret != EAGAIN) {
2633 0 : goto immediately;
2634 : }
2635 :
2636 1 : return req;
2637 :
2638 : immediately:
2639 0 : if (ret == EOK) {
2640 0 : tevent_req_done(req);
2641 : } else {
2642 0 : tevent_req_error(req, ret);
2643 : }
2644 0 : tevent_req_post(req, ev);
2645 0 : return req;
2646 : }
2647 :
2648 : static errno_t
2649 2 : sdap_nested_group_lookup_external_step(struct tevent_req *req)
2650 : {
2651 2 : struct tevent_req *subreq = NULL;
2652 2 : struct sdap_nested_group_lookup_external_state *state = NULL;
2653 2 : state = tevent_req_data(req,
2654 : struct sdap_nested_group_lookup_external_state);
2655 :
2656 6 : subreq = state->ext_ctx->ext_member_resolve_send(state,
2657 : state->ev,
2658 2 : state->entries[state->eniter].key.str,
2659 2 : state->ext_ctx->pvt);
2660 2 : if (subreq == NULL) {
2661 0 : return ENOMEM;
2662 : }
2663 2 : DEBUG(SSSDBG_TRACE_FUNC, "Refreshing member %lu/%lu\n",
2664 : state->eniter, state->n_entries);
2665 2 : tevent_req_set_callback(subreq,
2666 : sdap_nested_group_lookup_external_done,
2667 : req);
2668 :
2669 2 : return EAGAIN;
2670 : }
2671 :
2672 : static void
2673 2 : sdap_nested_group_lookup_external_done(struct tevent_req *subreq)
2674 : {
2675 : errno_t ret;
2676 2 : struct tevent_req *req = NULL;
2677 2 : struct sdap_nested_group_lookup_external_state *state = NULL;
2678 : enum sysdb_member_type member_type;
2679 : struct sysdb_attrs *member;
2680 : struct sss_domain_info *member_dom;
2681 :
2682 2 : req = tevent_req_callback_data(subreq, struct tevent_req);
2683 2 : state = tevent_req_data(req,
2684 : struct sdap_nested_group_lookup_external_state);
2685 :
2686 2 : ret = state->ext_ctx->ext_member_resolve_recv(state, subreq,
2687 : &member_type,
2688 : &member_dom,
2689 : &member);
2690 2 : talloc_free(subreq);
2691 2 : if (ret == EOK) {
2692 2 : DEBUG(SSSDBG_TRACE_FUNC, "Refreshed member %lu\n", state->eniter);
2693 4 : state->ext_members[state->eniter].missing_mem = \
2694 2 : state->entries[state->eniter].value.ptr;
2695 2 : state->ext_members[state->eniter].dom = member_dom;
2696 :
2697 4 : state->ext_members[state->eniter].ext_member_attr = \
2698 2 : talloc_steal(state->ext_members,
2699 : state->entries[state->eniter].key.str);
2700 2 : state->ext_members[state->eniter].member_type = member_type;
2701 4 : state->ext_members[state->eniter].attrs = \
2702 2 : talloc_steal(state->ext_members, member);
2703 : }
2704 :
2705 2 : state->eniter++;
2706 2 : if (state->eniter >= state->n_entries) {
2707 1 : DEBUG(SSSDBG_TRACE_FUNC, "All external members processed\n");
2708 1 : ret = sdap_nested_group_lookup_external_link(req);
2709 1 : if (ret != EOK) {
2710 0 : tevent_req_error(req, ret);
2711 0 : return;
2712 : }
2713 1 : tevent_req_done(req);
2714 1 : return;
2715 : }
2716 :
2717 1 : ret = sdap_nested_group_lookup_external_step(req);
2718 1 : if (ret != EOK && ret != EAGAIN) {
2719 0 : tevent_req_error(req, ret);
2720 0 : return;
2721 : }
2722 :
2723 1 : return;
2724 : }
2725 :
2726 : static errno_t
2727 1 : sdap_nested_group_lookup_external_link(struct tevent_req *req)
2728 : {
2729 : errno_t ret, tret;
2730 1 : bool in_transaction = false;
2731 1 : struct sdap_nested_group_lookup_external_state *state = NULL;
2732 1 : state = tevent_req_data(req,
2733 : struct sdap_nested_group_lookup_external_state);
2734 :
2735 1 : ret = sysdb_transaction_start(state->group_dom->sysdb);
2736 1 : if (ret != EOK) {
2737 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
2738 0 : goto fail;
2739 : }
2740 1 : in_transaction = true;
2741 :
2742 :
2743 3 : for (size_t i = 0; i < state->eniter; i++) {
2744 2 : if (state->ext_members[i].attrs == NULL) {
2745 0 : DEBUG(SSSDBG_MINOR_FAILURE, "The member %s could not be resolved\n",
2746 : state->ext_members[i].ext_member_attr);
2747 0 : continue;
2748 : }
2749 :
2750 2 : ret = sdap_nested_group_lookup_external_link_member(state,
2751 2 : &state->ext_members[i]);
2752 2 : if (ret != EOK) {
2753 0 : goto fail;
2754 : }
2755 : }
2756 :
2757 1 : ret = sysdb_transaction_commit(state->group_dom->sysdb);
2758 1 : if (ret != EOK) {
2759 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
2760 0 : goto fail;
2761 : }
2762 1 : in_transaction = false;
2763 :
2764 1 : return EOK;
2765 :
2766 : fail:
2767 0 : if (in_transaction) {
2768 0 : tret = sysdb_transaction_cancel(state->group_dom->sysdb);
2769 0 : if (tret != EOK) {
2770 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to cancel transaction\n");
2771 : }
2772 : }
2773 0 : return EFAULT;
2774 : }
2775 :
2776 : static errno_t
2777 2 : sdap_nested_group_lookup_external_link_member(
2778 : struct sdap_nested_group_lookup_external_state *state,
2779 : struct sdap_ext_member *member)
2780 : {
2781 : const char *name;
2782 : int ret;
2783 2 : const char **parents = NULL;
2784 : size_t i;
2785 : TALLOC_CTX *tmp_ctx;
2786 : const char *orig_dn;
2787 :
2788 2 : tmp_ctx = talloc_new(state);
2789 2 : if (tmp_ctx == NULL) {
2790 0 : return ENOMEM;
2791 : }
2792 :
2793 2 : ret = sysdb_attrs_get_string(member->attrs, SYSDB_NAME, &name);
2794 2 : if (ret != EOK) {
2795 0 : DEBUG(SSSDBG_CRIT_FAILURE, "No name for a user\n");
2796 0 : goto done;
2797 : }
2798 :
2799 : /* This only works because the groups were saved in a previous
2800 : * transaction */
2801 5 : for (i=0; i < member->missing_mem->parent_dn_idx; i++) {
2802 3 : orig_dn = member->missing_mem->parent_group_dns[i];
2803 3 : DEBUG(SSSDBG_TRACE_INTERNAL,
2804 : "Linking external members %s from domain %s to parents of %s\n",
2805 : name, member->dom->name, orig_dn);
2806 3 : ret = sdap_nested_group_memberof_dn_by_original_dn(tmp_ctx,
2807 : state->group_dom,
2808 : orig_dn,
2809 : &parents);
2810 3 : if (ret != EOK) {
2811 0 : DEBUG(SSSDBG_MINOR_FAILURE,
2812 : "Cannot find parents of %s\n", orig_dn);
2813 0 : continue;
2814 : }
2815 :
2816 : /* We don't have to remove the members here, since all members attributes
2817 : * are always written anew
2818 : */
2819 3 : ret = sysdb_update_members_dn(member->dom, name, member->member_type,
2820 : parents, NULL);
2821 3 : if (ret != EOK) {
2822 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot link %s@%s to its parents\n",
2823 : name, member->dom->name);
2824 0 : goto done;
2825 : }
2826 :
2827 : }
2828 :
2829 2 : ret = EOK;
2830 : done:
2831 2 : talloc_free(tmp_ctx);
2832 2 : return ret;
2833 : }
2834 :
2835 : static errno_t
2836 3 : sdap_nested_group_memberof_dn_by_original_dn(
2837 : TALLOC_CTX *mem_ctx,
2838 : struct sss_domain_info *group_dom,
2839 : const char *original_dn,
2840 : const char ***_parents)
2841 : {
2842 : errno_t ret;
2843 : char *sanitized_dn;
2844 : char *filter;
2845 3 : const char *attrs[] = { SYSDB_NAME,
2846 : SYSDB_MEMBEROF,
2847 : NULL };
2848 3 : struct ldb_message **msgs = NULL;
2849 : size_t count;
2850 : TALLOC_CTX *tmp_ctx;
2851 : struct ldb_message_element *memberof;
2852 : const char **parents;
2853 :
2854 3 : tmp_ctx = talloc_new(mem_ctx);
2855 3 : if (tmp_ctx == NULL) {
2856 0 : return ENOMEM;
2857 : }
2858 :
2859 3 : ret = sss_filter_sanitize(tmp_ctx, original_dn, &sanitized_dn);
2860 3 : if (ret != EOK) {
2861 0 : DEBUG(SSSDBG_CRIT_FAILURE,
2862 : "Cannot sanitize originalDN [%s]\n", original_dn);
2863 0 : goto done;
2864 : }
2865 :
2866 3 : filter = talloc_asprintf(tmp_ctx, "(%s=%s)", SYSDB_ORIG_DN, sanitized_dn);
2867 3 : if (filter == NULL) {
2868 0 : goto done;
2869 : }
2870 :
2871 3 : ret = sysdb_search_groups(tmp_ctx, group_dom, filter, attrs,
2872 : &count, &msgs);
2873 3 : if (ret != EOK) {
2874 0 : goto done;
2875 : }
2876 :
2877 3 : if (count != 1) {
2878 0 : DEBUG(SSSDBG_OP_FAILURE,
2879 : "More than one entry found by originalDN?\n");
2880 0 : goto done;
2881 : }
2882 :
2883 3 : memberof = ldb_msg_find_element(msgs[0], SYSDB_MEMBEROF);
2884 3 : if (memberof == NULL || memberof->num_values == 0) {
2885 0 : DEBUG(SSSDBG_MINOR_FAILURE,
2886 : "The external group is not a member of any groups\n");
2887 0 : ret = ENOENT;
2888 0 : goto done;
2889 : }
2890 :
2891 3 : parents = talloc_zero_array(tmp_ctx,
2892 : const char *,
2893 : memberof->num_values + 1);
2894 3 : if (parents == NULL) {
2895 0 : ret = ENOMEM;
2896 0 : goto done;
2897 : }
2898 :
2899 8 : for (size_t i = 0; i < memberof->num_values; i++) {
2900 10 : parents[i] = talloc_strdup(parents,
2901 5 : (const char *) memberof->values[i].data);
2902 5 : if (parents[i] == NULL) {
2903 0 : ret = ENOMEM;
2904 0 : goto done;
2905 : }
2906 : }
2907 :
2908 3 : *_parents = talloc_steal(mem_ctx, parents);
2909 3 : ret = EOK;
2910 : done:
2911 3 : talloc_free(tmp_ctx);
2912 3 : return ret;
2913 : }
2914 :
2915 : errno_t
2916 1 : sdap_nested_group_lookup_external_recv(TALLOC_CTX *mem_ctx,
2917 : struct tevent_req *req)
2918 : {
2919 1 : TEVENT_REQ_RETURN_ON_ERROR(req);
2920 :
2921 1 : return EOK;
2922 : }
|