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 0 : ret = EIO;
69 0 : goto fail;
70 : }
71 :
72 3 : return req;
73 :
74 : fail:
75 0 : tevent_req_error(req, ret);
76 0 : tevent_req_post(req, rctx->ev);
77 0 : return req;
78 : }
79 :
80 : static DBusMessage *
81 0 : sss_dp_get_domains_msg(void *pvt)
82 : {
83 : struct sss_dp_domains_info *info;
84 0 : DBusMessage *msg = NULL;
85 : dbus_bool_t dbret;
86 :
87 0 : info = talloc_get_type(pvt, struct sss_dp_domains_info);
88 :
89 0 : msg = dbus_message_new_method_call(NULL,
90 : DP_PATH,
91 : DATA_PROVIDER_IFACE,
92 : DATA_PROVIDER_IFACE_GETDOMAINS);
93 0 : if (msg == NULL) {
94 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory?!\n");
95 0 : return NULL;
96 : }
97 :
98 0 : 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 0 : dbret = dbus_message_append_args(msg,
107 : DBUS_TYPE_STRING, &info->hint,
108 : DBUS_TYPE_INVALID);
109 0 : 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 0 : 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, false);
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, false);
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, false);
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; dom = get_next_domain(dom, true)) {
349 0 : if (!IS_SUBDOMAIN(dom)) {
350 0 : diff = now - dom->subdomains_last_checked.tv_sec;
351 : /* not a subdomain */
352 0 : continue;
353 : }
354 0 : if (strcasecmp(dom->name, hint) == 0) {
355 0 : if (diff >= rctx->domains_timeout) {
356 : /* Timeout, expired, fetch domains again */
357 0 : return EAGAIN;
358 : }
359 : }
360 : }
361 : }
362 :
363 0 : return EOK;
364 : }
365 :
366 : struct get_domains_state {
367 : struct resp_ctx *rctx;
368 : struct sss_nc_ctx *optional_ncache;
369 : };
370 :
371 1 : static void get_domains_at_startup_done(struct tevent_req *req)
372 : {
373 : int ret;
374 : struct get_domains_state *state;
375 :
376 1 : state = tevent_req_callback_data(req, struct get_domains_state);
377 :
378 1 : ret = sss_dp_get_domains_recv(req);
379 1 : talloc_free(req);
380 1 : if (ret != EOK) {
381 0 : DEBUG(SSSDBG_MINOR_FAILURE, "sss_dp_get_domains request failed.\n");
382 : }
383 :
384 1 : if (state->optional_ncache != NULL) {
385 1 : ret = sss_ncache_reset_repopulate_permanent(state->rctx,
386 : state->optional_ncache);
387 1 : if (ret != EOK) {
388 0 : DEBUG(SSSDBG_MINOR_FAILURE, "sss_dp_get_domains request failed.\n");
389 : }
390 : }
391 :
392 1 : talloc_free(state);
393 1 : return;
394 : }
395 :
396 1 : static void get_domains_at_startup(struct tevent_context *ev,
397 : struct tevent_immediate *imm,
398 : void *pvt)
399 : {
400 : struct tevent_req *req;
401 : struct get_domains_state *state;
402 :
403 1 : state = talloc_get_type(pvt, struct get_domains_state);
404 :
405 1 : req = sss_dp_get_domains_send(state, state->rctx, true, NULL);
406 1 : if (req == NULL) {
407 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_dp_get_domains_send failed.\n");
408 0 : talloc_free(state);
409 0 : return;
410 : }
411 :
412 1 : tevent_req_set_callback(req, get_domains_at_startup_done, state);
413 1 : return;
414 : }
415 :
416 1 : errno_t schedule_get_domains_task(TALLOC_CTX *mem_ctx,
417 : struct tevent_context *ev,
418 : struct resp_ctx *rctx,
419 : struct sss_nc_ctx *optional_ncache)
420 : {
421 : struct tevent_immediate *imm;
422 : struct get_domains_state *state;
423 :
424 1 : state = talloc(mem_ctx, struct get_domains_state);
425 1 : if (state == NULL) {
426 0 : return ENOMEM;
427 : }
428 1 : state->rctx = rctx;
429 1 : state->optional_ncache = optional_ncache;
430 :
431 1 : imm = tevent_create_immediate(mem_ctx);
432 1 : if (imm == NULL) {
433 0 : DEBUG(SSSDBG_OP_FAILURE, "tevent_create_immediate failed.\n");
434 0 : talloc_free(state);
435 0 : return ENOMEM;
436 : }
437 :
438 1 : tevent_schedule_immediate(imm, ev, get_domains_at_startup, state);
439 :
440 1 : return EOK;
441 : }
442 :
443 : struct sss_parse_inp_state {
444 : struct resp_ctx *rctx;
445 : const char *rawinp;
446 :
447 : char *name;
448 : char *domname;
449 : errno_t error;
450 : };
451 :
452 : static void sss_parse_inp_done(struct tevent_req *subreq);
453 :
454 : struct tevent_req *
455 4 : sss_parse_inp_send(TALLOC_CTX *mem_ctx, struct resp_ctx *rctx,
456 : const char *rawinp)
457 : {
458 : errno_t ret;
459 : struct tevent_req *req;
460 : struct tevent_req *subreq;
461 : struct sss_parse_inp_state *state;
462 :
463 4 : req = tevent_req_create(mem_ctx, &state, struct sss_parse_inp_state);
464 4 : if (req == NULL) {
465 0 : return NULL;
466 : }
467 4 : state->rawinp = rawinp;
468 4 : state->rctx = rctx;
469 :
470 : /* If the subdomains haven't been checked yet, we need to always
471 : * attach to the post-startup subdomain request and only then parse
472 : * the input. Otherwise, we might not be able to parse input with a
473 : * flat domain name specifier */
474 4 : if (rctx->get_domains_last_call.tv_sec > 0) {
475 9 : ret = sss_parse_name_for_domains(state, rctx->domains,
476 3 : rctx->default_domain, rawinp,
477 6 : &state->domname, &state->name);
478 3 : if (ret == EOK) {
479 : /* Was able to use cached domains */
480 1 : goto done;
481 2 : } else if (ret != EAGAIN) {
482 1 : DEBUG(SSSDBG_OP_FAILURE, "Invalid name received [%s]\n", rawinp);
483 1 : ret = ERR_INPUT_PARSE;
484 1 : goto done;
485 : }
486 : }
487 :
488 : /* EAGAIN - check the DP for subdomains */
489 :
490 2 : DEBUG(SSSDBG_FUNC_DATA, "Requesting info for [%s] from [%s]\n",
491 : state->name, state->domname ? state->domname : "<ALL>");
492 :
493 : /* We explicitly use force=false here. This request should decide itself
494 : * if it's time to re-use the cached subdomain list or refresh. If the
495 : * caller needs to specify the 'force' parameter, they should use the
496 : * sss_dp_get_domains_send() request itself
497 : */
498 2 : subreq = sss_dp_get_domains_send(state, rctx, false, state->domname);
499 2 : if (subreq == NULL) {
500 0 : ret = ENOMEM;
501 0 : goto done;
502 : }
503 2 : tevent_req_set_callback(subreq, sss_parse_inp_done, req);
504 2 : return req;
505 :
506 : done:
507 2 : if (ret == EOK) {
508 1 : tevent_req_done(req);
509 : } else {
510 1 : tevent_req_error(req, ret);
511 : }
512 2 : tevent_req_post(req, rctx->ev);
513 2 : return req;
514 : }
515 :
516 2 : static void sss_parse_inp_done(struct tevent_req *subreq)
517 : {
518 : errno_t ret;
519 2 : struct tevent_req *req = tevent_req_callback_data(subreq,
520 : struct tevent_req);
521 2 : struct sss_parse_inp_state *state = tevent_req_data(req,
522 : struct sss_parse_inp_state);
523 :
524 2 : ret = sss_dp_get_domains_recv(subreq);
525 2 : talloc_free(subreq);
526 2 : if (ret != EOK) {
527 0 : tevent_req_error(req, ret);
528 0 : return;
529 : }
530 :
531 2 : state->error = ERR_OK;
532 :
533 4 : ret = sss_parse_name_for_domains(state, state->rctx->domains,
534 2 : state->rctx->default_domain,
535 : state->rawinp,
536 : &state->domname, &state->name);
537 2 : if (ret == EAGAIN && state->domname != NULL && state->name == NULL) {
538 0 : DEBUG(SSSDBG_OP_FAILURE,
539 : "Unknown domain in [%s]\n", state->rawinp);
540 0 : state->error = ERR_DOMAIN_NOT_FOUND;
541 2 : } else if (ret != EOK) {
542 0 : DEBUG(SSSDBG_OP_FAILURE,
543 : "Invalid name received [%s]\n", state->rawinp);
544 0 : state->error = ERR_INPUT_PARSE;
545 : }
546 :
547 2 : if (state->error != ERR_OK) {
548 0 : tevent_req_error(req, state->error);
549 0 : return;
550 : }
551 :
552 : /* Was able to parse the name now */
553 2 : tevent_req_done(req);
554 : }
555 :
556 4 : errno_t sss_parse_inp_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
557 : char **_name, char **_domname)
558 : {
559 4 : struct sss_parse_inp_state *state = tevent_req_data(req,
560 : struct sss_parse_inp_state);
561 :
562 4 : if (state->error != ERR_DOMAIN_NOT_FOUND) {
563 4 : TEVENT_REQ_RETURN_ON_ERROR(req);
564 : }
565 :
566 3 : if (_name) {
567 3 : *_name = talloc_steal(mem_ctx, state->name);
568 : }
569 :
570 3 : if (_domname) {
571 3 : *_domname = talloc_steal(mem_ctx, state->domname);
572 : }
573 :
574 3 : return state->error;
575 : }
|