Line data Source code
1 : /*
2 : SSSD
3 :
4 : Authors:
5 : Stephen Gallagher <sgallagh@redhat.com>
6 :
7 : Copyright (C) 2012 Red Hat
8 :
9 : This program is free software; you can redistribute it and/or modify
10 : it under the terms of the GNU General Public License as published by
11 : the Free Software Foundation; either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : This program is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU General Public License for more details.
18 :
19 : You should have received a copy of the GNU General Public License
20 : along with this program. If not, see <http://www.gnu.org/licenses/>.
21 : */
22 : #include "util/util.h"
23 : #include "util/strtonum.h"
24 : #include "providers/ad/ad_common.h"
25 : #include "providers/ad/ad_id.h"
26 : #include "providers/ad/ad_domain_info.h"
27 : #include "providers/ldap/sdap_async_enum.h"
28 : #include "providers/ldap/sdap_idmap.h"
29 :
30 : static void
31 0 : disable_gc(struct ad_options *ad_options)
32 : {
33 : errno_t ret;
34 :
35 0 : if (dp_opt_get_bool(ad_options->basic, AD_ENABLE_GC) == false) {
36 0 : return;
37 : }
38 :
39 0 : DEBUG(SSSDBG_IMPORTANT_INFO, "POSIX attributes were requested "
40 : "but are not present on the server side. Global Catalog "
41 : "lookups will be disabled\n");
42 :
43 0 : ret = dp_opt_set_bool(ad_options->basic,
44 : AD_ENABLE_GC, false);
45 0 : if (ret != EOK) {
46 0 : DEBUG(SSSDBG_MINOR_FAILURE,
47 : "Could not turn off GC support\n");
48 : /* Not fatal */
49 : }
50 : }
51 :
52 : struct ad_handle_acct_info_state {
53 : struct be_req *breq;
54 : struct be_acct_req *ar;
55 : struct sdap_id_ctx *ctx;
56 : struct sdap_id_conn_ctx **conn;
57 : struct sdap_domain *sdom;
58 : size_t cindex;
59 : struct ad_options *ad_options;
60 :
61 : int dp_error;
62 : const char *err;
63 : };
64 :
65 : static errno_t ad_handle_acct_info_step(struct tevent_req *req);
66 : static void ad_handle_acct_info_done(struct tevent_req *subreq);
67 :
68 : struct tevent_req *
69 0 : ad_handle_acct_info_send(TALLOC_CTX *mem_ctx,
70 : struct be_req *breq,
71 : struct be_acct_req *ar,
72 : struct sdap_id_ctx *ctx,
73 : struct ad_options *ad_options,
74 : struct sdap_domain *sdom,
75 : struct sdap_id_conn_ctx **conn)
76 : {
77 : struct tevent_req *req;
78 : struct ad_handle_acct_info_state *state;
79 0 : struct be_ctx *be_ctx = be_req_get_be_ctx(breq);
80 : errno_t ret;
81 :
82 0 : req = tevent_req_create(mem_ctx, &state, struct ad_handle_acct_info_state);
83 0 : if (req == NULL) {
84 0 : return NULL;
85 : }
86 0 : state->breq = breq;
87 0 : state->ar = ar;
88 0 : state->ctx = ctx;
89 0 : state->sdom = sdom;
90 0 : state->conn = conn;
91 0 : state->ad_options = ad_options;
92 0 : state->cindex = 0;
93 :
94 0 : if (sss_domain_get_state(sdom->dom) == DOM_INACTIVE) {
95 0 : ret = ERR_SUBDOM_INACTIVE;
96 0 : goto immediate;
97 : }
98 :
99 0 : ret = ad_handle_acct_info_step(req);
100 0 : if (ret != EAGAIN) {
101 0 : goto immediate;
102 : }
103 :
104 : /* Lookup in progress */
105 0 : return req;
106 :
107 : immediate:
108 0 : if (ret != EOK) {
109 0 : tevent_req_error(req, ret);
110 : } else {
111 0 : tevent_req_done(req);
112 : }
113 0 : tevent_req_post(req, be_ctx->ev);
114 0 : return req;
115 : }
116 :
117 : static errno_t
118 0 : ad_handle_acct_info_step(struct tevent_req *req)
119 : {
120 : struct tevent_req *subreq;
121 0 : struct ad_handle_acct_info_state *state = tevent_req_data(req,
122 : struct ad_handle_acct_info_state);
123 0 : bool noexist_delete = false;
124 :
125 0 : if (state->conn[state->cindex] == NULL) {
126 0 : return EOK;
127 : }
128 :
129 0 : if (state->conn[state->cindex+1] == NULL) {
130 0 : noexist_delete = true;
131 : }
132 :
133 0 : subreq = sdap_handle_acct_req_send(state, state->ctx->be,
134 : state->ar, state->ctx,
135 : state->sdom,
136 0 : state->conn[state->cindex],
137 : noexist_delete);
138 0 : if (subreq == NULL) {
139 0 : return ENOMEM;
140 : }
141 0 : tevent_req_set_callback(subreq, ad_handle_acct_info_done, req);
142 0 : return EAGAIN;
143 : }
144 :
145 : static void
146 0 : ad_handle_acct_info_done(struct tevent_req *subreq)
147 : {
148 : errno_t ret;
149 : int dp_error;
150 : int sdap_err;
151 : const char *err;
152 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
153 : struct tevent_req);
154 0 : struct ad_handle_acct_info_state *state = tevent_req_data(req,
155 : struct ad_handle_acct_info_state);
156 :
157 0 : ret = sdap_handle_acct_req_recv(subreq, &dp_error, &err, &sdap_err);
158 0 : if (dp_error == DP_ERR_OFFLINE
159 0 : && state->conn[state->cindex+1] != NULL
160 0 : && state->conn[state->cindex]->ignore_mark_offline) {
161 : /* This is a special case: GC does not work.
162 : * We need to Fall back to ldap
163 : */
164 0 : ret = EOK;
165 0 : sdap_err = ENOENT;
166 : }
167 0 : talloc_zfree(subreq);
168 0 : if (ret != EOK) {
169 : /* if GC was not used dp error should be set */
170 0 : state->dp_error = dp_error;
171 0 : state->err = err;
172 :
173 0 : goto fail;
174 : }
175 :
176 0 : if (sdap_err == EOK) {
177 0 : tevent_req_done(req);
178 0 : return;
179 0 : } else if (sdap_err == ERR_NO_POSIX) {
180 0 : disable_gc(state->ad_options);
181 0 : } else if (sdap_err != ENOENT) {
182 0 : ret = EIO;
183 0 : goto fail;
184 : }
185 :
186 : /* Ret is only ENOENT or ERR_NO_POSIX now. Try the next connection */
187 0 : state->cindex++;
188 0 : ret = ad_handle_acct_info_step(req);
189 0 : if (ret != EAGAIN) {
190 : /* No additional search in progress. Save the last
191 : * error status, we'll be returning it.
192 : */
193 0 : state->dp_error = dp_error;
194 0 : state->err = err;
195 :
196 0 : if (ret == EOK) {
197 : /* No more connections */
198 0 : tevent_req_done(req);
199 : } else {
200 0 : goto fail;
201 : }
202 0 : return;
203 : }
204 :
205 : /* Another lookup in progress */
206 0 : return;
207 :
208 : fail:
209 0 : if (IS_SUBDOMAIN(state->sdom->dom)) {
210 : /* Deactivate subdomain on lookup errors instead of going
211 : * offline completely.
212 : * This is a stopgap, until our failover is per-domain,
213 : * not per-backend. Unfortunately, we can't rewrite the error
214 : * code on some reported codes only, because sdap_id_op code
215 : * encapsulated the failover as well..
216 : */
217 0 : ret = ERR_SUBDOM_INACTIVE;
218 : }
219 0 : tevent_req_error(req, ret);
220 0 : return;
221 : }
222 :
223 : errno_t
224 0 : ad_handle_acct_info_recv(struct tevent_req *req,
225 : int *_dp_error, const char **_err)
226 : {
227 0 : struct ad_handle_acct_info_state *state = tevent_req_data(req,
228 : struct ad_handle_acct_info_state);
229 :
230 0 : if (_dp_error) {
231 0 : *_dp_error = state->dp_error;
232 : }
233 :
234 0 : if (_err) {
235 0 : *_err = state->err;
236 : }
237 :
238 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
239 0 : return EOK;
240 : }
241 :
242 : struct sdap_id_conn_ctx **
243 0 : get_conn_list(struct be_req *breq, struct ad_id_ctx *ad_ctx,
244 : struct sss_domain_info *dom, struct be_acct_req *ar)
245 : {
246 : struct sdap_id_conn_ctx **clist;
247 :
248 0 : switch (ar->entry_type & BE_REQ_TYPE_MASK) {
249 : case BE_REQ_USER: /* user */
250 0 : clist = ad_user_conn_list(breq, ad_ctx, dom);
251 0 : break;
252 : case BE_REQ_BY_SECID: /* by SID */
253 : case BE_REQ_USER_AND_GROUP: /* get SID */
254 : case BE_REQ_GROUP: /* group */
255 : case BE_REQ_INITGROUPS: /* init groups for user */
256 0 : clist = ad_gc_conn_list(breq, ad_ctx, dom);
257 0 : break;
258 : default:
259 : /* Requests for other object should only contact LDAP by default */
260 0 : clist = ad_ldap_conn_list(breq, ad_ctx, dom);
261 0 : break;
262 : }
263 :
264 0 : return clist;
265 : }
266 :
267 0 : static bool ad_account_can_shortcut(struct be_ctx *be_ctx,
268 : struct sdap_idmap_ctx *idmap_ctx,
269 : int filter_type,
270 : const char *filter_value,
271 : const char *filter_domain)
272 : {
273 0 : struct sss_domain_info *domain = be_ctx->domain;
274 0 : struct sss_domain_info *req_dom = NULL;
275 : enum idmap_error_code err;
276 0 : char *sid = NULL;
277 0 : const char *csid = NULL;
278 : uint32_t id;
279 0 : bool shortcut = false;
280 : errno_t ret;
281 :
282 0 : if (!sdap_idmap_domain_has_algorithmic_mapping(idmap_ctx, domain->name,
283 0 : domain->domain_id)) {
284 0 : goto done;
285 : }
286 :
287 0 : switch (filter_type) {
288 : case BE_FILTER_IDNUM:
289 : /* convert value to ID */
290 0 : errno = 0;
291 0 : id = strtouint32(filter_value, NULL, 10);
292 0 : if (errno != 0) {
293 0 : ret = errno;
294 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Unable to convert filter value to "
295 : "number [%d]: %s\n", ret, strerror(ret));
296 0 : goto done;
297 : }
298 :
299 : /* convert the ID to its SID equivalent */
300 0 : err = sss_idmap_unix_to_sid(idmap_ctx->map, id, &sid);
301 0 : if (err != IDMAP_SUCCESS) {
302 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Mapping ID [%s] to SID failed: "
303 : "[%s]\n", filter_value, idmap_error_string(err));
304 0 : goto done;
305 : }
306 : /* fall through */
307 : case BE_FILTER_SECID:
308 0 : csid = sid == NULL ? filter_value : sid;
309 :
310 0 : req_dom = find_domain_by_sid(domain, csid);
311 0 : if (req_dom == NULL) {
312 0 : DEBUG(SSSDBG_OP_FAILURE, "Invalid domain\n");
313 0 : goto done;
314 : }
315 :
316 0 : if (strcasecmp(req_dom->name, filter_domain) != 0) {
317 0 : shortcut = true;
318 : }
319 0 : break;
320 : default:
321 0 : break;
322 : }
323 :
324 : done:
325 0 : if (sid != NULL) {
326 0 : sss_idmap_free_sid(idmap_ctx->map, sid);
327 : }
328 :
329 0 : return shortcut;
330 : }
331 :
332 : static void ad_account_info_complete(struct tevent_req *req);
333 :
334 : struct ad_account_info_state {
335 : struct be_req *be_req;
336 : struct sss_domain_info *dom;
337 : };
338 :
339 : void
340 0 : ad_account_info_handler(struct be_req *be_req)
341 : {
342 : struct ad_id_ctx *ad_ctx;
343 : struct be_acct_req *ar;
344 : struct sdap_id_ctx *sdap_id_ctx;
345 0 : struct be_ctx *be_ctx = be_req_get_be_ctx(be_req);
346 : struct tevent_req *req;
347 : struct sss_domain_info *dom;
348 : struct sdap_domain *sdom;
349 : struct sdap_id_conn_ctx **clist;
350 : bool shortcut;
351 : errno_t ret;
352 : struct ad_account_info_state *state;
353 :
354 0 : ad_ctx = talloc_get_type(be_ctx->bet_info[BET_ID].pvt_bet_data,
355 : struct ad_id_ctx);
356 0 : ar = talloc_get_type(be_req_get_data(be_req), struct be_acct_req);
357 0 : sdap_id_ctx = ad_ctx->sdap_id_ctx;
358 :
359 0 : if (be_is_offline(be_ctx)) {
360 0 : return be_req_terminate(be_req, DP_ERR_OFFLINE, EAGAIN, "Offline");
361 : }
362 :
363 0 : if (sdap_is_enum_request(ar)) {
364 0 : DEBUG(SSSDBG_TRACE_LIBS, "Skipping enumeration on demand\n");
365 0 : return sdap_handler_done(be_req, DP_ERR_OK, EOK, "Success");
366 : }
367 :
368 : /* Try to shortcut if this is ID or SID search and it belongs to
369 : * other domain range than is in ar->domain. */
370 0 : shortcut = ad_account_can_shortcut(be_ctx, sdap_id_ctx->opts->idmap_ctx,
371 0 : ar->filter_type, ar->filter_value,
372 0 : ar->domain);
373 0 : if (shortcut) {
374 0 : DEBUG(SSSDBG_TRACE_FUNC, "This ID is from different domain\n");
375 0 : be_req_terminate(be_req, DP_ERR_OK, EOK, NULL);
376 0 : return;
377 : }
378 :
379 0 : dom = be_ctx->domain;
380 0 : if (strcasecmp(ar->domain, be_ctx->domain->name) != 0) {
381 : /* Subdomain request, verify subdomain */
382 0 : dom = find_domain_by_name(be_ctx->domain, ar->domain, true);
383 : }
384 :
385 0 : if (dom == NULL) {
386 0 : ret = EINVAL;
387 0 : goto fail;
388 : }
389 :
390 : /* Determine whether to connect to GC, LDAP or try both */
391 0 : clist = get_conn_list(be_req, ad_ctx, dom, ar);
392 0 : if (clist == NULL) {
393 0 : ret = EIO;
394 0 : goto fail;
395 : }
396 :
397 0 : sdom = sdap_domain_get(sdap_id_ctx->opts, dom);
398 0 : if (sdom == NULL) {
399 0 : ret = EIO;
400 0 : goto fail;
401 : }
402 :
403 0 : state = talloc(be_req, struct ad_account_info_state);
404 0 : if (state == NULL) {
405 0 : ret = ENOMEM;
406 0 : goto fail;
407 : }
408 0 : state->dom = sdom->dom;
409 0 : state->be_req = be_req;
410 :
411 0 : req = ad_handle_acct_info_send(be_req, be_req, ar, sdap_id_ctx,
412 : ad_ctx->ad_options, sdom, clist);
413 0 : if (req == NULL) {
414 0 : ret = ENOMEM;
415 0 : goto fail;
416 : }
417 0 : tevent_req_set_callback(req, ad_account_info_complete, state);
418 0 : return;
419 :
420 : fail:
421 0 : be_req_terminate(be_req, DP_ERR_FATAL, ret, NULL);
422 : }
423 :
424 : static void
425 0 : ad_account_info_complete(struct tevent_req *req)
426 : {
427 : struct be_req *be_req;
428 : errno_t ret;
429 : int dp_error;
430 0 : const char *error_text = "Internal error";
431 : const char *req_error_text;
432 : struct ad_account_info_state *state;
433 :
434 0 : state = tevent_req_callback_data(req, struct ad_account_info_state);
435 0 : be_req = state->be_req;
436 :
437 0 : ret = ad_handle_acct_info_recv(req, &dp_error, &req_error_text);
438 0 : talloc_zfree(req);
439 0 : if (ret == ERR_SUBDOM_INACTIVE) {
440 0 : be_mark_dom_offline(state->dom, be_req_get_be_ctx(be_req));
441 0 : return be_req_terminate(be_req, DP_ERR_OFFLINE, EAGAIN, "Offline");
442 0 : } else if (dp_error == DP_ERR_OK) {
443 0 : if (ret == EOK) {
444 0 : error_text = NULL;
445 : } else {
446 0 : DEBUG(SSSDBG_FATAL_FAILURE,
447 : "Bug: dp_error is OK on failed request\n");
448 0 : dp_error = DP_ERR_FATAL;
449 0 : error_text = req_error_text;
450 : }
451 0 : } else if (dp_error == DP_ERR_OFFLINE) {
452 0 : error_text = "Offline";
453 0 : } else if (dp_error == DP_ERR_FATAL && ret == ENOMEM) {
454 0 : error_text = "Out of memory";
455 : } else {
456 0 : error_text = req_error_text;
457 : }
458 :
459 0 : return be_req_terminate(be_req, dp_error, ret, error_text);
460 : }
461 :
462 : void
463 0 : ad_check_online(struct be_req *be_req)
464 : {
465 : struct ad_id_ctx *ad_ctx;
466 0 : struct be_ctx *be_ctx = be_req_get_be_ctx(be_req);
467 :
468 0 : ad_ctx = talloc_get_type(be_ctx->bet_info[BET_ID].pvt_bet_data,
469 : struct ad_id_ctx);
470 :
471 0 : return sdap_do_online_check(be_req, ad_ctx->sdap_id_ctx);
472 : }
473 :
474 : struct ad_enumeration_state {
475 : struct ad_id_ctx *id_ctx;
476 : struct ldap_enum_ctx *ectx;
477 : struct sdap_id_op *sdap_op;
478 : struct tevent_context *ev;
479 :
480 : const char *realm;
481 : struct sdap_domain *sdom;
482 : struct sdap_domain *sditer;
483 : };
484 :
485 : static void ad_enumeration_conn_done(struct tevent_req *subreq);
486 : static void ad_enumeration_master_done(struct tevent_req *subreq);
487 : static errno_t ad_enum_sdom(struct tevent_req *req, struct sdap_domain *sd,
488 : struct ad_id_ctx *id_ctx);
489 : static void ad_enumeration_done(struct tevent_req *subreq);
490 :
491 : struct tevent_req *
492 0 : ad_enumeration_send(TALLOC_CTX *mem_ctx,
493 : struct tevent_context *ev,
494 : struct be_ctx *be_ctx,
495 : struct be_ptask *be_ptask,
496 : void *pvt)
497 : {
498 : struct tevent_req *req;
499 : struct tevent_req *subreq;
500 : struct ad_enumeration_state *state;
501 : struct ldap_enum_ctx *ectx;
502 : errno_t ret;
503 :
504 0 : req = tevent_req_create(mem_ctx, &state, struct ad_enumeration_state);
505 0 : if (req == NULL) return NULL;
506 :
507 0 : ectx = talloc_get_type(pvt, struct ldap_enum_ctx);
508 0 : if (ectx == NULL) {
509 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot retrieve ldap_enum_ctx!\n");
510 0 : ret = EFAULT;
511 0 : goto fail;
512 : }
513 :
514 0 : state->ectx = ectx;
515 0 : state->ev = ev;
516 0 : state->sdom = ectx->sdom;
517 0 : state->sditer = state->sdom;
518 0 : state->id_ctx = talloc_get_type(ectx->pvt, struct ad_id_ctx);
519 :
520 0 : state->realm = dp_opt_get_cstring(state->id_ctx->ad_options->basic,
521 : AD_KRB5_REALM);
522 0 : if (state->realm == NULL) {
523 0 : DEBUG(SSSDBG_CONF_SETTINGS, "Missing realm\n");
524 0 : ret = EINVAL;
525 0 : goto fail;
526 : }
527 :
528 0 : state->sdap_op = sdap_id_op_create(state,
529 0 : state->id_ctx->ldap_ctx->conn_cache);
530 0 : if (state->sdap_op == NULL) {
531 0 : DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create failed.\n");
532 0 : ret = ENOMEM;
533 0 : goto fail;
534 : }
535 :
536 0 : subreq = sdap_id_op_connect_send(state->sdap_op, state, &ret);
537 0 : if (subreq == NULL) {
538 0 : DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_connect_send failed: %d(%s).\n",
539 : ret, strerror(ret));
540 0 : goto fail;
541 : }
542 0 : tevent_req_set_callback(subreq, ad_enumeration_conn_done, req);
543 :
544 0 : return req;
545 :
546 : fail:
547 0 : tevent_req_error(req, ret);
548 0 : tevent_req_post(req, ev);
549 0 : return req;
550 : }
551 :
552 : static void
553 0 : ad_enumeration_conn_done(struct tevent_req *subreq)
554 : {
555 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
556 : struct tevent_req);
557 0 : struct ad_enumeration_state *state = tevent_req_data(req,
558 : struct ad_enumeration_state);
559 : int ret, dp_error;
560 :
561 0 : ret = sdap_id_op_connect_recv(subreq, &dp_error);
562 0 : talloc_zfree(subreq);
563 0 : if (ret != EOK) {
564 0 : if (dp_error == DP_ERR_OFFLINE) {
565 0 : DEBUG(SSSDBG_TRACE_FUNC,
566 : "Backend is marked offline, retry later!\n");
567 0 : tevent_req_done(req);
568 : } else {
569 0 : DEBUG(SSSDBG_MINOR_FAILURE,
570 : "Domain enumeration failed to connect to " \
571 : "LDAP server: (%d)[%s]\n", ret, strerror(ret));
572 0 : tevent_req_error(req, ret);
573 : }
574 0 : return;
575 : }
576 :
577 0 : subreq = ad_master_domain_send(state, state->ev,
578 0 : state->id_ctx->ldap_ctx,
579 : state->sdap_op,
580 0 : state->sdom->dom->name);
581 0 : if (subreq == NULL) {
582 0 : DEBUG(SSSDBG_OP_FAILURE, "ad_master_domain_send failed.\n");
583 0 : tevent_req_error(req, ret);
584 0 : return;
585 : }
586 0 : tevent_req_set_callback(subreq, ad_enumeration_master_done, req);
587 : }
588 :
589 : static void
590 0 : ad_enumeration_master_done(struct tevent_req *subreq)
591 : {
592 : errno_t ret;
593 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
594 : struct tevent_req);
595 0 : struct ad_enumeration_state *state = tevent_req_data(req,
596 : struct ad_enumeration_state);
597 : char *flat_name;
598 : char *master_sid;
599 : char *forest;
600 :
601 0 : ret = ad_master_domain_recv(subreq, state,
602 : &flat_name, &master_sid, NULL, &forest);
603 0 : talloc_zfree(subreq);
604 0 : if (ret != EOK) {
605 0 : DEBUG(SSSDBG_OP_FAILURE, "Cannot retrieve master domain info\n");
606 0 : tevent_req_error(req, ret);
607 0 : return;
608 : }
609 :
610 0 : ret = sysdb_master_domain_add_info(state->sdom->dom, state->realm,
611 : flat_name, master_sid, forest);
612 0 : if (ret != EOK) {
613 0 : DEBUG(SSSDBG_OP_FAILURE, "Cannot save master domain info\n");
614 0 : tevent_req_error(req, ret);
615 0 : return;
616 : }
617 :
618 0 : ret = ad_enum_sdom(req, state->sdom, state->id_ctx);
619 0 : if (ret != EOK) {
620 0 : DEBUG(SSSDBG_OP_FAILURE,
621 : "Could not enumerate domain %s\n", state->sdom->dom->name);
622 0 : tevent_req_error(req, ret);
623 0 : return;
624 : }
625 :
626 : /* Execution will resume in ad_enumeration_done */
627 : }
628 :
629 : static errno_t
630 0 : ad_enum_sdom(struct tevent_req *req,
631 : struct sdap_domain *sd,
632 : struct ad_id_ctx *id_ctx)
633 : {
634 : struct sdap_id_conn_ctx *user_conn;
635 : struct tevent_req *subreq;
636 0 : struct ad_enumeration_state *state = tevent_req_data(req,
637 : struct ad_enumeration_state);
638 :
639 0 : if (dp_opt_get_bool(id_ctx->ad_options->basic, AD_ENABLE_GC)) {
640 0 : user_conn = id_ctx->gc_ctx;
641 : } else {
642 0 : user_conn = id_ctx->ldap_ctx;
643 : }
644 :
645 : /* Groups are searched for in LDAP, users in GC. Services (if present,
646 : * which is unlikely in AD) from LDAP as well
647 : */
648 0 : subreq = sdap_dom_enum_ex_send(state, state->ev,
649 : id_ctx->sdap_id_ctx,
650 : sd,
651 : user_conn, /* Users */
652 : id_ctx->ldap_ctx, /* Groups */
653 : id_ctx->ldap_ctx); /* Services */
654 0 : if (subreq == NULL) {
655 : /* The ptask API will reschedule the enumeration on its own on
656 : * failure */
657 0 : DEBUG(SSSDBG_OP_FAILURE,
658 : "Failed to schedule enumeration, retrying later!\n");
659 0 : return ENOMEM;
660 : }
661 0 : tevent_req_set_callback(subreq, ad_enumeration_done, req);
662 :
663 0 : return EOK;
664 : }
665 :
666 : static errno_t ad_enum_cross_dom_members(struct sdap_options *opts,
667 : struct sss_domain_info *dom);
668 :
669 : static void
670 0 : ad_enumeration_done(struct tevent_req *subreq)
671 : {
672 : errno_t ret;
673 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
674 : struct tevent_req);
675 0 : struct ad_enumeration_state *state = tevent_req_data(req,
676 : struct ad_enumeration_state);
677 :
678 0 : ret = sdap_dom_enum_ex_recv(subreq);
679 0 : talloc_zfree(subreq);
680 0 : if (ret == ERR_NO_POSIX) {
681 : /* Retry enumerating the same domain again, this time w/o
682 : * connecting to GC
683 : */
684 0 : disable_gc(state->id_ctx->ad_options);
685 0 : ret = ad_enum_sdom(req, state->sditer, state->id_ctx);
686 0 : if (ret != EOK) {
687 0 : DEBUG(SSSDBG_OP_FAILURE,
688 : "Could not retry domain %s\n", state->sditer->dom->name);
689 0 : tevent_req_error(req, ret);
690 0 : return;
691 : }
692 :
693 : /* Execution will resume in ad_enumeration_done */
694 0 : return;
695 0 : } else if (ret != EOK) {
696 0 : DEBUG(SSSDBG_OP_FAILURE,
697 : "Could not enumerate domain %s\n", state->sditer->dom->name);
698 0 : tevent_req_error(req, ret);
699 0 : return;
700 : }
701 :
702 : do {
703 0 : state->sditer = state->sditer->next;
704 0 : } while (state->sditer &&
705 0 : state->sditer->dom->enumerate == false);
706 :
707 0 : if (state->sditer != NULL) {
708 0 : ret = ad_enum_sdom(req, state->sditer, state->sditer->pvt);
709 0 : if (ret != EOK) {
710 0 : DEBUG(SSSDBG_OP_FAILURE, "Could not enumerate domain %s\n",
711 : state->sditer->dom->name);
712 0 : tevent_req_error(req, ret);
713 0 : return;
714 : }
715 :
716 : /* Execution will resume in ad_enumeration_done */
717 0 : return;
718 : }
719 :
720 : /* No more subdomains to enumerate. Check if we need to fixup
721 : * cross-domain membership
722 : */
723 0 : if (state->sditer != state->sdom) {
724 : /* We did enumerate at least one subdomain. Walk the subdomains
725 : * and fixup members for each of them
726 : */
727 0 : for (state->sditer = state->sdom;
728 0 : state->sditer;
729 0 : state->sditer = state->sditer->next) {
730 0 : ret = ad_enum_cross_dom_members(state->id_ctx->ad_options->id,
731 0 : state->sditer->dom);
732 0 : if (ret != EOK) {
733 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Could not check cross-domain "
734 : "memberships for %s, group memberships might be "
735 : "incomplete!\n", state->sdom->dom->name);
736 0 : continue;
737 : }
738 : }
739 : }
740 :
741 0 : tevent_req_done(req);
742 : }
743 :
744 : static errno_t ad_group_extra_members(TALLOC_CTX *mem_ctx,
745 : const struct ldb_message *group,
746 : struct sss_domain_info *dom,
747 : char ***_group_only);
748 : static errno_t ad_group_add_member(struct sdap_options *opts,
749 : struct sss_domain_info *group_domain,
750 : struct ldb_dn *group_dn,
751 : const char *member);
752 :
753 : static errno_t
754 0 : ad_enum_cross_dom_members(struct sdap_options *opts,
755 : struct sss_domain_info *dom)
756 : {
757 : errno_t ret;
758 : errno_t sret;
759 : char *filter;
760 : TALLOC_CTX *tmp_ctx;
761 0 : const char *attrs[] = {
762 : SYSDB_NAME,
763 : SYSDB_MEMBER,
764 : SYSDB_ORIG_MEMBER,
765 : NULL
766 : };
767 : size_t count, i, mi;
768 : struct ldb_message **msgs;
769 0 : bool in_transaction = false;
770 : char **group_only;
771 :
772 0 : tmp_ctx = talloc_new(NULL);
773 0 : if (tmp_ctx == NULL) return ENOMEM;
774 :
775 0 : ret = sysdb_transaction_start(dom->sysdb);
776 0 : if (ret != EOK) {
777 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
778 0 : goto done;
779 : }
780 0 : in_transaction = true;
781 :
782 0 : filter = talloc_asprintf(tmp_ctx, "(%s=*)", SYSDB_NAME);
783 0 : if (filter == NULL) {
784 0 : ret = ENOMEM;
785 0 : goto done;
786 : }
787 :
788 0 : ret = sysdb_search_groups(tmp_ctx, dom, filter, attrs, &count, &msgs);
789 0 : if (ret != EOK) {
790 0 : goto done;
791 : }
792 :
793 0 : for (i = 0; i < count; i++) {
794 0 : ret = ad_group_extra_members(tmp_ctx, msgs[i], dom, &group_only);
795 0 : if (ret != EOK) {
796 0 : DEBUG(SSSDBG_OP_FAILURE, "Failed to check extra members\n");
797 0 : continue;
798 0 : } else if (group_only == NULL) {
799 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "No extra members\n");
800 0 : continue;
801 : }
802 :
803 : /* Group has extra members */
804 0 : for (mi = 0; group_only[mi]; mi++) {
805 0 : ret = ad_group_add_member(opts, dom, msgs[i]->dn, group_only[mi]);
806 0 : if (ret != EOK) {
807 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Failed to add [%s]: %s\n",
808 : group_only[mi], strerror(ret));
809 0 : continue;
810 : }
811 : }
812 :
813 0 : talloc_zfree(group_only);
814 : }
815 :
816 0 : ret = sysdb_transaction_commit(dom->sysdb);
817 0 : if (ret != EOK) {
818 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
819 0 : goto done;
820 : }
821 0 : in_transaction = false;
822 :
823 0 : ret = EOK;
824 : done:
825 0 : if (in_transaction) {
826 0 : sret = sysdb_transaction_cancel(dom->sysdb);
827 0 : if (sret != EOK) {
828 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n");
829 : }
830 : }
831 0 : talloc_free(tmp_ctx);
832 0 : return ret;
833 : }
834 :
835 : static errno_t
836 : ad_group_stored_orig_members(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom,
837 : struct ldb_dn *dn, char ***_odn_list);
838 :
839 : static errno_t
840 0 : ad_group_extra_members(TALLOC_CTX *mem_ctx, const struct ldb_message *group,
841 : struct sss_domain_info *dom, char ***_group_only)
842 : {
843 : TALLOC_CTX *tmp_ctx;
844 : struct ldb_message_element *m, *om;
845 : const char *name;
846 : errno_t ret;
847 : char **sysdb_odn_list;
848 : const char **group_odn_list;
849 0 : char **group_only = NULL;
850 :
851 0 : if (_group_only == NULL) return EINVAL;
852 0 : *_group_only = NULL;
853 :
854 0 : tmp_ctx = talloc_new(NULL);
855 0 : if (tmp_ctx == NULL) return ENOMEM;
856 :
857 0 : om = ldb_msg_find_element(group, SYSDB_ORIG_MEMBER);
858 0 : m = ldb_msg_find_element(group, SYSDB_MEMBER);
859 0 : name = ldb_msg_find_attr_as_string(group, SYSDB_NAME, NULL);
860 0 : if (name == NULL) {
861 0 : DEBUG(SSSDBG_OP_FAILURE, "A group with no name!\n");
862 0 : ret = EFAULT;
863 0 : goto done;
864 : }
865 :
866 0 : if (om == NULL || om->num_values == 0) {
867 0 : DEBUG(SSSDBG_TRACE_FUNC, "Group %s has no original members\n", name);
868 0 : ret = EOK;
869 0 : goto done;
870 : }
871 :
872 0 : if (m == NULL || (m->num_values < om->num_values)) {
873 0 : DEBUG(SSSDBG_TRACE_FUNC,
874 : "Group %s has %d members but %d original members\n",
875 : name, m ? m->num_values : 0, om->num_values);
876 :
877 : /* Get the list of originalDN attributes that are already
878 : * linked to the group
879 : */
880 0 : ret = ad_group_stored_orig_members(tmp_ctx, dom, group->dn,
881 : &sysdb_odn_list);
882 0 : if (ret != EOK) {
883 0 : DEBUG(SSSDBG_OP_FAILURE,
884 : "Could not retrieve list of original members for %s\n",
885 : name);
886 0 : goto done;
887 : }
888 :
889 : /* Get the list of original DN attributes the group had in AD */
890 0 : group_odn_list = sss_ldb_el_to_string_list(tmp_ctx, om);
891 0 : if (group_odn_list == NULL) {
892 0 : ret = EFAULT;
893 0 : goto done;
894 : }
895 :
896 : /* Compare the two lists */
897 0 : ret = diff_string_lists(tmp_ctx, discard_const(group_odn_list),
898 : sysdb_odn_list, &group_only, NULL, NULL);
899 0 : if (ret != EOK) {
900 0 : DEBUG(SSSDBG_OP_FAILURE,
901 : "Could not compare lists of members for %s\n", name);
902 0 : goto done;
903 : }
904 : }
905 :
906 0 : ret = EOK;
907 0 : *_group_only = talloc_steal(mem_ctx, group_only);
908 : done:
909 0 : talloc_free(tmp_ctx);
910 0 : return ret;
911 : }
912 :
913 : static errno_t
914 0 : ad_group_stored_orig_members(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom,
915 : struct ldb_dn *dn, char ***_odn_list)
916 : {
917 : errno_t ret;
918 : TALLOC_CTX *tmp_ctx;
919 : size_t m_count, i;
920 : struct ldb_message **members;
921 0 : const char *attrs[] = {
922 : SYSDB_NAME,
923 : SYSDB_ORIG_DN,
924 : NULL
925 : };
926 : char **odn_list;
927 : const char *odn;
928 : size_t oi;
929 :
930 0 : tmp_ctx = talloc_new(NULL);
931 0 : if (tmp_ctx == NULL) return ENOMEM;
932 :
933 : /* Get all entries member element points to */
934 0 : ret = sysdb_asq_search(tmp_ctx, dom, dn, NULL, SYSDB_MEMBER,
935 : attrs, &m_count, &members);
936 0 : if (ret != EOK) {
937 0 : goto done;
938 : }
939 :
940 0 : odn_list = talloc_zero_array(tmp_ctx, char *, m_count + 1);
941 0 : if (odn_list == NULL) {
942 0 : ret = ENOMEM;
943 0 : goto done;
944 : }
945 :
946 : /* Get a list of their original DNs */
947 0 : oi = 0;
948 0 : for (i = 0; i < m_count; i++) {
949 0 : odn = ldb_msg_find_attr_as_string(members[i], SYSDB_ORIG_DN, NULL);
950 0 : if (odn == NULL) {
951 0 : continue;
952 : }
953 :
954 0 : odn_list[oi] = talloc_strdup(odn_list, odn);
955 0 : if (odn_list[oi] == NULL) {
956 0 : ret = ENOMEM;
957 0 : goto done;
958 : }
959 0 : oi++;
960 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Member %s already in sysdb\n", odn);
961 : }
962 :
963 0 : ret = EOK;
964 0 : *_odn_list = talloc_steal(mem_ctx, odn_list);
965 : done:
966 0 : talloc_free(tmp_ctx);
967 0 : return ret;
968 : }
969 :
970 : static errno_t
971 0 : ad_group_add_member(struct sdap_options *opts,
972 : struct sss_domain_info *group_domain,
973 : struct ldb_dn *group_dn,
974 : const char *member)
975 : {
976 : struct sdap_domain *sd;
977 : struct ldb_dn *base_dn;
978 : TALLOC_CTX *tmp_ctx;
979 : errno_t ret;
980 : const char *mem_filter;
981 : size_t msgs_count;
982 : struct ldb_message **msgs;
983 :
984 : /* This member would be from a different domain */
985 0 : sd = sdap_domain_get_by_dn(opts, member);
986 0 : if (sd == NULL) {
987 0 : DEBUG(SSSDBG_MINOR_FAILURE, "No matching domain for %s\n", member);
988 0 : return ENOENT;
989 : }
990 :
991 0 : tmp_ctx = talloc_new(NULL);
992 0 : if (tmp_ctx == NULL) return ENOMEM;
993 :
994 0 : mem_filter = talloc_asprintf(tmp_ctx, "(%s=%s)",
995 : SYSDB_ORIG_DN, member);
996 0 : if (mem_filter == NULL) {
997 0 : ret = ENOMEM;
998 0 : goto done;
999 : }
1000 :
1001 0 : base_dn = sysdb_domain_dn(tmp_ctx, sd->dom);
1002 0 : if (base_dn == NULL) {
1003 0 : ret = ENOMEM;
1004 0 : goto done;
1005 : }
1006 :
1007 0 : ret = sysdb_search_entry(tmp_ctx, sd->dom->sysdb, base_dn,
1008 : LDB_SCOPE_SUBTREE, mem_filter, NULL,
1009 : &msgs_count, &msgs);
1010 0 : if (ret == ENOENT) {
1011 0 : DEBUG(SSSDBG_TRACE_FUNC, "No member [%s] in sysdb\n", member);
1012 0 : ret = EOK;
1013 0 : goto done;
1014 0 : } else if (ret != EOK) {
1015 0 : goto done;
1016 : }
1017 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "[%s] found in sysdb\n", member);
1018 :
1019 0 : if (msgs_count != 1) {
1020 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1021 : "Search by orig DN returned %zd results!\n", msgs_count);
1022 0 : ret = EFAULT;
1023 0 : goto done;
1024 : }
1025 :
1026 0 : ret = sysdb_mod_group_member(group_domain, msgs[0]->dn, group_dn, SYSDB_MOD_ADD);
1027 0 : if (ret != EOK) {
1028 0 : DEBUG(SSSDBG_OP_FAILURE, "Could not add [%s] as a member of [%s]\n",
1029 : ldb_dn_get_linearized(msgs[0]->dn),
1030 : ldb_dn_get_linearized(group_dn));
1031 0 : goto done;
1032 : }
1033 :
1034 0 : ret = EOK;
1035 : done:
1036 0 : talloc_free(tmp_ctx);
1037 0 : return ret;
1038 : }
1039 :
1040 : errno_t
1041 0 : ad_enumeration_recv(struct tevent_req *req)
1042 : {
1043 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
1044 0 : return EOK;
1045 : }
|