chiark / gitweb /
bus-proxyd: do not free NULL items
[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                                 policy_category = POLICY_CATEGORY_USER;
216                                 state = STATE_POLICY;
217                         } else {
218                                 log_error("Unexpected token (5) in %s:%u.", path, line);
219                                 return -EINVAL;
220                         }
221
222                         break;
223
224                 case STATE_POLICY_GROUP:
225
226                         if (t == XML_ATTRIBUTE_VALUE) {
227                                 free(policy_group);
228                                 policy_group = name;
229                                 name = NULL;
230                                 policy_category = POLICY_CATEGORY_GROUP;
231                                 state = STATE_POLICY;
232                         } else {
233                                 log_error("Unexpected token (6) at %s:%u.", path, line);
234                                 return -EINVAL;
235                         }
236
237                         break;
238
239                 case STATE_POLICY_OTHER_ATTRIBUTE:
240
241                         if (t == XML_ATTRIBUTE_VALUE)
242                                 state = STATE_POLICY;
243                         else {
244                                 log_error("Unexpected token (7) in %s:%u.", path, line);
245                                 return -EINVAL;
246                         }
247
248                         break;
249
250                 case STATE_ALLOW_DENY:
251
252                         assert(i);
253
254                         if (t == XML_ATTRIBUTE_NAME) {
255                                 PolicyItemClass ic;
256
257                                 if (startswith(name, "send_"))
258                                         ic = POLICY_ITEM_SEND;
259                                 else if (startswith(name, "receive_"))
260                                         ic = POLICY_ITEM_RECV;
261                                 else if (streq(name, "own"))
262                                         ic = POLICY_ITEM_OWN;
263                                 else if (streq(name, "own_prefix"))
264                                         ic = POLICY_ITEM_OWN_PREFIX;
265                                 else if (streq(name, "user"))
266                                         ic = POLICY_ITEM_USER;
267                                 else if (streq(name, "group"))
268                                         ic = POLICY_ITEM_GROUP;
269                                 else {
270                                         log_error("Unknown attribute %s= at %s:%u, ignoring.", name, path, line);
271                                         state = STATE_ALLOW_DENY_OTHER_ATTRIBUTE;
272                                         break;
273                                 }
274
275                                 if (i->class != _POLICY_ITEM_CLASS_UNSET && ic != i->class) {
276                                         log_error("send_ and receive_ fields mixed on same tag at %s:%u.", path, line);
277                                         return -EINVAL;
278                                 }
279
280                                 i->class = ic;
281
282                                 if (ic == POLICY_ITEM_SEND || ic == POLICY_ITEM_RECV) {
283                                         const char *u;
284
285                                         u = strchr(name, '_');
286                                         assert(u);
287
288                                         u++;
289
290                                         if (streq(u, "interface"))
291                                                 state = STATE_ALLOW_DENY_INTERFACE;
292                                         else if (streq(u, "member"))
293                                                 state = STATE_ALLOW_DENY_MEMBER;
294                                         else if (streq(u, "error"))
295                                                 state = STATE_ALLOW_DENY_ERROR;
296                                         else if (streq(u, "path"))
297                                                 state = STATE_ALLOW_DENY_PATH;
298                                         else if (streq(u, "type"))
299                                                 state = STATE_ALLOW_DENY_MESSAGE_TYPE;
300                                         else if ((streq(u, "destination") && ic == POLICY_ITEM_SEND) ||
301                                                  (streq(u, "sender") && ic == POLICY_ITEM_RECV))
302                                                 state = STATE_ALLOW_DENY_NAME;
303                                         else {
304                                                 log_error("Unknown attribute %s= at %s:%u, ignoring.", name, path, line);
305                                                 state = STATE_ALLOW_DENY_OTHER_ATTRIBUTE;
306                                                 break;
307                                         }
308                                 } else
309                                         state = STATE_ALLOW_DENY_NAME;
310
311                         } else if (t == XML_TAG_CLOSE_EMPTY ||
312                                    (t == XML_TAG_CLOSE && streq(name, i->type == POLICY_ITEM_ALLOW ? "allow" : "deny"))) {
313
314                                 if (i->class == _POLICY_ITEM_CLASS_UNSET) {
315                                         log_error("Policy not set at %s:%u.", path, line);
316                                         return -EINVAL;
317                                 }
318
319                                 if (policy_category == POLICY_CATEGORY_DEFAULT)
320                                         LIST_PREPEND(items, p->default_items, i);
321                                 else if (policy_category == POLICY_CATEGORY_MANDATORY)
322                                         LIST_PREPEND(items, p->default_items, i);
323                                 else if (policy_category == POLICY_CATEGORY_USER) {
324                                         PolicyItem *first;
325
326                                         assert_cc(sizeof(uid_t) == sizeof(uint32_t));
327
328                                         r = hashmap_ensure_allocated(&p->user_items, trivial_hash_func, trivial_compare_func);
329                                         if (r < 0)
330                                                 return log_oom();
331
332                                         first = hashmap_get(p->user_items, UINT32_TO_PTR(i->uid));
333                                         LIST_PREPEND(items, first, i);
334
335                                         r = hashmap_replace(p->user_items, UINT32_TO_PTR(i->uid), first);
336                                         if (r < 0) {
337                                                 LIST_REMOVE(items, first, i);
338                                                 return log_oom();
339                                         }
340                                 } else if (policy_category == POLICY_CATEGORY_GROUP) {
341                                         PolicyItem *first;
342
343                                         assert_cc(sizeof(gid_t) == sizeof(uint32_t));
344
345                                         r = hashmap_ensure_allocated(&p->group_items, trivial_hash_func, trivial_compare_func);
346                                         if (r < 0)
347                                                 return log_oom();
348
349                                         first = hashmap_get(p->group_items, UINT32_TO_PTR(i->gid));
350                                         LIST_PREPEND(items, first, i);
351
352                                         r = hashmap_replace(p->group_items, UINT32_TO_PTR(i->gid), first);
353                                         if (r < 0) {
354                                                 LIST_REMOVE(items, first, i);
355                                                 return log_oom();
356                                         }
357                                 }
358
359                                 state = STATE_POLICY;
360                                 i = NULL;
361
362                         } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
363                                 log_error("Unexpected token (8) at %s:%u.", path, line);
364                                 return -EINVAL;
365                         }
366
367                         break;
368
369                 case STATE_ALLOW_DENY_INTERFACE:
370
371                         if (t == XML_ATTRIBUTE_VALUE) {
372                                 assert(i);
373                                 if (i->interface) {
374                                         log_error("Duplicate interface at %s:%u.", path, line);
375                                         return -EINVAL;
376                                 }
377
378                                 i->interface = name;
379                                 name = NULL;
380                                 state = STATE_ALLOW_DENY;
381                         } else {
382                                 log_error("Unexpected token (9) at %s:%u.", path, line);
383                                 return -EINVAL;
384                         }
385
386                         break;
387
388                 case STATE_ALLOW_DENY_MEMBER:
389
390                         if (t == XML_ATTRIBUTE_VALUE) {
391                                 assert(i);
392                                 if (i->member) {
393                                         log_error("Duplicate member in %s:%u.", path, line);
394                                         return -EINVAL;
395                                 }
396
397                                 i->member = name;
398                                 name = NULL;
399                                 state = STATE_ALLOW_DENY;
400                         } else {
401                                 log_error("Unexpected token (10) in %s:%u.", path, line);
402                                 return -EINVAL;
403                         }
404
405                         break;
406
407                 case STATE_ALLOW_DENY_ERROR:
408
409                         if (t == XML_ATTRIBUTE_VALUE) {
410                                 assert(i);
411                                 if (i->error) {
412                                         log_error("Duplicate error in %s:%u.", path, line);
413                                         return -EINVAL;
414                                 }
415
416                                 i->error = name;
417                                 name = NULL;
418                                 state = STATE_ALLOW_DENY;
419                         } else {
420                                 log_error("Unexpected token (11) in %s:%u.", path, line);
421                                 return -EINVAL;
422                         }
423
424                         break;
425
426                 case STATE_ALLOW_DENY_PATH:
427
428                         if (t == XML_ATTRIBUTE_VALUE) {
429                                 assert(i);
430                                 if (i->path) {
431                                         log_error("Duplicate path in %s:%u.", path, line);
432                                         return -EINVAL;
433                                 }
434
435                                 i->path = name;
436                                 name = NULL;
437                                 state = STATE_ALLOW_DENY;
438                         } else {
439                                 log_error("Unexpected token (12) in %s:%u.", path, line);
440                                 return -EINVAL;
441                         }
442
443                         break;
444
445                 case STATE_ALLOW_DENY_MESSAGE_TYPE:
446
447                         if (t == XML_ATTRIBUTE_VALUE) {
448                                 assert(i);
449
450                                 if (i->message_type != 0) {
451                                         log_error("Duplicate message type in %s:%u.", path, line);
452                                         return -EINVAL;
453                                 }
454
455                                 r = bus_message_type_from_string(name, &i->message_type);
456                                 if (r < 0) {
457                                         log_error("Invalid message type in %s:%u.", path, line);
458                                         return -EINVAL;
459                                 }
460
461                                 state = STATE_ALLOW_DENY;
462                         } else {
463                                 log_error("Unexpected token (13) in %s:%u.", path, line);
464                                 return -EINVAL;
465                         }
466
467                         break;
468
469                 case STATE_ALLOW_DENY_NAME:
470
471                         if (t == XML_ATTRIBUTE_VALUE) {
472                                 assert(i);
473                                 if (i->name) {
474                                         log_error("Duplicate name in %s:%u.", path, line);
475                                         return -EINVAL;
476                                 }
477
478                                 i->name = name;
479                                 name = NULL;
480                                 state = STATE_ALLOW_DENY;
481                         } else {
482                                 log_error("Unexpected token (14) in %s:%u.", path, line);
483                                 return -EINVAL;
484                         }
485
486                         break;
487
488                 case STATE_ALLOW_DENY_OTHER_ATTRIBUTE:
489
490                         if (t == XML_ATTRIBUTE_VALUE)
491                                 state = STATE_ALLOW_DENY;
492                         else {
493                                 log_error("Unexpected token (15) in %s:%u.", path, line);
494                                 return -EINVAL;
495                         }
496
497                         break;
498
499                 case STATE_OTHER:
500
501                         if (t == XML_TAG_OPEN)
502                                 n_other++;
503                         else if (t == XML_TAG_CLOSE || t == XML_TAG_CLOSE_EMPTY) {
504
505                                 if (n_other == 0)
506                                         state = STATE_BUSCONFIG;
507                                 else
508                                         n_other--;
509                         }
510
511                         break;
512                 }
513         }
514 }
515
516 int policy_load(Policy *p) {
517         _cleanup_strv_free_ char **l = NULL;
518         char **i;
519         int r;
520
521         assert(p);
522
523         file_load(p, "/etc/dbus-1/system.conf");
524         file_load(p, "/etc/dbus-1/system-local.conf");
525
526         r = conf_files_list(&l, ".conf", NULL, "/etc/dbus-1/system.d/", NULL);
527         if (r < 0) {
528                 log_error("Failed to get configuration file list: %s", strerror(-r));
529                 return r;
530         }
531
532         STRV_FOREACH(i, l)
533                 file_load(p, *i);
534
535         return 0;
536 }
537
538 void policy_free(Policy *p) {
539         PolicyItem *i, *first;
540
541         if (!p)
542                 return;
543
544         while ((i = p->default_items)) {
545                 LIST_REMOVE(items, p->default_items, i);
546                 policy_item_free(i);
547         }
548
549         while ((i = p->mandatory_items)) {
550                 LIST_REMOVE(items, p->mandatory_items, i);
551                 policy_item_free(i);
552         }
553
554         while ((first = hashmap_steal_first(p->user_items))) {
555
556                 while ((i = first)) {
557                         LIST_REMOVE(items, first, i);
558                         policy_item_free(i);
559                 }
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
570         hashmap_free(p->user_items);
571         hashmap_free(p->group_items);
572
573         p->user_items = p->group_items = NULL;
574 }
575
576 static void dump_items(PolicyItem *i) {
577
578         if (!i)
579                 return;
580
581         printf("Type: %s\n"
582                "Class: %s\n",
583                policy_item_type_to_string(i->type),
584                policy_item_class_to_string(i->class));
585
586         if (i->interface)
587                 printf("Interface: %s\n",
588                        i->interface);
589
590         if (i->member)
591                 printf("Member: %s\n",
592                        i->member);
593
594         if (i->error)
595                 printf("Error: %s\n",
596                        i->error);
597
598         if (i->path)
599                 printf("Path: %s\n",
600                        i->path);
601
602         if (i->name)
603                 printf("Name: %s\n",
604                        i->name);
605
606         if (i->message_type != 0)
607                 printf("Message Type: %s\n",
608                        bus_message_type_to_string(i->message_type));
609
610         if (i->uid_valid) {
611                 _cleanup_free_ char *user;
612
613                 user = uid_to_name(i->uid);
614
615                 printf("User: %s\n",
616                        strna(user));
617         }
618
619         if (i->gid_valid) {
620                 _cleanup_free_ char *group;
621
622                 group = gid_to_name(i->gid);
623
624                 printf("Group: %s\n",
625                        strna(group));
626         }
627
628         if (i->items_next) {
629                 printf("--\n");
630                 dump_items(i->items_next);
631         }
632 }
633
634 static void dump_hashmap_items(Hashmap *h) {
635         PolicyItem *i;
636         Iterator j;
637         void *k;
638
639         HASHMAP_FOREACH_KEY(i, k, h, j) {
640                 printf("Item for %u:\n", PTR_TO_UINT(k));
641                 dump_items(i);
642         }
643 }
644
645 noreturn void policy_dump(Policy *p) {
646
647         printf("→ Default Items:\n");
648         dump_items(p->default_items);
649
650         printf("→ Mandatory Items:\n");
651         dump_items(p->mandatory_items);
652
653         printf("→ Group Items:\n");
654         dump_hashmap_items(p->group_items);
655
656         printf("→ User Items:\n");
657         dump_hashmap_items(p->user_items);
658         exit(0);
659 }
660
661 static const char* const policy_item_type_table[_POLICY_ITEM_TYPE_MAX] = {
662         [_POLICY_ITEM_TYPE_UNSET] = "unset",
663         [POLICY_ITEM_ALLOW] = "allow",
664         [POLICY_ITEM_DENY] = "deny",
665 };
666 DEFINE_STRING_TABLE_LOOKUP(policy_item_type, PolicyItemType);
667
668 static const char* const policy_item_class_table[_POLICY_ITEM_CLASS_MAX] = {
669         [_POLICY_ITEM_CLASS_UNSET] = "unset",
670         [POLICY_ITEM_SEND] = "send",
671         [POLICY_ITEM_RECV] = "recv",
672         [POLICY_ITEM_OWN] = "own",
673         [POLICY_ITEM_OWN_PREFIX] = "own-prefix",
674         [POLICY_ITEM_USER] = "user",
675         [POLICY_ITEM_GROUP] = "group",
676 };
677 DEFINE_STRING_TABLE_LOOKUP(policy_item_class, PolicyItemClass);