Line data Source code
1 : /*
2 : SSSD
3 :
4 : IPA Backend Module -- Access control
5 :
6 : Authors:
7 : Sumit Bose <sbose@redhat.com>
8 : Stephen Gallagher <sgallagh@redhat.com>
9 :
10 : Copyright (C) 2011 Red Hat
11 :
12 : This program is free software; you can redistribute it and/or modify
13 : it under the terms of the GNU General Public License as published by
14 : the Free Software Foundation; either version 3 of the License, or
15 : (at your option) any later version.
16 :
17 : This program is distributed in the hope that it will be useful,
18 : but WITHOUT ANY WARRANTY; without even the implied warranty of
19 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 : GNU General Public License for more details.
21 :
22 : You should have received a copy of the GNU General Public License
23 : along with this program. If not, see <http://www.gnu.org/licenses/>.
24 : */
25 :
26 : #include <stdlib.h>
27 : #include <string.h>
28 : #include <errno.h>
29 : #include "providers/ipa/ipa_hbac.h"
30 : #include "util/sss_utf8.h"
31 :
32 : #ifndef HAVE_ERRNO_T
33 : #define HAVE_ERRNO_T
34 : typedef int errno_t;
35 : #endif
36 :
37 : #ifndef EOK
38 : #define EOK 0
39 : #endif
40 :
41 : /* HBAC logging system */
42 :
43 : /* debug macro */
44 : #define HBAC_DEBUG(level, format, ...) do { \
45 : if (hbac_debug_fn != NULL) { \
46 : hbac_debug_fn(__FILE__, __LINE__, level, format, ##__VA_ARGS__); \
47 : } \
48 : } while (0)
49 :
50 : /* static pointer to external logging function */
51 : static hbac_debug_fn_t hbac_debug_fn = NULL;
52 :
53 : /* setup function for external logging function */
54 0 : void hbac_enable_debug(hbac_debug_fn_t external_debug_fn)
55 : {
56 0 : hbac_debug_fn = external_debug_fn;
57 0 : }
58 :
59 : /* auxiliary function for hbac_request_element logging */
60 : static void hbac_request_element_debug_print(struct hbac_request_element *el,
61 : const char *label);
62 :
63 : /* auxiliary function for hbac_eval_req logging */
64 : static void hbac_req_debug_print(struct hbac_eval_req *req);
65 :
66 : /* auxiliary function for hbac_rule_element logging */
67 : static void hbac_rule_element_debug_print(struct hbac_rule_element *el,
68 : const char *label);
69 :
70 : /* auxiliary function for hbac_rule logging */
71 : static void hbac_rule_debug_print(struct hbac_rule *rule);
72 :
73 :
74 : /* Placeholder structure for future HBAC time-based
75 : * evaluation rules
76 : */
77 : struct hbac_time_rules {
78 : int not_yet_implemented;
79 : };
80 :
81 : enum hbac_eval_result_int {
82 : HBAC_EVAL_MATCH_ERROR = -1,
83 : HBAC_EVAL_MATCHED,
84 : HBAC_EVAL_UNMATCHED
85 : };
86 :
87 88 : static bool hbac_rule_element_is_complete(struct hbac_rule_element *el)
88 : {
89 88 : if (el == NULL) return false;
90 84 : if (el->category == HBAC_CATEGORY_ALL) return true;
91 :
92 42 : if (el->names == NULL && el->groups == NULL) return false;
93 :
94 42 : if ((el->names && el->names[0] != NULL)
95 18 : || (el->groups && el->groups[0] != NULL))
96 30 : return true;
97 :
98 : /* If other categories are added, handle them here */
99 :
100 12 : return false;
101 : }
102 :
103 22 : bool hbac_rule_is_complete(struct hbac_rule *rule, uint32_t *missing_attrs)
104 : {
105 22 : bool complete = true;
106 :
107 22 : *missing_attrs = 0;
108 :
109 22 : if (rule == NULL) {
110 : /* No rule passed in? */
111 0 : return false;
112 : }
113 :
114 : /* Make sure we have all elements */
115 22 : if (!hbac_rule_element_is_complete(rule->users)) {
116 3 : complete = false;
117 3 : *missing_attrs |= HBAC_RULE_ELEMENT_USERS;
118 : }
119 :
120 22 : if (!hbac_rule_element_is_complete(rule->services)) {
121 3 : complete = false;
122 3 : *missing_attrs |= HBAC_RULE_ELEMENT_SERVICES;
123 : }
124 :
125 22 : if (!hbac_rule_element_is_complete(rule->targethosts)) {
126 5 : complete = false;
127 5 : *missing_attrs |= HBAC_RULE_ELEMENT_TARGETHOSTS;
128 : }
129 :
130 22 : if (!hbac_rule_element_is_complete(rule->srchosts)) {
131 5 : complete = false;
132 5 : *missing_attrs |= HBAC_RULE_ELEMENT_SOURCEHOSTS;
133 : }
134 :
135 22 : return complete;
136 : }
137 :
138 : enum hbac_eval_result_int hbac_evaluate_rule(struct hbac_rule *rule,
139 : struct hbac_eval_req *hbac_req,
140 : enum hbac_error_code *error);
141 :
142 22 : enum hbac_eval_result hbac_evaluate(struct hbac_rule **rules,
143 : struct hbac_eval_req *hbac_req,
144 : struct hbac_info **info)
145 : {
146 : enum hbac_error_code ret;
147 22 : enum hbac_eval_result result = HBAC_EVAL_DENY;
148 : enum hbac_eval_result_int intermediate_result;
149 :
150 22 : HBAC_DEBUG(HBAC_DBG_INFO, "[< hbac_evaluate()\n");
151 22 : hbac_req_debug_print(hbac_req);
152 :
153 22 : if (info) {
154 22 : *info = malloc(sizeof(struct hbac_info));
155 22 : if (!*info) {
156 0 : HBAC_DEBUG(HBAC_DBG_ERROR, "Out of memory.\n");
157 0 : return HBAC_EVAL_OOM;
158 : }
159 22 : (*info)->code = HBAC_ERROR_UNKNOWN;
160 22 : (*info)->rule_name = NULL;
161 : }
162 :
163 64 : for (uint32_t i = 0; rules[i]; i++) {
164 22 : hbac_rule_debug_print(rules[i]);
165 22 : intermediate_result = hbac_evaluate_rule(rules[i], hbac_req, &ret);
166 22 : if (intermediate_result == HBAC_EVAL_UNMATCHED) {
167 : /* This rule did not match at all. Skip it */
168 10 : HBAC_DEBUG(HBAC_DBG_INFO, "The rule [%s] did not match.\n",
169 : rules[i]->name);
170 10 : continue;
171 12 : } else if (intermediate_result == HBAC_EVAL_MATCHED) {
172 12 : HBAC_DEBUG(HBAC_DBG_INFO, "ALLOWED by rule [%s].\n", rules[i]->name);
173 12 : result = HBAC_EVAL_ALLOW;
174 12 : if (info) {
175 12 : (*info)->code = HBAC_SUCCESS;
176 12 : (*info)->rule_name = strdup(rules[i]->name);
177 12 : if (!(*info)->rule_name) {
178 0 : HBAC_DEBUG(HBAC_DBG_ERROR, "Out of memory.\n");
179 0 : result = HBAC_EVAL_ERROR;
180 0 : (*info)->code = HBAC_ERROR_OUT_OF_MEMORY;
181 : }
182 : }
183 12 : break;
184 : } else {
185 : /* An error occurred processing this rule */
186 0 : HBAC_DEBUG(HBAC_DBG_ERROR,
187 : "Error occurred during evaluating of rule [%s].\n",
188 : rules[i]->name);
189 0 : result = HBAC_EVAL_ERROR;
190 0 : if (info) {
191 0 : (*info)->code = ret;
192 0 : (*info)->rule_name = strdup(rules[i]->name);
193 : }
194 : /* Explicitly not checking the result of strdup(), since if
195 : * it's NULL, we can't do anything anyway.
196 : */
197 0 : goto done;
198 : }
199 : }
200 :
201 : /* If we've reached the end of the loop, we have either set the
202 : * result to ALLOW explicitly or we'll stick with the default DENY.
203 : */
204 : done:
205 :
206 22 : HBAC_DEBUG(HBAC_DBG_INFO, "hbac_evaluate() >]\n");
207 22 : return result;
208 : }
209 :
210 : static errno_t hbac_evaluate_element(struct hbac_rule_element *rule_el,
211 : struct hbac_request_element *req_el,
212 : bool *matched);
213 :
214 22 : enum hbac_eval_result_int hbac_evaluate_rule(struct hbac_rule *rule,
215 : struct hbac_eval_req *hbac_req,
216 : enum hbac_error_code *error)
217 : {
218 : errno_t ret;
219 : bool matched;
220 :
221 22 : if (!rule->enabled) return HBAC_EVAL_UNMATCHED;
222 :
223 : /* Make sure we have all elements */
224 22 : if (!rule->users
225 22 : || !rule->services
226 22 : || !rule->targethosts
227 22 : || !rule->srchosts) {
228 0 : *error = HBAC_ERROR_UNPARSEABLE_RULE;
229 0 : return HBAC_EVAL_MATCH_ERROR;
230 : }
231 :
232 : /* Check users */
233 22 : ret = hbac_evaluate_element(rule->users,
234 : hbac_req->user,
235 : &matched);
236 22 : if (ret != EOK) {
237 0 : *error = HBAC_ERROR_UNPARSEABLE_RULE;
238 0 : return HBAC_EVAL_MATCH_ERROR;
239 22 : } else if (!matched) {
240 6 : return HBAC_EVAL_UNMATCHED;
241 : }
242 :
243 : /* Check services */
244 16 : ret = hbac_evaluate_element(rule->services,
245 : hbac_req->service,
246 : &matched);
247 16 : if (ret != EOK) {
248 0 : *error = HBAC_ERROR_UNPARSEABLE_RULE;
249 0 : return HBAC_EVAL_MATCH_ERROR;
250 16 : } else if (!matched) {
251 2 : return HBAC_EVAL_UNMATCHED;
252 : }
253 :
254 : /* Check target hosts */
255 14 : ret = hbac_evaluate_element(rule->targethosts,
256 : hbac_req->targethost,
257 : &matched);
258 14 : if (ret != EOK) {
259 0 : *error = HBAC_ERROR_UNPARSEABLE_RULE;
260 0 : return HBAC_EVAL_MATCH_ERROR;
261 14 : } else if (!matched) {
262 0 : return HBAC_EVAL_UNMATCHED;
263 : }
264 :
265 : /* Check source hosts */
266 14 : ret = hbac_evaluate_element(rule->srchosts,
267 : hbac_req->srchost,
268 : &matched);
269 14 : if (ret != EOK) {
270 0 : *error = HBAC_ERROR_UNPARSEABLE_RULE;
271 0 : return HBAC_EVAL_MATCH_ERROR;
272 14 : } else if (!matched) {
273 2 : return HBAC_EVAL_UNMATCHED;
274 : }
275 12 : return HBAC_EVAL_MATCHED;
276 : }
277 :
278 66 : static errno_t hbac_evaluate_element(struct hbac_rule_element *rule_el,
279 : struct hbac_request_element *req_el,
280 : bool *matched)
281 : {
282 : size_t i, j;
283 : const uint8_t *rule_name;
284 : const uint8_t *req_name;
285 : int ret;
286 :
287 66 : if (rule_el->category & HBAC_CATEGORY_ALL) {
288 33 : *matched = true;
289 33 : return EOK;
290 : }
291 :
292 : /* First check the name list */
293 33 : if (rule_el->names) {
294 34 : for (i = 0; rule_el->names[i]; i++) {
295 27 : if (req_el->name != NULL) {
296 27 : rule_name = (const uint8_t *) rule_el->names[i];
297 27 : req_name = (const uint8_t *) req_el->name;
298 :
299 : /* Do a case-insensitive comparison. */
300 27 : ret = sss_utf8_case_eq(rule_name, req_name);
301 27 : if (ret != EOK && ret != ENOMATCH) {
302 0 : return ret;
303 27 : } else if (ret == EOK) {
304 20 : *matched = true;
305 20 : return EOK;
306 : }
307 : }
308 : }
309 : }
310 :
311 13 : if (rule_el->groups) {
312 : /* Not found in the name list
313 : * Check for group membership
314 : */
315 11 : for (i = 0; rule_el->groups[i]; i++) {
316 6 : rule_name = (const uint8_t *) rule_el->groups[i];
317 :
318 12 : for (j = 0; req_el->groups[j]; j++) {
319 9 : req_name = (const uint8_t *) req_el->groups[j];
320 :
321 : /* Do a case-insensitive comparison. */
322 9 : ret = sss_utf8_case_eq(rule_name, req_name);
323 9 : if (ret != EOK && ret != ENOMATCH) {
324 0 : return ret;
325 9 : } else if (ret == EOK) {
326 3 : *matched = true;
327 3 : return EOK;
328 : }
329 : }
330 : }
331 : }
332 :
333 : /* Not found in groups either */
334 10 : *matched = false;
335 10 : return EOK;
336 : }
337 :
338 6 : const char *hbac_result_string(enum hbac_eval_result result)
339 : {
340 6 : switch(result) {
341 : case HBAC_EVAL_ALLOW:
342 2 : return "HBAC_EVAL_ALLOW";
343 : case HBAC_EVAL_DENY:
344 2 : return "HBAC_EVAL_DENY";
345 : case HBAC_EVAL_ERROR:
346 2 : return "HBAC_EVAL_ERROR";
347 : case HBAC_EVAL_OOM:
348 0 : return "Could not allocate memory for hbac_info object";
349 : }
350 0 : return "HBAC_EVAL_ERROR";
351 : }
352 :
353 30 : void hbac_free_info(struct hbac_info *info)
354 : {
355 30 : if (info == NULL) return;
356 :
357 22 : free(info->rule_name);
358 22 : free(info);
359 : }
360 :
361 10 : const char *hbac_error_string(enum hbac_error_code code)
362 : {
363 10 : switch(code) {
364 : case HBAC_SUCCESS:
365 2 : return "Success";
366 : case HBAC_ERROR_NOT_IMPLEMENTED:
367 2 : return "Function is not yet implemented";
368 : case HBAC_ERROR_OUT_OF_MEMORY:
369 2 : return "Out of memory";
370 : case HBAC_ERROR_UNPARSEABLE_RULE:
371 2 : return "Rule could not be evaluated";
372 : case HBAC_ERROR_UNKNOWN:
373 : default:
374 2 : return "Unknown error code";
375 : }
376 : }
377 :
378 88 : static void hbac_request_element_debug_print(struct hbac_request_element *el,
379 : const char *label)
380 : {
381 88 : if (el) {
382 72 : if (el->name) {
383 72 : HBAC_DEBUG(HBAC_DBG_TRACE, "\t\t%s [%s]\n", label, el->name);
384 : }
385 :
386 72 : if (el->groups) {
387 72 : if (el->groups[0]) {
388 48 : HBAC_DEBUG(HBAC_DBG_TRACE, "\t\t%s_group:\n", label);
389 144 : for (int i = 0; el->groups[i]; i++) {
390 96 : HBAC_DEBUG(HBAC_DBG_TRACE, "\t\t\t[%s]\n", el->groups[i]);
391 : }
392 : } else {
393 24 : HBAC_DEBUG(HBAC_DBG_TRACE, "\t\t%s_group (none)\n", label);
394 : }
395 : }
396 : } else {
397 16 : HBAC_DEBUG(HBAC_DBG_TRACE, "\t%s (none)\n", label);
398 : }
399 88 : }
400 :
401 22 : static void hbac_req_debug_print(struct hbac_eval_req *req)
402 : {
403 22 : HBAC_DEBUG(HBAC_DBG_TRACE, "\tREQUEST:\n");
404 22 : if (req) {
405 22 : struct tm *local_time = NULL;
406 : size_t ret;
407 22 : const size_t buff_size = 100;
408 22 : char time_buff[buff_size];
409 :
410 22 : hbac_request_element_debug_print(req->service, "service");
411 22 : hbac_request_element_debug_print(req->user, "user");
412 22 : hbac_request_element_debug_print(req->targethost, "targethost");
413 22 : hbac_request_element_debug_print(req->srchost, "srchost");
414 :
415 22 : local_time = localtime(&req->request_time);
416 22 : if (local_time == NULL) {
417 6 : return;
418 : }
419 :
420 16 : ret = strftime(time_buff, buff_size, "%Y-%m-%d %H:%M:%S", local_time);
421 16 : if (ret <= 0) {
422 0 : return;
423 : }
424 :
425 16 : HBAC_DEBUG(HBAC_DBG_TRACE, "\t\trequest time %s\n", time_buff);
426 : } else {
427 0 : HBAC_DEBUG(HBAC_DBG_TRACE, "\tRequest is EMPTY.\n");
428 : }
429 : }
430 :
431 88 : static void hbac_rule_element_debug_print(struct hbac_rule_element *el,
432 : const char *label)
433 : {
434 88 : if (el) {
435 88 : HBAC_DEBUG(HBAC_DBG_TRACE, "\t\tcategory [%#x] [%s]\n", el->category,
436 : (el->category == HBAC_CATEGORY_ALL) ? "ALL" : "NONE");
437 :
438 88 : if (el->names) {
439 39 : if (el->names[0]) {
440 39 : HBAC_DEBUG(HBAC_DBG_TRACE, "\t\t%s_names:\n", label);
441 78 : for (int i = 0; el->names[i]; i++) {
442 39 : HBAC_DEBUG(HBAC_DBG_TRACE, "\t\t\t[%s]\n", el->names[i]);
443 : }
444 : } else {
445 0 : HBAC_DEBUG(HBAC_DBG_TRACE, "\t\t%s_names (none)\n", label);
446 : }
447 : }
448 :
449 88 : if (el->groups) {
450 30 : if (el->groups[0]) {
451 6 : HBAC_DEBUG(HBAC_DBG_TRACE, "\t\t%s_groups:\n", label);
452 12 : for (int i = 0; el->groups[i]; i++) {
453 6 : HBAC_DEBUG(HBAC_DBG_TRACE, "\t\t\t[%s]\n", el->groups[i]);
454 : }
455 : } else {
456 24 : HBAC_DEBUG(HBAC_DBG_TRACE, "\t\t%s_groups (none)\n", label);
457 : }
458 : }
459 : }
460 88 : }
461 :
462 22 : static void hbac_rule_debug_print(struct hbac_rule *rule)
463 : {
464 22 : if (rule) {
465 22 : HBAC_DEBUG(HBAC_DBG_TRACE, "\tRULE [%s] [%s]:\n",
466 : rule->name, (rule->enabled) ? "ENABLED" : "DISABLED");
467 22 : if (rule->services) {
468 22 : HBAC_DEBUG(HBAC_DBG_TRACE, "\tservices:\n");
469 22 : hbac_rule_element_debug_print(rule->services, "services");
470 : } else {
471 0 : HBAC_DEBUG(HBAC_DBG_TRACE, "\tservices (none)\n");
472 : }
473 :
474 22 : if (rule->users) {
475 22 : HBAC_DEBUG(HBAC_DBG_TRACE, "\tusers:\n");
476 22 : hbac_rule_element_debug_print(rule->users, "users");
477 : } else {
478 0 : HBAC_DEBUG(HBAC_DBG_TRACE, "\tusers (none)\n");
479 : }
480 :
481 22 : if (rule->targethosts) {
482 22 : HBAC_DEBUG(HBAC_DBG_TRACE, "\ttargethosts:\n");
483 22 : hbac_rule_element_debug_print(rule->targethosts, "targethosts");
484 : } else {
485 0 : HBAC_DEBUG(HBAC_DBG_TRACE, "\ttargethosts (none)\n");
486 : }
487 :
488 22 : if (rule->srchosts) {
489 22 : HBAC_DEBUG(HBAC_DBG_TRACE, "\tsrchosts:\n");
490 22 : hbac_rule_element_debug_print(rule->srchosts, "srchosts");
491 : } else {
492 0 : HBAC_DEBUG(HBAC_DBG_TRACE, "\tsrchosts (none)\n");
493 : }
494 : }
495 22 : }
|