sepolicy-analyze.c revision c30dd63f56ba5035eeb604b0b9b48f36ef5e8937
1#include <getopt.h>
2#include <unistd.h>
3#include <stddef.h>
4#include <stdlib.h>
5#include <sys/mman.h>
6#include <sys/types.h>
7#include <sys/stat.h>
8#include <fcntl.h>
9#include <stdio.h>
10#include <sepol/policydb/policydb.h>
11#include <sepol/policydb/services.h>
12#include <sepol/policydb/expand.h>
13#include <sepol/policydb/util.h>
14#include <stdbool.h>
15
16void usage(char *arg0)
17{
18    fprintf(stderr, "%s [-e|--equiv] [-d|--diff] [-D|--dups] [-p|--permissive] -P <policy file>\n", arg0);
19    exit(1);
20}
21
22int load_policy(char *filename, policydb_t * policydb, struct policy_file *pf)
23{
24    int fd;
25    struct stat sb;
26    void *map;
27    int ret;
28
29    fd = open(filename, O_RDONLY);
30    if (fd < 0) {
31        fprintf(stderr, "Can't open '%s':  %s\n", filename, strerror(errno));
32        return 1;
33    }
34    if (fstat(fd, &sb) < 0) {
35        fprintf(stderr, "Can't stat '%s':  %s\n", filename, strerror(errno));
36        close(fd);
37        return 1;
38    }
39    map = mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
40    if (map == MAP_FAILED) {
41        fprintf(stderr, "Can't mmap '%s':  %s\n", filename, strerror(errno));
42        close(fd);
43        return 1;
44    }
45
46    policy_file_init(pf);
47    pf->type = PF_USE_MEMORY;
48    pf->data = map;
49    pf->len = sb.st_size;
50    if (policydb_init(policydb)) {
51        fprintf(stderr, "Could not initialize policydb!\n");
52        close(fd);
53        munmap(map, sb.st_size);
54        return 1;
55    }
56    ret = policydb_read(policydb, pf, 0);
57    if (ret) {
58        fprintf(stderr, "error(s) encountered while parsing configuration\n");
59        close(fd);
60        munmap(map, sb.st_size);
61        return 1;
62    }
63
64    return 0;
65}
66
67static int insert_type_rule(avtab_key_t * k, avtab_datum_t * d,
68                            struct avtab_node *type_rules)
69{
70    struct avtab_node *p, *c, *n;
71
72    for (p = type_rules, c = type_rules->next; c; p = c, c = c->next) {
73        /*
74         * Find the insertion point, keeping the list
75         * ordered by source type, then target type, then
76         * target class.
77         */
78        if (k->source_type < c->key.source_type)
79            break;
80        if (k->source_type == c->key.source_type &&
81            k->target_type < c->key.target_type)
82            break;
83        if (k->source_type == c->key.source_type &&
84            k->target_type == c->key.target_type &&
85            k->target_class <= c->key.target_class)
86            break;
87    }
88
89    if (c &&
90        k->source_type == c->key.source_type &&
91        k->target_type == c->key.target_type &&
92        k->target_class == c->key.target_class) {
93        c->datum.data |= d->data;
94        return 0;
95    }
96
97    /* Insert the rule */
98    n = malloc(sizeof(struct avtab_node));
99    if (!n) {
100        fprintf(stderr, "out of memory\n");
101        exit(1);
102    }
103
104    n->key = *k;
105    n->datum = *d;
106    n->next = p->next;
107    p->next = n;
108    return 0;
109}
110
111static int create_type_rules_helper(avtab_key_t * k, avtab_datum_t * d,
112                                    void *args)
113{
114    struct avtab_node *type_rules = args;
115    avtab_key_t key;
116
117    /*
118     * Insert the rule into the list for
119     * the source type.  The source type value
120     * is cleared as we want to compare against other type
121     * rules with different source types.
122     */
123    key = *k;
124    key.source_type = 0;
125    if (k->source_type == k->target_type) {
126        /* Clear target type as well; this is a self rule. */
127        key.target_type = 0;
128    }
129    if (insert_type_rule(&key, d, &type_rules[k->source_type - 1]))
130        return -1;
131
132    if (k->source_type == k->target_type)
133        return 0;
134
135    /*
136     * If the target type differs, then we also
137     * insert the rule into the list for the target
138     * type.  We clear the target type value so that
139     * we can compare against other type rules with
140     * different target types.
141     */
142    key = *k;
143    key.target_type = 0;
144    if (insert_type_rule(&key, d, &type_rules[k->target_type - 1]))
145        return -1;
146
147    return 0;
148}
149
150static int create_type_rules(avtab_key_t * k, avtab_datum_t * d, void *args)
151{
152    if (k->specified & AVTAB_ALLOWED)
153        return create_type_rules_helper(k, d, args);
154    return 0;
155}
156
157static int create_type_rules_cond(avtab_key_t * k, avtab_datum_t * d,
158                                  void *args)
159{
160    if ((k->specified & (AVTAB_ALLOWED|AVTAB_ENABLED)) ==
161        (AVTAB_ALLOWED|AVTAB_ENABLED))
162        return create_type_rules_helper(k, d, args);
163    return 0;
164}
165
166static void free_type_rules(struct avtab_node *l)
167{
168    struct avtab_node *tmp;
169
170    while (l) {
171        tmp = l;
172        l = l->next;
173        free(tmp);
174    }
175}
176
177static void display_allow(policydb_t *policydb, avtab_key_t *key, int idx,
178                          uint32_t perms)
179{
180    printf("    allow %s %s:%s { %s };\n",
181           policydb->p_type_val_to_name[key->source_type
182                                        ? key->source_type - 1 : idx],
183           key->target_type == key->source_type ? "self" :
184           policydb->p_type_val_to_name[key->target_type
185                                        ? key->target_type - 1 : idx],
186           policydb->p_class_val_to_name[key->target_class - 1],
187           sepol_av_to_string
188           (policydb, key->target_class, perms));
189}
190
191static int find_match(policydb_t *policydb, struct avtab_node *l1,
192                      int idx1, struct avtab_node *l2, int idx2)
193{
194    struct avtab_node *c;
195    uint32_t perms1, perms2;
196
197    for (c = l2; c; c = c->next) {
198        if (l1->key.source_type < c->key.source_type)
199            break;
200        if (l1->key.source_type == c->key.source_type &&
201            l1->key.target_type < c->key.target_type)
202            break;
203        if (l1->key.source_type == c->key.source_type &&
204            l1->key.target_type == c->key.target_type &&
205            l1->key.target_class <= c->key.target_class)
206            break;
207    }
208
209    if (c &&
210        l1->key.source_type == c->key.source_type &&
211        l1->key.target_type == c->key.target_type &&
212        l1->key.target_class == c->key.target_class) {
213        perms1 = l1->datum.data & ~c->datum.data;
214        perms2 = c->datum.data & ~l1->datum.data;
215        if (perms1 || perms2) {
216            if (perms1)
217                display_allow(policydb, &l1->key, idx1, perms1);
218            if (perms2)
219                display_allow(policydb, &c->key, idx2, perms2);
220            printf("\n");
221            return 1;
222        }
223    }
224
225    return 0;
226}
227
228static int analyze_types(policydb_t * policydb, char equiv, char diff)
229{
230    avtab_t exp_avtab, exp_cond_avtab;
231    struct avtab_node *type_rules, *l1, *l2;
232    struct type_datum *type;
233    size_t i, j;
234
235    /*
236     * Create a list of access vector rules for each type
237     * from the access vector table.
238     */
239    type_rules = malloc(sizeof(struct avtab_node) * policydb->p_types.nprim);
240    if (!type_rules) {
241        fprintf(stderr, "out of memory\n");
242        exit(1);
243    }
244    memset(type_rules, 0, sizeof(struct avtab_node) * policydb->p_types.nprim);
245
246    if (avtab_init(&exp_avtab) || avtab_init(&exp_cond_avtab)) {
247        fputs("out of memory\n", stderr);
248        return -1;
249    }
250
251    if (expand_avtab(policydb, &policydb->te_avtab, &exp_avtab)) {
252        fputs("out of memory\n", stderr);
253        avtab_destroy(&exp_avtab);
254        return -1;
255    }
256
257    if (expand_avtab(policydb, &policydb->te_cond_avtab, &exp_cond_avtab)) {
258        fputs("out of memory\n", stderr);
259        avtab_destroy(&exp_avtab);
260        return -1;
261    }
262
263    if (avtab_map(&exp_avtab, create_type_rules, type_rules))
264        exit(1);
265
266    if (avtab_map(&exp_cond_avtab, create_type_rules_cond, type_rules))
267        exit(1);
268
269    avtab_destroy(&exp_avtab);
270    avtab_destroy(&exp_cond_avtab);
271
272    /*
273     * Compare the type lists and identify similar types.
274     */
275    for (i = 0; i < policydb->p_types.nprim - 1; i++) {
276        if (!type_rules[i].next)
277            continue;
278        type = policydb->type_val_to_struct[i];
279        if (type->flavor) {
280            free_type_rules(type_rules[i].next);
281            type_rules[i].next = NULL;
282            continue;
283        }
284        for (j = i + 1; j < policydb->p_types.nprim; j++) {
285            type = policydb->type_val_to_struct[j];
286            if (type->flavor) {
287                free_type_rules(type_rules[j].next);
288                type_rules[j].next = NULL;
289                continue;
290            }
291            for (l1 = type_rules[i].next, l2 = type_rules[j].next;
292                 l1 && l2; l1 = l1->next, l2 = l2->next) {
293                if (l1->key.source_type != l2->key.source_type)
294                    break;
295                if (l1->key.target_type != l2->key.target_type)
296                    break;
297                if (l1->key.target_class != l2->key.target_class
298                    || l1->datum.data != l2->datum.data)
299                    break;
300            }
301            if (l1 || l2) {
302                if (diff) {
303                    printf
304                        ("Types %s and %s differ, starting with:\n",
305                         policydb->p_type_val_to_name[i],
306                         policydb->p_type_val_to_name[j]);
307
308                    if (l1 && l2) {
309                        if (find_match(policydb, l1, i, l2, j))
310                            continue;
311                        if (find_match(policydb, l2, j, l1, i))
312                            continue;
313                    }
314                    if (l1)
315                        display_allow(policydb, &l1->key, i, l1->datum.data);
316                    if (l2)
317                        display_allow(policydb, &l2->key, j, l2->datum.data);
318                    printf("\n");
319                }
320                continue;
321            }
322            free_type_rules(type_rules[j].next);
323            type_rules[j].next = NULL;
324            if (equiv) {
325                printf("Types %s and %s are equivalent.\n",
326                       policydb->p_type_val_to_name[i],
327                       policydb->p_type_val_to_name[j]);
328            }
329        }
330        free_type_rules(type_rules[i].next);
331        type_rules[i].next = NULL;
332    }
333
334    free(type_rules);
335    return 0;
336}
337
338static int find_dups_helper(avtab_key_t * k, avtab_datum_t * d,
339                            void *args)
340{
341    policydb_t *policydb = args;
342    ebitmap_t *sattr, *tattr;
343    ebitmap_node_t *snode, *tnode;
344    unsigned int i, j;
345    avtab_key_t avkey;
346    avtab_ptr_t node;
347    struct type_datum *stype, *ttype, *stype2, *ttype2;
348    bool attrib1, attrib2;
349
350    if (!(k->specified & AVTAB_ALLOWED))
351        return 0;
352
353    if (k->source_type == k->target_type)
354        return 0; /* self rule */
355
356    avkey.target_class = k->target_class;
357    avkey.specified = k->specified;
358
359    sattr = &policydb->type_attr_map[k->source_type - 1];
360    tattr = &policydb->type_attr_map[k->target_type - 1];
361    stype = policydb->type_val_to_struct[k->source_type - 1];
362    ttype = policydb->type_val_to_struct[k->target_type - 1];
363    attrib1 = stype->flavor || ttype->flavor;
364    ebitmap_for_each_bit(sattr, snode, i) {
365        if (!ebitmap_node_get_bit(snode, i))
366            continue;
367        ebitmap_for_each_bit(tattr, tnode, j) {
368            if (!ebitmap_node_get_bit(tnode, j))
369                continue;
370            avkey.source_type = i + 1;
371            avkey.target_type = j + 1;
372            if (avkey.source_type == k->source_type &&
373                avkey.target_type == k->target_type)
374                continue;
375            if (avkey.source_type == avkey.target_type)
376                continue; /* self rule */
377            stype2 = policydb->type_val_to_struct[avkey.source_type - 1];
378            ttype2 = policydb->type_val_to_struct[avkey.target_type - 1];
379            attrib2 = stype2->flavor || ttype2->flavor;
380            if (attrib1 && attrib2)
381                continue; /* overlapping attribute-based rules */
382            for (node = avtab_search_node(&policydb->te_avtab, &avkey);
383                 node != NULL;
384                 node = avtab_search_node_next(node, avkey.specified)) {
385                uint32_t perms = node->datum.data & d->data;
386                if ((attrib1 && perms == node->datum.data) ||
387                    (attrib2 && perms == d->data)) {
388                    /*
389                     * The attribute-based rule is a superset of the
390                     * non-attribute-based rule.  This is a dup.
391                     */
392                    printf("Duplicate allow rule found:\n");
393                    display_allow(policydb, k, i, d->data);
394                    display_allow(policydb, &node->key, i, node->datum.data);
395                    printf("\n");
396                }
397            }
398        }
399    }
400
401    return 0;
402}
403
404static int find_dups(policydb_t * policydb)
405{
406    if (avtab_map(&policydb->te_avtab, find_dups_helper, policydb))
407        return -1;
408    return 0;
409}
410
411static int list_permissive(policydb_t * policydb)
412{
413    struct ebitmap_node *n;
414    unsigned int bit;
415
416    /*
417     * iterate over all domains and check if domain is in permissive
418     */
419    ebitmap_for_each_bit(&policydb->permissive_map, n, bit)
420    {
421        if (ebitmap_node_get_bit(n, bit)) {
422            printf("%s\n", policydb->p_type_val_to_name[bit -1]);
423        }
424    }
425    return 0;
426}
427
428int main(int argc, char **argv)
429{
430    char *policy = NULL;
431    struct policy_file pf;
432    policydb_t policydb;
433    char ch;
434    char equiv = 0, diff = 0, dups = 0, permissive = 0;
435
436    struct option long_options[] = {
437        {"equiv", no_argument, NULL, 'e'},
438        {"diff", no_argument, NULL, 'd'},
439        {"dups", no_argument, NULL, 'D'},
440        {"permissive", no_argument, NULL, 'p'},
441        {"policy", required_argument, NULL, 'P'},
442        {NULL, 0, NULL, 0}
443    };
444
445    while ((ch = getopt_long(argc, argv, "edDpP:", long_options, NULL)) != -1) {
446        switch (ch) {
447        case 'e':
448            equiv = 1;
449            break;
450        case 'd':
451            diff = 1;
452            break;
453        case 'D':
454            dups = 1;
455            break;
456        case 'p':
457            permissive = 1;
458            break;
459        case 'P':
460            policy = optarg;
461            break;
462        default:
463            usage(argv[0]);
464        }
465    }
466
467    if (!policy || (!equiv && !diff && !dups && !permissive))
468        usage(argv[0]);
469
470    if (load_policy(policy, &policydb, &pf))
471        exit(1);
472
473    if (equiv || diff)
474        analyze_types(&policydb, equiv, diff);
475
476    if (dups)
477        find_dups(&policydb);
478
479    if (permissive)
480        list_permissive(&policydb);
481
482    policydb_destroy(&policydb);
483
484    return 0;
485}
486