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