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