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