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 : }
84 0 : if (ret != EOK) {
85 0 : DEBUG(SSSDBG_OP_FAILURE,
86 : "sysdb_attrs_get_string_array failed.\n");
87 0 : goto done;
88 : }
89 :
90 0 : ret = sysdb_attrs_get_string_array(reply[g], "memberOf",
91 : tmp_ctx, &mof);
92 0 : if (ret == ENOENT) {
93 : /* no IPA groups, try next external group. */
94 0 : continue;
95 : }
96 0 : if (ret != EOK) {
97 0 : DEBUG(SSSDBG_OP_FAILURE,
98 : "sysdb_attrs_get_string_array failed.\n");
99 0 : goto done;
100 : }
101 :
102 0 : for (s = 0; ext_sids[s] != NULL; s++) {
103 : /* hash_lookup does not modify key.str. */
104 0 : key.str = discard_const(ext_sids[s]);
105 0 : ret = hash_lookup(ext_group_hash, &key, &value);
106 0 : if (ret == HASH_SUCCESS) {
107 0 : if (value.type != HASH_VALUE_PTR) {
108 0 : DEBUG(SSSDBG_OP_FAILURE, "Unexpected value type.\n");
109 0 : ret = EINVAL;
110 0 : goto done;
111 : }
112 :
113 0 : for (m = 0; mof[m] != NULL; m++) {
114 : /* hash_enter does not modify m_key.str. */
115 0 : m_key.str = discard_const(mof[m]);
116 0 : DEBUG(SSSDBG_TRACE_ALL, "Adding group [%s] to SID [%s].\n",
117 : m_key.str, key.str);
118 0 : ret = hash_enter(value.ptr, &m_key, &m_value);
119 0 : if (ret != HASH_SUCCESS) {
120 0 : DEBUG(SSSDBG_OP_FAILURE, "hash_enter failed.\n");
121 0 : goto done;
122 : }
123 : }
124 0 : } else if (ret == HASH_ERROR_KEY_NOT_FOUND) {
125 0 : ret = sss_hash_create(ext_group_hash, 5, &m_hash);
126 0 : if (ret != HASH_SUCCESS) {
127 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_hash_create failed.\n");
128 0 : goto done;
129 : }
130 :
131 0 : value.type = HASH_VALUE_PTR;
132 0 : value.ptr = m_hash;
133 :
134 0 : DEBUG(SSSDBG_TRACE_ALL,
135 : "Adding SID [%s] to external group hash.\n", key.str);
136 0 : ret = hash_enter(ext_group_hash, &key, &value);
137 0 : if (ret != HASH_SUCCESS) {
138 0 : DEBUG(SSSDBG_OP_FAILURE, "hash_enter failed.\n");
139 0 : goto done;
140 : }
141 :
142 0 : for (m = 0; mof[m] != NULL; m++) {
143 : /* hash_enter does not modify m_key.str. */
144 0 : m_key.str = discard_const(mof[m]);
145 0 : DEBUG(SSSDBG_TRACE_ALL, "Adding group [%s] to SID [%s].\n",
146 : m_key.str, key.str);
147 0 : ret = hash_enter(m_hash, &m_key, &m_value);
148 0 : if (ret != HASH_SUCCESS) {
149 0 : DEBUG(SSSDBG_OP_FAILURE, "hash_enter failed.\n");
150 0 : goto done;
151 : }
152 : }
153 : } else {
154 0 : DEBUG(SSSDBG_OP_FAILURE, "hash_lookup failed.\n");
155 0 : goto done;
156 : }
157 : }
158 : }
159 :
160 0 : ret = EOK;
161 : done:
162 0 : if (ret != EOK) {
163 0 : talloc_free(ext_group_hash);
164 : } else {
165 0 : *_ext_group_hash = ext_group_hash;
166 : }
167 :
168 0 : talloc_free(tmp_ctx);
169 :
170 0 : return ret;
171 : }
172 :
173 0 : static errno_t find_ipa_ext_memberships(TALLOC_CTX *mem_ctx,
174 : const char *user_name,
175 : struct sss_domain_info *user_dom,
176 : hash_table_t *ext_group_hash,
177 : struct ldb_dn **_user_dn,
178 : char ***_groups)
179 : {
180 : int ret;
181 0 : TALLOC_CTX *tmp_ctx = NULL;
182 : struct ldb_result *result;
183 0 : char **groups = NULL;
184 : size_t c;
185 : const char *sid;
186 : hash_key_t key;
187 : hash_value_t value;
188 : hash_entry_t *entry;
189 : struct hash_iter_context_t *iter;
190 : hash_table_t *group_hash;
191 : size_t g_count;
192 0 : struct ldb_dn *user_dn = NULL;
193 :
194 0 : tmp_ctx = talloc_new(NULL);
195 0 : if (tmp_ctx == NULL) {
196 0 : return ENOMEM;
197 : }
198 :
199 0 : ret = sysdb_initgroups(tmp_ctx, user_dom, user_name, &result);
200 0 : if (ret != EOK) {
201 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_initgroups failed.\n");
202 0 : goto done;
203 : }
204 :
205 0 : if (result->count == 0) {
206 0 : DEBUG(SSSDBG_MINOR_FAILURE, "User [%s] not found in cache.\n",
207 : user_name);
208 0 : ret = EOK;
209 0 : goto done;
210 : }
211 :
212 0 : ret = sss_hash_create(tmp_ctx, 10, &group_hash);
213 0 : if (ret != HASH_SUCCESS) {
214 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_hash_create failed.\n");
215 0 : goto done;
216 : }
217 :
218 0 : key.type = HASH_KEY_STRING;
219 :
220 : /* The IPA external domains can have references to group and user SIDs.
221 : * This means that we not only want to look up the group SIDs but the SID
222 : * of the user (first element of result) as well. */
223 0 : for (c = 0; c < result->count; c++) {
224 0 : sid = ldb_msg_find_attr_as_string(result->msgs[c], SYSDB_SID_STR,
225 : NULL);
226 0 : if (sid == NULL) {
227 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Group [%s] does not have a SID.\n",
228 : ldb_dn_get_linearized(result->msgs[c]->dn));
229 0 : continue;
230 : }
231 :
232 0 : key.str = discard_const(sid);
233 0 : ret = hash_lookup(ext_group_hash, &key, &value);
234 0 : if (ret == HASH_ERROR_KEY_NOT_FOUND) {
235 0 : DEBUG(SSSDBG_TRACE_ALL, "SID [%s] not found in ext group hash.\n",
236 : sid);
237 0 : } else if (ret == HASH_SUCCESS) {
238 0 : iter = new_hash_iter_context(value.ptr);
239 0 : if (iter == NULL) {
240 0 : DEBUG(SSSDBG_OP_FAILURE, "new_hash_iter_context failed.\n");
241 0 : ret = EINVAL;
242 0 : goto done;
243 : }
244 :
245 0 : while ((entry = iter->next(iter)) != NULL) {
246 0 : ret = hash_enter(group_hash, &entry->key, &entry->value);
247 0 : if (ret != HASH_SUCCESS) {
248 0 : DEBUG(SSSDBG_OP_FAILURE, "Failed to add group [%s].\n",
249 : entry->key.str);
250 : }
251 : }
252 :
253 0 : talloc_free(iter);
254 : } else {
255 0 : DEBUG(SSSDBG_OP_FAILURE, "hash_lookup failed for SID [%s].\n",
256 : sid);
257 : }
258 : }
259 :
260 0 : g_count = hash_count(group_hash);
261 0 : if (g_count == 0) {
262 0 : DEBUG(SSSDBG_TRACE_FUNC, "No external groupmemberships found.\n");
263 0 : ret = EOK;
264 0 : goto done;
265 : }
266 :
267 0 : groups = talloc_zero_array(mem_ctx, char *, g_count + 1);
268 0 : if (groups == NULL) {
269 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
270 0 : ret = ENOMEM;
271 0 : goto done;
272 : }
273 :
274 0 : iter = new_hash_iter_context(group_hash);
275 0 : if (iter == NULL) {
276 0 : DEBUG(SSSDBG_OP_FAILURE, "new_hash_iter_context failed.\n");
277 0 : ret = EINVAL;
278 0 : goto done;
279 : }
280 :
281 0 : c = 0;
282 0 : while ((entry = iter->next(iter)) != NULL) {
283 0 : groups[c] = talloc_strdup(groups, entry->key.str);
284 0 : if (groups[c] == NULL) {
285 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
286 0 : ret = ENOMEM;
287 0 : goto done;
288 : }
289 0 : c++;
290 : }
291 :
292 0 : user_dn = ldb_dn_copy(mem_ctx, result->msgs[0]->dn);
293 0 : if (user_dn == NULL) {
294 0 : DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_copy failed.\n");
295 0 : ret = ENOMEM;
296 0 : goto done;
297 : }
298 :
299 0 : ret = EOK;
300 : done:
301 0 : *_user_dn = user_dn;
302 0 : *_groups = groups;
303 :
304 0 : talloc_free(tmp_ctx);
305 :
306 0 : return ret;
307 : }
308 :
309 0 : static errno_t add_ad_user_to_cached_groups(struct ldb_dn *user_dn,
310 : struct sss_domain_info *user_dom,
311 : struct sss_domain_info *group_dom,
312 : char **groups,
313 : bool *missing_groups)
314 : {
315 : size_t c;
316 : struct sysdb_attrs *user_attrs;
317 : size_t msgs_count;
318 : struct ldb_message **msgs;
319 : char *subfilter;
320 : TALLOC_CTX *tmp_ctx;
321 : int ret;
322 :
323 0 : *missing_groups = false;
324 :
325 0 : tmp_ctx = talloc_new(NULL);
326 0 : if (tmp_ctx == NULL) {
327 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
328 0 : return ENOMEM;
329 : }
330 :
331 0 : for (c = 0; groups[c] != NULL; c++) {
332 0 : if (groups[c][0] == '\0') {
333 0 : continue;
334 : }
335 :
336 0 : subfilter = talloc_asprintf(tmp_ctx, "(%s=%s)", SYSDB_ORIG_DN, groups[c]);
337 0 : if (subfilter == NULL) {
338 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
339 0 : ret = ENOMEM;
340 0 : goto done;
341 : }
342 :
343 0 : ret = sysdb_search_groups(tmp_ctx, group_dom, subfilter, NULL,
344 : &msgs_count, &msgs);
345 0 : if (ret != EOK) {
346 0 : if (ret == ENOENT) {
347 0 : DEBUG(SSSDBG_TRACE_ALL, "Group [%s] not in the cache.\n",
348 : groups[c]);
349 0 : *missing_groups = true;
350 0 : continue;
351 : } else {
352 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_entry failed.\n");
353 0 : goto done;
354 : }
355 : }
356 :
357 : /* TODO? Do we have to remove members as well? I think not because the AD
358 : * query before removes all memberships. */
359 :
360 0 : ret = sysdb_mod_group_member(group_dom, user_dn, msgs[0]->dn,
361 : LDB_FLAG_MOD_ADD);
362 0 : if (ret != EOK && ret != EEXIST) {
363 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_mod_group_member failed.\n");
364 0 : goto done;
365 : }
366 :
367 0 : user_attrs = sysdb_new_attrs(tmp_ctx);
368 0 : if (user_attrs == NULL) {
369 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n");
370 0 : ret = ENOMEM;
371 0 : goto done;
372 : }
373 :
374 0 : ret = sysdb_attrs_add_string(user_attrs, SYSDB_ORIG_MEMBEROF,
375 0 : groups[c]);
376 0 : if (ret != EOK) {
377 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_string failed.\n");
378 0 : goto done;
379 : }
380 :
381 0 : ret = sysdb_set_entry_attr(user_dom->sysdb, user_dn, user_attrs,
382 : LDB_FLAG_MOD_ADD);
383 0 : if (ret != EOK && ret != EEXIST) {
384 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_set_entry_attr failed.\n");
385 0 : goto done;
386 : }
387 :
388 : /* mark group as already processed */
389 0 : groups[c][0] = '\0';
390 : }
391 :
392 0 : ret = EOK;
393 : done:
394 0 : talloc_free(tmp_ctx);
395 :
396 0 : return ret;
397 : }
398 :
399 : static struct tevent_req *ipa_add_ad_memberships_send(TALLOC_CTX *mem_ctx,
400 : struct tevent_context *ev,
401 : struct sdap_id_ctx *sdap_id_ctx,
402 : struct ldb_dn *user_dn,
403 : struct sss_domain_info *user_dom,
404 : char **groups,
405 : struct sss_domain_info *group_dom);
406 : static void ipa_add_ad_memberships_done(struct tevent_req *subreq);
407 :
408 : struct get_ad_membership_state {
409 : struct tevent_context *ev;
410 : struct ipa_server_mode_ctx *server_mode;
411 : struct sdap_id_op *sdap_op;
412 : struct sdap_id_ctx *sdap_id_ctx;
413 : struct fo_server *srv;
414 : char *user_name;
415 : struct sss_domain_info *user_dom;
416 :
417 : int dp_error;
418 : const char *domain;
419 : size_t reply_count;
420 : struct sysdb_attrs **reply;
421 : };
422 :
423 : static void ipa_get_ad_memberships_connect_done(struct tevent_req *subreq);
424 : static void ipa_get_ext_groups_done(struct tevent_req *subreq);
425 : static errno_t ipa_add_ext_groups_step(struct tevent_req *req);
426 : static errno_t ipa_add_ad_memberships_recv(struct tevent_req *req,
427 : int *dp_error_out);
428 :
429 0 : struct tevent_req *ipa_get_ad_memberships_send(TALLOC_CTX *mem_ctx,
430 : struct tevent_context *ev,
431 : struct be_acct_req *ar,
432 : struct ipa_server_mode_ctx *server_mode,
433 : struct sss_domain_info *user_dom,
434 : struct sdap_id_ctx *sdap_id_ctx,
435 : const char *domain)
436 : {
437 : int ret;
438 : struct tevent_req *req;
439 : struct tevent_req *subreq;
440 : struct get_ad_membership_state *state;
441 :
442 0 : req = tevent_req_create(mem_ctx, &state, struct get_ad_membership_state);
443 0 : if (req == NULL) {
444 0 : DEBUG(SSSDBG_OP_FAILURE, "tevent_req_create failed.\n");
445 0 : return NULL;
446 : }
447 :
448 0 : state->ev = ev;
449 0 : state->user_dom = user_dom;
450 0 : state->sdap_id_ctx = sdap_id_ctx;
451 0 : state->srv = NULL;
452 0 : state->domain = domain;
453 0 : state->dp_error = -1;
454 :
455 0 : if (((ar->entry_type & BE_REQ_TYPE_MASK) != BE_REQ_INITGROUPS
456 0 : && (ar->entry_type & BE_REQ_TYPE_MASK) != BE_REQ_USER)
457 0 : || ar->filter_type != BE_FILTER_NAME) {
458 0 : DEBUG(SSSDBG_OP_FAILURE, "Unsupported request type.\n");
459 0 : ret = EINVAL;
460 0 : goto done;
461 : }
462 :
463 0 : state->user_name = talloc_strdup(state, ar->filter_value);
464 0 : if (state->user_name == NULL) {
465 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_Strdup failed.\n");
466 0 : ret = ENOMEM;
467 0 : goto done;
468 : }
469 :
470 0 : state->sdap_op = sdap_id_op_create(state,
471 0 : state->sdap_id_ctx->conn->conn_cache);
472 0 : if (state->sdap_op == NULL) {
473 0 : DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create failed\n");
474 0 : ret = ENOMEM;
475 0 : goto done;
476 : }
477 :
478 0 : state->server_mode = server_mode;
479 0 : if (server_mode->ext_groups == NULL) {
480 0 : server_mode->ext_groups = talloc_zero(server_mode,
481 : struct ipa_ext_groups);
482 0 : if (server_mode->ext_groups == NULL) {
483 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
484 0 : ret = ENOMEM;
485 0 : goto done;
486 : }
487 : }
488 :
489 0 : if (server_mode->ext_groups->next_update > time(NULL)) {
490 0 : DEBUG(SSSDBG_TRACE_FUNC, "External group information still valid.\n");
491 0 : ret = ipa_add_ext_groups_step(req);
492 0 : if (ret == EOK) {
493 0 : goto done;
494 0 : } else if (ret == EAGAIN) {
495 0 : return req;
496 : } else {
497 0 : DEBUG(SSSDBG_OP_FAILURE, "ipa_add_ext_groups_step failed.\n");
498 0 : goto done;
499 : }
500 :
501 : }
502 :
503 0 : subreq = sdap_id_op_connect_send(state->sdap_op, state, &ret);
504 0 : if (subreq == NULL) {
505 0 : DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_connect_send failed: %d(%s).\n",
506 : ret, strerror(ret));
507 0 : goto done;
508 : }
509 :
510 0 : tevent_req_set_callback(subreq, ipa_get_ad_memberships_connect_done, req);
511 :
512 0 : return req;
513 :
514 : done:
515 0 : if (ret != EOK) {
516 0 : state->dp_error = DP_ERR_FATAL;
517 0 : tevent_req_error(req, ret);
518 : } else {
519 0 : state->dp_error = DP_ERR_OK;
520 0 : tevent_req_done(req);
521 : }
522 0 : tevent_req_post(req, state->ev);
523 :
524 0 : return req;
525 : }
526 :
527 0 : static void ipa_get_ad_memberships_connect_done(struct tevent_req *subreq)
528 : {
529 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
530 : struct tevent_req);
531 0 : struct get_ad_membership_state *state = tevent_req_data(req,
532 : struct get_ad_membership_state);
533 : int ret;
534 : char *basedn;
535 :
536 0 : ret = sdap_id_op_connect_recv(subreq, &state->dp_error);
537 0 : talloc_zfree(subreq);
538 0 : if (ret != EOK) {
539 0 : if (state->dp_error == DP_ERR_OFFLINE) {
540 0 : DEBUG(SSSDBG_MINOR_FAILURE,
541 : "No IPA server is available, going offline\n");
542 : } else {
543 0 : DEBUG(SSSDBG_OP_FAILURE,
544 : "Failed to connect to IPA server: [%d](%s)\n",
545 : ret, strerror(ret));
546 : }
547 :
548 0 : goto fail;
549 : }
550 :
551 :
552 0 : ret = domain_to_basedn(state, state->domain, &basedn);
553 0 : if (ret != EOK) {
554 0 : DEBUG(SSSDBG_OP_FAILURE, "domain_to_basedn failed.\n");
555 0 : goto fail;
556 : }
557 :
558 0 : subreq = sdap_get_generic_send(state, state->ev, state->sdap_id_ctx->opts,
559 : sdap_id_op_handle(state->sdap_op), basedn,
560 : LDAP_SCOPE_SUBTREE,
561 : IPA_EXT_GROUPS_FILTER, NULL, NULL, 0,
562 0 : dp_opt_get_int(state->sdap_id_ctx->opts->basic,
563 : SDAP_ENUM_SEARCH_TIMEOUT),
564 : false);
565 0 : if (subreq == NULL) {
566 0 : DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send failed.\n");
567 0 : ret = ENOMEM;
568 0 : goto fail;
569 : }
570 :
571 0 : tevent_req_set_callback(subreq, ipa_get_ext_groups_done, req);
572 0 : return;
573 :
574 : fail:
575 0 : tevent_req_error(req, ret);
576 0 : return;
577 : }
578 :
579 0 : static void ipa_get_ext_groups_done(struct tevent_req *subreq)
580 : {
581 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
582 : struct tevent_req);
583 0 : struct get_ad_membership_state *state = tevent_req_data(req,
584 : struct get_ad_membership_state);
585 : int ret;
586 : hash_table_t *ext_group_hash;
587 :
588 0 : ret = sdap_get_generic_recv(subreq, state,
589 : &state->reply_count, &state->reply);
590 0 : talloc_zfree(subreq);
591 0 : if (ret != EOK) {
592 0 : DEBUG(SSSDBG_OP_FAILURE, "ipa_get_ext_groups request failed.\n");
593 0 : tevent_req_error(req, ret);
594 0 : return;
595 : }
596 :
597 0 : DEBUG(SSSDBG_TRACE_FUNC, "[%zu] external groups found.\n",
598 : state->reply_count);
599 :
600 0 : ret = process_ext_groups(state->server_mode->ext_groups,
601 : state->reply_count, state->reply, &ext_group_hash);
602 0 : if (ret != EOK) {
603 0 : DEBUG(SSSDBG_OP_FAILURE, "process_ext_groups failed.\n");
604 0 : goto fail;
605 : }
606 :
607 0 : state->server_mode->ext_groups->ext_groups = ext_group_hash;
608 : /* Do we have to make the update timeout configurable? */
609 0 : state->server_mode->ext_groups->next_update = time(NULL) + 10;
610 :
611 0 : ret = ipa_add_ext_groups_step(req);
612 0 : if (ret == EOK) {
613 0 : tevent_req_done(req);
614 0 : return;
615 0 : } else if (ret == EAGAIN) {
616 0 : return;
617 : } else {
618 0 : DEBUG(SSSDBG_OP_FAILURE, "ipa_add_ext_groups_step failed.\n");
619 0 : goto fail;
620 : }
621 :
622 : fail:
623 0 : tevent_req_error(req, ret);
624 0 : return;
625 : }
626 :
627 0 : static errno_t ipa_add_ext_groups_step(struct tevent_req *req)
628 : {
629 0 : struct get_ad_membership_state *state = tevent_req_data(req,
630 : struct get_ad_membership_state);
631 : struct ldb_dn *user_dn;
632 : int ret;
633 0 : char **groups = NULL;
634 : struct tevent_req *subreq;
635 :
636 0 : ret = find_ipa_ext_memberships(state, state->user_name, state->user_dom,
637 0 : state->server_mode->ext_groups->ext_groups,
638 : &user_dn, &groups);
639 0 : if (ret != EOK) {
640 0 : DEBUG(SSSDBG_OP_FAILURE, "find_ipa_ext_memberships failed.\n");
641 0 : goto fail;
642 : }
643 :
644 0 : if (groups == NULL) {
645 0 : DEBUG(SSSDBG_TRACE_ALL, "No external groups memberships found.\n");
646 0 : state->dp_error = DP_ERR_OK;
647 0 : return EOK;
648 : }
649 :
650 0 : subreq = ipa_add_ad_memberships_send(state, state->ev, state->sdap_id_ctx,
651 : user_dn, state->user_dom, groups,
652 0 : state->sdap_id_ctx->be->domain);
653 0 : if (subreq == NULL) {
654 0 : DEBUG(SSSDBG_OP_FAILURE, "ipa_add_ad_memberships_send failed.\n");
655 0 : ret = ENOMEM;
656 0 : goto fail;
657 : }
658 :
659 0 : tevent_req_set_callback(subreq, ipa_add_ad_memberships_done, req);
660 0 : return EAGAIN;
661 :
662 : fail:
663 0 : tevent_req_error(req, ret);
664 0 : return ret;
665 : }
666 :
667 0 : static void ipa_add_ad_memberships_done(struct tevent_req *subreq)
668 : {
669 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
670 : struct tevent_req);
671 0 : struct get_ad_membership_state *state = tevent_req_data(req,
672 : struct get_ad_membership_state);
673 : int ret;
674 :
675 0 : ret = ipa_add_ad_memberships_recv(subreq, &state->dp_error);
676 0 : talloc_zfree(subreq);
677 0 : if (ret != EOK) {
678 0 : DEBUG(SSSDBG_OP_FAILURE, "ipa_add_ad_memberships request failed.\n");
679 0 : tevent_req_error(req, ret);
680 0 : return;
681 : }
682 :
683 0 : state->dp_error = DP_ERR_OK;
684 0 : tevent_req_done(req);
685 0 : return;
686 : }
687 :
688 0 : errno_t ipa_get_ad_memberships_recv(struct tevent_req *req, int *dp_error_out)
689 : {
690 0 : struct get_ad_membership_state *state = tevent_req_data(req,
691 : struct get_ad_membership_state);
692 :
693 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
694 :
695 0 : if (dp_error_out) {
696 0 : *dp_error_out = state->dp_error;
697 : }
698 :
699 0 : return EOK;
700 : }
701 :
702 : struct add_ad_membership_state {
703 : struct tevent_context *ev;
704 : struct sdap_id_ctx *sdap_id_ctx;
705 : struct sdap_id_op *sdap_op;
706 : struct ldb_dn *user_dn;
707 : struct sss_domain_info *user_dom;
708 : struct sss_domain_info *group_dom;
709 : char **groups;
710 : int dp_error;
711 : size_t iter;
712 : struct sdap_domain *group_sdom;
713 : };
714 :
715 : static void ipa_add_ad_memberships_connect_done(struct tevent_req *subreq);
716 : static void ipa_add_ad_memberships_get_next(struct tevent_req *req);
717 : static void ipa_add_ad_memberships_get_group_done(struct tevent_req *subreq);
718 0 : static struct tevent_req *ipa_add_ad_memberships_send(TALLOC_CTX *mem_ctx,
719 : struct tevent_context *ev,
720 : struct sdap_id_ctx *sdap_id_ctx,
721 : struct ldb_dn *user_dn,
722 : struct sss_domain_info *user_dom,
723 : char **groups,
724 : struct sss_domain_info *group_dom)
725 : {
726 : int ret;
727 : struct tevent_req *req;
728 : struct tevent_req *subreq;
729 : struct add_ad_membership_state *state;
730 0 : bool missing_groups = false;
731 :
732 0 : req = tevent_req_create(mem_ctx, &state, struct add_ad_membership_state);
733 0 : if (req == NULL) {
734 0 : DEBUG(SSSDBG_OP_FAILURE, "tevent_req_create failed.\n");
735 0 : return NULL;
736 : }
737 :
738 0 : state->ev = ev;
739 0 : state->user_dom = user_dom;
740 0 : state->sdap_id_ctx = sdap_id_ctx;
741 0 : state->user_dn = user_dn;
742 0 : state->group_dom = group_dom;
743 0 : state->groups = groups;
744 0 : state->dp_error = -1;
745 0 : state->iter = 0;
746 0 : state->group_sdom = sdap_domain_get(sdap_id_ctx->opts, group_dom);
747 0 : if (state->group_sdom == NULL) {
748 0 : ret = EIO;
749 0 : goto done;
750 : }
751 :
752 0 : ret = add_ad_user_to_cached_groups(user_dn, user_dom, group_dom, groups,
753 : &missing_groups);
754 0 : if (ret != EOK) {
755 0 : DEBUG(SSSDBG_OP_FAILURE, "add_ad_user_to_cached_groups failed.\n");
756 0 : goto done;
757 : }
758 :
759 0 : if (!missing_groups) {
760 0 : DEBUG(SSSDBG_TRACE_ALL, "All groups found in cache.\n");
761 0 : ret = EOK;
762 0 : goto done;
763 : }
764 :
765 0 : state->sdap_op = sdap_id_op_create(state,
766 0 : state->sdap_id_ctx->conn->conn_cache);
767 0 : if (state->sdap_op == NULL) {
768 0 : DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create failed\n");
769 0 : ret = ENOMEM;
770 0 : goto done;
771 : }
772 :
773 0 : subreq = sdap_id_op_connect_send(state->sdap_op, state, &ret);
774 0 : if (subreq == NULL) {
775 0 : DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_connect_send failed: %d(%s).\n",
776 : ret, strerror(ret));
777 0 : goto done;
778 : }
779 :
780 0 : tevent_req_set_callback(subreq, ipa_add_ad_memberships_connect_done, req);
781 :
782 0 : return req;
783 :
784 : done:
785 0 : if (ret != EOK) {
786 0 : state->dp_error = DP_ERR_FATAL;
787 0 : tevent_req_error(req, ret);
788 : } else {
789 0 : state->dp_error = DP_ERR_OK;
790 0 : tevent_req_done(req);
791 : }
792 0 : tevent_req_post(req, state->ev);
793 :
794 0 : return req;
795 : }
796 :
797 0 : static void ipa_add_ad_memberships_connect_done(struct tevent_req *subreq)
798 : {
799 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
800 : struct tevent_req);
801 0 : struct add_ad_membership_state *state = tevent_req_data(req,
802 : struct add_ad_membership_state);
803 : int ret;
804 :
805 0 : ret = sdap_id_op_connect_recv(subreq, &state->dp_error);
806 0 : talloc_zfree(subreq);
807 0 : if (ret != EOK) {
808 0 : if (state->dp_error == DP_ERR_OFFLINE) {
809 0 : DEBUG(SSSDBG_MINOR_FAILURE,
810 : "No IPA server is available, going offline\n");
811 : } else {
812 0 : DEBUG(SSSDBG_OP_FAILURE,
813 : "Failed to connect to IPA server: [%d](%s)\n",
814 : ret, strerror(ret));
815 : }
816 :
817 0 : tevent_req_error(req, ret);
818 0 : return;
819 : }
820 :
821 0 : state->iter = 0;
822 0 : ipa_add_ad_memberships_get_next(req);
823 : }
824 :
825 0 : static void ipa_add_ad_memberships_get_next(struct tevent_req *req)
826 : {
827 0 : struct add_ad_membership_state *state = tevent_req_data(req,
828 : struct add_ad_membership_state);
829 : struct tevent_req *subreq;
830 : struct ldb_dn *group_dn;
831 : int ret;
832 : const struct ldb_val *val;
833 : bool missing_groups;
834 :
835 0 : while (state->groups[state->iter] != NULL
836 0 : && state->groups[state->iter][0] == '\0') {
837 0 : state->iter++;
838 : }
839 :
840 0 : if (state->groups[state->iter] == NULL) {
841 0 : ret = add_ad_user_to_cached_groups(state->user_dn, state->user_dom,
842 : state->group_dom, state->groups,
843 : &missing_groups);
844 0 : if (ret != EOK) {
845 0 : DEBUG(SSSDBG_OP_FAILURE, "add_ad_user_to_cached_groups failed.\n");
846 0 : goto fail;
847 : }
848 :
849 0 : if (missing_groups) {
850 0 : DEBUG(SSSDBG_CRIT_FAILURE, "There are unresolved external group "
851 : "memberships even after all groups "
852 : "have been looked up on the LDAP "
853 : "server.\n");
854 : }
855 0 : tevent_req_done(req);
856 0 : return;
857 : }
858 :
859 0 : group_dn = ldb_dn_new(state, sysdb_ctx_get_ldb(state->group_dom->sysdb),
860 0 : state->groups[state->iter]);
861 0 : if (group_dn == NULL) {
862 0 : DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed.\n");
863 0 : ret = ENOMEM;
864 0 : goto fail;
865 : }
866 :
867 0 : val = ldb_dn_get_component_val(group_dn, 0);
868 :
869 : /* TODO: here is would be useful for have a filter type like BE_FILTER_DN to
870 : * directly fetch the group with the corresponding DN. */
871 0 : subreq = groups_get_send(state, state->ev,
872 : state->sdap_id_ctx, state->group_sdom,
873 0 : state->sdap_id_ctx->conn,
874 0 : (const char *) val->data,
875 : BE_FILTER_NAME, BE_ATTR_CORE,
876 : false, false);
877 0 : if (subreq == NULL) {
878 0 : DEBUG(SSSDBG_OP_FAILURE, "groups_get_send failed.\n");
879 0 : ret = ENOMEM;
880 0 : goto fail;
881 : }
882 :
883 0 : tevent_req_set_callback(subreq, ipa_add_ad_memberships_get_group_done, req);
884 0 : return;
885 :
886 : fail:
887 0 : tevent_req_error(req, ret);
888 : }
889 :
890 0 : static void ipa_add_ad_memberships_get_group_done(struct tevent_req *subreq)
891 : {
892 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
893 : struct tevent_req);
894 0 : struct add_ad_membership_state *state = tevent_req_data(req,
895 : struct add_ad_membership_state);
896 : int ret;
897 :
898 0 : ret = groups_get_recv(subreq, &state->dp_error, NULL);
899 0 : talloc_zfree(subreq);
900 0 : if (ret != EOK) {
901 0 : DEBUG(SSSDBG_OP_FAILURE, "Failed to read group [%s] from LDAP [%d](%s)\n",
902 : state->groups[state->iter], ret, strerror(ret));
903 :
904 0 : tevent_req_error(req, ret);
905 0 : return;
906 : }
907 :
908 0 : state->iter++;
909 0 : ipa_add_ad_memberships_get_next(req);
910 : }
911 :
912 0 : static errno_t ipa_add_ad_memberships_recv(struct tevent_req *req,
913 : int *dp_error_out)
914 : {
915 0 : struct add_ad_membership_state *state = tevent_req_data(req,
916 : struct add_ad_membership_state);
917 :
918 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
919 :
920 0 : if (dp_error_out) {
921 0 : *dp_error_out = state->dp_error;
922 : }
923 :
924 0 : return EOK;
925 : }
|