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/backend.h"
30 : #include "providers/ldap/ldap_common.h"
31 : #include "providers/ldap/sdap.h"
32 : #include "providers/ldap/sdap_ops.h"
33 : #include "providers/ldap/sdap_sudo.h"
34 : #include "providers/ldap/sdap_sudo_shared.h"
35 : #include "db/sysdb_sudo.h"
36 :
37 : struct sdap_sudo_load_sudoers_state {
38 : struct sysdb_attrs **rules;
39 : size_t num_rules;
40 : };
41 :
42 : static void sdap_sudo_load_sudoers_done(struct tevent_req *subreq);
43 :
44 : static struct tevent_req *
45 0 : sdap_sudo_load_sudoers_send(TALLOC_CTX *mem_ctx,
46 : struct tevent_context *ev,
47 : struct sdap_options *opts,
48 : struct sdap_handle *sh,
49 : const char *ldap_filter)
50 : {
51 : struct tevent_req *req;
52 : struct tevent_req *subreq;
53 : struct sdap_sudo_load_sudoers_state *state;
54 : struct sdap_search_base **sb;
55 : int ret;
56 :
57 0 : req = tevent_req_create(mem_ctx, &state,
58 : struct sdap_sudo_load_sudoers_state);
59 0 : if (!req) {
60 0 : return NULL;
61 : }
62 :
63 0 : state->rules = NULL;
64 0 : state->num_rules = 0;
65 :
66 0 : sb = opts->sdom->sudo_search_bases;
67 0 : if (sb == NULL) {
68 0 : DEBUG(SSSDBG_CRIT_FAILURE,
69 : "SUDOERS lookup request without a search base\n");
70 0 : ret = EINVAL;
71 0 : goto immediately;
72 : }
73 :
74 0 : DEBUG(SSSDBG_TRACE_FUNC, "About to fetch sudo rules\n");
75 :
76 0 : subreq = sdap_search_bases_send(state, ev, opts, sh, sb,
77 : opts->sudorule_map, true, 0,
78 : ldap_filter, NULL);
79 0 : if (subreq == NULL) {
80 0 : ret = ENOMEM;
81 0 : goto immediately;
82 : }
83 :
84 0 : tevent_req_set_callback(subreq, sdap_sudo_load_sudoers_done, req);
85 :
86 0 : ret = EOK;
87 :
88 : immediately:
89 0 : if (ret != EOK) {
90 0 : tevent_req_error(req, ret);
91 0 : tevent_req_post(req, ev);
92 : }
93 :
94 0 : return req;
95 : }
96 :
97 0 : static void sdap_sudo_load_sudoers_done(struct tevent_req *subreq)
98 : {
99 : struct tevent_req *req;
100 : struct sdap_sudo_load_sudoers_state *state;
101 : errno_t ret;
102 :
103 0 : req = tevent_req_callback_data(subreq, struct tevent_req);
104 0 : state = tevent_req_data(req, struct sdap_sudo_load_sudoers_state);
105 :
106 0 : ret = sdap_search_bases_recv(subreq, state, &state->num_rules,
107 : &state->rules);
108 0 : talloc_zfree(subreq);
109 0 : if (ret != EOK) {
110 0 : tevent_req_error(req, ret);
111 0 : return;
112 : }
113 :
114 0 : DEBUG(SSSDBG_IMPORTANT_INFO, "Received %zu sudo rules\n",
115 : state->num_rules);
116 :
117 0 : tevent_req_done(req);
118 :
119 0 : return;
120 : }
121 :
122 0 : static int sdap_sudo_load_sudoers_recv(struct tevent_req *req,
123 : TALLOC_CTX *mem_ctx,
124 : size_t *num_rules,
125 : struct sysdb_attrs ***rules)
126 : {
127 : struct sdap_sudo_load_sudoers_state *state;
128 :
129 0 : state = tevent_req_data(req, struct sdap_sudo_load_sudoers_state);
130 :
131 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
132 :
133 0 : *num_rules = state->num_rules;
134 0 : *rules = talloc_steal(mem_ctx, state->rules);
135 :
136 0 : return EOK;
137 : }
138 :
139 0 : static char *sdap_sudo_build_host_filter(TALLOC_CTX *mem_ctx,
140 : struct sdap_attr_map *map,
141 : char **hostnames,
142 : char **ip_addr,
143 : bool netgroups,
144 : bool regexp)
145 : {
146 0 : TALLOC_CTX *tmp_ctx = NULL;
147 0 : char *filter = NULL;
148 : int i;
149 :
150 0 : tmp_ctx = talloc_new(NULL);
151 0 : if (tmp_ctx == NULL) {
152 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
153 0 : return NULL;
154 : }
155 :
156 0 : filter = talloc_strdup(tmp_ctx, "(|");
157 0 : if (filter == NULL) {
158 0 : goto done;
159 : }
160 :
161 : /* sudoHost is not specified */
162 0 : filter = talloc_asprintf_append_buffer(filter, "(!(%s=*))",
163 0 : map[SDAP_AT_SUDO_HOST].name);
164 0 : if (filter == NULL) {
165 0 : goto done;
166 : }
167 :
168 : /* ALL */
169 0 : filter = talloc_asprintf_append_buffer(filter, "(%s=ALL)",
170 0 : map[SDAP_AT_SUDO_HOST].name);
171 0 : if (filter == NULL) {
172 0 : goto done;
173 : }
174 :
175 : /* hostnames */
176 0 : if (hostnames != NULL) {
177 0 : for (i = 0; hostnames[i] != NULL; i++) {
178 0 : filter = talloc_asprintf_append_buffer(filter, "(%s=%s)",
179 0 : map[SDAP_AT_SUDO_HOST].name,
180 0 : hostnames[i]);
181 0 : if (filter == NULL) {
182 0 : goto done;
183 : }
184 : }
185 : }
186 :
187 : /* ip addresses and networks */
188 0 : if (ip_addr != NULL) {
189 0 : for (i = 0; ip_addr[i] != NULL; i++) {
190 0 : filter = talloc_asprintf_append_buffer(filter, "(%s=%s)",
191 0 : map[SDAP_AT_SUDO_HOST].name,
192 0 : ip_addr[i]);
193 0 : if (filter == NULL) {
194 0 : goto done;
195 : }
196 : }
197 : }
198 :
199 : /* sudoHost contains netgroup - will be filtered more by sudo */
200 0 : if (netgroups) {
201 0 : filter = talloc_asprintf_append_buffer(filter, SDAP_SUDO_FILTER_NETGROUP,
202 0 : map[SDAP_AT_SUDO_HOST].name,
203 : "*");
204 0 : if (filter == NULL) {
205 0 : goto done;
206 : }
207 : }
208 :
209 : /* sudoHost contains regexp - will be filtered more by sudo */
210 : /* from sudo match.c :
211 : * #define has_meta(s) (strpbrk(s, "\\?*[]") != NULL)
212 : */
213 0 : if (regexp) {
214 0 : filter = talloc_asprintf_append_buffer(filter,
215 : "(|(%s=*\\\\*)(%s=*?*)(%s=*\\2A*)"
216 : "(%s=*[*]*))",
217 0 : map[SDAP_AT_SUDO_HOST].name,
218 0 : map[SDAP_AT_SUDO_HOST].name,
219 0 : map[SDAP_AT_SUDO_HOST].name,
220 0 : map[SDAP_AT_SUDO_HOST].name);
221 0 : if (filter == NULL) {
222 0 : goto done;
223 : }
224 : }
225 :
226 0 : filter = talloc_strdup_append_buffer(filter, ")");
227 0 : if (filter == NULL) {
228 0 : goto done;
229 : }
230 :
231 0 : talloc_steal(mem_ctx, filter);
232 :
233 : done:
234 0 : talloc_free(tmp_ctx);
235 :
236 0 : return filter;
237 : }
238 :
239 0 : static char *sdap_sudo_get_filter(TALLOC_CTX *mem_ctx,
240 : struct sdap_attr_map *map,
241 : struct sdap_sudo_ctx *sudo_ctx,
242 : const char *rule_filter)
243 : {
244 0 : TALLOC_CTX *tmp_ctx = NULL;
245 0 : char *host_filter = NULL;
246 0 : char *filter = NULL;
247 :
248 0 : if (!sudo_ctx->use_host_filter) {
249 0 : return talloc_strdup(mem_ctx, rule_filter);
250 : }
251 :
252 0 : tmp_ctx = talloc_new(NULL);
253 0 : if (tmp_ctx == NULL) {
254 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
255 0 : return NULL;
256 : }
257 :
258 0 : host_filter = sdap_sudo_build_host_filter(tmp_ctx, map,
259 : sudo_ctx->hostnames,
260 : sudo_ctx->ip_addr,
261 0 : sudo_ctx->include_netgroups,
262 0 : sudo_ctx->include_regexp);
263 0 : if (host_filter == NULL) {
264 0 : goto done;
265 : }
266 :
267 0 : filter = sdap_combine_filters(tmp_ctx, rule_filter, host_filter);
268 0 : if (filter == NULL) {
269 0 : goto done;
270 : }
271 :
272 0 : talloc_steal(mem_ctx, filter);
273 :
274 : done:
275 0 : talloc_free(tmp_ctx);
276 0 : return filter;
277 : }
278 :
279 : struct sdap_sudo_refresh_state {
280 : struct sdap_sudo_ctx *sudo_ctx;
281 : struct tevent_context *ev;
282 : struct sdap_server_opts *srv_opts;
283 : struct sdap_options *opts;
284 : struct sdap_id_op *sdap_op;
285 : struct sysdb_ctx *sysdb;
286 : struct sss_domain_info *domain;
287 :
288 : const char *search_filter;
289 : const char *delete_filter;
290 :
291 : int dp_error;
292 : size_t num_rules;
293 : };
294 :
295 : static errno_t sdap_sudo_refresh_retry(struct tevent_req *req);
296 : static void sdap_sudo_refresh_connect_done(struct tevent_req *subreq);
297 : static void sdap_sudo_refresh_hostinfo_done(struct tevent_req *subreq);
298 : static errno_t sdap_sudo_refresh_sudoers(struct tevent_req *req);
299 : static void sdap_sudo_refresh_done(struct tevent_req *subreq);
300 :
301 0 : struct tevent_req *sdap_sudo_refresh_send(TALLOC_CTX *mem_ctx,
302 : struct sdap_sudo_ctx *sudo_ctx,
303 : const char *search_filter,
304 : const char *delete_filter)
305 : {
306 : struct tevent_req *req;
307 : struct sdap_sudo_refresh_state *state;
308 0 : struct sdap_id_ctx *id_ctx = sudo_ctx->id_ctx;
309 : int ret;
310 :
311 0 : req = tevent_req_create(mem_ctx, &state, struct sdap_sudo_refresh_state);
312 0 : if (!req) {
313 0 : return NULL;
314 : }
315 :
316 : /* if we don't have a search filter, this request is meaningless */
317 0 : if (search_filter == NULL) {
318 0 : ret = EINVAL;
319 0 : goto immediately;
320 : }
321 :
322 0 : state->sudo_ctx = sudo_ctx;
323 0 : state->ev = id_ctx->be->ev;
324 0 : state->opts = id_ctx->opts;
325 0 : state->domain = id_ctx->be->domain;
326 0 : state->sysdb = id_ctx->be->domain->sysdb;
327 0 : state->dp_error = DP_ERR_FATAL;
328 :
329 0 : state->sdap_op = sdap_id_op_create(state, id_ctx->conn->conn_cache);
330 0 : if (!state->sdap_op) {
331 0 : DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create() failed\n");
332 0 : ret = ENOMEM;
333 0 : goto immediately;
334 : }
335 :
336 0 : state->search_filter = talloc_strdup(state, search_filter);
337 0 : if (state->search_filter == NULL) {
338 0 : ret = ENOMEM;
339 0 : goto immediately;
340 : }
341 :
342 0 : state->delete_filter = talloc_strdup(state, delete_filter);
343 0 : if (delete_filter != NULL && state->delete_filter == NULL) {
344 0 : ret = ENOMEM;
345 0 : goto immediately;
346 : }
347 :
348 0 : ret = sdap_sudo_refresh_retry(req);
349 0 : if (ret == EAGAIN) {
350 : /* asynchronous processing */
351 0 : return req;
352 : }
353 :
354 : immediately:
355 0 : if (ret == EOK) {
356 0 : tevent_req_done(req);
357 : } else {
358 0 : tevent_req_error(req, ret);
359 : }
360 0 : tevent_req_post(req, id_ctx->be->ev);
361 :
362 0 : return req;
363 : }
364 :
365 0 : static errno_t sdap_sudo_refresh_retry(struct tevent_req *req)
366 : {
367 : struct sdap_sudo_refresh_state *state;
368 : struct tevent_req *subreq;
369 : int ret;
370 :
371 0 : state = tevent_req_data(req, struct sdap_sudo_refresh_state);
372 :
373 0 : subreq = sdap_id_op_connect_send(state->sdap_op, state, &ret);
374 0 : if (subreq == NULL) {
375 0 : DEBUG(SSSDBG_CRIT_FAILURE, "sdap_id_op_connect_send() failed: "
376 : "%d(%s)\n", ret, strerror(ret));
377 0 : return ret;
378 : }
379 :
380 0 : tevent_req_set_callback(subreq, sdap_sudo_refresh_connect_done, req);
381 :
382 0 : return EAGAIN;
383 : }
384 :
385 0 : static void sdap_sudo_refresh_connect_done(struct tevent_req *subreq)
386 : {
387 : struct tevent_req *req;
388 : struct sdap_sudo_refresh_state *state;
389 : int dp_error;
390 : int ret;
391 :
392 0 : req = tevent_req_callback_data(subreq, struct tevent_req);
393 0 : state = tevent_req_data(req, struct sdap_sudo_refresh_state);
394 :
395 0 : ret = sdap_id_op_connect_recv(subreq, &dp_error);
396 0 : talloc_zfree(subreq);
397 :
398 0 : if (ret != EOK) {
399 0 : DEBUG(SSSDBG_CRIT_FAILURE, "SUDO LDAP connection failed "
400 : "[%d]: %s\n", ret, strerror(ret));
401 0 : state->dp_error = dp_error;
402 0 : tevent_req_error(req, ret);
403 0 : return;
404 : }
405 :
406 0 : DEBUG(SSSDBG_TRACE_FUNC, "SUDO LDAP connection successful\n");
407 :
408 : /* Obtain srv_opts here in case of first connection. */
409 0 : state->srv_opts = state->sudo_ctx->id_ctx->srv_opts;
410 :
411 : /* Renew host information if needed. */
412 0 : if (state->sudo_ctx->run_hostinfo) {
413 0 : subreq = sdap_sudo_get_hostinfo_send(state, state->opts,
414 0 : state->sudo_ctx->id_ctx->be);
415 0 : if (subreq == NULL) {
416 0 : state->dp_error = DP_ERR_FATAL;
417 0 : tevent_req_error(req, ENOMEM);
418 0 : return;
419 : }
420 :
421 0 : tevent_req_set_callback(subreq, sdap_sudo_refresh_hostinfo_done, req);
422 0 : state->sudo_ctx->run_hostinfo = false;
423 0 : return;
424 : }
425 :
426 0 : ret = sdap_sudo_refresh_sudoers(req);
427 0 : if (ret != EAGAIN) {
428 0 : state->dp_error = DP_ERR_FATAL;
429 0 : tevent_req_error(req, ret);
430 : }
431 : }
432 :
433 0 : static void sdap_sudo_refresh_hostinfo_done(struct tevent_req *subreq)
434 : {
435 : struct sdap_sudo_ctx *sudo_ctx;
436 : struct sdap_sudo_refresh_state *state;
437 : struct tevent_req *req;
438 : errno_t ret;
439 :
440 0 : req = tevent_req_callback_data(subreq, struct tevent_req);
441 0 : state = tevent_req_data(req, struct sdap_sudo_refresh_state);
442 :
443 0 : sudo_ctx = state->sudo_ctx;
444 :
445 0 : ret = sdap_sudo_get_hostinfo_recv(sudo_ctx, subreq, &sudo_ctx->hostnames,
446 : &sudo_ctx->ip_addr);
447 0 : talloc_zfree(subreq);
448 0 : if (ret != EOK) {
449 0 : DEBUG(SSSDBG_OP_FAILURE, "Unable to retrieve host information, "
450 : "host filter will be disabled [%d]: %s\n",
451 : ret, sss_strerror(ret));
452 0 : sudo_ctx->use_host_filter = false;
453 : } else {
454 0 : sudo_ctx->use_host_filter = true;
455 : }
456 :
457 0 : ret = sdap_sudo_refresh_sudoers(req);
458 0 : if (ret != EAGAIN) {
459 0 : state->dp_error = DP_ERR_FATAL;
460 0 : tevent_req_error(req, ret);
461 : }
462 0 : }
463 :
464 0 : static errno_t sdap_sudo_refresh_sudoers(struct tevent_req *req)
465 : {
466 : struct sdap_sudo_refresh_state *state;
467 : struct tevent_req *subreq;
468 : char *filter;
469 :
470 0 : state = tevent_req_data(req, struct sdap_sudo_refresh_state);
471 :
472 : /* We are connected. Host information may have changed during transition
473 : * from offline to online state. At this point we can combine search
474 : * and host filter. */
475 0 : filter = sdap_sudo_get_filter(state, state->opts->sudorule_map,
476 : state->sudo_ctx, state->search_filter);
477 0 : if (filter == NULL) {
478 0 : return ENOMEM;
479 : }
480 :
481 0 : subreq = sdap_sudo_load_sudoers_send(state, state->ev,
482 : state->opts,
483 : sdap_id_op_handle(state->sdap_op),
484 : filter);
485 0 : if (subreq == NULL) {
486 0 : talloc_free(filter);
487 0 : return ENOMEM;
488 : }
489 :
490 0 : tevent_req_set_callback(subreq, sdap_sudo_refresh_done, req);
491 :
492 0 : return EAGAIN;
493 : }
494 :
495 0 : static void sdap_sudo_refresh_done(struct tevent_req *subreq)
496 : {
497 : struct tevent_req *req;
498 : struct sdap_sudo_refresh_state *state;
499 0 : struct sysdb_attrs **rules = NULL;
500 0 : size_t rules_count = 0;
501 0 : char *usn = NULL;
502 : int dp_error;
503 : int ret;
504 : errno_t sret;
505 0 : bool in_transaction = false;
506 :
507 0 : req = tevent_req_callback_data(subreq, struct tevent_req);
508 0 : state = tevent_req_data(req, struct sdap_sudo_refresh_state);
509 :
510 0 : ret = sdap_sudo_load_sudoers_recv(subreq, state, &rules_count, &rules);
511 0 : talloc_zfree(subreq);
512 :
513 0 : ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
514 0 : if (dp_error == DP_ERR_OK && ret != EOK) {
515 : /* retry */
516 0 : ret = sdap_sudo_refresh_retry(req);
517 0 : if (ret != EOK) {
518 0 : tevent_req_error(req, ret);
519 : }
520 0 : return;
521 0 : } else if (ret != EOK) {
522 0 : tevent_req_error(req, ret);
523 0 : return;
524 : }
525 :
526 0 : DEBUG(SSSDBG_TRACE_FUNC, "Received %zu rules\n", rules_count);
527 :
528 : /* start transaction */
529 0 : ret = sysdb_transaction_start(state->sysdb);
530 0 : if (ret != EOK) {
531 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
532 0 : goto done;
533 : }
534 0 : in_transaction = true;
535 :
536 : /* purge cache */
537 0 : ret = sysdb_sudo_purge(state->domain, state->delete_filter,
538 : rules, rules_count);
539 0 : if (ret != EOK) {
540 0 : goto done;
541 : }
542 :
543 : /* store rules */
544 0 : ret = sysdb_sudo_store(state->domain, rules, rules_count);
545 0 : if (ret != EOK) {
546 0 : goto done;
547 : }
548 :
549 : /* commit transaction */
550 0 : ret = sysdb_transaction_commit(state->sysdb);
551 0 : if (ret != EOK) {
552 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
553 0 : goto done;
554 : }
555 0 : in_transaction = false;
556 :
557 0 : DEBUG(SSSDBG_TRACE_FUNC, "Sudoers is successfuly stored in cache\n");
558 :
559 : /* remember new usn */
560 0 : ret = sysdb_get_highest_usn(state, rules, rules_count, &usn);
561 0 : if (ret == EOK) {
562 0 : sdap_sudo_set_usn(state->srv_opts, usn);
563 : } else {
564 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Unable to get highest USN [%d]: %s\n",
565 : ret, sss_strerror(ret));
566 : }
567 :
568 0 : ret = EOK;
569 0 : state->num_rules = rules_count;
570 :
571 : done:
572 0 : if (in_transaction) {
573 0 : sret = sysdb_transaction_cancel(state->sysdb);
574 0 : if (sret != EOK) {
575 0 : DEBUG(SSSDBG_OP_FAILURE, "Could not cancel transaction\n");
576 : }
577 : }
578 :
579 0 : state->dp_error = dp_error;
580 0 : if (ret == EOK) {
581 0 : tevent_req_done(req);
582 : } else {
583 0 : tevent_req_error(req, ret);
584 : }
585 : }
586 :
587 0 : int sdap_sudo_refresh_recv(TALLOC_CTX *mem_ctx,
588 : struct tevent_req *req,
589 : int *dp_error,
590 : size_t *num_rules)
591 : {
592 : struct sdap_sudo_refresh_state *state;
593 :
594 0 : state = tevent_req_data(req, struct sdap_sudo_refresh_state);
595 :
596 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
597 :
598 0 : *dp_error = state->dp_error;
599 :
600 0 : if (num_rules != NULL) {
601 0 : *num_rules = state->num_rules;
602 : }
603 :
604 0 : return EOK;
605 : }
|