Line data Source code
1 : /*
2 : SSSD
3 :
4 : IPA Backend Module -- Access control
5 :
6 : Authors:
7 : Sumit Bose <sbose@redhat.com>
8 :
9 : Copyright (C) 2009 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 <sys/param.h>
26 : #include <security/pam_modules.h>
27 :
28 : #include "util/util.h"
29 : #include "providers/ldap/sdap_async.h"
30 : #include "providers/ldap/sdap_access.h"
31 : #include "providers/ipa/ipa_common.h"
32 : #include "providers/ipa/ipa_access.h"
33 : #include "providers/ipa/ipa_hbac.h"
34 : #include "providers/ipa/ipa_hosts.h"
35 : #include "providers/ipa/ipa_hbac_private.h"
36 : #include "providers/ipa/ipa_hbac_rules.h"
37 :
38 : /* External logging function for HBAC. */
39 0 : void hbac_debug_messages(const char *file, int line,
40 : enum hbac_debug_level level,
41 : const char *fmt, ...)
42 : {
43 : int loglevel;
44 :
45 0 : switch(level) {
46 : case HBAC_DBG_FATAL:
47 0 : loglevel = SSSDBG_FATAL_FAILURE;
48 0 : break;
49 : case HBAC_DBG_ERROR:
50 0 : loglevel = SSSDBG_OP_FAILURE;
51 0 : break;
52 : case HBAC_DBG_WARNING:
53 0 : loglevel = SSSDBG_MINOR_FAILURE;
54 0 : break;
55 : case HBAC_DBG_INFO:
56 0 : loglevel = SSSDBG_CONF_SETTINGS;
57 0 : break;
58 : case HBAC_DBG_TRACE:
59 0 : loglevel = SSSDBG_TRACE_INTERNAL;
60 0 : break;
61 : default:
62 0 : loglevel = SSSDBG_UNRESOLVED;
63 0 : break;
64 : }
65 :
66 0 : if (DEBUG_IS_SET(loglevel)) {
67 : va_list ap;
68 0 : char *message = NULL;
69 : int ret;
70 :
71 0 : va_start(ap, fmt);
72 0 : ret = vasprintf(&message, fmt, ap);
73 0 : va_end(ap);
74 0 : if (ret < 0) {
75 : /* ENOMEM */
76 0 : free(message);
77 0 : return;
78 : }
79 :
80 0 : debug_fn(__FILE__, __LINE__, "hbac", loglevel, "[%s:%i] %s",
81 : file, line, message);
82 0 : free(message);
83 : }
84 : }
85 :
86 0 : static void ipa_access_reply(struct hbac_ctx *hbac_ctx, int pam_status)
87 : {
88 0 : struct be_req *be_req = hbac_ctx->be_req;
89 : struct pam_data *pd;
90 0 : pd = talloc_get_type(be_req_get_data(be_req), struct pam_data);
91 0 : pd->pam_status = pam_status;
92 :
93 : /* destroy HBAC context now to release all used resources and LDAP connection */
94 0 : talloc_zfree(hbac_ctx);
95 :
96 0 : if (pam_status == PAM_SUCCESS || pam_status == PAM_PERM_DENIED) {
97 0 : be_req_terminate(be_req, DP_ERR_OK, pam_status, NULL);
98 : } else {
99 0 : be_req_terminate(be_req, DP_ERR_FATAL, pam_status, NULL);
100 : }
101 0 : }
102 :
103 : enum hbac_result {
104 : HBAC_ALLOW = 1,
105 : HBAC_DENY,
106 : HBAC_NOT_APPLICABLE
107 : };
108 :
109 : enum check_result {
110 : RULE_APPLICABLE = 0,
111 : RULE_NOT_APPLICABLE,
112 : RULE_ERROR
113 : };
114 :
115 : static void ipa_hbac_check(struct tevent_req *req);
116 : static int hbac_retry(struct hbac_ctx *hbac_ctx);
117 : static void hbac_connect_done(struct tevent_req *subreq);
118 : static bool hbac_check_step_result(struct hbac_ctx *hbac_ctx, int ret);
119 :
120 : static int hbac_get_host_info_step(struct hbac_ctx *hbac_ctx);
121 :
122 : static void ipa_hbac_evaluate_rules(struct hbac_ctx *hbac_ctx);
123 :
124 0 : void ipa_access_handler(struct be_req *be_req)
125 : {
126 : struct pam_data *pd;
127 : struct ipa_access_ctx *ipa_access_ctx;
128 : struct tevent_req *req;
129 : struct sss_domain_info *dom;
130 0 : struct be_ctx *be_ctx = be_req_get_be_ctx(be_req);
131 :
132 0 : pd = talloc_get_type(be_req_get_data(be_req), struct pam_data);
133 :
134 0 : ipa_access_ctx = talloc_get_type(be_ctx->bet_info[BET_ACCESS].pvt_bet_data,
135 : struct ipa_access_ctx);
136 :
137 0 : dom = be_ctx->domain;
138 0 : if (strcasecmp(pd->domain, be_ctx->domain->name) != 0) {
139 : /* Subdomain request, verify subdomain */
140 0 : dom = find_domain_by_name(be_ctx->domain, pd->domain, true);
141 : }
142 :
143 : /* First, verify that this account isn't locked.
144 : * We need to do this in case the auth phase was
145 : * skipped (such as during GSSAPI single-sign-on
146 : * or SSH public key exchange.
147 : */
148 0 : req = sdap_access_send(be_req, be_ctx->ev, be_ctx, dom,
149 : ipa_access_ctx->sdap_access_ctx,
150 0 : ipa_access_ctx->sdap_access_ctx->id_ctx->conn,
151 : pd);
152 0 : if (!req) {
153 0 : be_req_terminate(be_req, DP_ERR_FATAL, PAM_SYSTEM_ERR, NULL);
154 0 : return;
155 : }
156 0 : tevent_req_set_callback(req, ipa_hbac_check, be_req);
157 : }
158 :
159 0 : static void ipa_hbac_check(struct tevent_req *req)
160 : {
161 : struct be_req *be_req;
162 : struct be_ctx *be_ctx;
163 : struct pam_data *pd;
164 0 : struct hbac_ctx *hbac_ctx = NULL;
165 : struct ipa_access_ctx *ipa_access_ctx;
166 : int ret;
167 :
168 0 : be_req = tevent_req_callback_data(req, struct be_req);
169 0 : be_ctx = be_req_get_be_ctx(be_req);
170 0 : pd = talloc_get_type(be_req_get_data(be_req), struct pam_data);
171 :
172 0 : ret = sdap_access_recv(req);
173 0 : talloc_zfree(req);
174 :
175 0 : switch(ret) {
176 : case EOK:
177 : /* Account wasn't locked. Continue below
178 : * to HBAC processing.
179 : */
180 0 : break;
181 : case ERR_ACCESS_DENIED:
182 : /* Account was locked. Return permission denied
183 : * here.
184 : */
185 0 : pd->pam_status = PAM_PERM_DENIED;
186 0 : be_req_terminate(be_req, DP_ERR_OK, pd->pam_status, NULL);
187 0 : return;
188 : case ERR_ACCOUNT_EXPIRED:
189 0 : pd->pam_status = PAM_ACCT_EXPIRED;
190 0 : be_req_terminate(be_req, DP_ERR_OK, pd->pam_status, NULL);
191 0 : return;
192 : default:
193 : /* We got an unexpected error. Return it as-is */
194 0 : pd->pam_status = PAM_SYSTEM_ERR;
195 0 : be_req_terminate(be_req, DP_ERR_FATAL, pd->pam_status,
196 : sss_strerror(ret));
197 0 : return;
198 : }
199 :
200 0 : hbac_ctx = talloc_zero(be_req, struct hbac_ctx);
201 0 : if (hbac_ctx == NULL) {
202 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc failed.\n");
203 0 : ret = ENOMEM;
204 0 : goto fail;
205 : }
206 :
207 0 : hbac_ctx->be_req = be_req;
208 0 : hbac_ctx->pd = pd;
209 0 : ipa_access_ctx = talloc_get_type(be_ctx->bet_info[BET_ACCESS].pvt_bet_data,
210 : struct ipa_access_ctx);
211 0 : hbac_ctx->access_ctx = ipa_access_ctx;
212 0 : hbac_ctx->sdap_ctx = ipa_access_ctx->sdap_ctx;
213 0 : hbac_ctx->ipa_options = ipa_access_ctx->ipa_options;
214 0 : hbac_ctx->tr_ctx = ipa_access_ctx->tr_ctx;
215 0 : hbac_ctx->search_bases = ipa_access_ctx->hbac_search_bases;
216 0 : if (hbac_ctx->search_bases == NULL) {
217 0 : DEBUG(SSSDBG_CRIT_FAILURE, "No HBAC search base found.\n");
218 0 : ret = EINVAL;
219 0 : goto fail;
220 : }
221 :
222 0 : ret = hbac_retry(hbac_ctx);
223 0 : if (ret != EOK) {
224 0 : goto fail;
225 : }
226 :
227 0 : return;
228 :
229 : fail:
230 0 : if (hbac_ctx) {
231 : /* Return an proper error */
232 0 : ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR);
233 : } else {
234 0 : be_req_terminate(be_req, DP_ERR_FATAL, PAM_SYSTEM_ERR, NULL);
235 : }
236 : }
237 :
238 0 : static int hbac_retry(struct hbac_ctx *hbac_ctx)
239 : {
240 : struct tevent_req *subreq;
241 : int ret;
242 : bool offline;
243 : time_t now, refresh_interval;
244 0 : struct ipa_access_ctx *access_ctx = hbac_ctx->access_ctx;
245 0 : struct be_ctx *be_ctx = be_req_get_be_ctx(hbac_ctx->be_req);
246 :
247 0 : offline = be_is_offline(be_ctx);
248 0 : DEBUG(SSSDBG_TRACE_ALL,
249 : "Connection status is [%s].\n", offline ? "offline" : "online");
250 :
251 0 : refresh_interval = dp_opt_get_int(hbac_ctx->ipa_options,
252 : IPA_HBAC_REFRESH);
253 :
254 0 : now = time(NULL);
255 0 : if (now < access_ctx->last_update + refresh_interval) {
256 : /* Simulate offline mode and just go to the cache */
257 0 : DEBUG(SSSDBG_TRACE_FUNC, "Performing cached HBAC evaluation\n");
258 0 : offline = true;
259 : }
260 :
261 0 : if (!offline) {
262 0 : if (hbac_ctx->sdap_op == NULL) {
263 0 : hbac_ctx->sdap_op = sdap_id_op_create(hbac_ctx,
264 0 : hbac_ctx->sdap_ctx->conn->conn_cache);
265 0 : if (hbac_ctx->sdap_op == NULL) {
266 0 : DEBUG(SSSDBG_CRIT_FAILURE, "sdap_id_op_create failed.\n");
267 0 : return EIO;
268 : }
269 : }
270 :
271 0 : subreq = sdap_id_op_connect_send(hbac_ctx->sdap_op, hbac_ctx, &ret);
272 0 : if (!subreq) {
273 0 : DEBUG(SSSDBG_CRIT_FAILURE,
274 : "sdap_id_op_connect_send failed: %d(%s).\n", ret, strerror(ret));
275 0 : talloc_zfree(hbac_ctx->sdap_op);
276 0 : return ret;
277 : }
278 :
279 0 : tevent_req_set_callback(subreq, hbac_connect_done, hbac_ctx);
280 : } else {
281 : /* Evaluate the rules based on what we have in the
282 : * sysdb
283 : */
284 0 : ipa_hbac_evaluate_rules(hbac_ctx);
285 0 : return EOK;
286 : }
287 0 : return EOK;
288 : }
289 :
290 0 : static void hbac_connect_done(struct tevent_req *subreq)
291 : {
292 0 : struct hbac_ctx *hbac_ctx = tevent_req_callback_data(subreq, struct hbac_ctx);
293 : int ret, dp_error;
294 :
295 0 : ret = sdap_id_op_connect_recv(subreq, &dp_error);
296 0 : talloc_zfree(subreq);
297 :
298 0 : if (dp_error == DP_ERR_OFFLINE) {
299 : /* switching to offline mode */
300 0 : talloc_zfree(hbac_ctx->sdap_op);
301 :
302 0 : ipa_hbac_evaluate_rules(hbac_ctx);
303 0 : return;
304 0 : } else if (ret != EOK) {
305 0 : goto fail;
306 : }
307 :
308 0 : ret = hbac_get_host_info_step(hbac_ctx);
309 0 : if (ret != EOK) {
310 0 : goto fail;
311 : }
312 :
313 0 : return;
314 :
315 : fail:
316 0 : ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR);
317 : }
318 :
319 0 : static void hbac_clear_rule_data(struct hbac_ctx *hbac_ctx)
320 : {
321 0 : hbac_ctx->host_count = 0;
322 0 : talloc_zfree(hbac_ctx->hosts);
323 :
324 0 : hbac_ctx->hostgroup_count = 0;
325 0 : talloc_zfree(hbac_ctx->hostgroups);
326 :
327 0 : hbac_ctx->service_count = 0;
328 0 : talloc_zfree(hbac_ctx->services);
329 :
330 0 : hbac_ctx->servicegroup_count = 0;
331 0 : talloc_zfree(hbac_ctx->servicegroups);
332 :
333 0 : hbac_ctx->rule_count = 0;
334 0 : talloc_zfree(hbac_ctx->rules);
335 0 : }
336 :
337 : /* Check whether the current HBAC request is processed in off-line mode */
338 0 : static inline bool hbac_ctx_is_offline(struct hbac_ctx *ctx)
339 : {
340 0 : return ctx == NULL || ctx->sdap_op == NULL;
341 : }
342 :
343 : /* Check the step result code and continue, retry, get offline result or abort accordingly */
344 0 : static bool hbac_check_step_result(struct hbac_ctx *hbac_ctx, int ret)
345 : {
346 : int dp_error;
347 :
348 0 : if (ret == EOK) {
349 0 : return true;
350 : }
351 :
352 0 : if (hbac_ctx_is_offline(hbac_ctx)) {
353 : /* already offline => the error is fatal */
354 0 : ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR);
355 0 : return false;
356 : }
357 :
358 0 : ret = sdap_id_op_done(hbac_ctx->sdap_op, ret, &dp_error);
359 0 : if (ret != EOK) {
360 0 : if (dp_error == DP_ERR_OFFLINE) {
361 : /* switching to offline mode */
362 0 : talloc_zfree(hbac_ctx->sdap_op);
363 :
364 : /* Free any of the results we've gotten */
365 0 : hbac_clear_rule_data(hbac_ctx);
366 :
367 0 : dp_error = DP_ERR_OK;
368 : }
369 :
370 0 : if (dp_error == DP_ERR_OK) {
371 : /* retry */
372 0 : ret = hbac_retry(hbac_ctx);
373 0 : if (ret == EOK) {
374 0 : return false;
375 : }
376 : }
377 : }
378 :
379 0 : ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR);
380 0 : return false;
381 : }
382 :
383 : static void hbac_get_service_info_step(struct tevent_req *req);
384 : static void hbac_get_rule_info_step(struct tevent_req *req);
385 : static void hbac_sysdb_save (struct tevent_req *req);
386 :
387 0 : static int hbac_get_host_info_step(struct hbac_ctx *hbac_ctx)
388 : {
389 0 : struct be_ctx *be_ctx = be_req_get_be_ctx(hbac_ctx->be_req);
390 : const char *hostname;
391 : struct tevent_req *req;
392 :
393 0 : if (dp_opt_get_bool(hbac_ctx->ipa_options, IPA_HBAC_SUPPORT_SRCHOST)) {
394 : /* Support srchost
395 : * -> we don't want any particular host,
396 : * we want all hosts
397 : */
398 0 : hostname = NULL;
399 :
400 : /* THIS FEATURE IS DEPRECATED */
401 0 : DEBUG(SSSDBG_MINOR_FAILURE, "WARNING: Using deprecated option "
402 : "ipa_hbac_support_srchost.\n");
403 0 : sss_log(SSS_LOG_NOTICE, "WARNING: Using deprecated option "
404 : "ipa_hbac_support_srchost.\n");
405 : } else {
406 0 : hostname = dp_opt_get_string(hbac_ctx->ipa_options, IPA_HOSTNAME);
407 : }
408 :
409 0 : req = ipa_host_info_send(hbac_ctx, be_ctx->ev,
410 : sdap_id_op_handle(hbac_ctx->sdap_op),
411 0 : hbac_ctx->sdap_ctx->opts,
412 : hostname,
413 0 : hbac_ctx->access_ctx->host_map,
414 0 : hbac_ctx->access_ctx->hostgroup_map,
415 0 : hbac_ctx->access_ctx->host_search_bases);
416 0 : if (req == NULL) {
417 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not get host info\n");
418 0 : return ENOMEM;
419 : }
420 0 : tevent_req_set_callback(req, hbac_get_service_info_step, hbac_ctx);
421 :
422 0 : return EOK;
423 : }
424 :
425 0 : static void hbac_get_service_info_step(struct tevent_req *req)
426 : {
427 : errno_t ret;
428 0 : struct hbac_ctx *hbac_ctx =
429 0 : tevent_req_callback_data(req, struct hbac_ctx);
430 0 : struct be_ctx *be_ctx = be_req_get_be_ctx(hbac_ctx->be_req);
431 :
432 0 : ret = ipa_host_info_recv(req, hbac_ctx,
433 : &hbac_ctx->host_count,
434 : &hbac_ctx->hosts,
435 : &hbac_ctx->hostgroup_count,
436 : &hbac_ctx->hostgroups);
437 0 : talloc_zfree(req);
438 0 : if (!hbac_check_step_result(hbac_ctx, ret)) {
439 0 : return;
440 : }
441 :
442 : /* Get services and service groups */
443 0 : req = ipa_hbac_service_info_send(hbac_ctx, be_ctx->ev,
444 : sdap_id_op_handle(hbac_ctx->sdap_op),
445 0 : hbac_ctx->sdap_ctx->opts,
446 : hbac_ctx->search_bases);
447 0 : if (req == NULL) {
448 0 : DEBUG(SSSDBG_CRIT_FAILURE,"Could not get service info\n");
449 0 : goto fail;
450 : }
451 0 : tevent_req_set_callback(req, hbac_get_rule_info_step, hbac_ctx);
452 0 : return;
453 :
454 : fail:
455 0 : ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR);
456 : }
457 :
458 0 : static void hbac_get_rule_info_step(struct tevent_req *req)
459 : {
460 : errno_t ret;
461 : size_t i;
462 : const char *ipa_hostname;
463 : const char *hostname;
464 0 : struct hbac_ctx *hbac_ctx =
465 0 : tevent_req_callback_data(req, struct hbac_ctx);
466 0 : struct be_ctx *be_ctx = be_req_get_be_ctx(hbac_ctx->be_req);
467 :
468 0 : ret = ipa_hbac_service_info_recv(req, hbac_ctx,
469 : &hbac_ctx->service_count,
470 : &hbac_ctx->services,
471 : &hbac_ctx->servicegroup_count,
472 : &hbac_ctx->servicegroups);
473 0 : talloc_zfree(req);
474 0 : if (!hbac_check_step_result(hbac_ctx, ret)) {
475 0 : return;
476 : }
477 :
478 : /* Get the ipa_host attrs */
479 0 : hbac_ctx->ipa_host = NULL;
480 0 : ipa_hostname = dp_opt_get_cstring(hbac_ctx->ipa_options, IPA_HOSTNAME);
481 0 : if (ipa_hostname == NULL) {
482 0 : DEBUG(SSSDBG_CRIT_FAILURE,
483 : "Missing ipa_hostname, this should never happen.\n");
484 0 : goto fail;
485 : }
486 :
487 0 : for (i = 0; i < hbac_ctx->host_count; i++) {
488 0 : ret = sysdb_attrs_get_string(hbac_ctx->hosts[i],
489 : SYSDB_FQDN,
490 : &hostname);
491 0 : if (ret != EOK) {
492 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not locate IPA host\n");
493 0 : goto fail;
494 : }
495 :
496 0 : if (strcasecmp(hostname, ipa_hostname) == 0) {
497 0 : hbac_ctx->ipa_host = hbac_ctx->hosts[i];
498 0 : break;
499 : }
500 : }
501 0 : if (hbac_ctx->ipa_host == NULL) {
502 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not locate IPA host\n");
503 0 : goto fail;
504 : }
505 :
506 :
507 : /* Get the list of applicable rules */
508 0 : req = ipa_hbac_rule_info_send(hbac_ctx,
509 : be_ctx->ev,
510 : sdap_id_op_handle(hbac_ctx->sdap_op),
511 0 : hbac_ctx->sdap_ctx->opts,
512 : hbac_ctx->search_bases,
513 : hbac_ctx->ipa_host);
514 0 : if (req == NULL) {
515 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not get rules\n");
516 0 : goto fail;
517 : }
518 :
519 0 : tevent_req_set_callback(req, hbac_sysdb_save, hbac_ctx);
520 0 : return;
521 :
522 : fail:
523 0 : ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR);
524 : }
525 :
526 0 : static void hbac_sysdb_save(struct tevent_req *req)
527 : {
528 : errno_t ret;
529 0 : bool in_transaction = false;
530 0 : struct hbac_ctx *hbac_ctx =
531 0 : tevent_req_callback_data(req, struct hbac_ctx);
532 0 : struct be_ctx *be_ctx = be_req_get_be_ctx(hbac_ctx->be_req);
533 0 : struct sss_domain_info *domain = be_ctx->domain;
534 : struct ldb_dn *base_dn;
535 0 : struct ipa_access_ctx *access_ctx =
536 0 : talloc_get_type(be_ctx->bet_info[BET_ACCESS].pvt_bet_data,
537 : struct ipa_access_ctx);
538 : TALLOC_CTX *tmp_ctx;
539 :
540 0 : ret = ipa_hbac_rule_info_recv(req, hbac_ctx,
541 : &hbac_ctx->rule_count,
542 : &hbac_ctx->rules);
543 0 : talloc_zfree(req);
544 0 : if (ret == ENOENT) {
545 : /* No rules were found that apply to this
546 : * host.
547 : */
548 :
549 0 : tmp_ctx = talloc_new(NULL);
550 0 : if (tmp_ctx == NULL) {
551 0 : ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR);
552 0 : return;
553 : }
554 : /* Delete any rules in the sysdb so offline logins
555 : * are also denied.
556 : */
557 0 : base_dn = sysdb_custom_subtree_dn(tmp_ctx, domain, HBAC_RULES_SUBDIR);
558 0 : if (base_dn == NULL) {
559 0 : talloc_free(tmp_ctx);
560 0 : ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR);
561 0 : return;
562 : }
563 :
564 0 : ret = sysdb_delete_recursive(domain->sysdb, base_dn, true);
565 0 : talloc_free(tmp_ctx);
566 0 : if (ret != EOK) {
567 0 : DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_delete_recursive failed.\n");
568 0 : ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR);
569 0 : return;
570 : }
571 :
572 : /* If no rules are found, we default to DENY */
573 0 : ipa_access_reply(hbac_ctx, PAM_PERM_DENIED);
574 0 : return;
575 : }
576 :
577 0 : if (!hbac_check_step_result(hbac_ctx, ret)) {
578 0 : return;
579 : }
580 :
581 0 : ret = sysdb_transaction_start(domain->sysdb);
582 0 : if (ret != EOK) {
583 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Could not start transaction\n");
584 0 : goto fail;
585 : }
586 0 : in_transaction = true;
587 :
588 : /* Save the hosts */
589 0 : ret = ipa_hbac_sysdb_save(domain,
590 : HBAC_HOSTS_SUBDIR, SYSDB_FQDN,
591 : hbac_ctx->host_count, hbac_ctx->hosts,
592 : HBAC_HOSTGROUPS_SUBDIR, SYSDB_NAME,
593 : hbac_ctx->hostgroup_count,
594 : hbac_ctx->hostgroups);
595 0 : if (ret != EOK) {
596 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Error saving hosts: [%d][%s]\n",
597 : ret, strerror(ret));
598 0 : goto fail;
599 : }
600 :
601 : /* Save the services */
602 0 : ret = ipa_hbac_sysdb_save(domain,
603 : HBAC_SERVICES_SUBDIR, IPA_CN,
604 : hbac_ctx->service_count, hbac_ctx->services,
605 : HBAC_SERVICEGROUPS_SUBDIR, IPA_CN,
606 : hbac_ctx->servicegroup_count,
607 : hbac_ctx->servicegroups);
608 0 : if (ret != EOK) {
609 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Error saving services: [%d][%s]\n",
610 : ret, strerror(ret));
611 0 : goto fail;
612 : }
613 : /* Save the rules */
614 0 : ret = ipa_hbac_sysdb_save(domain,
615 : HBAC_RULES_SUBDIR, IPA_UNIQUE_ID,
616 : hbac_ctx->rule_count,
617 : hbac_ctx->rules,
618 : NULL, NULL, 0, NULL);
619 0 : if (ret != EOK) {
620 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Error saving rules: [%d][%s]\n",
621 : ret, strerror(ret));
622 0 : goto fail;
623 : }
624 :
625 0 : ret = sysdb_transaction_commit(domain->sysdb);
626 0 : if (ret != EOK) {
627 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
628 0 : goto fail;
629 : }
630 0 : in_transaction = false;
631 :
632 : /* We don't need the rule data any longer,
633 : * the rest of the processing relies on
634 : * sysdb lookups.
635 : */
636 0 : hbac_clear_rule_data(hbac_ctx);
637 :
638 :
639 0 : access_ctx->last_update = time(NULL);
640 :
641 : /* Now evaluate the request against the rules */
642 0 : ipa_hbac_evaluate_rules(hbac_ctx);
643 :
644 0 : return;
645 :
646 : fail:
647 0 : if (in_transaction) {
648 0 : ret = sysdb_transaction_cancel(domain->sysdb);
649 0 : if (ret != EOK) {
650 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Could not cancel transaction\n");
651 : }
652 : }
653 0 : ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR);
654 : }
655 :
656 0 : void ipa_hbac_evaluate_rules(struct hbac_ctx *hbac_ctx)
657 : {
658 0 : struct be_ctx *be_ctx = be_req_get_be_ctx(hbac_ctx->be_req);
659 : errno_t ret;
660 : struct hbac_rule **hbac_rules;
661 : struct hbac_eval_req *eval_req;
662 : enum hbac_eval_result result;
663 : struct hbac_info *info;
664 :
665 : /* Get HBAC rules from the sysdb */
666 0 : ret = hbac_get_cached_rules(hbac_ctx, be_ctx->domain,
667 : &hbac_ctx->rule_count, &hbac_ctx->rules);
668 0 : if (ret != EOK) {
669 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not retrieve rules from the cache\n");
670 0 : ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR);
671 : }
672 :
673 0 : ret = hbac_ctx_to_rules(hbac_ctx, hbac_ctx,
674 : &hbac_rules, &eval_req);
675 0 : if (ret == EPERM) {
676 0 : DEBUG(SSSDBG_CRIT_FAILURE,
677 : "DENY rules detected. Denying access to all users\n");
678 0 : ipa_access_reply(hbac_ctx, PAM_PERM_DENIED);
679 0 : return;
680 0 : } else if (ret != EOK) {
681 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not construct HBAC rules\n");
682 0 : ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR);
683 0 : return;
684 : }
685 :
686 0 : hbac_enable_debug(hbac_debug_messages);
687 :
688 0 : result = hbac_evaluate(hbac_rules, eval_req, &info);
689 0 : if (result == HBAC_EVAL_ALLOW) {
690 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Access granted by HBAC rule [%s]\n",
691 : info->rule_name);
692 0 : hbac_free_info(info);
693 0 : ipa_access_reply(hbac_ctx, PAM_SUCCESS);
694 0 : return;
695 0 : } else if (result == HBAC_EVAL_ERROR) {
696 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Error [%s] occurred in rule [%s]\n",
697 : hbac_error_string(info->code),
698 : info->rule_name);
699 0 : hbac_free_info(info);
700 0 : ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR);
701 0 : return;
702 0 : } else if (result == HBAC_EVAL_OOM) {
703 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Insufficient memory\n");
704 0 : ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR);
705 0 : return;
706 : }
707 :
708 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Access denied by HBAC rules\n");
709 0 : hbac_free_info(info);
710 0 : ipa_access_reply(hbac_ctx, PAM_PERM_DENIED);
711 : }
712 :
713 0 : errno_t hbac_get_cached_rules(TALLOC_CTX *mem_ctx,
714 : struct sss_domain_info *domain,
715 : size_t *_rule_count,
716 : struct sysdb_attrs ***_rules)
717 : {
718 : errno_t ret;
719 : struct ldb_message **msgs;
720 : struct sysdb_attrs **rules;
721 : size_t rule_count;
722 : TALLOC_CTX *tmp_ctx;
723 : char *filter;
724 0 : const char *attrs[] = { OBJECTCLASS,
725 : IPA_CN,
726 : SYSDB_ORIG_DN,
727 : IPA_UNIQUE_ID,
728 : IPA_ENABLED_FLAG,
729 : IPA_ACCESS_RULE_TYPE,
730 : IPA_MEMBER_USER,
731 : IPA_USER_CATEGORY,
732 : IPA_MEMBER_SERVICE,
733 : IPA_SERVICE_CATEGORY,
734 : IPA_SOURCE_HOST,
735 : IPA_SOURCE_HOST_CATEGORY,
736 : IPA_EXTERNAL_HOST,
737 : IPA_MEMBER_HOST,
738 : IPA_HOST_CATEGORY,
739 : NULL };
740 :
741 0 : tmp_ctx = talloc_new(NULL);
742 0 : if (tmp_ctx == NULL) return ENOMEM;
743 :
744 0 : filter = talloc_asprintf(tmp_ctx, "(objectClass=%s)", IPA_HBAC_RULE);
745 0 : if (filter == NULL) {
746 0 : ret = ENOMEM;
747 0 : goto done;
748 : }
749 :
750 0 : ret = sysdb_search_custom(tmp_ctx, domain, filter,
751 : HBAC_RULES_SUBDIR, attrs,
752 : &rule_count, &msgs);
753 0 : if (ret != EOK && ret != ENOENT) {
754 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Error looking up HBAC rules\n");
755 0 : goto done;
756 0 : } if (ret == ENOENT) {
757 0 : rule_count = 0;
758 : }
759 :
760 0 : ret = sysdb_msg2attrs(tmp_ctx, rule_count, msgs, &rules);
761 0 : if (ret != EOK) {
762 0 : DEBUG(SSSDBG_CRIT_FAILURE,
763 : "Could not convert ldb message to sysdb_attrs\n");
764 0 : goto done;
765 : }
766 :
767 0 : if (_rules) *_rules = talloc_steal(mem_ctx, rules);
768 0 : if (_rule_count) *_rule_count = rule_count;
769 :
770 0 : ret = EOK;
771 : done:
772 0 : talloc_free(tmp_ctx);
773 0 : return ret;
774 : }
|