Line data Source code
1 : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2 :
3 : /***
4 : This file is part of systemd.
5 :
6 : Copyright 2013 Lennart Poettering
7 :
8 : systemd is free software; you can redistribute it and/or modify it
9 : under the terms of the GNU Lesser General Public License as published by
10 : the Free Software Foundation; either version 2.1 of the License, or
11 : (at your option) any later version.
12 :
13 : systemd is distributed in the hope that it will be useful, but
14 : WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 : Lesser General Public License for more details.
17 :
18 : You should have received a copy of the GNU Lesser General Public License
19 : along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 : ***/
21 :
22 : #include "xml.h"
23 : #include "fileio.h"
24 : #include "strv.h"
25 : #include "set.h"
26 : #include "conf-files.h"
27 : #include "bus-internal.h"
28 : #include "bus-xml-policy.h"
29 : #include "sd-login.h"
30 : #include "formats-util.h"
31 :
32 70 : static void policy_item_free(PolicyItem *i) {
33 70 : assert(i);
34 :
35 70 : free(i->interface);
36 70 : free(i->member);
37 70 : free(i->error);
38 70 : free(i->name);
39 70 : free(i->path);
40 70 : free(i);
41 70 : }
42 :
43 9 : DEFINE_TRIVIAL_CLEANUP_FUNC(PolicyItem*, policy_item_free);
44 :
45 70 : static void item_append(PolicyItem *i, PolicyItem **list) {
46 :
47 : PolicyItem *tail;
48 :
49 70 : LIST_FIND_TAIL(items, *list, tail);
50 70 : LIST_INSERT_AFTER(items, *list, tail, i);
51 70 : }
52 :
53 9 : static int file_load(Policy *p, const char *path) {
54 :
55 18 : _cleanup_free_ char *c = NULL, *policy_user = NULL, *policy_group = NULL;
56 18 : _cleanup_(policy_item_freep) PolicyItem *i = NULL;
57 9 : void *xml_state = NULL;
58 9 : unsigned n_other = 0;
59 : const char *q;
60 : int r;
61 :
62 : enum {
63 : STATE_OUTSIDE,
64 : STATE_BUSCONFIG,
65 : STATE_POLICY,
66 : STATE_POLICY_CONTEXT,
67 : STATE_POLICY_CONSOLE,
68 : STATE_POLICY_USER,
69 : STATE_POLICY_GROUP,
70 : STATE_POLICY_OTHER_ATTRIBUTE,
71 : STATE_ALLOW_DENY,
72 : STATE_ALLOW_DENY_INTERFACE,
73 : STATE_ALLOW_DENY_MEMBER,
74 : STATE_ALLOW_DENY_ERROR,
75 : STATE_ALLOW_DENY_PATH,
76 : STATE_ALLOW_DENY_MESSAGE_TYPE,
77 : STATE_ALLOW_DENY_NAME,
78 : STATE_ALLOW_DENY_OTHER_ATTRIBUTE,
79 : STATE_OTHER,
80 9 : } state = STATE_OUTSIDE;
81 :
82 : enum {
83 : POLICY_CATEGORY_NONE,
84 : POLICY_CATEGORY_DEFAULT,
85 : POLICY_CATEGORY_MANDATORY,
86 : POLICY_CATEGORY_ON_CONSOLE,
87 : POLICY_CATEGORY_NO_CONSOLE,
88 : POLICY_CATEGORY_USER,
89 : POLICY_CATEGORY_GROUP
90 9 : } policy_category = POLICY_CATEGORY_NONE;
91 :
92 9 : unsigned line = 0;
93 :
94 9 : assert(p);
95 :
96 9 : r = read_full_file(path, &c, NULL);
97 9 : if (r < 0) {
98 0 : if (r == -ENOENT)
99 0 : return 0;
100 0 : if (r == -EISDIR)
101 0 : return r;
102 :
103 0 : return log_error_errno(r, "Failed to load %s: %m", path);
104 : }
105 :
106 9 : q = c;
107 : for (;;) {
108 1758 : _cleanup_free_ char *name = NULL;
109 : int t;
110 :
111 879 : t = xml_tokenize(&q, &name, &xml_state, &line);
112 879 : if (t < 0)
113 0 : return log_error_errno(t, "XML parse failure in %s: %m", path);
114 :
115 879 : switch (state) {
116 :
117 : case STATE_OUTSIDE:
118 :
119 47 : if (t == XML_TAG_OPEN) {
120 9 : if (streq(name, "busconfig"))
121 9 : state = STATE_BUSCONFIG;
122 : else {
123 0 : log_error("Unexpected tag %s at %s:%u.", name, path, line);
124 0 : return -EINVAL;
125 : }
126 :
127 38 : } else if (t == XML_END)
128 9 : return 0;
129 29 : else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
130 0 : log_error("Unexpected token (1) at %s:%u.", path, line);
131 0 : return -EINVAL;
132 : }
133 :
134 38 : break;
135 :
136 : case STATE_BUSCONFIG:
137 :
138 187 : if (t == XML_TAG_OPEN) {
139 73 : if (streq(name, "policy")) {
140 16 : state = STATE_POLICY;
141 16 : policy_category = POLICY_CATEGORY_NONE;
142 16 : free(policy_user);
143 16 : free(policy_group);
144 16 : policy_user = policy_group = NULL;
145 : } else {
146 57 : state = STATE_OTHER;
147 57 : n_other = 0;
148 : }
149 114 : } else if (t == XML_TAG_CLOSE_EMPTY ||
150 9 : (t == XML_TAG_CLOSE && streq(name, "busconfig")))
151 9 : state = STATE_OUTSIDE;
152 105 : else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
153 0 : log_error("Unexpected token (2) at %s:%u.", path, line);
154 0 : return -EINVAL;
155 : }
156 :
157 187 : break;
158 :
159 : case STATE_POLICY:
160 :
161 197 : if (t == XML_ATTRIBUTE_NAME) {
162 16 : if (streq(name, "context"))
163 11 : state = STATE_POLICY_CONTEXT;
164 5 : else if (streq(name, "at_console"))
165 0 : state = STATE_POLICY_CONSOLE;
166 5 : else if (streq(name, "user"))
167 5 : state = STATE_POLICY_USER;
168 0 : else if (streq(name, "group"))
169 0 : state = STATE_POLICY_GROUP;
170 : else {
171 0 : log_warning("Attribute %s of <policy> tag unknown at %s:%u, ignoring.", name, path, line);
172 0 : state = STATE_POLICY_OTHER_ATTRIBUTE;
173 : }
174 181 : } else if (t == XML_TAG_CLOSE_EMPTY ||
175 16 : (t == XML_TAG_CLOSE && streq(name, "policy")))
176 16 : state = STATE_BUSCONFIG;
177 165 : else if (t == XML_TAG_OPEN) {
178 : PolicyItemType it;
179 :
180 70 : if (streq(name, "allow"))
181 37 : it = POLICY_ITEM_ALLOW;
182 33 : else if (streq(name, "deny"))
183 33 : it = POLICY_ITEM_DENY;
184 : else {
185 0 : log_warning("Unknown tag %s in <policy> %s:%u.", name, path, line);
186 0 : return -EINVAL;
187 : }
188 :
189 70 : assert(!i);
190 70 : i = new0(PolicyItem, 1);
191 70 : if (!i)
192 0 : return log_oom();
193 :
194 70 : i->type = it;
195 70 : state = STATE_ALLOW_DENY;
196 :
197 95 : } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
198 0 : log_error("Unexpected token (3) at %s:%u.", path, line);
199 0 : return -EINVAL;
200 : }
201 :
202 197 : break;
203 :
204 : case STATE_POLICY_CONTEXT:
205 :
206 11 : if (t == XML_ATTRIBUTE_VALUE) {
207 11 : if (streq(name, "default")) {
208 9 : policy_category = POLICY_CATEGORY_DEFAULT;
209 9 : state = STATE_POLICY;
210 2 : } else if (streq(name, "mandatory")) {
211 2 : policy_category = POLICY_CATEGORY_MANDATORY;
212 2 : state = STATE_POLICY;
213 : } else {
214 0 : log_error("context= parameter %s unknown for <policy> at %s:%u.", name, path, line);
215 0 : return -EINVAL;
216 : }
217 : } else {
218 0 : log_error("Unexpected token (4) at %s:%u.", path, line);
219 0 : return -EINVAL;
220 : }
221 :
222 11 : break;
223 :
224 : case STATE_POLICY_CONSOLE:
225 :
226 0 : if (t == XML_ATTRIBUTE_VALUE) {
227 0 : if (streq(name, "true")) {
228 0 : policy_category = POLICY_CATEGORY_ON_CONSOLE;
229 0 : state = STATE_POLICY;
230 0 : } else if (streq(name, "false")) {
231 0 : policy_category = POLICY_CATEGORY_NO_CONSOLE;
232 0 : state = STATE_POLICY;
233 : } else {
234 0 : log_error("at_console= parameter %s unknown for <policy> at %s:%u.", name, path, line);
235 0 : return -EINVAL;
236 : }
237 : } else {
238 0 : log_error("Unexpected token (4.1) at %s:%u.", path, line);
239 0 : return -EINVAL;
240 : }
241 :
242 0 : break;
243 :
244 : case STATE_POLICY_USER:
245 :
246 5 : if (t == XML_ATTRIBUTE_VALUE) {
247 5 : free(policy_user);
248 5 : policy_user = name;
249 5 : name = NULL;
250 5 : policy_category = POLICY_CATEGORY_USER;
251 5 : state = STATE_POLICY;
252 : } else {
253 0 : log_error("Unexpected token (5) in %s:%u.", path, line);
254 0 : return -EINVAL;
255 : }
256 :
257 5 : break;
258 :
259 : case STATE_POLICY_GROUP:
260 :
261 0 : if (t == XML_ATTRIBUTE_VALUE) {
262 0 : free(policy_group);
263 0 : policy_group = name;
264 0 : name = NULL;
265 0 : policy_category = POLICY_CATEGORY_GROUP;
266 0 : state = STATE_POLICY;
267 : } else {
268 0 : log_error("Unexpected token (6) at %s:%u.", path, line);
269 0 : return -EINVAL;
270 : }
271 :
272 0 : break;
273 :
274 : case STATE_POLICY_OTHER_ATTRIBUTE:
275 :
276 0 : if (t == XML_ATTRIBUTE_VALUE)
277 0 : state = STATE_POLICY;
278 : else {
279 0 : log_error("Unexpected token (7) in %s:%u.", path, line);
280 0 : return -EINVAL;
281 : }
282 :
283 0 : break;
284 :
285 : case STATE_ALLOW_DENY:
286 :
287 164 : assert(i);
288 :
289 164 : if (t == XML_ATTRIBUTE_NAME) {
290 : PolicyItemClass ic;
291 :
292 94 : if (startswith(name, "send_"))
293 52 : ic = POLICY_ITEM_SEND;
294 42 : else if (startswith(name, "receive_"))
295 15 : ic = POLICY_ITEM_RECV;
296 27 : else if (streq(name, "own"))
297 11 : ic = POLICY_ITEM_OWN;
298 16 : else if (streq(name, "own_prefix"))
299 3 : ic = POLICY_ITEM_OWN_PREFIX;
300 13 : else if (streq(name, "user"))
301 8 : ic = POLICY_ITEM_USER;
302 5 : else if (streq(name, "group"))
303 3 : ic = POLICY_ITEM_GROUP;
304 2 : else if (STR_IN_SET(name, "eavesdrop", "log")) {
305 2 : log_debug("Unsupported attribute %s= at %s:%u, ignoring.", name, path, line);
306 2 : state = STATE_ALLOW_DENY_OTHER_ATTRIBUTE;
307 2 : break;
308 : } else {
309 0 : log_error("Unknown attribute %s= at %s:%u, ignoring.", name, path, line);
310 0 : state = STATE_ALLOW_DENY_OTHER_ATTRIBUTE;
311 0 : break;
312 : }
313 :
314 92 : if (i->class != _POLICY_ITEM_CLASS_UNSET && ic != i->class) {
315 0 : log_error("send_, receive_/eavesdrop fields mixed on same tag at %s:%u.", path, line);
316 0 : return -EINVAL;
317 : }
318 :
319 92 : i->class = ic;
320 :
321 157 : if (ic == POLICY_ITEM_SEND || ic == POLICY_ITEM_RECV) {
322 : const char *u;
323 :
324 67 : u = strchr(name, '_');
325 67 : assert(u);
326 :
327 67 : u++;
328 :
329 67 : if (streq(u, "interface"))
330 16 : state = STATE_ALLOW_DENY_INTERFACE;
331 51 : else if (streq(u, "member"))
332 8 : state = STATE_ALLOW_DENY_MEMBER;
333 43 : else if (streq(u, "error"))
334 0 : state = STATE_ALLOW_DENY_ERROR;
335 43 : else if (streq(u, "path"))
336 2 : state = STATE_ALLOW_DENY_PATH;
337 41 : else if (streq(u, "type"))
338 21 : state = STATE_ALLOW_DENY_MESSAGE_TYPE;
339 26 : else if ((streq(u, "destination") && ic == POLICY_ITEM_SEND) ||
340 10 : (streq(u, "sender") && ic == POLICY_ITEM_RECV))
341 18 : state = STATE_ALLOW_DENY_NAME;
342 : else {
343 2 : if (streq(u, "requested_reply"))
344 2 : log_debug("Unsupported attribute %s= at %s:%u, ignoring.", name, path, line);
345 : else
346 0 : log_error("Unknown attribute %s= at %s:%u, ignoring.", name, path, line);
347 2 : state = STATE_ALLOW_DENY_OTHER_ATTRIBUTE;
348 2 : break;
349 : }
350 : } else
351 25 : state = STATE_ALLOW_DENY_NAME;
352 :
353 70 : } else if (t == XML_TAG_CLOSE_EMPTY ||
354 0 : (t == XML_TAG_CLOSE && streq(name, i->type == POLICY_ITEM_ALLOW ? "allow" : "deny"))) {
355 :
356 : /* If the tag is fully empty so far, we consider it a recv */
357 70 : if (i->class == _POLICY_ITEM_CLASS_UNSET)
358 1 : i->class = POLICY_ITEM_RECV;
359 :
360 70 : if (policy_category == POLICY_CATEGORY_DEFAULT)
361 46 : item_append(i, &p->default_items);
362 24 : else if (policy_category == POLICY_CATEGORY_MANDATORY)
363 17 : item_append(i, &p->mandatory_items);
364 7 : else if (policy_category == POLICY_CATEGORY_ON_CONSOLE)
365 0 : item_append(i, &p->on_console_items);
366 7 : else if (policy_category == POLICY_CATEGORY_NO_CONSOLE)
367 0 : item_append(i, &p->no_console_items);
368 7 : else if (policy_category == POLICY_CATEGORY_USER) {
369 7 : const char *u = policy_user;
370 :
371 : assert_cc(sizeof(uid_t) == sizeof(uint32_t));
372 :
373 7 : r = hashmap_ensure_allocated(&p->user_items, NULL);
374 7 : if (r < 0)
375 0 : return log_oom();
376 :
377 7 : if (!u) {
378 0 : log_error("User policy without name");
379 0 : return -EINVAL;
380 : }
381 :
382 7 : r = get_user_creds(&u, &i->uid, NULL, NULL, NULL);
383 7 : if (r < 0) {
384 0 : log_error_errno(r, "Failed to resolve user %s, ignoring policy: %m", u);
385 0 : free(i);
386 : } else {
387 : PolicyItem *first;
388 :
389 7 : first = hashmap_get(p->user_items, UINT32_TO_PTR(i->uid));
390 7 : item_append(i, &first);
391 7 : i->uid_valid = true;
392 :
393 7 : r = hashmap_replace(p->user_items, UINT32_TO_PTR(i->uid), first);
394 7 : if (r < 0) {
395 0 : LIST_REMOVE(items, first, i);
396 0 : return log_oom();
397 : }
398 : }
399 :
400 0 : } else if (policy_category == POLICY_CATEGORY_GROUP) {
401 0 : const char *g = policy_group;
402 :
403 : assert_cc(sizeof(gid_t) == sizeof(uint32_t));
404 :
405 0 : r = hashmap_ensure_allocated(&p->group_items, NULL);
406 0 : if (r < 0)
407 0 : return log_oom();
408 :
409 0 : if (!g) {
410 0 : log_error("Group policy without name");
411 0 : return -EINVAL;
412 : }
413 :
414 0 : r = get_group_creds(&g, &i->gid);
415 0 : if (r < 0) {
416 0 : log_error_errno(r, "Failed to resolve group %s, ignoring policy: %m", g);
417 0 : free(i);
418 : } else {
419 : PolicyItem *first;
420 :
421 0 : first = hashmap_get(p->group_items, UINT32_TO_PTR(i->gid));
422 0 : item_append(i, &first);
423 0 : i->gid_valid = true;
424 :
425 0 : r = hashmap_replace(p->group_items, UINT32_TO_PTR(i->gid), first);
426 0 : if (r < 0) {
427 0 : LIST_REMOVE(items, first, i);
428 0 : return log_oom();
429 : }
430 : }
431 : }
432 :
433 70 : state = STATE_POLICY;
434 70 : i = NULL;
435 :
436 0 : } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
437 0 : log_error("Unexpected token (8) at %s:%u.", path, line);
438 0 : return -EINVAL;
439 : }
440 :
441 160 : break;
442 :
443 : case STATE_ALLOW_DENY_INTERFACE:
444 :
445 16 : if (t == XML_ATTRIBUTE_VALUE) {
446 16 : assert(i);
447 16 : if (i->interface) {
448 0 : log_error("Duplicate interface at %s:%u.", path, line);
449 0 : return -EINVAL;
450 : }
451 :
452 16 : if (!streq(name, "*")) {
453 16 : i->interface = name;
454 16 : name = NULL;
455 : }
456 16 : state = STATE_ALLOW_DENY;
457 : } else {
458 0 : log_error("Unexpected token (9) at %s:%u.", path, line);
459 0 : return -EINVAL;
460 : }
461 :
462 16 : break;
463 :
464 : case STATE_ALLOW_DENY_MEMBER:
465 :
466 8 : if (t == XML_ATTRIBUTE_VALUE) {
467 8 : assert(i);
468 8 : if (i->member) {
469 0 : log_error("Duplicate member in %s:%u.", path, line);
470 0 : return -EINVAL;
471 : }
472 :
473 8 : if (!streq(name, "*")) {
474 8 : i->member = name;
475 8 : name = NULL;
476 : }
477 8 : state = STATE_ALLOW_DENY;
478 : } else {
479 0 : log_error("Unexpected token (10) in %s:%u.", path, line);
480 0 : return -EINVAL;
481 : }
482 :
483 8 : break;
484 :
485 : case STATE_ALLOW_DENY_ERROR:
486 :
487 0 : if (t == XML_ATTRIBUTE_VALUE) {
488 0 : assert(i);
489 0 : if (i->error) {
490 0 : log_error("Duplicate error in %s:%u.", path, line);
491 0 : return -EINVAL;
492 : }
493 :
494 0 : if (!streq(name, "*")) {
495 0 : i->error = name;
496 0 : name = NULL;
497 : }
498 0 : state = STATE_ALLOW_DENY;
499 : } else {
500 0 : log_error("Unexpected token (11) in %s:%u.", path, line);
501 0 : return -EINVAL;
502 : }
503 :
504 0 : break;
505 :
506 : case STATE_ALLOW_DENY_PATH:
507 :
508 2 : if (t == XML_ATTRIBUTE_VALUE) {
509 2 : assert(i);
510 2 : if (i->path) {
511 0 : log_error("Duplicate path in %s:%u.", path, line);
512 0 : return -EINVAL;
513 : }
514 :
515 2 : if (!streq(name, "*")) {
516 2 : i->path = name;
517 2 : name = NULL;
518 : }
519 2 : state = STATE_ALLOW_DENY;
520 : } else {
521 0 : log_error("Unexpected token (12) in %s:%u.", path, line);
522 0 : return -EINVAL;
523 : }
524 :
525 2 : break;
526 :
527 : case STATE_ALLOW_DENY_MESSAGE_TYPE:
528 :
529 21 : if (t == XML_ATTRIBUTE_VALUE) {
530 21 : assert(i);
531 :
532 21 : if (i->message_type != 0) {
533 0 : log_error("Duplicate message type in %s:%u.", path, line);
534 0 : return -EINVAL;
535 : }
536 :
537 21 : if (!streq(name, "*")) {
538 21 : r = bus_message_type_from_string(name, &i->message_type);
539 21 : if (r < 0) {
540 0 : log_error("Invalid message type in %s:%u.", path, line);
541 0 : return -EINVAL;
542 : }
543 : }
544 :
545 21 : state = STATE_ALLOW_DENY;
546 : } else {
547 0 : log_error("Unexpected token (13) in %s:%u.", path, line);
548 0 : return -EINVAL;
549 : }
550 :
551 21 : break;
552 :
553 : case STATE_ALLOW_DENY_NAME:
554 :
555 43 : if (t == XML_ATTRIBUTE_VALUE) {
556 43 : assert(i);
557 43 : if (i->name) {
558 0 : log_error("Duplicate name in %s:%u.", path, line);
559 0 : return -EINVAL;
560 : }
561 :
562 43 : switch (i->class) {
563 : case POLICY_ITEM_USER:
564 8 : if (!streq(name, "*")) {
565 3 : const char *u = name;
566 :
567 3 : r = get_user_creds(&u, &i->uid, NULL, NULL, NULL);
568 3 : if (r < 0)
569 0 : log_error_errno(r, "Failed to resolve user %s: %m", name);
570 : else
571 3 : i->uid_valid = true;
572 : }
573 8 : break;
574 : case POLICY_ITEM_GROUP:
575 3 : if (!streq(name, "*")) {
576 3 : const char *g = name;
577 :
578 3 : r = get_group_creds(&g, &i->gid);
579 3 : if (r < 0)
580 0 : log_error_errno(r, "Failed to resolve group %s: %m", name);
581 : else
582 3 : i->gid_valid = true;
583 : }
584 3 : break;
585 :
586 : case POLICY_ITEM_SEND:
587 : case POLICY_ITEM_RECV:
588 :
589 18 : if (streq(name, "*")) {
590 1 : free(name);
591 1 : name = NULL;
592 : }
593 18 : break;
594 :
595 :
596 : default:
597 14 : break;
598 : }
599 :
600 43 : i->name = name;
601 43 : name = NULL;
602 :
603 43 : state = STATE_ALLOW_DENY;
604 : } else {
605 0 : log_error("Unexpected token (14) in %s:%u.", path, line);
606 0 : return -EINVAL;
607 : }
608 :
609 43 : break;
610 :
611 : case STATE_ALLOW_DENY_OTHER_ATTRIBUTE:
612 :
613 4 : if (t == XML_ATTRIBUTE_VALUE)
614 4 : state = STATE_ALLOW_DENY;
615 : else {
616 0 : log_error("Unexpected token (15) in %s:%u.", path, line);
617 0 : return -EINVAL;
618 : }
619 :
620 4 : break;
621 :
622 : case STATE_OTHER:
623 :
624 174 : if (t == XML_TAG_OPEN)
625 0 : n_other++;
626 174 : else if (t == XML_TAG_CLOSE || t == XML_TAG_CLOSE_EMPTY) {
627 :
628 57 : if (n_other == 0)
629 57 : state = STATE_BUSCONFIG;
630 : else
631 0 : n_other--;
632 : }
633 :
634 174 : break;
635 : }
636 870 : }
637 : }
638 :
639 : enum {
640 : DENY,
641 : ALLOW,
642 : DUNNO,
643 : };
644 :
645 14 : static const char *verdict_to_string(int v) {
646 14 : switch (v) {
647 :
648 : case DENY:
649 9 : return "DENY";
650 : case ALLOW:
651 0 : return "ALLOW";
652 : case DUNNO:
653 5 : return "DUNNO";
654 : }
655 :
656 0 : return NULL;
657 : }
658 :
659 : struct policy_check_filter {
660 : PolicyItemClass class;
661 : uid_t uid;
662 : gid_t gid;
663 : int message_type;
664 : const char *name;
665 : const char *interface;
666 : const char *path;
667 : const char *member;
668 : };
669 :
670 41 : static int is_permissive(PolicyItem *i) {
671 :
672 41 : assert(i);
673 :
674 41 : return (i->type == POLICY_ITEM_ALLOW) ? ALLOW : DENY;
675 : }
676 :
677 82 : static int check_policy_item(PolicyItem *i, const struct policy_check_filter *filter) {
678 :
679 82 : assert(i);
680 82 : assert(filter);
681 :
682 82 : switch (i->class) {
683 : case POLICY_ITEM_SEND:
684 : case POLICY_ITEM_RECV:
685 :
686 28 : if (i->name && !streq_ptr(i->name, filter->name))
687 2 : break;
688 :
689 26 : if ((i->message_type != 0) && (i->message_type != filter->message_type))
690 0 : break;
691 :
692 26 : if (i->path && !streq_ptr(i->path, filter->path))
693 0 : break;
694 :
695 26 : if (i->member && !streq_ptr(i->member, filter->member))
696 0 : break;
697 :
698 26 : if (i->interface && !streq_ptr(i->interface, filter->interface))
699 9 : break;
700 :
701 17 : return is_permissive(i);
702 :
703 : case POLICY_ITEM_OWN:
704 38 : assert(filter->name);
705 :
706 38 : if (streq(i->name, "*") || streq(i->name, filter->name))
707 16 : return is_permissive(i);
708 22 : break;
709 :
710 : case POLICY_ITEM_OWN_PREFIX:
711 8 : assert(filter->name);
712 :
713 8 : if (streq(i->name, "*") || service_name_startswith(filter->name, i->name))
714 3 : return is_permissive(i);
715 5 : break;
716 :
717 : case POLICY_ITEM_USER:
718 6 : if (filter->uid != UID_INVALID)
719 6 : if ((streq_ptr(i->name, "*") || (i->uid_valid && i->uid == filter->uid)))
720 4 : return is_permissive(i);
721 2 : break;
722 :
723 : case POLICY_ITEM_GROUP:
724 2 : if (filter->gid != GID_INVALID)
725 2 : if ((streq_ptr(i->name, "*") || (i->gid_valid && i->gid == filter->gid)))
726 1 : return is_permissive(i);
727 1 : break;
728 :
729 : case POLICY_ITEM_IGNORE:
730 : default:
731 0 : break;
732 : }
733 :
734 41 : return DUNNO;
735 : }
736 :
737 142 : static int check_policy_items(PolicyItem *items, const struct policy_check_filter *filter) {
738 :
739 : PolicyItem *i;
740 142 : int verdict = DUNNO;
741 :
742 142 : assert(filter);
743 :
744 : /* Check all policies in a set - a broader one might be followed by a more specific one,
745 : * and the order of rules in policy definitions matters */
746 265 : LIST_FOREACH(items, i, items) {
747 : int v;
748 :
749 172 : if (i->class != filter->class &&
750 57 : !(i->class == POLICY_ITEM_OWN_PREFIX && filter->class == POLICY_ITEM_OWN))
751 41 : continue;
752 :
753 82 : v = check_policy_item(i, filter);
754 82 : if (v != DUNNO)
755 41 : verdict = v;
756 : }
757 :
758 142 : return verdict;
759 : }
760 :
761 42 : static int policy_check(Policy *p, const struct policy_check_filter *filter) {
762 :
763 : PolicyItem *items;
764 : int verdict, v;
765 :
766 42 : assert(p);
767 42 : assert(filter);
768 :
769 42 : assert(IN_SET(filter->class, POLICY_ITEM_SEND, POLICY_ITEM_RECV, POLICY_ITEM_OWN, POLICY_ITEM_USER, POLICY_ITEM_GROUP));
770 :
771 : /*
772 : * The policy check is implemented by the following logic:
773 : *
774 : * 1. Check default items
775 : * 2. Check group items
776 : * 3. Check user items
777 : * 4. Check on/no_console items
778 : * 5. Check mandatory items
779 : *
780 : * Later rules override earlier rules.
781 : */
782 :
783 42 : verdict = check_policy_items(p->default_items, filter);
784 :
785 42 : if (filter->gid != GID_INVALID) {
786 42 : items = hashmap_get(p->group_items, UINT32_TO_PTR(filter->gid));
787 42 : if (items) {
788 0 : v = check_policy_items(items, filter);
789 0 : if (v != DUNNO)
790 0 : verdict = v;
791 : }
792 : }
793 :
794 42 : if (filter->uid != UID_INVALID) {
795 42 : items = hashmap_get(p->user_items, UINT32_TO_PTR(filter->uid));
796 42 : if (items) {
797 16 : v = check_policy_items(items, filter);
798 16 : if (v != DUNNO)
799 6 : verdict = v;
800 : }
801 : }
802 :
803 42 : if (filter->uid != UID_INVALID && sd_uid_get_seats(filter->uid, -1, NULL) > 0)
804 0 : v = check_policy_items(p->on_console_items, filter);
805 : else
806 42 : v = check_policy_items(p->no_console_items, filter);
807 42 : if (v != DUNNO)
808 0 : verdict = v;
809 :
810 42 : v = check_policy_items(p->mandatory_items, filter);
811 42 : if (v != DUNNO)
812 2 : verdict = v;
813 :
814 42 : return verdict;
815 : }
816 :
817 20 : bool policy_check_own(Policy *p, uid_t uid, gid_t gid, const char *name) {
818 :
819 20 : struct policy_check_filter filter = {
820 : .class = POLICY_ITEM_OWN,
821 : .uid = uid,
822 : .gid = gid,
823 : .name = name,
824 : };
825 :
826 : int verdict;
827 :
828 20 : assert(p);
829 20 : assert(name);
830 :
831 20 : verdict = policy_check(p, &filter);
832 :
833 20 : log_full(LOG_AUTH | (verdict != ALLOW ? LOG_WARNING : LOG_DEBUG),
834 : "Ownership permission check for uid=" UID_FMT " gid=" GID_FMT" name=%s: %s",
835 : uid, gid, strna(name), strna(verdict_to_string(verdict)));
836 :
837 20 : return verdict == ALLOW;
838 : }
839 :
840 3 : bool policy_check_hello(Policy *p, uid_t uid, gid_t gid) {
841 :
842 3 : struct policy_check_filter filter = {
843 : .uid = uid,
844 : .gid = gid,
845 : };
846 : int verdict;
847 :
848 3 : assert(p);
849 :
850 3 : filter.class = POLICY_ITEM_USER;
851 3 : verdict = policy_check(p, &filter);
852 :
853 3 : if (verdict != DENY) {
854 : int v;
855 :
856 2 : filter.class = POLICY_ITEM_GROUP;
857 2 : v = policy_check(p, &filter);
858 2 : if (v != DUNNO)
859 1 : verdict = v;
860 : }
861 :
862 3 : log_full(LOG_AUTH | (verdict != ALLOW ? LOG_WARNING : LOG_DEBUG),
863 : "Hello permission check for uid=" UID_FMT " gid=" GID_FMT": %s",
864 : uid, gid, strna(verdict_to_string(verdict)));
865 :
866 3 : return verdict == ALLOW;
867 : }
868 :
869 7 : bool policy_check_one_recv(Policy *p,
870 : uid_t uid,
871 : gid_t gid,
872 : int message_type,
873 : const char *name,
874 : const char *path,
875 : const char *interface,
876 : const char *member) {
877 :
878 7 : struct policy_check_filter filter = {
879 : .class = POLICY_ITEM_RECV,
880 : .uid = uid,
881 : .gid = gid,
882 : .message_type = message_type,
883 : .name = name,
884 : .interface = interface,
885 : .path = path,
886 : .member = member,
887 : };
888 :
889 7 : assert(p);
890 :
891 7 : return policy_check(p, &filter) == ALLOW;
892 : }
893 :
894 0 : bool policy_check_recv(Policy *p,
895 : uid_t uid,
896 : gid_t gid,
897 : int message_type,
898 : Set *names,
899 : char **namesv,
900 : const char *path,
901 : const char *interface,
902 : const char *member,
903 : bool dbus_to_kernel) {
904 :
905 0 : char *n, **nv, *last = NULL;
906 0 : bool allow = false;
907 : Iterator i;
908 :
909 0 : assert(p);
910 :
911 0 : if (set_isempty(names) && strv_isempty(namesv)) {
912 0 : allow = policy_check_one_recv(p, uid, gid, message_type, NULL, path, interface, member);
913 : } else {
914 0 : SET_FOREACH(n, names, i) {
915 0 : last = n;
916 0 : allow = policy_check_one_recv(p, uid, gid, message_type, n, path, interface, member);
917 0 : if (allow)
918 0 : break;
919 : }
920 0 : if (!allow) {
921 0 : STRV_FOREACH(nv, namesv) {
922 0 : last = *nv;
923 0 : allow = policy_check_one_recv(p, uid, gid, message_type, *nv, path, interface, member);
924 0 : if (allow)
925 0 : break;
926 : }
927 : }
928 : }
929 :
930 0 : log_full(LOG_AUTH | (!allow ? LOG_WARNING : LOG_DEBUG),
931 : "Receive permission check %s for uid=" UID_FMT " gid=" GID_FMT" message=%s name=%s path=%s interface=%s member=%s: %s",
932 : dbus_to_kernel ? "dbus-1 to kernel" : "kernel to dbus-1", uid, gid, bus_message_type_to_string(message_type), strna(last),
933 : strna(path), strna(interface), strna(member), allow ? "ALLOW" : "DENY");
934 :
935 0 : return allow;
936 : }
937 :
938 10 : bool policy_check_one_send(Policy *p,
939 : uid_t uid,
940 : gid_t gid,
941 : int message_type,
942 : const char *name,
943 : const char *path,
944 : const char *interface,
945 : const char *member) {
946 :
947 10 : struct policy_check_filter filter = {
948 : .class = POLICY_ITEM_SEND,
949 : .uid = uid,
950 : .gid = gid,
951 : .message_type = message_type,
952 : .name = name,
953 : .interface = interface,
954 : .path = path,
955 : .member = member,
956 : };
957 :
958 10 : assert(p);
959 :
960 10 : return policy_check(p, &filter) == ALLOW;
961 : }
962 :
963 0 : bool policy_check_send(Policy *p,
964 : uid_t uid,
965 : gid_t gid,
966 : int message_type,
967 : Set *names,
968 : char **namesv,
969 : const char *path,
970 : const char *interface,
971 : const char *member,
972 : bool dbus_to_kernel,
973 : char **out_used_name) {
974 :
975 0 : char *n, **nv, *last = NULL;
976 0 : bool allow = false;
977 : Iterator i;
978 :
979 0 : assert(p);
980 :
981 0 : if (set_isempty(names) && strv_isempty(namesv)) {
982 0 : allow = policy_check_one_send(p, uid, gid, message_type, NULL, path, interface, member);
983 : } else {
984 0 : SET_FOREACH(n, names, i) {
985 0 : last = n;
986 0 : allow = policy_check_one_send(p, uid, gid, message_type, n, path, interface, member);
987 0 : if (allow)
988 0 : break;
989 : }
990 0 : if (!allow) {
991 0 : STRV_FOREACH(nv, namesv) {
992 0 : last = *nv;
993 0 : allow = policy_check_one_send(p, uid, gid, message_type, *nv, path, interface, member);
994 0 : if (allow)
995 0 : break;
996 : }
997 : }
998 : }
999 :
1000 0 : if (out_used_name)
1001 0 : *out_used_name = last;
1002 :
1003 0 : log_full(LOG_AUTH | (!allow ? LOG_WARNING : LOG_DEBUG),
1004 : "Send permission check %s for uid=" UID_FMT " gid=" GID_FMT" message=%s name=%s path=%s interface=%s member=%s: %s",
1005 : dbus_to_kernel ? "dbus-1 to kernel" : "kernel to dbus-1", uid, gid, bus_message_type_to_string(message_type), strna(last),
1006 : strna(path), strna(interface), strna(member), allow ? "ALLOW" : "DENY");
1007 :
1008 0 : return allow;
1009 : }
1010 :
1011 9 : int policy_load(Policy *p, char **files) {
1012 : char **i;
1013 : int r;
1014 :
1015 9 : assert(p);
1016 :
1017 18 : STRV_FOREACH(i, files) {
1018 :
1019 9 : r = file_load(p, *i);
1020 9 : if (r == -EISDIR) {
1021 0 : _cleanup_strv_free_ char **l = NULL;
1022 : char **j;
1023 :
1024 0 : r = conf_files_list(&l, ".conf", NULL, *i, NULL);
1025 0 : if (r < 0)
1026 0 : return log_error_errno(r, "Failed to get configuration file list: %m");
1027 :
1028 0 : STRV_FOREACH(j, l)
1029 0 : file_load(p, *j);
1030 : }
1031 :
1032 : /* We ignore all errors but EISDIR, and just proceed. */
1033 : }
1034 :
1035 9 : return 0;
1036 : }
1037 :
1038 9 : void policy_free(Policy *p) {
1039 : PolicyItem *i, *first;
1040 :
1041 9 : if (!p)
1042 0 : return;
1043 :
1044 64 : while ((i = p->default_items)) {
1045 46 : LIST_REMOVE(items, p->default_items, i);
1046 46 : policy_item_free(i);
1047 : }
1048 :
1049 35 : while ((i = p->mandatory_items)) {
1050 17 : LIST_REMOVE(items, p->mandatory_items, i);
1051 17 : policy_item_free(i);
1052 : }
1053 :
1054 18 : while ((i = p->on_console_items)) {
1055 0 : LIST_REMOVE(items, p->on_console_items, i);
1056 0 : policy_item_free(i);
1057 : }
1058 :
1059 18 : while ((i = p->no_console_items)) {
1060 0 : LIST_REMOVE(items, p->no_console_items, i);
1061 0 : policy_item_free(i);
1062 : }
1063 :
1064 23 : while ((first = hashmap_steal_first(p->user_items))) {
1065 :
1066 17 : while ((i = first)) {
1067 7 : LIST_REMOVE(items, first, i);
1068 7 : policy_item_free(i);
1069 : }
1070 : }
1071 :
1072 18 : while ((first = hashmap_steal_first(p->group_items))) {
1073 :
1074 0 : while ((i = first)) {
1075 0 : LIST_REMOVE(items, first, i);
1076 0 : policy_item_free(i);
1077 : }
1078 : }
1079 :
1080 9 : hashmap_free(p->user_items);
1081 9 : hashmap_free(p->group_items);
1082 :
1083 9 : p->user_items = p->group_items = NULL;
1084 : }
1085 :
1086 30 : static void dump_items(PolicyItem *items, const char *prefix) {
1087 :
1088 : PolicyItem *i;
1089 :
1090 30 : if (!items)
1091 20 : return;
1092 :
1093 10 : if (!prefix)
1094 0 : prefix = "";
1095 :
1096 73 : LIST_FOREACH(items, i, items) {
1097 :
1098 63 : printf("%sType: %s\n"
1099 : "%sClass: %s\n",
1100 : prefix, policy_item_type_to_string(i->type),
1101 : prefix, policy_item_class_to_string(i->class));
1102 :
1103 63 : if (i->interface)
1104 16 : printf("%sInterface: %s\n",
1105 : prefix, i->interface);
1106 :
1107 63 : if (i->member)
1108 8 : printf("%sMember: %s\n",
1109 : prefix, i->member);
1110 :
1111 63 : if (i->error)
1112 0 : printf("%sError: %s\n",
1113 : prefix, i->error);
1114 :
1115 63 : if (i->path)
1116 2 : printf("%sPath: %s\n",
1117 : prefix, i->path);
1118 :
1119 63 : if (i->name)
1120 37 : printf("%sName: %s\n",
1121 : prefix, i->name);
1122 :
1123 63 : if (i->message_type != 0)
1124 19 : printf("%sMessage Type: %s\n",
1125 19 : prefix, bus_message_type_to_string(i->message_type));
1126 :
1127 63 : if (i->uid_valid) {
1128 6 : _cleanup_free_ char *user;
1129 :
1130 6 : user = uid_to_name(i->uid);
1131 :
1132 6 : printf("%sUser: %s ("UID_FMT")\n",
1133 : prefix, strna(user), i->uid);
1134 : }
1135 :
1136 63 : if (i->gid_valid) {
1137 3 : _cleanup_free_ char *group;
1138 :
1139 3 : group = gid_to_name(i->gid);
1140 :
1141 3 : printf("%sGroup: %s ("GID_FMT")\n",
1142 : prefix, strna(group), i->gid);
1143 : }
1144 63 : printf("%s-\n", prefix);
1145 : }
1146 : }
1147 :
1148 14 : static void dump_hashmap_items(Hashmap *h) {
1149 : PolicyItem *i;
1150 : Iterator j;
1151 : void *k;
1152 :
1153 30 : HASHMAP_FOREACH_KEY(i, k, h, j) {
1154 2 : printf("\t%s Item for %u:\n", draw_special_char(DRAW_ARROW), PTR_TO_UINT(k));
1155 2 : dump_items(i, "\t\t");
1156 : }
1157 14 : }
1158 :
1159 7 : void policy_dump(Policy *p) {
1160 :
1161 7 : printf("%s Default Items:\n", draw_special_char(DRAW_ARROW));
1162 7 : dump_items(p->default_items, "\t");
1163 :
1164 7 : printf("%s Group Items:\n", draw_special_char(DRAW_ARROW));
1165 7 : dump_hashmap_items(p->group_items);
1166 :
1167 7 : printf("%s User Items:\n", draw_special_char(DRAW_ARROW));
1168 7 : dump_hashmap_items(p->user_items);
1169 :
1170 7 : printf("%s On-Console Items:\n", draw_special_char(DRAW_ARROW));
1171 7 : dump_items(p->on_console_items, "\t");
1172 :
1173 7 : printf("%s No-Console Items:\n", draw_special_char(DRAW_ARROW));
1174 7 : dump_items(p->no_console_items, "\t");
1175 :
1176 7 : printf("%s Mandatory Items:\n", draw_special_char(DRAW_ARROW));
1177 7 : dump_items(p->mandatory_items, "\t");
1178 :
1179 7 : fflush(stdout);
1180 7 : }
1181 :
1182 0 : int shared_policy_new(SharedPolicy **out) {
1183 : SharedPolicy *sp;
1184 : int r;
1185 :
1186 0 : sp = new0(SharedPolicy, 1);
1187 0 : if (!sp)
1188 0 : return log_oom();
1189 :
1190 0 : r = pthread_mutex_init(&sp->lock, NULL);
1191 0 : if (r < 0) {
1192 0 : log_error_errno(r, "Cannot initialize shared policy mutex: %m");
1193 0 : goto exit_free;
1194 : }
1195 :
1196 0 : r = pthread_rwlock_init(&sp->rwlock, NULL);
1197 0 : if (r < 0) {
1198 0 : log_error_errno(r, "Cannot initialize shared policy rwlock: %m");
1199 0 : goto exit_mutex;
1200 : }
1201 :
1202 0 : *out = sp;
1203 0 : sp = NULL;
1204 0 : return 0;
1205 :
1206 : /* pthread lock destruction is not fail-safe... meh! */
1207 : exit_mutex:
1208 0 : pthread_mutex_destroy(&sp->lock);
1209 : exit_free:
1210 0 : free(sp);
1211 0 : return r;
1212 : }
1213 :
1214 0 : SharedPolicy *shared_policy_free(SharedPolicy *sp) {
1215 0 : if (!sp)
1216 0 : return NULL;
1217 :
1218 0 : policy_free(sp->policy);
1219 0 : pthread_rwlock_destroy(&sp->rwlock);
1220 0 : pthread_mutex_destroy(&sp->lock);
1221 0 : strv_free(sp->configuration);
1222 0 : free(sp);
1223 :
1224 0 : return NULL;
1225 : }
1226 :
1227 0 : static int shared_policy_reload_unlocked(SharedPolicy *sp, char **configuration) {
1228 0 : Policy old, buffer = {};
1229 : bool free_old;
1230 : int r;
1231 :
1232 0 : assert(sp);
1233 :
1234 0 : r = policy_load(&buffer, configuration);
1235 0 : if (r < 0)
1236 0 : return log_error_errno(r, "Failed to load policy: %m");
1237 :
1238 0 : log_debug("Reloading configuration");
1239 : /* policy_dump(&buffer); */
1240 :
1241 0 : pthread_rwlock_wrlock(&sp->rwlock);
1242 0 : memcpy(&old, &sp->buffer, sizeof(old));
1243 0 : memcpy(&sp->buffer, &buffer, sizeof(buffer));
1244 0 : free_old = !!sp->policy;
1245 0 : sp->policy = &sp->buffer;
1246 0 : pthread_rwlock_unlock(&sp->rwlock);
1247 :
1248 0 : if (free_old)
1249 0 : policy_free(&old);
1250 :
1251 0 : return 0;
1252 : }
1253 :
1254 0 : int shared_policy_reload(SharedPolicy *sp) {
1255 : int r;
1256 :
1257 0 : assert(sp);
1258 :
1259 0 : pthread_mutex_lock(&sp->lock);
1260 0 : r = shared_policy_reload_unlocked(sp, sp->configuration);
1261 0 : pthread_mutex_unlock(&sp->lock);
1262 :
1263 0 : return r;
1264 : }
1265 :
1266 0 : int shared_policy_preload(SharedPolicy *sp, char **configuration) {
1267 0 : _cleanup_strv_free_ char **conf = NULL;
1268 0 : int r = 0;
1269 :
1270 0 : assert(sp);
1271 :
1272 0 : conf = strv_copy(configuration);
1273 0 : if (!conf)
1274 0 : return log_oom();
1275 :
1276 0 : pthread_mutex_lock(&sp->lock);
1277 0 : if (!sp->policy) {
1278 0 : r = shared_policy_reload_unlocked(sp, conf);
1279 0 : if (r >= 0) {
1280 0 : sp->configuration = conf;
1281 0 : conf = NULL;
1282 : }
1283 : }
1284 0 : pthread_mutex_unlock(&sp->lock);
1285 :
1286 0 : return r;
1287 : }
1288 :
1289 0 : Policy *shared_policy_acquire(SharedPolicy *sp) {
1290 0 : assert(sp);
1291 :
1292 0 : pthread_rwlock_rdlock(&sp->rwlock);
1293 0 : if (sp->policy)
1294 0 : return sp->policy;
1295 0 : pthread_rwlock_unlock(&sp->rwlock);
1296 :
1297 0 : return NULL;
1298 : }
1299 :
1300 0 : void shared_policy_release(SharedPolicy *sp, Policy *p) {
1301 0 : assert(sp);
1302 0 : assert(!p || sp->policy == p);
1303 :
1304 0 : if (p)
1305 0 : pthread_rwlock_unlock(&sp->rwlock);
1306 0 : }
1307 :
1308 : static const char* const policy_item_type_table[_POLICY_ITEM_TYPE_MAX] = {
1309 : [_POLICY_ITEM_TYPE_UNSET] = "unset",
1310 : [POLICY_ITEM_ALLOW] = "allow",
1311 : [POLICY_ITEM_DENY] = "deny",
1312 : };
1313 73 : DEFINE_STRING_TABLE_LOOKUP(policy_item_type, PolicyItemType);
1314 :
1315 : static const char* const policy_item_class_table[_POLICY_ITEM_CLASS_MAX] = {
1316 : [_POLICY_ITEM_CLASS_UNSET] = "unset",
1317 : [POLICY_ITEM_SEND] = "send",
1318 : [POLICY_ITEM_RECV] = "recv",
1319 : [POLICY_ITEM_OWN] = "own",
1320 : [POLICY_ITEM_OWN_PREFIX] = "own-prefix",
1321 : [POLICY_ITEM_USER] = "user",
1322 : [POLICY_ITEM_GROUP] = "group",
1323 : [POLICY_ITEM_IGNORE] = "ignore",
1324 : };
1325 83 : DEFINE_STRING_TABLE_LOOKUP(policy_item_class, PolicyItemClass);
|