Line data Source code
1 : /*
2 : SSSD
3 :
4 : IPA Identity Backend Module for sub-domains - evaluate external group
5 : memberships
6 :
7 : Authors:
8 : Sumit Bose <sbose@redhat.com>
9 :
10 : Copyright (C) 2013 Red Hat
11 :
12 : This program is free software; you can redistribute it and/or modify
13 : it under the terms of the GNU General Public License as published by
14 : the Free Software Foundation; either version 3 of the License, or
15 : (at your option) any later version.
16 :
17 : This program is distributed in the hope that it will be useful,
18 : but WITHOUT ANY WARRANTY; without even the implied warranty of
19 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 : GNU General Public License for more details.
21 :
22 : You should have received a copy of the GNU General Public License
23 : along with this program. If not, see <http://www.gnu.org/licenses/>.
24 : */
25 :
26 : #include "util/util.h"
27 : #include "db/sysdb.h"
28 : #include "providers/ldap/ldap_common.h"
29 : #include "providers/ldap/sdap_async.h"
30 : #include "providers/ipa/ipa_id.h"
31 : #include "providers/ad/ad_id.h"
32 : #include "providers/ipa/ipa_subdomains.h"
33 :
34 : #define IPA_EXT_GROUPS_FILTER "objectClass=ipaexternalgroup"
35 :
36 : struct ipa_ext_groups {
37 : time_t next_update;
38 : hash_table_t *ext_groups;
39 : };
40 :
41 0 : static errno_t process_ext_groups(TALLOC_CTX *mem_ctx, size_t reply_count,
42 : struct sysdb_attrs **reply,
43 : hash_table_t **_ext_group_hash)
44 : {
45 : int ret;
46 0 : hash_table_t *ext_group_hash = NULL;
47 : hash_key_t key;
48 : hash_value_t value;
49 0 : hash_table_t *m_hash = NULL;
50 : hash_key_t m_key;
51 : hash_value_t m_value;
52 : size_t g;
53 : size_t s;
54 : size_t m;
55 0 : TALLOC_CTX *tmp_ctx = NULL;
56 : const char **ext_sids;
57 : const char **mof;
58 :
59 0 : tmp_ctx = talloc_new(NULL);
60 0 : if (tmp_ctx == NULL) {
61 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
62 0 : ret = ENOMEM;
63 0 : goto done;
64 : }
65 :
66 0 : ret = sss_hash_create(mem_ctx, reply_count, &ext_group_hash);
67 0 : if (ret != HASH_SUCCESS) {
68 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_hash_create failed.\n");
69 0 : goto done;
70 : }
71 :
72 0 : key.type = HASH_KEY_STRING;
73 0 : m_key.type = HASH_KEY_STRING;
74 0 : m_value.type = HASH_VALUE_PTR;
75 0 : m_value.ptr = NULL;
76 :
77 0 : for (g = 0; g < reply_count; g++) {
78 0 : ret = sysdb_attrs_get_string_array(reply[g], "ipaExternalMember",
79 : tmp_ctx, &ext_sids);
80 0 : if (ret == ENOENT) {
81 : /* no external members, try next external group. */
82 0 : continue;
83 0 : } else if (ret != EOK) {
84 0 : DEBUG(SSSDBG_OP_FAILURE,
85 : "sysdb_attrs_get_string_array failed.\n");
86 0 : goto done;
87 : }
88 :
89 0 : ret = sysdb_attrs_get_string_array(reply[g], "memberOf",
90 : tmp_ctx, &mof);
91 0 : if (ret == ENOENT) {
92 : /* no IPA groups, try next external group. */
93 0 : continue;
94 0 : } else if (ret != EOK) {
95 0 : DEBUG(SSSDBG_OP_FAILURE,
96 : "sysdb_attrs_get_string_array failed.\n");
97 0 : goto done;
98 : }
99 :
100 0 : for (s = 0; ext_sids[s] != NULL; s++) {
101 : /* hash_lookup does not modify key.str. */
102 0 : key.str = discard_const(ext_sids[s]);
103 0 : ret = hash_lookup(ext_group_hash, &key, &value);
104 0 : if (ret == HASH_SUCCESS) {
105 0 : if (value.type != HASH_VALUE_PTR) {
106 0 : DEBUG(SSSDBG_OP_FAILURE, "Unexpected value type.\n");
107 0 : ret = EINVAL;
108 0 : goto done;
109 : }
110 :
111 0 : for (m = 0; mof[m] != NULL; m++) {
112 : /* hash_enter does not modify m_key.str. */
113 0 : m_key.str = discard_const(mof[m]);
114 0 : DEBUG(SSSDBG_TRACE_ALL, "Adding group [%s] to SID [%s].\n",
115 : m_key.str, key.str);
116 0 : ret = hash_enter(value.ptr, &m_key, &m_value);
117 0 : if (ret != HASH_SUCCESS) {
118 0 : DEBUG(SSSDBG_OP_FAILURE, "hash_enter failed.\n");
119 0 : goto done;
120 : }
121 : }
122 0 : } else if (ret == HASH_ERROR_KEY_NOT_FOUND) {
123 0 : ret = sss_hash_create(ext_group_hash, 5, &m_hash);
124 0 : if (ret != HASH_SUCCESS) {
125 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_hash_create failed.\n");
126 0 : goto done;
127 : }
128 :
129 0 : value.type = HASH_VALUE_PTR;
130 0 : value.ptr = m_hash;
131 :
132 0 : DEBUG(SSSDBG_TRACE_ALL,
133 : "Adding SID [%s] to external group hash.\n", key.str);
134 0 : ret = hash_enter(ext_group_hash, &key, &value);
135 0 : if (ret != HASH_SUCCESS) {
136 0 : DEBUG(SSSDBG_OP_FAILURE, "hash_enter failed.\n");
137 0 : goto done;
138 : }
139 :
140 0 : for (m = 0; mof[m] != NULL; m++) {
141 : /* hash_enter does not modify m_key.str. */
142 0 : m_key.str = discard_const(mof[m]);
143 0 : DEBUG(SSSDBG_TRACE_ALL, "Adding group [%s] to SID [%s].\n",
144 : m_key.str, key.str);
145 0 : ret = hash_enter(m_hash, &m_key, &m_value);
146 0 : if (ret != HASH_SUCCESS) {
147 0 : DEBUG(SSSDBG_OP_FAILURE, "hash_enter failed.\n");
148 0 : goto done;
149 : }
150 : }
151 : } else {
152 0 : DEBUG(SSSDBG_OP_FAILURE, "hash_lookup failed.\n");
153 0 : goto done;
154 : }
155 : }
156 : }
157 :
158 0 : ret = EOK;
159 : done:
160 0 : if (ret != EOK) {
161 0 : talloc_free(ext_group_hash);
162 : } else {
163 0 : *_ext_group_hash = ext_group_hash;
164 : }
165 :
166 0 : talloc_free(tmp_ctx);
167 :
168 0 : return ret;
169 : }
170 :
171 0 : static errno_t find_ipa_ext_memberships(TALLOC_CTX *mem_ctx,
172 : const char *user_name,
173 : struct sss_domain_info *user_dom,
174 : hash_table_t *ext_group_hash,
175 : struct ldb_dn **_user_dn,
176 : char ***_groups)
177 : {
178 : int ret;
179 0 : TALLOC_CTX *tmp_ctx = NULL;
180 : struct ldb_result *result;
181 0 : char **groups = NULL;
182 : size_t c;
183 : const char *sid;
184 : hash_key_t key;
185 : hash_value_t value;
186 : hash_entry_t *entry;
187 : struct hash_iter_context_t *iter;
188 : hash_table_t *group_hash;
189 : size_t g_count;
190 0 : struct ldb_dn *user_dn = NULL;
191 :
192 0 : tmp_ctx = talloc_new(NULL);
193 0 : if (tmp_ctx == NULL) {
194 0 : return ENOMEM;
195 : }
196 :
197 0 : ret = sysdb_initgroups(tmp_ctx, user_dom, user_name, &result);
198 0 : if (ret != EOK) {
199 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_initgroups failed.\n");
200 0 : goto done;
201 : }
202 :
203 0 : if (result->count == 0) {
204 0 : DEBUG(SSSDBG_MINOR_FAILURE, "User [%s] not found in cache.\n",
205 : user_name);
206 0 : ret = EOK;
207 0 : goto done;
208 : }
209 :
210 0 : ret = sss_hash_create(tmp_ctx, 10, &group_hash);
211 0 : if (ret != HASH_SUCCESS) {
212 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_hash_create failed.\n");
213 0 : goto done;
214 : }
215 :
216 0 : key.type = HASH_KEY_STRING;
217 :
218 : /* The IPA external domains can have references to group and user SIDs.
219 : * This means that we not only want to look up the group SIDs but the SID
220 : * of the user (first element of result) as well. */
221 0 : for (c = 0; c < result->count; c++) {
222 0 : sid = ldb_msg_find_attr_as_string(result->msgs[c], SYSDB_SID_STR,
223 : NULL);
224 0 : if (sid == NULL) {
225 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Group [%s] does not have a SID.\n",
226 : ldb_dn_get_linearized(result->msgs[c]->dn));
227 0 : continue;
228 : }
229 :
230 0 : key.str = discard_const(sid);
231 0 : ret = hash_lookup(ext_group_hash, &key, &value);
232 0 : if (ret == HASH_ERROR_KEY_NOT_FOUND) {
233 0 : DEBUG(SSSDBG_TRACE_ALL, "SID [%s] not found in ext group hash.\n",
234 : sid);
235 0 : } else if (ret == HASH_SUCCESS) {
236 0 : iter = new_hash_iter_context(value.ptr);
237 0 : if (iter == NULL) {
238 0 : DEBUG(SSSDBG_OP_FAILURE, "new_hash_iter_context failed.\n");
239 0 : ret = EINVAL;
240 0 : goto done;
241 : }
242 :
243 0 : while ((entry = iter->next(iter)) != NULL) {
244 0 : ret = hash_enter(group_hash, &entry->key, &entry->value);
245 0 : if (ret != HASH_SUCCESS) {
246 0 : DEBUG(SSSDBG_OP_FAILURE, "Failed to add group [%s].\n",
247 : entry->key.str);
248 : }
249 : }
250 :
251 0 : talloc_free(iter);
252 : } else {
253 0 : DEBUG(SSSDBG_OP_FAILURE, "hash_lookup failed for SID [%s].\n",
254 : sid);
255 : }
256 : }
257 :
258 0 : g_count = hash_count(group_hash);
259 0 : if (g_count == 0) {
260 0 : DEBUG(SSSDBG_TRACE_FUNC, "No external groupmemberships found.\n");
261 0 : ret = EOK;
262 0 : goto done;
263 : }
264 :
265 0 : groups = talloc_zero_array(mem_ctx, char *, g_count + 1);
266 0 : if (groups == NULL) {
267 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
268 0 : ret = ENOMEM;
269 0 : goto done;
270 : }
271 :
272 0 : iter = new_hash_iter_context(group_hash);
273 0 : if (iter == NULL) {
274 0 : DEBUG(SSSDBG_OP_FAILURE, "new_hash_iter_context failed.\n");
275 0 : ret = EINVAL;
276 0 : goto done;
277 : }
278 :
279 0 : c = 0;
280 0 : while ((entry = iter->next(iter)) != NULL) {
281 0 : groups[c] = talloc_strdup(groups, entry->key.str);
282 0 : if (groups[c] == NULL) {
283 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
284 0 : ret = ENOMEM;
285 0 : goto done;
286 : }
287 0 : c++;
288 : }
289 :
290 0 : user_dn = ldb_dn_copy(mem_ctx, result->msgs[0]->dn);
291 0 : if (user_dn == NULL) {
292 0 : DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_copy failed.\n");
293 0 : ret = ENOMEM;
294 0 : goto done;
295 : }
296 :
297 0 : ret = EOK;
298 : done:
299 0 : *_user_dn = user_dn;
300 0 : *_groups = groups;
301 :
302 0 : talloc_free(tmp_ctx);
303 :
304 0 : return ret;
305 : }
306 :
307 0 : static errno_t add_ad_user_to_cached_groups(struct ldb_dn *user_dn,
308 : struct sss_domain_info *user_dom,
309 : struct sss_domain_info *group_dom,
310 : char **groups,
311 : bool *missing_groups)
312 : {
313 : size_t c;
314 : struct sysdb_attrs *user_attrs;
315 : size_t msgs_count;
316 : struct ldb_message **msgs;
317 : char *subfilter;
318 : TALLOC_CTX *tmp_ctx;
319 : int ret;
320 :
321 0 : *missing_groups = false;
322 :
323 0 : tmp_ctx = talloc_new(NULL);
324 0 : if (tmp_ctx == NULL) {
325 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
326 0 : return ENOMEM;
327 : }
328 :
329 0 : for (c = 0; groups[c] != NULL; c++) {
330 0 : if (groups[c][0] == '\0') {
331 0 : continue;
332 : }
333 :
334 0 : subfilter = talloc_asprintf(tmp_ctx, "(%s=%s)", SYSDB_ORIG_DN, groups[c]);
335 0 : if (subfilter == NULL) {
336 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
337 0 : ret = ENOMEM;
338 0 : goto done;
339 : }
340 :
341 0 : ret = sysdb_search_groups(tmp_ctx, group_dom, subfilter, NULL,
342 : &msgs_count, &msgs);
343 0 : if (ret != EOK) {
344 0 : if (ret == ENOENT) {
345 0 : DEBUG(SSSDBG_TRACE_ALL, "Group [%s] not in the cache.\n",
346 : groups[c]);
347 0 : *missing_groups = true;
348 0 : continue;
349 : } else {
350 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_entry failed.\n");
351 0 : goto done;
352 : }
353 : }
354 :
355 : /* TODO? Do we have to remove members as well? I think not because the AD
356 : * query before removes all memberships. */
357 :
358 0 : ret = sysdb_mod_group_member(group_dom, user_dn, msgs[0]->dn,
359 : LDB_FLAG_MOD_ADD);
360 0 : if (ret != EOK && ret != EEXIST) {
361 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_mod_group_member failed.\n");
362 0 : goto done;
363 : }
364 :
365 0 : user_attrs = sysdb_new_attrs(tmp_ctx);
366 0 : if (user_attrs == NULL) {
367 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n");
368 0 : ret = ENOMEM;
369 0 : goto done;
370 : }
371 :
372 0 : ret = sysdb_attrs_add_string(user_attrs, SYSDB_ORIG_MEMBEROF,
373 0 : groups[c]);
374 0 : if (ret != EOK) {
375 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_string failed.\n");
376 0 : goto done;
377 : }
378 :
379 0 : ret = sysdb_set_entry_attr(user_dom->sysdb, user_dn, user_attrs,
380 : LDB_FLAG_MOD_ADD);
381 0 : if (ret != EOK && ret != EEXIST) {
382 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_set_entry_attr failed.\n");
383 0 : goto done;
384 : }
385 :
386 : /* mark group as already processed */
387 0 : groups[c][0] = '\0';
388 : }
389 :
390 0 : ret = EOK;
391 : done:
392 0 : talloc_free(tmp_ctx);
393 :
394 0 : return ret;
395 : }
396 :
397 : static struct tevent_req *ipa_add_ad_memberships_send(TALLOC_CTX *mem_ctx,
398 : struct tevent_context *ev,
399 : struct sdap_id_ctx *sdap_id_ctx,
400 : struct ldb_dn *user_dn,
401 : struct sss_domain_info *user_dom,
402 : char **groups,
403 : struct sss_domain_info *group_dom);
404 : static void ipa_add_ad_memberships_done(struct tevent_req *subreq);
405 :
406 : struct get_ad_membership_state {
407 : struct tevent_context *ev;
408 : struct ipa_server_mode_ctx *server_mode;
409 : struct sdap_id_op *sdap_op;
410 : struct sdap_id_ctx *sdap_id_ctx;
411 : struct fo_server *srv;
412 : char *user_name;
413 : struct sss_domain_info *user_dom;
414 :
415 : int dp_error;
416 : const char *domain;
417 : size_t reply_count;
418 : struct sysdb_attrs **reply;
419 : };
420 :
421 : static void ipa_get_ad_memberships_connect_done(struct tevent_req *subreq);
422 : static void ipa_get_ext_groups_done(struct tevent_req *subreq);
423 : static errno_t ipa_add_ext_groups_step(struct tevent_req *req);
424 : static errno_t ipa_add_ad_memberships_recv(struct tevent_req *req,
425 : int *dp_error_out);
426 :
427 0 : struct tevent_req *ipa_get_ad_memberships_send(TALLOC_CTX *mem_ctx,
428 : struct tevent_context *ev,
429 : struct be_acct_req *ar,
430 : struct ipa_server_mode_ctx *server_mode,
431 : struct sss_domain_info *user_dom,
432 : struct sdap_id_ctx *sdap_id_ctx,
433 : const char *domain)
434 : {
435 : int ret;
436 : struct tevent_req *req;
437 : struct tevent_req *subreq;
438 : struct get_ad_membership_state *state;
439 :
440 0 : req = tevent_req_create(mem_ctx, &state, struct get_ad_membership_state);
441 0 : if (req == NULL) {
442 0 : DEBUG(SSSDBG_OP_FAILURE, "tevent_req_create failed.\n");
443 0 : return NULL;
444 : }
445 :
446 0 : state->ev = ev;
447 0 : state->user_dom = user_dom;
448 0 : state->sdap_id_ctx = sdap_id_ctx;
449 0 : state->srv = NULL;
450 0 : state->domain = domain;
451 0 : state->dp_error = -1;
452 :
453 0 : if (((ar->entry_type & BE_REQ_TYPE_MASK) != BE_REQ_INITGROUPS
454 0 : && (ar->entry_type & BE_REQ_TYPE_MASK) != BE_REQ_USER)
455 0 : || ar->filter_type != BE_FILTER_NAME) {
456 0 : DEBUG(SSSDBG_OP_FAILURE, "Unsupported request type.\n");
457 0 : ret = EINVAL;
458 0 : goto done;
459 : }
460 :
461 0 : state->user_name = talloc_strdup(state, ar->filter_value);
462 0 : if (state->user_name == NULL) {
463 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_Strdup failed.\n");
464 0 : ret = ENOMEM;
465 0 : goto done;
466 : }
467 :
468 0 : state->sdap_op = sdap_id_op_create(state,
469 0 : state->sdap_id_ctx->conn->conn_cache);
470 0 : if (state->sdap_op == NULL) {
471 0 : DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create failed\n");
472 0 : ret = ENOMEM;
473 0 : goto done;
474 : }
475 :
476 0 : state->server_mode = server_mode;
477 0 : if (server_mode->ext_groups == NULL) {
478 0 : server_mode->ext_groups = talloc_zero(server_mode,
479 : struct ipa_ext_groups);
480 0 : if (server_mode->ext_groups == NULL) {
481 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
482 0 : ret = ENOMEM;
483 0 : goto done;
484 : }
485 : }
486 :
487 0 : if (server_mode->ext_groups->next_update > time(NULL)) {
488 0 : DEBUG(SSSDBG_TRACE_FUNC, "External group information still valid.\n");
489 0 : ret = ipa_add_ext_groups_step(req);
490 0 : if (ret == EOK) {
491 0 : goto done;
492 0 : } else if (ret == EAGAIN) {
493 0 : return req;
494 : } else {
495 0 : DEBUG(SSSDBG_OP_FAILURE, "ipa_add_ext_groups_step failed.\n");
496 0 : goto done;
497 : }
498 :
499 : }
500 :
501 0 : subreq = sdap_id_op_connect_send(state->sdap_op, state, &ret);
502 0 : if (subreq == NULL) {
503 0 : DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_connect_send failed: %d(%s).\n",
504 : ret, strerror(ret));
505 0 : goto done;
506 : }
507 :
508 0 : tevent_req_set_callback(subreq, ipa_get_ad_memberships_connect_done, req);
509 :
510 0 : return req;
511 :
512 : done:
513 0 : if (ret != EOK) {
514 0 : state->dp_error = DP_ERR_FATAL;
515 0 : tevent_req_error(req, ret);
516 : } else {
517 0 : state->dp_error = DP_ERR_OK;
518 0 : tevent_req_done(req);
519 : }
520 0 : tevent_req_post(req, state->ev);
521 :
522 0 : return req;
523 : }
524 :
525 0 : static void ipa_get_ad_memberships_connect_done(struct tevent_req *subreq)
526 : {
527 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
528 : struct tevent_req);
529 0 : struct get_ad_membership_state *state = tevent_req_data(req,
530 : struct get_ad_membership_state);
531 : int ret;
532 : char *basedn;
533 :
534 0 : ret = sdap_id_op_connect_recv(subreq, &state->dp_error);
535 0 : talloc_zfree(subreq);
536 0 : if (ret != EOK) {
537 0 : if (state->dp_error == DP_ERR_OFFLINE) {
538 0 : DEBUG(SSSDBG_MINOR_FAILURE,
539 : "No IPA server is available, going offline\n");
540 : } else {
541 0 : DEBUG(SSSDBG_OP_FAILURE,
542 : "Failed to connect to IPA server: [%d](%s)\n",
543 : ret, strerror(ret));
544 : }
545 :
546 0 : goto fail;
547 : }
548 :
549 :
550 0 : ret = domain_to_basedn(state, state->domain, &basedn);
551 0 : if (ret != EOK) {
552 0 : DEBUG(SSSDBG_OP_FAILURE, "domain_to_basedn failed.\n");
553 0 : goto fail;
554 : }
555 :
556 0 : subreq = sdap_get_generic_send(state, state->ev, state->sdap_id_ctx->opts,
557 : sdap_id_op_handle(state->sdap_op), basedn,
558 : LDAP_SCOPE_SUBTREE,
559 : IPA_EXT_GROUPS_FILTER, NULL, NULL, 0,
560 0 : dp_opt_get_int(state->sdap_id_ctx->opts->basic,
561 : SDAP_ENUM_SEARCH_TIMEOUT),
562 : false);
563 0 : if (subreq == NULL) {
564 0 : DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send failed.\n");
565 0 : ret = ENOMEM;
566 0 : goto fail;
567 : }
568 :
569 0 : tevent_req_set_callback(subreq, ipa_get_ext_groups_done, req);
570 0 : return;
571 :
572 : fail:
573 0 : tevent_req_error(req, ret);
574 0 : return;
575 : }
576 :
577 0 : static void ipa_get_ext_groups_done(struct tevent_req *subreq)
578 : {
579 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
580 : struct tevent_req);
581 0 : struct get_ad_membership_state *state = tevent_req_data(req,
582 : struct get_ad_membership_state);
583 : int ret;
584 : hash_table_t *ext_group_hash;
585 :
586 0 : ret = sdap_get_generic_recv(subreq, state,
587 : &state->reply_count, &state->reply);
588 0 : talloc_zfree(subreq);
589 0 : if (ret != EOK) {
590 0 : DEBUG(SSSDBG_OP_FAILURE, "ipa_get_ext_groups request failed.\n");
591 0 : tevent_req_error(req, ret);
592 0 : return;
593 : }
594 :
595 0 : DEBUG(SSSDBG_TRACE_FUNC, "[%zu] external groups found.\n",
596 : state->reply_count);
597 :
598 0 : ret = process_ext_groups(state->server_mode->ext_groups,
599 : state->reply_count, state->reply, &ext_group_hash);
600 0 : if (ret != EOK) {
601 0 : DEBUG(SSSDBG_OP_FAILURE, "process_ext_groups failed.\n");
602 0 : goto fail;
603 : }
604 :
605 0 : state->server_mode->ext_groups->ext_groups = ext_group_hash;
606 : /* Do we have to make the update timeout configurable? */
607 0 : state->server_mode->ext_groups->next_update = time(NULL) + 10;
608 :
609 0 : ret = ipa_add_ext_groups_step(req);
610 0 : if (ret == EOK) {
611 0 : tevent_req_done(req);
612 0 : return;
613 0 : } else if (ret == EAGAIN) {
614 0 : return;
615 : } else {
616 0 : DEBUG(SSSDBG_OP_FAILURE, "ipa_add_ext_groups_step failed.\n");
617 0 : goto fail;
618 : }
619 :
620 : fail:
621 0 : tevent_req_error(req, ret);
622 0 : return;
623 : }
624 :
625 0 : static errno_t ipa_add_ext_groups_step(struct tevent_req *req)
626 : {
627 0 : struct get_ad_membership_state *state = tevent_req_data(req,
628 : struct get_ad_membership_state);
629 : struct ldb_dn *user_dn;
630 : int ret;
631 0 : char **groups = NULL;
632 : struct tevent_req *subreq;
633 :
634 0 : ret = find_ipa_ext_memberships(state, state->user_name, state->user_dom,
635 0 : state->server_mode->ext_groups->ext_groups,
636 : &user_dn, &groups);
637 0 : if (ret != EOK) {
638 0 : DEBUG(SSSDBG_OP_FAILURE, "find_ipa_ext_memberships failed.\n");
639 0 : goto fail;
640 : }
641 :
642 0 : if (groups == NULL) {
643 0 : DEBUG(SSSDBG_TRACE_ALL, "No external groups memberships found.\n");
644 0 : state->dp_error = DP_ERR_OK;
645 0 : return EOK;
646 : }
647 :
648 0 : subreq = ipa_add_ad_memberships_send(state, state->ev, state->sdap_id_ctx,
649 : user_dn, state->user_dom, groups,
650 0 : state->sdap_id_ctx->be->domain);
651 0 : if (subreq == NULL) {
652 0 : DEBUG(SSSDBG_OP_FAILURE, "ipa_add_ad_memberships_send failed.\n");
653 0 : ret = ENOMEM;
654 0 : goto fail;
655 : }
656 :
657 0 : tevent_req_set_callback(subreq, ipa_add_ad_memberships_done, req);
658 0 : return EAGAIN;
659 :
660 : fail:
661 0 : tevent_req_error(req, ret);
662 0 : return ret;
663 : }
664 :
665 0 : static void ipa_add_ad_memberships_done(struct tevent_req *subreq)
666 : {
667 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
668 : struct tevent_req);
669 0 : struct get_ad_membership_state *state = tevent_req_data(req,
670 : struct get_ad_membership_state);
671 : int ret;
672 :
673 0 : ret = ipa_add_ad_memberships_recv(subreq, &state->dp_error);
674 0 : talloc_zfree(subreq);
675 0 : if (ret != EOK) {
676 0 : DEBUG(SSSDBG_OP_FAILURE, "ipa_add_ad_memberships request failed.\n");
677 0 : tevent_req_error(req, ret);
678 0 : return;
679 : }
680 :
681 0 : state->dp_error = DP_ERR_OK;
682 0 : tevent_req_done(req);
683 0 : return;
684 : }
685 :
686 0 : errno_t ipa_get_ad_memberships_recv(struct tevent_req *req, int *dp_error_out)
687 : {
688 0 : struct get_ad_membership_state *state = tevent_req_data(req,
689 : struct get_ad_membership_state);
690 :
691 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
692 :
693 0 : if (dp_error_out) {
694 0 : *dp_error_out = state->dp_error;
695 : }
696 :
697 0 : return EOK;
698 : }
699 :
700 : struct add_ad_membership_state {
701 : struct tevent_context *ev;
702 : struct sdap_id_ctx *sdap_id_ctx;
703 : struct sdap_id_op *sdap_op;
704 : struct ldb_dn *user_dn;
705 : struct sss_domain_info *user_dom;
706 : struct sss_domain_info *group_dom;
707 : char **groups;
708 : int dp_error;
709 : size_t iter;
710 : struct sdap_domain *group_sdom;
711 : };
712 :
713 : static void ipa_add_ad_memberships_connect_done(struct tevent_req *subreq);
714 : static void ipa_add_ad_memberships_get_next(struct tevent_req *req);
715 : static void ipa_add_ad_memberships_get_group_done(struct tevent_req *subreq);
716 0 : static struct tevent_req *ipa_add_ad_memberships_send(TALLOC_CTX *mem_ctx,
717 : struct tevent_context *ev,
718 : struct sdap_id_ctx *sdap_id_ctx,
719 : struct ldb_dn *user_dn,
720 : struct sss_domain_info *user_dom,
721 : char **groups,
722 : struct sss_domain_info *group_dom)
723 : {
724 : int ret;
725 : struct tevent_req *req;
726 : struct tevent_req *subreq;
727 : struct add_ad_membership_state *state;
728 0 : bool missing_groups = false;
729 :
730 0 : req = tevent_req_create(mem_ctx, &state, struct add_ad_membership_state);
731 0 : if (req == NULL) {
732 0 : DEBUG(SSSDBG_OP_FAILURE, "tevent_req_create failed.\n");
733 0 : return NULL;
734 : }
735 :
736 0 : state->ev = ev;
737 0 : state->user_dom = user_dom;
738 0 : state->sdap_id_ctx = sdap_id_ctx;
739 0 : state->user_dn = user_dn;
740 0 : state->group_dom = group_dom;
741 0 : state->groups = groups;
742 0 : state->dp_error = -1;
743 0 : state->iter = 0;
744 0 : state->group_sdom = sdap_domain_get(sdap_id_ctx->opts, group_dom);
745 0 : if (state->group_sdom == NULL) {
746 0 : ret = EIO;
747 0 : goto done;
748 : }
749 :
750 0 : ret = add_ad_user_to_cached_groups(user_dn, user_dom, group_dom, groups,
751 : &missing_groups);
752 0 : if (ret != EOK) {
753 0 : DEBUG(SSSDBG_OP_FAILURE, "add_ad_user_to_cached_groups failed.\n");
754 0 : goto done;
755 : }
756 :
757 0 : if (!missing_groups) {
758 0 : DEBUG(SSSDBG_TRACE_ALL, "All groups found in cache.\n");
759 0 : ret = EOK;
760 0 : goto done;
761 : }
762 :
763 0 : state->sdap_op = sdap_id_op_create(state,
764 0 : state->sdap_id_ctx->conn->conn_cache);
765 0 : if (state->sdap_op == NULL) {
766 0 : DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create failed\n");
767 0 : ret = ENOMEM;
768 0 : goto done;
769 : }
770 :
771 0 : subreq = sdap_id_op_connect_send(state->sdap_op, state, &ret);
772 0 : if (subreq == NULL) {
773 0 : DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_connect_send failed: %d(%s).\n",
774 : ret, strerror(ret));
775 0 : goto done;
776 : }
777 :
778 0 : tevent_req_set_callback(subreq, ipa_add_ad_memberships_connect_done, req);
779 :
780 0 : return req;
781 :
782 : done:
783 0 : if (ret != EOK) {
784 0 : state->dp_error = DP_ERR_FATAL;
785 0 : tevent_req_error(req, ret);
786 : } else {
787 0 : state->dp_error = DP_ERR_OK;
788 0 : tevent_req_done(req);
789 : }
790 0 : tevent_req_post(req, state->ev);
791 :
792 0 : return req;
793 : }
794 :
795 0 : static void ipa_add_ad_memberships_connect_done(struct tevent_req *subreq)
796 : {
797 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
798 : struct tevent_req);
799 0 : struct add_ad_membership_state *state = tevent_req_data(req,
800 : struct add_ad_membership_state);
801 : int ret;
802 :
803 0 : ret = sdap_id_op_connect_recv(subreq, &state->dp_error);
804 0 : talloc_zfree(subreq);
805 0 : if (ret != EOK) {
806 0 : if (state->dp_error == DP_ERR_OFFLINE) {
807 0 : DEBUG(SSSDBG_MINOR_FAILURE,
808 : "No IPA server is available, going offline\n");
809 : } else {
810 0 : DEBUG(SSSDBG_OP_FAILURE,
811 : "Failed to connect to IPA server: [%d](%s)\n",
812 : ret, strerror(ret));
813 : }
814 :
815 0 : tevent_req_error(req, ret);
816 0 : return;
817 : }
818 :
819 0 : state->iter = 0;
820 0 : ipa_add_ad_memberships_get_next(req);
821 : }
822 :
823 0 : static void ipa_add_ad_memberships_get_next(struct tevent_req *req)
824 : {
825 0 : struct add_ad_membership_state *state = tevent_req_data(req,
826 : struct add_ad_membership_state);
827 : struct tevent_req *subreq;
828 : struct ldb_dn *group_dn;
829 : int ret;
830 : const struct ldb_val *val;
831 : bool missing_groups;
832 :
833 0 : while (state->groups[state->iter] != NULL
834 0 : && state->groups[state->iter][0] == '\0') {
835 0 : state->iter++;
836 : }
837 :
838 0 : if (state->groups[state->iter] == NULL) {
839 0 : ret = add_ad_user_to_cached_groups(state->user_dn, state->user_dom,
840 : state->group_dom, state->groups,
841 : &missing_groups);
842 0 : if (ret != EOK) {
843 0 : DEBUG(SSSDBG_OP_FAILURE, "add_ad_user_to_cached_groups failed.\n");
844 0 : goto fail;
845 : }
846 :
847 0 : if (missing_groups) {
848 0 : DEBUG(SSSDBG_CRIT_FAILURE, "There are unresolved external group "
849 : "memberships even after all groups "
850 : "have been looked up on the LDAP "
851 : "server.\n");
852 : }
853 0 : tevent_req_done(req);
854 0 : return;
855 : }
856 :
857 0 : group_dn = ldb_dn_new(state, sysdb_ctx_get_ldb(state->group_dom->sysdb),
858 0 : state->groups[state->iter]);
859 0 : if (group_dn == NULL) {
860 0 : DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed.\n");
861 0 : ret = ENOMEM;
862 0 : goto fail;
863 : }
864 :
865 0 : val = ldb_dn_get_rdn_val(group_dn);
866 0 : if (val == NULL || val->data == NULL) {
867 0 : DEBUG(SSSDBG_OP_FAILURE,
868 : "Invalid group DN [%s].\n", state->groups[state->iter]);
869 0 : ret = EINVAL;
870 0 : goto fail;
871 : }
872 :
873 : /* TODO: here is would be useful for have a filter type like BE_FILTER_DN to
874 : * directly fetch the group with the corresponding DN. */
875 0 : subreq = groups_get_send(state, state->ev,
876 : state->sdap_id_ctx, state->group_sdom,
877 0 : state->sdap_id_ctx->conn,
878 0 : (const char *) val->data,
879 : BE_FILTER_NAME, BE_ATTR_CORE,
880 : false, false);
881 0 : if (subreq == NULL) {
882 0 : DEBUG(SSSDBG_OP_FAILURE, "groups_get_send failed.\n");
883 0 : ret = ENOMEM;
884 0 : goto fail;
885 : }
886 :
887 0 : tevent_req_set_callback(subreq, ipa_add_ad_memberships_get_group_done, req);
888 0 : return;
889 :
890 : fail:
891 0 : tevent_req_error(req, ret);
892 : }
893 :
894 0 : static void ipa_add_ad_memberships_get_group_done(struct tevent_req *subreq)
895 : {
896 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
897 : struct tevent_req);
898 0 : struct add_ad_membership_state *state = tevent_req_data(req,
899 : struct add_ad_membership_state);
900 : int ret;
901 :
902 0 : ret = groups_get_recv(subreq, &state->dp_error, NULL);
903 0 : talloc_zfree(subreq);
904 0 : if (ret != EOK) {
905 0 : DEBUG(SSSDBG_OP_FAILURE, "Failed to read group [%s] from LDAP [%d](%s)\n",
906 : state->groups[state->iter], ret, strerror(ret));
907 :
908 0 : tevent_req_error(req, ret);
909 0 : return;
910 : }
911 :
912 0 : state->iter++;
913 0 : ipa_add_ad_memberships_get_next(req);
914 : }
915 :
916 0 : static errno_t ipa_add_ad_memberships_recv(struct tevent_req *req,
917 : int *dp_error_out)
918 : {
919 0 : struct add_ad_membership_state *state = tevent_req_data(req,
920 : struct add_ad_membership_state);
921 :
922 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
923 :
924 0 : if (dp_error_out) {
925 0 : *dp_error_out = state->dp_error;
926 : }
927 :
928 0 : return EOK;
929 : }
930 :
931 : static errno_t
932 0 : search_user_or_group_by_sid_str(TALLOC_CTX *mem_ctx,
933 : struct sss_domain_info *domain,
934 : const char *sid_str,
935 : enum sysdb_member_type *_member_type,
936 : struct ldb_message **_msg)
937 : {
938 : errno_t ret;
939 0 : struct ldb_message *msg = NULL;
940 0 : const char *attrs[] = { SYSDB_NAME,
941 : SYSDB_SID_STR,
942 : SYSDB_ORIG_DN,
943 : SYSDB_OBJECTCLASS,
944 : SYSDB_CACHE_EXPIRE,
945 : NULL };
946 0 : TALLOC_CTX *tmp_ctx = NULL;
947 0 : char *sanitized_sid = NULL;
948 :
949 0 : tmp_ctx = talloc_new(NULL);
950 0 : if (tmp_ctx == NULL) {
951 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
952 0 : return ENOMEM;
953 : }
954 :
955 : /* In theory SID shouldn't contain any special LDAP characters, but let's
956 : * be paranoid
957 : */
958 0 : ret = sss_filter_sanitize(tmp_ctx, sid_str, &sanitized_sid);
959 0 : if (ret != EOK) {
960 0 : goto done;
961 : }
962 :
963 0 : ret = sysdb_search_user_by_sid_str(tmp_ctx, domain,
964 : sid_str, attrs, &msg);
965 0 : if (ret == EOK) {
966 0 : *_member_type = SYSDB_MEMBER_USER;
967 0 : } else if (ret == ENOENT) {
968 0 : ret = sysdb_search_group_by_sid_str(tmp_ctx, domain,
969 : sid_str, attrs, &msg);
970 0 : if (ret == EOK) {
971 0 : *_member_type = SYSDB_MEMBER_GROUP;
972 : }
973 : }
974 :
975 0 : switch (ret) {
976 : case EOK:
977 0 : DEBUG(SSSDBG_TRACE_FUNC, "Found %s in sysdb\n", sid_str);
978 0 : *_msg = talloc_steal(mem_ctx, msg);
979 0 : break;
980 : case ENOENT:
981 0 : DEBUG(SSSDBG_TRACE_FUNC,
982 : "Could not find %s in sysdb", sid_str);
983 0 : break;
984 : default:
985 0 : DEBUG(SSSDBG_OP_FAILURE,
986 : "Error looking for %s in sysdb [%d]: %s\n",
987 : sid_str, ret, sss_strerror(ret));
988 0 : break;
989 : }
990 :
991 : done:
992 0 : talloc_free(tmp_ctx);
993 0 : return ret;
994 : }
995 :
996 : static errno_t
997 0 : ipa_ext_group_member_check(TALLOC_CTX *mem_ctx,
998 : struct sss_domain_info *member_dom,
999 : const char *ext_member,
1000 : enum sysdb_member_type *_member_type,
1001 : struct sysdb_attrs **_member)
1002 : {
1003 0 : TALLOC_CTX *tmp_ctx = NULL;
1004 : errno_t ret;
1005 : uint64_t expire;
1006 0 : time_t now = time(NULL);
1007 : struct ldb_message *msg;
1008 : struct sysdb_attrs **members;
1009 :
1010 0 : tmp_ctx = talloc_new(NULL);
1011 0 : if (tmp_ctx == NULL) {
1012 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
1013 0 : return ENOMEM;
1014 : }
1015 :
1016 0 : ret = search_user_or_group_by_sid_str(tmp_ctx, member_dom, ext_member,
1017 : _member_type, &msg);
1018 0 : if (ret != EOK) {
1019 0 : DEBUG(SSSDBG_OP_FAILURE,
1020 : "Error looking up sid %s: [%d]: %s\n",
1021 : ext_member, ret, sss_strerror(ret));
1022 0 : goto done;
1023 : }
1024 :
1025 0 : ret = sysdb_msg2attrs(tmp_ctx, 1, &msg, &members);
1026 0 : if (ret != EOK) {
1027 0 : DEBUG(SSSDBG_OP_FAILURE,
1028 : "Could not convert result to sysdb_attrs [%d]: %s\n",
1029 : ret, sss_strerror(ret));
1030 0 : goto done;
1031 : }
1032 :
1033 : /* Return the member both expired and valid */
1034 0 : *_member = talloc_steal(mem_ctx, members[0]);
1035 :
1036 0 : expire = ldb_msg_find_attr_as_uint64(msg, SYSDB_CACHE_EXPIRE, 0);
1037 0 : if (expire != 0 && expire <= now) {
1038 0 : DEBUG(SSSDBG_TRACE_FUNC, "%s is expired", ext_member);
1039 0 : ret = EAGAIN;
1040 0 : goto done;
1041 : }
1042 :
1043 : done:
1044 0 : talloc_free(tmp_ctx);
1045 0 : return ret;
1046 : }
1047 :
1048 : /* For the IPA external member resolution, we expect a SID as the input.
1049 : * The _recv() function output is the member and a type (user/group)
1050 : * since nothing else can be a group member.
1051 : */
1052 : struct ipa_ext_member_state {
1053 : const char *ext_member;
1054 : struct sss_domain_info *dom;
1055 :
1056 : enum sysdb_member_type member_type;
1057 : struct sysdb_attrs *member;
1058 : };
1059 :
1060 : static void ipa_ext_group_member_done(struct tevent_req *subreq);
1061 :
1062 0 : struct tevent_req *ipa_ext_group_member_send(TALLOC_CTX *mem_ctx,
1063 : struct tevent_context *ev,
1064 : const char *ext_member,
1065 : void *pvt)
1066 : {
1067 : struct ipa_id_ctx *ipa_ctx;
1068 : struct ipa_ext_member_state *state;
1069 : struct tevent_req *req;
1070 : struct tevent_req *subreq;
1071 : struct be_acct_req *ar;
1072 : errno_t ret;
1073 :
1074 0 : req = tevent_req_create(mem_ctx, &state, struct ipa_ext_member_state);
1075 0 : if (req == NULL) {
1076 0 : return NULL;
1077 : }
1078 0 : state->ext_member = ext_member;
1079 :
1080 0 : ipa_ctx = talloc_get_type(pvt, struct ipa_id_ctx);
1081 0 : if (ipa_ctx == NULL) {
1082 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Wrong private context!\n");
1083 0 : ret = EINVAL;
1084 0 : goto immediate;
1085 : }
1086 :
1087 0 : state->dom = find_domain_by_sid(ipa_ctx->sdap_id_ctx->be->domain,
1088 : ext_member);
1089 0 : if (state->dom == NULL) {
1090 0 : DEBUG(SSSDBG_MINOR_FAILURE,
1091 : "Cannot find domain of SID [%s]\n", ext_member);
1092 0 : ret = ENOENT;
1093 0 : goto immediate;
1094 : }
1095 :
1096 0 : ret = ipa_ext_group_member_check(state, state->dom, ext_member,
1097 0 : &state->member_type, &state->member);
1098 0 : if (ret == EOK) {
1099 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
1100 : "external member %s already cached\n", ext_member);
1101 0 : goto immediate;
1102 : }
1103 :
1104 0 : ret = get_be_acct_req_for_sid(state, ext_member, state->dom->name, &ar);
1105 0 : if (ret != EOK) {
1106 0 : DEBUG(SSSDBG_MINOR_FAILURE,
1107 : "Cannot create the account request for [%s]\n", ext_member);
1108 0 : goto immediate;
1109 : }
1110 :
1111 0 : subreq = dp_req_send(state, ipa_ctx->sdap_id_ctx->be->provider, NULL,
1112 0 : ar->domain, "External Member",
1113 : DPT_ID, DPM_ACCOUNT_HANDLER, 0, ar, NULL);
1114 0 : if (subreq == NULL) {
1115 0 : ret = ENOMEM;
1116 0 : goto immediate;
1117 : }
1118 0 : tevent_req_set_callback(subreq, ipa_ext_group_member_done, req);
1119 :
1120 0 : return req;
1121 :
1122 : immediate:
1123 0 : if (ret != EOK) {
1124 0 : tevent_req_error(req, ret);
1125 : } else {
1126 0 : tevent_req_done(req);
1127 : }
1128 0 : tevent_req_post(req, ev);
1129 0 : return req;
1130 : }
1131 :
1132 0 : static void ipa_ext_group_member_done(struct tevent_req *subreq)
1133 : {
1134 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
1135 : struct tevent_req);
1136 0 : struct ipa_ext_member_state *state = tevent_req_data(req,
1137 : struct ipa_ext_member_state);
1138 : errno_t ret;
1139 : struct ldb_message *msg;
1140 : struct sysdb_attrs **members;
1141 : struct dp_reply_std *reply;
1142 :
1143 :
1144 0 : ret = dp_req_recv_ptr(state, subreq, struct dp_reply_std, &reply);
1145 0 : talloc_free(subreq);
1146 0 : if (ret != EOK) {
1147 0 : DEBUG(SSSDBG_OP_FAILURE, "dp_req_recv failed\n");
1148 0 : tevent_req_error(req, ret);
1149 0 : return;
1150 0 : } else if (reply->dp_error != DP_ERR_OK) {
1151 0 : DEBUG(SSSDBG_MINOR_FAILURE,
1152 : "Cannot refresh data from DP: %u,%u: %s\n",
1153 : reply->dp_error, reply->error, reply->message);
1154 0 : tevent_req_error(req, EIO);
1155 0 : return;
1156 : }
1157 :
1158 0 : ret = search_user_or_group_by_sid_str(state,
1159 : state->dom,
1160 : state->ext_member,
1161 : &state->member_type,
1162 : &msg);
1163 0 : if (ret != EOK) {
1164 0 : DEBUG(ret == ENOENT ? SSSDBG_TRACE_FUNC : SSSDBG_OP_FAILURE,
1165 : "Could not find %s in sysdb [%d]: %s\n",
1166 : state->ext_member, ret, sss_strerror(ret));
1167 0 : tevent_req_error(req, ret);
1168 0 : return;
1169 : }
1170 :
1171 0 : ret = sysdb_msg2attrs(state, 1, &msg, &members);
1172 0 : if (ret != EOK) {
1173 0 : DEBUG(SSSDBG_OP_FAILURE,
1174 : "Could not convert result to sysdb_attrs [%d]: %s\n",
1175 : ret, sss_strerror(ret));
1176 0 : tevent_req_error(req, ret);
1177 0 : return;
1178 : }
1179 :
1180 0 : state->member = members[0];
1181 0 : tevent_req_done(req);
1182 : }
1183 :
1184 0 : errno_t ipa_ext_group_member_recv(TALLOC_CTX *mem_ctx,
1185 : struct tevent_req *req,
1186 : enum sysdb_member_type *_member_type,
1187 : struct sss_domain_info **_dom,
1188 : struct sysdb_attrs **_member)
1189 : {
1190 0 : struct ipa_ext_member_state *state = tevent_req_data(req,
1191 : struct ipa_ext_member_state);
1192 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
1193 :
1194 0 : if (_member_type != NULL) {
1195 0 : *_member_type = state->member_type;
1196 : }
1197 :
1198 0 : if (_dom) {
1199 0 : *_dom = state->dom;
1200 : }
1201 :
1202 0 : if (_member != NULL) {
1203 0 : *_member = talloc_steal(mem_ctx, state->member);
1204 : }
1205 :
1206 0 : return EOK;
1207 : }
|