Line data Source code
1 : /*
2 : Authors:
3 : Pavel Březina <pbrezina@redhat.com>
4 : Jakub Hrozek <jhrozek@redhat.com>
5 :
6 : Copyright (C) 2011 Red Hat
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program. If not, see <http://www.gnu.org/licenses/>.
20 : */
21 :
22 : #include "config.h"
23 :
24 : #include <stdint.h>
25 : #include <string.h>
26 : #include <talloc.h>
27 :
28 : #include "util/util.h"
29 : #include "db/sysdb_sudo.h"
30 : #include "responder/sudo/sudosrv_private.h"
31 : #include "providers/data_provider.h"
32 :
33 : static errno_t sudosrv_get_user(struct sudo_dom_ctx *dctx);
34 :
35 0 : errno_t sudosrv_get_sudorules(struct sudo_dom_ctx *dctx)
36 : {
37 : errno_t ret;
38 :
39 0 : dctx->check_provider = true;
40 0 : ret = sudosrv_get_user(dctx);
41 0 : if (ret == EAGAIN) {
42 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
43 : "Looking up the user info from Data Provider\n");
44 0 : return EAGAIN;
45 0 : } else if (ret != EOK) {
46 0 : DEBUG(SSSDBG_OP_FAILURE,
47 : "Error looking up user information [%d]: %s\n", ret, strerror(ret));
48 0 : return ret;
49 : }
50 :
51 : /* OK, got the user from cache. Try to get the rules. */
52 0 : ret = sudosrv_get_rules(dctx->cmd_ctx);
53 0 : if (ret == EAGAIN) {
54 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
55 : "Looking up the sudo rules from Data Provider\n");
56 0 : return EAGAIN;
57 0 : } else if (ret != EOK) {
58 0 : DEBUG(SSSDBG_OP_FAILURE,
59 : "Error looking up sudo rules [%d]: %s\n", ret, strerror(ret));
60 0 : return ret;
61 : }
62 :
63 0 : return EOK;
64 : }
65 :
66 : static void sudosrv_dp_send_acct_req_done(struct tevent_req *req);
67 : static void sudosrv_check_user_dp_callback(uint16_t err_maj, uint32_t err_min,
68 : const char *err_msg, void *ptr);
69 :
70 0 : static errno_t sudosrv_get_user(struct sudo_dom_ctx *dctx)
71 : {
72 0 : TALLOC_CTX *tmp_ctx = NULL;
73 0 : struct sss_domain_info *dom = dctx->domain;
74 0 : struct sudo_cmd_ctx *cmd_ctx = dctx->cmd_ctx;
75 0 : struct cli_ctx *cli_ctx = dctx->cmd_ctx->cli_ctx;
76 : struct ldb_result *user;
77 0 : time_t cache_expire = 0;
78 : struct tevent_req *dpreq;
79 : struct dp_callback_ctx *cb_ctx;
80 0 : const char *original_name = NULL;
81 0 : const char *extra_flag = NULL;
82 0 : const char *search_name = NULL;
83 0 : char *name = NULL;
84 0 : uid_t uid = 0;
85 : errno_t ret;
86 :
87 0 : tmp_ctx = talloc_new(NULL);
88 0 : if (tmp_ctx == NULL) {
89 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
90 0 : return ENOMEM;
91 : }
92 :
93 0 : while (dom) {
94 : /* if it is a domainless search, skip domains that require fully
95 : * qualified names instead */
96 0 : while (dom && cmd_ctx->check_next && dom->fqnames) {
97 0 : dom = get_next_domain(dom, false);
98 : }
99 :
100 0 : if (!dom) break;
101 :
102 : /* make sure to update the dctx if we changed domain */
103 0 : dctx->domain = dom;
104 :
105 0 : talloc_free(name);
106 0 : name = sss_get_cased_name(tmp_ctx, cmd_ctx->username,
107 0 : dom->case_sensitive);
108 0 : if (name == NULL) {
109 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory\n");
110 0 : ret = ENOMEM;
111 0 : goto done;
112 : }
113 :
114 0 : name = sss_reverse_replace_space(tmp_ctx, name,
115 0 : cmd_ctx->sudo_ctx->rctx->override_space);
116 0 : if (name == NULL) {
117 0 : DEBUG(SSSDBG_CRIT_FAILURE,
118 : "sss_reverse_replace_whitespaces failed\n");
119 0 : return ENOMEM;
120 : }
121 :
122 0 : DEBUG(SSSDBG_FUNC_DATA, "Requesting info about [%s@%s]\n",
123 : name, dom->name);
124 :
125 0 : ret = sysdb_getpwnam_with_views(dctx, dctx->domain, name, &user);
126 0 : if (ret != EOK) {
127 0 : DEBUG(SSSDBG_OP_FAILURE,
128 : "Failed to make request to our cache!\n");
129 0 : ret = EIO;
130 0 : goto done;
131 : }
132 :
133 0 : if (user->count > 1) {
134 0 : DEBUG(SSSDBG_CRIT_FAILURE,
135 : "getpwnam call returned more than one result !?!\n");
136 0 : ret = EIO;
137 0 : goto done;
138 : }
139 :
140 0 : if (user->count == 0 && !dctx->check_provider) {
141 : /* if a multidomain search, try with next */
142 0 : if (cmd_ctx->check_next) {
143 0 : dctx->check_provider = true;
144 0 : dom = get_next_domain(dom, false);
145 0 : if (dom) continue;
146 : }
147 :
148 0 : DEBUG(SSSDBG_MINOR_FAILURE, "No results for getpwnam call\n");
149 0 : ret = ENOENT;
150 0 : goto done;
151 : }
152 :
153 : /* One result found, check cache expiry */
154 0 : if (user->count == 1) {
155 0 : cache_expire = ldb_msg_find_attr_as_uint64(user->msgs[0],
156 : SYSDB_CACHE_EXPIRE, 0);
157 : }
158 :
159 : /* If cache miss and we haven't checked DP yet OR the entry is
160 : * outdated, go to DP */
161 0 : if ((user->count == 0 || cache_expire < time(NULL))
162 0 : && dctx->check_provider) {
163 :
164 0 : search_name = cmd_ctx->username;
165 0 : if (is_local_view(dom->view_name)) {
166 : /* Search with original name in case of local view. */
167 0 : if (user->count != 0) {
168 0 : search_name = ldb_msg_find_attr_as_string(user->msgs[0],
169 : SYSDB_NAME, NULL);
170 : }
171 0 : } else if (DOM_HAS_VIEWS(dom) && (user->count == 0
172 0 : || ldb_msg_find_attr_as_string(user->msgs[0],
173 : OVERRIDE_PREFIX SYSDB_NAME,
174 : NULL) != NULL)) {
175 0 : extra_flag = EXTRA_INPUT_MAYBE_WITH_VIEW;
176 : }
177 :
178 0 : dpreq = sss_dp_get_account_send(cli_ctx, cli_ctx->rctx,
179 : dom, false, SSS_DP_INITGROUPS,
180 : search_name, 0, extra_flag);
181 0 : if (!dpreq) {
182 0 : DEBUG(SSSDBG_CRIT_FAILURE,
183 : "Out of memory sending data provider request\n");
184 0 : ret = ENOMEM;
185 0 : goto done;
186 : }
187 :
188 0 : cb_ctx = talloc_zero(cli_ctx, struct dp_callback_ctx);
189 0 : if(!cb_ctx) {
190 0 : talloc_zfree(dpreq);
191 0 : ret = ENOMEM;
192 0 : goto done;
193 : }
194 :
195 0 : cb_ctx->callback = sudosrv_check_user_dp_callback;
196 0 : cb_ctx->ptr = dctx;
197 0 : cb_ctx->cctx = cli_ctx;
198 0 : cb_ctx->mem_ctx = cli_ctx;
199 :
200 0 : tevent_req_set_callback(dpreq, sudosrv_dp_send_acct_req_done, cb_ctx);
201 :
202 : /* tell caller we are in an async call */
203 0 : ret = EAGAIN;
204 0 : goto done;
205 : }
206 :
207 : /* check uid */
208 0 : uid = sss_view_ldb_msg_find_attr_as_uint64(dom, user->msgs[0],
209 : SYSDB_UIDNUM, 0);
210 0 : if (uid != cmd_ctx->uid) {
211 : /* if a multidomain search, try with next */
212 0 : if (cmd_ctx->check_next) {
213 0 : dctx->check_provider = true;
214 0 : dom = get_next_domain(dom, false);
215 0 : if (dom) continue;
216 : }
217 :
218 0 : DEBUG(SSSDBG_MINOR_FAILURE, "UID does not match\n");
219 0 : ret = ENOENT;
220 0 : goto done;
221 : }
222 :
223 : /* user is stored in cache, remember cased and original name */
224 0 : original_name = ldb_msg_find_attr_as_string(user->msgs[0],
225 : SYSDB_NAME, NULL);
226 0 : if (original_name == NULL) {
227 0 : DEBUG(SSSDBG_CRIT_FAILURE, "A user with no name?\n");
228 0 : ret = EFAULT;
229 0 : goto done;
230 : }
231 :
232 0 : cmd_ctx->cased_username = talloc_move(cmd_ctx, &name);
233 0 : cmd_ctx->orig_username = talloc_strdup(cmd_ctx, original_name);
234 0 : if (cmd_ctx->orig_username == NULL) {
235 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory\n");
236 0 : ret = ENOMEM;
237 0 : goto done;
238 : }
239 :
240 : /* and set domain */
241 0 : cmd_ctx->domain = dom;
242 :
243 0 : DEBUG(SSSDBG_TRACE_FUNC, "Returning info for user [%s@%s]\n",
244 : cmd_ctx->username, dctx->domain->name);
245 0 : ret = EOK;
246 0 : goto done;
247 : }
248 :
249 0 : ret = ENOENT;
250 : done:
251 0 : talloc_free(tmp_ctx);
252 0 : return ret;
253 : }
254 :
255 0 : static void sudosrv_dp_send_acct_req_done(struct tevent_req *req)
256 : {
257 0 : struct dp_callback_ctx *cb_ctx =
258 0 : tevent_req_callback_data(req, struct dp_callback_ctx);
259 :
260 : errno_t ret;
261 : dbus_uint16_t err_maj;
262 : dbus_uint32_t err_min;
263 : char *err_msg;
264 :
265 0 : ret = sss_dp_get_account_recv(cb_ctx->mem_ctx, req,
266 : &err_maj, &err_min,
267 : &err_msg);
268 0 : talloc_zfree(req);
269 0 : if (ret != EOK) {
270 0 : DEBUG(SSSDBG_CRIT_FAILURE,
271 : "Fatal error, killing connection!\n");
272 0 : talloc_free(cb_ctx->cctx);
273 0 : return;
274 : }
275 :
276 0 : cb_ctx->callback(err_maj, err_min, err_msg, cb_ctx->ptr);
277 : }
278 :
279 0 : static void sudosrv_check_user_dp_callback(uint16_t err_maj, uint32_t err_min,
280 : const char *err_msg, void *ptr)
281 : {
282 : errno_t ret;
283 0 : struct sudo_dom_ctx *dctx = talloc_get_type(ptr, struct sudo_dom_ctx);
284 :
285 0 : if (err_maj) {
286 0 : DEBUG(SSSDBG_CRIT_FAILURE,
287 : "Unable to get information from Data Provider\n"
288 : "Error: %u, %u, %s\n",
289 : (unsigned int)err_maj, (unsigned int)err_min, err_msg);
290 : }
291 :
292 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
293 : "Data Provider returned, check the cache again\n");
294 0 : dctx->check_provider = false;
295 0 : ret = sudosrv_get_user(dctx);
296 0 : if (ret == EAGAIN) {
297 0 : goto done;
298 0 : } else if (ret != EOK) {
299 0 : DEBUG(SSSDBG_OP_FAILURE,
300 : "Could not look up the user [%d]: %s\n",
301 : ret, strerror(ret));
302 0 : sudosrv_cmd_done(dctx->cmd_ctx, ret);
303 0 : return;
304 : }
305 :
306 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Looking up sudo rules..\n");
307 0 : ret = sudosrv_get_rules(dctx->cmd_ctx);
308 0 : if (ret == EAGAIN) {
309 0 : goto done;
310 0 : } else if (ret != EOK) {
311 0 : DEBUG(SSSDBG_OP_FAILURE,
312 : "Error getting sudo rules [%d]: %s\n",
313 : ret, strerror(ret));
314 0 : sudosrv_cmd_done(dctx->cmd_ctx, EIO);
315 0 : return;
316 : }
317 :
318 : done:
319 0 : sudosrv_cmd_done(dctx->cmd_ctx, ret);
320 : }
321 :
322 : static errno_t sudosrv_get_sudorules_from_cache(TALLOC_CTX *mem_ctx,
323 : struct sudo_cmd_ctx *cmd_ctx,
324 : struct sysdb_attrs ***_rules,
325 : uint32_t *_num_rules);
326 : static void
327 : sudosrv_get_sudorules_dp_callback(uint16_t err_maj, uint32_t err_min,
328 : const char *err_msg, void *ptr);
329 : static void
330 : sudosrv_dp_req_done(struct tevent_req *req);
331 :
332 : static errno_t sudosrv_get_sudorules_query_cache(TALLOC_CTX *mem_ctx,
333 : struct sss_domain_info *domain,
334 : const char **attrs,
335 : unsigned int flags,
336 : const char *username,
337 : uid_t uid,
338 : char **groupnames,
339 : bool inverse_order,
340 : struct sysdb_attrs ***_rules,
341 : uint32_t *_count);
342 :
343 0 : errno_t sudosrv_get_rules(struct sudo_cmd_ctx *cmd_ctx)
344 : {
345 0 : TALLOC_CTX *tmp_ctx = NULL;
346 0 : struct tevent_req *dpreq = NULL;
347 0 : struct dp_callback_ctx *cb_ctx = NULL;
348 0 : char **groupnames = NULL;
349 0 : uint32_t expired_rules_num = 0;
350 0 : struct sysdb_attrs **expired_rules = NULL;
351 : errno_t ret;
352 0 : unsigned int flags = SYSDB_SUDO_FILTER_NONE;
353 0 : const char *attrs[] = { SYSDB_NAME,
354 : NULL };
355 :
356 0 : if (cmd_ctx->domain == NULL) {
357 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Domain is not set!\n");
358 0 : return EFAULT;
359 : }
360 :
361 0 : tmp_ctx = talloc_new(NULL);
362 0 : if (tmp_ctx == NULL) {
363 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
364 0 : return ENOMEM;
365 : }
366 :
367 0 : switch (cmd_ctx->type) {
368 : case SSS_SUDO_DEFAULTS:
369 0 : DEBUG(SSSDBG_TRACE_FUNC, "Retrieving default options "
370 : "for [%s] from [%s]\n", cmd_ctx->orig_username,
371 : cmd_ctx->domain->name);
372 0 : break;
373 : case SSS_SUDO_USER:
374 0 : DEBUG(SSSDBG_TRACE_FUNC, "Retrieving rules "
375 : "for [%s] from [%s]\n", cmd_ctx->orig_username,
376 : cmd_ctx->domain->name);
377 0 : break;
378 : }
379 :
380 : /* Fetch all expired rules:
381 : * sudo asks sssd twice - for defaults and for rules. If we refresh all
382 : * expired rules for this user and defaults at once we will save one
383 : * provider call
384 : */
385 0 : ret = sysdb_get_sudo_user_info(tmp_ctx, cmd_ctx->domain,
386 : cmd_ctx->orig_username, NULL, &groupnames);
387 0 : if (ret != EOK) {
388 0 : DEBUG(SSSDBG_CRIT_FAILURE,
389 : "Unable to retrieve user info [%d]: %s\n", ret, strerror(ret));
390 0 : goto done;
391 : }
392 :
393 0 : flags = SYSDB_SUDO_FILTER_INCLUDE_ALL
394 : | SYSDB_SUDO_FILTER_INCLUDE_DFL
395 : | SYSDB_SUDO_FILTER_ONLY_EXPIRED
396 : | SYSDB_SUDO_FILTER_USERINFO;
397 0 : ret = sudosrv_get_sudorules_query_cache(tmp_ctx,
398 : cmd_ctx->domain, attrs, flags,
399 : cmd_ctx->orig_username,
400 : cmd_ctx->uid, groupnames,
401 0 : cmd_ctx->sudo_ctx->inverse_order,
402 : &expired_rules, &expired_rules_num);
403 0 : if (ret != EOK) {
404 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to retrieve expired sudo rules "
405 : "[%d]: %s\n", ret, strerror(ret));
406 0 : goto done;
407 : }
408 :
409 0 : cmd_ctx->expired_rules_num = expired_rules_num;
410 0 : if (expired_rules_num > 0) {
411 : /* refresh expired rules then continue */
412 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Refreshing %d expired rules\n",
413 : expired_rules_num);
414 0 : dpreq = sss_dp_get_sudoers_send(tmp_ctx, cmd_ctx->cli_ctx->rctx,
415 : cmd_ctx->domain, false,
416 : SSS_DP_SUDO_REFRESH_RULES,
417 : cmd_ctx->orig_username,
418 : expired_rules_num, expired_rules);
419 0 : if (dpreq == NULL) {
420 0 : DEBUG(SSSDBG_CRIT_FAILURE,
421 : "Cannot issue DP request.\n");
422 0 : ret = EIO;
423 0 : goto done;
424 : }
425 :
426 0 : cb_ctx = talloc_zero(tmp_ctx, struct dp_callback_ctx);
427 0 : if (!cb_ctx) {
428 0 : talloc_zfree(dpreq);
429 0 : ret = ENOMEM;
430 0 : goto done;
431 : }
432 :
433 0 : cb_ctx->callback = sudosrv_get_sudorules_dp_callback;
434 0 : cb_ctx->ptr = cmd_ctx;
435 0 : cb_ctx->cctx = cmd_ctx->cli_ctx;
436 0 : cb_ctx->mem_ctx = cmd_ctx;
437 :
438 0 : tevent_req_set_callback(dpreq, sudosrv_dp_req_done, cb_ctx);
439 0 : ret = EAGAIN;
440 :
441 : } else {
442 : /* nothing is expired return what we have in the cache */
443 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "About to get sudo rules from cache\n");
444 0 : ret = sudosrv_get_sudorules_from_cache(cmd_ctx, cmd_ctx,
445 : &cmd_ctx->rules,
446 : &cmd_ctx->num_rules);
447 0 : if (ret != EOK) {
448 0 : DEBUG(SSSDBG_OP_FAILURE,
449 : "Failed to make a request to our cache [%d]: %s\n",
450 : ret, strerror(ret));
451 0 : goto done;
452 : }
453 : }
454 :
455 0 : if (dpreq != NULL) {
456 0 : talloc_steal(cmd_ctx->cli_ctx, dpreq);
457 : }
458 :
459 0 : if (cb_ctx != NULL) {
460 0 : talloc_steal(cmd_ctx, cb_ctx);
461 : }
462 :
463 : done:
464 0 : talloc_free(tmp_ctx);
465 0 : return ret;
466 : }
467 :
468 : static void
469 0 : sudosrv_dp_req_done(struct tevent_req *req)
470 : {
471 0 : struct dp_callback_ctx *cb_ctx =
472 0 : tevent_req_callback_data(req, struct dp_callback_ctx);
473 : struct cli_ctx *cli_ctx;
474 :
475 : errno_t ret;
476 : dbus_uint16_t err_maj;
477 : dbus_uint32_t err_min;
478 : char *err_msg;
479 :
480 0 : cli_ctx = talloc_get_type(cb_ctx->cctx, struct cli_ctx);
481 :
482 0 : ret = sss_dp_get_sudoers_recv(cb_ctx->mem_ctx, req,
483 : &err_maj, &err_min,
484 : &err_msg);
485 0 : talloc_free(req);
486 0 : if (ret != EOK) {
487 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Fatal error, killing connection!\n");
488 0 : talloc_free(cli_ctx);
489 0 : return;
490 : }
491 :
492 0 : cb_ctx->callback(err_maj, err_min, err_msg, cb_ctx->ptr);
493 : }
494 :
495 : static void
496 0 : sudosrv_dp_oob_req_done(struct tevent_req *req)
497 : {
498 0 : DEBUG(SSSDBG_TRACE_FUNC, "Out of band refresh finished\n");
499 0 : talloc_free(req);
500 0 : }
501 :
502 : static void
503 0 : sudosrv_get_sudorules_dp_callback(uint16_t err_maj, uint32_t err_min,
504 : const char *err_msg, void *ptr)
505 : {
506 0 : struct sudo_cmd_ctx *cmd_ctx = talloc_get_type(ptr, struct sudo_cmd_ctx);
507 0 : struct tevent_req *dpreq = NULL;
508 : errno_t ret;
509 :
510 0 : if (err_maj) {
511 0 : DEBUG(SSSDBG_CRIT_FAILURE,
512 : "Unable to get information from Data Provider\n"
513 : "Error: %u, %u, %s\n"
514 : "Will try to return what we have in cache\n",
515 : (unsigned int)err_maj, (unsigned int)err_min, err_msg);
516 : }
517 :
518 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "About to get sudo rules from cache\n");
519 0 : ret = sudosrv_get_sudorules_from_cache(cmd_ctx, cmd_ctx, &cmd_ctx->rules,
520 : &cmd_ctx->num_rules);
521 0 : if (ret != EOK) {
522 0 : DEBUG(SSSDBG_OP_FAILURE,
523 : "Failed to make a request to our cache [%d]: %s\n",
524 : ret, strerror(ret));
525 0 : sudosrv_cmd_done(cmd_ctx, EIO);
526 0 : return;
527 : }
528 :
529 0 : if (cmd_ctx->expired_rules_num > 0
530 0 : && err_min == ENOENT) {
531 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
532 : "Some expired rules were removed from the server, "
533 : "scheduling full refresh out of band\n");
534 0 : dpreq = sss_dp_get_sudoers_send(cmd_ctx->cli_ctx->rctx,
535 0 : cmd_ctx->cli_ctx->rctx,
536 : cmd_ctx->domain, false,
537 : SSS_DP_SUDO_FULL_REFRESH,
538 : cmd_ctx->orig_username,
539 : 0, NULL);
540 0 : if (dpreq == NULL) {
541 0 : DEBUG(SSSDBG_CRIT_FAILURE,
542 : "Cannot issue DP request.\n");
543 : } else {
544 0 : tevent_req_set_callback(dpreq, sudosrv_dp_oob_req_done, NULL);
545 : }
546 : }
547 :
548 0 : sudosrv_cmd_done(cmd_ctx, ret);
549 : }
550 :
551 0 : static errno_t sudosrv_get_sudorules_from_cache(TALLOC_CTX *mem_ctx,
552 : struct sudo_cmd_ctx *cmd_ctx,
553 : struct sysdb_attrs ***_rules,
554 : uint32_t *_num_rules)
555 : {
556 : TALLOC_CTX *tmp_ctx;
557 : errno_t ret;
558 0 : char **groupnames = NULL;
559 0 : const char *debug_name = NULL;
560 0 : unsigned int flags = SYSDB_SUDO_FILTER_NONE;
561 0 : struct sysdb_attrs **rules = NULL;
562 0 : uint32_t num_rules = 0;
563 0 : const char *attrs[] = { SYSDB_OBJECTCLASS,
564 : SYSDB_SUDO_CACHE_AT_CN,
565 : SYSDB_SUDO_CACHE_AT_USER,
566 : SYSDB_SUDO_CACHE_AT_HOST,
567 : SYSDB_SUDO_CACHE_AT_COMMAND,
568 : SYSDB_SUDO_CACHE_AT_OPTION,
569 : SYSDB_SUDO_CACHE_AT_RUNAS,
570 : SYSDB_SUDO_CACHE_AT_RUNASUSER,
571 : SYSDB_SUDO_CACHE_AT_RUNASGROUP,
572 : SYSDB_SUDO_CACHE_AT_NOTBEFORE,
573 : SYSDB_SUDO_CACHE_AT_NOTAFTER,
574 : SYSDB_SUDO_CACHE_AT_ORDER,
575 : NULL };
576 :
577 0 : if (cmd_ctx->domain == NULL) {
578 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Domain is not set!\n");
579 0 : return EFAULT;
580 : }
581 :
582 0 : tmp_ctx = talloc_new(NULL);
583 0 : if (tmp_ctx == NULL) {
584 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
585 0 : return ENOMEM;
586 : }
587 :
588 0 : switch (cmd_ctx->type) {
589 : case SSS_SUDO_USER:
590 0 : debug_name = cmd_ctx->cased_username;
591 0 : ret = sysdb_get_sudo_user_info(tmp_ctx,
592 : cmd_ctx->domain,
593 : cmd_ctx->orig_username,
594 : NULL, &groupnames);
595 0 : if (ret != EOK) {
596 0 : DEBUG(SSSDBG_CRIT_FAILURE,
597 : "Unable to retrieve user info [%d]: %s\n",
598 : ret, strerror(ret));
599 0 : goto done;
600 : }
601 0 : flags = SYSDB_SUDO_FILTER_USERINFO | SYSDB_SUDO_FILTER_INCLUDE_ALL;
602 0 : break;
603 : case SSS_SUDO_DEFAULTS:
604 0 : debug_name = "<default options>";
605 0 : flags = SYSDB_SUDO_FILTER_INCLUDE_DFL;
606 0 : break;
607 : }
608 :
609 0 : ret = sudosrv_get_sudorules_query_cache(tmp_ctx,
610 : cmd_ctx->domain, attrs, flags,
611 : cmd_ctx->orig_username,
612 : cmd_ctx->uid, groupnames,
613 0 : cmd_ctx->sudo_ctx->inverse_order,
614 : &rules, &num_rules);
615 0 : if (ret != EOK) {
616 0 : DEBUG(SSSDBG_CRIT_FAILURE,
617 : "Unable to retrieve sudo rules [%d]: %s\n", ret, strerror(ret));
618 0 : goto done;
619 : }
620 :
621 0 : DEBUG(SSSDBG_TRACE_FUNC, "Returning %d rules for [%s@%s]\n",
622 : num_rules, debug_name, cmd_ctx->domain->name);
623 :
624 0 : if (_rules != NULL) {
625 0 : *_rules = talloc_steal(mem_ctx, rules);
626 : }
627 :
628 0 : if (_num_rules != NULL) {
629 0 : *_num_rules = num_rules;
630 : }
631 :
632 0 : ret = EOK;
633 : done:
634 0 : talloc_free(tmp_ctx);
635 0 : return ret;
636 : }
637 :
638 : static errno_t
639 : sort_sudo_rules(struct sysdb_attrs **rules, size_t count, bool higher_wins);
640 :
641 0 : static errno_t sudosrv_get_sudorules_query_cache(TALLOC_CTX *mem_ctx,
642 : struct sss_domain_info *domain,
643 : const char **attrs,
644 : unsigned int flags,
645 : const char *username,
646 : uid_t uid,
647 : char **groupnames,
648 : bool inverse_order,
649 : struct sysdb_attrs ***_rules,
650 : uint32_t *_count)
651 : {
652 : TALLOC_CTX *tmp_ctx;
653 : char *filter;
654 : errno_t ret;
655 : size_t count;
656 : struct sysdb_attrs **rules;
657 : struct ldb_message **msgs;
658 :
659 0 : tmp_ctx = talloc_new(NULL);
660 0 : if (tmp_ctx == NULL) return ENOMEM;
661 :
662 0 : ret = sysdb_get_sudo_filter(tmp_ctx, username, uid, groupnames,
663 : flags, &filter);
664 0 : if (ret != EOK) {
665 0 : DEBUG(SSSDBG_CRIT_FAILURE,
666 : "Could not construct the search filter [%d]: %s\n",
667 : ret, strerror(ret));
668 0 : goto done;
669 : }
670 :
671 0 : DEBUG(SSSDBG_FUNC_DATA, "Searching sysdb with [%s]\n", filter);
672 :
673 0 : if (IS_SUBDOMAIN(domain)) {
674 : /* rules are stored inside parent domain tree */
675 0 : domain = domain->parent;
676 : }
677 :
678 0 : ret = sysdb_search_custom(tmp_ctx, domain, filter,
679 : SUDORULE_SUBDIR, attrs,
680 : &count, &msgs);
681 0 : if (ret != EOK && ret != ENOENT) {
682 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Error looking up SUDO rules\n");
683 0 : goto done;
684 0 : } if (ret == ENOENT) {
685 0 : *_rules = NULL;
686 0 : *_count = 0;
687 0 : ret = EOK;
688 0 : goto done;
689 : }
690 :
691 0 : ret = sysdb_msg2attrs(tmp_ctx, count, msgs, &rules);
692 0 : if (ret != EOK) {
693 0 : DEBUG(SSSDBG_CRIT_FAILURE,
694 : "Could not convert ldb message to sysdb_attrs\n");
695 0 : goto done;
696 : }
697 :
698 0 : ret = sort_sudo_rules(rules, count, inverse_order);
699 0 : if (ret != EOK) {
700 0 : DEBUG(SSSDBG_OP_FAILURE,
701 : "Could not sort rules by sudoOrder\n");
702 0 : goto done;
703 : }
704 :
705 0 : *_rules = talloc_steal(mem_ctx, rules);
706 0 : *_count = (uint32_t)count;
707 :
708 0 : ret = EOK;
709 : done:
710 0 : talloc_free(tmp_ctx);
711 0 : return ret;
712 : }
713 :
714 : static int
715 0 : sudo_order_cmp(const void *a, const void *b, bool lower_wins)
716 : {
717 : struct sysdb_attrs *r1, *r2;
718 : uint32_t o1, o2;
719 : int ret;
720 :
721 0 : r1 = * (struct sysdb_attrs * const *) a;
722 0 : r2 = * (struct sysdb_attrs * const *) b;
723 0 : if (!r1 || !r2) {
724 0 : DEBUG(SSSDBG_CRIT_FAILURE, "BUG: Wrong data?\n");
725 0 : return 0;
726 : }
727 :
728 0 : ret = sysdb_attrs_get_uint32_t(r1, SYSDB_SUDO_CACHE_AT_ORDER, &o1);
729 0 : if (ret == ENOENT) {
730 : /* man sudoers-ldap: If the sudoOrder attribute is not present,
731 : * a value of 0 is assumed */
732 0 : o1 = 0;
733 0 : } else if (ret != EOK) {
734 0 : DEBUG(SSSDBG_OP_FAILURE, "Cannot get sudoOrder value\n");
735 0 : return 0;
736 : }
737 :
738 0 : ret = sysdb_attrs_get_uint32_t(r2, SYSDB_SUDO_CACHE_AT_ORDER, &o2);
739 0 : if (ret == ENOENT) {
740 : /* man sudoers-ldap: If the sudoOrder attribute is not present,
741 : * a value of 0 is assumed */
742 0 : o2 = 0;
743 0 : } else if (ret != EOK) {
744 0 : DEBUG(SSSDBG_OP_FAILURE, "Cannot get sudoOrder value\n");
745 0 : return 0;
746 : }
747 :
748 0 : if (lower_wins) {
749 : /* The lowest value takes priority. Original wrong SSSD behaviour. */
750 0 : if (o1 > o2) {
751 0 : return 1;
752 0 : } else if (o1 < o2) {
753 0 : return -1;
754 : }
755 : } else {
756 : /* The higher value takes priority. Standard LDAP behaviour. */
757 0 : if (o1 < o2) {
758 0 : return 1;
759 0 : } else if (o1 > o2) {
760 0 : return -1;
761 : }
762 : }
763 :
764 0 : return 0;
765 : }
766 :
767 : static int
768 0 : sudo_order_low_cmp_fn(const void *a, const void *b)
769 : {
770 0 : return sudo_order_cmp(a, b, true);
771 : }
772 :
773 : static int
774 0 : sudo_order_high_cmp_fn(const void *a, const void *b)
775 : {
776 0 : return sudo_order_cmp(a, b, false);
777 : }
778 :
779 : static errno_t
780 0 : sort_sudo_rules(struct sysdb_attrs **rules, size_t count, bool lower_wins)
781 : {
782 0 : if (lower_wins) {
783 0 : DEBUG(SSSDBG_TRACE_FUNC, "Sorting rules with lower-wins logic\n");
784 0 : qsort(rules, count, sizeof(struct sysdb_attrs *),
785 : sudo_order_low_cmp_fn);
786 : } else {
787 0 : DEBUG(SSSDBG_TRACE_FUNC, "Sorting rules with higher-wins logic\n");
788 0 : qsort(rules, count, sizeof(struct sysdb_attrs *),
789 : sudo_order_high_cmp_fn);
790 : }
791 :
792 0 : return EOK;
793 : }
|