Line data Source code
1 : /*
2 : SSSD
3 :
4 : Async LDAP Helper routines for sudo
5 :
6 : Authors:
7 : Pavel Březina <pbrezina@redhat.com>
8 :
9 : Copyright (C) 2012 Red Hat
10 :
11 : This program is free software; you can redistribute it and/or modify
12 : it under the terms of the GNU General Public License as published by
13 : the Free Software Foundation; either version 3 of the License, or
14 : (at your option) any later version.
15 :
16 : This program is distributed in the hope that it will be useful,
17 : but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : GNU General Public License for more details.
20 :
21 : You should have received a copy of the GNU General Public License
22 : along with this program. If not, see <http://www.gnu.org/licenses/>.
23 : */
24 :
25 : #include <errno.h>
26 : #include <talloc.h>
27 : #include <tevent.h>
28 :
29 : #include "providers/dp_backend.h"
30 : #include "providers/ldap/ldap_common.h"
31 : #include "providers/ldap/sdap.h"
32 : #include "providers/ldap/sdap_async.h"
33 : #include "providers/ldap/sdap_sudo.h"
34 : #include "providers/ldap/sdap_sudo_cache.h"
35 : #include "db/sysdb_sudo.h"
36 :
37 : struct sdap_sudo_refresh_state {
38 : struct be_ctx *be_ctx;
39 : struct sdap_options *opts;
40 : struct sdap_id_op *sdap_op;
41 : struct sdap_id_conn_cache *sdap_conn_cache;
42 : struct sysdb_ctx *sysdb;
43 : struct sss_domain_info *domain;
44 :
45 : const char *ldap_filter; /* search */
46 : const char *sysdb_filter; /* delete */
47 :
48 : int dp_error;
49 : int error;
50 : char *highest_usn;
51 : size_t num_rules;
52 : };
53 :
54 : struct sdap_sudo_load_sudoers_state {
55 : struct tevent_context *ev;
56 : struct sdap_options *opts;
57 : struct sdap_handle *sh;
58 : struct sysdb_attrs **ldap_rules; /* search result will be stored here */
59 : size_t ldap_rules_count; /* search result will be stored here */
60 :
61 : const char **attrs;
62 : const char *filter;
63 : size_t base_iter;
64 : struct sdap_search_base **search_bases;
65 : int timeout;
66 : };
67 :
68 : static int sdap_sudo_refresh_retry(struct tevent_req *req);
69 :
70 : static void sdap_sudo_refresh_connect_done(struct tevent_req *subreq);
71 :
72 : static struct tevent_req * sdap_sudo_load_sudoers_send(TALLOC_CTX *mem_ctx,
73 : struct tevent_context *ev,
74 : struct sdap_options *opts,
75 : struct sdap_handle *sh,
76 : const char *ldap_filter);
77 :
78 : static errno_t sdap_sudo_load_sudoers_next_base(struct tevent_req *req);
79 :
80 : static void sdap_sudo_load_sudoers_process(struct tevent_req *subreq);
81 :
82 : static int sdap_sudo_load_sudoers_recv(struct tevent_req *req,
83 : TALLOC_CTX *mem_ctx,
84 : size_t *rules_count,
85 : struct sysdb_attrs ***rules);
86 :
87 : static void sdap_sudo_refresh_load_done(struct tevent_req *subreq);
88 :
89 : static int sdap_sudo_purge_sudoers(struct sss_domain_info *dom,
90 : const char *filter,
91 : struct sdap_attr_map *map,
92 : size_t rules_count,
93 : struct sysdb_attrs **rules);
94 :
95 : static int sdap_sudo_store_sudoers(TALLOC_CTX *mem_ctx,
96 : struct sss_domain_info *domain,
97 : struct sdap_options *opts,
98 : size_t rules_count,
99 : struct sysdb_attrs **rules,
100 : int cache_timeout,
101 : time_t now,
102 : char **_usn);
103 :
104 0 : struct tevent_req *sdap_sudo_refresh_send(TALLOC_CTX *mem_ctx,
105 : struct be_ctx *be_ctx,
106 : struct sdap_options *opts,
107 : struct sdap_id_conn_cache *conn_cache,
108 : const char *ldap_filter,
109 : const char *sysdb_filter)
110 : {
111 : struct tevent_req *req;
112 : struct sdap_sudo_refresh_state *state;
113 : int ret;
114 :
115 0 : req = tevent_req_create(mem_ctx, &state, struct sdap_sudo_refresh_state);
116 0 : if (!req) {
117 0 : return NULL;
118 : }
119 :
120 : /* if we don't have a search filter, this request is meaningless */
121 0 : if (ldap_filter == NULL) {
122 0 : ret = EINVAL;
123 0 : goto immediately;
124 : }
125 :
126 0 : state->be_ctx = be_ctx;
127 0 : state->opts = opts;
128 0 : state->sdap_op = NULL;
129 0 : state->sdap_conn_cache = conn_cache;
130 0 : state->sysdb = be_ctx->domain->sysdb;
131 0 : state->domain = be_ctx->domain;
132 0 : state->ldap_filter = talloc_strdup(state, ldap_filter);
133 0 : state->sysdb_filter = talloc_strdup(state, sysdb_filter);
134 0 : state->dp_error = DP_ERR_OK;
135 0 : state->error = EOK;
136 0 : state->highest_usn = NULL;
137 :
138 0 : if (state->ldap_filter == NULL) {
139 0 : ret = ENOMEM;
140 0 : goto immediately;
141 : }
142 :
143 0 : if (sysdb_filter != NULL && state->sysdb_filter == NULL) {
144 0 : ret = ENOMEM;
145 0 : goto immediately;
146 : }
147 :
148 0 : ret = sdap_sudo_refresh_retry(req);
149 0 : if (ret == EAGAIN) {
150 : /* asynchronous processing */
151 0 : return req;
152 : }
153 :
154 : immediately:
155 0 : if (ret == EOK) {
156 0 : tevent_req_done(req);
157 : } else {
158 0 : tevent_req_error(req, ret);
159 : }
160 0 : tevent_req_post(req, be_ctx->ev);
161 :
162 0 : return req;
163 : }
164 :
165 0 : int sdap_sudo_refresh_recv(TALLOC_CTX *mem_ctx,
166 : struct tevent_req *req,
167 : int *dp_error,
168 : int *error,
169 : char **usn,
170 : size_t *num_rules)
171 : {
172 : struct sdap_sudo_refresh_state *state;
173 :
174 0 : state = tevent_req_data(req, struct sdap_sudo_refresh_state);
175 :
176 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
177 :
178 0 : *dp_error = state->dp_error;
179 0 : *error = state->error;
180 :
181 0 : if (usn != NULL && state->highest_usn != NULL) {
182 0 : *usn = talloc_steal(mem_ctx, state->highest_usn);
183 : }
184 :
185 0 : if (num_rules != NULL) {
186 0 : *num_rules = state->num_rules;
187 : }
188 :
189 0 : return EOK;
190 : }
191 :
192 0 : static int sdap_sudo_refresh_retry(struct tevent_req *req)
193 : {
194 : struct sdap_sudo_refresh_state *state;
195 : struct tevent_req *subreq;
196 : int ret;
197 :
198 0 : state = tevent_req_data(req, struct sdap_sudo_refresh_state);
199 :
200 0 : if (be_is_offline(state->be_ctx)) {
201 0 : state->dp_error = DP_ERR_OFFLINE;
202 0 : state->error = EAGAIN;
203 0 : return EOK;
204 : }
205 :
206 0 : if (state->sdap_op == NULL) {
207 0 : state->sdap_op = sdap_id_op_create(state, state->sdap_conn_cache);
208 0 : if (state->sdap_op == NULL) {
209 0 : DEBUG(SSSDBG_CRIT_FAILURE, "sdap_id_op_create() failed\n");
210 0 : state->dp_error = DP_ERR_FATAL;
211 0 : state->error = EIO;
212 0 : return EIO;
213 : }
214 : }
215 :
216 0 : subreq = sdap_id_op_connect_send(state->sdap_op, state, &ret);
217 0 : if (subreq == NULL) {
218 0 : DEBUG(SSSDBG_CRIT_FAILURE,
219 : "sdap_id_op_connect_send() failed: %d(%s)\n", ret, strerror(ret));
220 0 : talloc_zfree(state->sdap_op);
221 0 : state->dp_error = DP_ERR_FATAL;
222 0 : state->error = ret;
223 0 : return ret;
224 : }
225 :
226 0 : tevent_req_set_callback(subreq, sdap_sudo_refresh_connect_done, req);
227 :
228 0 : return EAGAIN;
229 : }
230 :
231 0 : static void sdap_sudo_refresh_connect_done(struct tevent_req *subreq)
232 : {
233 : struct tevent_req *req; /* req from sdap_sudo_refresh_send() */
234 : struct sdap_sudo_refresh_state *state;
235 : int dp_error;
236 : int ret;
237 :
238 0 : req = tevent_req_callback_data(subreq, struct tevent_req);
239 0 : state = tevent_req_data(req, struct sdap_sudo_refresh_state);
240 :
241 0 : ret = sdap_id_op_connect_recv(subreq, &dp_error);
242 0 : talloc_zfree(subreq);
243 :
244 0 : if (dp_error == DP_ERR_OFFLINE) {
245 0 : talloc_zfree(state->sdap_op);
246 0 : state->dp_error = DP_ERR_OFFLINE;
247 0 : state->error = EAGAIN;
248 0 : tevent_req_done(req);
249 0 : return;
250 0 : } else if (ret != EOK) {
251 0 : DEBUG(SSSDBG_CRIT_FAILURE,
252 : "SUDO LDAP connection failed - %s\n", strerror(ret));
253 0 : goto fail;
254 : }
255 :
256 0 : DEBUG(SSSDBG_TRACE_FUNC, "SUDO LDAP connection successful\n");
257 :
258 0 : subreq = sdap_sudo_load_sudoers_send(state, state->be_ctx->ev,
259 : state->opts,
260 : sdap_id_op_handle(state->sdap_op),
261 : state->ldap_filter);
262 0 : if (subreq == NULL) {
263 0 : ret = EFAULT;
264 0 : goto fail;
265 : }
266 :
267 0 : tevent_req_set_callback(subreq, sdap_sudo_refresh_load_done, req);
268 :
269 0 : return;
270 :
271 : fail:
272 0 : state->dp_error = DP_ERR_FATAL;
273 0 : state->error = ret;
274 0 : tevent_req_error(req, ret);
275 : }
276 :
277 0 : static struct tevent_req * sdap_sudo_load_sudoers_send(TALLOC_CTX *mem_ctx,
278 : struct tevent_context *ev,
279 : struct sdap_options *opts,
280 : struct sdap_handle *sh,
281 : const char *ldap_filter)
282 :
283 :
284 :
285 : {
286 : struct tevent_req *req;
287 : struct sdap_sudo_load_sudoers_state *state;
288 : int ret;
289 :
290 0 : req = tevent_req_create(mem_ctx, &state, struct sdap_sudo_load_sudoers_state);
291 0 : if (!req) {
292 0 : return NULL;
293 : }
294 :
295 0 : state->ev = ev;
296 0 : state->opts = opts;
297 0 : state->sh = sh;
298 0 : state->base_iter = 0;
299 0 : state->search_bases = opts->sdom->sudo_search_bases;
300 0 : state->filter = ldap_filter;
301 0 : state->timeout = dp_opt_get_int(opts->basic, SDAP_SEARCH_TIMEOUT);
302 0 : state->ldap_rules = NULL;
303 0 : state->ldap_rules_count = 0;
304 :
305 0 : if (!state->search_bases) {
306 0 : DEBUG(SSSDBG_CRIT_FAILURE,
307 : "SUDOERS lookup request without a search base\n");
308 0 : ret = EINVAL;
309 0 : goto done;
310 : }
311 :
312 : /* create attrs from map */
313 0 : ret = build_attrs_from_map(state, opts->sudorule_map, SDAP_OPTS_SUDO,
314 0 : NULL, &state->attrs, NULL);
315 0 : if (ret != EOK) {
316 0 : goto fail;
317 : }
318 :
319 : /* begin search */
320 0 : ret = sdap_sudo_load_sudoers_next_base(req);
321 :
322 : done:
323 0 : if (ret != EOK) {
324 0 : tevent_req_error(req, ret);
325 0 : tevent_req_post(req, ev);
326 : }
327 :
328 0 : return req;
329 :
330 : fail:
331 0 : talloc_zfree(req);
332 0 : return NULL;
333 : }
334 :
335 0 : static errno_t sdap_sudo_load_sudoers_next_base(struct tevent_req *req)
336 : {
337 : struct sdap_sudo_load_sudoers_state *state;
338 : struct sdap_search_base *search_base;
339 : struct tevent_req *subreq;
340 : char *filter;
341 :
342 0 : state = tevent_req_data(req, struct sdap_sudo_load_sudoers_state);
343 0 : search_base = state->search_bases[state->base_iter];
344 0 : if (search_base == NULL) {
345 : /* should not happen */
346 0 : DEBUG(SSSDBG_CRIT_FAILURE, "search_base is null\n");
347 0 : return EFAULT;
348 : }
349 :
350 : /* create filter */
351 0 : filter = sdap_get_id_specific_filter(state, state->filter,
352 : search_base->filter);
353 0 : if (filter == NULL) {
354 0 : return ENOMEM;
355 : }
356 :
357 : /* send request */
358 0 : DEBUG(SSSDBG_TRACE_FUNC,
359 : "Searching for sudo rules with base [%s]\n",
360 : search_base->basedn);
361 :
362 0 : subreq = sdap_get_generic_send(state,
363 : state->ev,
364 : state->opts,
365 : state->sh,
366 : search_base->basedn,
367 : search_base->scope,
368 : filter,
369 : state->attrs,
370 0 : state->opts->sudorule_map,
371 : SDAP_OPTS_SUDO,
372 : state->timeout,
373 : true);
374 0 : if (subreq == NULL) {
375 0 : return ENOMEM;
376 : }
377 :
378 0 : tevent_req_set_callback(subreq, sdap_sudo_load_sudoers_process, req);
379 :
380 0 : return EOK;
381 : }
382 :
383 0 : static void sdap_sudo_load_sudoers_process(struct tevent_req *subreq)
384 : {
385 : struct tevent_req *req;
386 : struct sdap_sudo_load_sudoers_state *state;
387 : struct sdap_search_base *search_base;
388 0 : struct sysdb_attrs **attrs = NULL;
389 : size_t count;
390 : int ret;
391 : int i;
392 :
393 0 : req = tevent_req_callback_data(subreq, struct tevent_req);
394 0 : state = tevent_req_data(req, struct sdap_sudo_load_sudoers_state);
395 0 : search_base = state->search_bases[state->base_iter];
396 :
397 0 : DEBUG(SSSDBG_TRACE_FUNC,
398 : "Receiving sudo rules with base [%s]\n",
399 : search_base->basedn);
400 :
401 0 : ret = sdap_get_generic_recv(subreq, state, &count, &attrs);
402 0 : talloc_zfree(subreq);
403 0 : if (ret) {
404 0 : tevent_req_error(req, ret);
405 0 : return;
406 : }
407 :
408 : /* add rules to result */
409 0 : if (count > 0) {
410 0 : state->ldap_rules = talloc_realloc(state, state->ldap_rules,
411 : struct sysdb_attrs *,
412 : state->ldap_rules_count + count);
413 0 : if (state->ldap_rules == NULL) {
414 0 : tevent_req_error(req, ENOMEM);
415 0 : return;
416 : }
417 :
418 0 : for (i = 0; i < count; i++) {
419 0 : state->ldap_rules[state->ldap_rules_count + i] = talloc_steal(
420 : state->ldap_rules, attrs[i]);
421 : }
422 :
423 0 : state->ldap_rules_count += count;
424 : }
425 :
426 : /* go to next base */
427 0 : state->base_iter++;
428 0 : if (state->search_bases[state->base_iter]) {
429 0 : ret = sdap_sudo_load_sudoers_next_base(req);
430 0 : if (ret != EOK) {
431 0 : tevent_req_error(req, ret);
432 : }
433 :
434 0 : return;
435 : }
436 :
437 : /* we are done */
438 0 : tevent_req_done(req);
439 : }
440 :
441 0 : static int sdap_sudo_load_sudoers_recv(struct tevent_req *req,
442 : TALLOC_CTX *mem_ctx,
443 : size_t *rules_count,
444 : struct sysdb_attrs ***rules)
445 : {
446 : struct sdap_sudo_load_sudoers_state *state;
447 :
448 0 : state = tevent_req_data(req, struct sdap_sudo_load_sudoers_state);
449 :
450 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
451 :
452 0 : *rules_count = state->ldap_rules_count;
453 0 : *rules = talloc_steal(mem_ctx, state->ldap_rules);
454 :
455 0 : return EOK;
456 : }
457 :
458 0 : static void sdap_sudo_refresh_load_done(struct tevent_req *subreq)
459 : {
460 : struct tevent_req *req; /* req from sdap_sudo_refresh_send() */
461 : struct sdap_sudo_refresh_state *state;
462 0 : struct sysdb_attrs **rules = NULL;
463 0 : size_t rules_count = 0;
464 : int ret;
465 : errno_t sret;
466 0 : bool in_transaction = false;
467 : time_t now;
468 :
469 0 : req = tevent_req_callback_data(subreq, struct tevent_req);
470 0 : state = tevent_req_data(req, struct sdap_sudo_refresh_state);
471 :
472 0 : ret = sdap_sudo_load_sudoers_recv(subreq, state, &rules_count, &rules);
473 0 : talloc_zfree(subreq);
474 0 : if (ret != EOK) {
475 0 : goto done;
476 : }
477 :
478 0 : DEBUG(SSSDBG_TRACE_FUNC, "Received %zu rules\n", rules_count);
479 :
480 : /* start transaction */
481 0 : ret = sysdb_transaction_start(state->sysdb);
482 0 : if (ret != EOK) {
483 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
484 0 : goto done;
485 : }
486 0 : in_transaction = true;
487 :
488 : /* purge cache */
489 0 : ret = sdap_sudo_purge_sudoers(state->domain, state->sysdb_filter,
490 0 : state->opts->sudorule_map, rules_count, rules);
491 0 : if (ret != EOK) {
492 0 : goto done;
493 : }
494 :
495 : /* store rules */
496 0 : now = time(NULL);
497 0 : ret = sdap_sudo_store_sudoers(state, state->domain,
498 : state->opts, rules_count, rules,
499 0 : state->domain->sudo_timeout, now,
500 : &state->highest_usn);
501 0 : if (ret != EOK) {
502 0 : goto done;
503 : }
504 :
505 : /* commit transaction */
506 0 : ret = sysdb_transaction_commit(state->sysdb);
507 0 : if (ret != EOK) {
508 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
509 0 : goto done;
510 : }
511 0 : in_transaction = false;
512 :
513 0 : DEBUG(SSSDBG_TRACE_FUNC, "Sudoers is successfuly stored in cache\n");
514 :
515 0 : ret = EOK;
516 0 : state->num_rules = rules_count;
517 :
518 : done:
519 0 : if (in_transaction) {
520 0 : sret = sysdb_transaction_cancel(state->sysdb);
521 0 : if (sret != EOK) {
522 0 : DEBUG(SSSDBG_OP_FAILURE, "Could not cancel transaction\n");
523 : }
524 : }
525 :
526 0 : state->error = ret;
527 0 : if (ret == EOK) {
528 0 : state->dp_error = DP_ERR_OK;
529 0 : tevent_req_done(req);
530 : } else {
531 0 : state->dp_error = DP_ERR_FATAL;
532 0 : tevent_req_error(req, ret);
533 : }
534 0 : }
535 :
536 0 : static int sdap_sudo_purge_sudoers(struct sss_domain_info *dom,
537 : const char *filter,
538 : struct sdap_attr_map *map,
539 : size_t rules_count,
540 : struct sysdb_attrs **rules)
541 : {
542 : const char *name;
543 : int i;
544 : errno_t ret;
545 :
546 0 : if (filter == NULL) {
547 : /* removes downloaded rules from the cache */
548 0 : if (rules_count == 0 || rules == NULL) {
549 0 : return EOK;
550 : }
551 :
552 0 : for (i = 0; i < rules_count; i++) {
553 0 : ret = sysdb_attrs_get_string(rules[i],
554 0 : map[SDAP_AT_SUDO_NAME].sys_name,
555 : &name);
556 0 : if (ret != EOK) {
557 0 : DEBUG(SSSDBG_MINOR_FAILURE,
558 : "Failed to retrieve rule name: [%s]\n", strerror(ret));
559 0 : continue;
560 : }
561 :
562 0 : ret = sysdb_sudo_purge_byname(dom, name);
563 0 : if (ret != EOK) {
564 0 : DEBUG(SSSDBG_MINOR_FAILURE,
565 : "Failed to delete rule %s: [%s]\n",
566 : name, strerror(ret));
567 0 : continue;
568 : }
569 : }
570 :
571 0 : ret = EOK;
572 : } else {
573 : /* purge cache by provided filter */
574 0 : ret = sysdb_sudo_purge_byfilter(dom, filter);
575 0 : if (ret != EOK) {
576 0 : goto done;
577 : }
578 : }
579 :
580 : done:
581 0 : if (ret != EOK) {
582 0 : DEBUG(SSSDBG_OP_FAILURE, "failed to purge sudo rules [%d]: %s\n",
583 : ret, strerror(ret));
584 : }
585 :
586 0 : return ret;
587 : }
588 :
589 0 : static int sdap_sudo_store_sudoers(TALLOC_CTX *mem_ctx,
590 : struct sss_domain_info *domain,
591 : struct sdap_options *opts,
592 : size_t rules_count,
593 : struct sysdb_attrs **rules,
594 : int cache_timeout,
595 : time_t now,
596 : char **_usn)
597 : {
598 : errno_t ret;
599 :
600 : /* Empty sudoers? Done. */
601 0 : if (rules_count == 0 || rules == NULL) {
602 0 : return EOK;
603 : }
604 :
605 0 : ret = sdap_save_native_sudorule_list(mem_ctx, domain,
606 : opts->sudorule_map, rules,
607 : rules_count, cache_timeout, now,
608 : _usn);
609 0 : if (ret != EOK) {
610 0 : DEBUG(SSSDBG_OP_FAILURE, "failed to save sudo rules [%d]: %s\n",
611 : ret, strerror(ret));
612 0 : return ret;
613 : }
614 :
615 0 : return EOK;
616 : }
|