1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Lennart Poettering
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.
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.
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/>.
25 #include "conf-files.h"
26 #include "bus-internal.h"
27 #include "bus-policy.h"
29 static void policy_item_free(PolicyItem *i) {
40 DEFINE_TRIVIAL_CLEANUP_FUNC(PolicyItem*, policy_item_free);
42 static int file_load(Policy *p, const char *path) {
44 _cleanup_free_ char *c = NULL, *policy_user = NULL, *policy_group = NULL;
45 _cleanup_(policy_item_freep) PolicyItem *i = NULL;
46 void *xml_state = NULL;
58 STATE_POLICY_OTHER_ATTRIBUTE,
60 STATE_ALLOW_DENY_INTERFACE,
61 STATE_ALLOW_DENY_MEMBER,
62 STATE_ALLOW_DENY_ERROR,
63 STATE_ALLOW_DENY_PATH,
64 STATE_ALLOW_DENY_MESSAGE_TYPE,
65 STATE_ALLOW_DENY_NAME,
66 STATE_ALLOW_DENY_OTHER_ATTRIBUTE,
68 } state = STATE_OUTSIDE;
72 POLICY_CATEGORY_DEFAULT,
73 POLICY_CATEGORY_MANDATORY,
76 } policy_category = POLICY_CATEGORY_NONE;
82 r = read_full_file(path, &c, NULL);
89 log_error("Failed to load %s: %s", path, strerror(-r));
95 _cleanup_free_ char *name = NULL;
98 t = xml_tokenize(&q, &name, &xml_state, &line);
100 log_error("XML parse failure in %s: %s", path, strerror(-t));
108 if (t == XML_TAG_OPEN) {
109 if (streq(name, "busconfig"))
110 state = STATE_BUSCONFIG;
112 log_error("Unexpected tag %s at %s:%u.", name, path, line);
116 } else if (t == XML_END)
118 else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
119 log_error("Unexpected token (1) at %s:%u.", path, line);
125 case STATE_BUSCONFIG:
127 if (t == XML_TAG_OPEN) {
128 if (streq(name, "policy")) {
129 state = STATE_POLICY;
130 policy_category = POLICY_CATEGORY_NONE;
133 policy_user = policy_group = NULL;
138 } else if (t == XML_TAG_CLOSE_EMPTY ||
139 (t == XML_TAG_CLOSE && streq(name, "busconfig")))
140 state = STATE_OUTSIDE;
141 else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
142 log_error("Unexpected token (2) at %s:%u.", path, line);
150 if (t == XML_ATTRIBUTE_NAME) {
151 if (streq(name, "context"))
152 state = STATE_POLICY_CONTEXT;
153 else if (streq(name, "user"))
154 state = STATE_POLICY_USER;
155 else if (streq(name, "group"))
156 state = STATE_POLICY_GROUP;
158 if (streq(name, "at_console"))
159 log_debug("Attribute %s of <policy> tag unsupported at %s:%u, ignoring.", name, path, line);
161 log_warning("Attribute %s of <policy> tag unknown at %s:%u, ignoring.", name, path, line);
162 state = STATE_POLICY_OTHER_ATTRIBUTE;
164 } else if (t == XML_TAG_CLOSE_EMPTY ||
165 (t == XML_TAG_CLOSE && streq(name, "policy")))
166 state = STATE_BUSCONFIG;
167 else if (t == XML_TAG_OPEN) {
170 if (streq(name, "allow"))
171 it = POLICY_ITEM_ALLOW;
172 else if (streq(name, "deny"))
173 it = POLICY_ITEM_DENY;
175 log_warning("Unknown tag %s in <policy> %s:%u.", name, path, line);
180 i = new0(PolicyItem, 1);
185 state = STATE_ALLOW_DENY;
187 } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
188 log_error("Unexpected token (3) at %s:%u.", path, line);
194 case STATE_POLICY_CONTEXT:
196 if (t == XML_ATTRIBUTE_VALUE) {
197 if (streq(name, "default")) {
198 policy_category = POLICY_CATEGORY_DEFAULT;
199 state = STATE_POLICY;
200 } else if (streq(name, "mandatory")) {
201 policy_category = POLICY_CATEGORY_MANDATORY;
202 state = STATE_POLICY;
204 log_error("context= parameter %s unknown for <policy> at %s:%u.", name, path, line);
208 log_error("Unexpected token (4) at %s:%u.", path, line);
214 case STATE_POLICY_USER:
216 if (t == XML_ATTRIBUTE_VALUE) {
220 policy_category = POLICY_CATEGORY_USER;
221 state = STATE_POLICY;
223 log_error("Unexpected token (5) in %s:%u.", path, line);
229 case STATE_POLICY_GROUP:
231 if (t == XML_ATTRIBUTE_VALUE) {
235 policy_category = POLICY_CATEGORY_GROUP;
236 state = STATE_POLICY;
238 log_error("Unexpected token (6) at %s:%u.", path, line);
244 case STATE_POLICY_OTHER_ATTRIBUTE:
246 if (t == XML_ATTRIBUTE_VALUE)
247 state = STATE_POLICY;
249 log_error("Unexpected token (7) in %s:%u.", path, line);
255 case STATE_ALLOW_DENY:
259 if (t == XML_ATTRIBUTE_NAME) {
262 if (startswith(name, "send_"))
263 ic = POLICY_ITEM_SEND;
264 else if (startswith(name, "receive_"))
265 ic = POLICY_ITEM_RECV;
266 else if (streq(name, "own"))
267 ic = POLICY_ITEM_OWN;
268 else if (streq(name, "own_prefix"))
269 ic = POLICY_ITEM_OWN_PREFIX;
270 else if (streq(name, "user"))
271 ic = POLICY_ITEM_USER;
272 else if (streq(name, "group"))
273 ic = POLICY_ITEM_GROUP;
274 else if (streq(name, "eavesdrop")) {
275 log_debug("Unsupported attribute %s= at %s:%u, ignoring.", name, path, line);
276 i->class = POLICY_ITEM_IGNORE;
277 state = STATE_ALLOW_DENY_OTHER_ATTRIBUTE;
280 log_error("Unknown attribute %s= at %s:%u, ignoring.", name, path, line);
281 state = STATE_ALLOW_DENY_OTHER_ATTRIBUTE;
285 if (i->class != _POLICY_ITEM_CLASS_UNSET && ic != i->class) {
286 log_error("send_ and receive_ fields mixed on same tag at %s:%u.", path, line);
292 if (ic == POLICY_ITEM_SEND || ic == POLICY_ITEM_RECV) {
295 u = strchr(name, '_');
300 if (streq(u, "interface"))
301 state = STATE_ALLOW_DENY_INTERFACE;
302 else if (streq(u, "member"))
303 state = STATE_ALLOW_DENY_MEMBER;
304 else if (streq(u, "error"))
305 state = STATE_ALLOW_DENY_ERROR;
306 else if (streq(u, "path"))
307 state = STATE_ALLOW_DENY_PATH;
308 else if (streq(u, "type"))
309 state = STATE_ALLOW_DENY_MESSAGE_TYPE;
310 else if ((streq(u, "destination") && ic == POLICY_ITEM_SEND) ||
311 (streq(u, "sender") && ic == POLICY_ITEM_RECV))
312 state = STATE_ALLOW_DENY_NAME;
314 if (streq(u, "requested_reply"))
315 log_debug("Unsupported attribute %s= at %s:%u, ignoring.", name, path, line);
317 log_error("Unknown attribute %s= at %s:%u, ignoring.", name, path, line);
318 state = STATE_ALLOW_DENY_OTHER_ATTRIBUTE;
322 state = STATE_ALLOW_DENY_NAME;
324 } else if (t == XML_TAG_CLOSE_EMPTY ||
325 (t == XML_TAG_CLOSE && streq(name, i->type == POLICY_ITEM_ALLOW ? "allow" : "deny"))) {
327 if (i->class == _POLICY_ITEM_CLASS_UNSET) {
328 log_error("Policy not set at %s:%u.", path, line);
332 if (policy_category == POLICY_CATEGORY_DEFAULT)
333 LIST_PREPEND(items, p->default_items, i);
334 else if (policy_category == POLICY_CATEGORY_MANDATORY)
335 LIST_PREPEND(items, p->default_items, i);
336 else if (policy_category == POLICY_CATEGORY_USER) {
337 const char *u = policy_user;
339 assert_cc(sizeof(uid_t) == sizeof(uint32_t));
341 r = hashmap_ensure_allocated(&p->user_items, trivial_hash_func, trivial_compare_func);
346 log_error("User policy without name");
350 r = get_user_creds(&u, &i->uid, NULL, NULL, NULL);
352 log_error("Failed to resolve user %s, ignoring policy: %s", u, strerror(-r));
357 first = hashmap_get(p->user_items, UINT32_TO_PTR(i->uid));
358 LIST_PREPEND(items, first, i);
360 r = hashmap_replace(p->user_items, UINT32_TO_PTR(i->uid), first);
362 LIST_REMOVE(items, first, i);
367 } else if (policy_category == POLICY_CATEGORY_GROUP) {
368 const char *g = policy_group;
370 assert_cc(sizeof(gid_t) == sizeof(uint32_t));
372 r = hashmap_ensure_allocated(&p->group_items, trivial_hash_func, trivial_compare_func);
377 log_error("Group policy without name");
381 r = get_group_creds(&g, &i->gid);
383 log_error("Failed to resolve group %s, ignoring policy: %s", g, strerror(-r));
388 first = hashmap_get(p->group_items, UINT32_TO_PTR(i->gid));
389 LIST_PREPEND(items, first, i);
391 r = hashmap_replace(p->group_items, UINT32_TO_PTR(i->gid), first);
393 LIST_REMOVE(items, first, i);
399 state = STATE_POLICY;
402 } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
403 log_error("Unexpected token (8) at %s:%u.", path, line);
409 case STATE_ALLOW_DENY_INTERFACE:
411 if (t == XML_ATTRIBUTE_VALUE) {
414 log_error("Duplicate interface at %s:%u.", path, line);
420 state = STATE_ALLOW_DENY;
422 log_error("Unexpected token (9) at %s:%u.", path, line);
428 case STATE_ALLOW_DENY_MEMBER:
430 if (t == XML_ATTRIBUTE_VALUE) {
433 log_error("Duplicate member in %s:%u.", path, line);
439 state = STATE_ALLOW_DENY;
441 log_error("Unexpected token (10) in %s:%u.", path, line);
447 case STATE_ALLOW_DENY_ERROR:
449 if (t == XML_ATTRIBUTE_VALUE) {
452 log_error("Duplicate error in %s:%u.", path, line);
458 state = STATE_ALLOW_DENY;
460 log_error("Unexpected token (11) in %s:%u.", path, line);
466 case STATE_ALLOW_DENY_PATH:
468 if (t == XML_ATTRIBUTE_VALUE) {
471 log_error("Duplicate path in %s:%u.", path, line);
477 state = STATE_ALLOW_DENY;
479 log_error("Unexpected token (12) in %s:%u.", path, line);
485 case STATE_ALLOW_DENY_MESSAGE_TYPE:
487 if (t == XML_ATTRIBUTE_VALUE) {
490 if (i->message_type != 0) {
491 log_error("Duplicate message type in %s:%u.", path, line);
495 r = bus_message_type_from_string(name, &i->message_type);
497 log_error("Invalid message type in %s:%u.", path, line);
501 state = STATE_ALLOW_DENY;
503 log_error("Unexpected token (13) in %s:%u.", path, line);
509 case STATE_ALLOW_DENY_NAME:
511 if (t == XML_ATTRIBUTE_VALUE) {
514 log_error("Duplicate name in %s:%u.", path, line);
520 state = STATE_ALLOW_DENY;
522 log_error("Unexpected token (14) in %s:%u.", path, line);
528 case STATE_ALLOW_DENY_OTHER_ATTRIBUTE:
530 if (t == XML_ATTRIBUTE_VALUE)
531 state = STATE_ALLOW_DENY;
533 log_error("Unexpected token (15) in %s:%u.", path, line);
541 if (t == XML_TAG_OPEN)
543 else if (t == XML_TAG_CLOSE || t == XML_TAG_CLOSE_EMPTY) {
546 state = STATE_BUSCONFIG;
556 int policy_load(Policy *p, char **files) {
562 STRV_FOREACH(i, files) {
564 r = file_load(p, *i);
566 _cleanup_strv_free_ char **l = NULL;
569 r = conf_files_list(&l, ".conf", NULL, *i, NULL);
571 log_error("Failed to get configuration file list: %s", strerror(-r));
579 /* We ignore all errors but EISDIR, and just proceed. */
585 void policy_free(Policy *p) {
586 PolicyItem *i, *first;
591 while ((i = p->default_items)) {
592 LIST_REMOVE(items, p->default_items, i);
596 while ((i = p->mandatory_items)) {
597 LIST_REMOVE(items, p->mandatory_items, i);
601 while ((first = hashmap_steal_first(p->user_items))) {
603 while ((i = first)) {
604 LIST_REMOVE(items, first, i);
609 while ((first = hashmap_steal_first(p->group_items))) {
611 while ((i = first)) {
612 LIST_REMOVE(items, first, i);
617 hashmap_free(p->user_items);
618 hashmap_free(p->group_items);
620 p->user_items = p->group_items = NULL;
623 static void dump_items(PolicyItem *i, const char *prefix) {
631 printf("%sType: %s\n"
633 prefix, policy_item_type_to_string(i->type),
634 prefix, policy_item_class_to_string(i->class));
637 printf("%sInterface: %s\n",
638 prefix, i->interface);
641 printf("%sMember: %s\n",
645 printf("%sError: %s\n",
649 printf("%sPath: %s\n",
653 printf("%sName: %s\n",
656 if (i->message_type != 0)
657 printf("%sMessage Type: %s\n",
658 prefix, bus_message_type_to_string(i->message_type));
661 _cleanup_free_ char *user;
663 user = uid_to_name(i->uid);
665 printf("%sUser: %s\n",
666 prefix, strna(user));
670 _cleanup_free_ char *group;
672 group = gid_to_name(i->gid);
674 printf("%sGroup: %s\n",
675 prefix, strna(group));
679 printf("%s%s\n", prefix, draw_special_char(DRAW_DASH));
680 dump_items(i->items_next, prefix);
684 static void dump_hashmap_items(Hashmap *h) {
689 HASHMAP_FOREACH_KEY(i, k, h, j) {
690 printf("\t%s Item for %u:\n", draw_special_char(DRAW_ARROW), PTR_TO_UINT(k));
691 dump_items(i, "\t\t");
695 noreturn void policy_dump(Policy *p) {
697 printf("%s Default Items:\n", draw_special_char(DRAW_ARROW));
698 dump_items(p->default_items, "\t");
700 printf("%s Group Items:\n", draw_special_char(DRAW_ARROW));
701 dump_hashmap_items(p->group_items);
703 printf("%s User Items:\n", draw_special_char(DRAW_ARROW));
704 dump_hashmap_items(p->user_items);
706 printf("%s Mandatory Items:\n", draw_special_char(DRAW_ARROW));
707 dump_items(p->mandatory_items, "\t");
712 static const char* const policy_item_type_table[_POLICY_ITEM_TYPE_MAX] = {
713 [_POLICY_ITEM_TYPE_UNSET] = "unset",
714 [POLICY_ITEM_ALLOW] = "allow",
715 [POLICY_ITEM_DENY] = "deny",
717 DEFINE_STRING_TABLE_LOOKUP(policy_item_type, PolicyItemType);
719 static const char* const policy_item_class_table[_POLICY_ITEM_CLASS_MAX] = {
720 [_POLICY_ITEM_CLASS_UNSET] = "unset",
721 [POLICY_ITEM_SEND] = "send",
722 [POLICY_ITEM_RECV] = "recv",
723 [POLICY_ITEM_OWN] = "own",
724 [POLICY_ITEM_OWN_PREFIX] = "own-prefix",
725 [POLICY_ITEM_USER] = "user",
726 [POLICY_ITEM_GROUP] = "group",
727 [POLICY_ITEM_IGNORE] = "ignore",
729 DEFINE_STRING_TABLE_LOOKUP(policy_item_class, PolicyItemClass);