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 
13 static int debug;
14 static int warn;
15 
neverallow_usage()16 void neverallow_usage() {
17     fprintf(stderr, "\tneverallow [-w|--warn] [-d|--debug] [-n|--neverallows <neverallow-rules>] | [-f|--file <neverallow-file>]\n");
18 }
19 
read_typeset(policydb_t * policydb,char ** ptr,char * end,type_set_t * typeset,uint32_t * flags)20 static 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;
178 err:
179     return -1;
180 }
181 
read_classperms(policydb_t * policydb,char ** ptr,char * end,class_perm_node_t ** perms)182 static 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     } while (p < end && openparens);
262 
263     if (p == end)
264         goto err;
265 
266     if (warn && !classperms)
267         fprintf(stderr, "Warning!  Empty class set\n");
268 
269     do {
270         while (p < end && isspace(*p))
271             p++;
272 
273         if (p == end)
274             goto err;
275 
276         if (*p == '~') {
277             if (debug)
278                 printf(" ~");
279             complement = true;
280             p++;
281             while (p < end && isspace(*p))
282                 p++;
283             if (p == end)
284                 goto err;
285         }
286 
287         if (*p == '{') {
288             if (debug && !openparens)
289                 printf(" {");
290             openparens++;
291             p++;
292             continue;
293         }
294 
295         if (*p == '}') {
296             if (debug && openparens == 1)
297                 printf(" }");
298             if (openparens == 0)
299                 goto err;
300             openparens--;
301             p++;
302             continue;
303         }
304 
305         if (*p == '#') {
306             while (p < end && *p != '\n')
307                 p++;
308             continue;
309         }
310 
311         start = p;
312         while (p < end && !isspace(*p) && *p != '{' && *p != '}' && *p != ';' && *p != '#')
313             p++;
314 
315         if (p == start)
316             goto err;
317 
318         id = calloc(1, p - start + 1);
319         if (!id)
320             goto err;
321         memcpy(id, start, p - start);
322         if (debug)
323             printf(" %s", id);
324 
325         if (!strcmp(id, "*")) {
326             for (node = classperms; node; node = node->next)
327                 node->data = ~0;
328             continue;
329         }
330 
331         for (node = classperms; node; node = node->next) {
332             cls = policydb->class_val_to_struct[node->tclass-1];
333             perm = hashtab_search(cls->permissions.table, id);
334             if (cls->comdatum && !perm)
335                 perm = hashtab_search(cls->comdatum->permissions.table, id);
336             if (!perm) {
337                 if (warn)
338                     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]);
339                 continue;
340             }
341             node->data |= 1U << (perm->s.value - 1);
342         }
343         free(id);
344     } while (p < end && openparens);
345 
346     if (p == end)
347         goto err;
348 
349     if (complement) {
350         for (node = classperms; node; node = node->next)
351             node->data = ~node->data;
352     }
353 
354     if (warn) {
355         for (node = classperms; node; node = node->next)
356             if (!node->data)
357                 fprintf(stderr, "Warning!  Empty permission set\n");
358     }
359 
360     *perms = classperms;
361     *ptr = p;
362     return 0;
363 err:
364     return -1;
365 }
366 
check_neverallows(policydb_t * policydb,char * text,char * end)367 static int check_neverallows(policydb_t *policydb, char *text, char *end)
368 {
369     const char *keyword = "neverallow";
370     size_t keyword_size = strlen(keyword), len;
371     struct avrule *neverallows = NULL, *avrule;
372     char *p, *start;
373 
374     p = text;
375     while (p < end) {
376         while (p < end && isspace(*p))
377             p++;
378 
379         if (*p == '#') {
380             while (p < end && *p != '\n')
381                 p++;
382             continue;
383         }
384 
385         start = p;
386         while (p < end && !isspace(*p))
387             p++;
388 
389         len = p - start;
390         if (len != keyword_size || strncmp(start, keyword, keyword_size))
391             continue;
392 
393         if (debug)
394             printf("neverallow");
395 
396         avrule = calloc(1, sizeof *avrule);
397         if (!avrule)
398             goto err;
399 
400         avrule->specified = AVRULE_NEVERALLOW;
401 
402         if (read_typeset(policydb, &p, end, &avrule->stypes, &avrule->flags))
403             goto err;
404 
405         if (read_typeset(policydb, &p, end, &avrule->ttypes, &avrule->flags))
406             goto err;
407 
408         if (read_classperms(policydb, &p, end, &avrule->perms))
409             goto err;
410 
411         while (p < end && *p != ';')
412             p++;
413 
414         if (p == end || *p != ';')
415             goto err;
416 
417         if (debug)
418             printf(";\n");
419 
420         avrule->next = neverallows;
421         neverallows = avrule;
422     }
423 
424     if (!neverallows)
425         goto err;
426 
427     return check_assertions(NULL, policydb, neverallows);
428 err:
429     if (errno == ENOMEM) {
430         fprintf(stderr, "Out of memory while parsing neverallow rules\n");
431     } else
432         fprintf(stderr, "Error while parsing neverallow rules\n");
433     return -1;
434 }
435 
check_neverallows_file(policydb_t * policydb,const char * filename)436 static int check_neverallows_file(policydb_t *policydb, const char *filename)
437 {
438     int fd;
439     struct stat sb;
440     char *text, *end;
441 
442     fd = open(filename, O_RDONLY);
443     if (fd < 0) {
444         fprintf(stderr, "Could not open %s:  %s\n", filename, strerror(errno));
445         return -1;
446     }
447     if (fstat(fd, &sb) < 0) {
448         fprintf(stderr, "Can't stat '%s':  %s\n", filename, strerror(errno));
449         close(fd);
450         return -1;
451     }
452     text = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
453     end = text + sb.st_size;
454     if (text == MAP_FAILED) {
455         fprintf(stderr, "Can't mmap '%s':  %s\n", filename, strerror(errno));
456         close(fd);
457         return -1;
458     }
459     close(fd);
460     return check_neverallows(policydb, text, end);
461 }
462 
check_neverallows_string(policydb_t * policydb,char * string,size_t len)463 static int check_neverallows_string(policydb_t *policydb, char *string, size_t len)
464 {
465     char *text, *end;
466     text = string;
467     end = text + len;
468     return check_neverallows(policydb, text, end);
469 }
470 
neverallow_func(int argc,char ** argv,policydb_t * policydb)471 int neverallow_func (int argc, char **argv, policydb_t *policydb) {
472     char *rules = 0, *file = 0;
473     char ch;
474 
475     struct option neverallow_options[] = {
476         {"debug", no_argument, NULL, 'd'},
477         {"file_input", required_argument, NULL, 'f'},
478         {"neverallow", required_argument, NULL, 'n'},
479         {"warn", no_argument, NULL, 'w'},
480         {NULL, 0, NULL, 0}
481     };
482 
483     while ((ch = getopt_long(argc, argv, "df:n:w", neverallow_options, NULL)) != -1) {
484         switch (ch) {
485         case 'd':
486             debug = 1;
487             break;
488         case 'f':
489             file = optarg;
490             break;
491         case 'n':
492             rules = optarg;
493             break;
494         case 'w':
495             warn = 1;
496             break;
497         default:
498             USAGE_ERROR = true;
499             return -1;
500         }
501     }
502 
503     if (!(rules || file) || (rules && file)){
504         USAGE_ERROR = true;
505         return -1;
506     }
507     if (file) {
508         return check_neverallows_file(policydb, file);
509     } else {
510         return check_neverallows_string(policydb, rules, strlen(rules));
511     }
512 }
513