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