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    *ptr = p;
177    return 0;
178err:
179    return -1;
180}
181
182static int read_classperms(policydb_t *policydb, char **ptr, char *end,
183                           class_perm_node_t **perms)
184{
185    char *p = *ptr;
186    unsigned openparens = 0;
187    char *id, *start;
188    class_datum_t *cls = NULL;
189    perm_datum_t *perm = NULL;
190    class_perm_node_t *classperms = NULL, *node = NULL;
191    bool complement = false;
192
193    while (p < end && isspace(*p))
194        p++;
195
196    if (p == end || *p != ':')
197        goto err;
198    p++;
199
200    if (debug)
201        printf(" :");
202
203    do {
204        while (p < end && isspace(*p))
205            p++;
206
207        if (p == end)
208            goto err;
209
210        if (*p == '{') {
211            if (debug && !openparens)
212                printf(" {");
213            openparens++;
214            p++;
215            continue;
216        }
217
218        if (*p == '}') {
219            if (debug && openparens == 1)
220                printf(" }");
221            if (openparens == 0)
222                goto err;
223            openparens--;
224            p++;
225            continue;
226        }
227
228        if (*p == '#') {
229            while (p < end && *p != '\n')
230                p++;
231            continue;
232        }
233
234        start = p;
235        while (p < end && !isspace(*p) && *p != '{' && *p != '}' && *p != ';' && *p != '#')
236            p++;
237
238        if (p == start)
239            goto err;
240
241        id = calloc(1, p - start + 1);
242        if (!id)
243            goto err;
244        memcpy(id, start, p - start);
245        if (debug)
246            printf(" %s", id);
247        cls = hashtab_search(policydb->p_classes.table, id);
248        if (!cls) {
249            if (warn)
250                fprintf(stderr, "Warning!  Class %s used in neverallow undefined in policy being checked.\n", id);
251            continue;
252        }
253
254        node = calloc(1, sizeof *node);
255        if (!node)
256            goto err;
257        node->tclass = cls->s.value;
258        node->next = classperms;
259        classperms = node;
260        free(id);
261        id = NULL;
262    } while (p < end && openparens);
263
264    if (p == end)
265        goto err;
266
267    if (warn && !classperms)
268        fprintf(stderr, "Warning!  Empty class set\n");
269
270    do {
271        while (p < end && isspace(*p))
272            p++;
273
274        if (p == end)
275            goto err;
276
277        if (*p == '~') {
278            if (debug)
279                printf(" ~");
280            complement = true;
281            p++;
282            while (p < end && isspace(*p))
283                p++;
284            if (p == end)
285                goto err;
286        }
287
288        if (*p == '{') {
289            if (debug && !openparens)
290                printf(" {");
291            openparens++;
292            p++;
293            continue;
294        }
295
296        if (*p == '}') {
297            if (debug && openparens == 1)
298                printf(" }");
299            if (openparens == 0)
300                goto err;
301            openparens--;
302            p++;
303            continue;
304        }
305
306        if (*p == '#') {
307            while (p < end && *p != '\n')
308                p++;
309            continue;
310        }
311
312        start = p;
313        while (p < end && !isspace(*p) && *p != '{' && *p != '}' && *p != ';' && *p != '#')
314            p++;
315
316        if (p == start)
317            goto err;
318
319        id = calloc(1, p - start + 1);
320        if (!id)
321            goto err;
322        memcpy(id, start, p - start);
323        if (debug)
324            printf(" %s", id);
325
326        if (!strcmp(id, "*")) {
327            for (node = classperms; node; node = node->next)
328                node->data = ~0;
329            free(id);
330            id = NULL;
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        id = NULL;
348    } while (p < end && openparens);
349
350    if (p == end)
351        goto err;
352
353    if (complement) {
354        for (node = classperms; node; node = node->next)
355            node->data = ~node->data;
356    }
357
358    if (warn) {
359        for (node = classperms; node; node = node->next)
360            if (!node->data)
361                fprintf(stderr, "Warning!  Empty permission set\n");
362    }
363
364    *perms = classperms;
365    *ptr = p;
366    return 0;
367err:
368    // free classperms memory
369    for (node = classperms; node; ) {
370      class_perm_node_t *freeptr = node;
371      node = node->next;
372      free(freeptr);
373    }
374    return -1;
375}
376
377static int check_neverallows(policydb_t *policydb, char *text, char *end)
378{
379    const char *keyword = "neverallow";
380    size_t keyword_size = strlen(keyword), len;
381    struct avrule *neverallows = NULL, *avrule;
382    char *p, *start;
383
384    p = text;
385    while (p < end) {
386        while (p < end && isspace(*p))
387            p++;
388
389        if (*p == '#') {
390            while (p < end && *p != '\n')
391                p++;
392            continue;
393        }
394
395        start = p;
396        while (p < end && !isspace(*p))
397            p++;
398
399        len = p - start;
400        if (len != keyword_size || strncmp(start, keyword, keyword_size))
401            continue;
402
403        if (debug)
404            printf("neverallow");
405
406        avrule = calloc(1, sizeof *avrule);
407        if (!avrule)
408            goto err;
409
410        avrule->specified = AVRULE_NEVERALLOW;
411
412        if (read_typeset(policydb, &p, end, &avrule->stypes, &avrule->flags))
413            goto err;
414
415        if (read_typeset(policydb, &p, end, &avrule->ttypes, &avrule->flags))
416            goto err;
417
418        if (read_classperms(policydb, &p, end, &avrule->perms))
419            goto err;
420
421        while (p < end && *p != ';')
422            p++;
423
424        if (p == end || *p != ';')
425            goto err;
426
427        if (debug)
428            printf(";\n");
429
430        avrule->next = neverallows;
431        neverallows = avrule;
432    }
433
434    if (!neverallows)
435        goto err;
436
437    return check_assertions(NULL, policydb, neverallows);
438err:
439    if (errno == ENOMEM) {
440        fprintf(stderr, "Out of memory while parsing neverallow rules\n");
441    } else
442        fprintf(stderr, "Error while parsing neverallow rules\n");
443    return -1;
444}
445
446static int check_neverallows_file(policydb_t *policydb, const char *filename)
447{
448    int fd;
449    struct stat sb;
450    char *text, *end;
451
452    fd = open(filename, O_RDONLY);
453    if (fd < 0) {
454        fprintf(stderr, "Could not open %s:  %s\n", filename, strerror(errno));
455        return -1;
456    }
457    if (fstat(fd, &sb) < 0) {
458        fprintf(stderr, "Can't stat '%s':  %s\n", filename, strerror(errno));
459        close(fd);
460        return -1;
461    }
462    text = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
463    end = text + sb.st_size;
464    if (text == MAP_FAILED) {
465        fprintf(stderr, "Can't mmap '%s':  %s\n", filename, strerror(errno));
466        close(fd);
467        return -1;
468    }
469    close(fd);
470    return check_neverallows(policydb, text, end);
471}
472
473static int check_neverallows_string(policydb_t *policydb, char *string, size_t len)
474{
475    char *text, *end;
476    text = string;
477    end = text + len;
478    return check_neverallows(policydb, text, end);
479}
480
481int neverallow_func (int argc, char **argv, policydb_t *policydb) {
482    char *rules = 0, *file = 0;
483    char ch;
484
485    struct option neverallow_options[] = {
486        {"debug", no_argument, NULL, 'd'},
487        {"file_input", required_argument, NULL, 'f'},
488        {"neverallow", required_argument, NULL, 'n'},
489        {"warn", no_argument, NULL, 'w'},
490        {NULL, 0, NULL, 0}
491    };
492
493    while ((ch = getopt_long(argc, argv, "df:n:w", neverallow_options, NULL)) != -1) {
494        switch (ch) {
495        case 'd':
496            debug = 1;
497            break;
498        case 'f':
499            file = optarg;
500            break;
501        case 'n':
502            rules = optarg;
503            break;
504        case 'w':
505            warn = 1;
506            break;
507        default:
508            USAGE_ERROR = true;
509            return -1;
510        }
511    }
512
513    if (!(rules || file) || (rules && file)){
514        USAGE_ERROR = true;
515        return -1;
516    }
517    if (file) {
518        return check_neverallows_file(policydb, file);
519    } else {
520        return check_neverallows_string(policydb, rules, strlen(rules));
521    }
522}
523