]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/bus-proxyd/bus-policy.c
06c16a7eb6d2ca7ee29b9b4540bc22d679a43219
[thirdparty/systemd.git] / src / bus-proxyd / bus-policy.c
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 "conf-files.h"
26 #include "bus-internal.h"
27 #include "bus-policy.h"
28
29 static void policy_item_free(PolicyItem *i) {
30 assert(i);
31
32 free(i->interface);
33 free(i->member);
34 free(i->error);
35 free(i->name);
36 free(i->path);
37 free(i);
38 }
39
40 DEFINE_TRIVIAL_CLEANUP_FUNC(PolicyItem*, policy_item_free);
41
42 static int file_load(Policy *p, const char *path) {
43
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;
47 unsigned n_other = 0;
48 const char *q;
49 int r;
50
51 enum {
52 STATE_OUTSIDE,
53 STATE_BUSCONFIG,
54 STATE_POLICY,
55 STATE_POLICY_CONTEXT,
56 STATE_POLICY_USER,
57 STATE_POLICY_GROUP,
58 STATE_POLICY_OTHER_ATTRIBUTE,
59 STATE_ALLOW_DENY,
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,
67 STATE_OTHER,
68 } state = STATE_OUTSIDE;
69
70 enum {
71 POLICY_CATEGORY_NONE,
72 POLICY_CATEGORY_DEFAULT,
73 POLICY_CATEGORY_MANDATORY,
74 POLICY_CATEGORY_USER,
75 POLICY_CATEGORY_GROUP
76 } policy_category = POLICY_CATEGORY_NONE;
77
78 unsigned line = 0;
79
80 assert(p);
81
82 r = read_full_file(path, &c, NULL);
83 if (r < 0) {
84 if (r == -ENOENT)
85 return 0;
86 if (r == -EISDIR)
87 return r;
88
89 log_error("Failed to load %s: %s", path, strerror(-r));
90 return r;
91 }
92
93 q = c;
94 for (;;) {
95 _cleanup_free_ char *name = NULL;
96 int t;
97
98 t = xml_tokenize(&q, &name, &xml_state, &line);
99 if (t < 0) {
100 log_error("XML parse failure in %s: %s", path, strerror(-t));
101 return t;
102 }
103
104 switch (state) {
105
106 case STATE_OUTSIDE:
107
108 if (t == XML_TAG_OPEN) {
109 if (streq(name, "busconfig"))
110 state = STATE_BUSCONFIG;
111 else {
112 log_error("Unexpected tag %s at %s:%u.", name, path, line);
113 return -EINVAL;
114 }
115
116 } else if (t == XML_END)
117 return 0;
118 else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
119 log_error("Unexpected token (1) at %s:%u.", path, line);
120 return -EINVAL;
121 }
122
123 break;
124
125 case STATE_BUSCONFIG:
126
127 if (t == XML_TAG_OPEN) {
128 if (streq(name, "policy")) {
129 state = STATE_POLICY;
130 policy_category = POLICY_CATEGORY_NONE;
131 free(policy_user);
132 free(policy_group);
133 policy_user = policy_group = NULL;
134 } else {
135 state = STATE_OTHER;
136 n_other = 0;
137 }
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);
143 return -EINVAL;
144 }
145
146 break;
147
148 case STATE_POLICY:
149
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;
157 else {
158 if (streq(name, "at_console"))
159 log_debug("Attribute %s of <policy> tag unsupported at %s:%u, ignoring.", name, path, line);
160 else
161 log_warning("Attribute %s of <policy> tag unknown at %s:%u, ignoring.", name, path, line);
162 state = STATE_POLICY_OTHER_ATTRIBUTE;
163 }
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) {
168 PolicyItemType it;
169
170 if (streq(name, "allow"))
171 it = POLICY_ITEM_ALLOW;
172 else if (streq(name, "deny"))
173 it = POLICY_ITEM_DENY;
174 else {
175 log_warning("Unknown tag %s in <policy> %s:%u.", name, path, line);
176 return -EINVAL;
177 }
178
179 assert(!i);
180 i = new0(PolicyItem, 1);
181 if (!i)
182 return log_oom();
183
184 i->type = it;
185 state = STATE_ALLOW_DENY;
186
187 } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
188 log_error("Unexpected token (3) at %s:%u.", path, line);
189 return -EINVAL;
190 }
191
192 break;
193
194 case STATE_POLICY_CONTEXT:
195
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;
203 } else {
204 log_error("context= parameter %s unknown for <policy> at %s:%u.", name, path, line);
205 return -EINVAL;
206 }
207 } else {
208 log_error("Unexpected token (4) at %s:%u.", path, line);
209 return -EINVAL;
210 }
211
212 break;
213
214 case STATE_POLICY_USER:
215
216 if (t == XML_ATTRIBUTE_VALUE) {
217 free(policy_user);
218 policy_user = name;
219 name = NULL;
220 policy_category = POLICY_CATEGORY_USER;
221 state = STATE_POLICY;
222 } else {
223 log_error("Unexpected token (5) in %s:%u.", path, line);
224 return -EINVAL;
225 }
226
227 break;
228
229 case STATE_POLICY_GROUP:
230
231 if (t == XML_ATTRIBUTE_VALUE) {
232 free(policy_group);
233 policy_group = name;
234 name = NULL;
235 policy_category = POLICY_CATEGORY_GROUP;
236 state = STATE_POLICY;
237 } else {
238 log_error("Unexpected token (6) at %s:%u.", path, line);
239 return -EINVAL;
240 }
241
242 break;
243
244 case STATE_POLICY_OTHER_ATTRIBUTE:
245
246 if (t == XML_ATTRIBUTE_VALUE)
247 state = STATE_POLICY;
248 else {
249 log_error("Unexpected token (7) in %s:%u.", path, line);
250 return -EINVAL;
251 }
252
253 break;
254
255 case STATE_ALLOW_DENY:
256
257 assert(i);
258
259 if (t == XML_ATTRIBUTE_NAME) {
260 PolicyItemClass ic;
261
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;
278 break;
279 } else {
280 log_error("Unknown attribute %s= at %s:%u, ignoring.", name, path, line);
281 state = STATE_ALLOW_DENY_OTHER_ATTRIBUTE;
282 break;
283 }
284
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);
287 return -EINVAL;
288 }
289
290 i->class = ic;
291
292 if (ic == POLICY_ITEM_SEND || ic == POLICY_ITEM_RECV) {
293 const char *u;
294
295 u = strchr(name, '_');
296 assert(u);
297
298 u++;
299
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;
313 else {
314 if (streq(u, "requested_reply"))
315 log_debug("Unsupported attribute %s= at %s:%u, ignoring.", name, path, line);
316 else
317 log_error("Unknown attribute %s= at %s:%u, ignoring.", name, path, line);
318 state = STATE_ALLOW_DENY_OTHER_ATTRIBUTE;
319 break;
320 }
321 } else
322 state = STATE_ALLOW_DENY_NAME;
323
324 } else if (t == XML_TAG_CLOSE_EMPTY ||
325 (t == XML_TAG_CLOSE && streq(name, i->type == POLICY_ITEM_ALLOW ? "allow" : "deny"))) {
326
327 if (i->class == _POLICY_ITEM_CLASS_UNSET) {
328 log_error("Policy not set at %s:%u.", path, line);
329 return -EINVAL;
330 }
331
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;
338
339 assert_cc(sizeof(uid_t) == sizeof(uint32_t));
340
341 r = hashmap_ensure_allocated(&p->user_items, trivial_hash_func, trivial_compare_func);
342 if (r < 0)
343 return log_oom();
344
345 if (!u) {
346 log_error("User policy without name");
347 return -EINVAL;
348 }
349
350 r = get_user_creds(&u, &i->uid, NULL, NULL, NULL);
351 if (r < 0) {
352 log_error("Failed to resolve user %s, ignoring policy: %s", u, strerror(-r));
353 free(i);
354 } else {
355 PolicyItem *first;
356
357 first = hashmap_get(p->user_items, UINT32_TO_PTR(i->uid));
358 LIST_PREPEND(items, first, i);
359
360 r = hashmap_replace(p->user_items, UINT32_TO_PTR(i->uid), first);
361 if (r < 0) {
362 LIST_REMOVE(items, first, i);
363 return log_oom();
364 }
365 }
366
367 } else if (policy_category == POLICY_CATEGORY_GROUP) {
368 const char *g = policy_group;
369
370 assert_cc(sizeof(gid_t) == sizeof(uint32_t));
371
372 r = hashmap_ensure_allocated(&p->group_items, trivial_hash_func, trivial_compare_func);
373 if (r < 0)
374 return log_oom();
375
376 if (!g) {
377 log_error("Group policy without name");
378 return -EINVAL;
379 }
380
381 r = get_group_creds(&g, &i->gid);
382 if (r < 0) {
383 log_error("Failed to resolve group %s, ignoring policy: %s", g, strerror(-r));
384 free(i);
385 } else {
386 PolicyItem *first;
387
388 first = hashmap_get(p->group_items, UINT32_TO_PTR(i->gid));
389 LIST_PREPEND(items, first, i);
390
391 r = hashmap_replace(p->group_items, UINT32_TO_PTR(i->gid), first);
392 if (r < 0) {
393 LIST_REMOVE(items, first, i);
394 return log_oom();
395 }
396 }
397 }
398
399 state = STATE_POLICY;
400 i = NULL;
401
402 } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
403 log_error("Unexpected token (8) at %s:%u.", path, line);
404 return -EINVAL;
405 }
406
407 break;
408
409 case STATE_ALLOW_DENY_INTERFACE:
410
411 if (t == XML_ATTRIBUTE_VALUE) {
412 assert(i);
413 if (i->interface) {
414 log_error("Duplicate interface at %s:%u.", path, line);
415 return -EINVAL;
416 }
417
418 i->interface = name;
419 name = NULL;
420 state = STATE_ALLOW_DENY;
421 } else {
422 log_error("Unexpected token (9) at %s:%u.", path, line);
423 return -EINVAL;
424 }
425
426 break;
427
428 case STATE_ALLOW_DENY_MEMBER:
429
430 if (t == XML_ATTRIBUTE_VALUE) {
431 assert(i);
432 if (i->member) {
433 log_error("Duplicate member in %s:%u.", path, line);
434 return -EINVAL;
435 }
436
437 i->member = name;
438 name = NULL;
439 state = STATE_ALLOW_DENY;
440 } else {
441 log_error("Unexpected token (10) in %s:%u.", path, line);
442 return -EINVAL;
443 }
444
445 break;
446
447 case STATE_ALLOW_DENY_ERROR:
448
449 if (t == XML_ATTRIBUTE_VALUE) {
450 assert(i);
451 if (i->error) {
452 log_error("Duplicate error in %s:%u.", path, line);
453 return -EINVAL;
454 }
455
456 i->error = name;
457 name = NULL;
458 state = STATE_ALLOW_DENY;
459 } else {
460 log_error("Unexpected token (11) in %s:%u.", path, line);
461 return -EINVAL;
462 }
463
464 break;
465
466 case STATE_ALLOW_DENY_PATH:
467
468 if (t == XML_ATTRIBUTE_VALUE) {
469 assert(i);
470 if (i->path) {
471 log_error("Duplicate path in %s:%u.", path, line);
472 return -EINVAL;
473 }
474
475 i->path = name;
476 name = NULL;
477 state = STATE_ALLOW_DENY;
478 } else {
479 log_error("Unexpected token (12) in %s:%u.", path, line);
480 return -EINVAL;
481 }
482
483 break;
484
485 case STATE_ALLOW_DENY_MESSAGE_TYPE:
486
487 if (t == XML_ATTRIBUTE_VALUE) {
488 assert(i);
489
490 if (i->message_type != 0) {
491 log_error("Duplicate message type in %s:%u.", path, line);
492 return -EINVAL;
493 }
494
495 r = bus_message_type_from_string(name, &i->message_type);
496 if (r < 0) {
497 log_error("Invalid message type in %s:%u.", path, line);
498 return -EINVAL;
499 }
500
501 state = STATE_ALLOW_DENY;
502 } else {
503 log_error("Unexpected token (13) in %s:%u.", path, line);
504 return -EINVAL;
505 }
506
507 break;
508
509 case STATE_ALLOW_DENY_NAME:
510
511 if (t == XML_ATTRIBUTE_VALUE) {
512 assert(i);
513 if (i->name) {
514 log_error("Duplicate name in %s:%u.", path, line);
515 return -EINVAL;
516 }
517
518 i->name = name;
519 name = NULL;
520 state = STATE_ALLOW_DENY;
521 } else {
522 log_error("Unexpected token (14) in %s:%u.", path, line);
523 return -EINVAL;
524 }
525
526 break;
527
528 case STATE_ALLOW_DENY_OTHER_ATTRIBUTE:
529
530 if (t == XML_ATTRIBUTE_VALUE)
531 state = STATE_ALLOW_DENY;
532 else {
533 log_error("Unexpected token (15) in %s:%u.", path, line);
534 return -EINVAL;
535 }
536
537 break;
538
539 case STATE_OTHER:
540
541 if (t == XML_TAG_OPEN)
542 n_other++;
543 else if (t == XML_TAG_CLOSE || t == XML_TAG_CLOSE_EMPTY) {
544
545 if (n_other == 0)
546 state = STATE_BUSCONFIG;
547 else
548 n_other--;
549 }
550
551 break;
552 }
553 }
554 }
555
556 int policy_load(Policy *p, char **files) {
557 char **i;
558 int r;
559
560 assert(p);
561
562 STRV_FOREACH(i, files) {
563
564 r = file_load(p, *i);
565 if (r == -EISDIR) {
566 _cleanup_strv_free_ char **l = NULL;
567 char **j;
568
569 r = conf_files_list(&l, ".conf", NULL, *i, NULL);
570 if (r < 0) {
571 log_error("Failed to get configuration file list: %s", strerror(-r));
572 return r;
573 }
574
575 STRV_FOREACH(j, l)
576 file_load(p, *j);
577 }
578
579 /* We ignore all errors but EISDIR, and just proceed. */
580 }
581
582 return 0;
583 }
584
585 void policy_free(Policy *p) {
586 PolicyItem *i, *first;
587
588 if (!p)
589 return;
590
591 while ((i = p->default_items)) {
592 LIST_REMOVE(items, p->default_items, i);
593 policy_item_free(i);
594 }
595
596 while ((i = p->mandatory_items)) {
597 LIST_REMOVE(items, p->mandatory_items, i);
598 policy_item_free(i);
599 }
600
601 while ((first = hashmap_steal_first(p->user_items))) {
602
603 while ((i = first)) {
604 LIST_REMOVE(items, first, i);
605 policy_item_free(i);
606 }
607 }
608
609 while ((first = hashmap_steal_first(p->group_items))) {
610
611 while ((i = first)) {
612 LIST_REMOVE(items, first, i);
613 policy_item_free(i);
614 }
615 }
616
617 hashmap_free(p->user_items);
618 hashmap_free(p->group_items);
619
620 p->user_items = p->group_items = NULL;
621 }
622
623 static void dump_items(PolicyItem *i, const char *prefix) {
624
625 if (!i)
626 return;
627
628 if (!prefix)
629 prefix = "";
630
631 printf("%sType: %s\n"
632 "%sClass: %s\n",
633 prefix, policy_item_type_to_string(i->type),
634 prefix, policy_item_class_to_string(i->class));
635
636 if (i->interface)
637 printf("%sInterface: %s\n",
638 prefix, i->interface);
639
640 if (i->member)
641 printf("%sMember: %s\n",
642 prefix, i->member);
643
644 if (i->error)
645 printf("%sError: %s\n",
646 prefix, i->error);
647
648 if (i->path)
649 printf("%sPath: %s\n",
650 prefix, i->path);
651
652 if (i->name)
653 printf("%sName: %s\n",
654 prefix, i->name);
655
656 if (i->message_type != 0)
657 printf("%sMessage Type: %s\n",
658 prefix, bus_message_type_to_string(i->message_type));
659
660 if (i->uid_valid) {
661 _cleanup_free_ char *user;
662
663 user = uid_to_name(i->uid);
664
665 printf("%sUser: %s\n",
666 prefix, strna(user));
667 }
668
669 if (i->gid_valid) {
670 _cleanup_free_ char *group;
671
672 group = gid_to_name(i->gid);
673
674 printf("%sGroup: %s\n",
675 prefix, strna(group));
676 }
677
678 if (i->items_next) {
679 printf("%s%s\n", prefix, draw_special_char(DRAW_DASH));
680 dump_items(i->items_next, prefix);
681 }
682 }
683
684 static void dump_hashmap_items(Hashmap *h) {
685 PolicyItem *i;
686 Iterator j;
687 void *k;
688
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");
692 }
693 }
694
695 noreturn void policy_dump(Policy *p) {
696
697 printf("%s Default Items:\n", draw_special_char(DRAW_ARROW));
698 dump_items(p->default_items, "\t");
699
700 printf("%s Group Items:\n", draw_special_char(DRAW_ARROW));
701 dump_hashmap_items(p->group_items);
702
703 printf("%s User Items:\n", draw_special_char(DRAW_ARROW));
704 dump_hashmap_items(p->user_items);
705
706 printf("%s Mandatory Items:\n", draw_special_char(DRAW_ARROW));
707 dump_items(p->mandatory_items, "\t");
708
709 exit(0);
710 }
711
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",
716 };
717 DEFINE_STRING_TABLE_LOOKUP(policy_item_type, PolicyItemType);
718
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",
728 };
729 DEFINE_STRING_TABLE_LOOKUP(policy_item_class, PolicyItemClass);