chiark / gitweb /
bus: add basic dbus1 policy parser
[elogind.git] / src / bus-proxyd / bus-policy.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include "xml.h"
23 #include "fileio.h"
24 #include "strv.h"
25 #include "conf-files.h"
26 #include "bus-internal.h"
27 #include "bus-policy.h"
28
29 static void policy_item_free(PolicyItem *i) {
30         assert(i);
31
32         free(i->interface);
33         free(i->member);
34         free(i->error);
35         free(i->name);
36         free(i->path);
37         free(i);
38 }
39
40 DEFINE_TRIVIAL_CLEANUP_FUNC(PolicyItem*, policy_item_free);
41
42 static int file_load(Policy *p, const char *path) {
43
44         _cleanup_free_ char *c = NULL, *policy_user = NULL, *policy_group = NULL;
45         _cleanup_(policy_item_freep) PolicyItem *i = NULL;
46         void *xml_state = NULL;
47         unsigned n_other = 0;
48         const char *q;
49         int r;
50
51         enum {
52                 STATE_OUTSIDE,
53                 STATE_BUSCONFIG,
54                 STATE_POLICY,
55                 STATE_POLICY_CONTEXT,
56                 STATE_POLICY_USER,
57                 STATE_POLICY_GROUP,
58                 STATE_POLICY_OTHER_ATTRIBUTE,
59                 STATE_ALLOW_DENY,
60                 STATE_ALLOW_DENY_INTERFACE,
61                 STATE_ALLOW_DENY_MEMBER,
62                 STATE_ALLOW_DENY_ERROR,
63                 STATE_ALLOW_DENY_PATH,
64                 STATE_ALLOW_DENY_MESSAGE_TYPE,
65                 STATE_ALLOW_DENY_NAME,
66                 STATE_ALLOW_DENY_OTHER_ATTRIBUTE,
67                 STATE_OTHER,
68         } state = STATE_OUTSIDE;
69
70         enum {
71                 POLICY_CATEGORY_NONE,
72                 POLICY_CATEGORY_DEFAULT,
73                 POLICY_CATEGORY_MANDATORY,
74                 POLICY_CATEGORY_USER,
75                 POLICY_CATEGORY_GROUP
76         } policy_category = POLICY_CATEGORY_NONE;
77
78         unsigned line = 0;
79
80         assert(p);
81
82         r = read_full_file(path, &c, NULL);
83         if (r < 0) {
84                 if (r == -ENOENT)
85                         return 0;
86
87                 log_error("Failed to load %s: %s", path, strerror(-r));
88                 return r;
89         }
90
91         q = c;
92         for (;;) {
93                 _cleanup_free_ char *name = NULL;
94                 int t;
95
96                 t = xml_tokenize(&q, &name, &xml_state, &line);
97                 if (t < 0) {
98                         log_error("XML parse failure in %s: %s", path, strerror(-t));
99                         return t;
100                 }
101
102                 switch (state) {
103
104                 case STATE_OUTSIDE:
105
106                         if (t == XML_TAG_OPEN) {
107                                 if (streq(name, "busconfig"))
108                                         state = STATE_BUSCONFIG;
109                                 else {
110                                         log_error("Unexpected tag %s at %s:%u.", name, path, line);
111                                         return -EINVAL;
112                                 }
113
114                         } else if (t == XML_END)
115                                 return 0;
116                         else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
117                                 log_error("Unexpected token (1) at %s:%u.", path, line);
118                                 return -EINVAL;
119                         }
120
121                         break;
122
123                 case STATE_BUSCONFIG:
124
125                         if (t == XML_TAG_OPEN) {
126                                 if (streq(name, "policy")) {
127                                         state = STATE_POLICY;
128                                         policy_category = POLICY_CATEGORY_NONE;
129                                         free(policy_user);
130                                         free(policy_group);
131                                         policy_user = policy_group = NULL;
132                                 } else {
133                                         state = STATE_OTHER;
134                                         n_other = 0;
135                                 }
136                         } else if (t == XML_TAG_CLOSE_EMPTY ||
137                                    (t == XML_TAG_CLOSE && streq(name, "busconfig")))
138                                 state = STATE_OUTSIDE;
139                         else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
140                                 log_error("Unexpected token (2) at %s:%u.", path, line);
141                                 return -EINVAL;
142                         }
143
144                         break;
145
146                 case STATE_POLICY:
147
148                         if (t == XML_ATTRIBUTE_NAME) {
149                                 if (streq(name, "context"))
150                                         state = STATE_POLICY_CONTEXT;
151                                 else if (streq(name, "user"))
152                                         state = STATE_POLICY_USER;
153                                 else if (streq(name, "group"))
154                                         state = STATE_POLICY_GROUP;
155                                 else {
156                                         log_warning("Attribute %s of <policy> tag unknown at %s:%u, ignoring.", name, path, line);
157                                         state = STATE_POLICY_OTHER_ATTRIBUTE;
158                                 }
159                         } else if (t == XML_TAG_CLOSE_EMPTY ||
160                                    (t == XML_TAG_CLOSE && streq(name, "policy")))
161                                 state = STATE_BUSCONFIG;
162                         else if (t == XML_TAG_OPEN) {
163                                 PolicyItemType it;
164
165                                 if (streq(name, "allow"))
166                                         it = POLICY_ITEM_ALLOW;
167                                 else if (streq(name, "deny"))
168                                         it = POLICY_ITEM_DENY;
169                                 else {
170                                         log_warning("Unknown tag %s in <policy> %s:%u.", name, path, line);
171                                         return -EINVAL;
172                                 }
173
174                                 assert(!i);
175                                 i = new0(PolicyItem, 1);
176                                 if (!i)
177                                         return log_oom();
178
179                                 i->type = it;
180                                 state = STATE_ALLOW_DENY;
181
182                         } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
183                                 log_error("Unexpected token (3) at %s:%u.", path, line);
184                                 return -EINVAL;
185                         }
186
187                         break;
188
189                 case STATE_POLICY_CONTEXT:
190
191                         if (t == XML_ATTRIBUTE_VALUE) {
192                                 if (streq(name, "default")) {
193                                         policy_category = POLICY_CATEGORY_DEFAULT;
194                                         state = STATE_POLICY;
195                                 } else if (streq(name, "mandatory")) {
196                                         policy_category = POLICY_CATEGORY_MANDATORY;
197                                         state = STATE_POLICY;
198                                 } else {
199                                         log_error("context= parameter %s unknown for <policy> at %s:%u.", name, path, line);
200                                         return -EINVAL;
201                                 }
202                         } else {
203                                 log_error("Unexpected token (4) at %s:%u.", path, line);
204                                 return -EINVAL;
205                         }
206
207                         break;
208
209                 case STATE_POLICY_USER:
210
211                         if (t == XML_ATTRIBUTE_VALUE) {
212                                 free(policy_user);
213                                 policy_user = name;
214                                 name = NULL;
215                                 state = STATE_POLICY;
216                         } else {
217                                 log_error("Unexpected token (5) in %s:%u.", path, line);
218                                 return -EINVAL;
219                         }
220
221                         break;
222
223                 case STATE_POLICY_GROUP:
224
225                         if (t == XML_ATTRIBUTE_VALUE) {
226                                 free(policy_group);
227                                 policy_group = name;
228                                 name = NULL;
229                                 state = STATE_POLICY;
230                         } else {
231                                 log_error("Unexpected token (6) at %s:%u.", path, line);
232                                 return -EINVAL;
233                         }
234
235                         break;
236
237                 case STATE_POLICY_OTHER_ATTRIBUTE:
238
239                         if (t == XML_ATTRIBUTE_VALUE)
240                                 state = STATE_POLICY;
241                         else {
242                                 log_error("Unexpected token (7) in %s:%u.", path, line);
243                                 return -EINVAL;
244                         }
245
246                         break;
247
248                 case STATE_ALLOW_DENY:
249
250                         assert(i);
251
252                         if (t == XML_ATTRIBUTE_NAME) {
253                                 PolicyItemClass ic;
254
255                                 if (startswith(name, "send_"))
256                                         ic = POLICY_ITEM_SEND;
257                                 else if (startswith(name, "receive_"))
258                                         ic = POLICY_ITEM_RECV;
259                                 else if (streq(name, "own"))
260                                         ic = POLICY_ITEM_OWN;
261                                 else if (streq(name, "own_prefix"))
262                                         ic = POLICY_ITEM_OWN_PREFIX;
263                                 else if (streq(name, "user"))
264                                         ic = POLICY_ITEM_USER;
265                                 else if (streq(name, "group"))
266                                         ic = POLICY_ITEM_GROUP;
267                                 else {
268                                         log_error("Unknown attribute %s= at %s:%u, ignoring.", name, path, line);
269                                         state = STATE_ALLOW_DENY_OTHER_ATTRIBUTE;
270                                         break;
271                                 }
272
273                                 if (i->class != _POLICY_ITEM_CLASS_UNSET && ic != i->class) {
274                                         log_error("send_ and receive_ fields mixed on same tag at %s:%u.", path, line);
275                                         return -EINVAL;
276                                 }
277
278                                 i->class = ic;
279
280                                 if (ic == POLICY_ITEM_SEND || ic == POLICY_ITEM_RECV) {
281                                         const char *u;
282
283                                         u = strchr(name, '_');
284                                         assert(u);
285
286                                         u++;
287
288                                         if (streq(u, "interface"))
289                                                 state = STATE_ALLOW_DENY_INTERFACE;
290                                         else if (streq(u, "member"))
291                                                 state = STATE_ALLOW_DENY_MEMBER;
292                                         else if (streq(u, "error"))
293                                                 state = STATE_ALLOW_DENY_ERROR;
294                                         else if (streq(u, "path"))
295                                                 state = STATE_ALLOW_DENY_PATH;
296                                         else if (streq(u, "type"))
297                                                 state = STATE_ALLOW_DENY_MESSAGE_TYPE;
298                                         else if ((streq(u, "destination") && ic == POLICY_ITEM_SEND) ||
299                                                  (streq(u, "sender") && ic == POLICY_ITEM_RECV))
300                                                 state = STATE_ALLOW_DENY_NAME;
301                                         else {
302                                                 log_error("Unknown attribute %s= at %s:%u, ignoring.", name, path, line);
303                                                 state = STATE_ALLOW_DENY_OTHER_ATTRIBUTE;
304                                                 break;
305                                         }
306                                 } else
307                                         state = STATE_ALLOW_DENY_NAME;
308
309                         } else if (t == XML_TAG_CLOSE_EMPTY ||
310                                    (t == XML_TAG_CLOSE && streq(name, i->type == POLICY_ITEM_ALLOW ? "allow" : "deny"))) {
311
312                                 if (i->class == _POLICY_ITEM_CLASS_UNSET) {
313                                         log_error("Policy not set at %s:%u.", path, line);
314                                         return -EINVAL;
315                                 }
316
317                                 if (policy_category == POLICY_CATEGORY_DEFAULT)
318                                         LIST_PREPEND(items, p->default_items, i);
319                                 else if (policy_category == POLICY_CATEGORY_MANDATORY)
320                                         LIST_PREPEND(items, p->default_items, i);
321                                 else if (policy_category == POLICY_CATEGORY_USER) {
322                                         PolicyItem *first;
323
324                                         assert_cc(sizeof(uid_t) == sizeof(uint32_t));
325
326                                         r = hashmap_ensure_allocated(&p->user_items, trivial_hash_func, trivial_compare_func);
327                                         if (r < 0)
328                                                 return log_oom();
329
330                                         first = hashmap_get(p->user_items, UINT32_TO_PTR(i->uid));
331                                         LIST_PREPEND(items, first, i);
332
333                                         r = hashmap_replace(p->user_items, UINT32_TO_PTR(i->uid), first);
334                                         if (r < 0) {
335                                                 LIST_REMOVE(items, first, i);
336                                                 return log_oom();
337                                         }
338                                 } else if (policy_category == POLICY_CATEGORY_GROUP) {
339                                         PolicyItem *first;
340
341                                         assert_cc(sizeof(gid_t) == sizeof(uint32_t));
342
343                                         r = hashmap_ensure_allocated(&p->group_items, trivial_hash_func, trivial_compare_func);
344                                         if (r < 0)
345                                                 return log_oom();
346
347                                         first = hashmap_get(p->group_items, UINT32_TO_PTR(i->gid));
348                                         LIST_PREPEND(items, first, i);
349
350                                         r = hashmap_replace(p->group_items, UINT32_TO_PTR(i->gid), first);
351                                         if (r < 0) {
352                                                 LIST_REMOVE(items, first, i);
353                                                 return log_oom();
354                                         }
355                                 }
356
357                                 state = STATE_POLICY;
358                                 i = NULL;
359
360                         } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
361                                 log_error("Unexpected token (8) at %s:%u.", path, line);
362                                 return -EINVAL;
363                         }
364
365                         break;
366
367                 case STATE_ALLOW_DENY_INTERFACE:
368
369                         if (t == XML_ATTRIBUTE_VALUE) {
370                                 assert(i);
371                                 if (i->interface) {
372                                         log_error("Duplicate interface at %s:%u.", path, line);
373                                         return -EINVAL;
374                                 }
375
376                                 i->interface = name;
377                                 name = NULL;
378                                 state = STATE_ALLOW_DENY;
379                         } else {
380                                 log_error("Unexpected token (9) at %s:%u.", path, line);
381                                 return -EINVAL;
382                         }
383
384                         break;
385
386                 case STATE_ALLOW_DENY_MEMBER:
387
388                         if (t == XML_ATTRIBUTE_VALUE) {
389                                 assert(i);
390                                 if (i->member) {
391                                         log_error("Duplicate member in %s:%u.", path, line);
392                                         return -EINVAL;
393                                 }
394
395                                 i->member = name;
396                                 name = NULL;
397                                 state = STATE_ALLOW_DENY;
398                         } else {
399                                 log_error("Unexpected token (10) in %s:%u.", path, line);
400                                 return -EINVAL;
401                         }
402
403                         break;
404
405                 case STATE_ALLOW_DENY_ERROR:
406
407                         if (t == XML_ATTRIBUTE_VALUE) {
408                                 assert(i);
409                                 if (i->error) {
410                                         log_error("Duplicate error in %s:%u.", path, line);
411                                         return -EINVAL;
412                                 }
413
414                                 i->error = name;
415                                 name = NULL;
416                                 state = STATE_ALLOW_DENY;
417                         } else {
418                                 log_error("Unexpected token (11) in %s:%u.", path, line);
419                                 return -EINVAL;
420                         }
421
422                         break;
423
424                 case STATE_ALLOW_DENY_PATH:
425
426                         if (t == XML_ATTRIBUTE_VALUE) {
427                                 assert(i);
428                                 if (i->path) {
429                                         log_error("Duplicate path in %s:%u.", path, line);
430                                         return -EINVAL;
431                                 }
432
433                                 i->path = name;
434                                 name = NULL;
435                                 state = STATE_ALLOW_DENY;
436                         } else {
437                                 log_error("Unexpected token (12) in %s:%u.", path, line);
438                                 return -EINVAL;
439                         }
440
441                         break;
442
443                 case STATE_ALLOW_DENY_MESSAGE_TYPE:
444
445                         if (t == XML_ATTRIBUTE_VALUE) {
446                                 assert(i);
447
448                                 if (i->message_type != 0) {
449                                         log_error("Duplicate message type in %s:%u.", path, line);
450                                         return -EINVAL;
451                                 }
452
453                                 r = bus_message_type_from_string(name, &i->message_type);
454                                 if (r < 0) {
455                                         log_error("Invalid message type in %s:%u.", path, line);
456                                         return -EINVAL;
457                                 }
458
459                                 state = STATE_ALLOW_DENY;
460                         } else {
461                                 log_error("Unexpected token (13) in %s:%u.", path, line);
462                                 return -EINVAL;
463                         }
464
465                         break;
466
467                 case STATE_ALLOW_DENY_NAME:
468
469                         if (t == XML_ATTRIBUTE_VALUE) {
470                                 assert(i);
471                                 if (i->name) {
472                                         log_error("Duplicate name in %s:%u.", path, line);
473                                         return -EINVAL;
474                                 }
475
476                                 i->name = name;
477                                 name = NULL;
478                                 state = STATE_ALLOW_DENY;
479                         } else {
480                                 log_error("Unexpected token (14) in %s:%u.", path, line);
481                                 return -EINVAL;
482                         }
483
484                         break;
485
486                 case STATE_ALLOW_DENY_OTHER_ATTRIBUTE:
487
488                         if (t == XML_ATTRIBUTE_VALUE)
489                                 state = STATE_ALLOW_DENY;
490                         else {
491                                 log_error("Unexpected token (15) in %s:%u.", path, line);
492                                 return -EINVAL;
493                         }
494
495                         break;
496
497                 case STATE_OTHER:
498
499                         if (t == XML_TAG_OPEN)
500                                 n_other++;
501                         else if (t == XML_TAG_CLOSE || t == XML_TAG_CLOSE_EMPTY) {
502
503                                 if (n_other == 0)
504                                         state = STATE_BUSCONFIG;
505                                 else
506                                         n_other--;
507                         }
508
509                         break;
510                 }
511         }
512 }
513
514 int policy_load(Policy *p) {
515         _cleanup_strv_free_ char **l = NULL;
516         char **i;
517         int r;
518
519         assert(p);
520
521         file_load(p, "/etc/dbus-1/system.conf");
522         file_load(p, "/etc/dbus-1/system-local.conf");
523
524         r = conf_files_list(&l, ".conf", NULL, "/etc/dbus-1/system.d/", NULL);
525         if (r < 0) {
526                 log_error("Failed to get configuration file list: %s", strerror(-r));
527                 return r;
528         }
529
530         STRV_FOREACH(i, l)
531                 file_load(p, *i);
532
533         return 0;
534 }
535
536 void policy_free(Policy *p) {
537         PolicyItem *i, *first;
538
539         if (!p)
540                 return;
541
542         while ((i = p->default_items)) {
543                 LIST_REMOVE(items, p->default_items, i);
544                 policy_item_free(i);
545         }
546
547         while ((i = p->mandatory_items)) {
548                 LIST_REMOVE(items, p->mandatory_items, i);
549                 policy_item_free(i);
550         }
551
552         while ((first = hashmap_steal_first(p->user_items))) {
553
554                 while ((i = first)) {
555                         LIST_REMOVE(items, first, i);
556                         policy_item_free(i);
557                 }
558
559                 policy_item_free(i);
560         }
561
562         while ((first = hashmap_steal_first(p->group_items))) {
563
564                 while ((i = first)) {
565                         LIST_REMOVE(items, first, i);
566                         policy_item_free(i);
567                 }
568
569                 policy_item_free(i);
570         }
571
572         hashmap_free(p->user_items);
573         hashmap_free(p->group_items);
574
575         p->user_items = p->group_items = NULL;
576 }
577
578 static void dump_items(PolicyItem *i) {
579
580         if (!i)
581                 return;
582
583         printf("Type: %s\n"
584                "Class: %s\n",
585                policy_item_type_to_string(i->type),
586                policy_item_class_to_string(i->class));
587
588         if (i->interface)
589                 printf("Interface: %s\n",
590                        i->interface);
591
592         if (i->member)
593                 printf("Member: %s\n",
594                        i->member);
595
596         if (i->error)
597                 printf("Error: %s\n",
598                        i->error);
599
600         if (i->path)
601                 printf("Path: %s\n",
602                        i->path);
603
604         if (i->name)
605                 printf("Name: %s\n",
606                        i->name);
607
608         if (i->message_type != 0)
609                 printf("Message Type: %s\n",
610                        bus_message_type_to_string(i->message_type));
611
612         if (i->uid_valid) {
613                 _cleanup_free_ char *user;
614
615                 user = uid_to_name(i->uid);
616
617                 printf("User: %s\n",
618                        strna(user));
619         }
620
621         if (i->gid_valid) {
622                 _cleanup_free_ char *group;
623
624                 group = gid_to_name(i->gid);
625
626                 printf("Group: %s\n",
627                        strna(group));
628         }
629
630         if (i->items_next) {
631                 printf("--\n");
632                 dump_items(i->items_next);
633         }
634 }
635
636 static void dump_hashmap_items(Hashmap *h) {
637         PolicyItem *i;
638         Iterator j;
639         char *k;
640
641         HASHMAP_FOREACH_KEY(i, k, h, j) {
642                 printf("Item for %s", k);
643                 dump_items(i);
644         }
645 }
646
647 void policy_dump(Policy *p) {
648
649         printf("→ Default Items:\n");
650         dump_items(p->default_items);
651
652         printf("→ Mandatory Items:\n");
653         dump_items(p->mandatory_items);
654
655         printf("→ Group Items:\n");
656         dump_hashmap_items(p->group_items);
657
658         printf("→ User Items:\n");
659         dump_hashmap_items(p->user_items);
660         exit(0);
661 }
662
663 static const char* const policy_item_type_table[_POLICY_ITEM_TYPE_MAX] = {
664         [_POLICY_ITEM_TYPE_UNSET] = "unset",
665         [POLICY_ITEM_ALLOW] = "allow",
666         [POLICY_ITEM_DENY] = "deny",
667 };
668 DEFINE_STRING_TABLE_LOOKUP(policy_item_type, PolicyItemType);
669
670 static const char* const policy_item_class_table[_POLICY_ITEM_CLASS_MAX] = {
671         [_POLICY_ITEM_CLASS_UNSET] = "unset",
672         [POLICY_ITEM_SEND] = "send",
673         [POLICY_ITEM_RECV] = "recv",
674         [POLICY_ITEM_OWN] = "own",
675         [POLICY_ITEM_OWN_PREFIX] = "own-prefix",
676         [POLICY_ITEM_USER] = "user",
677         [POLICY_ITEM_GROUP] = "group",
678 };
679 DEFINE_STRING_TABLE_LOOKUP(policy_item_class, PolicyItemClass);