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-message.h"
28 #include "bus-xml-policy.h"
30 static void policy_item_free(PolicyItem *i) {
41 DEFINE_TRIVIAL_CLEANUP_FUNC(PolicyItem*, policy_item_free);
43 static void item_append(PolicyItem *i, PolicyItem **list) {
47 LIST_FIND_TAIL(items, *list, tail);
48 LIST_INSERT_AFTER(items, *list, tail, i);
51 static int file_load(Policy *p, const char *path) {
53 _cleanup_free_ char *c = NULL, *policy_user = NULL, *policy_group = NULL;
54 _cleanup_(policy_item_freep) PolicyItem *i = NULL;
55 void *xml_state = NULL;
67 STATE_POLICY_OTHER_ATTRIBUTE,
69 STATE_ALLOW_DENY_INTERFACE,
70 STATE_ALLOW_DENY_MEMBER,
71 STATE_ALLOW_DENY_ERROR,
72 STATE_ALLOW_DENY_PATH,
73 STATE_ALLOW_DENY_MESSAGE_TYPE,
74 STATE_ALLOW_DENY_NAME,
75 STATE_ALLOW_DENY_OTHER_ATTRIBUTE,
77 } state = STATE_OUTSIDE;
81 POLICY_CATEGORY_DEFAULT,
82 POLICY_CATEGORY_MANDATORY,
85 } policy_category = POLICY_CATEGORY_NONE;
91 r = read_full_file(path, &c, NULL);
98 return log_error_errno(r, "Failed to load %s: %m", path);
103 _cleanup_free_ char *name = NULL;
106 t = xml_tokenize(&q, &name, &xml_state, &line);
108 return log_error_errno(t, "XML parse failure in %s: %m", path);
114 if (t == XML_TAG_OPEN) {
115 if (streq(name, "busconfig"))
116 state = STATE_BUSCONFIG;
118 log_error("Unexpected tag %s at %s:%u.", name, path, line);
122 } else if (t == XML_END)
124 else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
125 log_error("Unexpected token (1) at %s:%u.", path, line);
131 case STATE_BUSCONFIG:
133 if (t == XML_TAG_OPEN) {
134 if (streq(name, "policy")) {
135 state = STATE_POLICY;
136 policy_category = POLICY_CATEGORY_NONE;
139 policy_user = policy_group = NULL;
144 } else if (t == XML_TAG_CLOSE_EMPTY ||
145 (t == XML_TAG_CLOSE && streq(name, "busconfig")))
146 state = STATE_OUTSIDE;
147 else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
148 log_error("Unexpected token (2) at %s:%u.", path, line);
156 if (t == XML_ATTRIBUTE_NAME) {
157 if (streq(name, "context"))
158 state = STATE_POLICY_CONTEXT;
159 else if (streq(name, "user"))
160 state = STATE_POLICY_USER;
161 else if (streq(name, "group"))
162 state = STATE_POLICY_GROUP;
164 if (streq(name, "at_console"))
165 log_debug("Attribute %s of <policy> tag unsupported at %s:%u, ignoring.", name, path, line);
167 log_warning("Attribute %s of <policy> tag unknown at %s:%u, ignoring.", name, path, line);
168 state = STATE_POLICY_OTHER_ATTRIBUTE;
170 } else if (t == XML_TAG_CLOSE_EMPTY ||
171 (t == XML_TAG_CLOSE && streq(name, "policy")))
172 state = STATE_BUSCONFIG;
173 else if (t == XML_TAG_OPEN) {
176 if (streq(name, "allow"))
177 it = POLICY_ITEM_ALLOW;
178 else if (streq(name, "deny"))
179 it = POLICY_ITEM_DENY;
181 log_warning("Unknown tag %s in <policy> %s:%u.", name, path, line);
186 i = new0(PolicyItem, 1);
191 state = STATE_ALLOW_DENY;
193 } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
194 log_error("Unexpected token (3) at %s:%u.", path, line);
200 case STATE_POLICY_CONTEXT:
202 if (t == XML_ATTRIBUTE_VALUE) {
203 if (streq(name, "default")) {
204 policy_category = POLICY_CATEGORY_DEFAULT;
205 state = STATE_POLICY;
206 } else if (streq(name, "mandatory")) {
207 policy_category = POLICY_CATEGORY_MANDATORY;
208 state = STATE_POLICY;
210 log_error("context= parameter %s unknown for <policy> at %s:%u.", name, path, line);
214 log_error("Unexpected token (4) at %s:%u.", path, line);
220 case STATE_POLICY_USER:
222 if (t == XML_ATTRIBUTE_VALUE) {
226 policy_category = POLICY_CATEGORY_USER;
227 state = STATE_POLICY;
229 log_error("Unexpected token (5) in %s:%u.", path, line);
235 case STATE_POLICY_GROUP:
237 if (t == XML_ATTRIBUTE_VALUE) {
241 policy_category = POLICY_CATEGORY_GROUP;
242 state = STATE_POLICY;
244 log_error("Unexpected token (6) at %s:%u.", path, line);
250 case STATE_POLICY_OTHER_ATTRIBUTE:
252 if (t == XML_ATTRIBUTE_VALUE)
253 state = STATE_POLICY;
255 log_error("Unexpected token (7) in %s:%u.", path, line);
261 case STATE_ALLOW_DENY:
265 if (t == XML_ATTRIBUTE_NAME) {
268 if (startswith(name, "send_"))
269 ic = POLICY_ITEM_SEND;
270 else if (startswith(name, "receive_"))
271 ic = POLICY_ITEM_RECV;
272 else if (streq(name, "own"))
273 ic = POLICY_ITEM_OWN;
274 else if (streq(name, "own_prefix"))
275 ic = POLICY_ITEM_OWN_PREFIX;
276 else if (streq(name, "user"))
277 ic = POLICY_ITEM_USER;
278 else if (streq(name, "group"))
279 ic = POLICY_ITEM_GROUP;
280 else if (streq(name, "eavesdrop")) {
281 log_debug("Unsupported attribute %s= at %s:%u, ignoring.", name, path, line);
282 state = STATE_ALLOW_DENY_OTHER_ATTRIBUTE;
285 log_error("Unknown attribute %s= at %s:%u, ignoring.", name, path, line);
286 state = STATE_ALLOW_DENY_OTHER_ATTRIBUTE;
290 if (i->class != _POLICY_ITEM_CLASS_UNSET && ic != i->class) {
291 log_error("send_, receive_/eavesdrop fields mixed on same tag at %s:%u.", path, line);
297 if (ic == POLICY_ITEM_SEND || ic == POLICY_ITEM_RECV) {
300 u = strchr(name, '_');
305 if (streq(u, "interface"))
306 state = STATE_ALLOW_DENY_INTERFACE;
307 else if (streq(u, "member"))
308 state = STATE_ALLOW_DENY_MEMBER;
309 else if (streq(u, "error"))
310 state = STATE_ALLOW_DENY_ERROR;
311 else if (streq(u, "path"))
312 state = STATE_ALLOW_DENY_PATH;
313 else if (streq(u, "type"))
314 state = STATE_ALLOW_DENY_MESSAGE_TYPE;
315 else if ((streq(u, "destination") && ic == POLICY_ITEM_SEND) ||
316 (streq(u, "sender") && ic == POLICY_ITEM_RECV))
317 state = STATE_ALLOW_DENY_NAME;
319 if (streq(u, "requested_reply"))
320 log_debug("Unsupported attribute %s= at %s:%u, ignoring.", name, path, line);
322 log_error("Unknown attribute %s= at %s:%u, ignoring.", name, path, line);
323 state = STATE_ALLOW_DENY_OTHER_ATTRIBUTE;
327 state = STATE_ALLOW_DENY_NAME;
329 } else if (t == XML_TAG_CLOSE_EMPTY ||
330 (t == XML_TAG_CLOSE && streq(name, i->type == POLICY_ITEM_ALLOW ? "allow" : "deny"))) {
332 /* If the tag is fully empty so far, we consider it a recv */
333 if (i->class == _POLICY_ITEM_CLASS_UNSET)
334 i->class = POLICY_ITEM_RECV;
336 if (policy_category == POLICY_CATEGORY_DEFAULT)
337 item_append(i, &p->default_items);
338 else if (policy_category == POLICY_CATEGORY_MANDATORY)
339 item_append(i, &p->mandatory_items);
340 else if (policy_category == POLICY_CATEGORY_USER) {
341 const char *u = policy_user;
343 assert_cc(sizeof(uid_t) == sizeof(uint32_t));
345 r = hashmap_ensure_allocated(&p->user_items, NULL);
350 log_error("User policy without name");
354 r = get_user_creds(&u, &i->uid, NULL, NULL, NULL);
356 log_error_errno(r, "Failed to resolve user %s, ignoring policy: %m", u);
361 first = hashmap_get(p->user_items, UINT32_TO_PTR(i->uid));
362 item_append(i, &first);
365 r = hashmap_replace(p->user_items, UINT32_TO_PTR(i->uid), first);
367 LIST_REMOVE(items, first, i);
372 } else if (policy_category == POLICY_CATEGORY_GROUP) {
373 const char *g = policy_group;
375 assert_cc(sizeof(gid_t) == sizeof(uint32_t));
377 r = hashmap_ensure_allocated(&p->group_items, NULL);
382 log_error("Group policy without name");
386 r = get_group_creds(&g, &i->gid);
388 log_error_errno(r, "Failed to resolve group %s, ignoring policy: %m", g);
393 first = hashmap_get(p->group_items, UINT32_TO_PTR(i->gid));
394 item_append(i, &first);
397 r = hashmap_replace(p->group_items, UINT32_TO_PTR(i->gid), first);
399 LIST_REMOVE(items, first, i);
405 state = STATE_POLICY;
408 } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
409 log_error("Unexpected token (8) at %s:%u.", path, line);
415 case STATE_ALLOW_DENY_INTERFACE:
417 if (t == XML_ATTRIBUTE_VALUE) {
420 log_error("Duplicate interface at %s:%u.", path, line);
426 state = STATE_ALLOW_DENY;
428 log_error("Unexpected token (9) at %s:%u.", path, line);
434 case STATE_ALLOW_DENY_MEMBER:
436 if (t == XML_ATTRIBUTE_VALUE) {
439 log_error("Duplicate member in %s:%u.", path, line);
445 state = STATE_ALLOW_DENY;
447 log_error("Unexpected token (10) in %s:%u.", path, line);
453 case STATE_ALLOW_DENY_ERROR:
455 if (t == XML_ATTRIBUTE_VALUE) {
458 log_error("Duplicate error in %s:%u.", path, line);
464 state = STATE_ALLOW_DENY;
466 log_error("Unexpected token (11) in %s:%u.", path, line);
472 case STATE_ALLOW_DENY_PATH:
474 if (t == XML_ATTRIBUTE_VALUE) {
477 log_error("Duplicate path in %s:%u.", path, line);
483 state = STATE_ALLOW_DENY;
485 log_error("Unexpected token (12) in %s:%u.", path, line);
491 case STATE_ALLOW_DENY_MESSAGE_TYPE:
493 if (t == XML_ATTRIBUTE_VALUE) {
496 if (i->message_type != 0) {
497 log_error("Duplicate message type in %s:%u.", path, line);
501 r = bus_message_type_from_string(name, &i->message_type);
503 log_error("Invalid message type in %s:%u.", path, line);
507 state = STATE_ALLOW_DENY;
509 log_error("Unexpected token (13) in %s:%u.", path, line);
515 case STATE_ALLOW_DENY_NAME:
517 if (t == XML_ATTRIBUTE_VALUE) {
520 log_error("Duplicate name in %s:%u.", path, line);
525 case POLICY_ITEM_USER:
526 if (!streq(name, "*")) {
527 const char *u = name;
529 r = get_user_creds(&u, &i->uid, NULL, NULL, NULL);
531 log_error_errno(r, "Failed to resolve user %s: %m", name);
536 case POLICY_ITEM_GROUP:
537 if (!streq(name, "*")) {
538 const char *g = name;
540 r = get_group_creds(&g, &i->gid);
542 log_error_errno(r, "Failed to resolve group %s: %m", name);
554 state = STATE_ALLOW_DENY;
556 log_error("Unexpected token (14) in %s:%u.", path, line);
562 case STATE_ALLOW_DENY_OTHER_ATTRIBUTE:
564 if (t == XML_ATTRIBUTE_VALUE)
565 state = STATE_ALLOW_DENY;
567 log_error("Unexpected token (15) in %s:%u.", path, line);
575 if (t == XML_TAG_OPEN)
577 else if (t == XML_TAG_CLOSE || t == XML_TAG_CLOSE_EMPTY) {
580 state = STATE_BUSCONFIG;
596 static const char *verdict_to_string(int v) {
610 struct policy_check_filter {
611 PolicyItemClass class;
616 const char *interface;
621 static int is_permissive(PolicyItem *i) {
625 return (i->type == POLICY_ITEM_ALLOW) ? ALLOW : DENY;
628 static int check_policy_item(PolicyItem *i, const struct policy_check_filter *filter) {
634 case POLICY_ITEM_SEND:
635 case POLICY_ITEM_RECV:
637 if (i->name && !streq_ptr(i->name, filter->name))
640 if ((i->message_type != 0) && (i->message_type != filter->message_type))
643 if (i->path && !streq_ptr(i->path, filter->path))
646 if (i->member && !streq_ptr(i->member, filter->member))
649 if (i->interface && !streq_ptr(i->interface, filter->interface))
652 return is_permissive(i);
654 case POLICY_ITEM_OWN:
655 assert(filter->name);
657 if (streq(i->name, "*") || streq(i->name, filter->name))
658 return is_permissive(i);
661 case POLICY_ITEM_OWN_PREFIX:
662 assert(filter->name);
664 if (streq(i->name, "*") || service_name_startswith(filter->name, i->name))
665 return is_permissive(i);
668 case POLICY_ITEM_USER:
669 if (filter->uid != UID_INVALID)
670 if ((streq_ptr(i->name, "*") || (i->uid_valid && i->uid == filter->uid)))
671 return is_permissive(i);
674 case POLICY_ITEM_GROUP:
675 if (filter->gid != GID_INVALID)
676 if ((streq_ptr(i->name, "*") || (i->gid_valid && i->gid == filter->gid)))
677 return is_permissive(i);
680 case POLICY_ITEM_IGNORE:
688 static int check_policy_items(PolicyItem *items, const struct policy_check_filter *filter) {
695 /* Check all policies in a set - a broader one might be followed by a more specific one,
696 * and the order of rules in policy definitions matters */
697 LIST_FOREACH(items, i, items) {
700 if (i->class != filter->class &&
701 !(i->class == POLICY_ITEM_OWN_PREFIX && filter->class == POLICY_ITEM_OWN))
704 v = check_policy_item(i, filter);
712 static int policy_check(Policy *p, const struct policy_check_filter *filter) {
720 assert(IN_SET(filter->class, POLICY_ITEM_SEND, POLICY_ITEM_RECV, POLICY_ITEM_OWN, POLICY_ITEM_USER, POLICY_ITEM_GROUP));
723 * The policy check is implemented by the following logic:
725 * 1. Check default items
726 * 2. Check group items
727 * 3. Check user items
728 * 4. Check mandatory items
730 * Later rules override earlier rules.
733 verdict = check_policy_items(p->default_items, filter);
735 if (filter->gid != GID_INVALID) {
736 items = hashmap_get(p->group_items, UINT32_TO_PTR(filter->gid));
738 v = check_policy_items(items, filter);
744 if (filter->uid != UID_INVALID) {
745 items = hashmap_get(p->user_items, UINT32_TO_PTR(filter->uid));
747 v = check_policy_items(items, filter);
753 v = check_policy_items(p->mandatory_items, filter);
760 bool policy_check_own(Policy *p, uid_t uid, gid_t gid, const char *name) {
762 struct policy_check_filter filter = {
763 .class = POLICY_ITEM_OWN,
774 verdict = policy_check(p, &filter);
776 log_full(LOG_AUTH | (verdict != ALLOW ? LOG_WARNING : LOG_DEBUG),
777 "Ownership permission check for uid=" UID_FMT " gid=" GID_FMT" name=%s: %s",
778 uid, gid, strna(name), strna(verdict_to_string(verdict)));
780 return verdict == ALLOW;
783 bool policy_check_hello(Policy *p, uid_t uid, gid_t gid) {
785 struct policy_check_filter filter = {
793 filter.class = POLICY_ITEM_USER;
794 verdict = policy_check(p, &filter);
796 if (verdict != DENY) {
799 filter.class = POLICY_ITEM_GROUP;
800 v = policy_check(p, &filter);
805 log_full(LOG_AUTH | (verdict != ALLOW ? LOG_WARNING : LOG_DEBUG),
806 "Hello permission check for uid=" UID_FMT " gid=" GID_FMT": %s",
807 uid, gid, strna(verdict_to_string(verdict)));
809 return verdict == ALLOW;
812 bool policy_check_recv(Policy *p,
818 const char *interface,
819 const char *member) {
821 struct policy_check_filter filter = {
822 .class = POLICY_ITEM_RECV,
825 .message_type = message_type,
827 .interface = interface,
836 verdict = policy_check(p, &filter);
838 log_full(LOG_AUTH | (verdict != ALLOW ? LOG_WARNING : LOG_DEBUG),
839 "Receive permission check for uid=" UID_FMT " gid=" GID_FMT" message=%s name=%s interface=%s path=%s member=%s: %s",
840 uid, gid, bus_message_type_to_string(message_type), strna(name), strna(path), strna(interface), strna(member), strna(verdict_to_string(verdict)));
842 return verdict == ALLOW;
845 bool policy_check_send(Policy *p,
851 const char *interface,
852 const char *member) {
854 struct policy_check_filter filter = {
855 .class = POLICY_ITEM_SEND,
858 .message_type = message_type,
860 .interface = interface,
869 verdict = policy_check(p, &filter);
871 log_full(LOG_AUTH | (verdict != ALLOW ? LOG_WARNING : LOG_DEBUG),
872 "Send permission check for uid=" UID_FMT " gid=" GID_FMT" message=%s name=%s interface=%s path=%s member=%s: %s",
873 uid, gid, bus_message_type_to_string(message_type), strna(name), strna(path), strna(interface), strna(member), strna(verdict_to_string(verdict)));
875 return verdict == ALLOW;
878 int policy_load(Policy *p, char **files) {
884 STRV_FOREACH(i, files) {
886 r = file_load(p, *i);
888 _cleanup_strv_free_ char **l = NULL;
891 r = conf_files_list(&l, ".conf", NULL, *i, NULL);
893 return log_error_errno(r, "Failed to get configuration file list: %m");
899 /* We ignore all errors but EISDIR, and just proceed. */
905 void policy_free(Policy *p) {
906 PolicyItem *i, *first;
911 while ((i = p->default_items)) {
912 LIST_REMOVE(items, p->default_items, i);
916 while ((i = p->mandatory_items)) {
917 LIST_REMOVE(items, p->mandatory_items, i);
921 while ((first = hashmap_steal_first(p->user_items))) {
923 while ((i = first)) {
924 LIST_REMOVE(items, first, i);
929 while ((first = hashmap_steal_first(p->group_items))) {
931 while ((i = first)) {
932 LIST_REMOVE(items, first, i);
937 hashmap_free(p->user_items);
938 hashmap_free(p->group_items);
940 p->user_items = p->group_items = NULL;
943 static void dump_items(PolicyItem *items, const char *prefix) {
953 LIST_FOREACH(items, i, items) {
955 printf("%sType: %s\n"
957 prefix, policy_item_type_to_string(i->type),
958 prefix, policy_item_class_to_string(i->class));
961 printf("%sInterface: %s\n",
962 prefix, i->interface);
965 printf("%sMember: %s\n",
969 printf("%sError: %s\n",
973 printf("%sPath: %s\n",
977 printf("%sName: %s\n",
980 if (i->message_type != 0)
981 printf("%sMessage Type: %s\n",
982 prefix, bus_message_type_to_string(i->message_type));
985 _cleanup_free_ char *user;
987 user = uid_to_name(i->uid);
989 printf("%sUser: %s (%d)\n",
990 prefix, strna(user), i->uid);
994 _cleanup_free_ char *group;
996 group = gid_to_name(i->gid);
998 printf("%sGroup: %s (%d)\n",
999 prefix, strna(group), i->gid);
1001 printf("%s-\n", prefix);
1005 static void dump_hashmap_items(Hashmap *h) {
1010 HASHMAP_FOREACH_KEY(i, k, h, j) {
1011 printf("\t%s Item for %u:\n", draw_special_char(DRAW_ARROW), PTR_TO_UINT(k));
1012 dump_items(i, "\t\t");
1016 void policy_dump(Policy *p) {
1018 printf("%s Default Items:\n", draw_special_char(DRAW_ARROW));
1019 dump_items(p->default_items, "\t");
1021 printf("%s Group Items:\n", draw_special_char(DRAW_ARROW));
1022 dump_hashmap_items(p->group_items);
1024 printf("%s User Items:\n", draw_special_char(DRAW_ARROW));
1025 dump_hashmap_items(p->user_items);
1027 printf("%s Mandatory Items:\n", draw_special_char(DRAW_ARROW));
1028 dump_items(p->mandatory_items, "\t");
1031 static const char* const policy_item_type_table[_POLICY_ITEM_TYPE_MAX] = {
1032 [_POLICY_ITEM_TYPE_UNSET] = "unset",
1033 [POLICY_ITEM_ALLOW] = "allow",
1034 [POLICY_ITEM_DENY] = "deny",
1036 DEFINE_STRING_TABLE_LOOKUP(policy_item_type, PolicyItemType);
1038 static const char* const policy_item_class_table[_POLICY_ITEM_CLASS_MAX] = {
1039 [_POLICY_ITEM_CLASS_UNSET] = "unset",
1040 [POLICY_ITEM_SEND] = "send",
1041 [POLICY_ITEM_RECV] = "recv",
1042 [POLICY_ITEM_OWN] = "own",
1043 [POLICY_ITEM_OWN_PREFIX] = "own-prefix",
1044 [POLICY_ITEM_USER] = "user",
1045 [POLICY_ITEM_GROUP] = "group",
1046 [POLICY_ITEM_IGNORE] = "ignore",
1048 DEFINE_STRING_TABLE_LOOKUP(policy_item_class, PolicyItemClass);