Line data Source code
1 : /*
2 : SSSD
3 :
4 : Async LDAP Helper routines - retrieving groups
5 :
6 : Copyright (C) Simo Sorce <ssorce@redhat.com> - 2009
7 : Copyright (C) 2010, Ralf Haferkamp <rhafer@suse.de>, Novell Inc.
8 : Copyright (C) Jan Zeleny <jzeleny@redhat.com> - 2011
9 :
10 : This program is free software; you can redistribute it and/or modify
11 : it under the terms of the GNU General Public License as published by
12 : the Free Software Foundation; either version 3 of the License, or
13 : (at your option) any later version.
14 :
15 : This program is distributed in the hope that it will be useful,
16 : but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : GNU General Public License for more details.
19 :
20 : You should have received a copy of the GNU General Public License
21 : along with this program. If not, see <http://www.gnu.org/licenses/>.
22 : */
23 :
24 : #include "util/util.h"
25 : #include "util/probes.h"
26 : #include "db/sysdb.h"
27 : #include "providers/ldap/sdap_async_private.h"
28 : #include "providers/ldap/ldap_common.h"
29 : #include "providers/ldap/sdap_idmap.h"
30 : #include "providers/ad/ad_common.h"
31 :
32 : /* ==Group-Parsing Routines=============================================== */
33 :
34 0 : static int sdap_find_entry_by_origDN(TALLOC_CTX *memctx,
35 : struct sysdb_ctx *ctx,
36 : struct sss_domain_info *domain,
37 : const char *orig_dn,
38 : char **_localdn,
39 : bool *_is_group)
40 : {
41 : TALLOC_CTX *tmpctx;
42 0 : const char *attrs[] = {SYSDB_OBJECTCLASS, NULL};
43 : struct ldb_dn *base_dn;
44 : char *filter;
45 : struct ldb_message **msgs;
46 : size_t num_msgs;
47 : int ret;
48 : char *sanitized_dn;
49 : const char *objectclass;
50 :
51 0 : tmpctx = talloc_new(NULL);
52 0 : if (!tmpctx) {
53 0 : return ENOMEM;
54 : }
55 :
56 0 : ret = sss_filter_sanitize(tmpctx, orig_dn, &sanitized_dn);
57 0 : if (ret != EOK) {
58 0 : ret = ENOMEM;
59 0 : goto done;
60 : }
61 :
62 0 : filter = talloc_asprintf(tmpctx, "%s=%s", SYSDB_ORIG_DN, sanitized_dn);
63 0 : if (!filter) {
64 0 : ret = ENOMEM;
65 0 : goto done;
66 : }
67 :
68 0 : base_dn = sysdb_domain_dn(tmpctx, domain);
69 0 : if (!base_dn) {
70 0 : ret = ENOMEM;
71 0 : goto done;
72 : }
73 :
74 0 : DEBUG(SSSDBG_TRACE_ALL, "Searching cache for [%s].\n", sanitized_dn);
75 0 : ret = sysdb_search_entry(tmpctx, ctx,
76 : base_dn, LDB_SCOPE_SUBTREE, filter, attrs,
77 : &num_msgs, &msgs);
78 0 : if (ret) {
79 0 : goto done;
80 : }
81 0 : if (num_msgs != 1) {
82 0 : ret = ENOENT;
83 0 : goto done;
84 : }
85 :
86 0 : *_localdn = talloc_strdup(memctx, ldb_dn_get_linearized(msgs[0]->dn));
87 0 : if (!*_localdn) {
88 0 : ret = ENOENT;
89 0 : goto done;
90 : }
91 :
92 0 : if (_is_group != NULL) {
93 0 : objectclass = ldb_msg_find_attr_as_string(msgs[0], SYSDB_OBJECTCLASS,
94 : NULL);
95 0 : if (objectclass == NULL) {
96 0 : DEBUG(SSSDBG_OP_FAILURE, "An antry without a %s?\n",
97 : SYSDB_OBJECTCLASS);
98 0 : ret = EINVAL;
99 0 : goto done;
100 : }
101 :
102 0 : *_is_group = strcmp(SYSDB_GROUP_CLASS, objectclass) == 0;
103 : }
104 :
105 0 : ret = EOK;
106 :
107 : done:
108 0 : talloc_zfree(tmpctx);
109 0 : return ret;
110 : }
111 :
112 : static errno_t
113 0 : sdap_get_members_with_primary_gid(TALLOC_CTX *mem_ctx,
114 : struct sss_domain_info *domain,
115 : gid_t gid, char ***_localdn, size_t *_ndn)
116 : {
117 : static const char *search_attrs[] = { SYSDB_NAME, NULL };
118 : char *filter;
119 : struct ldb_message **msgs;
120 : size_t count;
121 : size_t i;
122 : errno_t ret;
123 : char **localdn;
124 :
125 : /* Don't search if the group is non-posix */
126 0 : if (!gid) return EOK;
127 :
128 0 : filter = talloc_asprintf(mem_ctx, "(%s=%llu)", SYSDB_GIDNUM,
129 : (unsigned long long) gid);
130 0 : if (!filter) {
131 0 : return ENOMEM;
132 : }
133 :
134 0 : ret = sysdb_search_users(mem_ctx, domain, filter,
135 : search_attrs, &count, &msgs);
136 0 : talloc_free(filter);
137 0 : if (ret == ENOENT) {
138 0 : *_localdn = NULL;
139 0 : *_ndn = 0;
140 0 : return EOK;
141 0 : } else if (ret != EOK) {
142 0 : return ret;
143 : }
144 :
145 0 : localdn = talloc_array(mem_ctx, char *, count);
146 0 : if (!localdn) {
147 0 : talloc_free(msgs);
148 0 : return ENOMEM;
149 : }
150 :
151 0 : for (i=0; i < count; i++) {
152 0 : localdn[i] = talloc_strdup(localdn,
153 0 : ldb_dn_get_linearized(msgs[i]->dn));
154 0 : if (!localdn[i]) {
155 0 : talloc_free(localdn);
156 0 : talloc_free(msgs);
157 0 : return ENOMEM;
158 : }
159 : }
160 :
161 0 : talloc_free(msgs);
162 0 : *_localdn = localdn;
163 0 : *_ndn = count;
164 0 : return EOK;
165 : }
166 :
167 : static errno_t
168 0 : sdap_dn_by_primary_gid(TALLOC_CTX *mem_ctx, struct sysdb_attrs *ldap_attrs,
169 : struct sss_domain_info *domain,
170 : struct sdap_options *opts,
171 : char ***_dn_list, size_t *_count)
172 : {
173 : gid_t gid;
174 : errno_t ret;
175 :
176 0 : ret = sysdb_attrs_get_uint32_t(ldap_attrs,
177 0 : opts->group_map[SDAP_AT_GROUP_GID].sys_name,
178 : &gid);
179 0 : if (ret == ENOENT) {
180 : /* Non-posix AD group. Skip. */
181 0 : *_dn_list = NULL;
182 0 : *_count = 0;
183 0 : return EOK;
184 0 : } else if (ret && ret != ENOENT) {
185 0 : return ret;
186 : }
187 :
188 0 : ret = sdap_get_members_with_primary_gid(mem_ctx, domain, gid,
189 : _dn_list, _count);
190 0 : if (ret) return ret;
191 :
192 0 : return EOK;
193 : }
194 :
195 0 : static bool has_member(struct ldb_message_element *member_el,
196 : char *member)
197 : {
198 : struct ldb_val val;
199 :
200 0 : val.data = (uint8_t *) member;
201 0 : val.length = strlen(member);
202 :
203 : /* This is bad complexity, but the this loop should only be invoked in
204 : * the very rare scenario of AD POSIX group that is primary group of
205 : * some users but has user member attributes at the same time
206 : */
207 0 : if (ldb_msg_find_val(member_el, &val) != NULL) {
208 0 : return true;
209 : }
210 :
211 0 : return false;
212 : }
213 :
214 0 : static void link_pgroup_members(struct sysdb_attrs *group_attrs,
215 : struct ldb_message_element *member_el,
216 : char **userdns,
217 : size_t nuserdns)
218 : {
219 : int i, j;
220 :
221 0 : j = 0;
222 0 : for (i=0; i < nuserdns; i++) {
223 0 : if (has_member(member_el, userdns[i])) {
224 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
225 : "Member %s already included, skipping\n", userdns[i]);
226 0 : continue;
227 : }
228 :
229 0 : member_el->values[member_el->num_values + j].data = (uint8_t *) \
230 0 : talloc_steal(group_attrs, userdns[i]);
231 0 : member_el->values[member_el->num_values + j].length = \
232 0 : strlen(userdns[i]);
233 0 : j++;
234 : }
235 0 : member_el->num_values += j;
236 0 : }
237 :
238 0 : static int sdap_fill_memberships(struct sdap_options *opts,
239 : struct sysdb_attrs *group_attrs,
240 : struct sysdb_ctx *ctx,
241 : struct sss_domain_info *domain,
242 : hash_table_t *ghosts,
243 : struct ldb_val *values,
244 : int num_values,
245 : char **userdns,
246 : size_t nuserdns)
247 : {
248 : struct ldb_message_element *el;
249 : int i, j;
250 : int ret;
251 : errno_t hret;
252 : hash_key_t key;
253 : hash_value_t value;
254 : struct sdap_domain *sdom;
255 : struct sysdb_ctx *member_sysdb;
256 : struct sss_domain_info *member_dom;
257 :
258 0 : ret = sysdb_attrs_get_el(group_attrs, SYSDB_MEMBER, &el);
259 0 : if (ret) {
260 0 : DEBUG(SSSDBG_MINOR_FAILURE, "sysdb_attrs_get_el failed\n");
261 0 : goto done;
262 : }
263 :
264 : /* Just allocate both big enough to contain all members for now */
265 0 : el->values = talloc_realloc(group_attrs, el->values, struct ldb_val,
266 : el->num_values + num_values + nuserdns);
267 0 : if (!el->values) {
268 0 : DEBUG(SSSDBG_MINOR_FAILURE, "No memory to allocate group attrs\n");
269 0 : ret = ENOMEM;
270 0 : goto done;
271 : }
272 :
273 0 : j = el->num_values;
274 0 : for (i = 0; i < num_values; i++) {
275 0 : if (ghosts == NULL) {
276 0 : hret = HASH_ERROR_KEY_NOT_FOUND;
277 : } else {
278 0 : key.type = HASH_KEY_STRING;
279 0 : key.str = (char *)values[i].data;
280 0 : hret = hash_lookup(ghosts, &key, &value);
281 : }
282 :
283 0 : if (hret == HASH_ERROR_KEY_NOT_FOUND) {
284 0 : sdom = sdap_domain_get_by_dn(opts, (char *)values[i].data);
285 0 : if (sdom == NULL) {
286 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Member [%s] is it out of domain "
287 : "scope?\n", (char *)values[i].data);
288 0 : member_sysdb = ctx;
289 0 : member_dom = domain;
290 : } else {
291 0 : member_sysdb = sdom->dom->sysdb;
292 0 : member_dom = sdom->dom;
293 : }
294 :
295 : /* sync search entry with this as origDN */
296 0 : ret = sdap_find_entry_by_origDN(el->values, member_sysdb,
297 0 : member_dom, (char *)values[i].data,
298 0 : (char **)&el->values[j].data,
299 : NULL);
300 0 : if (ret == ENOENT) {
301 : /* member may be outside of the configured search bases
302 : * or out of scope of nesting limit */
303 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Member [%s] was not found in "
304 : "cache. Is it out of scope?\n", (char *)values[i].data);
305 0 : continue;
306 : }
307 0 : if (ret != EOK) {
308 0 : DEBUG(SSSDBG_MINOR_FAILURE,
309 : "'sdap_find_entry_by_origDN' failed for member [%s].\n",
310 : (char *)values[i].data);
311 0 : goto done;
312 : }
313 :
314 0 : DEBUG(SSSDBG_TRACE_LIBS, " member #%d (%s): [%s]\n",
315 : i, (char *)values[i].data,
316 : (char *)el->values[j].data);
317 :
318 0 : el->values[j].length = strlen((char *)el->values[j].data);
319 0 : j++;
320 0 : } else if (hret != HASH_SUCCESS) {
321 0 : DEBUG(SSSDBG_MINOR_FAILURE,
322 : "hash_lookup failed: [%d]: %s\n", hret, strerror(hret));
323 0 : ret = EFAULT;
324 0 : goto done;
325 : }
326 :
327 : /* If the member is in ghost table, it has
328 : * already been processed - just skip it */
329 : }
330 0 : el->num_values = j;
331 :
332 0 : link_pgroup_members(group_attrs, el, userdns, nuserdns);
333 0 : ret = EOK;
334 :
335 : done:
336 0 : return ret;
337 : }
338 :
339 : /* ==Save-Group-Entry===================================================== */
340 :
341 : /* FIXME: support non legacy */
342 : /* FIXME: support storing additional attributes */
343 :
344 : static errno_t
345 0 : sdap_store_group_with_gid(struct sss_domain_info *domain,
346 : const char *name,
347 : gid_t gid,
348 : struct sysdb_attrs *group_attrs,
349 : uint64_t cache_timeout,
350 : bool posix_group,
351 : time_t now)
352 : {
353 : errno_t ret;
354 :
355 : /* make sure that non-posix (empty or explicit gid=0) groups have the
356 : * gidNumber set to zero even if updating existing group */
357 0 : if (!posix_group) {
358 0 : ret = sysdb_attrs_add_uint32(group_attrs, SYSDB_GIDNUM, 0);
359 0 : if (ret) {
360 0 : DEBUG(SSSDBG_OP_FAILURE,
361 : "Could not set explicit GID 0 for %s\n", name);
362 0 : return ret;
363 : }
364 : }
365 :
366 0 : ret = sysdb_store_group(domain, name, gid, group_attrs,
367 : cache_timeout, now);
368 0 : if (ret) {
369 0 : DEBUG(SSSDBG_OP_FAILURE, "Could not store group %s\n", name);
370 0 : return ret;
371 : }
372 :
373 0 : return ret;
374 : }
375 :
376 : static errno_t
377 0 : sdap_process_ghost_members(struct sysdb_attrs *attrs,
378 : struct sdap_options *opts,
379 : hash_table_t *ghosts,
380 : bool populate_members,
381 : bool store_original_member,
382 : struct sysdb_attrs *sysdb_attrs)
383 : {
384 : errno_t ret;
385 : struct ldb_message_element *gh;
386 : struct ldb_message_element *memberel;
387 : struct ldb_message_element *sysdb_memberel;
388 : struct ldb_message_element *ghostel;
389 : size_t cnt;
390 : int i;
391 : int hret;
392 : hash_key_t key;
393 : hash_value_t value;
394 :
395 0 : ret = sysdb_attrs_get_el(attrs, SYSDB_GHOST, &gh);
396 0 : if (ret != EOK) {
397 0 : DEBUG(SSSDBG_MINOR_FAILURE,
398 : "Error reading ghost attributes: [%s]\n",
399 : strerror(ret));
400 0 : return ret;
401 : }
402 :
403 0 : ret = sysdb_attrs_get_el_ext(attrs,
404 0 : opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name,
405 : false, &memberel);
406 0 : if (ret == ENOENT) {
407 : /* Create a dummy element with no values in order for the loop to just
408 : * fall through and make sure the attrs array is not reallocated.
409 : */
410 0 : memberel = talloc(attrs, struct ldb_message_element);
411 0 : if (memberel == NULL) {
412 0 : return ENOMEM;
413 : }
414 0 : memberel->num_values = 0;
415 0 : memberel->values = NULL;
416 0 : } else if (ret != EOK) {
417 0 : DEBUG(SSSDBG_MINOR_FAILURE,
418 : "Error reading members: [%s]\n", strerror(ret));
419 0 : return ret;
420 : }
421 :
422 0 : if (store_original_member) {
423 0 : DEBUG(SSSDBG_TRACE_FUNC, "The group has %d members\n", memberel->num_values);
424 0 : for (i = 0; i < memberel->num_values; i++) {
425 0 : ret = sysdb_attrs_add_string(sysdb_attrs, SYSDB_ORIG_MEMBER,
426 0 : (const char *) memberel->values[i].data);
427 0 : if (ret) {
428 0 : DEBUG(SSSDBG_OP_FAILURE, "Could not add member [%s]\n",
429 : (const char *) memberel->values[i].data);
430 0 : return ret;
431 : }
432 : }
433 : }
434 :
435 0 : if (populate_members) {
436 0 : ret = sysdb_attrs_get_el(sysdb_attrs, SYSDB_MEMBER, &sysdb_memberel);
437 0 : if (ret != EOK) {
438 0 : DEBUG(SSSDBG_MINOR_FAILURE,
439 : "Error reading group members from group_attrs: [%s]\n",
440 : strerror(ret));
441 0 : return ret;
442 : }
443 0 : sysdb_memberel->values = memberel->values;
444 0 : sysdb_memberel->num_values = memberel->num_values;
445 : }
446 :
447 0 : ret = sysdb_attrs_get_el(sysdb_attrs, SYSDB_GHOST, &ghostel);
448 0 : if (ret != EOK) {
449 0 : DEBUG(SSSDBG_MINOR_FAILURE,
450 : "Error getting ghost element: [%s]\n", strerror(ret));
451 0 : return ret;
452 : }
453 0 : ghostel->values = gh->values;
454 0 : ghostel->num_values = gh->num_values;
455 :
456 0 : cnt = ghostel->num_values + memberel->num_values;
457 0 : DEBUG(SSSDBG_TRACE_FUNC, "Group has %zu members\n", cnt);
458 :
459 : /* Now process RFC2307bis ghost hash table */
460 0 : if (ghosts && cnt > 0) {
461 0 : ghostel->values = talloc_realloc(sysdb_attrs, ghostel->values,
462 : struct ldb_val, cnt);
463 0 : if (ghostel->values == NULL) {
464 0 : return ENOMEM;
465 : }
466 :
467 0 : for (i = 0; i < memberel->num_values; i++) {
468 0 : key.type = HASH_KEY_STRING;
469 0 : key.str = (char *) memberel->values[i].data;
470 0 : hret = hash_lookup(ghosts, &key, &value);
471 0 : if (hret == HASH_ERROR_KEY_NOT_FOUND) {
472 0 : continue;
473 0 : } else if (hret != HASH_SUCCESS) {
474 0 : DEBUG(SSSDBG_MINOR_FAILURE,
475 : "Error checking hash table: [%s]\n",
476 : hash_error_string(hret));
477 0 : return EFAULT;
478 : }
479 :
480 0 : DEBUG(SSSDBG_TRACE_FUNC,
481 : "Adding ghost member for group [%s]\n", (char *) value.ptr);
482 0 : ghostel->values[ghostel->num_values].data = \
483 0 : (uint8_t *) talloc_strdup(ghostel->values, value.ptr);
484 0 : if (ghostel->values[ghostel->num_values].data == NULL) {
485 0 : return ENOMEM;
486 : }
487 0 : ghostel->values[ghostel->num_values].length = strlen(value.ptr);
488 0 : ghostel->num_values++;
489 : }
490 : }
491 :
492 0 : return EOK;
493 : }
494 :
495 0 : static int sdap_save_group(TALLOC_CTX *memctx,
496 : struct sdap_options *opts,
497 : struct sss_domain_info *dom,
498 : struct sysdb_attrs *attrs,
499 : bool populate_members,
500 : bool store_original_member,
501 : hash_table_t *ghosts,
502 : char **_usn_value,
503 : time_t now)
504 : {
505 : struct ldb_message_element *el;
506 : struct sysdb_attrs *group_attrs;
507 0 : const char *group_name = NULL;
508 : gid_t gid;
509 : errno_t ret;
510 0 : char *usn_value = NULL;
511 0 : TALLOC_CTX *tmpctx = NULL;
512 : bool posix_group;
513 : bool use_id_mapping;
514 : bool need_filter;
515 : char *sid_str;
516 : struct sss_domain_info *subdomain;
517 :
518 0 : tmpctx = talloc_new(NULL);
519 0 : if (!tmpctx) {
520 0 : ret = ENOMEM;
521 0 : goto done;
522 : }
523 :
524 0 : group_attrs = sysdb_new_attrs(tmpctx);
525 0 : if (group_attrs == NULL) {
526 0 : ret = ENOMEM;
527 0 : goto done;
528 : }
529 :
530 : /* Always store SID string if available */
531 0 : ret = sdap_attrs_get_sid_str(tmpctx, opts->idmap_ctx, attrs,
532 0 : opts->group_map[SDAP_AT_GROUP_OBJECTSID].sys_name,
533 : &sid_str);
534 0 : if (ret == EOK) {
535 0 : ret = sysdb_attrs_add_string(group_attrs, SYSDB_SID_STR, sid_str);
536 0 : if (ret != EOK) {
537 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Could not add SID string: [%s]\n",
538 : sss_strerror(ret));
539 0 : goto done;
540 : }
541 0 : } else if (ret == ENOENT) {
542 0 : DEBUG(SSSDBG_TRACE_ALL, "objectSID: not available for group [%s].\n",
543 : group_name);
544 0 : sid_str = NULL;
545 : } else {
546 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Could not identify objectSID: [%s]\n",
547 : sss_strerror(ret));
548 0 : sid_str = NULL;
549 : }
550 :
551 : /* Always store UUID if available */
552 0 : ret = sysdb_handle_original_uuid(
553 0 : opts->group_map[SDAP_AT_GROUP_UUID].def_name,
554 : attrs,
555 0 : opts->group_map[SDAP_AT_GROUP_UUID].sys_name,
556 : group_attrs, SYSDB_UUID);
557 0 : if (ret != EOK) {
558 0 : DEBUG((ret == ENOENT) ? SSSDBG_TRACE_ALL : SSSDBG_MINOR_FAILURE,
559 : "Failed to retrieve UUID [%d][%s].\n", ret, sss_strerror(ret));
560 : }
561 :
562 : /* If this object has a SID available, we will determine the correct
563 : * domain by its SID. */
564 0 : if (sid_str != NULL) {
565 0 : subdomain = sss_get_domain_by_sid_ldap_fallback(get_domains_head(dom),
566 : sid_str);
567 0 : if (subdomain) {
568 0 : dom = subdomain;
569 : } else {
570 0 : DEBUG(SSSDBG_TRACE_FUNC, "SID %s does not belong to any known "
571 : "domain\n", sid_str);
572 : }
573 : }
574 :
575 0 : ret = sdap_get_group_primary_name(tmpctx, opts, attrs, dom, &group_name);
576 0 : if (ret != EOK) {
577 0 : DEBUG(SSSDBG_OP_FAILURE, "Failed to get group name\n");
578 0 : goto done;
579 : }
580 0 : DEBUG(SSSDBG_TRACE_FUNC, "Processing group %s\n", group_name);
581 :
582 0 : posix_group = true;
583 0 : ret = sdap_check_ad_group_type(dom, opts, attrs, group_name,
584 : &need_filter);
585 0 : if (ret != EOK) {
586 0 : goto done;
587 : }
588 0 : if (need_filter) {
589 0 : posix_group = false;
590 0 : gid = 0;
591 :
592 0 : ret = sysdb_attrs_add_bool(group_attrs, SYSDB_POSIX, false);
593 0 : if (ret != EOK) {
594 0 : DEBUG(SSSDBG_OP_FAILURE,
595 : "Error: Failed to mark group as non-posix!\n");
596 0 : goto done;
597 : }
598 : }
599 :
600 0 : if (posix_group) {
601 0 : use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(opts->idmap_ctx,
602 0 : dom->name,
603 : sid_str);
604 0 : if (use_id_mapping) {
605 0 : posix_group = true;
606 :
607 0 : if (sid_str == NULL) {
608 0 : DEBUG(SSSDBG_MINOR_FAILURE, "SID not available, cannot map a " \
609 : "unix ID to group [%s].\n", group_name);
610 0 : ret = ENOENT;
611 0 : goto done;
612 : }
613 :
614 0 : DEBUG(SSSDBG_TRACE_LIBS,
615 : "Mapping group [%s] objectSID [%s] to unix ID\n",
616 : group_name, sid_str);
617 :
618 : /* Convert the SID into a UNIX group ID */
619 0 : ret = sdap_idmap_sid_to_unix(opts->idmap_ctx, sid_str, &gid);
620 0 : if (ret == ENOTSUP) {
621 : /* ENOTSUP is returned if built-in SID was provided
622 : * => do not store the group, but return EOK */
623 0 : DEBUG(SSSDBG_TRACE_FUNC, "Skipping built-in object.\n");
624 0 : ret = EOK;
625 0 : goto done;
626 0 : } else if (ret != EOK) {
627 0 : DEBUG(SSSDBG_MINOR_FAILURE,
628 : "Could not convert SID string: [%s]\n",
629 : sss_strerror(ret));
630 0 : goto done;
631 : }
632 :
633 : /* Store the GID in the ldap_attrs so it doesn't get
634 : * treated as a missing attribute from LDAP and removed.
635 : */
636 0 : ret = sdap_replace_id(attrs, SYSDB_GIDNUM, gid);
637 0 : if (ret) {
638 0 : DEBUG(SSSDBG_OP_FAILURE, "Cannot set the id-mapped GID\n");
639 0 : goto done;
640 : }
641 : } else {
642 0 : ret = sysdb_attrs_get_bool(attrs, SYSDB_POSIX, &posix_group);
643 0 : if (ret == ENOENT) {
644 0 : posix_group = true;
645 0 : } else if (ret != EOK) {
646 0 : DEBUG(SSSDBG_MINOR_FAILURE,
647 : "Error reading posix attribute: [%s]\n",
648 : sss_strerror(ret));
649 0 : goto done;
650 : }
651 :
652 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
653 : "This is%s a posix group\n", (posix_group)?"":" not");
654 0 : ret = sysdb_attrs_add_bool(group_attrs, SYSDB_POSIX, posix_group);
655 0 : if (ret != EOK) {
656 0 : DEBUG(SSSDBG_MINOR_FAILURE,
657 : "Error setting posix attribute: [%s]\n",
658 : sss_strerror(ret));
659 0 : goto done;
660 : }
661 :
662 0 : ret = sysdb_attrs_get_uint32_t(attrs,
663 0 : opts->group_map[SDAP_AT_GROUP_GID].sys_name,
664 : &gid);
665 0 : if (ret != EOK) {
666 0 : DEBUG(SSSDBG_CRIT_FAILURE,
667 : "no gid provided for [%s] in domain [%s].\n",
668 : group_name, dom->name);
669 0 : ret = EINVAL;
670 0 : goto done;
671 : }
672 : }
673 : }
674 :
675 : /* check that the gid is valid for this domain */
676 0 : if (posix_group) {
677 0 : if (OUT_OF_ID_RANGE(gid, dom->id_min, dom->id_max)) {
678 0 : DEBUG(SSSDBG_MINOR_FAILURE,
679 : "Group [%s] filtered out! (id out of range)\n", group_name);
680 0 : ret = EINVAL;
681 0 : goto done;
682 : }
683 : /* Group ID OK */
684 : }
685 :
686 0 : ret = sdap_attrs_add_string(attrs, SYSDB_ORIG_DN, "original DN",
687 : group_name, group_attrs);
688 0 : if (ret != EOK) {
689 0 : DEBUG(SSSDBG_MINOR_FAILURE,
690 : "Error setting original DN: [%s]\n",
691 : sss_strerror(ret));
692 0 : goto done;
693 : }
694 :
695 0 : ret = sdap_attrs_add_string(attrs,
696 : opts->group_map[SDAP_AT_GROUP_MODSTAMP].sys_name,
697 : "original mod-Timestamp",
698 : group_name, group_attrs);
699 0 : if (ret != EOK) {
700 0 : DEBUG(SSSDBG_MINOR_FAILURE,
701 : "Error setting mod timestamp: [%s]\n",
702 : sss_strerror(ret));
703 0 : goto done;
704 : }
705 :
706 0 : ret = sysdb_attrs_get_el(attrs,
707 0 : opts->group_map[SDAP_AT_GROUP_USN].sys_name, &el);
708 0 : if (ret) {
709 0 : DEBUG(SSSDBG_MINOR_FAILURE,
710 : "Error looking up group USN: [%s]\n",
711 : sss_strerror(ret));
712 0 : goto done;
713 : }
714 0 : if (el->num_values == 0) {
715 0 : DEBUG(SSSDBG_TRACE_FUNC,
716 : "Original USN value is not available for [%s].\n", group_name);
717 : } else {
718 0 : ret = sysdb_attrs_add_string(group_attrs,
719 0 : opts->group_map[SDAP_AT_GROUP_USN].sys_name,
720 0 : (const char*)el->values[0].data);
721 0 : if (ret) {
722 0 : DEBUG(SSSDBG_MINOR_FAILURE,
723 : "Error setting group USN: [%s]\n",
724 : sss_strerror(ret));
725 0 : goto done;
726 : }
727 0 : usn_value = talloc_strdup(tmpctx, (const char*)el->values[0].data);
728 0 : if (!usn_value) {
729 0 : ret = ENOMEM;
730 0 : goto done;
731 : }
732 : }
733 :
734 0 : ret = sdap_process_ghost_members(attrs, opts, ghosts,
735 : populate_members, store_original_member,
736 : group_attrs);
737 0 : if (ret != EOK) {
738 0 : DEBUG(SSSDBG_OP_FAILURE, "Failed to save ghost members\n");
739 0 : goto done;
740 : }
741 :
742 0 : ret = sdap_save_all_names(group_name, attrs, dom, group_attrs);
743 0 : if (ret != EOK) {
744 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to save group names\n");
745 0 : goto done;
746 : }
747 0 : DEBUG(SSSDBG_TRACE_FUNC, "Storing info for group %s\n", group_name);
748 :
749 0 : ret = sdap_store_group_with_gid(dom, group_name, gid, group_attrs,
750 0 : dom->group_timeout,
751 : posix_group, now);
752 0 : if (ret) {
753 0 : DEBUG(SSSDBG_MINOR_FAILURE,
754 : "Could not store group with GID: [%s]\n",
755 : sss_strerror(ret));
756 0 : goto done;
757 : }
758 :
759 0 : if (_usn_value) {
760 0 : *_usn_value = talloc_steal(memctx, usn_value);
761 : }
762 :
763 0 : talloc_steal(memctx, group_attrs);
764 0 : ret = EOK;
765 :
766 : done:
767 0 : if (ret) {
768 0 : DEBUG(SSSDBG_MINOR_FAILURE,
769 : "Failed to save group [%s]: [%s]\n",
770 : group_name ? group_name : "Unknown",
771 : sss_strerror(ret));
772 : }
773 0 : talloc_free(tmpctx);
774 0 : return ret;
775 : }
776 :
777 : static errno_t
778 0 : are_sids_from_same_dom(const char *sid1, const char *sid2, bool *_result)
779 : {
780 : size_t len_prefix_sid1;
781 : size_t len_prefix_sid2;
782 : char *rid1, *rid2;
783 : bool result;
784 :
785 0 : rid1 = strrchr(sid1, '-');
786 0 : if (rid1 == NULL) {
787 0 : return EINVAL;
788 : }
789 :
790 0 : rid2 = strrchr(sid2, '-');
791 0 : if (rid2 == NULL) {
792 0 : return EINVAL;
793 : }
794 :
795 0 : len_prefix_sid1 = rid1 - sid1;
796 0 : len_prefix_sid2 = rid2 - sid2;
797 :
798 0 : result = (len_prefix_sid1 == len_prefix_sid2) &&
799 0 : (strncmp(sid1, sid2, len_prefix_sid1) == 0);
800 :
801 0 : *_result = result;
802 :
803 0 : return EOK;
804 : }
805 :
806 : static errno_t
807 0 : retain_extern_members(TALLOC_CTX *mem_ctx,
808 : struct sss_domain_info *dom,
809 : const char *group_name,
810 : const char *group_sid,
811 : char ***_userdns,
812 : size_t *_nuserdns)
813 : {
814 : TALLOC_CTX *tmp_ctx;
815 : const char **sids, **dns;
816 : bool same_domain;
817 : errno_t ret;
818 : size_t i, n;
819 0 : size_t nuserdns = 0;
820 0 : const char **userdns = NULL;
821 :
822 0 : tmp_ctx = talloc_new(NULL);
823 0 : if (tmp_ctx == NULL) {
824 0 : return ENOMEM;
825 : }
826 :
827 0 : ret = sysdb_get_sids_of_members(tmp_ctx, dom, group_name, &sids, &dns, &n);
828 0 : if (ret != EOK) {
829 0 : if (ret != ENOENT) {
830 0 : DEBUG(SSSDBG_TRACE_ALL,
831 : "get_sids_of_members failed: %d [%s]\n",
832 : ret, sss_strerror(ret));
833 : }
834 0 : goto done;
835 : }
836 :
837 0 : for (i=0; i < n; i++) {
838 0 : ret = are_sids_from_same_dom(group_sid, sids[i], &same_domain);
839 0 : if (ret == EOK && !same_domain) {
840 0 : DEBUG(SSSDBG_TRACE_ALL, "extern member: %s\n", dns[i]);
841 0 : nuserdns++;
842 0 : userdns = talloc_realloc(tmp_ctx, userdns, const char*, nuserdns);
843 0 : if (userdns == NULL) {
844 0 : ret = ENOMEM;
845 0 : goto done;
846 : }
847 0 : userdns[nuserdns-1] = talloc_steal(userdns, dns[i]);
848 : }
849 : }
850 0 : *_nuserdns = nuserdns;
851 0 : *_userdns = discard_const(talloc_steal(mem_ctx, userdns));
852 0 : ret = EOK;
853 :
854 : done:
855 0 : talloc_free(tmp_ctx);
856 0 : return ret;
857 : }
858 :
859 : /* ==Save-Group-Memebrs=================================================== */
860 :
861 : /* FIXME: support non legacy */
862 : /* FIXME: support storing additional attributes */
863 :
864 0 : static int sdap_save_grpmem(TALLOC_CTX *memctx,
865 : struct sysdb_ctx *ctx,
866 : struct sdap_options *opts,
867 : struct sss_domain_info *dom,
868 : struct sysdb_attrs *attrs,
869 : hash_table_t *ghosts,
870 : time_t now)
871 : {
872 : struct ldb_message_element *el;
873 0 : struct sysdb_attrs *group_attrs = NULL;
874 : const char *group_sid;
875 : const char *group_name;
876 0 : char **userdns = NULL;
877 0 : size_t nuserdns = 0;
878 0 : struct sss_domain_info *group_dom = NULL;
879 : int ret;
880 :
881 0 : if (dom->ignore_group_members) {
882 0 : DEBUG(SSSDBG_CRIT_FAILURE,
883 : "Group members are ignored, nothing to do. If you see this " \
884 : "message it might indicate an error in the group processing " \
885 : "logic.\n");
886 0 : return EOK;
887 : }
888 :
889 0 : ret = sysdb_attrs_get_string(attrs, SYSDB_SID_STR, &group_sid);
890 0 : if (ret != EOK) {
891 : /* Try harder. */
892 0 : ret = sdap_attrs_get_sid_str(memctx, opts->idmap_ctx, attrs,
893 0 : opts->group_map[SDAP_AT_GROUP_OBJECTSID].sys_name,
894 : discard_const(&group_sid));
895 0 : if (ret != EOK) {
896 0 : DEBUG(SSSDBG_TRACE_FUNC, "Failed to get group sid\n");
897 0 : group_sid = NULL;
898 : }
899 : }
900 :
901 0 : if (group_sid != NULL) {
902 0 : group_dom = sss_get_domain_by_sid_ldap_fallback(get_domains_head(dom),
903 : group_sid);
904 0 : if (group_dom == NULL) {
905 0 : DEBUG(SSSDBG_TRACE_FUNC, "SID [%s] does not belong to any known "
906 : "domain, using [%s].\n", group_sid,
907 : dom->name);
908 : }
909 : }
910 :
911 0 : if (group_dom == NULL) {
912 0 : group_dom = dom;
913 : }
914 :
915 0 : ret = sdap_get_group_primary_name(memctx, opts, attrs, group_dom,
916 : &group_name);
917 0 : if (ret != EOK) {
918 0 : DEBUG(SSSDBG_OP_FAILURE, "Failed to get group name\n");
919 0 : goto fail;
920 : }
921 0 : DEBUG(SSSDBG_TRACE_FUNC, "Processing group %s\n", group_name);
922 :
923 : /* With AD we also want to merge in parent groups of primary GID as they
924 : * are reported with tokenGroups, too
925 : */
926 0 : if (opts->schema_type == SDAP_SCHEMA_AD) {
927 0 : ret = sdap_dn_by_primary_gid(memctx, attrs, group_dom, opts,
928 : &userdns, &nuserdns);
929 0 : if (ret != EOK) {
930 0 : DEBUG(SSSDBG_MINOR_FAILURE,
931 : "sdap_dn_by_primary_gid failed: [%d][%s].\n",
932 : ret, strerror(ret));
933 0 : goto fail;
934 : }
935 : }
936 :
937 : /* This is a temporal solution until the IPA provider is able to
938 : * resolve external group membership.
939 : * https://fedorahosted.org/sssd/ticket/2522
940 : */
941 0 : if (opts->schema_type == SDAP_SCHEMA_IPA_V1) {
942 0 : if (group_sid != NULL) {
943 0 : ret = retain_extern_members(memctx, group_dom, group_name,
944 : group_sid, &userdns, &nuserdns);
945 0 : if (ret != EOK) {
946 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
947 : "retain_extern_members failed: %d:[%s].\n",
948 : ret, sss_strerror(ret));
949 : }
950 : }
951 : }
952 :
953 0 : ret = sysdb_attrs_get_el(attrs,
954 0 : opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name, &el);
955 0 : if (ret != EOK) {
956 0 : DEBUG(SSSDBG_MINOR_FAILURE, "sysdb_attrs_get_el failed: [%d][%s].\n",
957 : ret, strerror(ret));
958 0 : goto fail;
959 : }
960 :
961 0 : if (el->num_values == 0 && nuserdns == 0) {
962 0 : DEBUG(SSSDBG_TRACE_FUNC,
963 : "No members for group [%s]\n", group_name);
964 : } else {
965 0 : DEBUG(SSSDBG_TRACE_FUNC,
966 : "Adding member users to group [%s]\n", group_name);
967 :
968 0 : group_attrs = sysdb_new_attrs(memctx);
969 0 : if (!group_attrs) {
970 0 : DEBUG(SSSDBG_MINOR_FAILURE, "sysdb_new_attrs failed\n");
971 0 : ret = ENOMEM;
972 0 : goto fail;
973 : }
974 :
975 0 : ret = sdap_fill_memberships(opts, group_attrs, ctx, group_dom, ghosts,
976 0 : el->values, el->num_values,
977 : userdns, nuserdns);
978 0 : if (ret) {
979 0 : DEBUG(SSSDBG_CRIT_FAILURE,
980 : "sdap_fill_memberships failed with [%d]: %s\n", ret,
981 : strerror(ret));
982 0 : goto fail;
983 : }
984 : }
985 :
986 0 : ret = sysdb_store_group(group_dom, group_name, 0, group_attrs,
987 0 : group_dom->group_timeout, now);
988 0 : if (ret) {
989 0 : DEBUG(SSSDBG_MINOR_FAILURE, "sysdb_store_group failed: [%d][%s].\n",
990 : ret, strerror(ret));
991 0 : goto fail;
992 : }
993 :
994 0 : return EOK;
995 :
996 : fail:
997 0 : DEBUG(SSSDBG_OP_FAILURE,
998 : "Failed to save members of group %s\n", group_name);
999 0 : return ret;
1000 : }
1001 :
1002 :
1003 : /* ==Generic-Function-to-save-multiple-groups============================= */
1004 :
1005 0 : static int sdap_save_groups(TALLOC_CTX *memctx,
1006 : struct sysdb_ctx *sysdb,
1007 : struct sss_domain_info *dom,
1008 : struct sdap_options *opts,
1009 : struct sysdb_attrs **groups,
1010 : int num_groups,
1011 : bool populate_members,
1012 : hash_table_t *ghosts,
1013 : bool save_orig_member,
1014 : char **_usn_value)
1015 : {
1016 : TALLOC_CTX *tmpctx;
1017 0 : char *higher_usn = NULL;
1018 : char *usn_value;
1019 : bool twopass;
1020 0 : bool has_nesting = false;
1021 : int ret;
1022 : errno_t sret;
1023 : int i;
1024 0 : struct sysdb_attrs **saved_groups = NULL;
1025 0 : int nsaved_groups = 0;
1026 : time_t now;
1027 0 : bool in_transaction = false;
1028 :
1029 0 : switch (opts->schema_type) {
1030 : case SDAP_SCHEMA_RFC2307:
1031 0 : twopass = false;
1032 0 : break;
1033 :
1034 : case SDAP_SCHEMA_RFC2307BIS:
1035 : case SDAP_SCHEMA_IPA_V1:
1036 : case SDAP_SCHEMA_AD:
1037 0 : twopass = true;
1038 0 : has_nesting = true;
1039 0 : break;
1040 :
1041 : default:
1042 0 : return EINVAL;
1043 : }
1044 :
1045 0 : tmpctx = talloc_new(memctx);
1046 0 : if (!tmpctx) {
1047 0 : return ENOMEM;
1048 : }
1049 :
1050 0 : ret = sysdb_transaction_start(sysdb);
1051 0 : if (ret) {
1052 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
1053 0 : goto done;
1054 : }
1055 0 : in_transaction = true;
1056 :
1057 0 : if (twopass && !populate_members) {
1058 0 : saved_groups = talloc_array(tmpctx, struct sysdb_attrs *,
1059 : num_groups);
1060 0 : if (!saved_groups) {
1061 0 : ret = ENOMEM;
1062 0 : goto done;
1063 : }
1064 : }
1065 :
1066 0 : now = time(NULL);
1067 0 : for (i = 0; i < num_groups; i++) {
1068 0 : usn_value = NULL;
1069 :
1070 : /* if 2 pass savemembers = false */
1071 0 : ret = sdap_save_group(tmpctx, opts, dom, groups[i],
1072 : populate_members,
1073 0 : has_nesting && save_orig_member,
1074 : ghosts, &usn_value, now);
1075 :
1076 : /* Do not fail completely on errors.
1077 : * Just report the failure to save and go on */
1078 0 : if (ret) {
1079 0 : DEBUG(SSSDBG_OP_FAILURE,
1080 : "Failed to store group %d. Ignoring.\n", i);
1081 : } else {
1082 0 : DEBUG(SSSDBG_TRACE_ALL, "Group %d processed!\n", i);
1083 0 : if (twopass && !populate_members) {
1084 0 : saved_groups[nsaved_groups] = groups[i];
1085 0 : nsaved_groups++;
1086 : }
1087 : }
1088 :
1089 0 : if (usn_value) {
1090 0 : if (higher_usn) {
1091 0 : if ((strlen(usn_value) > strlen(higher_usn)) ||
1092 0 : (strcmp(usn_value, higher_usn) > 0)) {
1093 0 : talloc_zfree(higher_usn);
1094 0 : higher_usn = usn_value;
1095 : } else {
1096 0 : talloc_zfree(usn_value);
1097 : }
1098 : } else {
1099 0 : higher_usn = usn_value;
1100 : }
1101 : }
1102 : }
1103 :
1104 0 : if (twopass && !populate_members) {
1105 :
1106 0 : for (i = 0; i < nsaved_groups; i++) {
1107 :
1108 0 : ret = sdap_save_grpmem(tmpctx, sysdb, opts, dom, saved_groups[i],
1109 : ghosts, now);
1110 : /* Do not fail completely on errors.
1111 : * Just report the failure to save and go on */
1112 0 : if (ret) {
1113 0 : DEBUG(SSSDBG_OP_FAILURE,
1114 : "Failed to store group %d members.\n", i);
1115 : } else {
1116 0 : DEBUG(SSSDBG_TRACE_ALL, "Group %d members processed!\n", i);
1117 : }
1118 : }
1119 : }
1120 :
1121 0 : ret = sysdb_transaction_commit(sysdb);
1122 0 : if (ret) {
1123 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction!\n");
1124 0 : goto done;
1125 : }
1126 0 : in_transaction = false;
1127 :
1128 0 : if (_usn_value) {
1129 0 : *_usn_value = talloc_steal(memctx, higher_usn);
1130 : }
1131 :
1132 : done:
1133 0 : if (in_transaction) {
1134 0 : sret = sysdb_transaction_cancel(sysdb);
1135 0 : if (sret != EOK) {
1136 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to cancel transaction\n");
1137 : }
1138 : }
1139 0 : talloc_zfree(tmpctx);
1140 0 : return ret;
1141 : }
1142 :
1143 :
1144 : /* ==Process-Groups======================================================= */
1145 :
1146 : struct sdap_process_group_state {
1147 : struct tevent_context *ev;
1148 : struct sdap_options *opts;
1149 : struct sdap_handle *sh;
1150 : struct sss_domain_info *dom;
1151 : struct sysdb_ctx *sysdb;
1152 :
1153 : struct sysdb_attrs *group;
1154 : struct ldb_message_element* sysdb_dns;
1155 : struct ldb_message_element* ghost_dns;
1156 : char **queued_members;
1157 : int queue_len;
1158 : const char **attrs;
1159 : const char *filter;
1160 : size_t queue_idx;
1161 : size_t count;
1162 : size_t check_count;
1163 :
1164 : bool enumeration;
1165 : };
1166 :
1167 : #define GROUPMEMBER_REQ_PARALLEL 50
1168 : static void sdap_process_group_members(struct tevent_req *subreq);
1169 :
1170 : static int sdap_process_group_members_2307bis(struct tevent_req *req,
1171 : struct sdap_process_group_state *state,
1172 : struct ldb_message_element *memberel);
1173 : static int sdap_process_group_members_2307(struct sdap_process_group_state *state,
1174 : struct ldb_message_element *memberel,
1175 : struct ldb_message_element *ghostel);
1176 :
1177 0 : static errno_t sdap_process_group_create_dns(TALLOC_CTX *mem_ctx,
1178 : size_t num_values,
1179 : struct ldb_message_element **_dns)
1180 : {
1181 : struct ldb_message_element *dns;
1182 :
1183 0 : dns = talloc(mem_ctx, struct ldb_message_element);
1184 0 : if (dns == NULL) {
1185 0 : return ENOMEM;
1186 : }
1187 :
1188 0 : dns->num_values = 0;
1189 0 : dns->values = talloc_array(dns, struct ldb_val,
1190 : num_values);
1191 0 : if (dns->values == NULL) {
1192 0 : talloc_zfree(dns);
1193 0 : return ENOMEM;
1194 : }
1195 :
1196 0 : *_dns = dns;
1197 :
1198 0 : return EOK;
1199 : }
1200 :
1201 : static struct tevent_req *
1202 0 : sdap_process_group_send(TALLOC_CTX *memctx,
1203 : struct tevent_context *ev,
1204 : struct sss_domain_info *dom,
1205 : struct sysdb_ctx *sysdb,
1206 : struct sdap_options *opts,
1207 : struct sdap_handle *sh,
1208 : struct sysdb_attrs *group,
1209 : bool enumeration)
1210 : {
1211 : struct ldb_message_element *el;
1212 : struct ldb_message_element *ghostel;
1213 : struct sdap_process_group_state *grp_state;
1214 0 : struct tevent_req *req = NULL;
1215 : const char **attrs;
1216 : char* filter;
1217 : int ret;
1218 :
1219 0 : req = tevent_req_create(memctx, &grp_state,
1220 : struct sdap_process_group_state);
1221 0 : if (!req) return NULL;
1222 :
1223 0 : ret = build_attrs_from_map(grp_state, opts->user_map, opts->user_map_cnt,
1224 : NULL, &attrs, NULL);
1225 0 : if (ret) {
1226 0 : goto done;
1227 : }
1228 :
1229 : /* FIXME: we ignore nested rfc2307bis groups for now */
1230 0 : filter = talloc_asprintf(grp_state, "(objectclass=%s)",
1231 0 : opts->user_map[SDAP_OC_USER].name);
1232 0 : if (!filter) {
1233 0 : talloc_zfree(req);
1234 0 : return NULL;
1235 : }
1236 :
1237 0 : grp_state->ev = ev;
1238 0 : grp_state->opts = opts;
1239 0 : grp_state->dom = dom;
1240 0 : grp_state->sh = sh;
1241 0 : grp_state->sysdb = sysdb;
1242 0 : grp_state->group = group;
1243 0 : grp_state->check_count = 0;
1244 0 : grp_state->queue_idx = 0;
1245 0 : grp_state->queued_members = NULL;
1246 0 : grp_state->queue_len = 0;
1247 0 : grp_state->filter = filter;
1248 0 : grp_state->attrs = attrs;
1249 0 : grp_state->enumeration = enumeration;
1250 :
1251 0 : ret = sysdb_attrs_get_el(group,
1252 0 : opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name,
1253 : &el);
1254 0 : if (ret) {
1255 0 : goto done;
1256 : }
1257 :
1258 : /* Group without members */
1259 0 : if (el->num_values == 0) {
1260 0 : DEBUG(SSSDBG_OP_FAILURE, "No Members. Done!\n");
1261 0 : ret = EOK;
1262 0 : goto done;
1263 : }
1264 :
1265 0 : ret = sysdb_attrs_get_el(group,
1266 : SYSDB_GHOST,
1267 : &ghostel);
1268 0 : if (ret) {
1269 0 : goto done;
1270 : }
1271 :
1272 0 : if (ghostel->num_values == 0) {
1273 : /* Element was probably newly created, look for "member" again */
1274 0 : ret = sysdb_attrs_get_el(group,
1275 0 : opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name,
1276 : &el);
1277 0 : if (ret != EOK) {
1278 0 : goto done;
1279 : }
1280 : }
1281 :
1282 :
1283 0 : ret = sdap_process_group_create_dns(grp_state, el->num_values,
1284 0 : &grp_state->sysdb_dns);
1285 0 : if (ret != EOK) {
1286 0 : goto done;
1287 : }
1288 :
1289 0 : ret = sdap_process_group_create_dns(grp_state, el->num_values,
1290 0 : &grp_state->ghost_dns);
1291 0 : if (ret != EOK) {
1292 0 : goto done;
1293 : }
1294 :
1295 0 : switch (opts->schema_type) {
1296 : case SDAP_SCHEMA_RFC2307:
1297 0 : ret = sdap_process_group_members_2307(grp_state, el, ghostel);
1298 0 : break;
1299 :
1300 : case SDAP_SCHEMA_IPA_V1:
1301 : case SDAP_SCHEMA_AD:
1302 : case SDAP_SCHEMA_RFC2307BIS:
1303 : /* Note that this code branch will be used only if
1304 : * ldap_nesting_level = 0 is set in config file
1305 : */
1306 0 : ret = sdap_process_group_members_2307bis(req, grp_state, el);
1307 0 : break;
1308 :
1309 : default:
1310 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1311 : "Unknown schema type %d\n", opts->schema_type);
1312 0 : ret = EINVAL;
1313 0 : break;
1314 : }
1315 :
1316 : done:
1317 : /* We managed to process all the entries */
1318 : /* EBUSY means we need to wait for entries in LDAP */
1319 0 : if (ret == EOK) {
1320 0 : DEBUG(SSSDBG_TRACE_LIBS, "All group members processed\n");
1321 0 : tevent_req_done(req);
1322 0 : tevent_req_post(req, ev);
1323 : }
1324 :
1325 0 : if (ret != EOK && ret != EBUSY) {
1326 0 : tevent_req_error(req, ret);
1327 0 : tevent_req_post(req, ev);
1328 : }
1329 0 : return req;
1330 : }
1331 :
1332 : static int
1333 0 : sdap_process_missing_member_2307bis(struct tevent_req *req,
1334 : char *user_dn,
1335 : unsigned num_users)
1336 : {
1337 0 : struct sdap_process_group_state *grp_state =
1338 0 : tevent_req_data(req, struct sdap_process_group_state);
1339 : struct tevent_req *subreq;
1340 :
1341 : /*
1342 : * Issue at most GROUPMEMBER_REQ_PARALLEL LDAP searches at once.
1343 : * The rest is sent while the results are being processed.
1344 : * We limit the number as of request here, as the Server might
1345 : * enforce limits on the number of pending operations per
1346 : * connection.
1347 : */
1348 0 : if (grp_state->check_count > GROUPMEMBER_REQ_PARALLEL) {
1349 0 : DEBUG(SSSDBG_TRACE_LIBS, " queueing search for: %s\n", user_dn);
1350 0 : if (!grp_state->queued_members) {
1351 0 : DEBUG(SSSDBG_TRACE_LIBS,
1352 : "Allocating queue for %zu members\n",
1353 : num_users - grp_state->check_count);
1354 :
1355 0 : grp_state->queued_members = talloc_array(grp_state, char *,
1356 : num_users - grp_state->check_count + 1);
1357 0 : if (!grp_state->queued_members) {
1358 0 : return ENOMEM;
1359 : }
1360 : }
1361 0 : grp_state->queued_members[grp_state->queue_len] = user_dn;
1362 0 : grp_state->queue_len++;
1363 : } else {
1364 0 : subreq = sdap_get_generic_send(grp_state,
1365 : grp_state->ev,
1366 : grp_state->opts,
1367 : grp_state->sh,
1368 : user_dn,
1369 : LDAP_SCOPE_BASE,
1370 : grp_state->filter,
1371 : grp_state->attrs,
1372 0 : grp_state->opts->user_map,
1373 0 : grp_state->opts->user_map_cnt,
1374 0 : dp_opt_get_int(grp_state->opts->basic,
1375 : SDAP_SEARCH_TIMEOUT),
1376 : false);
1377 0 : if (!subreq) {
1378 0 : return ENOMEM;
1379 : }
1380 0 : tevent_req_set_callback(subreq, sdap_process_group_members, req);
1381 : }
1382 :
1383 0 : grp_state->check_count++;
1384 0 : return EOK;
1385 : }
1386 :
1387 : static int
1388 0 : sdap_process_group_members_2307bis(struct tevent_req *req,
1389 : struct sdap_process_group_state *state,
1390 : struct ldb_message_element *memberel)
1391 : {
1392 : char *member_dn;
1393 : char *strdn;
1394 : int ret;
1395 : int i;
1396 : int nesting_level;
1397 : bool is_group;
1398 :
1399 0 : nesting_level = dp_opt_get_int(state->opts->basic, SDAP_NESTING_LEVEL);
1400 :
1401 0 : for (i=0; i < memberel->num_values; i++) {
1402 0 : member_dn = (char *)memberel->values[i].data;
1403 :
1404 0 : ret = sdap_find_entry_by_origDN(state->sysdb_dns->values,
1405 : state->sysdb,
1406 : state->dom,
1407 : member_dn,
1408 : &strdn,
1409 : &is_group);
1410 :
1411 0 : if (ret == EOK) {
1412 0 : if (nesting_level == 0 && is_group) {
1413 : /* Ignore group members which are groups themselves. */
1414 0 : continue;
1415 : }
1416 :
1417 : /*
1418 : * User already cached in sysdb. Remember the sysdb DN for later
1419 : * use by sdap_save_groups()
1420 : */
1421 0 : DEBUG(SSSDBG_TRACE_LIBS, "sysdbdn: %s\n", strdn);
1422 0 : state->sysdb_dns->values[state->sysdb_dns->num_values].data =
1423 : (uint8_t*) strdn;
1424 0 : state->sysdb_dns->values[state->sysdb_dns->num_values].length =
1425 0 : strlen(strdn);
1426 0 : state->sysdb_dns->num_values++;
1427 0 : } else if (ret == ENOENT) {
1428 0 : if (!state->enumeration) {
1429 : /* The user is not in sysdb, need to add it
1430 : * We don't need to do this if we're in an enumeration,
1431 : * because all real members should all be populated
1432 : * already by the first pass of the enumeration.
1433 : * Also, we don't want to be holding the sysdb
1434 : * transaction while we're performing LDAP lookups.
1435 : */
1436 0 : DEBUG(SSSDBG_TRACE_LIBS,
1437 : "Searching LDAP for missing user entry\n");
1438 0 : ret = sdap_process_missing_member_2307bis(req,
1439 : member_dn,
1440 : memberel->num_values);
1441 0 : if (ret != EOK) {
1442 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1443 : "Error processing missing member #%d (%s):\n",
1444 : i, member_dn);
1445 0 : return ret;
1446 : }
1447 : }
1448 : } else {
1449 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1450 : "Error checking cache for member #%d (%s):\n",
1451 : i, (char *)memberel->values[i].data);
1452 0 : return ret;
1453 : }
1454 : }
1455 :
1456 0 : if (state->queue_len > 0) {
1457 0 : state->queued_members[state->queue_len]=NULL;
1458 : }
1459 :
1460 0 : if (state->check_count == 0) {
1461 : /*
1462 : * All group members are already cached in sysdb, we are done
1463 : * with this group. To avoid redundant sysdb lookups, populate the
1464 : * "member" attribute of the group entry with the sysdb DNs of
1465 : * the members.
1466 : */
1467 0 : ret = EOK;
1468 0 : memberel->values = talloc_steal(state->group, state->sysdb_dns->values);
1469 0 : memberel->num_values = state->sysdb_dns->num_values;
1470 : } else {
1471 0 : state->count = state->check_count;
1472 0 : ret = EBUSY;
1473 : }
1474 :
1475 0 : return ret;
1476 : }
1477 :
1478 : static int
1479 0 : sdap_add_group_member_2307(struct ldb_message_element *sysdb_dns,
1480 : const char *username)
1481 : {
1482 0 : sysdb_dns->values[sysdb_dns->num_values].data =
1483 0 : (uint8_t *) talloc_strdup(sysdb_dns->values, username);
1484 0 : if (sysdb_dns->values[sysdb_dns->num_values].data == NULL) {
1485 0 : return ENOMEM;
1486 : }
1487 0 : sysdb_dns->values[sysdb_dns->num_values].length =
1488 0 : strlen(username);
1489 0 : sysdb_dns->num_values++;
1490 :
1491 0 : return EOK;
1492 : }
1493 :
1494 : static int
1495 0 : sdap_process_missing_member_2307(struct sdap_process_group_state *state,
1496 : char *member_name)
1497 : {
1498 : int ret;
1499 : TALLOC_CTX *tmp_ctx;
1500 : const char *filter;
1501 : const char *username;
1502 : const char *user_dn;
1503 : size_t count;
1504 0 : struct ldb_message **msgs = NULL;
1505 : static const char *attrs[] = { SYSDB_NAME, NULL };
1506 :
1507 0 : tmp_ctx = talloc_new(NULL);
1508 0 : if (!tmp_ctx) return ENOMEM;
1509 :
1510 : /* Check for the alias in the sysdb */
1511 0 : filter = talloc_asprintf(tmp_ctx, "(%s=%s)", SYSDB_NAME_ALIAS, member_name);
1512 0 : if (!filter) {
1513 0 : ret = ENOMEM;
1514 0 : goto done;
1515 : }
1516 :
1517 0 : ret = sysdb_search_users(tmp_ctx, state->dom, filter,
1518 : attrs, &count, &msgs);
1519 0 : if (ret == EOK && count > 0) {
1520 : /* Entry exists but the group references it with an alias. */
1521 :
1522 0 : if (count != 1) {
1523 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1524 : "More than one entry with this alias?\n");
1525 0 : ret = EIO;
1526 0 : goto done;
1527 : }
1528 :
1529 : /* fill username with primary name */
1530 0 : username = ldb_msg_find_attr_as_string(msgs[0], SYSDB_NAME, NULL);
1531 0 : if (username == NULL) {
1532 0 : ret = EINVAL;
1533 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Inconsistent sysdb: user "
1534 : "without primary name?\n");
1535 0 : goto done;
1536 : }
1537 0 : user_dn = sysdb_user_strdn(tmp_ctx, state->dom->name, username);
1538 0 : if (user_dn == NULL) {
1539 0 : return ENOMEM;
1540 : }
1541 :
1542 0 : ret = sdap_add_group_member_2307(state->sysdb_dns, user_dn);
1543 0 : if (ret != EOK) {
1544 0 : DEBUG(SSSDBG_OP_FAILURE, "Could not add group member %s\n", username);
1545 : }
1546 0 : } else if (ret == ENOENT || count == 0) {
1547 : /* The entry really does not exist, add a ghost */
1548 0 : DEBUG(SSSDBG_TRACE_FUNC, "Adding a ghost entry\n");
1549 0 : ret = sdap_add_group_member_2307(state->ghost_dns, member_name);
1550 0 : if (ret != EOK) {
1551 0 : DEBUG(SSSDBG_OP_FAILURE, "Could not add group member %s\n", member_name);
1552 : }
1553 : } else {
1554 0 : ret = EIO;
1555 : }
1556 :
1557 : done:
1558 0 : talloc_free(tmp_ctx);
1559 0 : return ret;
1560 : }
1561 :
1562 : static int
1563 0 : sdap_process_group_members_2307(struct sdap_process_group_state *state,
1564 : struct ldb_message_element *memberel,
1565 : struct ldb_message_element *ghostel)
1566 : {
1567 : struct ldb_message *msg;
1568 : char *member_name;
1569 : char *userdn;
1570 : int ret;
1571 : int i;
1572 :
1573 0 : for (i=0; i < memberel->num_values; i++) {
1574 0 : member_name = (char *)memberel->values[i].data;
1575 :
1576 : /* We need to skip over zero-length usernames */
1577 0 : if (member_name[0] == '\0') continue;
1578 :
1579 0 : ret = sysdb_search_user_by_name(state, state->dom, member_name,
1580 : NULL, &msg);
1581 0 : if (ret == EOK) {
1582 : /*
1583 : * User already cached in sysdb. Remember the sysdb DN for later
1584 : * use by sdap_save_groups()
1585 : */
1586 0 : DEBUG(SSSDBG_TRACE_LIBS,
1587 : "Member already cached in sysdb: %s\n", member_name);
1588 :
1589 0 : userdn = sysdb_user_strdn(state->sysdb_dns, state->dom->name, member_name);
1590 0 : if (userdn == NULL) {
1591 0 : return ENOMEM;
1592 : }
1593 :
1594 0 : ret = sdap_add_group_member_2307(state->sysdb_dns, userdn);
1595 0 : if (ret != EOK) {
1596 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1597 : "Could not add member %s into sysdb\n", member_name);
1598 0 : goto done;
1599 : }
1600 0 : } else if (ret == ENOENT) {
1601 : /* The user is not in sysdb, need to add it */
1602 0 : DEBUG(SSSDBG_TRACE_LIBS, "member #%d (%s): not found in sysdb\n",
1603 : i, member_name);
1604 :
1605 0 : ret = sdap_process_missing_member_2307(state, member_name);
1606 0 : if (ret != EOK) {
1607 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1608 : "Error processing missing member #%d (%s):\n",
1609 : i, member_name);
1610 0 : goto done;
1611 : }
1612 : } else {
1613 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1614 : "Error checking cache for member #%d (%s):\n",
1615 : i, (char *) memberel->values[i].data);
1616 0 : goto done;
1617 : }
1618 : }
1619 :
1620 0 : ret = EOK;
1621 0 : talloc_free(memberel->values);
1622 0 : memberel->values = talloc_steal(state->group, state->sysdb_dns->values);
1623 0 : memberel->num_values = state->sysdb_dns->num_values;
1624 0 : talloc_free(ghostel->values);
1625 0 : ghostel->values = talloc_steal(state->group, state->ghost_dns->values);
1626 0 : ghostel->num_values = state->ghost_dns->num_values;
1627 :
1628 : done:
1629 0 : return ret;
1630 : }
1631 :
1632 0 : static void sdap_process_group_members(struct tevent_req *subreq)
1633 : {
1634 : struct sysdb_attrs **usr_attrs;
1635 : size_t count;
1636 : int ret;
1637 0 : struct tevent_req *req =
1638 0 : tevent_req_callback_data(subreq, struct tevent_req);
1639 0 : struct sdap_process_group_state *state =
1640 0 : tevent_req_data(req, struct sdap_process_group_state);
1641 : struct ldb_message_element *el;
1642 : uint8_t* name_string;
1643 :
1644 0 : state->check_count--;
1645 0 : DEBUG(SSSDBG_TRACE_ALL, "Members remaining: %zu\n", state->check_count);
1646 :
1647 0 : ret = sdap_get_generic_recv(subreq, state, &count, &usr_attrs);
1648 0 : talloc_zfree(subreq);
1649 0 : if (ret) {
1650 0 : goto next;
1651 : }
1652 0 : if (count != 1) {
1653 0 : ret = EINVAL;
1654 0 : DEBUG(SSSDBG_TRACE_LIBS,
1655 : "Expected one user entry and got %zu\n", count);
1656 0 : goto next;
1657 : }
1658 0 : ret = sysdb_attrs_get_el(usr_attrs[0],
1659 0 : state->opts->user_map[SDAP_AT_USER_NAME].sys_name, &el);
1660 0 : if (el->num_values == 0) {
1661 0 : ret = EINVAL;
1662 : }
1663 0 : if (ret) {
1664 0 : DEBUG(SSSDBG_OP_FAILURE, "Failed to get the member's name\n");
1665 0 : goto next;
1666 : }
1667 :
1668 0 : name_string = el[0].values[0].data;
1669 0 : state->ghost_dns->values[state->ghost_dns->num_values].data =
1670 0 : talloc_steal(state->ghost_dns->values, name_string);
1671 0 : state->ghost_dns->values[state->ghost_dns->num_values].length =
1672 0 : strlen((char *)name_string);
1673 0 : state->ghost_dns->num_values++;
1674 :
1675 : next:
1676 0 : if (ret) {
1677 0 : DEBUG(SSSDBG_TRACE_FUNC,
1678 : "Error reading group member[%d]: %s. Skipping\n",
1679 : ret, strerror(ret));
1680 0 : state->count--;
1681 : }
1682 : /* Are there more searches for uncached users to submit ? */
1683 0 : if (state->queued_members && state->queued_members[state->queue_idx]) {
1684 0 : subreq = sdap_get_generic_send(state,
1685 : state->ev, state->opts, state->sh,
1686 0 : state->queued_members[state->queue_idx],
1687 : LDAP_SCOPE_BASE,
1688 : state->filter,
1689 : state->attrs,
1690 0 : state->opts->user_map,
1691 0 : state->opts->user_map_cnt,
1692 0 : dp_opt_get_int(state->opts->basic,
1693 : SDAP_SEARCH_TIMEOUT),
1694 : false);
1695 0 : if (!subreq) {
1696 0 : tevent_req_error(req, ENOMEM);
1697 0 : return;
1698 : }
1699 :
1700 0 : tevent_req_set_callback(subreq,
1701 : sdap_process_group_members, req);
1702 0 : state->queue_idx++;
1703 : }
1704 :
1705 0 : if (state->check_count == 0) {
1706 : /*
1707 : * To avoid redundant sysdb lookups, populate the "member" attribute
1708 : * of the group entry with the sysdb DNs of the members.
1709 : */
1710 0 : ret = sysdb_attrs_get_el(state->group,
1711 0 : state->opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name,
1712 : &el);
1713 0 : if (ret != EOK) {
1714 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1715 : "Failed to get the group member attribute [%d]: %s\n",
1716 : ret, strerror(ret));
1717 0 : tevent_req_error(req, ret);
1718 0 : return;
1719 : }
1720 0 : el->values = talloc_steal(state->group, state->sysdb_dns->values);
1721 0 : el->num_values = state->sysdb_dns->num_values;
1722 :
1723 0 : ret = sysdb_attrs_get_el(state->group, SYSDB_GHOST, &el);
1724 0 : if (ret != EOK) {
1725 0 : tevent_req_error(req, ret);
1726 0 : return;
1727 : }
1728 0 : el->values = talloc_steal(state->group, state->ghost_dns->values);
1729 0 : el->num_values = state->ghost_dns->num_values;
1730 0 : DEBUG(SSSDBG_TRACE_ALL, "Processed Group - Done\n");
1731 0 : tevent_req_done(req);
1732 : }
1733 : }
1734 :
1735 0 : static int sdap_process_group_recv(struct tevent_req *req)
1736 : {
1737 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
1738 :
1739 0 : return EOK;
1740 : }
1741 :
1742 :
1743 : /* ==Search-Groups-with-filter============================================ */
1744 :
1745 : struct sdap_get_groups_state {
1746 : struct tevent_context *ev;
1747 : struct sdap_options *opts;
1748 : struct sdap_handle *sh;
1749 : struct sss_domain_info *dom;
1750 : struct sdap_domain *sdom;
1751 : struct sysdb_ctx *sysdb;
1752 : const char **attrs;
1753 : const char *base_filter;
1754 : char *filter;
1755 : int timeout;
1756 : enum sdap_entry_lookup_type lookup_type;
1757 : bool no_members;
1758 :
1759 : char *higher_usn;
1760 : struct sysdb_attrs **groups;
1761 : size_t count;
1762 : size_t check_count;
1763 : hash_table_t *missing_external;
1764 :
1765 : hash_table_t *user_hash;
1766 : hash_table_t *group_hash;
1767 :
1768 : size_t base_iter;
1769 : struct sdap_search_base **search_bases;
1770 :
1771 : struct sdap_handle *ldap_sh;
1772 : struct sdap_id_op *op;
1773 : };
1774 :
1775 : static errno_t sdap_get_groups_next_base(struct tevent_req *req);
1776 : static void sdap_get_groups_ldap_connect_done(struct tevent_req *subreq);
1777 : static void sdap_get_groups_process(struct tevent_req *subreq);
1778 : static void sdap_get_groups_done(struct tevent_req *subreq);
1779 :
1780 0 : struct tevent_req *sdap_get_groups_send(TALLOC_CTX *memctx,
1781 : struct tevent_context *ev,
1782 : struct sdap_domain *sdom,
1783 : struct sdap_options *opts,
1784 : struct sdap_handle *sh,
1785 : const char **attrs,
1786 : const char *filter,
1787 : int timeout,
1788 : enum sdap_entry_lookup_type lookup_type,
1789 : bool no_members)
1790 : {
1791 : errno_t ret;
1792 : struct tevent_req *req;
1793 : struct tevent_req *subreq;
1794 : struct sdap_get_groups_state *state;
1795 : struct ad_id_ctx *subdom_id_ctx;
1796 :
1797 0 : req = tevent_req_create(memctx, &state, struct sdap_get_groups_state);
1798 0 : if (!req) return NULL;
1799 :
1800 0 : state->ev = ev;
1801 0 : state->opts = opts;
1802 0 : state->sdom = sdom;
1803 0 : state->dom = sdom->dom;
1804 0 : state->sh = sh;
1805 0 : state->sysdb = sdom->dom->sysdb;
1806 0 : state->attrs = attrs;
1807 0 : state->higher_usn = NULL;
1808 0 : state->groups = NULL;
1809 0 : state->count = 0;
1810 0 : state->timeout = timeout;
1811 0 : state->lookup_type = lookup_type;
1812 0 : state->no_members = no_members;
1813 0 : state->base_filter = filter;
1814 0 : state->base_iter = 0;
1815 0 : state->search_bases = sdom->group_search_bases;
1816 :
1817 0 : if (!state->search_bases) {
1818 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1819 : "Group lookup request without a search base\n");
1820 0 : ret = EINVAL;
1821 0 : goto done;
1822 : }
1823 :
1824 : /* With AD by default the Global Catalog is used for lookup. But the GC
1825 : * group object might not have full group membership data. To make sure we
1826 : * connect to an LDAP server of the group's domain. */
1827 0 : if (state->opts->schema_type == SDAP_SCHEMA_AD && sdom->pvt != NULL) {
1828 0 : subdom_id_ctx = talloc_get_type(sdom->pvt, struct ad_id_ctx);
1829 0 : state->op = sdap_id_op_create(state, subdom_id_ctx->ldap_ctx->conn_cache);
1830 0 : if (!state->op) {
1831 0 : DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create failed\n");
1832 0 : ret = ENOMEM;
1833 0 : goto done;
1834 : }
1835 :
1836 0 : subreq = sdap_id_op_connect_send(state->op, state, &ret);
1837 0 : if (subreq == NULL) {
1838 0 : ret = ENOMEM;
1839 0 : goto done;
1840 : }
1841 :
1842 0 : tevent_req_set_callback(subreq,
1843 : sdap_get_groups_ldap_connect_done,
1844 : req);
1845 0 : return req;
1846 : }
1847 :
1848 0 : ret = sdap_get_groups_next_base(req);
1849 :
1850 : done:
1851 0 : if (ret != EOK) {
1852 0 : tevent_req_error(req, ret);
1853 0 : tevent_req_post(req, ev);
1854 : }
1855 :
1856 0 : return req;
1857 : }
1858 :
1859 0 : static void sdap_get_groups_ldap_connect_done(struct tevent_req *subreq)
1860 : {
1861 : struct tevent_req *req;
1862 : struct sdap_get_groups_state *state;
1863 : int ret;
1864 : int dp_error;
1865 :
1866 0 : req = tevent_req_callback_data(subreq, struct tevent_req);
1867 0 : state = tevent_req_data(req, struct sdap_get_groups_state);
1868 :
1869 0 : ret = sdap_id_op_connect_recv(subreq, &dp_error);
1870 0 : talloc_zfree(subreq);
1871 :
1872 0 : if (ret != EOK) {
1873 0 : tevent_req_error(req, ret);
1874 0 : return;
1875 : }
1876 :
1877 0 : state->ldap_sh = sdap_id_op_handle(state->op);
1878 :
1879 0 : ret = sdap_get_groups_next_base(req);
1880 0 : if (ret != EOK) {
1881 0 : tevent_req_error(req, ret);
1882 : }
1883 :
1884 0 : return;
1885 : }
1886 :
1887 0 : static errno_t sdap_get_groups_next_base(struct tevent_req *req)
1888 : {
1889 : struct tevent_req *subreq;
1890 : struct sdap_get_groups_state *state;
1891 0 : bool need_paging = false;
1892 0 : int sizelimit = 0;
1893 :
1894 0 : state = tevent_req_data(req, struct sdap_get_groups_state);
1895 :
1896 0 : talloc_zfree(state->filter);
1897 0 : state->filter = sdap_combine_filters(state, state->base_filter,
1898 0 : state->search_bases[state->base_iter]->filter);
1899 0 : if (!state->filter) {
1900 0 : return ENOMEM;
1901 : }
1902 :
1903 0 : DEBUG(SSSDBG_TRACE_FUNC,
1904 : "Searching for groups with base [%s]\n",
1905 : state->search_bases[state->base_iter]->basedn);
1906 :
1907 0 : switch (state->lookup_type) {
1908 : case SDAP_LOOKUP_SINGLE:
1909 0 : break;
1910 : /* Only requests that can return multiple entries should require
1911 : * the paging control
1912 : */
1913 : case SDAP_LOOKUP_WILDCARD:
1914 0 : sizelimit = dp_opt_get_int(state->opts->basic, SDAP_WILDCARD_LIMIT);
1915 0 : need_paging = true;
1916 0 : break;
1917 : case SDAP_LOOKUP_ENUMERATE:
1918 0 : need_paging = true;
1919 0 : break;
1920 : }
1921 :
1922 0 : subreq = sdap_get_and_parse_generic_send(
1923 : state, state->ev, state->opts,
1924 0 : state->ldap_sh != NULL ? state->ldap_sh : state->sh,
1925 0 : state->search_bases[state->base_iter]->basedn,
1926 0 : state->search_bases[state->base_iter]->scope,
1927 0 : state->filter, state->attrs,
1928 0 : state->opts->group_map, SDAP_OPTS_GROUP,
1929 : 0, NULL, NULL, sizelimit, state->timeout,
1930 : need_paging);
1931 0 : if (!subreq) {
1932 0 : return ENOMEM;
1933 : }
1934 0 : tevent_req_set_callback(subreq, sdap_get_groups_process, req);
1935 :
1936 0 : return EOK;
1937 : }
1938 :
1939 : static void sdap_nested_done(struct tevent_req *req);
1940 : static void sdap_search_group_copy_batch(struct sdap_get_groups_state *state,
1941 : struct sysdb_attrs **groups,
1942 : size_t count);
1943 : static void sdap_ad_match_rule_members_process(struct tevent_req *subreq);
1944 :
1945 0 : static void sdap_get_groups_process(struct tevent_req *subreq)
1946 : {
1947 0 : struct tevent_req *req =
1948 0 : tevent_req_callback_data(subreq, struct tevent_req);
1949 0 : struct sdap_get_groups_state *state =
1950 0 : tevent_req_data(req, struct sdap_get_groups_state);
1951 : int ret;
1952 : int i;
1953 0 : bool next_base = false;
1954 : size_t count;
1955 : struct sysdb_attrs **groups;
1956 : char **groupnamelist;
1957 :
1958 0 : ret = sdap_get_and_parse_generic_recv(subreq, state,
1959 : &count, &groups);
1960 0 : talloc_zfree(subreq);
1961 0 : if (ret) {
1962 0 : tevent_req_error(req, ret);
1963 0 : return;
1964 : }
1965 :
1966 0 : DEBUG(SSSDBG_TRACE_FUNC,
1967 : "Search for groups, returned %zu results.\n", count);
1968 :
1969 0 : if (state->lookup_type == SDAP_LOOKUP_WILDCARD || \
1970 0 : state->lookup_type == SDAP_LOOKUP_ENUMERATE || \
1971 0 : count == 0) {
1972 : /* No users found in this search or looking up multiple entries */
1973 0 : next_base = true;
1974 : }
1975 :
1976 : /* Add this batch of groups to the list */
1977 0 : if (count > 0) {
1978 0 : state->groups =
1979 0 : talloc_realloc(state,
1980 : state->groups,
1981 : struct sysdb_attrs *,
1982 : state->count + count + 1);
1983 0 : if (!state->groups) {
1984 0 : tevent_req_error(req, ENOMEM);
1985 0 : return;
1986 : }
1987 :
1988 0 : sdap_search_group_copy_batch(state, groups, count);
1989 : }
1990 :
1991 0 : if (next_base) {
1992 0 : state->base_iter++;
1993 0 : if (state->search_bases[state->base_iter]) {
1994 : /* There are more search bases to try */
1995 0 : ret = sdap_get_groups_next_base(req);
1996 0 : if (ret != EOK) {
1997 0 : tevent_req_error(req, ret);
1998 : }
1999 0 : return;
2000 : }
2001 : }
2002 :
2003 : /* No more search bases
2004 : * Return ENOENT if no groups were found
2005 : */
2006 0 : if (state->count == 0) {
2007 0 : tevent_req_error(req, ENOENT);
2008 0 : return;
2009 : }
2010 :
2011 0 : if (state->no_members) {
2012 0 : ret = sysdb_attrs_primary_name_list(state->sysdb, state,
2013 : state->groups, state->count,
2014 0 : state->opts->group_map[SDAP_AT_GROUP_NAME].name,
2015 : &groupnamelist);
2016 0 : if (ret != EOK) {
2017 0 : DEBUG(SSSDBG_OP_FAILURE,
2018 : "sysdb_attrs_primary_name_list failed.\n");
2019 0 : tevent_req_error(req, ret);
2020 0 : return;
2021 : }
2022 :
2023 0 : ret = sdap_add_incomplete_groups(state->sysdb, state->dom, state->opts,
2024 : groupnamelist, state->groups,
2025 0 : state->count);
2026 0 : if (ret == EOK) {
2027 0 : DEBUG(SSSDBG_TRACE_LIBS,
2028 : "Writing only group data without members was successful.\n");
2029 0 : tevent_req_done(req);
2030 : } else {
2031 0 : DEBUG(SSSDBG_OP_FAILURE, "sdap_add_incomplete_groups failed.\n");
2032 0 : tevent_req_error(req, ret);
2033 : }
2034 0 : return;
2035 : }
2036 :
2037 : /* Check whether we need to do nested searches
2038 : * for RFC2307bis/FreeIPA/ActiveDirectory
2039 : * We don't need to do this for enumeration,
2040 : * because all groups will be picked up anyway.
2041 : *
2042 : * We can also skip this if we're using the
2043 : * LDAP_MATCHING_RULE_IN_CHAIN available in
2044 : * AD 2008 and later
2045 : */
2046 0 : if (state->lookup_type == SDAP_LOOKUP_SINGLE) {
2047 0 : if ((state->opts->schema_type != SDAP_SCHEMA_RFC2307)
2048 0 : && (dp_opt_get_int(state->opts->basic, SDAP_NESTING_LEVEL) != 0)
2049 0 : && !dp_opt_get_bool(state->opts->basic, SDAP_AD_MATCHING_RULE_GROUPS)) {
2050 0 : subreq = sdap_nested_group_send(state, state->ev, state->sdom,
2051 : state->opts, state->sh,
2052 0 : state->groups[0]);
2053 0 : if (!subreq) {
2054 0 : tevent_req_error(req, EIO);
2055 0 : return;
2056 : }
2057 :
2058 0 : tevent_req_set_callback(subreq, sdap_nested_done, req);
2059 0 : return;
2060 : }
2061 : }
2062 :
2063 : /* We have all of the groups. Save them to the sysdb */
2064 0 : state->check_count = state->count;
2065 :
2066 : /* If we're using LDAP_MATCHING_RULE_IN_CHAIN, start a subreq to
2067 : * retrieve the members so we can save them in a single step.
2068 : */
2069 0 : if (state->lookup_type == SDAP_LOOKUP_SINGLE
2070 0 : && (state->opts->schema_type != SDAP_SCHEMA_RFC2307)
2071 0 : && state->opts->support_matching_rule
2072 0 : && dp_opt_get_bool(state->opts->basic, SDAP_AD_MATCHING_RULE_GROUPS)) {
2073 0 : subreq = sdap_get_ad_match_rule_members_send(
2074 : state, state->ev, state->opts, state->sh,
2075 0 : state->groups[0], state->timeout);
2076 0 : if (!subreq) {
2077 0 : tevent_req_error(req, ENOMEM);
2078 0 : return;
2079 : }
2080 0 : tevent_req_set_callback(subreq,
2081 : sdap_ad_match_rule_members_process,
2082 : req);
2083 0 : return;
2084 : }
2085 :
2086 0 : ret = sysdb_transaction_start(state->sysdb);
2087 0 : if (ret != EOK) {
2088 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Failed to start transaction\n");
2089 0 : tevent_req_error(req, ret);
2090 0 : return;
2091 : }
2092 :
2093 0 : if ((state->lookup_type == SDAP_LOOKUP_ENUMERATE
2094 0 : || state->lookup_type == SDAP_LOOKUP_WILDCARD)
2095 0 : && state->opts->schema_type != SDAP_SCHEMA_RFC2307
2096 0 : && dp_opt_get_int(state->opts->basic, SDAP_NESTING_LEVEL) != 0) {
2097 0 : DEBUG(SSSDBG_TRACE_ALL, "Saving groups without members first "
2098 : "to allow unrolling of nested groups.\n");
2099 0 : ret = sdap_save_groups(state, state->sysdb, state->dom, state->opts,
2100 0 : state->groups, state->count, false,
2101 : NULL, true, NULL);
2102 0 : if (ret) {
2103 0 : DEBUG(SSSDBG_OP_FAILURE, "Failed to store groups.\n");
2104 0 : tevent_req_error(req, ret);
2105 0 : return;
2106 : }
2107 : }
2108 :
2109 0 : for (i = 0; i < state->count; i++) {
2110 0 : subreq = sdap_process_group_send(state, state->ev, state->dom,
2111 : state->sysdb, state->opts,
2112 0 : state->sh, state->groups[i],
2113 0 : state->lookup_type == SDAP_LOOKUP_ENUMERATE);
2114 :
2115 0 : if (!subreq) {
2116 0 : tevent_req_error(req, ENOMEM);
2117 0 : return;
2118 : }
2119 0 : tevent_req_set_callback(subreq, sdap_get_groups_done, req);
2120 : }
2121 : }
2122 :
2123 0 : static void sdap_search_group_copy_batch(struct sdap_get_groups_state *state,
2124 : struct sysdb_attrs **groups,
2125 : size_t count)
2126 : {
2127 : size_t copied;
2128 : bool filter;
2129 :
2130 : /* Always copy all objects for wildcard lookups. */
2131 0 : filter = state->lookup_type == SDAP_LOOKUP_SINGLE ? true : false;
2132 :
2133 0 : copied = sdap_steal_objects_in_dom(state->opts,
2134 : state->groups,
2135 : state->count,
2136 : state->dom,
2137 : groups, count, filter);
2138 :
2139 0 : state->count += copied;
2140 0 : state->groups[state->count] = NULL;
2141 0 : }
2142 :
2143 0 : static void sdap_get_groups_done(struct tevent_req *subreq)
2144 : {
2145 0 : struct tevent_req *req =
2146 0 : tevent_req_callback_data(subreq, struct tevent_req);
2147 0 : struct sdap_get_groups_state *state =
2148 0 : tevent_req_data(req, struct sdap_get_groups_state);
2149 :
2150 : int ret;
2151 : errno_t sysret;
2152 :
2153 0 : ret = sdap_process_group_recv(subreq);
2154 0 : talloc_zfree(subreq);
2155 0 : if (ret) {
2156 0 : sysret = sysdb_transaction_cancel(state->sysdb);
2157 0 : if (sysret != EOK) {
2158 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Could not cancel sysdb transaction\n");
2159 : }
2160 0 : tevent_req_error(req, ret);
2161 0 : return;
2162 : }
2163 :
2164 0 : state->check_count--;
2165 0 : DEBUG(SSSDBG_TRACE_ALL, "Groups remaining: %zu\n", state->check_count);
2166 :
2167 :
2168 0 : if (state->check_count == 0) {
2169 0 : DEBUG(SSSDBG_TRACE_ALL, "All groups processed\n");
2170 :
2171 : /* If ignore_group_members is set for the domain, don't update
2172 : * group memberships in the cache.
2173 : *
2174 : * If enumeration is on, don't overwrite orig_members as they've been
2175 : * saved earlier.
2176 : */
2177 0 : ret = sdap_save_groups(state, state->sysdb, state->dom, state->opts,
2178 0 : state->groups, state->count,
2179 0 : !state->dom->ignore_group_members, NULL,
2180 0 : state->lookup_type == SDAP_LOOKUP_SINGLE,
2181 0 : &state->higher_usn);
2182 0 : if (ret) {
2183 0 : DEBUG(SSSDBG_OP_FAILURE, "Failed to store groups.\n");
2184 0 : tevent_req_error(req, ret);
2185 0 : return;
2186 : }
2187 0 : DEBUG(SSSDBG_TRACE_ALL, "Saving %zu Groups - Done\n", state->count);
2188 0 : sysret = sysdb_transaction_commit(state->sysdb);
2189 0 : if (sysret != EOK) {
2190 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Couldn't commit transaction\n");
2191 0 : tevent_req_error(req, sysret);
2192 : } else {
2193 0 : tevent_req_done(req);
2194 : }
2195 : }
2196 : }
2197 :
2198 : static errno_t sdap_nested_group_populate_users(TALLOC_CTX *mem_ctx,
2199 : struct sysdb_ctx *sysdb,
2200 : struct sss_domain_info *domain,
2201 : struct sdap_options *opts,
2202 : struct sysdb_attrs **users,
2203 : int num_users,
2204 : hash_table_t **_ghosts);
2205 :
2206 0 : static void sdap_ad_match_rule_members_process(struct tevent_req *subreq)
2207 : {
2208 : errno_t ret;
2209 0 : TALLOC_CTX *tmp_ctx = NULL;
2210 0 : struct tevent_req *req =
2211 0 : tevent_req_callback_data(subreq, struct tevent_req);
2212 0 : struct sdap_get_groups_state *state = tevent_req_data(req,
2213 : struct sdap_get_groups_state);
2214 : struct sysdb_attrs **users;
2215 0 : struct sysdb_attrs *group = state->groups[0];
2216 : struct ldb_message_element *member_el;
2217 : struct ldb_message_element *orig_dn_el;
2218 0 : size_t count = 0;
2219 : size_t i;
2220 : hash_table_t *ghosts;
2221 :
2222 0 : ret = sdap_get_ad_match_rule_members_recv(subreq, state,
2223 : &count, &users);
2224 0 : talloc_zfree(subreq);
2225 0 : if (ret != EOK && ret != ENOENT) {
2226 0 : DEBUG(SSSDBG_MINOR_FAILURE,
2227 : "Could not retrieve members using AD match rule. [%s]\n",
2228 : strerror(ret));
2229 :
2230 0 : goto done;
2231 : }
2232 :
2233 : /* Save the group and users to the cache */
2234 :
2235 : /* Truncate the member attribute of the group.
2236 : * It will be repopulated below, and it may currently
2237 : * be incomplete anyway, thanks to the range extension.
2238 : */
2239 :
2240 0 : ret = sysdb_attrs_get_el(group, SYSDB_MEMBER, &member_el);
2241 0 : if (ret != EOK) {
2242 0 : goto done;
2243 : }
2244 :
2245 0 : member_el->num_values = 0;
2246 0 : talloc_zfree(member_el->values);
2247 :
2248 0 : tmp_ctx = talloc_new(NULL);
2249 0 : if (!tmp_ctx) {
2250 0 : ret = ENOMEM;
2251 0 : goto done;
2252 : }
2253 :
2254 : /* Figure out which users are already cached in the sysdb and
2255 : * which ones need to be added as ghost users.
2256 : */
2257 0 : ret = sdap_nested_group_populate_users(tmp_ctx, state->sysdb, state->dom,
2258 : state->opts, users, count,
2259 : &ghosts);
2260 0 : if (ret != EOK) {
2261 0 : DEBUG(SSSDBG_MINOR_FAILURE,
2262 : "Could not determine which users are ghosts: [%s]\n",
2263 : strerror(ret));
2264 0 : goto done;
2265 : }
2266 :
2267 : /* Add any entries that aren't in the ghost hash table to the
2268 : * member element of the group. This will get converted to a
2269 : * native sysdb representation later in sdap_save_groups().
2270 : */
2271 :
2272 : /* Add all of the users as members
2273 : */
2274 0 : member_el->values = talloc_zero_array(tmp_ctx, struct ldb_val, count);
2275 0 : if (!member_el->values) {
2276 0 : ret = ENOMEM;
2277 0 : goto done;
2278 : }
2279 :
2280 : /* Copy the origDN values of the users into the member element */
2281 0 : for (i = 0; i < count; i++) {
2282 0 : ret = sysdb_attrs_get_el(users[i], SYSDB_ORIG_DN,
2283 : &orig_dn_el);
2284 0 : if (ret != EOK) {
2285 : /* This should never happen. Every entry should have
2286 : * an originalDN.
2287 : */
2288 0 : DEBUG(SSSDBG_MINOR_FAILURE,
2289 : "BUG: Missing originalDN for user?\n");
2290 0 : goto done;
2291 : }
2292 :
2293 : /* These values will have the same lifespan, so instead
2294 : * of copying them, just point at the data.
2295 : */
2296 0 : member_el->values[i].data = orig_dn_el->values[0].data;
2297 0 : member_el->values[i].length = orig_dn_el->values[0].length;
2298 : }
2299 0 : member_el->num_values = count;
2300 :
2301 : /* Now save the group, users and ghosts to the cache */
2302 0 : ret = sdap_save_groups(tmp_ctx, state->sysdb, state->dom,
2303 : state->opts, state->groups, 1,
2304 : false, ghosts, true, NULL);
2305 0 : if (ret != EOK) {
2306 0 : DEBUG(SSSDBG_MINOR_FAILURE,
2307 : "Could not save group to the cache: [%s]\n",
2308 : strerror(ret));
2309 0 : goto done;
2310 : }
2311 :
2312 0 : ret = EOK;
2313 :
2314 : done:
2315 0 : talloc_free(tmp_ctx);
2316 :
2317 0 : if (ret == EOK) {
2318 0 : tevent_req_done(req);
2319 : } else {
2320 0 : tevent_req_error(req, ret);
2321 : }
2322 0 : }
2323 :
2324 0 : int sdap_get_groups_recv(struct tevent_req *req,
2325 : TALLOC_CTX *mem_ctx, char **usn_value)
2326 : {
2327 0 : struct sdap_get_groups_state *state = tevent_req_data(req,
2328 : struct sdap_get_groups_state);
2329 :
2330 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
2331 :
2332 0 : if (usn_value) {
2333 0 : *usn_value = talloc_steal(mem_ctx, state->higher_usn);
2334 : }
2335 :
2336 0 : return EOK;
2337 : }
2338 :
2339 : static void sdap_nested_ext_done(struct tevent_req *subreq);
2340 :
2341 0 : static void sdap_nested_done(struct tevent_req *subreq)
2342 : {
2343 : errno_t ret, tret;
2344 : unsigned long user_count;
2345 : unsigned long group_count;
2346 0 : bool in_transaction = false;
2347 0 : struct sysdb_attrs **users = NULL;
2348 0 : struct sysdb_attrs **groups = NULL;
2349 : hash_table_t *ghosts;
2350 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
2351 : struct tevent_req);
2352 0 : struct sdap_get_groups_state *state = tevent_req_data(req,
2353 : struct sdap_get_groups_state);
2354 :
2355 0 : ret = sdap_nested_group_recv(state, subreq, &user_count, &users,
2356 : &group_count, &groups,
2357 : &state->missing_external);
2358 0 : talloc_zfree(subreq);
2359 0 : if (ret != EOK) {
2360 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Nested group processing failed: [%d][%s]\n",
2361 : ret, strerror(ret));
2362 0 : goto fail;
2363 : }
2364 :
2365 : /* Save all of the users first so that they are in
2366 : * place for the groups to add them.
2367 : */
2368 0 : ret = sysdb_transaction_start(state->sysdb);
2369 0 : if (ret != EOK) {
2370 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
2371 0 : goto fail;
2372 : }
2373 0 : in_transaction = true;
2374 :
2375 : PROBE(SDAP_NESTED_GROUP_POPULATE_PRE);
2376 0 : ret = sdap_nested_group_populate_users(state, state->sysdb,
2377 : state->dom, state->opts,
2378 : users, user_count, &ghosts);
2379 : PROBE(SDAP_NESTED_GROUP_POPULATE_POST);
2380 0 : if (ret != EOK) {
2381 0 : goto fail;
2382 : }
2383 :
2384 : PROBE(SDAP_NESTED_GROUP_SAVE_PRE);
2385 0 : ret = sdap_save_groups(state, state->sysdb, state->dom, state->opts,
2386 : groups, group_count, false, ghosts, true,
2387 : &state->higher_usn);
2388 : PROBE(SDAP_NESTED_GROUP_SAVE_POST);
2389 0 : if (ret != EOK) {
2390 0 : goto fail;
2391 : }
2392 :
2393 0 : ret = sysdb_transaction_commit(state->sysdb);
2394 0 : if (ret != EOK) {
2395 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
2396 0 : goto fail;
2397 : }
2398 0 : in_transaction = false;
2399 :
2400 0 : if (hash_count(state->missing_external) == 0) {
2401 : /* No external members. Processing complete */
2402 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "No external members, done");
2403 0 : tevent_req_done(req);
2404 0 : return;
2405 : }
2406 :
2407 : /* At the moment, we need to save the direct groups & members in one
2408 : * transaction and then query the others in a separate requests
2409 : */
2410 0 : subreq = sdap_nested_group_lookup_external_send(state, state->ev,
2411 : state->dom,
2412 0 : state->opts->ext_ctx,
2413 : state->missing_external);
2414 0 : if (subreq == NULL) {
2415 0 : ret = ENOMEM;
2416 0 : goto fail;
2417 : }
2418 0 : tevent_req_set_callback(subreq, sdap_nested_ext_done, req);
2419 0 : return;
2420 :
2421 : fail:
2422 0 : if (in_transaction) {
2423 0 : tret = sysdb_transaction_cancel(state->sysdb);
2424 0 : if (tret != EOK) {
2425 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to cancel transaction\n");
2426 : }
2427 : }
2428 0 : tevent_req_error(req, ret);
2429 : }
2430 :
2431 0 : static void sdap_nested_ext_done(struct tevent_req *subreq)
2432 : {
2433 : errno_t ret;
2434 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
2435 : struct tevent_req);
2436 0 : struct sdap_get_groups_state *state = tevent_req_data(req,
2437 : struct sdap_get_groups_state);
2438 :
2439 0 : ret = sdap_nested_group_lookup_external_recv(state, subreq);
2440 0 : talloc_free(subreq);
2441 0 : if (ret != EOK) {
2442 0 : DEBUG(SSSDBG_OP_FAILURE,
2443 : "Cannot resolve external members [%d]: %s\n",
2444 : ret, sss_strerror(ret));
2445 0 : tevent_req_error(req, ret);
2446 0 : return;
2447 : }
2448 :
2449 0 : tevent_req_done(req);
2450 0 : return;
2451 : }
2452 :
2453 0 : static errno_t sdap_nested_group_populate_users(TALLOC_CTX *mem_ctx,
2454 : struct sysdb_ctx *sysdb,
2455 : struct sss_domain_info *domain,
2456 : struct sdap_options *opts,
2457 : struct sysdb_attrs **users,
2458 : int num_users,
2459 : hash_table_t **_ghosts)
2460 : {
2461 : int i;
2462 : errno_t ret, sret;
2463 : struct ldb_message_element *el;
2464 : const char *username;
2465 : char *clean_orig_dn;
2466 : const char *original_dn;
2467 : struct sss_domain_info *user_dom;
2468 : struct sdap_domain *sdap_dom;
2469 :
2470 : TALLOC_CTX *tmp_ctx;
2471 : struct ldb_message **msgs;
2472 : char *filter;
2473 : const char *sysdb_name;
2474 : struct sysdb_attrs *attrs;
2475 : static const char *search_attrs[] = { SYSDB_NAME, NULL };
2476 : hash_table_t *ghosts;
2477 : hash_key_t key;
2478 : hash_value_t value;
2479 : size_t count;
2480 0 : bool in_transaction = false;
2481 :
2482 0 : if (_ghosts == NULL) {
2483 0 : return EINVAL;
2484 : }
2485 :
2486 0 : if (num_users == 0) {
2487 : /* Nothing to do if there are no users */
2488 0 : *_ghosts = NULL;
2489 0 : return EOK;
2490 : }
2491 :
2492 0 : tmp_ctx = talloc_new(NULL);
2493 0 : if (!tmp_ctx) return ENOMEM;
2494 :
2495 0 : ret = sss_hash_create(tmp_ctx, num_users, &ghosts);
2496 0 : if (ret != HASH_SUCCESS) {
2497 0 : ret = ENOMEM;
2498 0 : goto done;
2499 : }
2500 :
2501 0 : ret = sysdb_transaction_start(sysdb);
2502 0 : if (ret) {
2503 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction!\n");
2504 0 : goto done;
2505 : }
2506 0 : in_transaction = true;
2507 :
2508 0 : for (i = 0; i < num_users; i++) {
2509 0 : ret = sysdb_attrs_get_el(users[i], SYSDB_ORIG_DN, &el);
2510 0 : if (el->num_values == 0) {
2511 0 : ret = EINVAL;
2512 : }
2513 0 : if (ret != EOK) {
2514 0 : DEBUG(SSSDBG_CRIT_FAILURE,
2515 : "User entry %d has no originalDN attribute\n", i);
2516 0 : goto done;
2517 : }
2518 0 : original_dn = (const char *) el->values[0].data;
2519 :
2520 0 : ret = sss_filter_sanitize(tmp_ctx, original_dn,
2521 : &clean_orig_dn);
2522 0 : if (ret != EOK) {
2523 0 : DEBUG(SSSDBG_CRIT_FAILURE,
2524 : "Cannot sanitize originalDN [%s]\n", original_dn);
2525 0 : goto done;
2526 : }
2527 :
2528 0 : sdap_dom = sdap_domain_get_by_dn(opts, original_dn);
2529 0 : user_dom = sdap_dom == NULL ? domain : sdap_dom->dom;
2530 :
2531 0 : ret = sdap_get_user_primary_name(tmp_ctx, opts, users[i],
2532 : user_dom, &username);
2533 0 : if (ret != EOK) {
2534 0 : DEBUG(SSSDBG_MINOR_FAILURE,
2535 : "User entry %d has no name attribute. Skipping\n", i);
2536 0 : continue;
2537 : }
2538 :
2539 : /* Check for the specified origDN in the sysdb */
2540 0 : filter = talloc_asprintf(tmp_ctx, "(%s=%s)",
2541 : SYSDB_ORIG_DN,
2542 : clean_orig_dn);
2543 0 : if (!filter) {
2544 0 : ret = ENOMEM;
2545 0 : goto done;
2546 : }
2547 : PROBE(SDAP_NESTED_GROUP_POPULATE_SEARCH_USERS_PRE);
2548 0 : ret = sysdb_search_users(tmp_ctx, user_dom, filter,
2549 : search_attrs, &count, &msgs);
2550 : PROBE(SDAP_NESTED_GROUP_POPULATE_SEARCH_USERS_POST);
2551 0 : talloc_zfree(filter);
2552 0 : talloc_zfree(clean_orig_dn);
2553 0 : if (ret != EOK && ret != ENOENT) {
2554 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Error checking cache for user entry\n");
2555 0 : goto done;
2556 0 : } else if (ret == EOK) {
2557 : /* The entry is cached but expired. Update the username
2558 : * if needed. */
2559 0 : if (count != 1) {
2560 0 : DEBUG(SSSDBG_CRIT_FAILURE,
2561 : "More than one entry with this origDN? Skipping\n");
2562 0 : continue;
2563 : }
2564 :
2565 0 : sysdb_name = ldb_msg_find_attr_as_string(msgs[0], SYSDB_NAME, NULL);
2566 0 : if (strcmp(sysdb_name, username) == 0) {
2567 : /* Username is correct, continue */
2568 0 : continue;
2569 : }
2570 :
2571 0 : attrs = sysdb_new_attrs(tmp_ctx);
2572 0 : if (!attrs) {
2573 0 : ret = ENOMEM;
2574 0 : goto done;
2575 : }
2576 :
2577 0 : ret = sysdb_attrs_add_string(attrs, SYSDB_NAME, username);
2578 0 : if (ret) goto done;
2579 0 : ret = sysdb_set_entry_attr(user_dom->sysdb, msgs[0]->dn, attrs,
2580 : SYSDB_MOD_REP);
2581 0 : if (ret != EOK) goto done;
2582 : } else {
2583 0 : key.type = HASH_KEY_STRING;
2584 0 : key.str = talloc_steal(ghosts, discard_const(original_dn));
2585 0 : value.type = HASH_VALUE_PTR;
2586 0 : value.ptr = talloc_steal(ghosts, discard_const(username));
2587 0 : ret = hash_enter(ghosts, &key, &value);
2588 0 : if (ret != HASH_SUCCESS) {
2589 0 : talloc_free(key.str);
2590 0 : talloc_free(value.ptr);
2591 0 : ret = ENOMEM;
2592 0 : goto done;
2593 : }
2594 : }
2595 : }
2596 :
2597 0 : ret = sysdb_transaction_commit(sysdb);
2598 0 : if (ret) {
2599 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction!\n");
2600 0 : goto done;
2601 : }
2602 0 : in_transaction = false;
2603 :
2604 0 : ret = EOK;
2605 : done:
2606 0 : if (in_transaction) {
2607 0 : sret = sysdb_transaction_cancel(sysdb);
2608 0 : if (sret != EOK) {
2609 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n");
2610 : }
2611 : }
2612 :
2613 0 : if (ret != EOK) {
2614 0 : *_ghosts = NULL;
2615 : } else {
2616 0 : *_ghosts = talloc_steal(mem_ctx, ghosts);
2617 : }
2618 0 : talloc_zfree(tmp_ctx);
2619 0 : return ret;
2620 : }
|