Commit Graph

767 Commits

Author SHA1 Message Date
James Carter
799ef8b66e libsepol/cil: Free duplicate datums in original calling function
Duplicate declarations are allowed for type, typeattribute, and
optional statements. When an allowed duplicate declaration is found,
the duplicate datum is free'd in cil_add_decl_to_symtab() and SEPOL_OK
is returned. This works for all the rules where a duplicate declaration
is allowed, but it confuses scanning tools.

When cil_add_decl_to_symtab() finds an allowed duplicate declaration,
return SEPOL_EEXIST and free the duplicate datum in the original
calling function.

Signed-off-by: James Carter <jwcart2@gmail.com>
Acked-by: Petr Lautrbach <plautrba@redhat.com>
2021-09-13 15:40:36 +02:00
Petr Lautrbach
2771dc4329 libsepol/cil: Fix detected RESOURCE_LEAK (CWE-772)
libsepol/cil/src/cil_binary.c:4823: alloc_arg: "bounds_check_type" allocates memory that is stored into "bad".
libsepol/cil/src/cil_binary.c:4840: var_assign: Assigning: "cur" = "bad".
libsepol/cil/src/cil_binary.c:4844: noescape: Resource "cur" is not freed or pointed-to in "cil_avrule_from_sepol".
libsepol/cil/src/cil_binary.c:4847: leaked_storage: Variable "cur" going out of scope leaks the storage it points to.
libsepol/cil/src/cil_binary.c:4847: leaked_storage: Variable "bad" going out of scope leaks the storage it points to.

Signed-off-by: Petr Lautrbach <plautrba@redhat.com>
Acked-by: James Carter <jwcart2@gmail.com>
2021-09-13 15:40:08 +02:00
Petr Lautrbach
38cb18e931 Update VERSIONs and Python bindings version to 3.3-rc1 for release
Signed-off-by: Petr Lautrbach <plautrba@redhat.com>
2021-09-08 09:49:46 +02:00
James Carter
ff143e5298 libsepol/cil: Limit the number of active line marks
A line mark functions like an open parenthesis, so the number of
active line marks should be limited like the number of open
parenthesis.

This issue was found by the secilc-fuzzer.

Signed-off-by: James Carter <jwcart2@gmail.com>
Acked-by: Nicolas Iooss <nicolas.iooss@m4x.org>
2021-09-08 09:45:35 +02:00
James Carter
d0b5ba03ba libsepol/cil: Add function to get number of items in a stack
Add the function, cil_stack_number_of_items(), to return the number
of items in the stack.

Signed-off-by: James Carter <jwcart2@gmail.com>
2021-09-08 09:45:23 +02:00
Petr Lautrbach
c304156133 libsepol: Fix detected RESOURCE_LEAKs
Fixes:
Error: RESOURCE_LEAK (CWE-772): [#def5]
libsepol/src/kernel_to_cil.c:2380: alloc_arg: "strs_init" allocates memory that is stored into "strs".
libsepol/src/kernel_to_cil.c:2386: noescape: Resource "strs" is not freed or pointed-to in "strs_add".
libsepol/src/kernel_to_cil.c:2386: noescape: Resource "strs" is not freed or pointed-to in "strs_add".
libsepol/src/kernel_to_cil.c:2386: noescape: Resource "strs" is not freed or pointed-to in "strs_add".
libsepol/src/kernel_to_cil.c:2507: leaked_storage: Variable "strs" going out of scope leaks the storage it points to.

libsepol/src/kernel_to_conf.c:2315: alloc_arg: "strs_init" allocates memory that is stored into "strs".
libsepol/src/kernel_to_conf.c:2321: noescape: Resource "strs" is not freed or pointed-to in "strs_add".
libsepol/src/kernel_to_conf.c:2321: noescape: Resource "strs" is not freed or pointed-to in "strs_add".
libsepol/src/kernel_to_conf.c:2321: noescape: Resource "strs" is not freed or pointed-to in "strs_add".
libsepol/src/kernel_to_conf.c:2385: leaked_storage: Variable "strs" going out of scope leaks the storage it points to.

Signed-off-by: Petr Lautrbach <plautrba@redhat.com>
Acked-by: James Carter <jwcart2@gmail.com>
2021-09-08 09:44:59 +02:00
James Carter
f3265d5802 libsepol/cil: Fix syntax checking in __cil_verify_syntax()
The function __cil_verify_syntax() is used to check the syntax of
CIL rules (and a few other common things like contexts and class
permissions). It does not correctly check the syntax combination
"CIL_SYN_STRING | CIL_SYN_N_LISTS, CIL_SYN_N_LISTS | CIL_SYN_END".
This should mean either a string followed by any number of lists
or any number of lists followed by the end of the rule. Instead,
while allowing the correct syntax, it allows any number of lists
followed by a string followed by any number of more lists followed
by the end of the rule and, also, any number of lists followed by a
string followed by the end of the rule.

Refactor the function to make it clearer to follow and so that once
checking begins for CIL_SYN_N_LISTS or CIL_SYN_N_STRINGS, then only
strings or lists are allowed until the end of the rule is found. In
addition, always check for CIL_SYN_END at the end.

Signed-off-by: James Carter <jwcart2@gmail.com>
2021-09-07 10:28:46 -04:00
James Carter
473ae1b829 libsepol/cil: Use size_t for len in __cil_verify_syntax()
Since the value passed into __cil_verify_syntax() as the len
parameter is always calculated from sizeof(syntax)/sizeof(*syntax),
use size_t for the calculated value in the calling function and for
the len parameter. In __cil_verify_syntax(), the variable i is only
compared to len, so make that size_t as well.

Signed-off-by: James Carter <jwcart2@gmail.com>
2021-09-07 10:28:46 -04:00
James Carter
6390a28a30 libsepol/cil: Remove redundant syntax checking
For every call to cil_fill_classperms_list(), the syntax of the
whole rule, including the class permissions, has already been
checked. There is no reason to check it again. Also, because the
class permissions appear in the middle of some rules, like
constraints, the syntax array does not end with CIL_SYN_END. This
is the only case where the syntax array does not end with CIL_SYN_END.
This prevents __cil_verify_syntax() from requiring that the syntax
array ends with CIL_SYN_END.

Remove the redundant syntax checking in cil_fill_classperms_list().

Signed-off-by: James Carter <jwcart2@gmail.com>
2021-09-07 10:28:46 -04:00
James Carter
68573634b4 libsepol/cil: Improve in-statement to allow use after inheritance
CIL's in-statement is resolved before block inheritance. This has
the advantage of allowing an in-statement to add rules to a base
block (say for a new permission) and having those rules also be
added everywhere that base block is inherited. But the disadvantage
of this behavior is that it is not possible to use an in-statement
on a block that is inherited for the simple reason that that block
does not exist when the in-statment is resolved.

Change the syntax of the in-statement to allow specifying whether
the rules should be added before or after inheritance. If neither
is specified, then the behavior remains the same. All current
in-statements will work as before.

Either the old syntax
  (in container_id
      cil_statement
      ...
  )
or the new syntax
  (in before|after container_id
      cil_statement
      ...
  )
may be used for in-statements. But only "(in after ..." will have
the new behavior. Using "(in before ..." will give the same
behavior as before.

Macro Example
;
(block b1
  (macro m1 ((type ARG1))
    (allow ARG1 self (C1 (P1a)))
  )
)
(in after b1.m1
  (allow ARG1 self (C1 (P1c)))
)
(type t1a)
(call b1.m1 (t1a))
(blockinherit b1)
(in after m1
  (allow ARG1 self (C1 (P1b)))
)
(type t1b)
(call m1 (t1b))
;
This results in the following rules:
  (allow t1a self (C1 (P1a)))
  (allow t1a self (C1 (P1c)))
  (allow t1b self (C1 (P1a)))
  (allow t1b self (C1 (P1b)))

Block Example
;
(block b2
  (block b
    (type ta)
    (allow ta self (C2 (P2a)))
  )
)
(in before b2.b
  (type tb)
  (allow tb self (C2 (P2b)))
)
(block c2
  (blockinherit b2)
  (in after b
    (type tc)
    (allow tc self (C2 (P2c)))
  )
)
;
This results in the following rules:
  (allow b2.b.ta self (C2 (P2a)))
  (allow b2.b.tb self (C2 (P2b)))
  (allow c2.b.ta self (C2 (P2a)))
  (allow c2.b.tb self (C2 (P2b)))
  (allow c2.b.tc self (C2 (P2c)))

Using in-statements on optionals also works as expected.

One additional change is that blockabstract and blockinherit rules
are not allowed when using an after in-statement. This is because
both of those are resolved before an after in-statement would be
resolved.

Signed-off-by: James Carter <jwcart2@gmail.com>
2021-09-07 10:28:27 -04:00
James Carter
2a6a883eed libsepol/cil: Simplify cil_tree_children_destroy()
Use a simpler recursive solution and set the head and tail pointers
of the starting node to NULL when done.

Remove the now uneeded setting of the head and tail pointers to NULL
in cil_resolve_in().

Signed-off-by: James Carter <jwcart2@gmail.com>
2021-09-02 10:49:22 -04:00
James Carter
05e8f155d5 libsepol/cil: Refactor the function __cil_build_ast_node_helper()
Refactor the function __cil_build_ast_node_helper() by moving the
check for illegal statements and the large if-then-else statement
to determine which function to call to parse the policy statements
to different functions.

There is no need to keep walking the nodes of a policy statement
that has already been completely parsed. This means that the
remaining nodes of any policy statement that does not contain a list
of policy statements can be skipped. This was done inconsistently
before. The following policy statements now have all nodes after
the first one skipped: blockinherit, blockabstract, classcommon,
user, userattribute, userbounds, userprefix, type, typeattribute,
typealias, typealiasactual, typebounds, typepermissive, role,
userrole, roletype, roletransition, roleallow, roleattribute,
rolebounds, bool, tunable, typetransition, typechange, typemember,
sensitivity, sensitivityalias, senistivityaliasactual, category,
categoryalias, categoryaliasactual, and ipaddr. The only policy
statements that do contain a list of policy statements are:
block, in, tunableif, booleanif, true (conditional block), false
(conditional block), macro, optional, and src_info.

Signed-off-by: James Carter <jwcart2@gmail.com>
2021-09-02 10:49:18 -04:00
James Carter
3cbae1b68b libsepol/cil: Don't destroy optionals whose parent will be destroyed
If an optional that is to be disabled is the child of an optional that
is going to be disabled, then there is no reason to add that optional
to the stack of disabled optionals, because it is going to be destroyed
anyways. This means that there is no reason to maintain a stack of
disabled optionals at all.

Instead of using a stack to track disabled optionals, use a pointer
that points to the top-most optional that is to be disabled. When a
rule fails to resolve in an optional, if the disabled optional pointer
has not been set, then set it to that optional. If the pointer has
been set already, then the optional is already going to be destroyed,
so nothing else needs to be done. The resolution failure and the fact
that the optional is being disabled is reported in either case.

Signed-off-by: James Carter <jwcart2@gmail.com>
2021-09-02 10:49:14 -04:00
James Carter
05d1c66aaa libsepol/cil: Properly check for parameter when inserting name
File names for typetransition rules are stored in their own datums.
This allows them to be passed as a parameter, but there needs to be
a check in __cil_insert_name() so that parameter names are not
mistaken for file name strings. This check did not verify that a
matching parameter name had the flavor of CIL_NAME.

Check that the parameter flavor is CIL_NAME and that the paramter
name matches the file name to be stored in the datum.

This bug was found by the secilc-fuzzer.

Signed-off-by: James Carter <jwcart2@gmail.com>
2021-09-02 10:48:08 -04:00
James Carter
b57535318a libsepol/cil: Reset expandtypeattribute rules when resetting AST
A list is created to store type attribute datums when resolving an
expandtypeattribute rule and that list needs to be destroyed if the
AST is reset or a memory leak will occur.

Destroy the list storing type attributes datums when resetting
expandtypeattribute rules.

This bug was found by the secilc-fuzzer.

Signed-off-by: James Carter <jwcart2@gmail.com>
2021-09-02 10:48:02 -04:00
James Carter
4469c9796e libsepol/cil: Properly check parse tree when printing error messages
The function cil_tree_get_next_path() does not check whether the
parse tree node that stores the high-level language file path of a
src_info rule actually exists before trying to read the path. This
can result in a NULL dereference.

Check that all of the parse tree nodes of a src_info rule exist
before reading the data from them.

This bug was found by the secilc-fuzzer.

Signed-off-by: James Carter <jwcart2@gmail.com>
2021-09-02 10:47:59 -04:00
James Carter
8d197879f9 libsepol/cil: Allow some duplicate macro and block declarations
The commit d155b410d4 (libsepol/cil:
Check for duplicate blocks, optionals, and macros) added checks when
copying blocks, macros, and optionals so that a duplicate would cause
an exit with an error. Unfortunately, some policies exist that depend
on this behavior when using inheritance.

The behavior is as follows.

For macros only the first declared macro matters.
;
(macro m ((type ARG1))
  (allow ARG1 self (CLASS (PERM1)))
)
(block b
  (macro m ((type ARG1))
    (allow ARG1 self (CLASS (PERM2)))
  )
)
(blockinherit b)
(type t)
(call m (t))
;
For this policy segment, the macro m in block b will not be called.
Only the original macro m will be.

This behavior has been used to override macros that are going to
be inherited. Only the inherited macros that have not already been
declared in the destination namespace will be used.

Blocks seem to work fine even though there are two of them
;
(block b1
  (blockinherit b2)
  (block b
    (type t1)
    (allow t1 self (CLASS (PERM)))
  )
)
(block b2
  (block b
    (type t2)
    (allow t2 self (CLASS (PERM)))
  )
)
(blockinherit b1)
;
In this example, the blockinherit of b2 will cause there to be
two block b's in block b1. Note that if both block b's tried to
declare the same type, then that would be an error. The blockinherit
of b1 will copy both block b's.

This behavior has been used to allow the use of in-statements for
a block that is being inherited. Since the in-statements are resolved
before block inheritance, this only works if a block with the same
name as the block to be inherited is declared in the namespace.

To support the use of these two behaviors, allow duplicate blocks
and macros when they occur as the result of block inheritance. In
any other circumstances and error for a redeclaration will be given.

Since the duplicate macro is not going to be used it is just skipped.
The duplicate block will use the datum of the original block. In both
cases a warning message will be produced (it will only be seen if
"-v" is used when compiling the policy).

Signed-off-by: James Carter <jwcart2@gmail.com>
2021-08-19 11:09:15 -04:00
James Carter
6a2508268d libsepol/cil: When writing AST use line marks for src_info nodes
In order to retain as much information as possible, when writing
out the CIL AST, use line mark notation to write out src_info
nodes. This includes using line marks to denote the original CIL
files the AST comes from.

The line numbers will not always be exactly correct because any
blank lines and comments in the original files will not be
represented in the AST.

Line marks are not written for the parse tree because the line
numbers will be widely inaccurate since each token will be on
a different line.

Signed-off-by: James Carter <jwcart2@gmail.com>
2021-08-19 11:09:03 -04:00
James Carter
28ae4adc44 libsepol/cil: Report correct high-level language line numbers
CIL supports specifiying the original high-level language file and
line numbers when reporting errors. This is done through line marks
and is mostly used to report the original Refpolicy file and line
number for neverallow rules that have been converted to CIL.

As long as the line mark remain simple, everything works fine, but
the wrong line numbers will be reported with more complex nextings
of line marks.

Example:
;;* lms 100 file01.hll
(type t1a)
(allow t1a self (CLASS (PERM)))
;;* lmx 200 file02.hll
(type t2a)
(allow t2a self (CLASS (PERM)))
;;* lme
(type t1b)
(allow t1b self (CLASS (PERM)))
(allow bad1b self (CLASS (PERM))) ; file01.hll:101 (Should be 106)
;;* lme

The primary problem is that the tree nodes can only store one hll
line number. Instead a number is needed that can be used by any
number of stacked line mark sections. This number would increment
line a normal line number except when in lmx sections (that have
the same line number throughout the section because they represent
an expansion of a line -- like the expansion of a macro call. This
number can go backwards when exiting a lms section within a lmx
section, because line number will increase in the lms section, but
outside the lmx section, the line number did not advance.

This number is called the hll_offset and this is the value that is
now stored in tree nodes instead of the hll line number. To calculate
the hll line number for a rule, a search is made for an ancestor of
the node that is a line mark and the line number for a lms section
is the hll line number stored in the line mark, plus the hll offset
of the rule, minus the hll offset of the line mark node, minus one.
(hll_lineno + hll_offset_rule - hll_offset_lm - 1)

Signed-off-by: James Carter <jwcart2@gmail.com>
2021-08-19 11:08:59 -04:00
James Carter
fb9ed092da libsepol/cil: Add line mark kind and line number to src info
To be able to write line mark information when writing the AST,
the line mark kind and line number is needed in the src info.

Instead of indicating whether the src info is for CIL or a hll,
differentiate between CIL, a normal hll line mark, and an expanded
hll line mark. Also include the line mark line number in the src
info nodes.

Signed-off-by: James Carter <jwcart2@gmail.com>
2021-08-19 11:08:56 -04:00
James Carter
31aa43d258 libsepol/cil: Create common string-to-unsigned-integer functions
The functions cil_fill_integer() and cil_fill_integer64() exist in
cil_build_ast.c, but these functions take a node and it would be
better to have a function that can be used in add_hll_linemark()
so that the common functinality is in one place.

Create cil_string_to_uint32() and cil_string_to_uint64() and use
these functions in cil_fill_integer(), cil_fill_integer64(), and
add_hll_linemark().

Signed-off-by: James Carter <jwcart2@gmail.com>
2021-08-19 11:08:54 -04:00
James Carter
0f12ee87ac libsepol/cil: Push line mark state first when processing a line mark
CIL line mark rules are used to annotate the original line and file
of a rule. It is mostly used for neverallow rules that have been
converted to CIL.

Pushing the current line mark state after processing a line mark
section does not make sense since that information is never used.
When the line mark section ends the information is just popped and
discarded. It also makes pop_hll_info() more complicated than it
needs to be.

Push the line mark state first and simplfy pop_hll_info().

Signed-off-by: James Carter <jwcart2@gmail.com>
2021-08-19 11:08:51 -04:00
James Carter
4388801685 libsepol/cil: Check for valid line mark type immediately
It clearer to check that the line mark type is a valid option right
after getting the token.

Check that the line mark type is one of the expected values right
awasy.

Signed-off-by: James Carter <jwcart2@gmail.com>
2021-08-19 11:08:47 -04:00
James Carter
2b988acf44 libsepol/cil: Check the token type after getting the next token
In add_hll_linemark(), cil_lexer_next() is called and the token
type is not checked after the call for the expected type (SYMBOL).

Check that the token type is SYMBOL after calling cil_lexer_next().

Signed-off-by: James Carter <jwcart2@gmail.com>
2021-08-19 11:08:44 -04:00
James Carter
8823bea1b0 libsepol/cil: Check syntax of src_info statement
Every rule other than src_info has their syntax checked when
building the AST. It wasn't considered necessary for src_info rules
because they were expected to always be generated by the parser and
aren't part of the CIL language. But there is no check preventing
them from occurring in a policy and the secilc fuzzer found some bugs
by using src_info rules in a policy. This caused some syntax checking
to be added. Since the parse AST from secil2tree will contain src_info
rules and since the goal is to be able to compile the output of
secil2tree, it makes sense to check the syntax of src_info rules
in the same way that all of the other rules are checked.

Check the syntax of src_info statements in the same way every other
rule is checked.

Signed-off-by: James Carter <jwcart2@gmail.com>
2021-08-19 11:08:40 -04:00
Evgeny Vereshchagin
33621cb7c8 libsepol/cil: move the fuzz target and build script to the selinux repository
It should make it easier to reproduce bugs found by OSS-Fuzz locally
without docker. The fuzz target can be built and run with the corpus
OSS-Fuzz has accumulated so far by running the following commands:
```
./scripts/oss-fuzz.sh
wget https://storage.googleapis.com/selinux-backup.clusterfuzz-external.appspot.com/corpus/libFuzzer/selinux_secilc-fuzzer/public.zip
unzip -d CORPUS public.zip
./out/secilc-fuzzer CORPUS/
```

It was tested in https://github.com/google/oss-fuzz/pull/6026
by pointing OSS-Fuzz to the branch containing the patch and
running all the tests with all the sanitizers and fuzzing engines
there: https://github.com/google/oss-fuzz/actions/runs/1024673143

[v2]
[1] oss-fuzz: make shellcheck happy

[2] oss-fuzz: build libsepol only

The fuzz target covers libsepol so it's unnecessary to build everything
else. Apart from that, the "LDFLAGS" kludge was removed since libsepol
is compatible with the sanitizers flags passed via CFLAGS only. It
should be brought back one way or another eventually though to fix
build failures like
```
clang -L/home/vagrant/selinux/selinux/DESTDIR/usr/lib -L/home/vagrant/selinux/selinux/DESTDIR/usr/lib -L../src  sefcontext_compile.o ../src/regex.o  -lselinux  -lpcre  ../src/libselinux.a -lsepol -o sefcontext_compile
/usr/bin/ld: sefcontext_compile.o: in function `usage':
/home/vagrant/selinux/selinux/libselinux/utils/sefcontext_compile.c:271: undefined reference to `__asan_report_load8'
/usr/bin/ld: /home/vagrant/selinux/selinux/libselinux/utils/sefcontext_compile.c:292: undefined reference to `__asan_handle_no_return'
/usr/bin/ld: sefcontext_compile.o: in function `asan.module_ctor':
```

[3] oss-fuzz: make it possible to run the script more than once
by removing various build artifacts

[4] oss-fuzz: make it possible to run the script from any directory

[5] oss-fuzz: be a little bit more specific about what the script does

[6] oss-fuzz: stop overwriting all the Makefiles

Signed-off-by: Evgeny Vereshchagin <evvers@ya.ru>
Acked-by: Nicolas Iooss <nicolas.iooss@m4x.org>
2021-08-17 10:33:47 -04:00
Christian Göttsche
f068bdcf2d libsepol: replace strerror by %m
The standard function `strerror(3)` is not thread safe.  This does not
only affect the concurrent usage of libselinux itself but also with
other `strerror(3)` linked libraries.
Use the thread safe GNU extension format specifier `%m`[1].

libselinux already uses the GNU extension format specifier `%ms`.

[1]: https://www.gnu.org/software/libc/manual/html_node/Other-Output-Conversions.html

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
2021-08-10 14:21:28 -04:00
Ondrej Mosnacek
d0f9a58920 libsepol/cil: remove obsolete comment
Commit a60343cabf ("libsepol/cil: remove unnecessary hash tables")
removed FILENAME_TRANS_TABLE_SIZE macro that this comment was referring
to. Remove the comment as well to avoid confusion.

Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
2021-08-05 09:55:18 -04:00
Nicolas Iooss
af29a23553
libsepol/cil: do not allow \0 in quoted strings
Using the '\0' character in strings in a CIL policy is not expected to
happen, and makes the flex tokenizer very slow. For example when
generating a file with:

    python -c 'print("\"" + "\0"*100000 + "\"")' > policy.cil

secilc fails after 26 seconds, on my desktop computer. Increasing the
numbers of \0 makes this time increase significantly. But replacing \0
with another character makes secilc fail in only few milliseconds.

Fix this "possible denial of service" issue by forbidding \0 in strings
in CIL policies.

Fixes: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=36016

Signed-off-by: Nicolas Iooss <nicolas.iooss@m4x.org>
2021-07-13 21:05:03 +02:00
James Carter
8470058934
libsepol/cil: Fix handling category sets in an expression
There are two problems that need to be addressed when resolving an
expression with category sets.

1. Only expand anonymous category sets in an expression.

Commit 982ec302b6 (libsepol/cil:
Account for anonymous category sets in an expression) attempted to
properly handle anonymous category sets when resolving category
expressions. Unfortunately, it did not check whether a category set
was actually an anonymous category set and expanded all category
sets in an expression. If a category set refers to itself in the
expression, then everything from the name of the category set to the
end of the expression is ignored.

For example, the rule "(categoryset cs (c0 cs c1 c2))", would be
equivalent to the rule "(categoryset cs (c0))" as everything from
"cs" to the end would be dropped. The secilc-fuzzer found that the
rule "(categoryset cat (not cat))" would cause a segfault since
"(not)" is not a valid expression and it is assumed to be valid
during later evaluation because syntax checking has already been
done.

Instead, check whether or not the category set is anonymous before
expanding it when resolving an expression.

2. Category sets cannot be used in a category range

A category range can be used to specify a large number of categories.
The range "(range c0 c1023)" refers to 1024 categories. Only categories
and category aliases can be used in a range. Determining if an
identifier is a category, an alias, or a set can only be done after
resolving the identifer.

Keep track of the current operator as an expression is being resolved
and if the expression involves categories and a category set is
encountered, then return an error if the expression is a category
range.

Signed-off-by: James Carter <jwcart2@gmail.com>
2021-07-13 21:04:54 +02:00
Christian Göttsche
07d6f1cea5
libsepol: assure string NUL-termination of ibdev_name
Clang complains:

    ibendport_record.c: In function ‘sepol_ibendport_get_ibdev_name’:
    ibendport_record.c:169:2: error: ‘strncpy’ specified bound 64 equals destination size [-Werror=stringop-truncation]
      169 |  strncpy(tmp_ibdev_name, ibendport->ibdev_name, IB_DEVICE_NAME_MAX);
          |  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ibendport_record.c: In function ‘sepol_ibendport_set_ibdev_name’:
    ibendport_record.c:189:2: error: ‘strncpy’ specified bound 64 equals destination size [-Werror=stringop-truncation]
      189 |  strncpy(tmp, ibdev_name, IB_DEVICE_NAME_MAX);
          |  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

strncpy(3) does not NUL-terminate the destination if the source is of
the same length or longer then the specified size.
The source of these copies are retrieved from
sepol_ibendport_alloc_ibdev_name(), which allocates a fixed amount of
IB_DEVICE_NAME_MAX bytes.
Reduce the size to copy by 1 of all memory regions allocated by
sepol_ibendport_alloc_ibdev_name().

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
2021-07-13 21:01:11 +02:00
Christian Göttsche
e1491388d5
libsepol: avoid implicit conversions
Avoid implicit conversions from signed to unsigned values, found by
UB sanitizers, by using unsigned values in the first place.

    expand.c:1644:18: runtime error: implicit conversion from type 'int' of value -1 (32-bit, signed) to type 'uint32_t' (aka 'unsigned int') changed the value to 4294967295 (32-bit, unsigned)

    expand.c:2892:24: runtime error: implicit conversion from type 'int' of value -2 (32-bit, signed) to type 'unsigned int' changed the value to 4294967294 (32-bit, unsigned)

    policy_define.c:2344:4: runtime error: implicit conversion from type 'int' of value -1048577 (32-bit, signed) to type 'unsigned int' changed the value to 4293918719 (32-bit, unsigned)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
2021-07-13 21:01:07 +02:00
Christian Göttsche
09405ba91c
libsepol: ignore UBSAN false-positives
Unsigned integer overflow is well-defined and not undefined behavior.
But it is still useful to enable undefined behavior sanitizer checks on
unsigned arithmetic to detect possible issues on counters or variables
with similar purpose.

Annotate functions, in which unsigned overflows are expected to happen,
with the respective Clang function attribute[1].
GCC does not support sanitizing unsigned integer arithmetic[2].

    avtab.c:76:2: runtime error: unsigned integer overflow: 6 * 3432918353 cannot be represented in type 'unsigned int'
    policydb.c:795:42: runtime error: unsigned integer overflow: 8160943042179512010 * 11 cannot be represented in type 'unsigned long'
    symtab.c:25:12: runtime error: left shift of 1766601759 by 4 places cannot be represented in type 'unsigned int'

[1]: https://clang.llvm.org/docs/AttributeReference.html#no-sanitize
[2]: https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
2021-07-13 21:01:03 +02:00
Christian Göttsche
44d56761be
libsepol: avoid unsigned integer overflow
Unsigned integer overflow is well-defined and not undefined behavior.
It is commonly used for hashing or pseudo random number generation.
But it is still useful to enable undefined behavior sanitizer checks on
unsigned arithmetic to detect possible issues on counters or variables
with similar purpose or missed overflow checks on user input.

Use a spaceship operator like comparison instead of subtraction.

    policydb.c:851:24: runtime error: unsigned integer overflow: 801 - 929 cannot be represented in type 'unsigned int'

Follow-up of: 1537ea8412 ("libsepol: avoid unsigned integer overflow")

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
2021-07-13 21:00:33 +02:00
James Carter
9af9169241 libsepol/cil: Improve checking for bad inheritance patterns
commits 37863b0b14 (libsepol/cil:
Improve degenerate inheritance check) and
74d00a8dec (libsepol/cil: Detect
degenerate inheritance and exit with an error) attempted to detect
and exit with an error when compiling policies that have degenerate
inheritances. These policies result in the exponential growth of memory
usage while copying the blocks that are inherited.

There were two problems with the previous attempts to detect this
bad inheritance problem. The first is that the quick check using
cil_possible_degenerate_inheritance() did not detect all patterns
of degenerate inheritance. The second problem is that the detection
of inheritance loops during the CIL_PASS_BLKIN_LINK pass did not
detect all inheritance loops which made it possible for the full
degenerate inheritance checking done with
cil_check_for_degenerate_inheritance() to have a stack overflow
when encountering the inheritance loops. Both the degenerate and
loop inheritance checks need to be done at the same time and done
after the CIL_PASS_BLKIN_LINK pass. Otherwise, if loops are being
detected first, then a degenerate policy can cause the consumption
of all system memory and if degenerate policy is being detected
first, then an inheritance loop can cause a stack overflow.

With the new approach, the quick check is eliminated and the full
check is always done after the CIL_PASS_BLKIN_LINK pass. Because
of this the "inheritance_check" field in struct cil_resolve_args
is not needed and removed and the functions
cil_print_recursive_blockinherit(), cil_check_recursive_blockinherit(),
and cil_possible_degenerate_inheritance() have been deleted. The
function cil_count_potential() is renamed cil_check_inheritances()
and has checks for both degenerate inheritance and inheritance loops.
The inheritance checking is improved and uses an approach similar
to commit c28525a26f (libsepol/cil:
Properly check for loops in sets).

As has been the case with these degenerate inheritance patches,
these issues were discovered by the secilc-fuzzer.

Signed-off-by: James Carter <jwcart2@gmail.com>
2021-07-07 12:29:15 -04:00
Nicolas Iooss
9d85aa60d1 libsepol: silence -Wextra-semi-stmt warning
On Ubuntu 20.04, when building with clang -Werror -Wextra-semi-stmt
(which is not the default build configuration), the compiler reports:

  ../cil/src/cil_binary.c:4293:22: error: empty expression statement
  has no effect; remove unnecessary ';' to silence this warning
  [-Werror,-Wextra-semi-stmt]
          mix(k->target_class);
                              ^
  ../cil/src/cil_binary.c:4294:21: error: empty expression statement
  has no effect; remove unnecessary ';' to silence this warning
  [-Werror,-Wextra-semi-stmt]
          mix(k->target_type);
                             ^
  ../cil/src/cil_binary.c:4295:21: error: empty expression statement
  has no effect; remove unnecessary ';' to silence this warning
  [-Werror,-Wextra-semi-stmt]
          mix(k->source_type);
                             ^
  ../cil/src/cil_binary.c:4296:19: error: empty expression statement
  has no effect; remove unnecessary ';' to silence this warning
  [-Werror,-Wextra-semi-stmt]
          mix(k->specified);
                           ^

Use a do { ... } while (0) construction to silence this warning.

Moreover the same warning appears when using two semicolons to end a
statement. Remove such occurrences, like what was already done in commit
811185648a ("libsepol: drop repeated semicolons").

Signed-off-by: Nicolas Iooss <nicolas.iooss@m4x.org>
2021-07-06 11:08:11 -04:00
Nicolas Iooss
fd705df050 libsepol/cil: do not override previous results of __cil_verify_classperms
When __cil_verify_map_class() verifies a classpermission, it calls
__verify_map_perm_classperms() on each item. If the first item reports a
failure and the next one succeeds, the failure is overwritten in
map_args->rc. This is a bug which causes a NULL pointer dereference in
the CIL compiler when compiling the following policy:

    (sid SID)
    (sidorder (SID))

    (class CLASS (PERM1))
    (classorder (CLASS))

    (classpermission CLSPERM)
    (classpermissionset CLSPERM (CLASS (PERM1)))
    (classmap files (CLAMAPxx x))
    (classmapping files CLAMAPxx CLSPERM)

Fixes: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=30286

Signed-off-by: Nicolas Iooss <nicolas.iooss@m4x.org>
2021-07-06 10:27:24 -04:00
James Carter
a0914acf2a
libsepol/cil: Provide option to allow qualified names in declarations
Qualified names have "dots" in them. They are generated when a CIL
policy is compiled and come from declarations in blocks. If a kernel
policy is decompiled into a CIL policy, the resulting policy could
have declarations that use qualified names. Compiling this policy would
result in an error because "dots" in declarations are not allowed.

Qualified names in a policy are normally used to refer to the name of
identifiers, blocks, macros, or optionals that are declared in a
different block (that is not a parent). Name resolution is based on
splitting a name based on the "dots", searching the parents up to the
global namespace for the first block using the first part of the name,
using the second part of the name to lookup the next block using the
first block's symbol tables, looking up the third block in the second's
symbol tables, and so on.

To allow the option of using qualified names in declarations:

1) Create a field in the struct cil_db called "qualified_names" which
is set to CIL_TRUE when qualified names are to be used. This field is
checked in cil_verify_name() and "dots" are allowed if qualified names
are being allowed.

2) Only allow the direct lookup of the whole name in the global symbol
table. This means that blocks, blockinherits, blockabstracts, and in-
statements cannot be allowed. Use the "qualified_names" field of the
cil_db to know when using one of these should result in an error.

3) Create the function cil_set_qualified_names() that is used to set
the "qualified_names" field. Export the function in libsepol.

Signed-off-by: James Carter <jwcart2@gmail.com>
2021-07-03 16:00:26 +02:00
Nicolas Iooss
af75f64194
libsepol/cil: make array cil_sym_sizes const
The values of this table are never modified.

Signed-off-by: Nicolas Iooss <nicolas.iooss@m4x.org>
2021-06-30 21:06:07 +02:00
James Carter
4ff514a33e
libsepol/cil: Only reset AST if optional has a declaration
When disabling optionals, the AST needs to be reset only if one
of the optional blocks being disabled contains a declaration.

Call the function cil_tree_subtree_has_decl() for each optional
block being disabled and only reset the AST if one of them has
a declaration in it.

Signed-off-by: James Carter <jwcart2@gmail.com>
2021-06-30 21:05:38 +02:00
James Carter
20271849d5
libsepol/cil: Add function to determine if a subtree has a declaration
Create the function cil_tree_subtree_has_decl() that returns CIL_TRUE
if the subtree has a declaration in it and CIL_FALSE otherwise.

Signed-off-by: James Carter <jwcart2@gmail.com>
2021-06-30 21:05:35 +02:00
James Carter
37863b0b14 libsepol/cil: Improve degenerate inheritance check
The commit 74d00a8dec (libsepol/cil:
Detect degenerate inheritance and exit with an error) detects the
use of inheritance (mostly by the secilc-fuzzer and not in any real
policies) that results in the exponential growth of the policy through
the copying of blocks that takes place with inheritance in CIL.
Unfortunately, the check takes place during the pass when all the
blocks are being copied, so it is possible to consume all of a system's
memory before an error is produced.

The new check happens in two parts. First, a check is made while the
block inheritance is being linked to the block it will inherit. In
this check, all of the parent nodes of the inheritance rule up to the
root node are checked and if enough of these blocks are being inherited
(>= CIL_DEGENERATE_INHERITANCE_DEPTH), then a flag is set for a more
in-depth check after the pass. This in-depth check will determine the
number of potential inheritances that will occur when resolving the
all of the inheritance rules. If this value is greater than
CIL_DEGENERATE_INHERITANCE_GROWTH * the original number of inheritance
rules and greater than CIL_DEGENERATE_INHERITANCE_MINIMUM (which is
set to 0x1 << CIL_DEGENERATE_INHERITANCE_DEPTH), then degenerate
inheritance is determined to have occurred and an error result will
be returned.

Since the potential number of inheritances can quickly be an extremely
large number, the count of potential inheritances is aborted as soon
as the threshold for degenerate inheritance has been exceeded.

Normal policies should rarely, if ever, have the in-depth check occur.

Signed-off-by: James Carter <jwcart2@gmail.com>
Acked-by: Nicolas Iooss <nicolas.iooss@m4x.org>
2021-06-24 10:23:38 -04:00
James Carter
36e494573d libsepol/cil: Reduce the initial symtab sizes for blocks
It is possible to create bad behaving policy that can consume all
of a system's memory (one way is through the use of inheritance).
Analyzing these policies shows that most of the memory usage is for
the block symtabs.

Most of the nineteen symtabs will most likely never be used, so give
these symtabs an initial size of 1. The others are given more
appropriate sizes.

Signed-off-by: James Carter <jwcart2@gmail.com>
Acked-by: Nicolas Iooss <nicolas.iooss@m4x.org>
2021-06-24 10:23:34 -04:00
James Carter
f33745a22b libsepol/cil: Check for empty list when marking neverallow attributes
When marking a type attribute as used in a neverallow (to help determine
whether or not it should be expanded), check if the attribute's expression
list is empty (no attributes are associated with it) before iterating
over the list.

Signed-off-by: James Carter <jwcart2@gmail.com>
Acked-by: Nicolas Iooss <nicolas.iooss@m4x.org>
2021-06-24 10:23:28 -04:00
James Carter
ac8b35d910 libsepol/cil: Fix syntax checking of defaultrange rule
When "glblub" was added as a default for the defaultrange rule, the
syntax array was updated because the "glblub" default does not need
to specify a range of "low", "high", or "low-high". Unfortunately,
additional checking was not added for the "source" and "target"
defaults to make sure they specified a range. This means that using
the "source" or "target" defaults without specifying the range will
result in a segfault.

When the "source" or "target" defaults are used, check that the rule
specifies a range as well.

This bug was found by the secilc-fuzzer.

Signed-off-by: James Carter <jwcart2@gmail.com>
Acked-by: Nicolas Iooss <nicolas.iooss@m4x.org>
2021-06-24 10:23:04 -04:00
James Carter
c28525a26f libsepol/cil: Properly check for loops in sets
Commit 61fbdce666 (ibsepol/cil: Check
for self-referential loops in sets) added checks for self-referential
loops in user, role, type, and category sets. Unfortunately, this
check ends up in an infinite loop if the set with the self-referential
loop is used in a different set that is checked before the bad set.

The problem with the old check is that only the initial datum is used
for the check. Instead, use a stack to track all of the set datums
that are currently involved as the check is made. A self-referential
loop occurs if a duplicate datum is found for any of the datums in the
stack.

Signed-off-by: James Carter <jwcart2@gmail.com>
Acked-by: Nicolas Iooss <nicolas.iooss@m4x.org>
2021-06-24 10:03:51 -04:00
James Carter
67a8dc8117 libsepol/cil: Allow duplicate optional blocks in most cases
The commit d155b410d4 (libsepol/cil:
Check for duplicate blocks, optionals, and macros) fixed a bug
that allowed duplicate blocks, optionals, and macros with the same
name in the same namespace. For blocks and macros, a duplicate
is always a problem, but optional block names are only used for
in-statement resolution. If no in-statement refers to an optional
block, then it does not matter if more than one with same name
exists.

One easy way to generate multiple optional blocks with the same
name declaration is to call a macro with an optional block multiple
times in the same namespace.

As an example, here is a portion of CIL policy
  (macro m1 ((type t))
    (optional op1
      (allow t self (CLASS (PERM)))
    )
  )
  (type t1)
  (call m1 (t1))
  (type t2)
  (call m1 (t2))
This will result in two optional blocks with the name op1.

There are three parts to allowing multiple optional blocks with
the same name declaration.

1) Track an optional block's enabled status in a different way.

   One hinderance to allowing multiple optional blocks with the same
   name declaration is that they cannot share the same datum. This is
   because the datum is used to get the struct cil_optional which has
   the enabled field and each block's enabled status is independent of
   the others.

   Remove the enabled field from struct cil_optional, so it only contains
   the datum. Use a stack to track which optional blocks are being
   disabled, so they can be deleted in the right order.

2) Allow multiple declarations of optional blocks.

   Update cil_allow_multiple_decls() so that a flavor of CIL_OPTIONAL
   will return CIL_TRUE. Also remove the check in cil_copy_optional().

3) Check if an in-statement refers to an optional with multiple
   declarations and exit with an error if it does.

Signed-off-by: James Carter <jwcart2@gmail.com>
Acked-by: Nicolas Iooss <nicolas.iooss@m4x.org>
2021-06-24 10:02:26 -04:00
Christian Göttsche
9fb8df7f16 libsepol: declare read-only arrays const
Make it more apparent that those data does not change and enforce it.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
Acked-by: James Carter <jwcart2@gmail.com>
2021-06-24 09:41:30 -04:00
Christian Göttsche
4572bf254a libsepol: declare file local variable static
Clang issues:

    module_to_cil.c:65:7: warning: no previous extern declaration for non-static variable 'out_file' [-Wmissing-variable-declarations]
    FILE *out_file;
          ^

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
Acked-by: James Carter <jwcart2@gmail.com>
2021-06-24 09:41:24 -04:00
Christian Göttsche
4fbc018a27 libsepol: drop unnecessary casts
`hashtab_search()` does take `const_hashtab_key_t` as second parameter,
which is a typedef for `const char *`.
Drop the unnecessary and const-violating cast.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
Acked-by: James Carter <jwcart2@gmail.com>
2021-06-24 09:41:17 -04:00