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