]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/bus-proxyd/bus-xml-policy.c
sd-bus: fix error handling of pthread API calls
[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"
3c70e3bb 28#include "bus-xml-policy.h"
87b93496 29#include "sd-login.h"
6482f626 30#include "formats-util.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;
b9191d7a 304 else if (STR_IN_SET(name, "eavesdrop", "log")) {
ba98e746 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
97b11eed
DH
589 if (streq(name, "*"))
590 name = mfree(name);
585b46db
LP
591 break;
592
593
9eacea6b
DM
594 default:
595 break;
596 }
597
bcf3295d
LP
598 i->name = name;
599 name = NULL;
9eacea6b 600
bcf3295d
LP
601 state = STATE_ALLOW_DENY;
602 } else {
603 log_error("Unexpected token (14) in %s:%u.", path, line);
604 return -EINVAL;
605 }
606
607 break;
608
609 case STATE_ALLOW_DENY_OTHER_ATTRIBUTE:
610
611 if (t == XML_ATTRIBUTE_VALUE)
612 state = STATE_ALLOW_DENY;
613 else {
614 log_error("Unexpected token (15) in %s:%u.", path, line);
615 return -EINVAL;
616 }
617
618 break;
619
620 case STATE_OTHER:
621
622 if (t == XML_TAG_OPEN)
623 n_other++;
624 else if (t == XML_TAG_CLOSE || t == XML_TAG_CLOSE_EMPTY) {
625
626 if (n_other == 0)
627 state = STATE_BUSCONFIG;
628 else
629 n_other--;
630 }
631
632 break;
633 }
634 }
635}
636
38349552 637enum {
78f9b196 638 DENY,
38349552
DM
639 ALLOW,
640 DUNNO,
38349552
DM
641};
642
78f9b196
LP
643static const char *verdict_to_string(int v) {
644 switch (v) {
645
646 case DENY:
647 return "DENY";
648 case ALLOW:
649 return "ALLOW";
650 case DUNNO:
651 return "DUNNO";
652 }
653
654 return NULL;
655}
656
078ef7b8 657struct policy_check_filter {
3a9cca11 658 PolicyItemClass class;
78f9b196
LP
659 uid_t uid;
660 gid_t gid;
078ef7b8 661 int message_type;
d46fbfb4 662 const char *name;
078ef7b8
DM
663 const char *interface;
664 const char *path;
d46fbfb4 665 const char *member;
078ef7b8
DM
666};
667
38349552
DM
668static int is_permissive(PolicyItem *i) {
669
94a2c2f6
TG
670 assert(i);
671
38349552
DM
672 return (i->type == POLICY_ITEM_ALLOW) ? ALLOW : DENY;
673}
674
078ef7b8 675static int check_policy_item(PolicyItem *i, const struct policy_check_filter *filter) {
38349552 676
94a2c2f6
TG
677 assert(i);
678 assert(filter);
679
38349552
DM
680 switch (i->class) {
681 case POLICY_ITEM_SEND:
38349552 682 case POLICY_ITEM_RECV:
078ef7b8 683
d46fbfb4
DM
684 if (i->name && !streq_ptr(i->name, filter->name))
685 break;
078ef7b8 686
278ebf8d 687 if ((i->message_type != 0) && (i->message_type != filter->message_type))
078ef7b8
DM
688 break;
689
690 if (i->path && !streq_ptr(i->path, filter->path))
691 break;
692
693 if (i->member && !streq_ptr(i->member, filter->member))
694 break;
695
696 if (i->interface && !streq_ptr(i->interface, filter->interface))
697 break;
698
699 return is_permissive(i);
38349552
DM
700
701 case POLICY_ITEM_OWN:
d46fbfb4 702 assert(filter->name);
94a2c2f6 703
e91c8c20 704 if (streq(i->name, "*") || streq(i->name, filter->name))
38349552
DM
705 return is_permissive(i);
706 break;
707
708 case POLICY_ITEM_OWN_PREFIX:
d46fbfb4 709 assert(filter->name);
94a2c2f6 710
3a9cca11 711 if (streq(i->name, "*") || service_name_startswith(filter->name, i->name))
38349552
DM
712 return is_permissive(i);
713 break;
714
715 case POLICY_ITEM_USER:
fed1e721 716 if (filter->uid != UID_INVALID)
78f9b196
LP
717 if ((streq_ptr(i->name, "*") || (i->uid_valid && i->uid == filter->uid)))
718 return is_permissive(i);
38349552
DM
719 break;
720
721 case POLICY_ITEM_GROUP:
fed1e721 722 if (filter->gid != GID_INVALID)
78f9b196
LP
723 if ((streq_ptr(i->name, "*") || (i->gid_valid && i->gid == filter->gid)))
724 return is_permissive(i);
38349552
DM
725 break;
726
727 case POLICY_ITEM_IGNORE:
728 default:
729 break;
730 }
731
732 return DUNNO;
733}
734
078ef7b8 735static int check_policy_items(PolicyItem *items, const struct policy_check_filter *filter) {
38349552
DM
736
737 PolicyItem *i;
78f9b196 738 int verdict = DUNNO;
38349552 739
94a2c2f6
TG
740 assert(filter);
741
38349552
DM
742 /* Check all policies in a set - a broader one might be followed by a more specific one,
743 * and the order of rules in policy definitions matters */
744 LIST_FOREACH(items, i, items) {
78f9b196
LP
745 int v;
746
3a9cca11 747 if (i->class != filter->class &&
278ebf8d 748 !(i->class == POLICY_ITEM_OWN_PREFIX && filter->class == POLICY_ITEM_OWN))
078ef7b8
DM
749 continue;
750
78f9b196
LP
751 v = check_policy_item(i, filter);
752 if (v != DUNNO)
753 verdict = v;
38349552
DM
754 }
755
78f9b196 756 return verdict;
38349552
DM
757}
758
078ef7b8 759static int policy_check(Policy *p, const struct policy_check_filter *filter) {
38349552
DM
760
761 PolicyItem *items;
78f9b196 762 int verdict, v;
38349552 763
94a2c2f6
TG
764 assert(p);
765 assert(filter);
766
278ebf8d
LP
767 assert(IN_SET(filter->class, POLICY_ITEM_SEND, POLICY_ITEM_RECV, POLICY_ITEM_OWN, POLICY_ITEM_USER, POLICY_ITEM_GROUP));
768
38349552
DM
769 /*
770 * The policy check is implemented by the following logic:
771 *
78f9b196
LP
772 * 1. Check default items
773 * 2. Check group items
774 * 3. Check user items
87b93496
DH
775 * 4. Check on/no_console items
776 * 5. Check mandatory items
78f9b196
LP
777 *
778 * Later rules override earlier rules.
38349552
DM
779 */
780
78f9b196 781 verdict = check_policy_items(p->default_items, filter);
38349552 782
fed1e721 783 if (filter->gid != GID_INVALID) {
78f9b196 784 items = hashmap_get(p->group_items, UINT32_TO_PTR(filter->gid));
38349552 785 if (items) {
78f9b196
LP
786 v = check_policy_items(items, filter);
787 if (v != DUNNO)
788 verdict = v;
38349552 789 }
78f9b196 790 }
38349552 791
fed1e721 792 if (filter->uid != UID_INVALID) {
78f9b196 793 items = hashmap_get(p->user_items, UINT32_TO_PTR(filter->uid));
38349552 794 if (items) {
78f9b196
LP
795 v = check_policy_items(items, filter);
796 if (v != DUNNO)
797 verdict = v;
38349552
DM
798 }
799 }
800
87b93496
DH
801 if (filter->uid != UID_INVALID && sd_uid_get_seats(filter->uid, -1, NULL) > 0)
802 v = check_policy_items(p->on_console_items, filter);
803 else
804 v = check_policy_items(p->no_console_items, filter);
805 if (v != DUNNO)
806 verdict = v;
807
78f9b196
LP
808 v = check_policy_items(p->mandatory_items, filter);
809 if (v != DUNNO)
810 verdict = v;
811
812 return verdict;
078ef7b8
DM
813}
814
78f9b196 815bool policy_check_own(Policy *p, uid_t uid, gid_t gid, const char *name) {
078ef7b8
DM
816
817 struct policy_check_filter filter = {
e91c8c20 818 .class = POLICY_ITEM_OWN,
78f9b196
LP
819 .uid = uid,
820 .gid = gid,
e91c8c20 821 .name = name,
078ef7b8
DM
822 };
823
78f9b196
LP
824 int verdict;
825
826 assert(p);
827 assert(name);
828
829 verdict = policy_check(p, &filter);
830
831 log_full(LOG_AUTH | (verdict != ALLOW ? LOG_WARNING : LOG_DEBUG),
832 "Ownership permission check for uid=" UID_FMT " gid=" GID_FMT" name=%s: %s",
833 uid, gid, strna(name), strna(verdict_to_string(verdict)));
834
835 return verdict == ALLOW;
078ef7b8
DM
836}
837
78f9b196 838bool policy_check_hello(Policy *p, uid_t uid, gid_t gid) {
078ef7b8
DM
839
840 struct policy_check_filter filter = {
78f9b196
LP
841 .uid = uid,
842 .gid = gid,
078ef7b8 843 };
78f9b196
LP
844 int verdict;
845
846 assert(p);
078ef7b8 847
e91c8c20 848 filter.class = POLICY_ITEM_USER;
78f9b196
LP
849 verdict = policy_check(p, &filter);
850
851 if (verdict != DENY) {
852 int v;
078ef7b8 853
78f9b196
LP
854 filter.class = POLICY_ITEM_GROUP;
855 v = policy_check(p, &filter);
856 if (v != DUNNO)
857 verdict = v;
858 }
078ef7b8 859
78f9b196
LP
860 log_full(LOG_AUTH | (verdict != ALLOW ? LOG_WARNING : LOG_DEBUG),
861 "Hello permission check for uid=" UID_FMT " gid=" GID_FMT": %s",
862 uid, gid, strna(verdict_to_string(verdict)));
863
864 return verdict == ALLOW;
078ef7b8
DM
865}
866
7447362c
DH
867bool policy_check_one_recv(Policy *p,
868 uid_t uid,
869 gid_t gid,
870 int message_type,
871 const char *name,
872 const char *path,
873 const char *interface,
874 const char *member) {
078ef7b8
DM
875
876 struct policy_check_filter filter = {
877 .class = POLICY_ITEM_RECV,
78f9b196
LP
878 .uid = uid,
879 .gid = gid,
078ef7b8 880 .message_type = message_type,
d46fbfb4 881 .name = name,
078ef7b8
DM
882 .interface = interface,
883 .path = path,
884 .member = member,
885 };
886
78f9b196
LP
887 assert(p);
888
7447362c 889 return policy_check(p, &filter) == ALLOW;
078ef7b8 890}
38349552 891
7447362c 892bool policy_check_recv(Policy *p,
78f9b196
LP
893 uid_t uid,
894 gid_t gid,
078ef7b8 895 int message_type,
7447362c
DH
896 Set *names,
897 char **namesv,
078ef7b8
DM
898 const char *path,
899 const char *interface,
b49c7806
DH
900 const char *member,
901 bool dbus_to_kernel) {
078ef7b8 902
7447362c
DH
903 char *n, **nv, *last = NULL;
904 bool allow = false;
905 Iterator i;
906
907 assert(p);
908
909 if (set_isempty(names) && strv_isempty(namesv)) {
910 allow = policy_check_one_recv(p, uid, gid, message_type, NULL, path, interface, member);
911 } else {
912 SET_FOREACH(n, names, i) {
913 last = n;
914 allow = policy_check_one_recv(p, uid, gid, message_type, n, path, interface, member);
915 if (allow)
916 break;
917 }
918 if (!allow) {
919 STRV_FOREACH(nv, namesv) {
920 last = *nv;
921 allow = policy_check_one_recv(p, uid, gid, message_type, *nv, path, interface, member);
922 if (allow)
923 break;
924 }
925 }
926 }
927
928 log_full(LOG_AUTH | (!allow ? LOG_WARNING : LOG_DEBUG),
929 "Receive permission check %s for uid=" UID_FMT " gid=" GID_FMT" message=%s name=%s path=%s interface=%s member=%s: %s",
930 dbus_to_kernel ? "dbus-1 to kernel" : "kernel to dbus-1", uid, gid, bus_message_type_to_string(message_type), strna(last),
931 strna(path), strna(interface), strna(member), allow ? "ALLOW" : "DENY");
932
933 return allow;
934}
935
936bool policy_check_one_send(Policy *p,
937 uid_t uid,
938 gid_t gid,
939 int message_type,
940 const char *name,
941 const char *path,
942 const char *interface,
943 const char *member) {
944
078ef7b8
DM
945 struct policy_check_filter filter = {
946 .class = POLICY_ITEM_SEND,
78f9b196
LP
947 .uid = uid,
948 .gid = gid,
078ef7b8 949 .message_type = message_type,
d46fbfb4 950 .name = name,
078ef7b8
DM
951 .interface = interface,
952 .path = path,
953 .member = member,
954 };
955
7447362c
DH
956 assert(p);
957
958 return policy_check(p, &filter) == ALLOW;
959}
960
961bool policy_check_send(Policy *p,
962 uid_t uid,
963 gid_t gid,
964 int message_type,
965 Set *names,
966 char **namesv,
967 const char *path,
968 const char *interface,
969 const char *member,
970 bool dbus_to_kernel,
971 char **out_used_name) {
972
973 char *n, **nv, *last = NULL;
974 bool allow = false;
975 Iterator i;
78f9b196
LP
976
977 assert(p);
978
7447362c
DH
979 if (set_isempty(names) && strv_isempty(namesv)) {
980 allow = policy_check_one_send(p, uid, gid, message_type, NULL, path, interface, member);
981 } else {
982 SET_FOREACH(n, names, i) {
983 last = n;
984 allow = policy_check_one_send(p, uid, gid, message_type, n, path, interface, member);
985 if (allow)
986 break;
987 }
988 if (!allow) {
989 STRV_FOREACH(nv, namesv) {
990 last = *nv;
991 allow = policy_check_one_send(p, uid, gid, message_type, *nv, path, interface, member);
992 if (allow)
993 break;
994 }
995 }
996 }
78f9b196 997
7447362c
DH
998 if (out_used_name)
999 *out_used_name = last;
1000
1001 log_full(LOG_AUTH | (!allow ? LOG_WARNING : LOG_DEBUG),
b49c7806 1002 "Send permission check %s for uid=" UID_FMT " gid=" GID_FMT" message=%s name=%s path=%s interface=%s member=%s: %s",
7447362c
DH
1003 dbus_to_kernel ? "dbus-1 to kernel" : "kernel to dbus-1", uid, gid, bus_message_type_to_string(message_type), strna(last),
1004 strna(path), strna(interface), strna(member), allow ? "ALLOW" : "DENY");
78f9b196 1005
7447362c 1006 return allow;
38349552
DM
1007}
1008
2e2b3608 1009int policy_load(Policy *p, char **files) {
bcf3295d
LP
1010 char **i;
1011 int r;
1012
1013 assert(p);
1014
2e2b3608 1015 STRV_FOREACH(i, files) {
bcf3295d 1016
2e2b3608
LP
1017 r = file_load(p, *i);
1018 if (r == -EISDIR) {
1019 _cleanup_strv_free_ char **l = NULL;
1020 char **j;
1021
1022 r = conf_files_list(&l, ".conf", NULL, *i, NULL);
23bbb0de
MS
1023 if (r < 0)
1024 return log_error_errno(r, "Failed to get configuration file list: %m");
2e2b3608
LP
1025
1026 STRV_FOREACH(j, l)
1027 file_load(p, *j);
1028 }
bcf3295d 1029
2e2b3608
LP
1030 /* We ignore all errors but EISDIR, and just proceed. */
1031 }
bcf3295d
LP
1032
1033 return 0;
1034}
1035
1036void policy_free(Policy *p) {
1037 PolicyItem *i, *first;
1038
1039 if (!p)
1040 return;
1041
1042 while ((i = p->default_items)) {
1043 LIST_REMOVE(items, p->default_items, i);
1044 policy_item_free(i);
1045 }
1046
1047 while ((i = p->mandatory_items)) {
1048 LIST_REMOVE(items, p->mandatory_items, i);
1049 policy_item_free(i);
1050 }
1051
87b93496
DH
1052 while ((i = p->on_console_items)) {
1053 LIST_REMOVE(items, p->on_console_items, i);
1054 policy_item_free(i);
1055 }
1056
1057 while ((i = p->no_console_items)) {
1058 LIST_REMOVE(items, p->no_console_items, i);
1059 policy_item_free(i);
1060 }
1061
bcf3295d
LP
1062 while ((first = hashmap_steal_first(p->user_items))) {
1063
1064 while ((i = first)) {
1065 LIST_REMOVE(items, first, i);
1066 policy_item_free(i);
1067 }
bcf3295d
LP
1068 }
1069
1070 while ((first = hashmap_steal_first(p->group_items))) {
1071
1072 while ((i = first)) {
1073 LIST_REMOVE(items, first, i);
1074 policy_item_free(i);
1075 }
bcf3295d
LP
1076 }
1077
1078 hashmap_free(p->user_items);
1079 hashmap_free(p->group_items);
1080
1081 p->user_items = p->group_items = NULL;
1082}
1083
080edb34 1084static void dump_items(PolicyItem *items, const char *prefix) {
bcf3295d 1085
080edb34
DM
1086 PolicyItem *i;
1087
1088 if (!items)
bcf3295d
LP
1089 return;
1090
13f8b8cb
LP
1091 if (!prefix)
1092 prefix = "";
1093
080edb34 1094 LIST_FOREACH(items, i, items) {
bcf3295d 1095
080edb34
DM
1096 printf("%sType: %s\n"
1097 "%sClass: %s\n",
1098 prefix, policy_item_type_to_string(i->type),
1099 prefix, policy_item_class_to_string(i->class));
bcf3295d 1100
080edb34
DM
1101 if (i->interface)
1102 printf("%sInterface: %s\n",
1103 prefix, i->interface);
bcf3295d 1104
080edb34
DM
1105 if (i->member)
1106 printf("%sMember: %s\n",
1107 prefix, i->member);
bcf3295d 1108
080edb34
DM
1109 if (i->error)
1110 printf("%sError: %s\n",
1111 prefix, i->error);
bcf3295d 1112
080edb34
DM
1113 if (i->path)
1114 printf("%sPath: %s\n",
1115 prefix, i->path);
bcf3295d 1116
080edb34
DM
1117 if (i->name)
1118 printf("%sName: %s\n",
1119 prefix, i->name);
bcf3295d 1120
080edb34
DM
1121 if (i->message_type != 0)
1122 printf("%sMessage Type: %s\n",
1123 prefix, bus_message_type_to_string(i->message_type));
bcf3295d 1124
080edb34
DM
1125 if (i->uid_valid) {
1126 _cleanup_free_ char *user;
bcf3295d 1127
080edb34 1128 user = uid_to_name(i->uid);
bcf3295d 1129
1fa2f38f 1130 printf("%sUser: %s ("UID_FMT")\n",
ed91202f 1131 prefix, strna(user), i->uid);
080edb34 1132 }
bcf3295d 1133
080edb34
DM
1134 if (i->gid_valid) {
1135 _cleanup_free_ char *group;
bcf3295d 1136
080edb34 1137 group = gid_to_name(i->gid);
bcf3295d 1138
1fa2f38f 1139 printf("%sGroup: %s ("GID_FMT")\n",
ed91202f 1140 prefix, strna(group), i->gid);
080edb34 1141 }
78f9b196 1142 printf("%s-\n", prefix);
bcf3295d
LP
1143 }
1144}
1145
1146static void dump_hashmap_items(Hashmap *h) {
1147 PolicyItem *i;
1148 Iterator j;
44574303 1149 void *k;
bcf3295d
LP
1150
1151 HASHMAP_FOREACH_KEY(i, k, h, j) {
13f8b8cb
LP
1152 printf("\t%s Item for %u:\n", draw_special_char(DRAW_ARROW), PTR_TO_UINT(k));
1153 dump_items(i, "\t\t");
bcf3295d
LP
1154 }
1155}
1156
e42bb8d4 1157void policy_dump(Policy *p) {
bcf3295d 1158
e76ae7ee 1159 printf("%s Default Items:\n", draw_special_char(DRAW_ARROW));
13f8b8cb 1160 dump_items(p->default_items, "\t");
bcf3295d 1161
e76ae7ee 1162 printf("%s Group Items:\n", draw_special_char(DRAW_ARROW));
bcf3295d
LP
1163 dump_hashmap_items(p->group_items);
1164
e76ae7ee 1165 printf("%s User Items:\n", draw_special_char(DRAW_ARROW));
bcf3295d 1166 dump_hashmap_items(p->user_items);
13f8b8cb 1167
87b93496
DH
1168 printf("%s On-Console Items:\n", draw_special_char(DRAW_ARROW));
1169 dump_items(p->on_console_items, "\t");
1170
1171 printf("%s No-Console Items:\n", draw_special_char(DRAW_ARROW));
1172 dump_items(p->no_console_items, "\t");
1173
13f8b8cb
LP
1174 printf("%s Mandatory Items:\n", draw_special_char(DRAW_ARROW));
1175 dump_items(p->mandatory_items, "\t");
48aae6d6
LP
1176
1177 fflush(stdout);
bcf3295d
LP
1178}
1179
c4bc1a84
DH
1180int shared_policy_new(SharedPolicy **out) {
1181 SharedPolicy *sp;
1182 int r;
1183
1184 sp = new0(SharedPolicy, 1);
1185 if (!sp)
1186 return log_oom();
1187
1188 r = pthread_mutex_init(&sp->lock, NULL);
50e0d56c
LP
1189 if (r != 0) {
1190 r = log_error_errno(r, "Cannot initialize shared policy mutex: %m");
c4bc1a84
DH
1191 goto exit_free;
1192 }
1193
1194 r = pthread_rwlock_init(&sp->rwlock, NULL);
50e0d56c
LP
1195 if (r != 0) {
1196 r = log_error_errno(r, "Cannot initialize shared policy rwlock: %m");
c4bc1a84
DH
1197 goto exit_mutex;
1198 }
1199
1200 *out = sp;
1201 sp = NULL;
1202 return 0;
1203
1204 /* pthread lock destruction is not fail-safe... meh! */
1205exit_mutex:
1206 pthread_mutex_destroy(&sp->lock);
1207exit_free:
1208 free(sp);
1209 return r;
1210}
1211
1212SharedPolicy *shared_policy_free(SharedPolicy *sp) {
1213 if (!sp)
1214 return NULL;
1215
1216 policy_free(sp->policy);
1217 pthread_rwlock_destroy(&sp->rwlock);
1218 pthread_mutex_destroy(&sp->lock);
0d620e53 1219 strv_free(sp->configuration);
c4bc1a84
DH
1220 free(sp);
1221
1222 return NULL;
1223}
1224
1225static int shared_policy_reload_unlocked(SharedPolicy *sp, char **configuration) {
1226 Policy old, buffer = {};
1227 bool free_old;
1228 int r;
1229
1230 assert(sp);
1231
1232 r = policy_load(&buffer, configuration);
1233 if (r < 0)
1234 return log_error_errno(r, "Failed to load policy: %m");
1235
0d620e53 1236 log_debug("Reloading configuration");
c4bc1a84
DH
1237 /* policy_dump(&buffer); */
1238
1239 pthread_rwlock_wrlock(&sp->rwlock);
1240 memcpy(&old, &sp->buffer, sizeof(old));
1241 memcpy(&sp->buffer, &buffer, sizeof(buffer));
1242 free_old = !!sp->policy;
1243 sp->policy = &sp->buffer;
1244 pthread_rwlock_unlock(&sp->rwlock);
1245
1246 if (free_old)
1247 policy_free(&old);
1248
1249 return 0;
1250}
1251
0d620e53 1252int shared_policy_reload(SharedPolicy *sp) {
c4bc1a84
DH
1253 int r;
1254
1255 assert(sp);
1256
1257 pthread_mutex_lock(&sp->lock);
0d620e53 1258 r = shared_policy_reload_unlocked(sp, sp->configuration);
c4bc1a84
DH
1259 pthread_mutex_unlock(&sp->lock);
1260
1261 return r;
1262}
1263
1264int shared_policy_preload(SharedPolicy *sp, char **configuration) {
0d620e53
DH
1265 _cleanup_strv_free_ char **conf = NULL;
1266 int r = 0;
c4bc1a84
DH
1267
1268 assert(sp);
1269
0d620e53
DH
1270 conf = strv_copy(configuration);
1271 if (!conf)
1272 return log_oom();
1273
c4bc1a84 1274 pthread_mutex_lock(&sp->lock);
0d620e53
DH
1275 if (!sp->policy) {
1276 r = shared_policy_reload_unlocked(sp, conf);
1277 if (r >= 0) {
1278 sp->configuration = conf;
1279 conf = NULL;
1280 }
1281 }
c4bc1a84
DH
1282 pthread_mutex_unlock(&sp->lock);
1283
1284 return r;
1285}
1286
1287Policy *shared_policy_acquire(SharedPolicy *sp) {
1288 assert(sp);
1289
1290 pthread_rwlock_rdlock(&sp->rwlock);
1291 if (sp->policy)
1292 return sp->policy;
1293 pthread_rwlock_unlock(&sp->rwlock);
1294
1295 return NULL;
1296}
1297
1298void shared_policy_release(SharedPolicy *sp, Policy *p) {
1299 assert(sp);
1300 assert(!p || sp->policy == p);
1301
1302 if (p)
1303 pthread_rwlock_unlock(&sp->rwlock);
1304}
1305
bcf3295d
LP
1306static const char* const policy_item_type_table[_POLICY_ITEM_TYPE_MAX] = {
1307 [_POLICY_ITEM_TYPE_UNSET] = "unset",
1308 [POLICY_ITEM_ALLOW] = "allow",
1309 [POLICY_ITEM_DENY] = "deny",
1310};
1311DEFINE_STRING_TABLE_LOOKUP(policy_item_type, PolicyItemType);
1312
1313static const char* const policy_item_class_table[_POLICY_ITEM_CLASS_MAX] = {
1314 [_POLICY_ITEM_CLASS_UNSET] = "unset",
1315 [POLICY_ITEM_SEND] = "send",
1316 [POLICY_ITEM_RECV] = "recv",
1317 [POLICY_ITEM_OWN] = "own",
1318 [POLICY_ITEM_OWN_PREFIX] = "own-prefix",
1319 [POLICY_ITEM_USER] = "user",
1320 [POLICY_ITEM_GROUP] = "group",
86bbe5bf 1321 [POLICY_ITEM_IGNORE] = "ignore",
bcf3295d
LP
1322};
1323DEFINE_STRING_TABLE_LOOKUP(policy_item_class, PolicyItemClass);