1#include <ctype.h>
2#include <fcntl.h>
3#include <getopt.h>
4#include <stdbool.h>
5#include <stdio.h>
6#include <sys/mman.h>
7#include <sys/stat.h>
8#include <sys/types.h>
9#include <unistd.h>
10
11#include "neverallow.h"
12
13static int debug;
14static int warn;
15
16void neverallow_usage() {
17    fprintf(stderr, "\tneverallow [-w|--warn] [-d|--debug] [-n|--neverallows <neverallow-rules>] | [-f|--file <neverallow-file>]\n");
18}
19
20static int read_typeset(policydb_t *policydb, char **ptr, char *end,
21                        type_set_t *typeset, uint32_t *flags)
22{
23    const char *keyword = "self";
24    size_t keyword_size = strlen(keyword), len;
25    char *p = *ptr;
26    unsigned openparens = 0;
27    char *start, *id;
28    type_datum_t *type;
29    struct ebitmap_node *n;
30    unsigned int bit;
31    bool negate = false;
32    int rc;
33
34    do {
35        while (p < end && isspace(*p))
36            p++;
37
38        if (p == end)
39            goto err;
40
41        if (*p == '~') {
42            if (debug)
43                printf(" ~");
44            typeset->flags = TYPE_COMP;
45            p++;
46            while (p < end && isspace(*p))
47                p++;
48            if (p == end)
49                goto err;
50        }
51
52        if (*p == '{') {
53            if (debug && !openparens)
54                printf(" {");
55            openparens++;
56            p++;
57            continue;
58        }
59
60        if (*p == '}') {
61            if (debug && openparens == 1)
62                printf(" }");
63            if (openparens == 0)
64                goto err;
65            openparens--;
66            p++;
67            continue;
68        }
69
70        if (*p == '*') {
71            if (debug)
72                printf(" *");
73            typeset->flags = TYPE_STAR;
74            p++;
75            continue;
76        }
77
78        if (*p == '-') {
79            if (debug)
80                printf(" -");
81            negate = true;
82            p++;
83            continue;
84        }
85
86        if (*p == '#') {
87            while (p < end && *p != '\n')
88                p++;
89            continue;
90        }
91
92        start = p;
93        while (p < end && !isspace(*p) && *p != ':' && *p != ';' && *p != '{' && *p != '}' && *p != '#')
94            p++;
95
96        if (p == start)
97            goto err;
98
99        len = p - start;
100        if (len == keyword_size && !strncmp(start, keyword, keyword_size)) {
101            if (debug)
102                printf(" self");
103            *flags |= RULE_SELF;
104            continue;
105        }
106
107        id = calloc(1, len + 1);
108        if (!id)
109            goto err;
110        memcpy(id, start, len);
111        if (debug)
112            printf(" %s", id);
113        type = hashtab_search(policydb->p_types.table, id);
114        if (!type) {
115            if (warn)
116                fprintf(stderr, "Warning!  Type or attribute %s used in neverallow undefined in policy being checked.\n", id);
117            negate = false;
118            continue;
119        }
120        free(id);
121
122        if (type->flavor == TYPE_ATTRIB) {
123            if (negate)
124                rc = ebitmap_union(&typeset->negset, &policydb->attr_type_map[type->s.value - 1]);
125            else
126                rc = ebitmap_union(&typeset->types, &policydb->attr_type_map[type->s.value - 1]);
127        } else if (negate) {
128            rc = ebitmap_set_bit(&typeset->negset, type->s.value - 1, 1);
129        } else {
130            rc = ebitmap_set_bit(&typeset->types, type->s.value - 1, 1);
131        }
132
133        negate = false;
134
135        if (rc)
136            goto err;
137
138    } while (p < end && openparens);
139
140    if (p == end)
141        goto err;
142
143    if (typeset->flags & TYPE_STAR) {
144        for (bit = 0; bit < policydb->p_types.nprim; bit++) {
145            if (ebitmap_get_bit(&typeset->negset, bit))
146                continue;
147            if (policydb->type_val_to_struct[bit] &&
148                policydb->type_val_to_struct[bit]->flavor == TYPE_ATTRIB)
149                continue;
150            if (ebitmap_set_bit(&typeset->types, bit, 1))
151                goto err;
152        }
153    }
154
155    ebitmap_for_each_bit(&typeset->negset, n, bit) {
156        if (!ebitmap_node_get_bit(n, bit))
157            continue;
158        if (ebitmap_set_bit(&typeset->types, bit, 0))
159            goto err;
160    }
161
162    if (typeset->flags & TYPE_COMP) {
163        for (bit = 0; bit < policydb->p_types.nprim; bit++) {
164            if (policydb->type_val_to_struct[bit] &&
165                policydb->type_val_to_struct[bit]->flavor == TYPE_ATTRIB)
166                continue;
167            if (ebitmap_get_bit(&typeset->types, bit))
168                ebitmap_set_bit(&typeset->types, bit, 0);
169            else {
170                if (ebitmap_set_bit(&typeset->types, bit, 1))
171                    goto err;
172            }
173        }
174    }
175
176    if (warn && ebitmap_length(&typeset->types) == 0 && !(*flags))
177        fprintf(stderr, "Warning!  Empty type set\n");
178
179    *ptr = p;
180    return 0;
181err:
182    return -1;
183}
184
185static int read_classperms(policydb_t *policydb, char **ptr, char *end,
186                           class_perm_node_t **perms)
187{
188    char *p = *ptr;
189    unsigned openparens = 0;
190    char *id, *start;
191    class_datum_t *cls = NULL;
192    perm_datum_t *perm = NULL;
193    class_perm_node_t *classperms = NULL, *node = NULL;
194    bool complement = false;
195
196    while (p < end && isspace(*p))
197        p++;
198
199    if (p == end || *p != ':')
200        goto err;
201    p++;
202
203    if (debug)
204        printf(" :");
205
206    do {
207        while (p < end && isspace(*p))
208            p++;
209
210        if (p == end)
211            goto err;
212
213        if (*p == '{') {
214            if (debug && !openparens)
215                printf(" {");
216            openparens++;
217            p++;
218            continue;
219        }
220
221        if (*p == '}') {
222            if (debug && openparens == 1)
223                printf(" }");
224            if (openparens == 0)
225                goto err;
226            openparens--;
227            p++;
228            continue;
229        }
230
231        if (*p == '#') {
232            while (p < end && *p != '\n')
233                p++;
234            continue;
235        }
236
237        start = p;
238        while (p < end && !isspace(*p) && *p != '{' && *p != '}' && *p != ';' && *p != '#')
239            p++;
240
241        if (p == start)
242            goto err;
243
244        id = calloc(1, p - start + 1);
245        if (!id)
246            goto err;
247        memcpy(id, start, p - start);
248        if (debug)
249            printf(" %s", id);
250        cls = hashtab_search(policydb->p_classes.table, id);
251        if (!cls) {
252            if (warn)
253                fprintf(stderr, "Warning!  Class %s used in neverallow undefined in policy being checked.\n", id);
254            continue;
255        }
256
257        node = calloc(1, sizeof *node);
258        if (!node)
259            goto err;
260        node->tclass = cls->s.value;
261        node->next = classperms;
262        classperms = node;
263        free(id);
264    } while (p < end && openparens);
265
266    if (p == end)
267        goto err;
268
269    if (warn && !classperms)
270        fprintf(stderr, "Warning!  Empty class set\n");
271
272    do {
273        while (p < end && isspace(*p))
274            p++;
275
276        if (p == end)
277            goto err;
278
279        if (*p == '~') {
280            if (debug)
281                printf(" ~");
282            complement = true;
283            p++;
284            while (p < end && isspace(*p))
285                p++;
286            if (p == end)
287                goto err;
288        }
289
290        if (*p == '{') {
291            if (debug && !openparens)
292                printf(" {");
293            openparens++;
294            p++;
295            continue;
296        }
297
298        if (*p == '}') {
299            if (debug && openparens == 1)
300                printf(" }");
301            if (openparens == 0)
302                goto err;
303            openparens--;
304            p++;
305            continue;
306        }
307
308        if (*p == '#') {
309            while (p < end && *p != '\n')
310                p++;
311            continue;
312        }
313
314        start = p;
315        while (p < end && !isspace(*p) && *p != '{' && *p != '}' && *p != ';' && *p != '#')
316            p++;
317
318        if (p == start)
319            goto err;
320
321        id = calloc(1, p - start + 1);
322        if (!id)
323            goto err;
324        memcpy(id, start, p - start);
325        if (debug)
326            printf(" %s", id);
327
328        if (!strcmp(id, "*")) {
329            for (node = classperms; node; node = node->next)
330                node->data = ~0;
331            continue;
332        }
333
334        for (node = classperms; node; node = node->next) {
335            cls = policydb->class_val_to_struct[node->tclass-1];
336            perm = hashtab_search(cls->permissions.table, id);
337            if (cls->comdatum && !perm)
338                perm = hashtab_search(cls->comdatum->permissions.table, id);
339            if (!perm) {
340                if (warn)
341                    fprintf(stderr, "Warning!  Permission %s used in neverallow undefined in class %s in policy being checked.\n", id, policydb->p_class_val_to_name[node->tclass-1]);
342                continue;
343            }
344            node->data |= 1U << (perm->s.value - 1);
345        }
346        free(id);
347    } while (p < end && openparens);
348
349    if (p == end)
350        goto err;
351
352    if (complement) {
353        for (node = classperms; node; node = node->next)
354            node->data = ~node->data;
355    }
356
357    if (warn) {
358        for (node = classperms; node; node = node->next)
359            if (!node->data)
360                fprintf(stderr, "Warning!  Empty permission set\n");
361    }
362
363    *perms = classperms;
364    *ptr = p;
365    return 0;
366err:
367    return -1;
368}
369
370static int check_neverallows(policydb_t *policydb, char *text, char *end)
371{
372    const char *keyword = "neverallow";
373    size_t keyword_size = strlen(keyword), len;
374    struct avrule *neverallows = NULL, *avrule;
375    char *p, *start;
376
377    p = text;
378    while (p < end) {
379        while (p < end && isspace(*p))
380            p++;
381
382        if (*p == '#') {
383            while (p < end && *p != '\n')
384                p++;
385            continue;
386        }
387
388        start = p;
389        while (p < end && !isspace(*p))
390            p++;
391
392        len = p - start;
393        if (len != keyword_size || strncmp(start, keyword, keyword_size))
394            continue;
395
396        if (debug)
397            printf("neverallow");
398
399        avrule = calloc(1, sizeof *avrule);
400        if (!avrule)
401            goto err;
402
403        avrule->specified = AVRULE_NEVERALLOW;
404
405        if (read_typeset(policydb, &p, end, &avrule->stypes, &avrule->flags))
406            goto err;
407
408        if (read_typeset(policydb, &p, end, &avrule->ttypes, &avrule->flags))
409            goto err;
410
411        if (read_classperms(policydb, &p, end, &avrule->perms))
412            goto err;
413
414        while (p < end && *p != ';')
415            p++;
416
417        if (p == end || *p != ';')
418            goto err;
419
420        if (debug)
421            printf(";\n");
422
423        avrule->next = neverallows;
424        neverallows = avrule;
425    }
426
427    if (!neverallows)
428        goto err;
429
430    return check_assertions(NULL, policydb, neverallows);
431err:
432    if (errno == ENOMEM) {
433        fprintf(stderr, "Out of memory while parsing neverallow rules\n");
434    } else
435        fprintf(stderr, "Error while parsing neverallow rules\n");
436    return -1;
437}
438
439static int check_neverallows_file(policydb_t *policydb, const char *filename)
440{
441    int fd;
442    struct stat sb;
443    char *text, *end;
444
445    fd = open(filename, O_RDONLY);
446    if (fd < 0) {
447        fprintf(stderr, "Could not open %s:  %s\n", filename, strerror(errno));
448        return -1;
449    }
450    if (fstat(fd, &sb) < 0) {
451        fprintf(stderr, "Can't stat '%s':  %s\n", filename, strerror(errno));
452        close(fd);
453        return -1;
454    }
455    text = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
456    end = text + sb.st_size;
457    if (text == MAP_FAILED) {
458        fprintf(stderr, "Can't mmap '%s':  %s\n", filename, strerror(errno));
459        close(fd);
460        return -1;
461    }
462    close(fd);
463    return check_neverallows(policydb, text, end);
464}
465
466static int check_neverallows_string(policydb_t *policydb, char *string, size_t len)
467{
468    char *text, *end;
469    text = string;
470    end = text + len;
471    return check_neverallows(policydb, text, end);
472}
473
474int neverallow_func (int argc, char **argv, policydb_t *policydb) {
475    char *rules = 0, *file = 0;
476    char ch;
477
478    struct option neverallow_options[] = {
479        {"debug", no_argument, NULL, 'd'},
480        {"file_input", required_argument, NULL, 'f'},
481        {"neverallow", required_argument, NULL, 'n'},
482        {"warn", no_argument, NULL, 'w'},
483        {NULL, 0, NULL, 0}
484    };
485
486    while ((ch = getopt_long(argc, argv, "df:n:w", neverallow_options, NULL)) != -1) {
487        switch (ch) {
488        case 'd':
489            debug = 1;
490            break;
491        case 'f':
492            file = optarg;
493            break;
494        case 'n':
495            rules = optarg;
496            break;
497        case 'w':
498            warn = 1;
499            break;
500        default:
501            USAGE_ERROR = true;
502            return -1;
503        }
504    }
505
506    if (!(rules || file) || (rules && file)){
507        USAGE_ERROR = true;
508        return -1;
509    }
510    if (file) {
511        return check_neverallows_file(policydb, file);
512    } else {
513        return check_neverallows_string(policydb, rules, strlen(rules));
514    }
515}
516