Line data Source code
1 : /*
2 : Authors:
3 : Jan Zeleny <jzeleny@redhat.com>
4 :
5 : Copyright (C) 2011 Red Hat
6 :
7 : This program is free software; you can redistribute it and/or modify
8 : it under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3 of the License, or
10 : (at your option) any later version.
11 :
12 : This program is distributed in the hope that it will be useful,
13 : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : GNU General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with this program. If not, see <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include "util/util.h"
22 : #include "responder/common/responder.h"
23 : #include "providers/data_provider.h"
24 : #include "db/sysdb.h"
25 :
26 : /* ========== Get subdomains for a domain ================= */
27 : static DBusMessage *sss_dp_get_domains_msg(void *pvt);
28 :
29 : struct sss_dp_domains_info {
30 : struct sss_domain_info *dom;
31 : const char *hint;
32 : };
33 :
34 : static struct tevent_req *
35 3 : get_subdomains_send(TALLOC_CTX *mem_ctx, struct resp_ctx *rctx,
36 : struct sss_domain_info *dom,
37 : const char *hint)
38 : {
39 : errno_t ret;
40 : struct tevent_req *req;
41 : struct sss_dp_req_state *state;
42 : struct sss_dp_domains_info *info;
43 : char *key;
44 :
45 3 : req = tevent_req_create(mem_ctx, &state, struct sss_dp_req_state);
46 3 : if (req == NULL) {
47 0 : return NULL;
48 : }
49 :
50 3 : info = talloc_zero(state, struct sss_dp_domains_info);
51 3 : if (!info) {
52 0 : ret = ENOMEM;
53 0 : goto fail;
54 : }
55 3 : info->hint = hint;
56 3 : info->dom = dom;
57 :
58 3 : key = talloc_asprintf(state, "domains@%s", dom->name);
59 3 : if (key == NULL) {
60 0 : ret = ENOMEM;
61 0 : goto fail;
62 : }
63 :
64 3 : ret = sss_dp_issue_request(state, rctx, key, dom,
65 : sss_dp_get_domains_msg, info, req);
66 3 : talloc_free(key);
67 3 : if (ret != EOK) {
68 3 : ret = EIO;
69 3 : goto fail;
70 : }
71 :
72 0 : return req;
73 :
74 : fail:
75 3 : tevent_req_error(req, ret);
76 3 : tevent_req_post(req, rctx->ev);
77 3 : return req;
78 : }
79 :
80 : static DBusMessage *
81 3 : sss_dp_get_domains_msg(void *pvt)
82 : {
83 : struct sss_dp_domains_info *info;
84 3 : DBusMessage *msg = NULL;
85 : dbus_bool_t dbret;
86 :
87 3 : info = talloc_get_type(pvt, struct sss_dp_domains_info);
88 :
89 3 : msg = dbus_message_new_method_call(NULL,
90 : DP_PATH,
91 : DATA_PROVIDER_IFACE,
92 : DATA_PROVIDER_IFACE_GETDOMAINS);
93 3 : if (msg == NULL) {
94 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory?!\n");
95 0 : return NULL;
96 : }
97 :
98 3 : DEBUG(SSSDBG_TRACE_FUNC,
99 : "Sending get domains request for [%s][%s]\n",
100 : info->dom->name, info->hint);
101 :
102 : /* Send the hint argument to provider as well. This will
103 : * be useful for some cases of transitional trust where
104 : * the server might not know all trusted domains
105 : */
106 3 : dbret = dbus_message_append_args(msg,
107 : DBUS_TYPE_STRING, &info->hint,
108 : DBUS_TYPE_INVALID);
109 3 : if (!dbret) {
110 0 : DEBUG(SSSDBG_OP_FAILURE ,"Failed to build message\n");
111 0 : dbus_message_unref(msg);
112 0 : return NULL;
113 : }
114 :
115 3 : return msg;
116 : }
117 :
118 : static errno_t
119 3 : get_next_domain_recv(TALLOC_CTX *mem_ctx,
120 : struct tevent_req *req,
121 : dbus_uint16_t *dp_err,
122 : dbus_uint32_t *dp_ret,
123 : char **err_msg)
124 : {
125 3 : return sss_dp_req_recv(mem_ctx, req, dp_err, dp_ret, err_msg);
126 : }
127 :
128 : /* ====== Iterate over all domains, searching for their subdomains ======= */
129 : static errno_t process_subdomains(struct sss_domain_info *dom);
130 : static void set_time_of_last_request(struct resp_ctx *rctx);
131 : static errno_t check_last_request(struct resp_ctx *rctx, const char *hint);
132 :
133 : struct sss_dp_get_domains_state {
134 : struct resp_ctx *rctx;
135 : struct sss_domain_info *dom;
136 : const char *hint;
137 : };
138 :
139 : static void
140 : sss_dp_get_domains_process(struct tevent_req *subreq);
141 :
142 3 : struct tevent_req *sss_dp_get_domains_send(TALLOC_CTX *mem_ctx,
143 : struct resp_ctx *rctx,
144 : bool force,
145 : const char *hint)
146 : {
147 : errno_t ret;
148 : struct tevent_req *req;
149 : struct tevent_req *subreq;
150 : struct sss_dp_get_domains_state *state;
151 :
152 3 : req = tevent_req_create(mem_ctx, &state, struct sss_dp_get_domains_state);
153 3 : if (req == NULL) {
154 0 : return NULL;
155 : }
156 :
157 3 : if (rctx->domains == NULL) {
158 0 : DEBUG(SSSDBG_CRIT_FAILURE, "No domains configured.\n");
159 0 : ret = EINVAL;
160 0 : goto immediately;
161 : }
162 :
163 3 : if (!force) {
164 2 : ret = check_last_request(rctx, hint);
165 2 : if (ret == EOK) {
166 0 : DEBUG(SSSDBG_TRACE_FUNC,
167 : "Last call was too recent, nothing to do!\n");
168 0 : goto immediately;
169 2 : } else if (ret != EAGAIN) {
170 0 : DEBUG(SSSDBG_TRACE_FUNC, "check_domain_request failed with [%d][%s]\n",
171 : ret, strerror(ret));
172 0 : goto immediately;
173 : }
174 : }
175 :
176 3 : state->rctx = rctx;
177 3 : if (hint != NULL) {
178 0 : state->hint = hint;
179 : } else {
180 3 : state->hint = talloc_strdup(state, "");
181 3 : if (state->hint == NULL) {
182 0 : ret = ENOMEM;
183 0 : goto immediately;
184 : }
185 : }
186 :
187 3 : state->dom = rctx->domains;
188 6 : while(state->dom != NULL && !NEED_CHECK_PROVIDER(state->dom->provider)) {
189 0 : state->dom = get_next_domain(state->dom, 0);
190 : }
191 :
192 3 : if (state->dom == NULL) {
193 : /* All domains were local */
194 0 : ret = EOK;
195 0 : goto immediately;
196 : }
197 :
198 3 : subreq = get_subdomains_send(req, rctx, state->dom, state->hint);
199 3 : if (subreq == NULL) {
200 0 : ret = ENOMEM;
201 0 : goto immediately;
202 : }
203 3 : tevent_req_set_callback(subreq, sss_dp_get_domains_process, req);
204 :
205 3 : return req;
206 :
207 : immediately:
208 0 : if (ret == EOK) {
209 0 : set_time_of_last_request(rctx);
210 0 : tevent_req_done(req);
211 : } else {
212 0 : tevent_req_error(req, ret);
213 : }
214 0 : tevent_req_post(req, rctx->ev);
215 :
216 0 : return req;
217 : }
218 :
219 : static void
220 3 : sss_dp_get_domains_process(struct tevent_req *subreq)
221 : {
222 : errno_t ret;
223 3 : struct tevent_req *req = tevent_req_callback_data(subreq,
224 : struct tevent_req);
225 3 : struct sss_dp_get_domains_state *state = tevent_req_data(req,
226 : struct sss_dp_get_domains_state);
227 : dbus_uint16_t dp_err;
228 : dbus_uint32_t dp_ret;
229 : char *err_msg;
230 :
231 3 : ret = get_next_domain_recv(req, subreq, &dp_err, &dp_ret, &err_msg);
232 3 : talloc_zfree(subreq);
233 3 : if (ret != EOK) {
234 0 : goto fail;
235 : }
236 :
237 3 : ret = process_subdomains(state->dom);
238 3 : if (ret != EOK) {
239 0 : DEBUG(SSSDBG_OP_FAILURE, "process_subdomains failed, "
240 : "trying next domain.\n");
241 0 : goto fail;
242 : }
243 :
244 : /* Advance to the next domain */
245 3 : state->dom = get_next_domain(state->dom, 0);
246 :
247 : /* Skip local domains */
248 6 : while(state->dom != NULL && !NEED_CHECK_PROVIDER(state->dom->provider)) {
249 0 : state->dom = get_next_domain(state->dom, 0);
250 : }
251 :
252 3 : if (state->dom == NULL) {
253 : /* All domains were local */
254 3 : set_time_of_last_request(state->rctx);
255 3 : tevent_req_done(req);
256 3 : return;
257 : }
258 :
259 0 : subreq = get_subdomains_send(req, state->rctx, state->dom, state->hint);
260 0 : if (subreq == NULL) {
261 0 : ret = ENOMEM;
262 0 : goto fail;
263 : }
264 0 : tevent_req_set_callback(subreq, sss_dp_get_domains_process, req);
265 0 : return;
266 :
267 : fail:
268 0 : tevent_req_error(req, ret);
269 0 : return;
270 : }
271 :
272 : static errno_t
273 3 : process_subdomains(struct sss_domain_info *domain)
274 : {
275 : int ret;
276 :
277 3 : if (domain->realm == NULL ||
278 0 : domain->flat_name == NULL ||
279 0 : domain->domain_id == NULL) {
280 3 : ret = sysdb_master_domain_update(domain);
281 3 : if (ret != EOK) {
282 0 : DEBUG(SSSDBG_FUNC_DATA, "sysdb_master_domain_get_info " \
283 : "failed.\n");
284 0 : goto done;
285 : }
286 : }
287 :
288 : /* Retrieve all subdomains of this domain from sysdb
289 : * and create their struct sss_domain_info representations
290 : */
291 3 : ret = sysdb_update_subdomains(domain);
292 3 : if (ret != EOK) {
293 0 : DEBUG(SSSDBG_FUNC_DATA, "sysdb_update_subdomains failed.\n");
294 0 : goto done;
295 : }
296 :
297 3 : errno = 0;
298 3 : ret = gettimeofday(&domain->subdomains_last_checked, NULL);
299 3 : if (ret == -1) {
300 0 : ret = errno;
301 0 : goto done;
302 : }
303 :
304 3 : ret = EOK;
305 :
306 : done:
307 3 : if (ret != EOK) {
308 0 : DEBUG(SSSDBG_OP_FAILURE, "Failed to update sub-domains "
309 : "of domain [%s].\n", domain->name);
310 : }
311 :
312 3 : return ret;
313 : }
314 :
315 3 : errno_t sss_dp_get_domains_recv(struct tevent_req *req)
316 : {
317 3 : TEVENT_REQ_RETURN_ON_ERROR(req);
318 :
319 3 : return EOK;
320 : }
321 :
322 3 : static void set_time_of_last_request(struct resp_ctx *rctx)
323 : {
324 : int ret;
325 :
326 3 : errno = 0;
327 3 : ret = gettimeofday(&rctx->get_domains_last_call, NULL);
328 3 : if (ret == -1) {
329 0 : ret = errno;
330 0 : DEBUG(SSSDBG_TRACE_FUNC, "gettimeofday failed [%d][%s].\n",
331 : ret, strerror(ret));
332 : }
333 3 : }
334 :
335 2 : static errno_t check_last_request(struct resp_ctx *rctx, const char *hint)
336 : {
337 : struct sss_domain_info *dom;
338 2 : time_t now = time(NULL);
339 : time_t diff;
340 :
341 2 : diff = now - rctx->get_domains_last_call.tv_sec;
342 2 : if (diff >= rctx->domains_timeout) {
343 : /* Timeout, expired, fetch domains again */
344 2 : return EAGAIN;
345 : }
346 :
347 0 : if (hint != NULL) {
348 0 : for (dom = rctx->domains; dom;
349 0 : dom = get_next_domain(dom, SSS_GND_DESCEND)) {
350 0 : if (!IS_SUBDOMAIN(dom)) {
351 0 : diff = now - dom->subdomains_last_checked.tv_sec;
352 : /* not a subdomain */
353 0 : continue;
354 : }
355 0 : if (strcasecmp(dom->name, hint) == 0) {
356 0 : if (diff >= rctx->domains_timeout) {
357 : /* Timeout, expired, fetch domains again */
358 0 : return EAGAIN;
359 : }
360 : }
361 : }
362 : }
363 :
364 0 : return EOK;
365 : }
366 :
367 : struct get_domains_state {
368 : struct resp_ctx *rctx;
369 : struct sss_nc_ctx *optional_ncache;
370 : };
371 :
372 1 : static void get_domains_at_startup_done(struct tevent_req *req)
373 : {
374 : int ret;
375 : struct get_domains_state *state;
376 :
377 1 : state = tevent_req_callback_data(req, struct get_domains_state);
378 :
379 1 : ret = sss_dp_get_domains_recv(req);
380 1 : talloc_free(req);
381 1 : if (ret != EOK) {
382 0 : DEBUG(SSSDBG_MINOR_FAILURE, "sss_dp_get_domains request failed.\n");
383 : }
384 :
385 1 : if (state->optional_ncache != NULL) {
386 1 : ret = sss_ncache_reset_repopulate_permanent(state->rctx,
387 : state->optional_ncache);
388 1 : if (ret != EOK) {
389 0 : DEBUG(SSSDBG_MINOR_FAILURE, "sss_dp_get_domains request failed.\n");
390 : }
391 : }
392 :
393 1 : talloc_free(state);
394 1 : return;
395 : }
396 :
397 1 : static void get_domains_at_startup(struct tevent_context *ev,
398 : struct tevent_immediate *imm,
399 : void *pvt)
400 : {
401 : struct tevent_req *req;
402 : struct get_domains_state *state;
403 :
404 1 : state = talloc_get_type(pvt, struct get_domains_state);
405 :
406 1 : req = sss_dp_get_domains_send(state, state->rctx, true, NULL);
407 1 : if (req == NULL) {
408 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_dp_get_domains_send failed.\n");
409 0 : talloc_free(state);
410 0 : return;
411 : }
412 :
413 1 : tevent_req_set_callback(req, get_domains_at_startup_done, state);
414 1 : return;
415 : }
416 :
417 1 : errno_t schedule_get_domains_task(TALLOC_CTX *mem_ctx,
418 : struct tevent_context *ev,
419 : struct resp_ctx *rctx,
420 : struct sss_nc_ctx *optional_ncache)
421 : {
422 : struct tevent_immediate *imm;
423 : struct get_domains_state *state;
424 :
425 1 : state = talloc(mem_ctx, struct get_domains_state);
426 1 : if (state == NULL) {
427 0 : return ENOMEM;
428 : }
429 1 : state->rctx = rctx;
430 1 : state->optional_ncache = optional_ncache;
431 :
432 1 : imm = tevent_create_immediate(mem_ctx);
433 1 : if (imm == NULL) {
434 0 : DEBUG(SSSDBG_OP_FAILURE, "tevent_create_immediate failed.\n");
435 0 : talloc_free(state);
436 0 : return ENOMEM;
437 : }
438 :
439 1 : tevent_schedule_immediate(imm, ev, get_domains_at_startup, state);
440 :
441 1 : return EOK;
442 : }
443 :
444 : struct sss_parse_inp_state {
445 : struct resp_ctx *rctx;
446 : const char *rawinp;
447 :
448 : char *name;
449 : char *domname;
450 : errno_t error;
451 : };
452 :
453 : static void sss_parse_inp_done(struct tevent_req *subreq);
454 :
455 : struct tevent_req *
456 4 : sss_parse_inp_send(TALLOC_CTX *mem_ctx, struct resp_ctx *rctx,
457 : const char *rawinp)
458 : {
459 : errno_t ret;
460 : struct tevent_req *req;
461 : struct tevent_req *subreq;
462 : struct sss_parse_inp_state *state;
463 :
464 4 : req = tevent_req_create(mem_ctx, &state, struct sss_parse_inp_state);
465 4 : if (req == NULL) {
466 0 : return NULL;
467 : }
468 4 : state->rawinp = rawinp;
469 4 : state->rctx = rctx;
470 :
471 : /* If the subdomains haven't been checked yet, we need to always
472 : * attach to the post-startup subdomain request and only then parse
473 : * the input. Otherwise, we might not be able to parse input with a
474 : * flat domain name specifier */
475 4 : if (rctx->get_domains_last_call.tv_sec > 0) {
476 9 : ret = sss_parse_name_for_domains(state, rctx->domains,
477 3 : rctx->default_domain, rawinp,
478 6 : &state->domname, &state->name);
479 3 : if (ret == EOK) {
480 : /* Was able to use cached domains */
481 1 : goto done;
482 2 : } else if (ret != EAGAIN) {
483 1 : DEBUG(SSSDBG_OP_FAILURE, "Invalid name received [%s]\n", rawinp);
484 1 : ret = ERR_INPUT_PARSE;
485 1 : goto done;
486 : }
487 : }
488 :
489 : /* EAGAIN - check the DP for subdomains */
490 :
491 2 : DEBUG(SSSDBG_FUNC_DATA, "Requesting info for [%s] from [%s]\n",
492 : state->name, state->domname ? state->domname : "<ALL>");
493 :
494 : /* We explicitly use force=false here. This request should decide itself
495 : * if it's time to re-use the cached subdomain list or refresh. If the
496 : * caller needs to specify the 'force' parameter, they should use the
497 : * sss_dp_get_domains_send() request itself
498 : */
499 2 : subreq = sss_dp_get_domains_send(state, rctx, false, state->domname);
500 2 : if (subreq == NULL) {
501 0 : ret = ENOMEM;
502 0 : goto done;
503 : }
504 2 : tevent_req_set_callback(subreq, sss_parse_inp_done, req);
505 2 : return req;
506 :
507 : done:
508 2 : if (ret == EOK) {
509 1 : tevent_req_done(req);
510 : } else {
511 1 : tevent_req_error(req, ret);
512 : }
513 2 : tevent_req_post(req, rctx->ev);
514 2 : return req;
515 : }
516 :
517 2 : static void sss_parse_inp_done(struct tevent_req *subreq)
518 : {
519 : errno_t ret;
520 2 : struct tevent_req *req = tevent_req_callback_data(subreq,
521 : struct tevent_req);
522 2 : struct sss_parse_inp_state *state = tevent_req_data(req,
523 : struct sss_parse_inp_state);
524 :
525 2 : ret = sss_dp_get_domains_recv(subreq);
526 2 : talloc_free(subreq);
527 2 : if (ret != EOK) {
528 0 : tevent_req_error(req, ret);
529 0 : return;
530 : }
531 :
532 2 : state->error = ERR_OK;
533 :
534 4 : ret = sss_parse_name_for_domains(state, state->rctx->domains,
535 2 : state->rctx->default_domain,
536 : state->rawinp,
537 : &state->domname, &state->name);
538 2 : if (ret == EAGAIN && state->domname != NULL && state->name == NULL) {
539 0 : DEBUG(SSSDBG_OP_FAILURE,
540 : "Unknown domain in [%s]\n", state->rawinp);
541 0 : state->error = ERR_DOMAIN_NOT_FOUND;
542 2 : } else if (ret != EOK) {
543 0 : DEBUG(SSSDBG_OP_FAILURE,
544 : "Invalid name received [%s]\n", state->rawinp);
545 0 : state->error = ERR_INPUT_PARSE;
546 : }
547 :
548 2 : if (state->error != ERR_OK) {
549 0 : tevent_req_error(req, state->error);
550 0 : return;
551 : }
552 :
553 : /* Was able to parse the name now */
554 2 : tevent_req_done(req);
555 : }
556 :
557 4 : errno_t sss_parse_inp_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
558 : char **_name, char **_domname)
559 : {
560 4 : struct sss_parse_inp_state *state = tevent_req_data(req,
561 : struct sss_parse_inp_state);
562 :
563 4 : if (state->error != ERR_DOMAIN_NOT_FOUND) {
564 4 : TEVENT_REQ_RETURN_ON_ERROR(req);
565 : }
566 :
567 3 : if (_name) {
568 3 : *_name = talloc_steal(mem_ctx, state->name);
569 : }
570 :
571 3 : if (_domname) {
572 3 : *_domname = talloc_steal(mem_ctx, state->domname);
573 : }
574 :
575 3 : return state->error;
576 : }
|